Repository navigation
fix(http2): handle CONTINUATION frames and padding - #6399
Open
binaryfire wants to merge 2 commits into
Open
binaryfire wants to merge 2 commits into
binaryfire wants to merge 2 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #6399 +/- ##
==========================================
- Coverage 84.08% 84.03% -0.05%
==========================================
Files 115 115
Lines 20237 20237
Branches 3637 3637
==========================================
- Hits 17016 17007 -9
- Misses 3221 3230 +9 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The HTTP/2 client only parsed a header block sent in one unpadded HEADERS frame: - CONTINUATION frames were ignored and every HEADERS frame was inflated as a complete block. A server must split a header block larger than the client's 16 KB max frame size, and nginx does, so those responses failed with an HPACK error. - The pad length and padding of a PADDED HEADERS frame went to HPACK. - The PRIORITY fields were skipped without a length check, so a HEADERS frame shorter than 5 bytes made the decoder read past the buffer. - The pad length and padding of a PADDED DATA frame ended up in the body. Header block fragments are now inflated as they arrive, with END_HEADERS marking the last one, as the server does. The response is returned once the block is complete, using END_STREAM from the HEADERS frame. Any other frame inside a block, or a CONTINUATION frame outside one, closes the connection with PROTOCOL_ERROR (RFC 9113 section 6.10). Padding is removed from HEADERS and DATA frames. Padding that doesn't fit in the frame, or a HEADERS frame too short for its priority fields, closes the connection. Flow control still counts the whole DATA frame.
The HTTP/2 server removed the padding from HEADERS frames but not from DATA frames, so when a client padded DATA (nghttp -b, for example) the pad length byte and the padding ended up in the request body. Remove it the same way as for HEADERS, and close the connection when the padding doesn't fit in the frame (RFC 9113 section 6.1). Flow control still counts the whole frame.
binaryfire
force-pushed
the
fix/http2-padding-continuation
branch
from
November 12, 2026 04:17
82cad71 to
89651ea
Compare
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.
Swoole\Coroutine\Http2\Clientcan only read a header block that arrives in a single, unpadded HEADERS frame, and both the client and the server keep the padding of DATA frames in the body.Client
recv()fails with -523 (HPACK error).nghttpd -b 255) fail the same way. In DATA frames they end up in the response body.Header block fragments are now decoded as they arrive, with END_HEADERS marking the last one, the same way the server does it. The client waits for END_HEADERS before it finishes a header block, and keeps END_STREAM from the HEADERS frame that started it. Padding is removed from HEADERS and DATA frames. DATA padding still counts toward flow control. The connection is closed with PROTOCOL_ERROR or FRAME_SIZE_ERROR on:
Server
The server already removes the padding from HEADERS frames but not from DATA frames, so when a client pads DATA (such as
nghttp -b 255) the padding ends up in$request->rawContent(). DATA is now handled the same way as HEADERS. #6389 also changes this DATA case, so whichever lands second needs a small rebase.Tests
continuation_frames.phptandpadded_frames.phptdrive the client from a raw socket peer, including the error cases and a WINDOW_UPDATE check that padding still counts.padded_data.phptcovers the server in BASE, PROCESS and coroutine mode. All three fail on master. I also checked the client against nginx (large headers) andnghttpd -b 255and the server againstnghttp -b 255, and ran the new tests under Valgrind and on a ZTS thread build, plus a THREAD mode server probe.Hypervel's HTTP/2 and gRPC clients use
Coroutine\Http2\Client, and this broke its CONTINUATION regression test.