Repository navigation
Conversation
…their CONTINUATION, so the connection serves on A buffered request whose trailers came as HEADERS(END_STREAM) without END_HEADERS was dispatched at that HEADERS: HeadersDone was still true from the request's first header block. The trailer block was never decoded, the stream left _streams, and the CONTINUATION that followed found no stream and ended the connection with GOAWAY(PROTOCOL_ERROR) (#497). A HEADERS frame now clears HeadersDone before it appends its block, so trailers reopen the request's header block and the request waits for its END_HEADERS; HandleContinuation already decodes the block and completes the request.
This was referenced Nov 5, 2026
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What was wrong
ioxide.http2dispatched such a buffered request at the HEADERS:HeadersDonewas still true from the request's first header block, soTryCompletesaw both flags set. The trailer block, still incomplete, was never decoded.TryCompletealso took the stream out of_streams, so the CONTINUATION found no stream andHandleContinuationended the connection with GOAWAY(PROTOCOL_ERROR), and every other stream on it.Details are in #497.
The fix
HeadersDonebefore it appends its block. For trailers that reopens the request's header block, soTryCompletewaits for its END_HEADERS, andHandleContinuationdecodes it and completes the request, as it already does for a request's first block. For a request's first block the flag was already clear._discardBlock, which already follows CONTINUATION.Behaviour
API
None.
Tests
New in the Unit suite (
Http2TrailerTests.cs), over its hand-driven peer:h2 request: a buffered request's trailers split over HEADERS and CONTINUATION merge into it, and the connection serves on/uploadwith 400 B of DATA, thenx-checksumin HEADERS(END_STREAM) andx-digestin a CONTINUATION, then a GET to/next./uploadwith both trailers,/nexthas to be served, and the server must send no GOAWAY.Against main's
ioxide.http2(the test only):With the fix: Unit: 146 passed.
Bench
None: the change is one store per HEADERS frame.
Fixes #497.