h2demo.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. // Copyright 2014 The Go Authors.
  2. // See https://code.google.com/p/go/source/browse/CONTRIBUTORS
  3. // Licensed under the same terms as Go itself:
  4. // https://code.google.com/p/go/source/browse/LICENSE
  5. package main
  6. import (
  7. "bytes"
  8. "crypto/tls"
  9. "flag"
  10. "fmt"
  11. "hash/crc32"
  12. "io"
  13. "io/ioutil"
  14. "log"
  15. "net"
  16. "net/http"
  17. "os/exec"
  18. "path"
  19. "regexp"
  20. "runtime"
  21. "strings"
  22. "sync"
  23. "time"
  24. "camlistore.org/pkg/googlestorage"
  25. "camlistore.org/pkg/singleflight"
  26. "github.com/bradfitz/http2"
  27. )
  28. var (
  29. openFirefox = flag.Bool("openff", false, "Open Firefox")
  30. prod = flag.Bool("prod", false, "Whether to configure itself to be the production http2.golang.org server.")
  31. )
  32. func homeOldHTTP(w http.ResponseWriter, r *http.Request) {
  33. io.WriteString(w, `<html>
  34. <body>
  35. <h1>Go + HTTP/2</h1>
  36. <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>
  37. <p>Unfortunately, you're <b>not</b> using HTTP/2 right now.</p>
  38. <p>See code & instructions for connecting at <a href="https://github.com/bradfitz/http2">https://github.com/bradfitz/http2</a>.</p>
  39. </body></html>`)
  40. }
  41. func home(w http.ResponseWriter, r *http.Request) {
  42. io.WriteString(w, `<html>
  43. <body>
  44. <h1>Go + HTTP/2</h1>
  45. <p>Welcome to <a href="https://golang.org/">the Go language</a>'s <a
  46. href="https://http2.github.io/">HTTP/2</a> demo & interop server.</p>
  47. <p>Congratulations, <b>you're using HTTP/2 right now</b>.</p>
  48. <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>
  49. <p> The code is currently at <a
  50. href="https://github.com/bradfitz/http2">github.com/bradfitz/http2</a>
  51. but will move to the Go standard library at some point in the future
  52. (enabled by default, without users needing to change their code).</p>
  53. <p>Contact info: <i>bradfitz@golang.org</i>, or <a
  54. href="https://github.com/bradfitz/http2/issues">file a bug</a>.</p>
  55. <h2>Handlers for testing</h2>
  56. <ul>
  57. <li>GET <a href="/reqinfo">/reqinfo</a> to dump the request + headers received</li>
  58. <li>GET <a href="/clockstream">/clockstream</a> streams the current time every second</li>
  59. <li>GET <a href="/file/gopher.png">/file/gopher.png</a> for a small file (does If-Modified-Since, Content-Range, etc)</li>
  60. <li>GET <a href="/file/go.src.tar.gz">/file/go.src.tar.gz</a> for a larger file (~10 MB)</li>
  61. <li>GET <a href="/redirect">/redirect</a> to redirect back to / (this page)</li>
  62. <li>GET <a href="/goroutines">/goroutines</a> to see all active goroutines in this server</li>
  63. <li>PUT something to <a href="/crc32">/crc32</a> to get a count of number of bytes and its CRC-32</li>
  64. </ul>
  65. </body></html>`)
  66. }
  67. func reqInfoHandler(w http.ResponseWriter, r *http.Request) {
  68. w.Header().Set("Content-Type", "text/plain")
  69. fmt.Fprintf(w, "Method: %s\n", r.Method)
  70. fmt.Fprintf(w, "Protocol: %s\n", r.Proto)
  71. fmt.Fprintf(w, "Host: %s\n", r.Host)
  72. fmt.Fprintf(w, "RemoteAddr: %s\n", r.RemoteAddr)
  73. fmt.Fprintf(w, "RequestURI: %q\n", r.RequestURI)
  74. fmt.Fprintf(w, "URL: %#v\n", r.URL)
  75. fmt.Fprintf(w, "Body.ContentLength: %d (-1 means unknown)\n", r.ContentLength)
  76. fmt.Fprintf(w, "Close: %v (relevant for HTTP/1 only)\n", r.Close)
  77. fmt.Fprintf(w, "TLS: %#v\n", r.TLS)
  78. fmt.Fprintf(w, "\nHeaders:\n")
  79. r.Header.Write(w)
  80. }
  81. func crcHandler(w http.ResponseWriter, r *http.Request) {
  82. if r.Method != "PUT" {
  83. http.Error(w, "PUT required.", 400)
  84. return
  85. }
  86. crc := crc32.NewIEEE()
  87. n, err := io.Copy(crc, r.Body)
  88. if err == nil {
  89. w.Header().Set("Content-Type", "text/plain")
  90. fmt.Fprintf(w, "bytes=%d, CRC32=%x", n, crc.Sum(nil))
  91. }
  92. }
  93. var (
  94. fsGrp singleflight.Group
  95. fsMu sync.Mutex // guards fsCache
  96. fsCache = map[string]http.Handler{}
  97. )
  98. // fileServer returns a file-serving handler that proxies URL.
  99. // It lazily fetches URL on the first access and caches its contents forever.
  100. func fileServer(url string) http.Handler {
  101. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  102. hi, err := fsGrp.Do(url, func() (interface{}, error) {
  103. fsMu.Lock()
  104. if h, ok := fsCache[url]; ok {
  105. fsMu.Unlock()
  106. return h, nil
  107. }
  108. fsMu.Unlock()
  109. res, err := http.Get(url)
  110. if err != nil {
  111. return nil, err
  112. }
  113. defer res.Body.Close()
  114. slurp, err := ioutil.ReadAll(res.Body)
  115. if err != nil {
  116. return nil, err
  117. }
  118. modTime := time.Now()
  119. var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  120. http.ServeContent(w, r, path.Base(url), modTime, bytes.NewReader(slurp))
  121. })
  122. fsMu.Lock()
  123. fsCache[url] = h
  124. fsMu.Unlock()
  125. return h, nil
  126. })
  127. if err != nil {
  128. http.Error(w, err.Error(), 500)
  129. return
  130. }
  131. hi.(http.Handler).ServeHTTP(w, r)
  132. })
  133. }
  134. func clockStreamHandler(w http.ResponseWriter, r *http.Request) {
  135. clientGone := w.(http.CloseNotifier).CloseNotify()
  136. w.Header().Set("Content-Type", "text/plain")
  137. ticker := time.NewTicker(1 * time.Second)
  138. defer ticker.Stop()
  139. fmt.Fprintf(w, "# ~1KB of junk to force browsers to start rendering immediately: \n")
  140. io.WriteString(w, strings.Repeat("# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", 13))
  141. for {
  142. fmt.Fprintf(w, "%v\n", time.Now())
  143. w.(http.Flusher).Flush()
  144. select {
  145. case <-ticker.C:
  146. case <-clientGone:
  147. log.Printf("Client %v disconnected from the clock", r.RemoteAddr)
  148. return
  149. }
  150. }
  151. }
  152. func registerHandlers() {
  153. mux2 := http.NewServeMux()
  154. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  155. if r.TLS == nil {
  156. http.Redirect(w, r, "https://http2.golang.org/", http.StatusFound)
  157. return
  158. }
  159. if r.ProtoMajor == 1 {
  160. if r.URL.Path == "/reqinfo" {
  161. reqInfoHandler(w, r)
  162. } else {
  163. homeOldHTTP(w, r)
  164. }
  165. return
  166. }
  167. mux2.ServeHTTP(w, r)
  168. })
  169. mux2.HandleFunc("/", home)
  170. mux2.Handle("/file/gopher.png", fileServer("https://golang.org/doc/gopher/frontpage.png"))
  171. mux2.Handle("/file/go.src.tar.gz", fileServer("https://storage.googleapis.com/golang/go1.4rc1.src.tar.gz"))
  172. mux2.HandleFunc("/reqinfo", reqInfoHandler)
  173. mux2.HandleFunc("/crc32", crcHandler)
  174. mux2.HandleFunc("/clockstream", clockStreamHandler)
  175. mux2.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
  176. http.Redirect(w, r, "/", http.StatusFound)
  177. })
  178. stripHomedir := regexp.MustCompile(`/(Users|home)/\w+`)
  179. mux2.HandleFunc("/goroutines", func(w http.ResponseWriter, r *http.Request) {
  180. w.Header().Set("Content-Type", "text/plain; charset=utf-8")
  181. buf := make([]byte, 2<<20)
  182. w.Write(stripHomedir.ReplaceAll(buf[:runtime.Stack(buf, true)], nil))
  183. })
  184. }
  185. func serveProdTLS() error {
  186. c, err := googlestorage.NewServiceClient()
  187. if err != nil {
  188. return err
  189. }
  190. slurp := func(key string) ([]byte, error) {
  191. const bucket = "http2-demo-server-tls"
  192. rc, _, err := c.GetObject(&googlestorage.Object{
  193. Bucket: bucket,
  194. Key: key,
  195. })
  196. if err != nil {
  197. return nil, fmt.Errorf("Error fetching GCS object %q in bucket %q: %v", key, bucket, err)
  198. }
  199. defer rc.Close()
  200. return ioutil.ReadAll(rc)
  201. }
  202. certPem, err := slurp("http2.golang.org.chained.pem")
  203. if err != nil {
  204. return err
  205. }
  206. keyPem, err := slurp("http2.golang.org.key")
  207. if err != nil {
  208. return err
  209. }
  210. cert, err := tls.X509KeyPair(certPem, keyPem)
  211. if err != nil {
  212. return err
  213. }
  214. srv := &http.Server{
  215. TLSConfig: &tls.Config{
  216. Certificates: []tls.Certificate{cert},
  217. },
  218. }
  219. http2.ConfigureServer(srv, &http2.Server{})
  220. ln, err := net.Listen("tcp", ":443")
  221. if err != nil {
  222. return err
  223. }
  224. return srv.Serve(tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig))
  225. }
  226. type tcpKeepAliveListener struct {
  227. *net.TCPListener
  228. }
  229. func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
  230. tc, err := ln.AcceptTCP()
  231. if err != nil {
  232. return
  233. }
  234. tc.SetKeepAlive(true)
  235. tc.SetKeepAlivePeriod(3 * time.Minute)
  236. return tc, nil
  237. }
  238. func serveProd() error {
  239. errc := make(chan error, 2)
  240. go func() { errc <- http.ListenAndServe(":80", nil) }()
  241. go func() { errc <- serveProdTLS() }()
  242. return <-errc
  243. }
  244. func main() {
  245. var srv http.Server
  246. flag.BoolVar(&http2.VerboseLogs, "verbose", false, "Verbose HTTP/2 debugging.")
  247. flag.StringVar(&srv.Addr, "addr", "localhost:4430", "host:port to listen on ")
  248. flag.Parse()
  249. registerHandlers()
  250. if *prod {
  251. log.Fatal(serveProd())
  252. }
  253. url := "https://" + srv.Addr + "/"
  254. log.Printf("Listening on " + url)
  255. http2.ConfigureServer(&srv, &http2.Server{})
  256. go func() {
  257. log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key"))
  258. }()
  259. if *openFirefox && runtime.GOOS == "darwin" {
  260. time.Sleep(250 * time.Millisecond)
  261. exec.Command("open", "-b", "org.mozilla.nightly", "https://localhost:4430/").Run()
  262. }
  263. select {}
  264. }