A stream dropped once the relay has asked to close a WebTransport session
would send its FIN or STOP_SENDING at once, ahead of the WT_CLOSE_SESSION
capsule. In a browser on the build host, a page whose stream was stopped
just before the close lost its renderer (reproduced on loopback: a page
writing a stream the relay refused crashed in most runs). The binding now
keeps such streams (WtSend, WtRecv hold the connection's Closing) until the
QUIC connection has closed, at most ten seconds, when they end with nothing
more on the wire; read_setup keeps the stream it refuses in `early`, so it
too is dropped after the close.
tests/webtransport_close.rs: a stream that breaks a rule before SETUP is
refused with PROTOCOL_VIOLATION, and the client reads the close and no
STOP_SENDING ahead of it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
WtConnection::close closes the WebTransport session (moq-transport-22 §6.6):
the MOQT code is the WT_CLOSE_SESSION capsule's 32-bit application error
code, unmapped, with the reason. closed() reads a peer's capsule as its code
and reason (by_peer); a QUIC close with the session still open gives no
MOQT code (u64::MAX), since its code is HTTP/3's.
tests/webtransport_close.rs: the relay refusing a grant without its proof,
refusing a session past its count, and ending one at its time limit, each
read by the client as the exact code and reason; a client's close recorded
as the peer's in the relay's summary. The browser e2e check of a bare grant
now requires code 2 and the reason over WebTransport too.
CI: the vendored crate's unit tests in the relay job; the browser and room
workflows also run on a change under vendor/. README and CHANGELOG.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Connection::close_session(code, reason) has the driver's worker write one
DATA frame holding a WT_CLOSE_SESSION capsule (draft-ietf-webtrans-http3-16
§6) on the CONNECT stream at the highest send priority, FIN it, and wait at
most two seconds for the peer to close its side before closing the QUIC
connection with H3_NO_ERROR, so the connection's close cannot overtake the
capsule. The reason is cut to 1024 bytes at a character boundary. After it,
this end opens no stream and sends no datagram; a close asked for just as
the last handle drops still goes out.
A session the peer closes is ConnectionError::SessionClosed (the release
reported it as ApplicationClosed, the variant of the QUIC connection's
close, whose code under HTTP/3 is an HTTP/3 error code), and
Connection::closed() reports the session's end as the driver saw it.
Unit tests of the capsule's bytes against the draft's layout. The
upstream's examples and dev-dependencies are left out so the crate's unit
tests run from the workspace (cargo test -p wtransport --lib). VENDORED.md
lists every change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The crate exactly as published (its .crate's SHA-256 is the lock's checksum,
b4273ce3...), with the upstream's MIT and Apache-2.0 licence files from the
commit its .cargo_vcs_info.json names. Patched into the joule-code workspace
and into joule-moq-room, a workspace of its own that builds the relay; both
locks lose only the registry source and checksum. check-lockfiles.sh skips
the vendored crate's own lock, as it does the other vendored tree's.
No change to the crate yet: the next commit adds the session close.
Co-Authored-By: Claude Opus 5.5 <[email protected]>