OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / net / http2 / h2demo / h2demo.go
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         "os"
23         "path"
24         "regexp"
25         "runtime"
26         "strconv"
27         "strings"
28         "sync"
29         "time"
30
31         "go4.org/syncutil/singleflight"
32         "golang.org/x/crypto/acme/autocert"
33         "golang.org/x/net/http2"
34 )
35
36 var (
37         prod = flag.Bool("prod", false, "Whether to configure itself to be the production http2.golang.org server.")
38
39         httpsAddr = flag.String("https_addr", "localhost:4430", "TLS address to listen on ('host:port' or ':port'). Required.")
40         httpAddr  = flag.String("http_addr", "", "Plain HTTP address to listen on ('host:port', or ':port'). Empty means no HTTP.")
41
42         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.")
43         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.")
44 )
45
46 func homeOldHTTP(w http.ResponseWriter, r *http.Request) {
47         io.WriteString(w, `<html>
48 <body>
49 <h1>Go + HTTP/2</h1>
50 <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>
51 <p>Unfortunately, you're <b>not</b> using HTTP/2 right now. To do so:</p>
52 <ul>
53    <li>Use Firefox Nightly or go to <b>about:config</b> and enable "network.http.spdy.enabled.http2draft"</li>
54    <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>
55 </ul>
56 <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>
57
58 </body></html>`)
59 }
60
61 func home(w http.ResponseWriter, r *http.Request) {
62         if r.URL.Path != "/" {
63                 http.NotFound(w, r)
64                 return
65         }
66         io.WriteString(w, `<html>
67 <body>
68 <h1>Go + HTTP/2</h1>
69
70 <p>Welcome to <a href="https://golang.org/">the Go language</a>'s <a
71 href="https://http2.github.io/">HTTP/2</a> demo & interop server.</p>
72
73 <p>Congratulations, <b>you're using HTTP/2 right now</b>.</p>
74
75 <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>
76
77 <p>
78 The code is at <a href="https://golang.org/x/net/http2">golang.org/x/net/http2</a> and
79 is used transparently by the Go standard library from Go 1.6 and later.
80 </p>
81
82 <p>Contact info: <i>bradfitz@golang.org</i>, or <a
83 href="https://golang.org/s/http2bug">file a bug</a>.</p>
84
85 <h2>Handlers for testing</h2>
86 <ul>
87   <li>GET <a href="/reqinfo">/reqinfo</a> to dump the request + headers received</li>
88   <li>GET <a href="/clockstream">/clockstream</a> streams the current time every second</li>
89   <li>GET <a href="/gophertiles">/gophertiles</a> to see a page with a bunch of images</li>
90   <li>GET <a href="/serverpush">/serverpush</a> to see a page with server push</li>
91   <li>GET <a href="/file/gopher.png">/file/gopher.png</a> for a small file (does If-Modified-Since, Content-Range, etc)</li>
92   <li>GET <a href="/file/go.src.tar.gz">/file/go.src.tar.gz</a> for a larger file (~10 MB)</li>
93   <li>GET <a href="/redirect">/redirect</a> to redirect back to / (this page)</li>
94   <li>GET <a href="/goroutines">/goroutines</a> to see all active goroutines in this server</li>
95   <li>PUT something to <a href="/crc32">/crc32</a> to get a count of number of bytes and its CRC-32</li>
96   <li>PUT something to <a href="/ECHO">/ECHO</a> and it will be streamed back to you capitalized</li>
97 </ul>
98
99 </body></html>`)
100 }
101
102 func reqInfoHandler(w http.ResponseWriter, r *http.Request) {
103         w.Header().Set("Content-Type", "text/plain")
104         fmt.Fprintf(w, "Method: %s\n", r.Method)
105         fmt.Fprintf(w, "Protocol: %s\n", r.Proto)
106         fmt.Fprintf(w, "Host: %s\n", r.Host)
107         fmt.Fprintf(w, "RemoteAddr: %s\n", r.RemoteAddr)
108         fmt.Fprintf(w, "RequestURI: %q\n", r.RequestURI)
109         fmt.Fprintf(w, "URL: %#v\n", r.URL)
110         fmt.Fprintf(w, "Body.ContentLength: %d (-1 means unknown)\n", r.ContentLength)
111         fmt.Fprintf(w, "Close: %v (relevant for HTTP/1 only)\n", r.Close)
112         fmt.Fprintf(w, "TLS: %#v\n", r.TLS)
113         fmt.Fprintf(w, "\nHeaders:\n")
114         r.Header.Write(w)
115 }
116
117 func crcHandler(w http.ResponseWriter, r *http.Request) {
118         if r.Method != "PUT" {
119                 http.Error(w, "PUT required.", 400)
120                 return
121         }
122         crc := crc32.NewIEEE()
123         n, err := io.Copy(crc, r.Body)
124         if err == nil {
125                 w.Header().Set("Content-Type", "text/plain")
126                 fmt.Fprintf(w, "bytes=%d, CRC32=%x", n, crc.Sum(nil))
127         }
128 }
129
130 type capitalizeReader struct {
131         r io.Reader
132 }
133
134 func (cr capitalizeReader) Read(p []byte) (n int, err error) {
135         n, err = cr.r.Read(p)
136         for i, b := range p[:n] {
137                 if b >= 'a' && b <= 'z' {
138                         p[i] = b - ('a' - 'A')
139                 }
140         }
141         return
142 }
143
144 type flushWriter struct {
145         w io.Writer
146 }
147
148 func (fw flushWriter) Write(p []byte) (n int, err error) {
149         n, err = fw.w.Write(p)
150         if f, ok := fw.w.(http.Flusher); ok {
151                 f.Flush()
152         }
153         return
154 }
155
156 func echoCapitalHandler(w http.ResponseWriter, r *http.Request) {
157         if r.Method != "PUT" {
158                 http.Error(w, "PUT required.", 400)
159                 return
160         }
161         io.Copy(flushWriter{w}, capitalizeReader{r.Body})
162 }
163
164 var (
165         fsGrp   singleflight.Group
166         fsMu    sync.Mutex // guards fsCache
167         fsCache = map[string]http.Handler{}
168 )
169
170 // fileServer returns a file-serving handler that proxies URL.
171 // It lazily fetches URL on the first access and caches its contents forever.
172 func fileServer(url string, latency time.Duration) http.Handler {
173         return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
174                 if latency > 0 {
175                         time.Sleep(latency)
176                 }
177                 hi, err := fsGrp.Do(url, func() (interface{}, error) {
178                         fsMu.Lock()
179                         if h, ok := fsCache[url]; ok {
180                                 fsMu.Unlock()
181                                 return h, nil
182                         }
183                         fsMu.Unlock()
184
185                         res, err := http.Get(url)
186                         if err != nil {
187                                 return nil, err
188                         }
189                         defer res.Body.Close()
190                         slurp, err := ioutil.ReadAll(res.Body)
191                         if err != nil {
192                                 return nil, err
193                         }
194
195                         modTime := time.Now()
196                         var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
197                                 http.ServeContent(w, r, path.Base(url), modTime, bytes.NewReader(slurp))
198                         })
199                         fsMu.Lock()
200                         fsCache[url] = h
201                         fsMu.Unlock()
202                         return h, nil
203                 })
204                 if err != nil {
205                         http.Error(w, err.Error(), 500)
206                         return
207                 }
208                 hi.(http.Handler).ServeHTTP(w, r)
209         })
210 }
211
212 func clockStreamHandler(w http.ResponseWriter, r *http.Request) {
213         clientGone := w.(http.CloseNotifier).CloseNotify()
214         w.Header().Set("Content-Type", "text/plain")
215         ticker := time.NewTicker(1 * time.Second)
216         defer ticker.Stop()
217         fmt.Fprintf(w, "# ~1KB of junk to force browsers to start rendering immediately: \n")
218         io.WriteString(w, strings.Repeat("# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", 13))
219
220         for {
221                 fmt.Fprintf(w, "%v\n", time.Now())
222                 w.(http.Flusher).Flush()
223                 select {
224                 case <-ticker.C:
225                 case <-clientGone:
226                         log.Printf("Client %v disconnected from the clock", r.RemoteAddr)
227                         return
228                 }
229         }
230 }
231
232 func registerHandlers() {
233         tiles := newGopherTilesHandler()
234         push := newPushHandler()
235
236         mux2 := http.NewServeMux()
237         http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
238                 switch {
239                 case r.URL.Path == "/gophertiles":
240                         tiles.ServeHTTP(w, r) // allow HTTP/2 + HTTP/1.x
241                         return
242                 case strings.HasPrefix(r.URL.Path, "/serverpush"):
243                         push.ServeHTTP(w, r) // allow HTTP/2 + HTTP/1.x
244                         return
245                 case r.TLS == nil: // do not allow HTTP/1.x for anything else
246                         http.Redirect(w, r, "https://"+httpsHost()+"/", http.StatusFound)
247                         return
248                 }
249                 if r.ProtoMajor == 1 {
250                         if r.URL.Path == "/reqinfo" {
251                                 reqInfoHandler(w, r)
252                                 return
253                         }
254                         homeOldHTTP(w, r)
255                         return
256                 }
257                 mux2.ServeHTTP(w, r)
258         })
259         mux2.HandleFunc("/", home)
260         mux2.Handle("/file/gopher.png", fileServer("https://golang.org/doc/gopher/frontpage.png", 0))
261         mux2.Handle("/file/go.src.tar.gz", fileServer("https://storage.googleapis.com/golang/go1.4.1.src.tar.gz", 0))
262         mux2.HandleFunc("/reqinfo", reqInfoHandler)
263         mux2.HandleFunc("/crc32", crcHandler)
264         mux2.HandleFunc("/ECHO", echoCapitalHandler)
265         mux2.HandleFunc("/clockstream", clockStreamHandler)
266         mux2.Handle("/gophertiles", tiles)
267         mux2.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
268                 http.Redirect(w, r, "/", http.StatusFound)
269         })
270         stripHomedir := regexp.MustCompile(`/(Users|home)/\w+`)
271         mux2.HandleFunc("/goroutines", func(w http.ResponseWriter, r *http.Request) {
272                 w.Header().Set("Content-Type", "text/plain; charset=utf-8")
273                 buf := make([]byte, 2<<20)
274                 w.Write(stripHomedir.ReplaceAll(buf[:runtime.Stack(buf, true)], nil))
275         })
276 }
277
278 var pushResources = map[string]http.Handler{
279         "/serverpush/static/jquery.min.js": fileServer("https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js", 100*time.Millisecond),
280         "/serverpush/static/godocs.js":     fileServer("https://golang.org/lib/godoc/godocs.js", 100*time.Millisecond),
281         "/serverpush/static/playground.js": fileServer("https://golang.org/lib/godoc/playground.js", 100*time.Millisecond),
282         "/serverpush/static/style.css":     fileServer("https://golang.org/lib/godoc/style.css", 100*time.Millisecond),
283 }
284
285 func newPushHandler() http.Handler {
286         return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
287                 for path, handler := range pushResources {
288                         if r.URL.Path == path {
289                                 handler.ServeHTTP(w, r)
290                                 return
291                         }
292                 }
293
294                 cacheBust := time.Now().UnixNano()
295                 if pusher, ok := w.(http.Pusher); ok {
296                         for path := range pushResources {
297                                 url := fmt.Sprintf("%s?%d", path, cacheBust)
298                                 if err := pusher.Push(url, nil); err != nil {
299                                         log.Printf("Failed to push %v: %v", path, err)
300                                 }
301                         }
302                 }
303                 time.Sleep(100 * time.Millisecond) // fake network latency + parsing time
304                 if err := pushTmpl.Execute(w, struct {
305                         CacheBust int64
306                         HTTPSHost string
307                         HTTPHost  string
308                 }{
309                         CacheBust: cacheBust,
310                         HTTPSHost: httpsHost(),
311                         HTTPHost:  httpHost(),
312                 }); err != nil {
313                         log.Printf("Executing server push template: %v", err)
314                 }
315         })
316 }
317
318 func newGopherTilesHandler() http.Handler {
319         const gopherURL = "https://blog.golang.org/go-programming-language-turns-two_gophers.jpg"
320         res, err := http.Get(gopherURL)
321         if err != nil {
322                 log.Fatal(err)
323         }
324         if res.StatusCode != 200 {
325                 log.Fatalf("Error fetching %s: %v", gopherURL, res.Status)
326         }
327         slurp, err := ioutil.ReadAll(res.Body)
328         res.Body.Close()
329         if err != nil {
330                 log.Fatal(err)
331         }
332         im, err := jpeg.Decode(bytes.NewReader(slurp))
333         if err != nil {
334                 if len(slurp) > 1024 {
335                         slurp = slurp[:1024]
336                 }
337                 log.Fatalf("Failed to decode gopher image: %v (got %q)", err, slurp)
338         }
339
340         type subImager interface {
341                 SubImage(image.Rectangle) image.Image
342         }
343         const tileSize = 32
344         xt := im.Bounds().Max.X / tileSize
345         yt := im.Bounds().Max.Y / tileSize
346         var tile [][][]byte // y -> x -> jpeg bytes
347         for yi := 0; yi < yt; yi++ {
348                 var row [][]byte
349                 for xi := 0; xi < xt; xi++ {
350                         si := im.(subImager).SubImage(image.Rectangle{
351                                 Min: image.Point{xi * tileSize, yi * tileSize},
352                                 Max: image.Point{(xi + 1) * tileSize, (yi + 1) * tileSize},
353                         })
354                         buf := new(bytes.Buffer)
355                         if err := jpeg.Encode(buf, si, &jpeg.Options{Quality: 90}); err != nil {
356                                 log.Fatal(err)
357                         }
358                         row = append(row, buf.Bytes())
359                 }
360                 tile = append(tile, row)
361         }
362         return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
363                 ms, _ := strconv.Atoi(r.FormValue("latency"))
364                 const nanosPerMilli = 1e6
365                 if r.FormValue("x") != "" {
366                         x, _ := strconv.Atoi(r.FormValue("x"))
367                         y, _ := strconv.Atoi(r.FormValue("y"))
368                         if ms <= 1000 {
369                                 time.Sleep(time.Duration(ms) * nanosPerMilli)
370                         }
371                         if x >= 0 && x < xt && y >= 0 && y < yt {
372                                 http.ServeContent(w, r, "", time.Time{}, bytes.NewReader(tile[y][x]))
373                                 return
374                         }
375                 }
376                 io.WriteString(w, "<html><body onload='showtimes()'>")
377                 fmt.Fprintf(w, "A grid of %d tiled images is below. Compare:<p>", xt*yt)
378                 for _, ms := range []int{0, 30, 200, 1000} {
379                         d := time.Duration(ms) * nanosPerMilli
380                         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",
381                                 httpsHost(), ms, d,
382                                 httpHost(), ms, d,
383                         )
384                 }
385                 io.WriteString(w, "<p>\n")
386                 cacheBust := time.Now().UnixNano()
387                 for y := 0; y < yt; y++ {
388                         for x := 0; x < xt; x++ {
389                                 fmt.Fprintf(w, "<img width=%d height=%d src='/gophertiles?x=%d&y=%d&cachebust=%d&latency=%d'>",
390                                         tileSize, tileSize, x, y, cacheBust, ms)
391                         }
392                         io.WriteString(w, "<br/>\n")
393                 }
394                 io.WriteString(w, `<p><div id='loadtimes'></div></p>
395 <script>
396 function showtimes() {
397         var times = 'Times from connection start:<br>'
398         times += 'DOM loaded: ' + (window.performance.timing.domContentLoadedEventEnd - window.performance.timing.connectStart) + 'ms<br>'
399         times += 'DOM complete (images loaded): ' + (window.performance.timing.domComplete - window.performance.timing.connectStart) + 'ms<br>'
400         document.getElementById('loadtimes').innerHTML = times
401 }
402 </script>
403 <hr><a href='/'>&lt;&lt Back to Go HTTP/2 demo server</a></body></html>`)
404         })
405 }
406
407 func httpsHost() string {
408         if *hostHTTPS != "" {
409                 return *hostHTTPS
410         }
411         if v := *httpsAddr; strings.HasPrefix(v, ":") {
412                 return "localhost" + v
413         } else {
414                 return v
415         }
416 }
417
418 func httpHost() string {
419         if *hostHTTP != "" {
420                 return *hostHTTP
421         }
422         if v := *httpAddr; strings.HasPrefix(v, ":") {
423                 return "localhost" + v
424         } else {
425                 return v
426         }
427 }
428
429 func serveProdTLS() error {
430         const cacheDir = "/var/cache/autocert"
431         if err := os.MkdirAll(cacheDir, 0700); err != nil {
432                 return err
433         }
434         m := autocert.Manager{
435                 Cache:      autocert.DirCache(cacheDir),
436                 Prompt:     autocert.AcceptTOS,
437                 HostPolicy: autocert.HostWhitelist("http2.golang.org"),
438         }
439         srv := &http.Server{
440                 TLSConfig: &tls.Config{
441                         GetCertificate: m.GetCertificate,
442                 },
443         }
444         http2.ConfigureServer(srv, &http2.Server{
445                 NewWriteScheduler: func() http2.WriteScheduler {
446                         return http2.NewPriorityWriteScheduler(nil)
447                 },
448         })
449         ln, err := net.Listen("tcp", ":443")
450         if err != nil {
451                 return err
452         }
453         return srv.Serve(tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig))
454 }
455
456 type tcpKeepAliveListener struct {
457         *net.TCPListener
458 }
459
460 func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
461         tc, err := ln.AcceptTCP()
462         if err != nil {
463                 return
464         }
465         tc.SetKeepAlive(true)
466         tc.SetKeepAlivePeriod(3 * time.Minute)
467         return tc, nil
468 }
469
470 func serveProd() error {
471         errc := make(chan error, 2)
472         go func() { errc <- http.ListenAndServe(":80", nil) }()
473         go func() { errc <- serveProdTLS() }()
474         return <-errc
475 }
476
477 const idleTimeout = 5 * time.Minute
478 const activeTimeout = 10 * time.Minute
479
480 // TODO: put this into the standard library and actually send
481 // PING frames and GOAWAY, etc: golang.org/issue/14204
482 func idleTimeoutHook() func(net.Conn, http.ConnState) {
483         var mu sync.Mutex
484         m := map[net.Conn]*time.Timer{}
485         return func(c net.Conn, cs http.ConnState) {
486                 mu.Lock()
487                 defer mu.Unlock()
488                 if t, ok := m[c]; ok {
489                         delete(m, c)
490                         t.Stop()
491                 }
492                 var d time.Duration
493                 switch cs {
494                 case http.StateNew, http.StateIdle:
495                         d = idleTimeout
496                 case http.StateActive:
497                         d = activeTimeout
498                 default:
499                         return
500                 }
501                 m[c] = time.AfterFunc(d, func() {
502                         log.Printf("closing idle conn %v after %v", c.RemoteAddr(), d)
503                         go c.Close()
504                 })
505         }
506 }
507
508 func main() {
509         var srv http.Server
510         flag.BoolVar(&http2.VerboseLogs, "verbose", false, "Verbose HTTP/2 debugging.")
511         flag.Parse()
512         srv.Addr = *httpsAddr
513         srv.ConnState = idleTimeoutHook()
514
515         registerHandlers()
516
517         if *prod {
518                 *hostHTTP = "http2.golang.org"
519                 *hostHTTPS = "http2.golang.org"
520                 log.Fatal(serveProd())
521         }
522
523         url := "https://" + httpsHost() + "/"
524         log.Printf("Listening on " + url)
525         http2.ConfigureServer(&srv, &http2.Server{})
526
527         if *httpAddr != "" {
528                 go func() {
529                         log.Printf("Listening on http://" + httpHost() + "/ (for unencrypted HTTP/1)")
530                         log.Fatal(http.ListenAndServe(*httpAddr, nil))
531                 }()
532         }
533
534         go func() {
535                 log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key"))
536         }()
537         select {}
538 }