# http3 — HTTP/3 in the Java HTTP Client (JEP 517, final in Java 26) Companion code for the ankurm.com article **HTTP/3 in the Java HTTP Client (JEP 517): Migrating from HttpClient 11**. All explanation lives in the article; this folder holds the runnable sources, a local HTTP/3 test server, and the transcripts they produced. ## What was tested against | Thing | Version | Note | |---|---|---| | JDK 26 | Temurin 26.0.2.1+1 | JEP 517 is delivered in 26 (GA 2026-03-17); this is the only JDK that can run HTTP/3 here | | JDK 25 | Temurin 25.0.4.1+1 | the "before": `HttpClient.Version.HTTP_3` does not exist, plain Java 11 code behaves the same as on 26 | | Test server | Hypercorn 0.18.0 (HTTP/1.1 + HTTP/2 on TCP) + aioquic 1.3.0 (HTTP/3 on UDP) | one Python process, same port number for both, `Alt-Svc` advertised. Hypercorn's own QUIC listener was dropped: it died on a `KeyError` under packet loss and the port went silent | | Loss injection | `iptables -m statistic --mode random` on `lo` | **not** `tc netem` (unavailable in the sandbox: `Specified qdisc kind is unknown`): loss only, no added delay | The JDK 26 lane in the other modules of this repo uses Amazon Corretto 26.0.2.1; this module used Temurin because that is what the sandbox had. The API is the same. ## Quickstart ```bash pip install hypercorn==0.18.0 aioquic==1.3.0 JDK25=/path/to/jdk-25 JDK26=/path/to/jdk-26 ./run.sh # about 45 minutes: the `HTTP_3_URI_ONLY` failure demos wait a full minute each and the loss table is 600 trials; needs root for 05 and 07; ONLY=07 regenerates just the loss table # or by hand: ./server/make-cert.sh && python3 server/serve.py h3 4433 & # h3 | h2 | h1 java src/OptIn.java https://localhost:4433/hello request ``` ## Layout | Path | What is in it | |---|---| | `src/Tls.java` | trusts only the self-signed test certificate | | `src/Basics.java` | plain Java 11 `HttpClient` code, no version set | | `src/OptIn.java` | the JEP 517 opt-in, client-wide or per request | | `src/Discovery.java` | the three `H3_DISCOVERY` modes | | `src/Fallback.java` | first-request latency and version for each way of asking, against servers that cannot do HTTP/3 | | `src/ViaProxy.java` | HTTP/3 and a proxy | | `src/Bench.java` | HTTP/1.1 vs HTTP/2 vs HTTP/3 download times under packet loss | | `server/` | `app.py` (shared responses + ASGI wrapper), `serve.py` (h3 / h2 / h1 modes), `h3server.py` (aioquic HTTP/3), `connect-proxy.py`, `make-cert.sh` | | `scripts/loss.sh` | adds and removes the iptables loss rules and the MTU change | | `output/` | every transcript quoted in the article (`01`-`08`) | Timings are machine dependent and the loss is random: treat `07-packet-loss.txt` as shape, not a benchmark of HTTP/3 implementations. The server is Python, the client is the JDK.