From 6e2cb00008cbf09e556b00f87603797fcaa47e09 Mon Sep 17 00:00:00 2001
From: Christopher Speller Welcome to the Go language's HTTP/2 demo & interop server. Unfortunately, you're not using HTTP/2 right now. To do so: See code & instructions for connecting at https://github.com/golang/net/tree/master/http2. Welcome to the Go language's HTTP/2 demo & interop server. Congratulations, you're using HTTP/2 right now. This server exists for others in the HTTP/2 community to test their HTTP/2 client implementations and point out flaws in our server.
-The code is at golang.org/x/net/http2 and
-is used transparently by the Go standard library from Go 1.6 and later.
- Contact info: bradfitz@golang.org, or file a bug. ", xt*yt)
- for _, ms := range []int{0, 30, 200, 1000} {
- d := time.Duration(ms) * nanosPerMilli
- fmt.Fprintf(w, "[HTTP/2, %v latency] [HTTP/1, %v latency] \n")
- cacheBust := time.Now().UnixNano()
- for y := 0; y < yt; y++ {
- for x := 0; x < xt; x++ {
- fmt.Fprintf(w, "Go + HTTP/2
-
-
-Go + HTTP/2
-
-Handlers for testing
-
-
-
-`)
-}
-
-func reqInfoHandler(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "text/plain")
- fmt.Fprintf(w, "Method: %s\n", r.Method)
- fmt.Fprintf(w, "Protocol: %s\n", r.Proto)
- fmt.Fprintf(w, "Host: %s\n", r.Host)
- fmt.Fprintf(w, "RemoteAddr: %s\n", r.RemoteAddr)
- fmt.Fprintf(w, "RequestURI: %q\n", r.RequestURI)
- fmt.Fprintf(w, "URL: %#v\n", r.URL)
- fmt.Fprintf(w, "Body.ContentLength: %d (-1 means unknown)\n", r.ContentLength)
- fmt.Fprintf(w, "Close: %v (relevant for HTTP/1 only)\n", r.Close)
- fmt.Fprintf(w, "TLS: %#v\n", r.TLS)
- fmt.Fprintf(w, "\nHeaders:\n")
- r.Header.Write(w)
-}
-
-func crcHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != "PUT" {
- http.Error(w, "PUT required.", 400)
- return
- }
- crc := crc32.NewIEEE()
- n, err := io.Copy(crc, r.Body)
- if err == nil {
- w.Header().Set("Content-Type", "text/plain")
- fmt.Fprintf(w, "bytes=%d, CRC32=%x", n, crc.Sum(nil))
- }
-}
-
-type capitalizeReader struct {
- r io.Reader
-}
-
-func (cr capitalizeReader) Read(p []byte) (n int, err error) {
- n, err = cr.r.Read(p)
- for i, b := range p[:n] {
- if b >= 'a' && b <= 'z' {
- p[i] = b - ('a' - 'A')
- }
- }
- return
-}
-
-type flushWriter struct {
- w io.Writer
-}
-
-func (fw flushWriter) Write(p []byte) (n int, err error) {
- n, err = fw.w.Write(p)
- if f, ok := fw.w.(http.Flusher); ok {
- f.Flush()
- }
- return
-}
-
-func echoCapitalHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != "PUT" {
- http.Error(w, "PUT required.", 400)
- return
- }
- io.Copy(flushWriter{w}, capitalizeReader{r.Body})
-}
-
-var (
- fsGrp singleflight.Group
- fsMu sync.Mutex // guards fsCache
- fsCache = map[string]http.Handler{}
-)
-
-// fileServer returns a file-serving handler that proxies URL.
-// It lazily fetches URL on the first access and caches its contents forever.
-func fileServer(url string, latency time.Duration) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if latency > 0 {
- time.Sleep(latency)
- }
- hi, err := fsGrp.Do(url, func() (interface{}, error) {
- fsMu.Lock()
- if h, ok := fsCache[url]; ok {
- fsMu.Unlock()
- return h, nil
- }
- fsMu.Unlock()
-
- res, err := http.Get(url)
- if err != nil {
- return nil, err
- }
- defer res.Body.Close()
- slurp, err := ioutil.ReadAll(res.Body)
- if err != nil {
- return nil, err
- }
-
- modTime := time.Now()
- var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- http.ServeContent(w, r, path.Base(url), modTime, bytes.NewReader(slurp))
- })
- fsMu.Lock()
- fsCache[url] = h
- fsMu.Unlock()
- return h, nil
- })
- if err != nil {
- http.Error(w, err.Error(), 500)
- return
- }
- hi.(http.Handler).ServeHTTP(w, r)
- })
-}
-
-func clockStreamHandler(w http.ResponseWriter, r *http.Request) {
- clientGone := w.(http.CloseNotifier).CloseNotify()
- w.Header().Set("Content-Type", "text/plain")
- ticker := time.NewTicker(1 * time.Second)
- defer ticker.Stop()
- fmt.Fprintf(w, "# ~1KB of junk to force browsers to start rendering immediately: \n")
- io.WriteString(w, strings.Repeat("# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", 13))
-
- for {
- fmt.Fprintf(w, "%v\n", time.Now())
- w.(http.Flusher).Flush()
- select {
- case <-ticker.C:
- case <-clientGone:
- log.Printf("Client %v disconnected from the clock", r.RemoteAddr)
- return
- }
- }
-}
-
-func registerHandlers() {
- tiles := newGopherTilesHandler()
- push := newPushHandler()
-
- mux2 := http.NewServeMux()
- http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- switch {
- case r.URL.Path == "/gophertiles":
- tiles.ServeHTTP(w, r) // allow HTTP/2 + HTTP/1.x
- return
- case strings.HasPrefix(r.URL.Path, "/serverpush"):
- push.ServeHTTP(w, r) // allow HTTP/2 + HTTP/1.x
- return
- case r.TLS == nil: // do not allow HTTP/1.x for anything else
- http.Redirect(w, r, "https://"+httpsHost()+"/", http.StatusFound)
- return
- }
- if r.ProtoMajor == 1 {
- if r.URL.Path == "/reqinfo" {
- reqInfoHandler(w, r)
- return
- }
- homeOldHTTP(w, r)
- return
- }
- mux2.ServeHTTP(w, r)
- })
- mux2.HandleFunc("/", home)
- mux2.Handle("/file/gopher.png", fileServer("https://golang.org/doc/gopher/frontpage.png", 0))
- mux2.Handle("/file/go.src.tar.gz", fileServer("https://storage.googleapis.com/golang/go1.4.1.src.tar.gz", 0))
- mux2.HandleFunc("/reqinfo", reqInfoHandler)
- mux2.HandleFunc("/crc32", crcHandler)
- mux2.HandleFunc("/ECHO", echoCapitalHandler)
- mux2.HandleFunc("/clockstream", clockStreamHandler)
- mux2.Handle("/gophertiles", tiles)
- mux2.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
- http.Redirect(w, r, "/", http.StatusFound)
- })
- stripHomedir := regexp.MustCompile(`/(Users|home)/\w+`)
- mux2.HandleFunc("/goroutines", func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- buf := make([]byte, 2<<20)
- w.Write(stripHomedir.ReplaceAll(buf[:runtime.Stack(buf, true)], nil))
- })
-}
-
-var pushResources = map[string]http.Handler{
- "/serverpush/static/jquery.min.js": fileServer("https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js", 100*time.Millisecond),
- "/serverpush/static/godocs.js": fileServer("https://golang.org/lib/godoc/godocs.js", 100*time.Millisecond),
- "/serverpush/static/playground.js": fileServer("https://golang.org/lib/godoc/playground.js", 100*time.Millisecond),
- "/serverpush/static/style.css": fileServer("https://golang.org/lib/godoc/style.css", 100*time.Millisecond),
-}
-
-func newPushHandler() http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- for path, handler := range pushResources {
- if r.URL.Path == path {
- handler.ServeHTTP(w, r)
- return
- }
- }
-
- cacheBust := time.Now().UnixNano()
- if pusher, ok := w.(http.Pusher); ok {
- for path := range pushResources {
- url := fmt.Sprintf("%s?%d", path, cacheBust)
- if err := pusher.Push(url, nil); err != nil {
- log.Printf("Failed to push %v: %v", path, err)
- }
- }
- }
- time.Sleep(100 * time.Millisecond) // fake network latency + parsing time
- if err := pushTmpl.Execute(w, struct {
- CacheBust int64
- HTTPSHost string
- HTTPHost string
- }{
- CacheBust: cacheBust,
- HTTPSHost: httpsHost(),
- HTTPHost: httpHost(),
- }); err != nil {
- log.Printf("Executing server push template: %v", err)
- }
- })
-}
-
-func newGopherTilesHandler() http.Handler {
- const gopherURL = "https://blog.golang.org/go-programming-language-turns-two_gophers.jpg"
- res, err := http.Get(gopherURL)
- if err != nil {
- log.Fatal(err)
- }
- if res.StatusCode != 200 {
- log.Fatalf("Error fetching %s: %v", gopherURL, res.Status)
- }
- slurp, err := ioutil.ReadAll(res.Body)
- res.Body.Close()
- if err != nil {
- log.Fatal(err)
- }
- im, err := jpeg.Decode(bytes.NewReader(slurp))
- if err != nil {
- if len(slurp) > 1024 {
- slurp = slurp[:1024]
- }
- log.Fatalf("Failed to decode gopher image: %v (got %q)", err, slurp)
- }
-
- type subImager interface {
- SubImage(image.Rectangle) image.Image
- }
- const tileSize = 32
- xt := im.Bounds().Max.X / tileSize
- yt := im.Bounds().Max.Y / tileSize
- var tile [][][]byte // y -> x -> jpeg bytes
- for yi := 0; yi < yt; yi++ {
- var row [][]byte
- for xi := 0; xi < xt; xi++ {
- si := im.(subImager).SubImage(image.Rectangle{
- Min: image.Point{xi * tileSize, yi * tileSize},
- Max: image.Point{(xi + 1) * tileSize, (yi + 1) * tileSize},
- })
- buf := new(bytes.Buffer)
- if err := jpeg.Encode(buf, si, &jpeg.Options{Quality: 90}); err != nil {
- log.Fatal(err)
- }
- row = append(row, buf.Bytes())
- }
- tile = append(tile, row)
- }
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- ms, _ := strconv.Atoi(r.FormValue("latency"))
- const nanosPerMilli = 1e6
- if r.FormValue("x") != "" {
- x, _ := strconv.Atoi(r.FormValue("x"))
- y, _ := strconv.Atoi(r.FormValue("y"))
- if ms <= 1000 {
- time.Sleep(time.Duration(ms) * nanosPerMilli)
- }
- if x >= 0 && x < xt && y >= 0 && y < yt {
- http.ServeContent(w, r, "", time.Time{}, bytes.NewReader(tile[y][x]))
- return
- }
- }
- io.WriteString(w, "")
- fmt.Fprintf(w, "A grid of %d tiled images is below. Compare:
\n",
- httpsHost(), ms, d,
- httpHost(), ms, d,
- )
- }
- io.WriteString(w, "",
- tileSize, tileSize, x, y, cacheBust, ms)
- }
- io.WriteString(w, "
\n")
- }
- io.WriteString(w, `