|
| 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