You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

487 lines
14 KiB

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