Files

97 lines
5.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# HTTP/3 in the JDK HttpClient (JEP 517, final in JDK 26). Regenerates every file in output/.
# JDK25=/path JDK26=/path [TRIALS=20] ./run.sh
# Needs: python3 with `pip install "hypercorn[h3]"`, openssl, and (for 05/06) root with iptables + ip.
# The loss tests change the loopback MTU and add iptables rules; both are undone on exit.
set -uo pipefail
unset JAVA_TOOL_OPTIONS
HERE="$(cd "$(dirname "$0")" && pwd)"; cd "$HERE"
JDK25="${JDK25:-/opt/jdks/jdk-25.0.4.1+1}"; JDK26="${JDK26:-/opt/jdks/jdk26}"; TRIALS="${TRIALS:-20}"
OUT="$HERE/output"; mkdir -p "$OUT"
URL=https://localhost:4433/hello
[ -f certs/cert.pem ] || ./server/make-cert.sh
SPID=""; PPID_=""
start() { python3 server/serve.py "$1" 4433 >/dev/null 2>&1 & SPID=$!; sleep 2; }
stop() { [ -n "$SPID" ] && kill -9 "$SPID" 2>/dev/null; wait "$SPID" 2>/dev/null; SPID=""; }
cleanup() { stop; [ -n "$PPID_" ] && kill -9 "$PPID_" 2>/dev/null; scripts/loss.sh off 2>/dev/null; }
trap cleanup EXIT
v() { "$1/bin/java" -version 2>&1 | sed -n 2p; }
J25="$JDK25/bin/java"; J26="$JDK26/bin/java"
ONLY="${ONLY:-}" # ONLY=07 regenerates just the loss table
if [ -z "$ONLY" ]; then
# 01 - plain Java 11 code: same source, JDK 25 and 26, server offers HTTP/3. Nothing changes.
start h3
{
for j in "$JDK25" "$JDK26"; do echo "\$ java src/Basics.java $URL ($(v $j))"; "$j/bin/java" src/Basics.java $URL 2>&1; echo; done
echo "# the alt-svc value above is the server advertising h3 on UDP 4433; nobody asked the client to use it."
} > "$OUT/01-java11-code-unchanged.txt"
# 02 - the opt-in, client level and request level; and the JDK 25 failure
{
echo "\$ java src/OptIn.java $URL client ($(v $JDK26))"; "$J26" src/OptIn.java $URL client 2>&1
echo; echo "\$ java src/OptIn.java $URL request ($(v $JDK26))"; "$J26" src/OptIn.java $URL request 2>&1
echo; echo "\$ java src/OptIn.java $URL client ($(v $JDK25))"; "$J25" src/OptIn.java $URL client 2>&1 | head -6
echo; echo "\$ first request of a fresh client-level HTTP_3 client, 8 fresh JVMs ($(v $JDK26))"
for i in 1 2 3 4 5 6 7 8; do "$J26" src/OptIn.java $URL client 2>&1 | sed -n 1p; done
} > "$OUT/02-opt-in.txt"
# 03 - the three discovery modes against a server that speaks HTTP/3 (h3 server still running)
{ echo "\$ java src/Discovery.java $URL (server: h1.1 + h2 on TCP, h3 on UDP, Alt-Svc advertised)"; "$J26" src/Discovery.java $URL 2>&1; } > "$OUT/03-discovery-modes.txt"
stop
# 04 - fallback: servers that do not speak HTTP/3 at all
{
start h2
echo "\$ java src/Fallback.java $URL 120 (server: h2 + h1.1 on TCP only; nothing listens on UDP 4433)"
"$J26" src/Fallback.java $URL 120 2>&1
echo; echo "\$ java -Djdk.httpclient.http3.maxDirectConnectionTimeout=1 src/Fallback.java $URL 120 (same server)"
"$J26" -Djdk.httpclient.http3.maxDirectConnectionTimeout=1 src/Fallback.java $URL 120 2>&1
echo; echo "\$ java -Djdk.httpclient.quic.maxInitialTimeout=3 src/Fallback.java $URL 120 (same server)"
"$J26" -Djdk.httpclient.quic.maxInitialTimeout=3 src/Fallback.java $URL 120 2>&1
stop; start h1
echo; echo "\$ java src/Discovery.java $URL (server: HTTP/1.1 only, TCP)"
"$J26" src/Discovery.java $URL 2>&1
stop
} > "$OUT/04-fallback.txt"
# 05 - a firewall that drops all UDP to 4433 (common in offices): the server is h3-capable but unreachable over QUIC
start h3; scripts/loss.sh 100 udp
{
echo "\$ iptables: DROP 100% of UDP to/from port 4433 (TCP untouched); server is the h3 server"
echo "\$ java src/Fallback.java $URL 120"
"$J26" src/Fallback.java $URL 120 2>&1
} > "$OUT/05-udp-blocked.txt"
scripts/loss.sh off; stop
# 06 - a proxy: HTTP/3 is not sent through it
start h3; python3 server/connect-proxy.py 4480 > "$OUT/.proxy.log" 2>&1 & PPID_=$!; sleep 1
{
echo "\$ java src/ViaProxy.java $URL 4480 (client.proxy(...) points at a CONNECT proxy on 4480)"
"$J26" src/ViaProxy.java $URL 4480 2>&1
echo; echo "\$ proxy log"; cat "$OUT/.proxy.log"
} > "$OUT/06-proxy.txt"
kill $PPID_ 2>/dev/null; PPID_=""; rm -f "$OUT/.proxy.log"; stop
fi
# 07 - HTTP/1.1 vs HTTP/2 vs HTTP/3 under random packet loss (same h3-capable server process for all three)
python3 server/serve.py h3 4433 2> "$OUT/.srv.err" >/dev/null & SPID=$!; sleep 2
{
echo "# loss = per-packet drop probability, applied in EACH direction, TCP and UDP port 4433, by iptables -m statistic on lo (MTU 1500)."
echo "# no added latency (loopback RTT is microseconds). $TRIALS trials per row after one discarded warm-up; $(v $JDK26)"
echo "# one = fresh client, 1 download of 1 MiB. many = fresh client, 10 parallel downloads of 100 KiB."
for L in 0 1 2 5 10; do
scripts/loss.sh $L
for w in one many; do for p in h1 h2 h3; do
timeout 600 "$J26" src/Bench.java $p $w $TRIALS $L 2>&1
done; done
echo
done
} > "$OUT/07-packet-loss.txt"
scripts/loss.sh off; stop
{
echo "# what the Hypercorn/aioquic server wrote to stderr during 07 (the loss run), exception lines only, counted"
grep -E "(Error|Exception)[:(]" "$OUT/.srv.err" | sed "s/^[ |+-]*//" | sort | uniq -c | sort -rn
} > "$OUT/08-server-errors.txt"; rm -f "$OUT/.srv.err"
echo "wrote $OUT"