JEP 517: HTTP/3 for the HTTP Client API
Owner | Daniel Fuchs |
Type | Feature |
Scope | SE |
Status | Candidate |
Component | core-libs / java.net |
Discussion | net dash dev at openjdk dot org |
Effort | L |
Duration | L |
Relates to | JEP 321: HTTP Client API |
Reviewed by | Alan Bateman, Bradford Wetmore, Paul Sandoz |
Created | 2022/08/05 14:58 |
Updated | 2025/05/02 16:09 |
Issue | 8291976 |
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
-
Update the HTTP Client API to send and receive HTTP/3 requests and responses.
-
Require only minor changes to the HTTP Client API and to application code.
-
Do not change the default protocol version from HTTP/2 to HTTP/3 but, rather, enable developers to opt-in to HTTP/3.
Non-Goals
-
It is not a goal to provide an API for the QUIC protocol, upon which HTTP/3 is based.
-
It is not a goal to support third-party secure-socket providers.
-
It is not a goal to provide a server-side implementation of the HTTP/3 protocol.
-
It is not a goal to update the protocol handler used by the legacy java.net.URL API, which only supports HTTP/1.1.
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 was standardized in 2022 by the Internet Engineering Task Force (IETF). HTTP/3 is a successor to HTTP/2 which 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
- Potentially faster handshakes,
- Avoidance of network congestion issues such as head-of-line blocking, and
- More reliable transport, especially in environments with high rates of packet loss.
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:
-
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 the
HttpClient
object has a preferred version ofHTTP_3
and theHttpRequest
object does not have a preferred version.) -
Send the first request using HTTP/3. If an HTTP/3 response is not received in a reasonable time then fall back to HTTP/1.1 or HTTP/2. (This is what happens when the
HttpRequest
object has a preferred version ofHTTP_3
.) -
Send the first request twice, once using HTTP/3 and once using HTTP/2 or HTTP/1.1. Use the protocol of the first response received. (This is what happens when you specify
Http3DiscoveryMode.ANY
as the value of theH3_DISCOVERY
request option, theHttpClient
object has a preferred version ofHTTP_3
, and theHttpRequest
object does not have a preferred version.) -
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 theH3_DISCOVERY
request option and at least one of theHttpClient
orHttpRequest
objects has a preferred version ofHTTP_3
.)
These approaches have tradeoffs: The first requires using HTTP/2 or HTTP/1.1 initially, the second requires waiting for a timeout, the third may load the HTTP/3 implementation but never again use it, and the fourth works only if you know in advance that the target server supports HTTP/3.
Because HTTP/3 is not yet widely deployed, no single approach will work for all circumstances. Therefore we are not proposing to make HTTP/3 the default at this time, though we may do so in the future.
Further potential enhancements
-
Enable limited configuration and tuning of the HTTP/3 implementation, for instance by means of JDK-specific system properties or, possibly, small new APIs.
-
Define new subclasses of
IOException
andSSLException
for HTTP/3-specific exception conditions. -
Enhance the
PushPromiseHandler
interface to support HTTP/3's ability to share promise responses across distinct request/response streams within the same connection. -
Add configuration options to the
HttpClient.Builder
andHttpRequest.Builder
APIs to control discovery of the target server.
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:
- 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:
These RFCs are of interest for discovering QUIC endpoints and measuring network path MTU:
- RFC 7838: HTTP Alternative Services
- RFC 8899: Packetization Layer Path MTU Discovery for Datagram Transports
- RFC 4821: Packetization Layer Path MTU Discovery
Two additional RFCs are of interest but may not be supported by the first implementation: