Skip to content

fix(net): read each IETF request header in its own task - #5208

Merged
kixelated merged 3 commits into
mainfrom
quest/m1/ietf-dispatch-headers
Oct 11, 2026
Merged

kixelated merged 3 commits into
mainfrom
quest/m1/ietf-dispatch-headers

Conversation

@kixelated

@kixelated kixelated commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

An incoming bidi stream that stalls before its type or body prevents run_dispatch from accepting later requests. QUIC can expose an earlier stream before its bytes arrive, so this can deadlock when later unread streams hold connection flow control.

Approach

Read and dispatch each request in an owned child task (Bidi::serve), mirroring the uni dispatcher. Preserve the distinction between abandoned streams and session-fatal header or dispatch errors. Main's AUTH requests (#4039) dispatch through the same per-request task, and the session's error still reaches the auth handle before the serve tasks drop. Add simulated-time coverage for silent types, stalled bodies, and malformed requests; remove the completed quest.

Decisions

  • Drop the receive-memory hold (maintainer, 2026-10-11). docs(quest): plan flow-controlled receive memory #5302 kept this PR held until quest/m1/quic/receive-memory.md bounds headers. Overruled: frame data can already retain the same ~625 MiB across concurrent streams, so request headers are not special and need no separate gating. Receive memory as a whole stays with that quest; its cross-reference to this quest is removed.
    • Keep the hold until receive memory lands
    • ✅ Land now; receive memory covers headers and frames together
  • Codex P1 (bound concurrent partial request headers): declined on the thread with the reasoning above.

Impact

Public API: none. Wire: none. An abandoned request remains a stream-local failure; malformed and unknown requests, including AUTH from a peer that did not negotiate it, still fail the session.

Alternatives

Keeping header reads in the accept loop with a timeout would retain head-of-line blocking and reject valid slow requests.

Validation

  • The silent-bidi regression fails on unchanged main and passes with the fix.
  • After merging main: all 597 ietf:: unit tests in moq-net pass, and quest check passes.
  • just check origin/main passed except two wall-clock relay tests under a loaded machine (shutdown_signal a_session_arriving_mid_drain_gets_what_is_left, drills relay_killed_mid_group_aborts_then_resumes::loopback, both connection timeouts); both pass when rerun alone.

(Written by GPT-6, updated by Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 2 commits October 10, 2026 08:10
Co-Authored-By: GPT-6 <noreply@openai.com>
Co-Authored-By: GPT-6 <noreply@openai.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Implemented with deterministic regression coverage. No public API or wire change, and no open implementation decisions. The separately tracked TS passthrough flake is recorded in validation; the remaining suites and feature gates pass. Suggested follow-ups are its existing quest and a release backport once this fix lands. This PR remains a draft as requested.

(Written by GPT-6)

@kixelated kixelated changed the title Read each IETF request header in its own task fix(net): read each IETF request header in its own task Oct 11, 2026
@kixelated
kixelated marked this pull request as ready for review October 11, 2026 01:39
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 152c76c9-dd8f-418e-b7f4-c992bed534a9



📥 Commits

Reviewing files that changed from the base of the PR and between 66c6344 and d0473d4.




📒 Files selected for processing (4)
  • quest/m1/README.md
  • quest/m1/ietf-dispatch-headers.md
  • quest/m1/quic/receive-memory.md
  • rs/moq-net/src/ietf/session.rs



💤 Files with no reviewable changes (3)
  • quest/m1/README.md
  • quest/m1/quic/receive-memory.md
  • quest/m1/ietf-dispatch-headers.md



Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review.





Walkthrough

run_dispatch accepts bidirectional streams while per-stream Bidi::serve tasks read and serve requests. A stream that ends before its header is ignored; malformed headers, dispatch failures, and unknown request types remain session-fatal. Tests cover dispatch behind stalled headers and fatal request errors. The related quest entry, planning document, and link were removed.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to d0473

Each bidirectional request now reads its header in its own task, so a stalled stream no longer blocks later requests. Malformed and unknown requests still end the session. No concrete merge-blocking risk remains.

Pre-merge checks | Passed 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 1 files.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly and concisely describes the main change: reading each IETF request header in its own task.
Description check Passed The description is directly related to the changeset and explains the problem, approach, decisions, impact, and validation results.

✨ Finishing Touches
✨ Simplify code
  • Commit to this branch
  • Create a new PR





  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T17:31:25.609594Z d0473d4 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dae30aaecf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

} = self;
let header = async {
let id = stream.reader.varint().await?;
let body = stream.reader.decode::<ietf::Body>().await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bound concurrent request-header buffering

When a peer opens many bidi streams and sends a declared body length followed by all but the final body byte on each, every spawned Bidi::serve retains its own Reader buffer indefinitely. Since ietf::Body permits 65,535 bytes and the default QUIC limit is 10,000 streams, one connection can move roughly 625 MiB out of the bounded transport receive window into application memory; previously, only the single header read by the accept loop could accumulate. Preserve concurrent type reads, but limit or reject excess in-progress headers without retaining every partial body, and exercise the stream-count slope.

AGENTS.md reference: AGENTS.md:L37-L37

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declining for this PR, per the maintainer's 2026-10-11 decision. Request headers aren't special here: a peer can already park the same ~625 MiB in application memory with partial frame data across 10k concurrent streams, so a header-only quota would gate this fix on a bound that the rest of the receive path doesn't have. Receive memory as a whole, headers and frames alike, is covered by quest/m1/quic/receive-memory.md, which holds connection credit until each unit is parsed and cancels streams by priority under pressure. This PR keeps its scope: no head-of-line blocking on bidi request headers.

(Written by Claude Opus 5.5)

Carries main's AUTH dispatch into the per-request Bidi task and drops the receive-memory hold on this quest, per the maintainer's 2026-10-11 decision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed d0473d4 against the current base, including all four changed files and the rebased AUTH dispatch/lifetime handling. No new actionable findings.

Direction: per-stream header reads remove head-of-line blocking without a timeout workaround. The fatal-error handoff preserves session errors, including closing the AUTH handle before its serving tasks drop. No public API or wire-format change identified.

Known risk remains: the existing concurrent partial-header buffering finding is deferred, not fixed. The maintainer-account reply explicitly lifts the hold and leaves headers and frames to the shared receive-memory work. This review does not claim bounded aggregate header memory.

Verification: static GitHub review only; no tests, memory benchmarks, or interop runs performed independently. Current-head Check, Platform, WASM and Android workflows were still running.

(Written by OpenAI)

@kixelated

Copy link
Copy Markdown
Collaborator Author

@codex review

Head: d0473d4. Receive-memory bounds are intentionally out of scope (see the PR description and the earlier thread).

(Written by Claude Opus 5.5)

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: d0473d4c05

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@kixelated

Copy link
Copy Markdown
Collaborator Author

Merge summary for d0473d4.

Changes: each incoming bidi request is read and dispatched in its own task (Bidi::serve), mirroring the uni dispatcher, so a stalled request header no longer blocks later requests. Abandoned streams stay stream-local; malformed, unknown, or unnegotiated (AUTH) requests still fail the session. Main is merged in, and AUTH requests (#4039) use the same path.

Decisions:

  • Receive-memory hold dropped (maintainer, 2026-10-11). Header and frame memory are both covered by quest/m1/quic/receive-memory.md.
  • Codex P1 on bounding partial headers declined for the same reason.
  • Release backport declined (maintainer).

Validation: CI is green, and the Codex review of d0473d4 found no issues.

(Written by Claude Opus 5.5)

@kixelated
kixelated added this pull request to the merge queue Oct 11, 2026
Merged via the queue into main with commit ddadaed Oct 11, 2026
8 checks passed
@kixelated
kixelated deleted the quest/m1/ietf-dispatch-headers branch October 11, 2026 17:53
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