🐛 Ignore 1xx interim responses in http2_adapter - #2601
Conversation
A `103 Early Hints` (or any 1xx) HEADERS frame was treated as the final response: the adapter parsed its `:status`, completed the response `Completer`, and then completed the same `Completer` again when the real final HEADERS frame arrived, crashing the isolate with `Bad state: Future already completed`. Interim headers also leaked into the final response because `responseHeaders` was never reset between frames. Skip 1xx responses instead of completing on them, and reset the accumulated headers per HEADERS frame so interim headers do not carry over into the final response. Closes cfug#2600 Co-Authored-By: Claude <noreply@anthropic.com>
|
Review notes — both non-blocking, verified locally (the regression test reproduces the exact 1. Trailers are now deterministically dropped — please disclose it and add a hardening testA trailer section arrives as a HEADERS frame without
Proper trailer support is an API addition — opened as #2602. 2. Malformed
|
New Pull Request Checklist
mainbranch to avoid conflicts (via merge from master or rebase)CHANGELOG.mdin the corresponding packageAdditional context and info (if any)
Closes #2600.
Problem. When an HTTP/2 server sends a
103 Early Hints(or any 1xx)HEADERS frame before the final response,
Http2Adapter._fetchtreated theinterim frame as final: it parsed the
103:status, completed the responseCompleter, and then completed the sameCompleteragain when the real200HEADERS frame arrived on the same stream, crashing the isolate withBad state: Future already completed. As a secondary effect,responseHeaderswas never reset between HEADERS frames, so interim headers (e.g.
link) leakedinto the final response.
Fix. In the HEADERS-frame handler, 1xx responses are now skipped (the
handler returns without completing), and the accumulated response headers are
reset per final HEADERS frame so interim headers don't carry over. Single-frame
responses (the common case) are unaffected.
Note (sensitive area — header handling, §3). This touches header handling,
so I kept the change minimal and additive: no public API change, no signature
change, default behavior for normal single-HEADERS-frame responses is
identical.
Verification. Added a regression test (
plugins/http2_adapter/test/early_hints_test.dart)that drives a local h2c server emitting
103then200on the same stream andasserts the request resolves with
200, bodyhello, and that the interimlinkheader does not leak. Confirmed genuine RED→GREEN: reverting only thesource change makes the test fail with the exact
Bad state: Future already completedcrash; with the fix it passes.redirect_test.dart(14 tests, sameHEADERS-frame path) and
headers_test.dartstill pass;dart analyzeanddart formatare clean on both changed files.AI disclosure (§8.3): implementation and tests were produced with Claude; a human owns and has reviewed the change.