Skip to content

Draft-20 backlog: session-layer request-stream rules - #116

Merged
floatdrop merged 2 commits into
draft-20from
draft20-backlog-session
Sep 27, 2026
Merged

floatdrop merged 2 commits into
draft-20from
draft20-backlog-session

Conversation

@floatdrop

Copy link
Copy Markdown
Owner

The session-layer items of the draft-20 backlog. There is one commit per item, and each was reviewed by moqt-reviewer. Every test was seen failing before its fix.

Commits

  • 8f8cbec Close the session on a REQUEST_OK or REQUEST_ERROR that answers no REQUEST_UPDATE (§10.9).
    • On a request stream, such a message can only answer a REQUEST_UPDATE this side sent. Otherwise the session is now closed with PROTOCOL_VIOLATION.
    • On a request this side sent, it is a second response (§5.1, §5.2, §6.2). This was already enforced for SUBSCRIBE and PUBLISH; it now also covers FETCH and the namespace requests.
    • On a stream this side answered, only the requester sends REQUEST_UPDATE (§10.9), except that a PUBLISH's subscriber may send one.
    • The rule applies to every RequestBroker, including accept-side ones, and to the relay's readRequestStream. That supersedes the forwarded-PUBLISH-only check.
    • NewRequestBroker's doc now says a request's own response must be read before a broker attaches.
  • 6fdf6bc Keep awaiting a request's response past an early GOAWAY (§10.4).
    • A GOAWAY before the response is checked: a second GOAWAY, or a New Session URI sent to a server, closes the session.
    • The request then keeps waiting for its response. The GOAWAY is put back as the stream's first follow-up, so readers handle it exactly like one sent after the response.
    • No new API.

Decisions and interpretations

  • Stray response closes the session. This is the maintainer's decision. The draft names no rule for such a message on a stream this side answered. It reverses cc1a999, which passed the message to the application.
  • Early GOAWAY keeps waiting, rather than returning a typed error. This is the maintainer's decision.
  • PUBLISH_NAMESPACE, SUBSCRIBE_NAMESPACE and SUBSCRIBE_TRACKS are excluded from the early-GOAWAY handling. Their response MUST be "the first message" on the stream (§6.1, §6.2). So an early GOAWAY there still fails the request as before; for the two subscription requests it also closes the session.

Verification

  • go test ./... and golangci-lint run pass.
  • go test -race passes for ./pkg/moqt/session/... and ./pkg/relay/....
  • make bench-quick shows ControlRoundTrip allocs/op unchanged.

🤖 Generated with Claude Code

floatdrop and others added 2 commits September 27, 2026 20:27
…R that answers no REQUEST_UPDATE (§10.9)

On a request stream, a REQUEST_OK or REQUEST_ERROR can only answer a
REQUEST_UPDATE this side sent (§10.9). Otherwise it answers nothing, and the
session is now closed with PROTOCOL_VIOLATION:
- on a request this side sent, it is a second response (§5.1, §5.2, §6.2).
  RequestBroker already closed on this for SUBSCRIBE and PUBLISH; it now
  does for FETCH and the namespace requests too;
- on a stream this side answered, only the requester sends REQUEST_UPDATE,
  bar a PUBLISH's subscriber (§10.9). The draft names no rule for a response
  from the requester there. Closing is the maintainer's decision, and
  reverses cc1a999's TestResponderStreamResponseKeepsSession, which passed
  such a response to Serve's callback.

The same rule covers every broker, accept-side ones included, and the
relay's readRequestStream: the relay sends no REQUEST_UPDATE on the SUBSCRIBE,
FETCH, namespace and forwarded-PUBLISH streams it reads there. That
supersedes the forwarded-PUBLISH-only check in readSubscribeUpdates and its
`forwarded` parameter.

NewRequestBroker's doc now states that a request's own response must be read
before a broker attaches to its stream.

Tests, each verified red first: TestResponderStreamStrayResponseCloses (an
accepted SUBSCRIBE and PUBLISH), TestRequesterStrayResponseCloses (this
side's FETCH and SUBSCRIBE_NAMESPACE), and TestRelay_StrayResponseCloses
(SUBSCRIBE, FETCH, PUBLISH, PUBLISH_NAMESPACE, SUBSCRIBE_NAMESPACE and
SUBSCRIBE_TRACKS, each with both messages).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… (§10.4)

"A GOAWAY MAY also be sent on a request stream to initiate migration of that
individual request" (§10.4), before the request's response too. The
requester failed the request instead ("unexpected GOAWAY in … response").

awaitRequestResponse now:
- checks such a GOAWAY as any on the stream is. A New Session URI sent to
  the server, or a second GOAWAY before the response, closes the session
  with PROTOCOL_VIOLATION;
- keeps reading for the response;
- puts the GOAWAY back at the front of the stream (replayStream), so the
  stream's next reader, a broker's Serve or a direct message.Parse, gets it
  as its first follow-up, just as one sent after the response. A later
  second GOAWAY is still caught there.

Not for PUBLISH_NAMESPACE, SUBSCRIBE_NAMESPACE or SUBSCRIBE_TRACKS, whose
response MUST be "the first message" on the stream (§6.1, §6.2). Those keep
their existing handling: the request fails, and for the two subscription
requests the session also closes.

No new API; a handle's Stream is the wrapper after an early GOAWAY.

Tests, written first and seen red: TestEarlyRequestGoawayThenResponse
(SUBSCRIBE through its broker, FETCH by direct read),
...ThenRejection, ...ViolationCloses (two before the response, one before
and one after, a URI to the server) and ...OnPublishNamespace.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@floatdrop
floatdrop merged commit 45cace3 into draft-20 Sep 27, 2026
11 checks passed
@floatdrop
floatdrop deleted the draft20-backlog-session branch September 27, 2026 17:34
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