HTTP/2 is a new major revision of HTTP protocol. It's designed for low-latency transport of content over the web. In this article I will show you how to set up a sample web application (with Jetty), how to analyze HTTP/2 traffic (with TLS decryption) and how to simulate high latency to see the difference between HTTP/1.1 and HTTP/2 in practice. I'm not going to explain the rationale behind HTTP/2 here, so if you're not familiar with it, I suggest reading RFC 7540 and/or "High Performance Browser Networking" by Ilya Grigorik.
Setting up Jetty
We will use Jetty 9.3 which supports HTTP/2 by default. Setting it up with the sample web app is as easy as:
# download and unpack jetty: wget http://repo1.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.3.10.v20160621/jetty-distribution-9.3.10.v20160621.tar.gz tar -xzf jetty-distribution-9.3.10.v20160621.tar.gz export JETTY_HOME=$(pwd)/jetty-distribution-9.3.10.v20160621 # prepare demo dir mkdir demo cd demo java -jar $JETTY_HOME/start.jar \ --add-to-startd=http,https,http2,http2c,deploy # enable servlets module required for smart push (more info below) echo "--module=servlets" >> start.d/server.ini # download demo application wget http://kaczmarzyk.net/blog/2016/06/ROOT.war -O webapps/ROOT.war # start Jetty java -jar $JETTY_HOME/start.jar
The demo page contains a picture which is built out of 220 small images. You can access it with HTTP/1.1 on http://localhost:8080/ or with HTTP/2 over TLS on https://localhost:8443/ (you will need a modern browser and have to accept a self-signed certificate).
Even with the minimal latency of localhost, the difference in page loading might be clearly visible. Later on I will show you how to simulate higher latency to make it even more explicit.
Capturing and analyzing traffic
We will now capture traffic with tcpdump and then analyze it with Wireshark (the latter could be used for both of these tasks, but it is easier to paste a tcpdump command here). Capturing traffic on
lo interface and port
8443 is as easy as:
tcpdump -i lo -s0 -XX -w /tmp/tcpdump.out port 8443
But since HTTP/2 traffic is over TLS, we need a way to decrypt it. To achieve that, we need the access to browser TLS keys. You can use
SSLKEYLOGFILE environment variable to tell Firefox/Chrome to which file to log these keys to:
export SSLKEYLOGFILE=/tmp/keylog.txt firefox https://localhost:8443
After loading the page, you can stop tcpdump with
CTRL+C. Then you are ready to analyze the captured packets with Wireshark. I used Wireshark 2.0.4. Older version that was default for my Linux Mint had some problems with the decryption.
First of all, you need to tell Wireshark to interpret traffic on port 8443 as HTTP over TLS. Go to
Edit->Preferences->Protocols->HTTP and then add 8443 to "SSL/TLS Ports" section.
File->Open and then select the
/tmp/tcpdump.out file. To decrypt the TLS session, choose
Edit->Preferences->Protocols->SSL and set "(Pre)-Master-Secret log filename" to
After that, you should be able to see HTTP/2 streams exchanged between the browser and the server:
Jetty's "smart push"
The sample web application has Jetty's "smart push" enabled. Jetty analyzes incoming requests and builds asset dependency tree based on Referer HTTP headers. On subsequent page loads, it uses HTTP/2 PUSH_PROMISE to send additional resources such as CSS styles or images even before the browser asks for them.
It requires servlets module to be enabled (we have done it during Jetty setup). Once done, enabling this feature is as easy as adding the following filter definition to
<filter> <filter-name>PushFilter</filter-name> <filter-class>org.eclipse.jetty.servlets.PushCacheFilter</filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>PushFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
You can remove it to see how it affects the page load.
To make the difference between HTTP/1.1 and HTTP/2 even more visible, you can simulate high latency on
lo interface. It is easy to set up with
tc command (available on most of Linux distributions):
sudo tc qdisc add dev lo root handle 1:0 netem delay 400ms
The command above adds 400ms latency. With such setting, the page load on my machine was ~4s for HTTP/2 and ~30s for HTTP/1.1. You can play with
tc to see that the difference is even larger for higher latency values.
When you are done with the experiments, you can disable the delay with the following command:
sudo tc qdisc del dev lo root
I believe that HTTP/2 is a game changer for the web development. However, I think that it's always essential to understand your application environment (e.g. the expected latency) and to test the features to fully understand them. In this short article I showed you some basic tools to run your own benchmarks. I hope you will find it helpful!