JDK-8350588 : Implement JEP 517: HTTP/3 for the HTTP Client API
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.net
  • Priority: P3
  • Status: Provisional
  • Resolution: Unresolved
  • Fix Versions: tbd
  • Submitted: 2025-02-24
  • Updated: 2025-05-08
Related Reports
Blocks :  
CSR :  
Description
Summary
-------

Update the HTTP Client API to support the HTTP/3 protocol. This will allow applications and libraries to interact with HTTP/3 servers and get the benefits of HTTP/3 with minimal code changes.

Problem
-------

The HTTP 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. HTTP/3 was standardized in 2022 by the Internet Engineering Task Force (IETF). [HTTP/3](https://www.rfc-editor.org/rfc/rfc9114) is an evolution of [HTTP/2](https://www.rfc-editor.org/rfc/rfc9113.html) based on [QUIC](https://www.rfc-editor.org/rfc/rfc9000) (pronounced "quick"), a new transport protocol over the User Datagram Protocol (UDP). 
Unfortunately, the HTTP Client API does not support this latest version of the HTTP protocol. Providing HTTP/3 support, within the HttpClient, provides application with greater connectivity scope, and protocol options, when accessing the Web’s services and resources.

This CSR proposes to update the HTTP Client API to support the HTTP/3 protocol. This will allow applications and libraries to interact with HTTP/3 servers and get the benefits of HTTP/3 with minimal code changes. To achieve this goal several small API updates are needed.

The `HttpClient.Version` enum currently only allows to choose between HTTP/1.1 and HTTP/2. In order to support HTTP/3, a new `HTTP_3` constant is needed.

Unlike previous versions of the protocol, HTTP/3 doesn’t define a standard port for servers to be listening on. Though it is expected that most HTTP/3 servers deployed will listen on UDP port 443, this is neither guaranteed nor specified by the RFCs. Because HTTP/3 goes over UDP, and previous versions go over TCP, there is no way to define an upgrade mechanism or negotiate the HTTP version during the TLS handshake. 
Instead, HTTP servers may use HTTP Alternative Services (RFC 7838) to advertise the availability of resources over HTTP/3. An http client, which is ALT-SVC aware can utilise this information to access services and resources over HTTP/3. Additionally, an HTTP client may attempt to connect directly to an HTTP server, as per the authority (host:port) extracted from a URI, over HTTP/3. As such, an API with a flexible connection policy is desirable.

Compared to HTTP/2, HTTP/3 allows server pushes to be shared between several request/responses on the same connection. 
The PushPromiseHandler interface needs to be extended to support this. Also it should be possible to figure out whether two different requests were performed through the same, or through different connections, as it may have an impact on whether push promise responses can be shared between them.


Solution
--------

The following API changes are proposed:

 * extend the `HttpClient.Version` enum with a new constant value: `HTTP_3`.
 * update the `HttpClient` class level API documentation to advertise and document  HTTP/3 support, enhance the `HttpClient.Builder.build` method API documentation
 * add a new sealed `HttpOption<T>` interface, to model options that can be set through the `HttpRequest.Builder` .
 * add a new method `HttpRequest.Builder.setOption(HttpOption<T> option, T value)` allowing to configure a request with options.
 * add a new method `Optional<T> HttpRequest.getOption(HttpOption<T> option)`, to get the value of an option previously set on the request through the `HttpRequest.Builder`.
 * define an `HttpOption.H3_DISCOVERY` constant, (of type `HttpOption<Http3DiscoveryMode>`) to model an option that allows to configure how to select/establish an HTTP/3 connection for the request, if the selected protocol version is `HTTP_3`.
 * define an `HttpOption.Http3DiscoveryMode` enum with three values, `ALT_SVC`, `ANY`, and `HTTP_3_URI_ONLY`  that can be set as value for the `H3_DISCOVERY` option, in order to change the default HttpClient behavior for a given request if needed.
 * update API documentation (and/or `@implNote`) of the various `Builder::version` / `HttpClient::version` / `HttpRequest::version` methods to link back to the paragraph in the `HttpClient` class-level `@implNote` that talk about protocol version selection.
 * modify `HttpResponse.PushPromiseHandler` with two new default methods that will be called in the event that an HTTP/3 push promise is received. A class implementing `HttpResponse.PushPromiseHandler` could override these methods to take advantage of the improved push promise mechanism brought by HTTP/3. The default implementation of these method will call existing methods so that existing push promise handlers still work, though they may not be able to take full advantage of the push promise sharing defined by HTTP/3. To support this a sealed `PushPromiseHandler.PushId` interface, implemented by a `PushId.Http3PushId` record is also defined.
 * add two new subclasses of IOException: `UnsupportedProtocolVersionException`, and `StreamLimitException`
 * add and document in module-info a few jdk-specific system properties to help configure some coarse grain aspects of the HTTP/3 and underlying QUIC protocols implementation.


Specification
-------------

See attached SpecDiff and ApiDiff

Changes to module-info.java are only visible in the ApiDiff

Comments
Thanks for that clarification Daniel
08-05-2025

> FWIW possible alternative wordings ;-) [~msheppar] Thanks! I adopted your suggested wording (with only some minor change)
08-05-2025

