Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ transport, benchmark tooling); worktrees isolate commits, not semantics.
- [Close codes](/quest/m1/close-codes.md) - a client sees the peer's application close code over WebSocket and raw QUIC, like WebTransport
- [Raw stream codes](/quest/m1/raw-stream-codes.md) - raw QUIC stream resets and stops carry the application's code, not an HTTP/3-mapped one
- [WebTransport close upstream](/quest/m1/wt-close-upstream.md) - web-transport-moq delivers the close capsule itself, and moq-tokio's `CLOSE_LINGER` is deleted
- [Browser close code](/quest/m1/browser-close-code.md) - a playwright case proves the page reads a relay's close code and reason, on every web-transport backend
- [Watch refusal](/quest/m1/watch-refusal.md) - `<moq-watch>` shows an origin refusal as an error instead of sitting offline
- [Resumed groups](/quest/m1/resume-latest.md) - a half-delivered group ends once the new copy is past it, so a group-only reader never parks after a mid-group failover
- [SUBSCRIBE_DROP](/quest/m1/subscribe-drop.md) - every stream group in a lite subscription arrives or is dropped by name, and lite-07 drops its stream count for it
Expand Down
35 changes: 35 additions & 0 deletions quest/m1/browser-close-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# [M] A browser sees the close code on every WebTransport backend

## Goal

A browser interop case proves a relay's rejection reaches the page: the relay
closes the session with an application code and reason, and the page reads
both from `WebTransport.closed`. Every `moq-dev/web-transport` backend that
speaks WebTransport (noq, quinn, iroh) delivers the close capsule, verified by
that case or an equivalent test, and any that doesn't is fixed upstream.

## Plan

[WebTransport close upstream](/quest/m1/wt-close-upstream.md) fixed
`web-transport-moq`'s close path with a Rust regression test, but only
Chromium treats the H3 control stream ending early as fatal, so no Rust peer
proves the browser path. The playwright interop harness in `test/interop/`
has no close-code scenario yet.

- Add the browser case: a relay (or `Request::reject`) closes with a code, and
the page asserts the code and reason. It should fail with the capsule fix
reverted.
- Audit `web-transport-noq`, `-quinn`, and `-iroh` for the same bug: a close
capsule sent from a task that doesn't keep the H3 control streams alive.
`web-transport-iroh` 0.7 sends no capsule at all, only a QUIC close; decide
there whether iroh's WebTransport path should send one.
- Fix upstream, release, and bump the pins, once wt-close finishes.

Decided with the maintainer: the browser case and the backend audit ship
together, since the case is what proves each backend.

Public API: none. Wire: none, unless iroh starts sending the capsule.

## Required

- [WebTransport close upstream](/quest/m1/wt-close-upstream.md) - the noq fix and pin bump the browser case needs
34 changes: 22 additions & 12 deletions quest/m1/wt-close-upstream.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# [S] WebTransport close delivers its capsule upstream
# [XS] WebTransport close delivers its capsule upstream

## Goal

Expand All @@ -20,22 +20,32 @@ detached task until `closed()` resolves, capped at a 10s `CLOSE_LINGER`.

Decided 2026-09-29: fix it at the source and remove the timeout workaround.

- In moq-dev/noq, the close path keeps whatever the capsule needs alive until
it is delivered, without the caller's help. Its regression test drops the
session right after `close()` and asserts the peer reads the capsule
before the H3 control stream ends; it must fail on 1.3.2. A Rust peer
alone proves nothing: `session_close_surfaces_a_rejection_code` in
`rs/moq-tokio/tests/broadcast.rs` passed on 1.3.2 without the linger,
because only Chromium treats the control stream's end as fatal. Add a
browser check too if the playwright harness makes it cheap.
- Release `web-transport-moq` 1.3.x and bump the pin on `main` (2.x on `dev`
if it has moved).
The upstream fix makes the close task own a clone of the whole session:
[moq-dev/noq#23](https://github.com/moq-dev/noq/pull/23) for 2.x (`dev`) and
its backport [moq-dev/noq#24](https://github.com/moq-dev/noq/pull/24) for
1.3.x (`main`). Their `web-transport-moq/tests/close_capsule.rs` drops the
session right after `close()` against a hand-rolled HTTP/3 peer and fails on
1.3.2 and 2.0.0.

Remaining, once the releases carrying them are published:

- Bump the `web-transport-moq` pin on `main` (and on `dev` if 2.0.x lands
first).
- Delete `CLOSE_LINGER`, its task, and its mock-session tests from
moq-tokio.
moq-tokio. They are exactly what #4429 added, so reverting it is enough.

A browser check is left to
[Browser close code](/quest/m1/browser-close-code.md): the playwright harness
has no close-code scenario, so it is not cheap to add here.

Public API: none. Wire: none.

## Required

- A `web-transport-moq` 1.3.x release that carries moq-dev/noq#24

## Related

- [Browser close code](/quest/m1/browser-close-code.md) - the playwright case that proves Chromium reads the code this quest unblocks
- [Close codes on every transport](/quest/m1/close-codes.md) - the same symptom over qmux and raw QUIC
- [UnknownSession log flood](/quest/m1/unknown-session-logs.md) - another `web-transport-moq` release and pin bump