fix: start each multipart part with its own header state - #1279
Open
pjfanning wants to merge 1 commit into
Open
Conversation
When a body part ends without a header-separating empty line, the
`BoundaryHeader` branch of `parseHeaderLines` emits that part and continues
into the next one, but passed its own `headers` and `headerCount` along. The
boundary starts a new part, so both should start empty -- `cth` was already
reset to `None` on the same call, which suggests the other two were simply
missed.
Two visible effects, both fixed here:
- the following part was reported carrying headers it never declared;
- `headerCount` was never reset either, so a run of such parts accumulated
towards `max-header-count` across parts rather than per part, failing
parts that are individually well within the limit.
The reset stays a direct self-call to `parseHeaderLines`: that call is what
makes the method `@tailrec`, and routing it through a helper would turn it
into an unoptimised mutual recursion.
Fixes apache#1278.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Motivation
Fixes #1278.
BodyPartParsercarried one body part's accumulatedheadersandheaderCountinto the next part whenever a part ended without a header-separating empty line. InparseHeaderLines, theBoundaryHeaderbranch emits the part it has just finished and then continues into the next one:The boundary starts a new part, so its header state should start empty.
cthwas already reset toNoneon that same call, which suggests the intent was to reset here and thatheaders/headerCountwere simply missed.This is long-standing and inherited from akka-http; it is not a regression.
Modification
Start the next part with a fresh
ListBufferand aheaderCountof 0.The reset deliberately stays a direct self-call to
parseHeaderLinesrather than being routed through a helper: that call is what makes the method@tailrec, and a mutual recursion here would be unoptimised — the stack overflow the trampoline inparseEntityalready guards against.Result
Two visible effects are fixed:
headerCountwas never reset either, so a run of such parts accumulated towardsmax-header-countacross parts rather than per part, failing parts individually well within the limit.Measured before the fix, for a three-part entity where each part declares one
Ageheader and no separating empty line — the parts inherit each other's headers:and with
max-header-count = 2, two parts of two headers each failed outright withmultipart part contains more than the configured limit of 2 headers.Tests
Three cases added to
MultipartUnmarshallersSpec(so each runs in both the CRLF and LF variants):max-header-count = 2);Content-Typeis not carried into the next part — this one already passed before the fix and is a regression guard for thecthreset that was already correct.Verified the first two fail on
mainand pass with the fix.sbt "http-tests/testOnly *MultipartUnmarshallersSpec* *MarshallingSpec *FileUploadDirectivesSpec"— 103 pass. Nativescalafmtclean. No MiMa run: the change is confined to a method body inside the anonymousGraphStageLogic.References
Fixes #1278. Noticed while reviewing #1266, which touches the same branch but does not change this behaviour; the two do not depend on each other, though they will conflict textually and whichever lands second will need a trivial rebase.
🤖 Generated with Claude Code