JEP draft: DRAFT: Support HTTP/3 in the HttpClient

OwnerDaniel Fuchs
TypeFeature
ScopeSE
StatusDraft
Releasetbd
Componentcore-libs / java.net
EffortL
DurationL
Created2022/08/05 14:58
Updated2024/05/15 16:50
Issue8291976

Summary

Update the HTTP Client to support the HTTP/3 protocol.

Goals

Non Goals

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:

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:

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:

The HTTP/3 protocol is defined by the following RFCs:

In addition, the following RFCs are of interest for discovering QUIC endpoints and measuring path MTU:

Some other RFCs are also of interest but may not be supported by the first implementation: