JEP 517: HTTP/3 for the HTTP Client API

OwnerDaniel Fuchs
TypeFeature
ScopeSE
StatusProposed to Target
Release26
Componentcore-libs / java.net
Discussionnet dash dev at openjdk dot org
EffortL
DurationL
Relates toJEP 321: HTTP Client API
Reviewed byAlan Bateman, Bradford Wetmore, Paul Sandoz
Endorsed byPaul Sandoz
Created2022/08/05 14:58
Updated2025/09/03 18:06
Issue8291976

Summary

Update the HTTP Client API to support the HTTP/3 protocol, so that libraries and applications can interact with HTTP/3 servers with minimal code change.

Goals

Non-Goals

Motivation

JEP 321 (JDK 11) added a modern HTTP Client API to the Java Platform. The API supports HTTP/1.1 and HTTP/2, and was designed to support future protocol versions with minimal change. The API prefers HTTP/2 by default, but transparently downgrades to HTTP/1.1 if the target server does not support HTTP/2.

The HTTP Client API makes it easy to write code that interacts with HTTP servers. For example, to send a GET request to https://openjdk.org/ and receive the response as a string:

import java.net.http.*;

...

var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://openjdk.org/"))
                         .GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assert response.statusCode() == 200;
String htmlText = response.body();

Here 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.

Unfortunately, the HTTP Client API does not support the latest version of the HTTP protocol. HTTP/3, the successor to HTTP/2, was standardized in 2022 by the Internet Engineering Task Force (IETF). HTTP/3 uses the QUIC reliable transport-layer protocol rather than TCP. QUIC is secured with Transport Layer Security (TLS) version 1.3.

Supporting HTTP/3 would enable applications using the HTTP Client API to benefit from the many improvements offered by the HTTP/3 protocol, including

HTTP/3 is already supported by most web browsers and deployed on about a third of all web sites. The Java Platform should support it for client-side use.

Description

To send a request using HTTP/3, you must opt-in to using it.

You can do this by setting the protocol version of an HttpClient object to HTTP/3, which will cause all requests sent with the client to prefer HTTP/3 by default:

var client = HttpClient.newBuilder()
                       .version(HttpClient.Version.HTTP_3)
                       .build();

Alternatively, you can set the preferred protocol version of an individual HttpRequest object:

var request = HttpRequest.newBuilder(URI.create("https://openjdk.org/"))
                         .version(HttpClient.Version.HTTP_3)
                         .GET().build();

No other changes are needed. After selecting HTTP/3 as the preferred version, either in the request or in the client, you send the request in the usual way. If the target server does not support HTTP/3 then the request will, by default, be transparently downgraded to HTTP/2 or even HTTP/1.1, as appropriate.

Negotiating HTTP protocol versions

It is impossible to determine, in advance, whether a target server supports HTTP/3. It is also impossible to upgrade an existing HTTP/1.1 or HTTP/2 connection to an HTTP/3 connection, since HTTP/1.1 and HTTP/2 are built on top of TCP streams while HTTP/3's QUIC is built on top of UDP datagrams.

How, then, does the HTTP Client API determine whether it can use HTTP/3 for a given target server? There are four basic approaches:

  1. Send the first request using HTTP/3. If an HTTP/3 connection cannot be established in a reasonable time then fall back to HTTP/2 or HTTP/1.1. (This is what happens when the HttpRequest object has a preferred version of HTTP_3.)

  2. Send the first request, attempting both an HTTP/3 connection and an HTTP/2 or HTTP/1.1 connection in parallel. Use the protocol of the first connection that succeeds. (This is what happens when the HttpClient object has a preferred version of HTTP_3 and the HttpRequest object does not have a preferred version.)

  3. Send the first request using HTTP/2 or HTTP/1.1. If the response from the server indicates that HTTP/3 is available as an alternative service then use HTTP/3 for all subsequent requests. (This is what happens when you specify [Http3DiscoveryMode.ALT_SVC] as the value of the H3_DISCOVERY request option and at least one of the HttpClient or HttpRequest objects has a preferred version of HTTP_3.)

  4. Send all requests using HTTP/3. If the server does not reply via HTTP/3 then fail; do not fall back to HTTP/1.1 or HTTP/2. (This is what happens when you specify Http3DiscoveryMode.HTTP_3_URI_ONLY as the value of the H3_DISCOVERY request option and at least one of the HttpClient or HttpRequest objects has a preferred version of HTTP_3.)

These approaches have tradeoffs: The first requires waiting for a timeout, the second may load the HTTP/3 implementation and establish an HTTP/3 connection but never again use it, the third requires using HTTP/2 or HTTP/1.1 initially, and the fourth works only if you know in advance that the target server supports HTTP/3.

Because HTTP/3 is not yet as widely deployed as HTTP/2 and HTTP/1.1, no single approach will work for all circumstances. Therefore we are not proposing to make HTTP/3 the default protocol version at this time, though we may do so in the future.

Further potential enhancements

Testing

We will do extensive unit and integration testing. We will also test interoperation with server-side HTTP/3 implementations including Netty, quic-go, quiche, Neqo, and nghttp3.

Risks and Assumptions

This first implementation of HTTP/3 will not support secure-socket providers other than the default provider, SunJSSE. Support for third-party secure-socket providers would require adding methods to the provider SPI, and then the maintainers of such providers would have to implement those methods. We may address this in future work.

Dependencies

The QUIC Protocol is defined by:

The HTTP/3 protocol is defined by:

These RFCs are of interest for discovering QUIC endpoints and measuring network path MTU. The first implementation supports HTTP Alternative Services but Path MTU Discovery is not implemented.

Two additional RFCs are of interest and are also supported by the first implementation: