Summary
Update the HTTP Client to support the HTTP/3 protocol.
Goals
Update the implementation of the HttpClient to send and receive HTTP/3 requests and responses.
Require only minor changes to the HttpClient API and to application code.
Non Goals
It is not a goal to change the default version from HTTP/2 to HTTP/3: use of HTTP/3 is an opt-in (see risk and assumptions below).
It is not a goal to provide an API for the underlying QUIC protocol on which HTTP/3 is based.
It is not a goal to provide an API for the Transport Layer Security (TLS) implementation changes that might be required to implement QUIC-TLS
It is not a goal to provide a server-side implementation of the HTTP/3 protocol, though one may be developed for testing purposes
Motivation
A modern HTTP Client was added to the Java Platform in Java 11 via JEP 321. The Client API is protocol agnostic and currently supports versions HTTP/1.1 and HTTP/2 of the HTTP protocol. It is designed to support future HTTP protocol versions with minimal API changes.
Here is an example using the HTTP Client, which sends a GET request to https://openjdk.org/ and receives the response as a string:
var httpClient = ... // e.g. HttpClient.newBuilder().proxy(...).build(); var request = HttpRequest.newBuilder(URI.create("https://openjdk.org/")).GET().build(); var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); assert response.statusCode() == 200; String htmlText = response.body();
In this example there is nothing explicit in the use of the API that depends on the HTTP protocol version. The application code is agnostic to the protocol.
In 2022, a new version of the HTTP protocol, HTTP/3, was standardized by the Internet Engineering Task Force (IETF). HTTP/3 is an evolution of HTTP/2 based on QUIC (pronounced "quick"), a new transport protocol over the User Datagram Protocol (UDP). Unlike previous versions of HTTP, HTTP/3 doesn't use TCP/IP. The QUIC protocol provides a reliable transport layer, using TLS 1.3 encryption at the transport level.
Supporting HTTP/3 will enable HTTP Client applications to benefit from the many improvements offered by the new protocol, such as:
potentially faster handshakes;
removal of head-of-line blocking issues; and
more reliable transport, especially in those environments with poor internet connections causing packet loss.
HTTP/3 and QUIC are already widely adopted by major companies, supported by many browser implementations, and developers are expected to demand support in the Java Platform.
Description
The Http Client API will remain backwards compatible with only minor enhancements to support HTTP/3. Specifically, only one area of API change is absolutely required, that to override the default protocol version and explicitly request that HTTP/3 be used.
To send a request using HTTP/3, a user of the API must opt in to HTTP/3 by setting the default version of their HttpClient to HTTP/3 or by explicitly setting the version of the HttpRequest to HTTP/3. If the target server doesn't support HTTP/3 the request may be transparently downgraded to HTTP/2 (or HTTP/1.1). In the example shown above, enabling HTTP/3 would simply require selecting HTTP/3 using the new addition to the API. For instance:
Selecting HTTP_3 either as default version for the client:
var httpClient = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_3) .proxy(...).build();
Or as explicit version for the request:
var request = HttpRequest.newBuilder(URI.create("https://openjdk.org")) .version(HttpClient.Version.HTTP_3) .GET().build();
Nothing else needs changing.
The following API enhancements might also be considered:
limited configuration for tuning the HTTP/3 implementation, for instance by means of JDK-specific system properties, or possibly by small API changes.
specific subclasses of IOException or SSLException may need to be introduced for detailing HTTP/3 specific errors.
or may need to be introduced for detailing HTTP/3 specific errors. enhancements to the PushPromiseHandler interface to support the HTTP/3 capability of sharing promise responses between different request/response streams opened on the same connection.
optional configuration options added to the HttpClient.Builder or HttpRequest.Builder for tuning discovery of the target QUIC endpoint.
Testing
New tests for HTTP/3 will be developed. Existing tests for the HttpClient, that do not depend on a specific version of the protocol and which already use data providers to test the HttpClient functionality through HTTP/1.1 and HTTP/2, will be extended to test the same functionality through HTTP/3. One time interoperability testing might be performed against existing known HTTP/3 servers.
Risks and Assumptions
The effort to come up with a finished implementation is estimated as large. While the additional API surface is not expected to be significant, the volume of specification material that constitutes the substantive QUIC protocol is rather large. The majority of effort is expected to reside within implementing the QUIC protocol itself. Providing a public API for the QUIC protocol is however not a goal of this JEP.
This first implementation of HTTP/3 will not support working with other security providers than the default SunJSSE provider. Working with 3rd party JSSE providers would require adding/modifying the current JSSE Provider SPI, and would require third party providers to implement those methods. This might be the object of another future JEP.
A new HTTP_3 constant is added to the HttpClient.Version enum. This could cause an IncompatibleClassChangeError if the new constant reaches a switch statement compiled before the addition of the constant, when the switch statement has no default clause. For compatibility reasons, it seems therefore more prudent to require HTTP_3 to be an opt-in: a caller that opts in for HTTP_3 should be prepared to receive responses that contain an HTTP_3 version. A caller that didn't opt-in, such as a caller that was coded before the introduction of the new constant, might not expect it.
In addition, HTTP/3 doesn't define any upgrade mechanism. To figure out whether a server supports HTTP/3, a client would have to either send the first request through HTTP/1.1 or HTTP/2 and hope to receive an alternate service header or frame, or attempt to initiate a QUIC handshake at the given URL. Because QUIC is based on UDP, determining that QUIC is not supported by the peer can only be achieved by waiting for a response until a timeout expires. Making HTTP_3 the default version would therefore cause a cost to be incurred by each request sent to a new server.
This policy could be revisited in years to come, if adoption of HTTP/3 becomes more widespread.
The HTTP/3 and QUIC specification are significantly large and make allowance for what client and server endpoints MUST or MAY implement. The initial implementation of this JEP may only implement a minimal subset of the specifications.
Dependencies
The QUIC Protocol is defined by the following RFCs:
RFC 8999: Version-Independent Properties of QUIC
RFC 9000: A UDP-Based Multiplexed and Secure Transport
RFC 9001: Using TLS to Secure QUIC
RFC 9002: QUIC Loss Detection and Congestion Control
The HTTP/3 protocol is defined by the following RFCs:
RFC 9114: HTTP/3
RFC 9204: QPACK: Field Compression for HTTP/3
In addition, the following RFCs are of interest for discovering QUIC endpoints and measuring path MTU:
RFC 7838: HTTP Alternative Services
RFC 8899: Packetization Layer Path MTU Discovery for Datagram Transports
RFC 4821: Packetization Layer Path MTU Discovery
Some other RFCs are also of interest but may not be supported by the first implementation: