Make body length 64-bit; bound getContent() at the array ceiling - #3102
Make body length 64-bit; bound getContent() at the array ceiling#3102predic8 wants to merge 2 commits into
Conversation
Body length was reported by an int getLength(): Body/ChunkedBody/Http2Body cast (int)streamedLength and AbstractBody summed chunk lengths as int. For a body >= 2 GB this silently overflowed to a negative value (exactly 2^31 bytes -> Integer.MIN_VALUE). Since #3039 tightened Header.getContentLength() to reject negative/non-numeric values, that overflowed length written via setContentLength() then threw MalformedHeaderException during response construction (e.g. ReturnInterceptor.createResponseFromRequest). The wire/streaming path is already 64-bit clean (Header content-length and Body(in, long length) use long, writeStreamed streams >2 GB fine); only the reported length API and getContent() materialization broke. - getLength() returns long across AbstractBody, Body, ChunkedBody, EmptyBody, the SSE StreamingBody and HTTP/2 Http2Body (streamedLength field widened), removing the (int) truncations. - getContent() throws the new BodyTooLargeException before allocating when the length exceeds MAX_ARRAY_LENGTH (Integer.MAX_VALUE - 8) instead of overflowing/OOMing; same guard on ChunkedBody.getRawLength(). A single byte[] cannot hold more than ~2 GB, so full-body materialization stays small-message only. getContentAsStream() is unaffected (wraps the chunk list). - BodyTooLargeException extends ReadingBodyException so existing body-error handling keeps working while the subtype stays distinguishable. - Narrowing consumers fixed: Message.estimateHeapSize() clamps; two out.write(getContent(), 0, getLength()) sites use the array's own length. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (11)
📝 WalkthroughWalkthroughBody length APIs now preserve values above ChangesBody length safety
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
This pull request needs "/ok-to-test" from an authorized committer. |
Problem
An HTTP message body's length was reported by an
int getLength():Body/ChunkedBody/Http2Bodycast(int) streamedLengthandAbstractBodysummed chunk lengths into anint. For a body ≥ 2 GB this silently overflowed to a negative value (exactly2^31bytes →Integer.MIN_VALUE=-2147483648).Since #3039 tightened
Header.getContentLength()to reject non-numeric/negative Content-Length values, that overflowed length — once written viasetContentLength()— throwsMalformedHeaderExceptionduring response construction:triggered by
ReturnInterceptor.createResponseFromRequest→setContentLength(body.getLength()).The wire/streaming path is already 64-bit clean (
Headercontent-length andBody(in, long length)uselong;writeStreamedstreams >2 GB without buffering). Only the reported length API and thegetContent()materialization broke.getContent()returns a singlebyte[], which a JVM cannot size beyond ~Integer.MAX_VALUE— a hard limit, so full-body materialization is inherently a small-message operation.Changes
getLength()returnslongacrossAbstractBody,Body,ChunkedBody,EmptyBody, the SSEStreamingBody, and HTTP/2Http2Body(itsstreamedLengthfield widened too). Removes the(int)truncations, so >2 GB bodies stream through with a correct Content-Length.getContent()is bounded: it throws the newBodyTooLargeExceptionbefore allocating when the length exceedsMAX_ARRAY_LENGTH(Integer.MAX_VALUE - 8), instead of overflowing /NegativeArraySizeException/ OOM. Same guard onChunkedBody.getRawLength().getContentAsStream()is unaffected (it wraps the chunk list, never one giant array).BodyTooLargeException extends ReadingBodyException(unchecked), so existingcatch (ReadingBodyException)handling keeps working while the subtype stays distinguishable.Message.estimateHeapSize()clamps; twoout.write(getContent(), 0, getLength())sites use the array's own.length.Accepted limitations
byte[]cap. Such interceptors now fail cleanly withBodyTooLargeExceptionrather than corrupting the Content-Length.Tests
BodyTest.getContentThrowsWhenBodyExceedsArrayLimitandBodyTest.largeBodyLengthRoundTripsThroughContentLength(the latter reproduces the overflow →MalformedHeaderExceptionpath at the length-API/header round-trip level).BodyTest(11),ChunkedBodyTest(35),HeaderTest(100),ReturnInterceptorTest(4),BodyDoesntThrowIOExceptionTest(1, confirms the new unchecked exception respects the no-checked-IOExceptionbody contract).🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests