神刀安全网

A kick-start into server push

I bet you heard about server push in the last few months. Every HTTP/2 article mentions it, but do you really know what it’s all about? The intention of this article is to give you a kick-start into the topic.

Why do we need server push?

To answer that question we have to look at how HTTP/1.x works. The “internet” consists of resources which are linked together. Take your standard web-page, it consists of a HTML file, various stylesheets, scripts, images and other assets.

<!DOCTYPE html> <html> <head>     <link rel="stylesheet" href="assets/css/app.css" type="text/css"> </head> <body>     <script src="assets/js/app.js"></script>     <script src="assets/js/index.js"></script> </body> </html> 

When we request a website your browser first loads a HTML file, looks for all assets inside it, and requests each asset on its own. Every asset can then require other assets on its own. Think about background images in your stylesheets or scripts loaded with AJAX.

A kick-start into server push

You clearly see that the browser needs to download the HTML file before the other assets. There’s also a lot of time spent connecting to the single resources.

With HTTP/2 and its multiplexed connections the browser doesn’t have to make multiple connections. It keeps the first connection open and downloads all assets from this domain over it.

A kick-start into server push

In reality it does a lot more but for the scope of the article this should do. We now save a lot of overhead, especially when we have many assets. With this feature we eliminate common hacks like script/style concatenation, spriting and domain sharding at protocol level. Pretty rad.

There are still big time steps in the waterfall. We know which files are required by our HTML file so why can’t we send them at once? We already do this. It’s called inlining.

Whats the problem with inlining?

Nowadays you see developers inlining every kind of asset. While there are some more or less useful strategies like “ Critical above the fold CSS ”, there are also really cruel ones like base64 images inside css files.

A kick-start into server push

In the end you will do this to avoid requests and get everything you need in one single request. This may look nice, but also introduces some new problems.

  • Browsers are really good at caching. If assets are inlined, they don’t get cached.
  • What files should we inline? Which part of the css? Voila, a new layer of complexity
  • base64 encoded images are way bigger than their source file.

HTTP/2 server push to the rescue

Server push allows a server to send request+response pairs to the browser. If the browser requests /index.html , and the server knows that /index.html contains a reference to /app.css and /app.js the server now can push both files rather than waiting for the browser to request it.

Hey browser, I think you’ll need app.css and app.js along with your requested index.html, so I’ll send them too.

A kick-start into server push

Compared to the inlining example we now can download all assets parallel, while delivering separate files which can be cached by the browser.

Push promise

To push a response to the browser the server opens a stream using a so called PUSH_PROMISE frame, which let the browser know exactly which resources are getting pushed. A PUSH_PROMISE is associated with a normal request, so that the browser knows which push belongs to which request.

Canceling of push streams

If a browser already has a resource cached he can send a RST_STREAM frame to tell the server he wants to cancel an incoming push. The RST_STREAM frame immediately closes a stream.

Be careful here. The server doesn’t know which files are already cached by the browser. Every pushed resource is a GET and therefore cacheable, this could lead to a lot of overhead if you push every asset on every request. Even if the browser sends a RST_STREAM frame.

Which files to push?

For me, this is the most complicated part. Can we push everything? As mentioned earlier this would lead to a waste of bandwidth and server resources. So choose wisely what files you push. I found two different ways to accomplish this.

Analyzing requested files

When a file is requests, we can search for requested assets and push them. A HTML file for example has <script> , <link> , <img> and other tags which point to other resources.

  • relatively simple to implement
  • but it doesn’t really make sense to push everything
  • it’s also hard to find indirect dependencies like background images defined in css.

Static push list

Instead of analyzing, we could provide a list of resources which we want to push. So wenn a file is request, we look into our list if we have something to push. Such a list could look something like this:

{     "index.html" : [         "app.css",         "app.js",         "bg.jpg"     ],     "contact.html" : [         "app.css",         "contact.js",     ] }     

  • free control over your pushes
  • harder to maintain

http2-push-manifest does a really good job in generating such lists from HTML files.

Implementations

Apache

The experimental mod_http2 apache module introduces the H2Push Directive which toggles the usage of the HTTP/2 server push protocol feature and is used in your section.

You have to write all your pushes as Link entries inside the response headers .

<Location /index.html>     Header add Link "</assets/css/app.css>;rel=preload"     Header add Link "</assets/js/app.js>;rel=preload" </Location> 

Node

node-http2 is a HTTP/2 implementation with a similar API to the standard node.js HTTP extension.

function onRequest(request, response) {     var filename = path.join(__dirname, request.url);          if(response.push){         var fileToPush = '/assets/css/app.css';                  //Get a push object from the response         var push = response.push(fileToPush);                  //Write HTTP status code for the push         push.writeHead(200);                  //Create read stream and pipe to the push object         fs.createReadStream(path.join(__dirname, fileToPush))         .pipe(push);     }          response.writeHead(200);     var fileStream = fs.createReadStream(filename);     fileStream.pipe(response);     fileStream.on('finish', response.end); };         

They also have examples .

Push Manifest

http2-push-manifest generates a list of static resources used in your HTML files by outputting a json file. This file can be used to create theheaders for apache. You can even use this in App Engine.

The usage is as simple as:

$ http2-push-manifest -f index.html 

which generates:

{   "/assets/css/app.css": {     "type": "style",     "weight": 1   },   "assets/js/app.js": {     "type": "script",     "weight": 1   } } 

You can use the json for you custom push implementation or even on App Engine.

NGINX

There is a experimental HTTP/2 Module but without an implementation of server push. The same goes for NGINX plus. I’ll keep you updated on this.

Go

Server push is possible with the official HTTP/2 package . I’ll provide an example in the future.

Debugging Server Push

When you first implement server push you have to check if it works. Unfortunately there isn’t something like a dedicated “pushed resources” button in your dev-tools. If you go to the timing tab in chrome dev-tools of a resource you’ll see that it has almost no TTFB and the download time is way lower (altough ‘Content Download’ sounds misleading here).

A kick-start into server push

Compared to a normal http request:

A kick-start into server push

If you really need to see whats going on you can peak into chrome://net-internals/#events

Search for PUSH_PROMISE and you’ll see your pushed assets.

A kick-start into server push

Browser Support

Every browser that supports HTTP/2 also supports server push. But they differ in how to handle prioritization with weight and priority.

Can I Use http2? Data on support for the http2 feature across the major browsers from caniuse.com.

TL;DR

At the moment of writing there’s only an experimental implementation of server push for apache and no implementation for NGINX. If you really want to push, you can start with node-http2 or HTTP/2 for Go .

Keep in mind that it can be much overhead if you push a lot of files. Especially if those files are already cached by the browser.

Further reading

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » A kick-start into server push

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