Repository navigation
Linearize only the header block of multi-segment input - #86
Merged
Merged
Conversation
The ReadOnlySequence entry points (UltraHardenedParser requests and responses, FlexibleParser) found the CRLFCRLF and then copied the whole sequence with ToArray() - any pipelined body included - before MaxTotalHeaderBytes was checked, which only happened inside the ROM parse. A tiny header followed by a huge pipelined body therefore forced an allocation the size of the body. The reader that located the terminator already knows the header block's length (reader.Consumed). The hardened entry points now reject a block over MaxTotalHeaderBytes with the same 431 before copying anything, and all three copy input.Slice(0, reader.Consumed) instead of the whole sequence. The ROM parse never reads past the terminator, so the parsed fields and bytesReadCount are unchanged. Fixes #65.
This was referenced Nov 2, 2026
Merged
# Conflicts: # src/Glyph11/Parser/FlexibleParser/FlexibleParser.TryExtractFullHeader.cs # src/Glyph11/Parser/UltraHardened/UltraHardenedParser.TryExtractFullHeader.cs # src/Glyph11/Parser/UltraHardened/UltraHardenedParser.TryExtractFullResponseHeader.cs
MDA2AV
added a commit
that referenced
this pull request
Nov 3, 2026
Describes the library as it is once #74-#86 are merged: requests and responses through TryParseRequestHeader / TryParseResponseHeader, the body-framing helpers, chunked decoding (partial chunks, the trailer cap), every ParserLimits field with its default and status code (including AllowBareLf and MaxTotalTrailerBytes), the two parser tiers, a summary of the hardening with a link to SECURITY.md, the tests, and what changes when migrating to 0.4.0. Glyph11.Native and Glyph11.Pico are marked discontinued. Also fixes the chunked example, which listed a ChunkResult.Error that does not exist (malformed framing throws).
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.
Fixes #65
Why
The
ReadOnlySequenceentry points found the CRLFCRLF, then copied the whole sequence withToArray()— any pipelined body included — and only then parsed, which is whereMaxTotalHeaderByteswas checked. A tiny header followed by a large pipelined body forced an allocation the size of the body, per parse:UltraHardenedParser.TryExtractFullHeaderValidated(requests)UltraHardenedParser.TryExtractFullResponseHeaderValidated(responses)FlexibleParser.TryExtractFullHeaderWhat changes
The
SequenceReaderthat located the terminator already knows the header block's length:reader.Consumed, CRLFCRLF included. So:MaxTotalHeaderByteswith the same 431 ("Total header size exceeds limit.") before copying anything;input.Slice(0, reader.Consumed)instead of the whole sequence.The ROM parse never reads past the terminator, so parsed fields and
bytesReadCountare unchanged; only the size of the copy changes. The XML summaries that said "linearized viaToArray()" now say what is copied.Out of scope, possible follow-up: a header section that never terminates is still buffered by the caller and rescanned on each call (single- and multi-segment alike). Rejecting an unterminated buffer that has already reached
MaxTotalHeaderByteswould bound that too, but it changes the incomplete path of both entry points, so it is not folded in here.Overlap with sibling PRs
TryReadPastHeaderEnd) and advances the samereaderpast a bare-LF block's\n\n, so this copy covers bare-LF blocks too. A trial merge of the two branches is conflict-free and passes all 493 tests.Tests
New
tests/Tests/MultiSegment.HeaderCopy.cs(10 cases), for requests, responses andFlexibleParser:MemoryMarshal.TryGetArray) is exactly the header length, and the thread allocates < 64 KiB for the call;MaxTotalHeaderBytes: 431, with < 1 MiB allocated for the call (so not the 8 MiB body); exactly at the limit: parses.9 of the 10 fail against
main(the exact-limit case passes on both). 413 passing, 0 failing (net10.0); the solution builds for net8.0/net9.0/net10.0.