📜 ⬆️ ⬇️

HTTP / 2 Server Push to Go 1.8

Translation of a small tutorial on using HTTP / 2 Server Push in the standard Go library.


Introduction


HTTP / 2 was coined to solve many of the HTTP / 1.x problems. Modern web pages use a lot of additional resources - HTML, scripts and style sheets, images, and so on. In HTTP / 1.x, each of these resources must be explicitly requested by a separate request and this can slow down the page load very much. The browser starts with loading HTML, learns about new necessary resources as the page is parsed. As a result, the server waits until the browser requests another resource and the network is just idle and is not being used effectively.


To improve latency, server push support was added to HTTP / 2, which allows the server to send the resources to the browser before they are explicitly requested. Often the server knows in advance what additional resources will be requested by this web page and can begin to send them along with the response to the initial page request. This allows the server to use the network channel as efficiently as possible, which would otherwise have been idle and to improve the page load time.



At the protocol level, HTTP / 2 server push works using a special type of frame - PUSH_PROMISE. The PUSH_PROMISE frame describes a request that, according to the server, will be requested by the browser soon. When receiving PUSH_PROMISE, the browser knows that the server will soon send this resource. There is a little later, the browser will request this resource, then the server will wait for push to end, and not to initiate a new HTTP request. This reduces the total time the browser spends on the network.


Server push in the net / http package


Go 1.8 added support for server push for http.Server . This feature is automatically available if the server is working in HTTP / 2 mode and the incoming connection is also open using the HTTP / 2 protocol. Next thing is in the technology - in any HTTP handler you check if the http.ResponseWriter server push type variable supports a simple push to the http.Pusher interface . .


For example, if the server knows that the app.js script is needed to render the page, the handler can forcefully send it using server push, if http.Pusher is available on this connection:


http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if pusher, ok := w.(http.Pusher); ok { // Push . if err := pusher.Push("/app.js", nil); err != nil { log.Printf("Failed to push: %v", err) } } // ... }) 

The Push method creates a new request to /app.js, assembles it into the PUSH_PROMISE frame and sends a request handler to the server, which will generate the necessary response to the client. The second argument of the method contains additional headers if they are necessary for this frame. For example, if the answer for /app.js should be with a different Accept-Endoding, then the PUSH_PROMISE should contain the Accept-Endoding header:


  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if pusher, ok := w.(http.Pusher); ok { // Push  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) } } // ... }) 

A full working example can be tried here:


 $ go get golang.org/x/blog/content/h2push/server 

If you start this server and log in to http: // localhost: 8080 , the network request inspector in your browser should show that the app.js and style.css have been "started" by the server.



Push the answer first


It makes sense to call the Push method before sending anything in the main response. Otherwise, there is an option to inadvertently generate duplicate responses. For example, imagine that in your handler you are writing back part of the HTML code:


 <html> <head> <link rel="stylesheet" href="a.css">... 

and then call Push ("a.css", nil). But the browser may have already parsed this HTML fragment before it received the PUSH_PROMISE frame, in which case it will send a new request for a.css in addition to the frame. The server should now service the request to a.css twice. Calling Push before giving the response body to the client eliminates this problem.


When to use server push?


The general answer here is when the network connection is idle. Finished sending HTML to a web application? Do not waste time, start giving away the resources that you will need. Perhaps you inline resources directly in HTML to reduce latency? Instead of inline, try pushing. Also a good example is page redirects, which are almost always a superfluous request from a client. There are a lot of different scenarios where server push can be useful - we are just starting to master them.


It would be an omission not to mention the pitfalls. First, using server push, you can only give away the resources that your server owns - that is, you cannot send resources from other sites or CDNs. Second, don’t give up resources if you are not sure that they will be needed by the client, it will be a waste of traffic. As a consequence - to avoid giving away the resources that are most likely already received by the client and coded. And the third - the naive "push all resources" approach usually results in performance degradation. As usual, in case of doubt - take measurements.


Some useful links for more in-depth understanding:


HTTP / 2 Push: The Details
Innovating with HTTP / 2 Server Push
Cache-Aware Server Push in H2O
The PRPL Pattern
Rules of Thumb for HTTP / 2 Push
Server Push in the HTTP / 2 spec


Conclusion


In Go 1.8, the standard library provides HTTP / 2 Server Push functionality out of the box, allowing you to create more efficient and optimized web applications.


You can see HTTP / 2 Server Push in action on this page .


')

Source: https://habr.com/ru/post/325370/


All Articles