github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/http2/h2demo/h2demo.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build h2demo 6 7 package main 8 9 import ( 10 "bytes" 11 "crypto/tls" 12 "flag" 13 "fmt" 14 "hash/crc32" 15 "image" 16 "image/jpeg" 17 "io" 18 "io/ioutil" 19 "log" 20 "net" 21 "net/http" 22 "path" 23 "regexp" 24 "runtime" 25 "strconv" 26 "strings" 27 "sync" 28 "time" 29 30 "camlistore.org/pkg/googlestorage" 31 "camlistore.org/pkg/singleflight" 32 "golang.org/x/net/http2" 33 ) 34 35 var ( 36 prod = flag.Bool("prod", false, "Whether to configure itself to be the production http2.golang.org server.") 37 38 httpsAddr = flag.String("https_addr", "localhost:4430", "TLS address to listen on ('host:port' or ':port'). Required.") 39 httpAddr = flag.String("http_addr", "", "Plain HTTP address to listen on ('host:port', or ':port'). Empty means no HTTP.") 40 41 hostHTTP = flag.String("http_host", "", "Optional host or host:port to use for http:// links to this service. By default, this is implied from -http_addr.") 42 hostHTTPS = flag.String("https_host", "", "Optional host or host:port to use for http:// links to this service. By default, this is implied from -https_addr.") 43 ) 44 45 func homeOldHTTP(w http.ResponseWriter, r *http.Request) { 46 io.WriteString(w, `<html> 47 <body> 48 <h1>Go + HTTP/2</h1> 49 <p>Welcome to <a href="https://golang.org/">the Go language</a>'s <a href="https://http2.github.io/">HTTP/2</a> demo & interop server.</p> 50 <p>Unfortunately, you're <b>not</b> using HTTP/2 right now. To do so:</p> 51 <ul> 52 <li>Use Firefox Nightly or go to <b>about:config</b> and enable "network.http.spdy.enabled.http2draft"</li> 53 <li>Use Google Chrome Canary and/or go to <b>chrome://flags/#enable-spdy4</b> to <i>Enable SPDY/4</i> (Chrome's name for HTTP/2)</li> 54 </ul> 55 <p>See code & instructions for connecting at <a href="https://github.com/golang/net/tree/master/http2">https://github.com/golang/net/tree/master/http2</a>.</p> 56 57 </body></html>`) 58 } 59 60 func home(w http.ResponseWriter, r *http.Request) { 61 if r.URL.Path != "/" { 62 http.NotFound(w, r) 63 return 64 } 65 io.WriteString(w, `<html> 66 <body> 67 <h1>Go + HTTP/2</h1> 68 69 <p>Welcome to <a href="https://golang.org/">the Go language</a>'s <a 70 href="https://http2.github.io/">HTTP/2</a> demo & interop server.</p> 71 72 <p>Congratulations, <b>you're using HTTP/2 right now</b>.</p> 73 74 <p>This server exists for others in the HTTP/2 community to test their HTTP/2 client implementations and point out flaws in our server.</p> 75 76 <p> 77 The code is at <a href="https://golang.org/x/net/http2">golang.org/x/net/http2</a> and 78 is used transparently by the Go standard library from Go 1.6 and later. 79 </p> 80 81 <p>Contact info: <i>bradfitz@golang.org</i>, or <a 82 href="https://golang.org/issues">file a bug</a>.</p> 83 84 <h2>Handlers for testing</h2> 85 <ul> 86 <li>GET <a href="/reqinfo">/reqinfo</a> to dump the request + headers received</li> 87 <li>GET <a href="/clockstream">/clockstream</a> streams the current time every second</li> 88 <li>GET <a href="/gophertiles">/gophertiles</a> to see a page with a bunch of images</li> 89 <li>GET <a href="/file/gopher.png">/file/gopher.png</a> for a small file (does If-Modified-Since, Content-Range, etc)</li> 90 <li>GET <a href="/file/go.src.tar.gz">/file/go.src.tar.gz</a> for a larger file (~10 MB)</li> 91 <li>GET <a href="/redirect">/redirect</a> to redirect back to / (this page)</li> 92 <li>GET <a href="/goroutines">/goroutines</a> to see all active goroutines in this server</li> 93 <li>PUT something to <a href="/crc32">/crc32</a> to get a count of number of bytes and its CRC-32</li> 94 <li>PUT something to <a href="/ECHO">/ECHO</a> and it will be streamed back to you capitalized</li> 95 </ul> 96 97 </body></html>`) 98 } 99 100 func reqInfoHandler(w http.ResponseWriter, r *http.Request) { 101 w.Header().Set("Content-Type", "text/plain") 102 fmt.Fprintf(w, "Method: %s\n", r.Method) 103 fmt.Fprintf(w, "Protocol: %s\n", r.Proto) 104 fmt.Fprintf(w, "Host: %s\n", r.Host) 105 fmt.Fprintf(w, "RemoteAddr: %s\n", r.RemoteAddr) 106 fmt.Fprintf(w, "RequestURI: %q\n", r.RequestURI) 107 fmt.Fprintf(w, "URL: %#v\n", r.URL) 108 fmt.Fprintf(w, "Body.ContentLength: %d (-1 means unknown)\n", r.ContentLength) 109 fmt.Fprintf(w, "Close: %v (relevant for HTTP/1 only)\n", r.Close) 110 fmt.Fprintf(w, "TLS: %#v\n", r.TLS) 111 fmt.Fprintf(w, "\nHeaders:\n") 112 r.Header.Write(w) 113 } 114 115 func crcHandler(w http.ResponseWriter, r *http.Request) { 116 if r.Method != "PUT" { 117 http.Error(w, "PUT required.", 400) 118 return 119 } 120 crc := crc32.NewIEEE() 121 n, err := io.Copy(crc, r.Body) 122 if err == nil { 123 w.Header().Set("Content-Type", "text/plain") 124 fmt.Fprintf(w, "bytes=%d, CRC32=%x", n, crc.Sum(nil)) 125 } 126 } 127 128 type capitalizeReader struct { 129 r io.Reader 130 } 131 132 func (cr capitalizeReader) Read(p []byte) (n int, err error) { 133 n, err = cr.r.Read(p) 134 for i, b := range p[:n] { 135 if b >= 'a' && b <= 'z' { 136 p[i] = b - ('a' - 'A') 137 } 138 } 139 return 140 } 141 142 type flushWriter struct { 143 w io.Writer 144 } 145 146 func (fw flushWriter) Write(p []byte) (n int, err error) { 147 n, err = fw.w.Write(p) 148 if f, ok := fw.w.(http.Flusher); ok { 149 f.Flush() 150 } 151 return 152 } 153 154 func echoCapitalHandler(w http.ResponseWriter, r *http.Request) { 155 if r.Method != "PUT" { 156 http.Error(w, "PUT required.", 400) 157 return 158 } 159 io.Copy(flushWriter{w}, capitalizeReader{r.Body}) 160 } 161 162 var ( 163 fsGrp singleflight.Group 164 fsMu sync.Mutex // guards fsCache 165 fsCache = map[string]http.Handler{} 166 ) 167 168 // fileServer returns a file-serving handler that proxies URL. 169 // It lazily fetches URL on the first access and caches its contents forever. 170 func fileServer(url string) http.Handler { 171 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 172 hi, err := fsGrp.Do(url, func() (interface{}, error) { 173 fsMu.Lock() 174 if h, ok := fsCache[url]; ok { 175 fsMu.Unlock() 176 return h, nil 177 } 178 fsMu.Unlock() 179 180 res, err := http.Get(url) 181 if err != nil { 182 return nil, err 183 } 184 defer res.Body.Close() 185 slurp, err := ioutil.ReadAll(res.Body) 186 if err != nil { 187 return nil, err 188 } 189 190 modTime := time.Now() 191 var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 192 http.ServeContent(w, r, path.Base(url), modTime, bytes.NewReader(slurp)) 193 }) 194 fsMu.Lock() 195 fsCache[url] = h 196 fsMu.Unlock() 197 return h, nil 198 }) 199 if err != nil { 200 http.Error(w, err.Error(), 500) 201 return 202 } 203 hi.(http.Handler).ServeHTTP(w, r) 204 }) 205 } 206 207 func clockStreamHandler(w http.ResponseWriter, r *http.Request) { 208 clientGone := w.(http.CloseNotifier).CloseNotify() 209 w.Header().Set("Content-Type", "text/plain") 210 ticker := time.NewTicker(1 * time.Second) 211 defer ticker.Stop() 212 fmt.Fprintf(w, "# ~1KB of junk to force browsers to start rendering immediately: \n") 213 io.WriteString(w, strings.Repeat("# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", 13)) 214 215 for { 216 fmt.Fprintf(w, "%v\n", time.Now()) 217 w.(http.Flusher).Flush() 218 select { 219 case <-ticker.C: 220 case <-clientGone: 221 log.Printf("Client %v disconnected from the clock", r.RemoteAddr) 222 return 223 } 224 } 225 } 226 227 func registerHandlers() { 228 tiles := newGopherTilesHandler() 229 230 mux2 := http.NewServeMux() 231 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 232 if r.TLS == nil { 233 if r.URL.Path == "/gophertiles" { 234 tiles.ServeHTTP(w, r) 235 return 236 } 237 http.Redirect(w, r, "https://"+httpsHost()+"/", http.StatusFound) 238 return 239 } 240 if r.ProtoMajor == 1 { 241 if r.URL.Path == "/reqinfo" { 242 reqInfoHandler(w, r) 243 return 244 } 245 homeOldHTTP(w, r) 246 return 247 } 248 mux2.ServeHTTP(w, r) 249 }) 250 mux2.HandleFunc("/", home) 251 mux2.Handle("/file/gopher.png", fileServer("https://golang.org/doc/gopher/frontpage.png")) 252 mux2.Handle("/file/go.src.tar.gz", fileServer("https://storage.googleapis.com/golang/go1.4.1.src.tar.gz")) 253 mux2.HandleFunc("/reqinfo", reqInfoHandler) 254 mux2.HandleFunc("/crc32", crcHandler) 255 mux2.HandleFunc("/ECHO", echoCapitalHandler) 256 mux2.HandleFunc("/clockstream", clockStreamHandler) 257 mux2.Handle("/gophertiles", tiles) 258 mux2.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { 259 http.Redirect(w, r, "/", http.StatusFound) 260 }) 261 stripHomedir := regexp.MustCompile(`/(Users|home)/\w+`) 262 mux2.HandleFunc("/goroutines", func(w http.ResponseWriter, r *http.Request) { 263 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 264 buf := make([]byte, 2<<20) 265 w.Write(stripHomedir.ReplaceAll(buf[:runtime.Stack(buf, true)], nil)) 266 }) 267 } 268 269 func newGopherTilesHandler() http.Handler { 270 const gopherURL = "https://blog.golang.org/go-programming-language-turns-two_gophers.jpg" 271 res, err := http.Get(gopherURL) 272 if err != nil { 273 log.Fatal(err) 274 } 275 if res.StatusCode != 200 { 276 log.Fatalf("Error fetching %s: %v", gopherURL, res.Status) 277 } 278 slurp, err := ioutil.ReadAll(res.Body) 279 res.Body.Close() 280 if err != nil { 281 log.Fatal(err) 282 } 283 im, err := jpeg.Decode(bytes.NewReader(slurp)) 284 if err != nil { 285 if len(slurp) > 1024 { 286 slurp = slurp[:1024] 287 } 288 log.Fatalf("Failed to decode gopher image: %v (got %q)", err, slurp) 289 } 290 291 type subImager interface { 292 SubImage(image.Rectangle) image.Image 293 } 294 const tileSize = 32 295 xt := im.Bounds().Max.X / tileSize 296 yt := im.Bounds().Max.Y / tileSize 297 var tile [][][]byte // y -> x -> jpeg bytes 298 for yi := 0; yi < yt; yi++ { 299 var row [][]byte 300 for xi := 0; xi < xt; xi++ { 301 si := im.(subImager).SubImage(image.Rectangle{ 302 Min: image.Point{xi * tileSize, yi * tileSize}, 303 Max: image.Point{(xi + 1) * tileSize, (yi + 1) * tileSize}, 304 }) 305 buf := new(bytes.Buffer) 306 if err := jpeg.Encode(buf, si, &jpeg.Options{Quality: 90}); err != nil { 307 log.Fatal(err) 308 } 309 row = append(row, buf.Bytes()) 310 } 311 tile = append(tile, row) 312 } 313 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 314 ms, _ := strconv.Atoi(r.FormValue("latency")) 315 const nanosPerMilli = 1e6 316 if r.FormValue("x") != "" { 317 x, _ := strconv.Atoi(r.FormValue("x")) 318 y, _ := strconv.Atoi(r.FormValue("y")) 319 if ms <= 1000 { 320 time.Sleep(time.Duration(ms) * nanosPerMilli) 321 } 322 if x >= 0 && x < xt && y >= 0 && y < yt { 323 http.ServeContent(w, r, "", time.Time{}, bytes.NewReader(tile[y][x])) 324 return 325 } 326 } 327 io.WriteString(w, "<html><body onload='showtimes()'>") 328 fmt.Fprintf(w, "A grid of %d tiled images is below. Compare:<p>", xt*yt) 329 for _, ms := range []int{0, 30, 200, 1000} { 330 d := time.Duration(ms) * nanosPerMilli 331 fmt.Fprintf(w, "[<a href='https://%s/gophertiles?latency=%d'>HTTP/2, %v latency</a>] [<a href='http://%s/gophertiles?latency=%d'>HTTP/1, %v latency</a>]<br>\n", 332 httpsHost(), ms, d, 333 httpHost(), ms, d, 334 ) 335 } 336 io.WriteString(w, "<p>\n") 337 cacheBust := time.Now().UnixNano() 338 for y := 0; y < yt; y++ { 339 for x := 0; x < xt; x++ { 340 fmt.Fprintf(w, "<img width=%d height=%d src='/gophertiles?x=%d&y=%d&cachebust=%d&latency=%d'>", 341 tileSize, tileSize, x, y, cacheBust, ms) 342 } 343 io.WriteString(w, "<br/>\n") 344 } 345 io.WriteString(w, `<p><div id='loadtimes'></div></p> 346 <script> 347 function showtimes() { 348 var times = 'Times from connection start:<br>' 349 times += 'DOM loaded: ' + (window.performance.timing.domContentLoadedEventEnd - window.performance.timing.connectStart) + 'ms<br>' 350 times += 'DOM complete (images loaded): ' + (window.performance.timing.domComplete - window.performance.timing.connectStart) + 'ms<br>' 351 document.getElementById('loadtimes').innerHTML = times 352 } 353 </script> 354 <hr><a href='/'><< Back to Go HTTP/2 demo server</a></body></html>`) 355 }) 356 } 357 358 func httpsHost() string { 359 if *hostHTTPS != "" { 360 return *hostHTTPS 361 } 362 if v := *httpsAddr; strings.HasPrefix(v, ":") { 363 return "localhost" + v 364 } else { 365 return v 366 } 367 } 368 369 func httpHost() string { 370 if *hostHTTP != "" { 371 return *hostHTTP 372 } 373 if v := *httpAddr; strings.HasPrefix(v, ":") { 374 return "localhost" + v 375 } else { 376 return v 377 } 378 } 379 380 func serveProdTLS() error { 381 c, err := googlestorage.NewServiceClient() 382 if err != nil { 383 return err 384 } 385 slurp := func(key string) ([]byte, error) { 386 const bucket = "http2-demo-server-tls" 387 rc, _, err := c.GetObject(&googlestorage.Object{ 388 Bucket: bucket, 389 Key: key, 390 }) 391 if err != nil { 392 return nil, fmt.Errorf("Error fetching GCS object %q in bucket %q: %v", key, bucket, err) 393 } 394 defer rc.Close() 395 return ioutil.ReadAll(rc) 396 } 397 certPem, err := slurp("http2.golang.org.chained.pem") 398 if err != nil { 399 return err 400 } 401 keyPem, err := slurp("http2.golang.org.key") 402 if err != nil { 403 return err 404 } 405 cert, err := tls.X509KeyPair(certPem, keyPem) 406 if err != nil { 407 return err 408 } 409 srv := &http.Server{ 410 TLSConfig: &tls.Config{ 411 Certificates: []tls.Certificate{cert}, 412 }, 413 } 414 http2.ConfigureServer(srv, &http2.Server{}) 415 ln, err := net.Listen("tcp", ":443") 416 if err != nil { 417 return err 418 } 419 return srv.Serve(tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig)) 420 } 421 422 type tcpKeepAliveListener struct { 423 *net.TCPListener 424 } 425 426 func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { 427 tc, err := ln.AcceptTCP() 428 if err != nil { 429 return 430 } 431 tc.SetKeepAlive(true) 432 tc.SetKeepAlivePeriod(3 * time.Minute) 433 return tc, nil 434 } 435 436 func serveProd() error { 437 errc := make(chan error, 2) 438 go func() { errc <- http.ListenAndServe(":80", nil) }() 439 go func() { errc <- serveProdTLS() }() 440 return <-errc 441 } 442 443 func main() { 444 var srv http.Server 445 flag.BoolVar(&http2.VerboseLogs, "verbose", false, "Verbose HTTP/2 debugging.") 446 flag.Parse() 447 srv.Addr = *httpsAddr 448 449 registerHandlers() 450 451 if *prod { 452 *hostHTTP = "http2.golang.org" 453 *hostHTTPS = "http2.golang.org" 454 log.Fatal(serveProd()) 455 } 456 457 url := "https://" + httpsHost() + "/" 458 log.Printf("Listening on " + url) 459 http2.ConfigureServer(&srv, &http2.Server{}) 460 461 if *httpAddr != "" { 462 go func() { 463 log.Printf("Listening on http://" + httpHost() + "/ (for unencrypted HTTP/1)") 464 log.Fatal(http.ListenAndServe(*httpAddr, nil)) 465 }() 466 } 467 468 go func() { 469 log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key")) 470 }() 471 select {} 472 }