Java 26: HTTP/3 for the HTTP Client API (JEP 517)
Zero-RTT connection setup, no head-of-line blocking, and connection migration across networks — Java’s built-in HttpClient now speaks HTTP/3. JEP 517 finalizes HTTP/3 support in JDK 26, making it the first JDK version with zero-dependency QUIC-based HTTP.
Status: Standard — available in JDK 26 without any flags.
What Changed
java.net.http.HttpClient now supports HTTP/3 (RFC 9114), which runs over QUIC (RFC 9000) instead of TCP. HTTP/3 is already used by ~30% of all web traffic (Google, Cloudflare, Meta). Until JDK 26, Java developers needed third-party libraries like Netty or Jetty for HTTP/3 support.
HTTP/3 Key Improvements
- No head-of-line blocking — Each QUIC stream is independently delivered; a lost packet on one stream doesn’t block others.
- Faster connection setup — QUIC combines TLS and transport handshakes, achieving 0-RTT or 1-RTT.
- Connection migration — Connections survive network changes (Wi-Fi → cellular) without reconnecting.
- Built-in encryption — QUIC mandates TLS 1.3 for all traffic.
Demo Highlights
1. HTTP Version Constants
JDK 26 adds HttpClient.Version.HTTP_3 alongside the existing HTTP_1_1 and HTTP_2 constants:
for (HttpClient.Version version : HttpClient.Version.values()) {
System.out.println("Available: " + version);
}
// Available: HTTP_1_1
// Available: HTTP_2
// Available: HTTP_32. Basic HTTP/3 GET Request
Configure the client with HTTP_3 and the transport upgrades automatically when the server supports it:
try (var client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.connectTimeout(Duration.ofSeconds(5))
.build()) {
var request = HttpRequest.newBuilder()
.uri(URI.create("https://www.google.com"))
.GET()
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Status: " + response.statusCode());
System.out.println("Protocol: " + response.version()); // → HTTP_3
}3. Version Negotiation Comparison
Make the same request with different preferred versions and inspect which protocol was actually negotiated:
HttpClient.Version[] versions = {
HttpClient.Version.HTTP_1_1,
HttpClient.Version.HTTP_2,
HttpClient.Version.HTTP_3
};
for (var preferredVersion : versions) {
try (var client = HttpClient.newBuilder()
.version(preferredVersion)
.connectTimeout(Duration.ofSeconds(5))
.build()) {
var response = client.send(request, HttpResponse.BodyHandlers.discarding());
System.out.println("Preferred: " + preferredVersion
+ " → Actual: " + response.version());
}
}4. Async HTTP/3 Requests
HTTP/3 works seamlessly with sendAsync. QUIC’s multiplexing means multiple concurrent requests over a single connection without head-of-line blocking:
CompletableFuture<HttpResponse<String>> future =
client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
// Non-blocking: do other work while waiting
HttpResponse<String> response = future.join();
System.out.println("Protocol: " + response.version());5. Concurrent Multiplexed Requests
HTTP/3’s QUIC transport eliminates head-of-line blocking. Multiple concurrent requests over the same connection don’t interfere:
List<String> urls = List.of(
"https://www.google.com",
"https://www.google.com/search?q=java",
"https://www.google.com/search?q=quic",
"https://www.google.com/search?q=http3"
);
List<CompletableFuture<HttpResponse<String>>> futures = urls.stream()
.map(url -> HttpRequest.newBuilder().uri(URI.create(url)).GET().build())
.map(req -> client.sendAsync(req, HttpResponse.BodyHandlers.ofString()))
.toList();
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();
// All 4 requests ran over QUIC streams in parallel — no blockingReal-World Use Cases
The companion Http3RealWorldExamples class demonstrates six production scenarios:
Version-Aware REST API Client
A microservice client that prefers HTTP/3 for reduced latency but logs the actual negotiated protocol for diagnostics:
try (var client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.connectTimeout(Duration.ofSeconds(5))
.build()) {
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/v1/users"))
.header("Accept", "application/json")
.GET()
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Protocol: " + response.version()); // Logged for diagnostics
}Health Check Service with Protocol Detection
Monitor multiple endpoints and report their HTTP version support alongside availability:
List<String> endpoints = List.of(
"https://www.google.com",
"https://cloudflare.com",
"https://www.github.com"
);
for (String endpoint : endpoints) {
long start = System.nanoTime();
var response = client.send(headRequest(endpoint), HttpResponse.BodyHandlers.discarding());
long latency = (System.nanoTime() - start) / 1_000_000;
String badge = switch (response.version()) {
case HTTP_3 -> "🟢 HTTP/3";
case HTTP_2 -> "🟡 HTTP/2";
case HTTP_1_1 -> "🔴 HTTP/1.1";
};
System.out.println(badge + " " + endpoint + " " + latency + "ms");
}Parallel API Aggregator (Fan-Out)
A BFF layer aggregates data from multiple upstream services in parallel. HTTP/3’s QUIC multiplexing avoids head-of-line blocking — a slow response doesn’t stall the others:
try (var client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build()) {
List<CompletableFuture<String>> futures = List.of(
"user-profile", "recent-orders", "notifications", "recommendations"
).stream()
.map(service -> client.sendAsync(requestFor(service), BodyHandlers.ofString())
.thenApply(r -> service + " → " + r.version() + " " + r.statusCode()))
.toList();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
}Resilient Downloader with Version Fallback
Download content preferring HTTP/3, but gracefully fall back through HTTP/2 → HTTP/1.1:
HttpClient.Version[] fallbackChain = {
HttpClient.Version.HTTP_3,
HttpClient.Version.HTTP_2,
HttpClient.Version.HTTP_1_1
};
for (var version : fallbackChain) {
try (var client = HttpClient.newBuilder()
.version(version)
.connectTimeout(Duration.ofSeconds(3))
.build()) {
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println("✅ Downloaded via " + response.version());
break;
}
} catch (Exception e) {
System.out.println("⚠️ " + version + " failed — trying next...");
}
}Other Use Cases in the Demo
- Protocol version audit — Scan endpoints and report which support HTTP/3 (compliance dashboards)
- Latency benchmark — Compare HTTP/2 vs HTTP/3 response times
Best For
HTTP/3 benefits are most visible for:
- High-latency or lossy networks (mobile, satellite, intercontinental)
- APIs with many small concurrent requests (microservices, fan-out)
- CDN integration (Cloudflare, Fastly, Akamai all support HTTP/3)
- Mobile backends where connection migration avoids reconnection overhead
Running the Demo
mvn compile exec:exec \
-Dexec.mainClass=org.example.standard.Http3DemoCheck out the full source on GitHub.