Introduction

HTTP/3 is the next iteration of the HTTP protocol.

HTTP/1.0 was released in 1996 and HTTP/1.1 in 1997; HTTP/1.x is a fairly simple textual protocol based on TCP, possibly wrapped in TLS, that experienced over the years a tremendous growth that was not anticipated in the late ’90s.
With the growth, a few issues in the HTTP/1.x scalability were identified, and addressed first by the SPDY protocol (HTTP/2 precursor) and then by HTTP/2.

The design of HTTP/2, released in 2015 (and also based on TCP), resolved many of the HTTP/1.x shortcomings and  protocol became binary and multiplexed.

The deployment at large of HTTP/2 revealed some issues in the HTTP/2 protocol itself, mainly due a shift towards mobile devices where connectivity is less reliable and packet loss more frequent.

Enter HTTP/3, which ditches TCP for QUIC (RFC 9000) to address the connectivity issues of HTTP/2.
HTTP/3 and QUIC are inextricably entangled together because HTTP/3 relies heavily on QUIC features that are not provided by any other lower-level protocol.

QUIC is based on UDP (rather than TCP) and has TLS built-in, rather than layered on top.
This means that you cannot offload TLS in a front-end server, like with HTTP/1.x and HTTP/2, and then forward the clear-text HTTP/x bytes to back-end servers.

Due to HTTP/3 relying heavily on QUIC features, it’s not possible anymore to separate the “carrier” protocol (QUIC) from the “semantic” protocol (HTTP). Therefor reverse proxying should either:

  • decrypt QUIC+HTTP/3, perform some proxy processing, and re-encrypt QUIC+HTTP/3 to forward to back-end servers; or
  • decrypt QUIC+HTTP/3, perform some proxy processing, and re-encode into a different protocol such as HTTP/2 or HTTP/1.x to forward to back-end servers, with the risk of losing features by using older HTTP protocol versions.

The Jetty Project has always been on the front at implementing Web protocols and standard, and QUIC+HTTP/3 is no exception.

Jetty’s HTTP/3 Support

At this time, Jetty’s support for HTTP/3 is still experimental and not recommended for production use.

We decided to use the Cloudflare’s Quiche library because QUIC’s use of TLS requires new APIs that are not available in OpenJDK; we could not implement QUIC in pure Java.

We wrapped the native calls to Quiche with either JNA or with Java 17’s Foreign APIs (JEP 412) and retrofitted the existing Jetty’s I/O library to work with UDP as well.
A nice side effect of this work is that now Jetty is a truly generic network server, as it can be used to implement any generic protocol (not just web protocols) on either TCP or UDP.

HTTP/3 was implemented in Jetty 10.0.8/11.0.8 for both the client and the server.
The implementation is quite similar to Jetty’s HTTP/2 implementation, since the protocols are quite similar as well.

HTTP/3 on the client is available in two forms:

  • Using the high-level APIs provided by Jetty’s HttpClient with the HTTP/3 specific transport (that only speaks HTTP/3), or with the dynamic transport (that can speak multiple protocols).
  • Using the low-level HTTP/3 APIs provided by Jetty’s HTTP3Client that allow you to deal directly with HTTP/3 sessions, streams and frames.

HTTP/3 on the server is available in two forms:

  • Using embedded code via HTTP3ServerConnector listening on a specific network port.
  • Using Jetty as a standalone server by enabling the http3 Jetty module.

In both cases, an incoming HTTP/3 request is processed and forwarded to your standard Web Applications, or to your Jetty Handlers.

Finally, the HTTP/3 specification at the IETF is still a draft and may change, and we prioritized a working implementation over performance.