docs(quic): OnRecieve and Abort, and serving a stream without a thread
The README's QUIC section gains receiving by callback: a server answering one request per stream with a size cap enforced by Abort, the threads the callbacks run on, what they may call, and the std::stop_callback rule for destroying the wrapper; its follower example now drops a lagging peer with Abort rather than a graceful Stop. The consumer notes give the full contract, the weak_ptr idiom for reaching a stream from its own callbacks, and list the callbacks among what runs on msquic's workers; the agent notes count the new test, describe the per-stream delivery in the partition list, and add the callbacks to the worker-thread gotcha. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
bdca27b871
commit
f29c4a83d2
3 changed files with 36 additions and 6 deletions
|
|
@ -28,7 +28,7 @@ Partitions and what they own:
|
|||
- `:URL`: `URL`, an absolute URL as RFC 3986 reads it (`Parse`), the resolution of a reference against it (`Resolve`, RFC 3986 §5.2, dot segments removed), the recomposition, and the pieces a request is built from. Inline, native only; what `ClientHTTP1::Fetch` follows a Location with.
|
||||
- `:HTTP1`: the RFC 9112 codec, inline: `SerializeRequest`, `SerializeResponse`, `SerializeContinue`, `MessageParser`, `MessageLimits`, `HTTP1ProtocolError`, and the framing it refuses to guess at.
|
||||
- `:ClientHTTP1`, `:ListenerHTTP1`: HTTP/1.1 over a `ByteStream&`. The listener is a `ListenerTCP` with a thread per accepted connection; the client keeps one pooled connection with a replay-once rule, and `ClientHTTP1::Fetch` is the URL fetch that follows redirects by RFC 9110 §15.4 with a client per hop (`FetchOptions`, `FetchResult`, `FetchStop`).
|
||||
- `:ClientQUIC`: `ClientQUIC` and `QUICStream` for both ends of a connection, `QUICSettings`, both QUIC credential structs, `QUICException`, `QUICClosedException`. The native unit holds the process-wide `MsQuicRuntime` (API table and registration, closed by a static destructor at exit), the stream registry with its refcounted handles, a `SendRequest` per send carrying its own outcome, the `CloseNotice` behind `OnClosed`, and the drain in `~ClientQUIC` (streams, then the connection's own `SHUTDOWN_COMPLETE`).
|
||||
- `:ClientQUIC`: `ClientQUIC` and `QUICStream` for both ends of a connection, `QUICSettings`, both QUIC credential structs, `QUICException`, `QUICClosedException`. The native unit holds the process-wide `MsQuicRuntime` (API table and registration, closed by a static destructor at exit), the stream registry with its refcounted handles, a `SendRequest` per send carrying its own outcome, the per-stream delivery of `OnRecieve`'s and `SendAsync`'s callbacks (whichever thread finds nobody delivering runs what is ready; the rest queue and return), the `CloseNotice` behind `OnClosed`, and the drain in `~ClientQUIC` (streams, then the connection's own `SHUTDOWN_COMPLETE`).
|
||||
- `:ListenerQUIC`: the msquic listener, its four `Listen*` variants, the self-signed certificate's on-disk cache (per user under `$XDG_CACHE_HOME/crafter-network/quic-cert/`, behind a lock; the OpenSSL work is module-internal functions of `:TLS`), `ComputeCertificateHashSHA256`, `GetSelfSignedCertificatePath`.
|
||||
- `:HTTP3`: QUIC varints, HTTP/3 frames, the QPACK static table, Huffman decoding, the control-stream preludes and the WebTransport stream prefixes. Native only: it throws for protocol errors.
|
||||
- `:ClientHTTP`, `:ListenerHTTP`: HTTP/3 on `:ClientQUIC` and `:ListenerQUIC`. The listener demuxes every bidirectional stream into an HTTP/3 request or a WebTransport data stream and accepts extended CONNECT when `wtRoutes` is non-empty.
|
||||
|
|
@ -51,7 +51,7 @@ crafter-build # native: TCP, TLS, HTTP/1.1, QUI
|
|||
crafter-build --no-quic # native without QUIC, HTTP/3, WebTransport and msquic
|
||||
crafter-build --local # Crafter.Thread from ../Crafter.Thread
|
||||
crafter-build --target=wasm32-wasip1 # the browser library
|
||||
crafter-build test # the suite, native: 37 tests
|
||||
crafter-build test # the suite, native: 38 tests
|
||||
crafter-build test 'Should*HTTP1' # a name glob
|
||||
crafter-build test --no-quic # the TCP, URL, TLS and HTTP/1.1 tests plus ShouldBuildWithoutQUIC: 19
|
||||
crafter-build test --list
|
||||
|
|
@ -122,7 +122,7 @@ Decided deliberately; change them only with the maintainer's say-so.
|
|||
- **Two self-signed certificates, both made in-process by `:TLS`.** `QUICServerCredentials::selfSigned` is kept per user at `$XDG_CACHE_HOME/crafter-network/quic-cert/` (else `~/.cache/...`, else `$XDG_RUNTIME_DIR/...`), reused across restarts until a day before it expires so a browser's pinned hash survives a restart, and shared by processes that start together through a record lock on `lock` there; the listener hands msquic the pair as an in-memory PKCS#12, not the paths. `TLSServerCredentials::selfSigned` is new every start and carries `keyEncipherment` the QUIC one lacks. `GetSelfSignedCertificatePath()` is the first, `GetSelfSignedCertificatePem()` the second. To start the QUIC one cold, delete that directory.
|
||||
- **`:TLS` declares module-internal functions** (`MakeWebTransportCertificatePem`, `CertificatePairExpiry`, `EncodePkcs12`, `CertificateSha256`) without `export`: `:ListenerQUIC` imports `:TLS` for them so libssl stays behind one partition, and a consumer never sees them.
|
||||
- **msquic's registration is closed by a static destructor that waits for every handle.** A test that returns from `main` with an accepted peer alive waits two minutes, the idle timeout; the tests `_Exit`. The exception, `ShouldSurviveConnectionChurn`, exists because a leaked stream handle once hung that close forever (`fix(quic)` in `git log`, with the `SHUTDOWN_ON_FAIL` story).
|
||||
- **Callbacks the interfaces say run "on the msquic worker" run as pool tasks**: `ClientQUIC::OnStream` and `OnDatagram` both enqueue. What arrives before a handler is set is queued: streams are delivered on `OnStream`; datagrams go to `RecieveDatagramSync`'s queue and never to a later `OnDatagram`. `OnClosed` and the native `QUICStream::SendAsync` callback are the exceptions: they do run on the worker, `OnClosed` inside `SHUTDOWN_COMPLETE` after the handle is closed (a pool task only when set after the close), `onSent` inside the send's `SEND_COMPLETE` (a pool task only for a send msquic never took), so that neither depends on a pool the host shares.
|
||||
- **Callbacks the interfaces say run "on the msquic worker" run as pool tasks**: `ClientQUIC::OnStream` and `OnDatagram` both enqueue. What arrives before a handler is set is queued: streams are delivered on `OnStream`; datagrams go to `RecieveDatagramSync`'s queue and never to a later `OnDatagram`. `OnClosed`, the native `QUICStream::SendAsync` callback and `OnRecieve`'s callbacks are the exceptions: they do run on the worker, `OnClosed` inside `SHUTDOWN_COMPLETE` after the handle is closed (a pool task only when set after the close), `onSent` inside the send's `SEND_COMPLETE` (a pool task only for a send msquic never took), `onData`/`onEnd` inside `RECEIVE` and the shutdown events (on the caller's thread for what `OnRecieve` finds waiting), so that none depends on a pool the host shares. A blocking call inside any of them stalls every connection on that worker.
|
||||
- **`~ClientQUIC` waits for the connection's `SHUTDOWN_COMPLETE`, up to 1 s, before `ConnectionClose`.** msquic sends `CONNECTION_CLOSE` from a `FLUSH_SEND` it queues while processing `ConnectionShutdown` and ignores once `ShutdownComplete` is set; a close queued right behind the shutdown completed it silently, and the peer only timed the connection out (`fix(quic)` in `git log`, `ShouldTellPeerOfCloseQUIC`). So a destructor on an msquic worker thread (inside a stream callback) blocks that second and then some; only `OnClosed`'s, which has nothing left to close, is free.
|
||||
- **`ListenerQUIC::ListenAsync*` run the accept loop on a `std::thread`**, not the pool as their comments say, and `ListenerHTTP::Listen` is `ListenSyncAsync` underneath, so a `ListenerHTTP`'s per-connection setup is a pool task. A saturated pool delays a new connection's SETTINGS; the early streams are buffered meanwhile (`ShouldNotDropEarlyStreams`).
|
||||
- **A `ClientQUIC` against an HTTP/3 server must drain the server's unidirectional streams** (control, QPACK encoder and decoder) with an `OnStream` reader, or they back up and msquic may abort the connection. `ClientHTTP` does it; a hand-rolled WebTransport client (`ShouldEchoWebTransport`) does it itself.
|
||||
|
|
|
|||
Loading…
Reference in a new issue