Skip to content

Commit 4197995

Browse files
committed
TASK-174: Gate independent HTTP/3 semantics and slow-peer lifecycle
Add passing aioquic and quic-go semantic matrices covering concurrency, binary streaming, trailers, cancellation, slow producers/consumers and graceful GOAWAY. Compare shared route behavior against executed native H1/H2 fixtures; retain native flow-control, received GOAWAY, diagnostic and source-hash evidence. Correct fixture emission-time ordering and preserve 62-bit Go control values. Mark task actions implemented while retaining In Progress for coordinator validation/finalization. Verified both real semantic matrices and compatible smoke matrices; TLS-on C++20 focused suites passed 101 tests and TLS-off suites passed 68 tests; both parity fixtures passed. Python contracts passed 14 tests; Go test/vet, Python compilation, changed-file cpplint, make check-local, make distdir and git diff --check passed. Evidence: build/task174-semantic-final and build/task174-smoke-final; reproduction documented in docs/task-174-http3-semantic-matrix.md. BSD, Windows and other nonlocal checks are deferred to CI/v3 PR. The check-local Doxygen subgate explicitly skipped unavailable local Doxygen and remains enforced in CI.
1 parent df90d07 commit 4197995

19 files changed

Lines changed: 1395 additions & 109 deletions

‎.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,5 @@ __pycache__/
114114
/build-task172/
115115
/build-task172-on/
116116
/build-task172-san/
117+
118+
!test/integ/http3_semantic_aioquic.py

‎Makefile.am‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,3 +687,5 @@ EXTRA_DIST += docs/task-170-http3-drain.md
687687
EXTRA_DIST += docs/task-172-http3-websocket-data-lifecycle.md
688688

