Skip to content

feat(http3): route requests through the HTTP/3 client scheduler - #15

Merged
takeokunn merged 101 commits into
mainfrom
p6-http3-native-client
Oct 27, 2026
Merged

takeokunn merged 101 commits into
mainfrom
p6-http3-native-client

Conversation

@takeokunn

@takeokunn takeokunn commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Relation to the HTTP/2 scheduler in PR #13

PR #13 adds an HTTP/2 connection mutex around stream setup and elects one caller to pump inbound frames. The native HTTP/3 scheduler uses a single-owner pump as well: the first request caller becomes the owner, and other callers enqueue and wait. QUIC assigns HTTP/3 stream IDs, so this scheduler does not reserve stream IDs under the HTTP/2 connection mutex.

The owner runs the connection loop synchronously until its request is complete and both the pending queue and active-stream table are empty. It opens pending streams, polls QUIC, services critical control and QPACK streams, and advances active request streams round-robin while respecting pending writes. Body and response callbacks run inline on the owner. A request submitted reentrantly from a callback is rejected.

The owner loop serializes scheduler-owned request QPACK preparation, HEADERS writing, and encoder commit. make-http3-client's :serialize callback defaults to invoking its thunk directly without a lock. Callers that mutate public QPACK state concurrently outside the owner loop must supply a :serialize callback that provides their synchronization.

Native scheduler callbacks and close behavior

Native scheduler operation requires poll-function, try-read-stream, and stream-write-pending-p together. try-write-stream is optional: when present, the scheduler uses it to submit request frames without waiting; when absent, it falls back to write-stream. Other synchronous operations continue to use write-stream.

close-http3-client closes HTTP/3 streams and stops the HTTP/3 scheduler, but leaves the underlying QUIC connection open. close-http3-quic-adapter closes HTTP/3 streams and then closes QUIC. Both return true after successful or already-completed close; a reentrant close by the close owner returns nil. If both HTTP/3 cleanup and QUIC close fail, close-http3-quic-adapter raises the HTTP/3 cleanup error and warns about the QUIC error. If only HTTP/3 cleanup fails and QUIC close succeeds, it raises the cleanup error. If only QUIC close fails, it raises the QUIC error.

Dependency pins

The root pins cl-crypto-kit at 761e9592 as a flake input so the updated cl-quic-kit input can follow the root crypto dependency. The cl-quic-kit pin advances to 46bfeacc for its public client-receive-frames(client, level, packet-number, frames) API, which lets tests supply frame batches at a selected packet number.

Scheduler test coverage

Scheduler tests exercise concurrent progress while one request streams a large upload; ownership handoff after the owner caller's deadline, unwind, interruption, and request error; QPACK-blocked request resumption; close waking all waiters with one condition; QUIC close failure preservation; and discovery of peer control and QPACK streams that arrive after setup. The late-stream test uses the optional accept-peer-unidirectional-stream callback supplied to make-http3-client.

Protocol-boundary tests verify that a response stream is not read while its QPACK section is blocked, duplicate control streams close QUIC while request-stream errors remain isolated, peer stream-count and prefix-byte limits apply before retention, and the public scheduler emits the dynamic-QPACK field section and encoder instructions.

The response-preservation test injects STOP_SENDING(H3_NO_ERROR) during a streaming request and verifies that the complete response is returned and further request writes stop.

Scheduler portability

The scheduler mutex and condition variable are available only under SBCL. Non-SBCL builds use no-op lock and wait wrappers, so concurrent callers are not serialized there. Scheduler concurrency tests use SBCL threads.

@takeokunn
takeokunn force-pushed the p6-http3-native-client branch from 15402f7 to ab28025 Compare October 25, 2026 04:20
@takeokunn
takeokunn force-pushed the p6-http3-native-client branch 8 times, most recently from 6ab6b8f to 5d0e0e6 Compare October 26, 2026 11:27
@takeokunn
takeokunn force-pushed the p6-http3-native-client branch from 5d0e0e6 to 61e5c09 Compare October 26, 2026 15:54
@takeokunn
takeokunn marked this pull request as ready for review October 27, 2026 07:39
@takeokunn
takeokunn merged commit f9d7ccd into main Oct 27, 2026
2 of 3 checks passed
@takeokunn
takeokunn deleted the p6-http3-native-client branch October 27, 2026 07:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant