parse Content-Length as digits with surrounding whitespace only (#1221) - #1243
Merged
Conversation
…he#1221) Motivation: `ContentLengthParser` skipped whitespace anywhere in the field value, so `Content-Length: 1 2` was read as 12 and `Content-Length: 5 5` as 55, and an empty value was read as 0. The field value is `1*DIGIT` surrounded by optional whitespace (RFC 9110, section 8.6 and RFC 9112, section 5). Every other length ambiguity is rejected by this parser already: two differing Content-Length headers, a Transfer-Encoding other than a single chunked, and chunked together with a Content-Length. This was the remaining spot where pekko-http could read a body length that another implementation in the request path reads differently or rejects. Modification: Skip whitespace before and after the digits, but require at least one digit and stop the value at the first non-digit. Result: A Content-Length value with whitespace between digits, or without any digit, is rejected with the "Illegal `Content-Length` header value" error that other malformed values already produce. Values with leading or trailing whitespace keep parsing as before. Note this rejects two inputs that were accepted before: whitespace inside the digits, and an empty value that was read as 0. Tests: - sbt "http-core/testOnly org.apache.pekko.http.impl.engine.parsing.RequestParserCRLFSpec org.apache.pekko.http.impl.engine.parsing.RequestParserLFSpec org.apache.pekko.http.impl.engine.parsing.ResponseParserSpec" - pass, 2 new tests that both fail without the change; leading whitespace stays covered by the existing "Content-length: 17" tests - sbt http-core/test - pass - sbt http-tests/test - pass - sbt http-core/mimaReportBinaryIssues - pass - sbt http-core/scalafmt http-core/Test/scalafmt - clean References: None - tightens Content-Length parsing to the grammar
apache#1221 rewrites the same lines that apache#1049 changed, so cherry picking it onto 1.4.x brings the pre-multiply overflow check along with it. apache#1049 was never backported, so its test came too: a value that wraps back to a small positive Long, which the old `result < 0` check did not catch. SpecializedHeaderValueParsers.scala and ContentLengthHeaderParserSpec.scala are now identical to main. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Philippus
approved these changes
Aug 27, 2026
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.
Backport of #1221 (cherry pick 5bdfd6d) to
1.4.x.Motivation
ContentLengthParserskips whitespace anywhere in the field value:So
Content-Length: 1 2is read as12,Content-Length: 5 5as55, and an emptyContent-Length:as0. The field value is1*DIGITsurrounded by optional whitespace (RFC 9110, section 8.6 and RFC 9112, section 5). This line dates back to the original Akka HTTP code, so1.4.xis affected exactly asmainwas — it is not a regression introduced onmain.The other ways two implementations could disagree about a body length are already closed here — two Content-Length headers with different values are rejected,
Transfer-Encodingaccepts only a singlechunked, andchunkedtogether with aContent-Lengthis rejected — which is what makes this one worth tightening: it is the remaining case where pekko-http reads a length that a proxy or another server in the request path reads differently, or rejects.Modification
Skip whitespace before and after the digits, require at least one digit, and end the value at the first non-digit.
This PR also brings
SpecializedHeaderValueParsersup to date with the overflow fix from #1049, which landed onmainbut was never backported. #1221 rewrites the same lines, so the two changes cannot be separated cleanly; rather than cherry pick a partial version of #1221, the whole file is brought to parity withmainand #1049's test is backported alongside it.1.4.xcurrently checks for overflow withif (result < 0)after the fact, which misses values that wrap back to a positiveLong:1.4.xtoday922337203685477580801844674407370955161711844674407370955161600The replacement checks
result > (Long.MaxValue - digit) / 10before each multiply, so overflow is caught on the digit that would cause it.Result
A value with whitespace between digits, or with no digit at all, is rejected with the same "Illegal `Content-Length` header value" error that other malformed values already produce. Leading and trailing whitespace still parse as before. A value that overflows
Longis rejected with the existing "must not exceed 63-bit integer range" error in the cases listed above that previously slipped through.This rejects three inputs that were accepted before: whitespace inside the digits, an empty value that used to be read as
0, and an overflowing value that wrapped back to a positiveLong. All are malformed, but it is a behaviour change for anyone who was relying on the lenient reading.After this PR
http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/SpecializedHeaderValueParsers.scalaandhttp-core/src/test/scala/org/apache/pekko/http/impl/engine/parsing/ContentLengthHeaderParserSpec.scalaare byte-identical tomain.No MiMa exclude is needed —
SpecializedHeaderValueParsersis@InternalApi private[parsing]and only method bodies change. #1049'snum-overflow.excludescovers its HTTP/2 andTimestampchanges, which are not part of this backport.Tests
RequestParserSpec: one test for whitespace inside the value and one for an empty value; both fail without the change. Leading whitespace stays covered by the existingContent-length: 17tests, so the accepted cases are pinned too.ContentLengthHeaderParserSpec:18446744073709551634(~2^64+18, wraps to18in a signedLong), which fails without the overflow hunk. This is the only testmainhas for that fix and it was the missing coverage for the overflow change riding along here.