So good news the HTTP/2 spec was finalised recently, meaning soon the way we talk to web sites will fundamentally change and yet no one will actually notice. Partially based on SPDY Googles attempt to speed up the web, HTTP/2 will start to make the web a lot faster.
The HTTP/2 spec, introduces a few big ticket features including:
- Header compression
- Server push
- Dependencies and prioritisation
While for the average user the change will be seamless for sites and developers it introduces new features and shakes up the way we think about maximising performance. Now the spec is finalised and support for HTTP/2 is appearing in browsers albeit just Firefox and Chrome currently, I want to have a play.
I’m particularly interested in multiplexing and header compression aspects, this should mean that a single handshake can have multiple resources passed back to the client, and that rather then individually requesting the resources to be compressed the content of the entire handshake is compressed based on what the header stated.
What does this mean in practice?
The second aspect that is intriguing is server push, where the server can send a list of other resources to be loaded by the client while the initial page is rendering. For example you could push jQuery library or a font, image that may take some time to load that you don’t want blocking the DOM. While SPDY has had a version of server push implemented, I have never seen any active implementations. Partly because with SPDY if you server push, you are forcing the object onto the client, regardless of if the client already has it in the cache. If implemented incorrectly it could be quite expensive way to load resources. However in certain circumstances I can see this being a powerful tool.
So when HTTP/2 goes mainstream, it should be faster, with better compression and better security then standard HTTP/1 requests.
While there is an experimental module for Apache to enable HTTP/2 support no such module exists for Nginx at this time. Support is coming, but well i’m impatient, so after an hour or so playing with the Apache Module and cursing an awful lot (I had forgotten how frustrating Apache is) I decided that really I wanted a solution for Nginx.
That solution, use a proxy server, to proxy the HTTP/2 traffic to Nginx and then see how many benefits we can get with such a setup.
At this point, you should really just stop for a second and consider the irony of using a proxy server in front of a proxy server, moment over.
NGHTTP2 is a suite of applications comprising of a client, server library and proxy for working with HTTP/2 traffic. My plan to use it’s proxy to sit in front of Nginx and see what sort of interaction we can get, what features we can use.
NGHTTP2 client is also quite handy for testing everything is up and running neatly.
Getting started with Nginx & HGHTTP2
So for this experiment, I fired up a base Ubuntu box on Vagrant, not paying particular attention, rather then loading up a nice Trusty install (Ubuntu 14) I fired up a Precise (12.4) box. Being a stuborn soul I stuck with using the Precise box which with hindsight was a mistake, therefor a lot of the issues I had with setup were caused by not having the right tools for the job.
I’m not going to detail every disaster but before starting make sure you have the following G++ and GCC both version 5+ and you have Python 3.4 and it’s dev tools installed.
So to get the proxy server for NGHTTP2 we need to compile it…
Yep currently no precompiled versions for your favourite linux distros but let’s face it compiling is a 20 second job and not really an issue, I will walk you through it…
Step 1 – Go get a coffee or other beverage this will take more then 5 minutes.
Step 2 – Check you have all the dependencies if you are using Trusty then these can be simply got using sudo apt-get install followed by dependencies you want to install. If using older versions, different distros etc you may have to play hunt the dependency.
To get going you will need the following:
make binutils autoconf automake autotools-dev libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev libjemalloc-dev python3.4-dev
In addition you will need cython, which is probably best installed via pip (which comes bundled with Python 3.4) so assuming you have Python 3.4 up and running: pip install -U cython which will get the latest version.
Next up is that one of the dependencies spdylay from the same author as NGHTTP2 which also needs compiling, assuming we have everything installed above then the good news is there are no more dependencies needed, so go grab the latest stable release from Github
Extract into folder and then to install it’s:
autoreconf -i automake autoconf ./configure make
Now I will be honest, chances are something is going to go wrong at the make stage, it always does! Normally it’s a missing dependency, good news the error message should be pretty verbose, so Googling it should give you which dependency is missing failry quickly. Also make sure you have the most recent versions for dependencies again if using older versions of Ubuntu this will require some tracking down.
However if everything has gone well spdylay should have compiled into the source folder, and
src/spdylay –version should return a version number.
Assuming that’s the case then I suggest installing it globally by running:
sudo make install
This will move spdylay to systemwide accessible location and it’s associated libraries. If you run:
You will now receive most likely an error message, about a missing library libspdylay.so.x where x is a number, not a problem, chances are the library is actually in /usr/local/lib/libspdylay.so however sudo updatedb followed by locate libspdylay.so should locate it if not.
Now spdylay is looking for the file in a different folder so we need to symlink the two locations:
sudo ln -s /usr/local/lib/libspdylay.so /lib/i386-linux-gnu/libspdylay.so.7 sudo ldconfig
rerunning spdylay –version should now return the correct version.
Step 4 – Compiling NGHTTP2
If your journey to this step was as ardous as mine, congratulations, yes you really should have booted a up to date version of Ubuntu to get this running!
The good news if you got spdylay compiled then NGHTTP2 should also compile assuming you have all the dependencies, for me cython was an early version, hence the suggestion to use pip earlier to make sure you have the latest version.
To install nghttp2, grab the latest release from Github, extract into a folder then once again it’s:
autoreconf -i automake autoconf ./configure make
Good news chances are assuming you have the extra Python dependencies sorted that this went far better then the spdlay install, once again we can check that its installed by going src/nghttp –version
Assuming it works, in the same way as spdylay previously we can make it available systemwide by going
sudo make install
And once again we will have to hunt down a stray library
sudo ln -s /usr/local/lib/libnghttp2.so /lib/i386-linux-gnu/libnghttp2.so.5 sudo ldconfig
Finally we can check everything is working by using nghttp2 client mode:
nghttp -nv https://nghttp2.org
Which should return some data.
[ 0.540] Connected
[ 0.815][NPN] server offers:
If you haven’t had to compile something in a while, or if this was your first outing compiling software congratulations you now have a working, NGHTTP2 client, server and proxy. If everything went smoothly and once again, if you are on an up to date distro no reason it shouldn’t this should have taken maybe 20 minutes, for me it was closer to 4 hours.
Setting up Nginx
The setup for Nginx is pretty standard, using the default settings, but switching the listening port to 444, and adding a second location that listens on port 80 and redirects traffic to the https:// end point.
So in my little example server, all traffic should be coming over https to port 443 which is where we will have NGHTTP2 listening, which will then proxy our requests to Nginx which is listening internally on port 444.
Setting up certificates for NGHTTP2
While it’s possible to run HTTP/2 without encryption it wasn’t intend for that to be the case the original SPDY implementation, had to use TLS to provide a way to sideload the standard in a way that wouldn’t break the web, but it was also a philosophical choice. However the lack of consensus (read lobbying, from the sort of folks you would expect) means while possible most implementations are expecting encryption. For my experiment I simply generated self signed certificates, needless to say if this project was on anything but a vagrant box, then a self signed certificate is probably not the way to go.
This was as simple as:
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ~/certs/self.key -out ~/certs/self.crt
note this was just to get things running.
Running NGHTTP/2 Proxy for Nginx
After all the hoop jumping actually setting up the proxy was remarkably easy, having checked Nginx was listening on then to run the proxy is simply:
sudo nghttpx -f*,443 -blocalhost,444 ~/certs/self.key ~/certs/self.crt
- -f indicates what to listen to on the front end
- -b indicates where to send the traffic
- Then provide the locations of your private key and certificate
um and that’s well that.
nghttp -nv https://192.168.50.4
[ 0.003] Connected
[ 0.019][NPN] server offers:
If I visit in Chrome it defaults to using spdy/3.1 connection, in Safari http/1.1
Getting the server to push
Getting Server Push going with NGHTTP2 is incredibly simple for any given resource you wish to push, simply include them in the header request so for example in Nginx:
This pushes the file example.js from the /assets/ directory, on each page load in the same way SPDY does so if we were to push a 1gb video file then you will probably have some very upset browsers.
When it comes to actually pushing resources, I suspect it’s less likely Nginx setting the header, but rather then backend application.
A small word of warning currently NGHTTP2 only supports relative assets being loaded, this is a limitation of the proxy and not the specification.
Where to go next?
So there we have it Nginx being accessed via HTTP/2 over a proxy with NGHTTP2, now it’s time to see what interesting things we can do?
Have you played with HTTP/2? I shall be documenting my adventures with my little test box, in a few follow up posts but would love to hear your experiences?