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='/'>&lt;&lt 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  }