In general HTTP/2 is about optimizing server resource usage. This is mainly achieved by using 1 connection between server and client and re-using that connection for all requests/responses for the duration of the session.
This is in sharp contrast with HTTP/1. Where a connection is created, a request is sent, a response is received and the connection is terminated. This is overhead is more or less hidden to the user because multiple connections are used in parallel.
HTTP/2 server push
HTML pages reference many other resources. Using HTTP/1 the client needs to parse the HTML page, identify the referenced resources and fetch them. Every round-trip incurring the connection setup/breakdown overhead.
Using HTTP/2 the server can send these referenced resources to the client before they are needed and because the same connection is used there is no connection setup/breakdown overhead.
Pushing page resources before they are needed will make a site/application more responsive to it’s users. But doing this manually for all pages of an application is only going to be feasible for the smallest of applications.
Web/ UI component frameworks may push framework resources that are needed. Multiple approaches can be taken in this space. All framework resources can be pushed or only the resources that are needed based on the components of the framework that are used.
What is available
Since HTTP/2 is a draft spec, it is still early days for HTTP/2. Currently there is no standard Servlet API but that can’t stop us, Jetty already has an experimental API.
Google Chrome Canary has support for HTTP/2 when started with –enable-spdy4 as start parameter.
Firefox has support for HTTP/2 when the network.http.spdy.enabled.http2draft is switched on.
In order to test server push I’ve taken one of my panoramic vacation photos and sliced it up into 400 parts. This may be a little over the top but as with all tests we want to test the limits.
The test has been executed using 2 web-modules:
- blog-http1-no-push – containing a servlet on URL /nopush that does not perform any pushes.
- blog-http2-push – containing a servlet on URL /push that executes server pushes for the image slices.
The blog-http1-no-push web module was deployed to a Jetty server containing only the http, annotations and deploy modules running on port 8080.
The blog-http2-push web module was deployed to a Jetty server containing only the http2, annotations and deploy modules running on port 8443.
Both these setups are available as Docker images:
- teyckmans/blog-http1-no-push containing the blog-http1-no-push web module
- teyckmans/blog-http2-push containing the blog-http2-push web module
Both web modules contain a single servlet. The servlets take a rows & columns attribute as parameters. This allows us to control the amount of resources that are contained in the generated page. They also control the amount of resources that are pushed by the blog-http2-push web module.
During testing I did notice that the server sometimes becomes unstable when trying to push all 400 image slices. I’ve contacted the jetty users mailing list, perhaps some additional configuration needs to be set when pushing a lot of resources. I’m waiting for their reply.
How do you use HTTP/2 in code
Initially there was a push method on the Dispatcher class, but while writing this blog the Jetty project deprecated that method and made a PushBuilder available via the Request class.
final Request jettyRequest = (Request) getRequest(); jettyRequest .getPushBuilder() .push(resourcePath);
Checkout the sources on Github https://github.com/teyckmans/http2-push
In order to have a correct test case I’ve deployed the Docker images in the Google cloud in the us-central1-a zone in order to have real network overhead. Measurements have been taken with cache disabled in Google Chrome Canary using the load number.
HTTP/1 – no push
3.01s (average of 6)
HTTP/2 – push
1.51s (average of 6)
Pretty spectacular difference if you ask me.
Do It Yourself
I’ve uploaded the Docker images to the docker hub so you can try it out and experience the difference yourself.
Use the following command lines to run the test web-modules.
HTTP/1 – no push
docker pull teyckmans/blog-http1-no-push
docker run –name blog-http1-no-push-1a -i -t -p 8080:8080 teyckmans/blog-http1-no-push
HTTP/2 – push
docker pull teyckmans/blog-http2-push
docker run –name blog-http2-push-1a -i -t -p 8443:8443 teyckmans/blog-http2-push