[~msheppar] the naming of the Http3DiscoveryMode constants was discussed at length. HTTP_3_URI_ONLY conveys the information that we should consider the URI as identifying the HTTP/3 endpoint only. So we will attempt an HTTP/3 connection at the host:port of the URI and not falback to HTTP/2 or HTTP/1.1 It's a better name than HTTP_3_ONLY which could leave some doubt about what information would be used to make the connection.
08-05-2025

FWIW possible alternative wordings ;-) Supporting HTTP/3 will enable applications using the HTTP Client API to benefit from the many improvements offered by the new protocol. Providing HTTP/3 support, within the HttpClient, provides application with greater connectivity scope, and protocol options, when accessing the Web’s services and resources. ----------oOo-------- Instead HTTP/3 advertises the use of HTTP Alternative Services (RFC 7838) to discover HTTP/3 endpoints. The RFC also allows to optimistically attempt an HTTP/3 connection at the authority (host:port) supplied in the URI. An API point that will allow some flexibility in how an HTTP/3 connection is established will be needed. Instead, HTTP servers may use HTTP Alternative Services (RFC 7838) to advertise (,or make known,) the availability of resources over HTTP/3. An http client, which is ALT-SVC aware can utilise this information to access services and resources over HTTP/3. Additionally, an http client may directly connect to an HTTP server, as per the authority (host:port) extracted from a URI, over HTTP/3. As such, an API with a flexible connection policy is desirable.
08-05-2025

for the Http3DiscoveryMode enum, the value HTTP_3_URI_ONLY is a curious one with the addition of URI, one is left thinking what's the inference of the URI, other than providing a unique name public static final HttpOption.Http3DiscoveryMode HTTP_3_URI_ONLY This instructs the HttpClient to only attempt an HTTP/3 connection with the origin server. The connection will only succeed if the origin server is listening for incoming HTTP/3 connections over QUIC at the same authority (host, port) as defined in the request URI. In this mode, HTTP Alternative Services are not used. based on the javadoc description the original HTTP_3_ONLY seems appropriate or, HTTP_3_DIRECT to indicate that a direct connection with an origin server is made
08-05-2025

The API has been updated with the following: 1. rename HttpRequestOption into HttpOption and make it a top level class. 2. move Http3DiscoveryMode as an inner class in HttpOption 3. improve the paragraph that talks about version selection in the HttpClient class-level impl note. 4. improve the API documentation of the various Builder.version and HttpClient and HttpRequest version methods, adding links to the above paragraph These changes are included in the attached `apidiff-http3(3).zip` and `specdiffOut(3).zip`
02-05-2025

[~darcy] > Please consider different names for H3DiscoveryMode and its constants. The "HTTP_3" prefix in each constant seems unfortunate. After discussion we renamed `enum H3DiscoveryMode { HTTP_3_ALT_SVC, HTTP_3_ANY, HTTP_3_ONLY }` into `enum Http3DiscoveryMode { ALT_SVC, ANY, HTTP_3_URI_ONLY }` The new HttpResponse::connectionLabel() method has been integrated into mainline so it's also no longer part of this CSR. specdiffOut(2).zip and apidiff-http3(2).zip attached.
18-04-2025

Thanks for the update [~dfuchs]; moving to Provisional, not Approved. A few addition pieces of feedback: > Implementation Requirements: > the default implementation invokes applyPushPromise(HttpRequest, HttpRequest, Function) . This allows PushPromiseHandlers from previous > releases to handle HTTP/3 push promise in a reasonable way. Wording like this ages poorly. Consider "This allows PushPromiseHandlers from previous prior to the introduction of HTTP/3 to handle HTTP/3 push promise in a reasonable way." Please consider different names for H3DiscoveryMode and its constants. The "HTTP_3" prefix in each constant seems unfortunate.
07-04-2025

Also uploaded the serial form of the java.net.http module/package as a PDF document see attached: Serialized Form java.net.http (1).pdf
07-04-2025

Updated apidiff and specdiff, ending with (1).zip: jdk.httpclient.keepalive.timeout property is clarified to also mention HTTP/3
04-04-2025

Moving to Draft, not Provisional. [~dfuchs], here is my first round of feedback, minor clean-ups: Missing since tag for HTTP_3 constant HttpRequest.getOption Should this method have an `@throws` clause for a NPE is the option argument is null? HttpRequest.Builder.setOption Should this have any specification on how null arguments are handled? Were other names considered for the constants defined on H3DiscoveryMode? For PushPromiseHandler.notifyAdditionalPromise, the text " The default implementation of this method does nothing. " should be in an implSpec tag. Please include null/non-null requirements for StreamLimitException, which would include its serial form.
03-04-2025