[ 
https://issues.apache.org/jira/browse/CAMEL-24153?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Claus Ibsen reassigned CAMEL-24153:
-----------------------------------

    Assignee: Claus Ibsen

> camel-aws2-s3 - Streaming upload silently truncates bodies larger than 
> partSize and orphans multipart uploads
> -------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-24153
>                 URL: https://issues.apache.org/jira/browse/CAMEL-24153
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-aws2-s3
>            Reporter: Federico Mariani
>            Assignee: Claus Ibsen
>            Priority: Critical
>              Labels: review-2026-07
>         Attachments: AWS2S3StreamUploadProducerTruncationTest.java
>
>
> Found during a code review of camel-aws2-s3 on main (4.22.0-SNAPSHOT).
> In {{AWS2S3StreamUploadProducer.processWithoutTimestampGrouping}} (lines 
> ~179-297), {{maxRead}} starts at {{partSize}} and is decremented after every 
> read. It is reset back to {{partSize}} only on the *part-flush* branches 
> (lines 198 and 288). When the *batch-flush* branch fires instead (line 271: 
> {{totalSize >= batchSize}} -> {{uploadPart(state); completeUpload(state); 
> state = null; continue;}}), {{maxRead}} stays {{0}}, so the next loop 
> iteration calls {{AWS2S3Utils.toByteArray(is, 0)}}, which reads nothing, and 
> the loop exits with the remainder of the input stream unread. The exchange 
> still completes successfully with the ETag of the truncated object.
> Note this happens with the *default* configuration ({{batchSize=1000000}} < 
> {{partSize=26214400}}): any body larger than partSize is truncated to the 
> first part.
> Additionally, line 183 ({{maxRead -= uploadAggregate.buffer.size()}}) can 
> drive {{maxRead}} negative when {{batchSize > bufferSize}}, dropping an 
> entire subsequent exchange body.
> *Secondary bug in the same loop*: since CAMEL-20728 changed {{state.index++}} 
> to {{state.index = 1}} (line 224), the guard {{if (state.index == 1)}} at 
> line 265 no longer means "first iteration", so {{createMultipartUpload}} is 
> invoked on *every* read iteration. Parts of a single logical upload are 
> spread across several uploadIds; {{completeMultipartUpload}} then fails with 
> {{InvalidPart}}, and the superseded uploads are never aborted (incomplete 
> multipart uploads accrue storage charges indefinitely).
> *Reproducer*: attached {{AWS2S3StreamUploadProducerTruncationTest.java}} 
> (pure unit test, mocked S3Client, goes under 
> {{components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/stream/}}).
>  It sends a 12288-byte body with {{partSize=8192}}, {{batchSize=4096}} and 
> fails with:
> {noformat}
> expected: <12288> but was: <8192>
> {noformat}
> The existing IT (S3StreamUploadS3MultipartIT) only sends bodies smaller than 
> partSize, which is why this path is untested.
> *Suggested fix*: reset {{maxRead}} after every flush (and clamp to a positive 
> read size), and guard the upload creation with {{if (state.initResponse == 
> null)}} instead of {{state.index == 1}}.
> _This issue was researched and filed by Claude Code (AI) on behalf of 
> Federico Mariani (fmariani). Full findings document from the review is 
> attached to the umbrella issues._



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to