From fa33df21410c950dc4bca7a2e33955dba7d7e455 Mon Sep 17 00:00:00 2001 From: Jaana Burcu Dogan Date: Fri, 24 Mar 2017 11:16:09 -0700 Subject: content: add HTTP/2 Server Push article Change-Id: Ibf1214b54dee3a3419dba26367b4f0189e5e4a6d Reviewed-on: https://go-review.googlesource.com/38603 Reviewed-by: Tom Bergan --- content/h2push.article | 123 +++++++++++++++++++++++++++++++++++++ content/h2push/networktimeline.png | Bin 0 -> 37238 bytes content/h2push/pusher.go | 39 ++++++++++++ content/h2push/serverpush.svg | 4 ++ 4 files changed, 166 insertions(+) create mode 100644 content/h2push.article create mode 100644 content/h2push/networktimeline.png create mode 100644 content/h2push/pusher.go create mode 100644 content/h2push/serverpush.svg diff --git a/content/h2push.article b/content/h2push.article new file mode 100644 index 0000000..9079f8d --- /dev/null +++ b/content/h2push.article @@ -0,0 +1,123 @@ +HTTP/2 Server Push +24 Mar 2017 +Tags: http, technical + +Jaana Burcu Dogan, Tom Bergan + +* Introduction + +HTTP/2 is designed to address many of the failings of HTTP/1.x. +Modern web pages use many resources: HTML, stylesheets, +scripts, images, and so on. In HTTP/1.x, each of these resources must +be requested explicitly. This can be a slow process. +The browser starts by fetching the HTML, then learns of more resources +incrementally as it parses and evaluates the page. Since the server +must wait for the browser to make each request, the network is often +idle and underutilized. + +To improve latency, HTTP/2 introduced _server_push_, which allows the +server to push resources to the browser before they are explicitly +requested. A server often knows many of the additional resources a +page will need and can start pushing those resources as it responds +to the initial request. This allows the server to fully utilize an +otherwise idle network and improve page load times. + +.image h2push/serverpush.svg _ 600 + +At the protocol level, HTTP/2 server push is driven by `PUSH_PROMISE` +frames. A `PUSH_PROMISE` describes a request that the server predicts the +browser will make in the near future. As soon as the browser receives +a `PUSH_PROMISE`, it knows that the server will deliver the resource. +If the browser later discovers that it needs this resource, it will +wait for the push to complete rather than sending a new request. +This reduces the time the browser spends waiting on the network. + +* Server Push in net/http + +Go 1.8 introduced support for pushing responses from an [[https://golang.org/pkg/net/http/#Server][`http.Server`]]. +This feature is available if the running server is an HTTP/2 server +and the incoming connection uses HTTP/2. In any HTTP handler, +you can assert if the http.ResponseWriter supports server push by checking +if it implements the new [[https://golang.org/pkg/net/http/#Pusher][`http.Pusher`]] interface. + +For example, if the server knows that `app.js` will be required to +render the page, the handler can initiate a push if `http.Pusher` +is available: + +.code h2push/pusher.go /START/,/END/ + +The Push call creates a synthetic request for `/app.js`, +synthesizes that request into a `PUSH_PROMISE` frame, then forwards +the synthetic request to the server's request handler, which will +generate the pushed response. The second argument to Push specifies +additional headers to include in the `PUSH_PROMISE`. For example, +if the response to `/app.js` varies on Accept-Encoding, +then the `PUSH_PROMISE` should include an Accept-Encoding value: + +.code h2push/pusher.go /START1/,/END1/ + +A fully working example is available at: + + $ go get golang.org/x/blog/content/h2push/server + + +If you run the server and load [[https://localhost:8080][https://localhost:8080]], +your browser's developer tools should show that `app.js` and +`style.css` were pushed by the server. + +.image h2push/networktimeline.png _ 605 + +* Start Your Pushes Before You Respond + +It's a good idea to call the Push method before sending any bytes +of the response. Otherwise it is possible to accidentally generate +duplicate responses. For example, suppose you write part of an HTML +response: + + + + + ... + + +Then you call Push("a.css", nil). The browser may parse this fragment +of HTML before it receives your PUSH_PROMISE, in which case the browser +will send a request for `a.css` in addition to receiving your +`PUSH_PROMISE`. Now the server will generate two responses for `a.css`. +Calling Push before writing the response avoids this possibility entirely. + +* When To Use Server Push + +Consider using server push any time your network link is idle. +Just finished sending the HTML for your web app? Don't waste time waiting, +start pushing the resources your client will need. Are you inlining +resources into your HTML file to reduce latency? Instead of inlining, +try pushing. Redirects are another good time to use push because there +is almost always a wasted round trip while the client follows the redirect. +There are many possible scenarios for using push -- we are only getting started. + +We would be remiss if we did not mention a few caveats. First, you can only +push resources your server is authoritative for -- this means you cannot +push resources that are hosted on third-party servers or CDNs. Second, +don't push resources unless you are confident they are actually needed +by the client, otherwise your push wastes bandwidth. A corollary is to +avoid pushing resources when it's likely that the client already has +those resources cached. Third, the naive approach of pushing all +resources on your page often makes performance worse. When in doubt, measure. + +The following links make for good supplemental reading: + +- [[https://calendar.perfplanet.com/2016/http2-push-the-details/][HTTP/2 Push: The Details]] +- [[https://www.igvita.com/2013/06/12/innovating-with-http-2.0-server-push/][Innovating with HTTP/2 Server Push]] +- [[https://github.com/h2o/h2o/issues/421][Cache-Aware Server Push in H2O]] +- [[https://developers.google.com/web/fundamentals/performance/prpl-pattern/][The PRPL Pattern]] +- [[https://docs.google.com/document/d/1K0NykTXBbbbTlv60t5MyJvXjqKGsCVNYHyLEXIxYMv0][Rules of Thumb for HTTP/2 Push]] +- [[https://tools.ietf.org/html/rfc7540#section-8.2][Server Push in the HTTP/2 spec]] + +* Conclusion + +With Go 1.8, the standard library provides out-of-the-box support for HTTP/2 +Server Push, giving you more flexibility to optimize your web applications. + +Go to our [[https://http2.golang.org/serverpush][HTTP/2 Server Push demo]] +page to see it in action. diff --git a/content/h2push/networktimeline.png b/content/h2push/networktimeline.png new file mode 100644 index 0000000..7fb5a1d Binary files /dev/null and b/content/h2push/networktimeline.png differ diff --git a/content/h2push/pusher.go b/content/h2push/pusher.go new file mode 100644 index 0000000..366328c --- /dev/null +++ b/content/h2push/pusher.go @@ -0,0 +1,39 @@ +// +build OMIT + +package main + +import ( + "log" + "net/http" +) + +func main() { + // START OMIT + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if pusher, ok := w.(http.Pusher); ok { + // Push is supported. + if err := pusher.Push("/app.js", nil); err != nil { + log.Printf("Failed to push: %v", err) + } + } + // ... + }) + // END OMIT + + // START1 OMIT + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if pusher, ok := w.(http.Pusher); ok { + // Push is supported. + options := &http.PushOptions{ + Header: http.Header{ + "Accept-Encoding": r.Header["Accept-Encoding"], + }, + } + if err := pusher.Push("/app.js", options); err != nil { + log.Printf("Failed to push: %v", err) + } + } + // ... + }) + // END1 OMIT +} diff --git a/content/h2push/serverpush.svg b/content/h2push/serverpush.svg new file mode 100644 index 0000000..aaa9961 --- /dev/null +++ b/content/h2push/serverpush.svg @@ -0,0 +1,4 @@ + + + + -- cgit v1.2.3