This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 66a07e331869 CAMEL-25743: camel-aws2-s3 - upload a body of known 
length with its own length, not a stale CamelAwsS3ContentLength (#28070)
66a07e331869 is described below

commit 66a07e3318695cc2d158f04d1acb68171ea0e1e1
Author: allthingssecurity <[email protected]>
AuthorDate: Mon Oct 19 19:59:07 2026 +0530

    CAMEL-25743: camel-aws2-s3 - upload a body of known length with its own 
length, not a stale CamelAwsS3ContentLength (#28070)
    
    Co-authored-by: Claude Opus 5.5 <[email protected]>
---
 .../camel/component/aws2/s3/AWS2S3Producer.java    | 24 +++++---
 .../aws2/s3/AWS2S3ProducerPayloadLengthTest.java   | 66 ++++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |  7 +++
 3 files changed, 89 insertions(+), 8 deletions(-)

diff --git 
a/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/AWS2S3Producer.java
 
b/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/AWS2S3Producer.java
index 680832b5389a..6341a79a7249 100644
--- 
a/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/AWS2S3Producer.java
+++ 
b/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/AWS2S3Producer.java
@@ -191,11 +191,8 @@ public class AWS2S3Producer extends DefaultProducer {
             contentLength = f.length();
         } else {
             // okay we use input stream
-            if (contentLength <= 0) {
-                // such as a java.nio.file.Path, byte[] or stream cache
-                contentLength = PayloadHelper.getLength(obj);
-            }
             inputStream = exchange.getIn().getMandatoryBody(InputStream.class);
+            contentLength = bodyLength(obj, inputStream, contentLength);
             if (contentLength <= 0) {
                 contentLength = PayloadHelper.getLength(inputStream);
                 if (contentLength == -1) {
@@ -369,11 +366,8 @@ public class AWS2S3Producer extends DefaultProducer {
                 contentLength = filePayload.length();
             } else {
                 // okay we use input stream
-                if (contentLength <= 0) {
-                    // such as a java.nio.file.Path, byte[] or stream cache
-                    contentLength = PayloadHelper.getLength(obj);
-                }
                 inputStream = 
exchange.getIn().getMandatoryBody(InputStream.class);
+                contentLength = bodyLength(obj, inputStream, contentLength);
                 if (contentLength <= 0) {
                     contentLength = PayloadHelper.getLength(inputStream);
                     if (contentLength == -1) {
@@ -397,6 +391,20 @@ public class AWS2S3Producer extends DefaultProducer {
         }
     }
 
+    /**
+     * The length of the body when it is known without reading it (such as a 
java.nio.file.Path, byte[], String or
+     * stream cache), otherwise the given content-length header. The header is 
not used for a body of known length, as
+     * it can be stale once the route changed the body (for example the length 
of an object read by the consumer), and
+     * the SDK sends exactly that many bytes.
+     */
+    private static long bodyLength(Object body, InputStream inputStream, long 
contentLengthHeader) {
+        long length = PayloadHelper.getLength(body);
+        if (length < 0) {
+            length = PayloadHelper.getLength(inputStream);
+        }
+        return length >= 0 ? length : contentLengthHeader;
+    }
+
     private void doPutObject(
             Exchange exchange, Map<String, String> objectMetadata,
             File file, InputStream inputStream, long contentLength) {
diff --git 
a/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/AWS2S3ProducerPayloadLengthTest.java
 
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/AWS2S3ProducerPayloadLengthTest.java
index 581fc0bdb9a3..bc4720018cac 100644
--- 
a/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/AWS2S3ProducerPayloadLengthTest.java
+++ 
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/AWS2S3ProducerPayloadLengthTest.java
@@ -149,4 +149,70 @@ public class AWS2S3ProducerPayloadLengthTest {
         assertEquals(PAYLOAD_SIZE, uploadedLength);
         assertArrayEquals(payload, uploadedContent);
     }
+
+    @Test
+    public void bodyLongerThanAStaleContentLengthHeaderIsStoredWhole() throws 
Exception {
+        // the consumer of another bucket set the header to the length of the 
object it read, then the route
+        // changed the body: the SDK sends only contentLength bytes, so the 
header must not cut the new body
+        String text = "a body made longer by the route";
+        Exchange exchange = new DefaultExchange(camelContext);
+        exchange.getIn().setHeader(AWS2S3Constants.KEY, "changed.txt");
+        exchange.getIn().setHeader(AWS2S3Constants.CONTENT_LENGTH, 10L);
+        exchange.getIn().setBody(text);
+
+        producer.process(exchange);
+
+        assertEquals(text.length(), uploadedLength);
+        assertArrayEquals(text.getBytes(StandardCharsets.UTF_8), 
uploadedContent);
+    }
+
+    @Test
+    public void 
bodyShorterThanAStaleContentLengthHeaderIsStoredWithItsLength() throws 
Exception {
+        byte[] body = "short".getBytes(StandardCharsets.UTF_8);
+        Exchange exchange = new DefaultExchange(camelContext);
+        exchange.getIn().setHeader(AWS2S3Constants.KEY, "short.txt");
+        exchange.getIn().setHeader(AWS2S3Constants.CONTENT_LENGTH, 1000L);
+        exchange.getIn().setBody(body);
+
+        producer.process(exchange);
+
+        assertEquals(body.length, uploadedLength);
+        assertArrayEquals(body, uploadedContent);
+    }
+
+    @Test
+    public void multiPartUploadOfABodyLongerThanAStaleContentLengthHeader() 
throws Exception {
+        when(configuration.isMultiPartUpload()).thenReturn(true);
+
+        Exchange exchange = new DefaultExchange(camelContext);
+        exchange.getIn().setHeader(AWS2S3Constants.KEY, "big.bin");
+        exchange.getIn().setHeader(AWS2S3Constants.CONTENT_LENGTH, 10L);
+        exchange.getIn().setBody(payload);
+
+        producer.process(exchange);
+
+        assertEquals(PAYLOAD_SIZE, uploadedLength);
+        assertArrayEquals(payload, uploadedContent);
+    }
+
+    @Test
+    public void streamOfUnknownLengthUsesTheContentLengthHeader() throws 
Exception {
+        // guards the case the header is for: a stream whose length is not 
known is sent with the header length
+        InputStream stream = new FilterInputStream(new 
ByteArrayInputStream(payload)) {
+            @Override
+            public boolean markSupported() {
+                return false;
+            }
+        };
+
+        Exchange exchange = new DefaultExchange(camelContext);
+        exchange.getIn().setHeader(AWS2S3Constants.KEY, "big.bin");
+        exchange.getIn().setHeader(AWS2S3Constants.CONTENT_LENGTH, (long) 
PAYLOAD_SIZE);
+        exchange.getIn().setBody(stream);
+
+        producer.process(exchange);
+
+        assertEquals(PAYLOAD_SIZE, uploadedLength);
+        assertArrayEquals(payload, uploadedContent);
+    }
 }
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 4d4f8a860fcc..32969ae2039e 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -1297,6 +1297,13 @@ In both cases the objects are no longer kept in the 
`inProgressRepository`; befo
 the same key) was skipped by all later polls until the endpoint was restarted. 
With the `fileName` option, a failed
 get still fails the poll.
 
+=== camel-aws2-s3 - the producer stores the whole body when the content-length 
header is stale
+
+When the length of the body is known without reading it (a `byte[]`, `String`, 
file, `Path` or cached stream), the
+producer now uploads it with that length, and the `CamelAwsS3ContentLength` 
header is only used for a stream whose
+length is not known. Previously the header took precedence: after a route 
changed the body of an object read by the
+aws2-s3 consumer (which sets the header), a longer body was stored cut to the 
old length and a shorter one failed.
+
 === camel-mina - the host name of the server is verified over SSL
 
 When `sslContextParameters` is set (on the endpoint, or with 
`useGlobalSslContextParameters` on the component), the

Reply via email to