Repository navigation
quest(m1): import #5257-#5259, #5347, #5348 #5358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| # [M] Malformed moq-lite input closes the session | ||
|
|
||
| ## Goal | ||
|
|
||
| On moq-lite, a peer that breaks a control-stream rule gets its session | ||
| closed with PROTOCOL_VIOLATION, in both `rs/moq-net` and `js/net`, and a | ||
| Report-level publisher answers PROBE targets instead of resetting the stream. | ||
| The rules are unchanged from lite-06 through lite-07-wip, so the fixes apply | ||
| to every lite version. Data-stream (FRAME) decode errors stay per-stream, as | ||
| the draft says, and a reset or FIN is never a violation. | ||
|
|
||
| ## Plan | ||
|
|
||
| Found by an external interop runner and verified against the code 2026-10-12. | ||
| Rust-to-Rust and Rust-to-JS sessions never hit these; third-party peers do. | ||
|
|
||
| - SETUP (#5257). A client that receives Path or Role in the server's SETUP | ||
| closes with PROTOCOL_VIOLATION. `lite/setup.rs` decodes Role through | ||
| `Role::from_code`, which turns 0 and unknown values into `None`, so keep | ||
| the parameter's presence; a server still reads an unknown Role as Both. | ||
| Check at the stored peer SETUP (`lite/subscriber.rs` `UniState::Setup`, | ||
| `js/net/src/lite/connection.ts`), which already closes on a | ||
| ProtocolViolation. Decided 2026-10-12: also make the server close on Path | ||
| over a binding whose handshake carries a request URI (WebTransport, | ||
| WebSocket), since the draft sentence is the same. | ||
| - PROBE (#5258). `ProbeServe::poll_probe` in `lite/publisher.rs` polls | ||
| `reader.poll_closed`, which fails on any pending byte, so the first target | ||
| resets the stream with MALFORMED_TRACK. Decode targets with `lite::Probe` | ||
| instead; at Report level ignore the value and keep reporting, and only FIN | ||
| or a reset ends the stream. In JS, `runProbe` never reads the stream, so a | ||
| buffered target probably hides the subscriber's FIN; read targets there | ||
| too. Decided 2026-10-12: our subscribers keep sending no target. Unit-test | ||
| the publishers; the Increase-level response belongs to | ||
| [Discover capacity](/quest/m1/quic-probe.md). | ||
| - Control decode errors (#5259). Decided 2026-10-12: any decode error on a | ||
| control stream closes the session with PROTOCOL_VIOLATION, not just the two | ||
| cases cited (GOAWAY URI over 8,192 bytes, Message Length mismatch). | ||
| `Control::poll` closes on `Error::Decode(_)` instead of logging. Delete the | ||
| comment that a malformed GOAWAY must not tear down a draining session: the | ||
| draft says MUST. Do the same in JS `#runBidis`, which closes today only on | ||
| `ProtocolViolation`. Also enforce that a server receiving a non-empty | ||
| GOAWAY URI closes the session. Land the PROBE fix first or in the same PR, | ||
| or a third-party PROBE target would close the whole session. | ||
| - The rule reaches every control decoder, not only `Control::poll` and | ||
| `#runBidis`. Child handlers that swallow a peer's decode error must | ||
| propagate it: `RequestServe::poll` resets and returns `Ok(())` for a | ||
| malformed SUBSCRIBE_UPDATE decoded after `RequestState::Decode`, and | ||
| `ProbeServe::poll` consumes `poll_probe`'s error. Response decoders on | ||
| streams we opened count too: Rust `RecvBandwidth::poll` logs a malformed | ||
| PROBE report from `ProbeStream::poll`, and JS `Subscriber.runProbe` catches | ||
| it. Prefer one shared close path over per-handler fixes. | ||
| - One test per rule per language, driving raw bytes like the runner does, | ||
| including a malformed SUBSCRIBE_UPDATE and a malformed PROBE report. | ||
| Share the session-close helper from | ||
| [AUTH protocol violations](/quest/m1/auth/violations.md) if it lands first. | ||
|
|
||
| Public API: none. Wire: none; the behavior now matches the draft. | ||
|
|
||
| ## Closes | ||
|
|
||
| - [#5257](https://github.com/moq-dev/moq/issues/5257) - client accepts Path or Role in the server's SETUP | ||
| - [#5258](https://github.com/moq-dev/moq/issues/5258) - Report-level publisher resets the Probe Stream on a target | ||
| - [#5259](https://github.com/moq-dev/moq/issues/5259) - control decode errors end only the stream | ||
|
|
||
| ## Related | ||
|
|
||
| - [Malformed moq-transport input](/quest/m2/ietf-malformed-close.md) - the same rule for draft-18 and draft-21 | ||
| - [AUTH protocol violations](/quest/m1/auth/violations.md) - the AUTH-only slice, in progress | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| # [S] A FETCH on release never stalls on a parked open group | ||
|
|
||
| ## Goal | ||
|
|
||
| On the 0.17 `release` line, a FETCH of a group that a live subscription | ||
| delivered only in part, after that subscription ended, receives every frame | ||
| the publisher writes and ends with the group. Release only: `main` deleted the | ||
| warm copy in #4741 and is unaffected. Lands with the next scheduled 0.17.x; no | ||
| dedicated cut. | ||
|
|
||
| ## Plan | ||
|
|
||
| Reported in #5348 and bisected to b425d430f (the #4922 backport). Traced | ||
| 2026-10-12; the pin check the commit message mentions is not the cause. | ||
|
|
||
| - Mechanism: the backport puts every viewer session on one front, so viewer | ||
| B's FETCH joins viewer A's parked front. When A's subscription ends | ||
| mid-group, `Action::Park` runs `warm_copy`, and `warm_rebuild` copies the | ||
| open live-edge group into a new group that it only finishes if the live one | ||
| finished. The upstream subscription is already cancelled, so nothing ever | ||
| writes to that group, and it is not aborted until `TRACK_IDLE_LINGER`. | ||
| `resume::Consumer::cached_group` then reaches `track::covering_group`, which | ||
| accepts any group that isn't aborted, finished or not. The FETCH gets the | ||
| cached frames and then waits forever. | ||
| - Decided 2026-10-12: an unfinished group in a warm segment is a cache miss | ||
| for FETCH. `cached_group` (`model/resume.rs`) skips it, and | ||
| `Fetching::poll_latch` does not latch onto a warm newest segment. The | ||
| pending fetch then marks the track used, the front re-splices, and the | ||
| FETCH goes upstream on the new copy (`pending_fetch_follows_a_newer_segment` | ||
| already covers that last hop). Rejected: a spliced `resume::Group` that | ||
| continues the open warm group, since it is more code on a line `main` already | ||
| deleted; and aborting open groups at park, since it breaks a returning | ||
| subscriber continuing an open warm group. | ||
| - Regression in release `model/origin.rs` tests, modelled on | ||
| `returning_reader_continues_an_open_warm_group` with `served_front()`: | ||
| subscribe, write two frames into open group 0, read one, drop the | ||
| subscriber and wait for the park, then `fetch_group(0, None)`. Expect a new | ||
| upstream track request that serves the whole group, and the fetch ends with | ||
| every frame. | ||
| - The PR targets `release` directly (CONTRIBUTING's backport flow), since | ||
| there is nothing to land on `main` first. | ||
|
|
||
| Public API: none. Wire: none. | ||
|
|
||
| ## Closes | ||
|
|
||
| - [#5348](https://github.com/moq-dev/moq/issues/5348) - FETCH of a partly delivered group stalls on 0.17.3 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The named hooks cover only control streams accepted from the peer, so malformed responses on locally opened streams still bypass this fix. For example, Rust
ProbeStream::pollreturns the decode error toRecvBandwidth::poll, which logs and swallows it, while JSSubscriber.runProbelikewise catches and logs malformed PROBE reports without closing the session. Include response-side decoders and regression tests, otherwise the stated rule that any control decode error closes the session remains false for publisher-to-subscriber traffic.AGENTS.md reference: AGENTS.md:L16-L18
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. b68e886 extends the rule to response decoders on locally opened streams (Rust RecvBandwidth/ProbeStream, JS Subscriber.runProbe), with a malformed PROBE report regression.
(Written by Claude Opus 5.5)