HTTP/3 for everyone (Daniel Stenberg) [FOSDEM 2020]


Daniel worked on curl and now on wolfssl.

HTTP/2 is only from 2013. Now we have HTTP/3.

HTTP/1 (1981) is based on a verb and headers. It began with TCP. It’s a chain of IP packets. It starts with a 3-way handshake. Then you get a reliable byte stream in clear text.

In practice, we don’t use http, we use https. So there’s a TLS layer between. It adds an extra handshake. It gives privacy and security (authentication).

HTTP/1.1 (1997) added the reuse of connection, which helps a lot to remove the handshake. It also allows to do connections in parallel. But this is an ineffective use of TCP. In the end, the connection reuse is almost never used, because all the connections are created in the beginning.

HTTP/2 (2015) uses a single connection per host, and puts parallel streams over a single connection. But now TCP head-of-line blocking becomes the problem, because a lost packet blocks all streams.

The middle-boxes in the Internet become a bottleneck. They are rarely upgraded, they make assumptions that are 20 years old. For example, if you use port 80, middle boxes assume that it’s HTTP/1.1 and mangle it. Changing TCP is even worse, e.g. TCP Fast Open. Trying to introduce a new transport protocol is really impossible, because it can’t be NATed. Even within HTTP you can’t do anything, e.g. brotli compression - middle boxes assume compression == gzip. Therefore, you must encrypt the data to avoid middle boxes messing up.

QUIC W3C working group tries to overcome this. It’s not an acronym, just a name. Similar to HTTP/2, it started at Google in 2013 and came into W3C in 2015. Google can do this because they have a popular browser and popular servers. QUIC WG started in 2016. In the beginning, it was just HTTP/2 frames over UDP. It has changed a lot since then.

QUIC gives the following improvements:

  • fixed head-of-line blocking in TCP and HTTP;
  • faster handshakes;
  • data comes earlier;
  • uses connection ID instead of IP addresses, so transport layer connection stays alive when you change IP address (essential for mobile);
  • it is always encrypted (TLS 1.3), less plaintext remaining than in TCP/TLS.

It uses UDP instead of IP and puts a reliable, encrypted, connection-oriented transport on top of that. QUIC provides streams (multiple within a connection). Both sides can initiate a stream. They can be unidirectional or bidirectional. They are independent, so one stream doesn’t block the others if a packet is lost.

The application protocol on top of QUIC can be anything. The idea was to do both DNS and HTTP over QUIC, to prove that it can be used for multiple applications. But DNS is delayed.

HTTP/3 is basically HTTP over QUIC. It is still a method and path, headers and body. Response still has response code, headers and body. Like HTTP/2, the data is binary (no transport encoding).

HTTP/3 is definitely faster than HTTP/1 and /2. The faster handshakes, the early data, the independent streams (especially for unreliable connections) all help. By how much still needs to be measured.

How can HTTP/3 in practice? We’re not going to change all the existing https URLs. So we will have an Alt-Svc response header that points to an HTTP/3 host/protocol/port. Unfortunately, many gateways (in companies) might block UDP. So probably most browsers will race the connections and open both HTTP/1,2 and HTTP/3 at the same time. There’s an other effort in DNS, to put in a HTTPSSVC that points to the HTTP/3 host/port.

HTTP/3 poses some challenges. UDP blocking affects between 3% and 7% of the clients. So clients will need a fallback. The traffic from HTTP/3 looks like a DDoS attack, so load balancers need to be adapted. It takes 2-3 times the CPU to serve the same amount of useful traffic. This is mainly due to unoptimised UDP stacks, while TCP has been optimised to death. The UDP APIs are pretty crappy for high volume transfers. And there’s no hardware offload, while the TCP/TLS traffic can be easily offloaded.

TLS is made to be sent over TCP. It defines additional framing to be able to put messages in a TCP stream. QUIC is a new standard so it can remove that framing. However, no TLS implementation provides such an API.

All QUIC implementations are currently userland. There is no standard API. So as a QUIC application, you need to marry to a specific library. Maybe it will be moved to the kernel, with a good high-volume API.

New tooling is needed to find back the streams in the QUIC UDP packets. Also qlog and qvis exist.

As of now, HTTP/3 and QUIC are not finalized yet. Maybe this summer it will be released. Meanwhile, over a dozen QUIC and HTTP/3 implementations exist already. They exist in many languages. There are monthly interops to test that the implementations can talk to each other.

It is implemented by curl, chrome, edge and firefox nightlies, nginx with a patch, and wireshark. There is nothing for safari, apache, IIS or official nginx. There’s a PR for OpenSSL to add the non-framing API - that still needs to be released and deployed in servers. Servers running it: facebook and a few cloudflare nodes.

So it will take a bit of time before HTTP/3 is widely deployed. Some will stick to HTTP/2 for a long time.

Websockets will probably not be ported to HTTP/3. Instead, a new webtransport spec will probably be drafted (first versions are there).