Federico Mariani created CAMEL-24153:
----------------------------------------
Summary: 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
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)