689689
EXTRA_DIST += docs/task-173-quic-interop-evidence.md
690+
691+
EXTRA_DIST += docs/task-174-http3-semantic-matrix.md
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# TASK-174 independent HTTP/3 semantic matrix
2+
3+
The explicit `--matrix task174` gate extends the existing smoke gate. Both
4+
clients use their independent native QUIC/TLS/H3 stacks against the bounded
5+
libhttpserver loopback UDP owner. The default smoke mode remains available.
6+
The TASK-173 hq-interop transfers are not used as HTTP/3 semantic evidence.
7+
8+
## Observed behavior
9+
10+
The real network run `build/task174-semantic-07` passed both clients. A final
11+
source replay evidence location is `build/task174-semantic-final`; focused local
12+
checks are recorded below.
13+
14+
| Category | aioquic 1.3.0 | quic-go v0.55.0 |
15+
| --- | --- | --- |
16+
| Verified handshake | QUIC v1, TLS 1.3, h3, no early data | Same |
17+
| Concurrency | Held stream 8; health stream 12 completes before release | Same |
18+
| Incremental binary body | Server reads six bytes before remaining body/FIN; exact 19-byte echo | Same |
19+
| Response streaming/trailers | Multiple awaited writes; separate `x-reply: done` trailers and one terminal FIN | Same |
20+
| Request trailers | `x-end: yes` visible after request EOF | Native public API lacks request-trailer sending; covered by aioquic and native regression |
21+
| Cancellation | Native error 268; one server cancellation; sibling request succeeds on the same connection | Same |
22+
| Slow producer | Six-byte prefix consumed; health completes on a sibling stream before upload resumes | Same |
23+
| Slow consumer | 4096-byte stream credit exhausted; writer pending; health succeeds; granting credit resumes exact 512 KiB body and trailers | Same |
24+
| GOAWAY | Received IDs `4611686018427387900`, `4`; accepted response completes; late stream 4 is rejected with 267; drain completes and connection closes with 256 | Same; received IDs decoded from captured packets |
25+
| H1/H2 parity | Method, raw target/query, normalized route, Host/authority, ordered repeated fields, binary body, response fields/trailers and missing-route 404 match executed native H1/H2 | Same |
26+
27+
The upload is `6669727374007365636f6e64ff746869726400`. The large body is
28+
`bytes(range(256)) * 2048`, SHA256
29+
`33bc8aab40703678c3ebe94d2dd8f2afff285dd901f9234e841e4679f8204fd5`.
30+
Parity normalizes field-name case and protocol framing, retaining duplicate
31+
value order and trailer separation. All protocols execute the same semantic
32+
handlers; H1 uses the real poll/worker connection owner and H2 uses the actual
33+
HTTP/2 engine and HPACK codec.
34+
35+
## Native observations and bounds
36+
37+
The fixture assigns monotonic event numbers and captures each stream identity
38+
when its handler enters. Uploads wait for an actual server prefix-read event.
39+
Cancellation requires exactly one matching server event. Targeted snapshot,
40+
release and drain commands refer to the accepted connection/stream.
41+
42+
Slow consumers leave one awaited writer pending because transport credit and
43+
bounded response storage are exhausted. The observed snapshot has five started
44+
writes and four completed writes. The 4 KiB stream window leaves 16 MiB of
45+
connection credit available to sibling/control streams. aioquic 1.3.0 normally
46+
grows credit from received offsets; this test adapter suppresses only the
47+
selected stream's `_write_stream_limits` during the withholding phase. It keeps
48+
ACKs, other stream credit and the native H3 handler running. quic-go uses its
49+
native 4 KiB receive window without reading the selected response body.
50+
Both qlogs must independently show credit exhaustion, sibling FIN, subsequent
51+
MAX_STREAM_DATA and resumed response FIN in that order.
52+
53+
The aioquic observer records actual GOAWAY control payloads while retaining its
54+
native handler. quic-go's public API has no received-ID observer, so the gate
55+
uses tshark's decrypted incoming H3 frame payloads. The late request stream is
56+
opened before drain; its headers are sent after both received GOAWAY stages.
57+
A server `drain_started` event alone cannot satisfy the gate. The drain ticket
58+
wait runs outside the serialized UDP owner with a five-second deadline.
59+
60+
Connections retain their existing 15-second lifetime. The fixture caps four
61+
connections, 16 active semantic requests per connection, executor turns, 64 KiB
62+
uploads, 512 KiB response accumulation, 16 MiB capture bytes and diagnostic
63+
output. Evidence directories are private; TLS secrets have mode 0600.
64+
`packets.pcap` contains real application-boundary datagrams with synthesized
65+
IPv4/UDP envelopes, rather than an interface capture.
66+
67+
## Provisioning and reproduction
68+
69+
Local tools: Clang 18 (C++20 in actual compiler output), Python 3.12, Go 1.26.9,
70+
and tshark 4.6.4. The clients retain the committed Python requirements and
71+
Go module/checksum pins. The Go client was rebuilt from this worktree.
72+
The official linux-amd64 Go archive SHA256 is
73+
`42d158b4d8f7b61ac0a830567c940a86098fb7aac52e467a5ebec03ef5cc2f8d`.
74+
Python dependencies and Go caches live in ignored `build/`. The decoder and
75+
its runtime libraries were extracted, read-only, from the existing
76+
`task163-runner-tools:local` image
77+
`sha256:354244ace64ea691e55628b3aca83c420acca54c65a4edc94130d61681be6266`.
78+
`build/tooling/tshark` invokes the extracted ELF loader and library path.
79+
The OpenSSL 3.5.9 provider is reused read-only from the repository's tooling.
80+
81+
```sh
82+
python3 -m venv build/http3-venv
83+
build/http3-venv/bin/python -m pip install -r test/integ/http3-client-requirements.txt
84+
GOPATH="$PWD/build/tooling/gopath" GOCACHE="$PWD/build/tooling/gocache" \
85+
build/tooling/go/bin/go build -C test/integ/http3-client-go \
86+
-o ../../../build/http3-client-go .
87+
bash bootstrap
88+
mkdir -p build/task174-on
89+
cd build/task174-on
90+
../../configure --enable-v3-tls --disable-examples CC=clang CXX=clang++ \
91+
CXXFLAGS='-O1 -g' \
92+
V3_TLS_CFLAGS='-I/home/etr/progs/libhttpserver/build/tooling/provider/include' \
93+
V3_TLS_LIBS='-L/home/etr/progs/libhttpserver/build/tooling/provider/lib -lssl -lcrypto'
94+
make -C src -j4 libhttpserver_v3core.la
95+
make -C test -j4 http3_udp_fixture http3_semantic_parity_fixture \
96+
http3_network_owner http3_exchange http3_streaming http3_cancellation \
97+
http3_qpack_scheduling http3_drain
98+
cd ../..
99+
python3 -m unittest discover -s test/integ -p http3_gate_runner_test.py
100+
python3 scripts/run-v3-http3-gates.py --matrix task174 \
101+
--build-dir build/task174-on --log-dir build/task174-semantic-final \
102+
--python "$PWD/build/http3-venv/bin/python" \
103+
--go-client build/http3-client-go --tshark build/tooling/tshark
104+
```
105+
106+
Use a fresh evidence directory on each invocation. Existing evidence is
107+
preserved. Each attempt retains stdout/stderr, exits/commands, captures, qlogs,
108+
TLS diagnostics and the primary failure. Completed client receipts remain when
109+
a later client fails. The manifest hashes the source inputs, Go module files,
110+
fixture/client binaries and the complete working diff, including new files.
111+
The final capture-derived GOAWAY TSV/command and per-client flow witness are
112+
retained beside the raw diagnostics. Fake-process contract tests establish
113+
runner rejection behavior; they are not interoperability evidence.
114+
115+
## Failed attempts and fixes
116+
117+
- `task174-semantic-01` and diagnostic probes `-02` through `-04`: a large
118+
response could stall. The fixture sampled `now` before applying a successful
119+
send completion, which committed a later emission timestamp. The next
120+
packet preparation therefore failed monotonic-time admission. Sampling
121+
after completion fixes the fixture; no production protocol change was needed.
122+
Diagnostic probes that forced extra owner turns are retained as failed
123+
investigation output, and are not accepted matrix evidence.
124+
- `task174-semantic-05`: an adapter selector error bypassed the wrong-CA
125+
observer. Corrected the selector and retained the failing receipt.
126+
- `task174-semantic-06`: Go's generic JSON decoding rounded the 62-bit received
127+
GOAWAY ID. The new failing Go regression reproduces this precisely; using
128+
`json.Decoder.UseNumber` preserves the actual wire value.
129+
- `task174-semantic-07`: both complete matrices passed, including executed
130+
H1/H2 parity. The added native qlog artifact checks also passed on this run.
131+
132+
BSD, Windows and other nonlocal checks are deferred to CI/the v3 PR per the
133+
repository's task workflow. No nonlocal passing evidence is claimed.
134+
135+
## Local verification
136+
137+
- TLS-on C++20 build: six focused suites, 101 tests, zero failures; native H1/H2 parity fixture passed.
138+
- TLS-off C++20 build: five focused suites, 68 tests, zero failures; native H1/H2 parity fixture passed.
139+
- Python runner contracts: 14 tests passed, including receipt rejection, received-control decoding, native flow witness order and primary-failure preservation.
140+
- Go adapter regression and `go vet`: passed. Python compilation and changed-file cpplint: passed.
141+
- `make check-local`: passed; its Doxygen subgate explicitly skipped because Doxygen is unavailable locally and remains enforced in CI.
142+
- `make distdir`: passed; the new fixture, shared route header, Python adapter, Go implementation/test and documentation are included.
143+
- `git diff --check`: passed. The default smoke gate is retained alongside the semantic replay.
144+
145+
Build/test outputs are retained under `build/tooling/`. Task tracking remains
146+
In Progress for the coordinator-owned validation/finalization phases.

0 commit comments

Comments
 (0)