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

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


The following commit(s) were added to refs/heads/camel-4.22.x by this push:
     new ba3cf8477bbc [backport camel-4.22.x] CAMEL-24409: keep binary bodies 
intact in rest client request validation (#26070)
ba3cf8477bbc is described below

commit ba3cf8477bbce7c7eba30f323500d018cb332755
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Sep 3 13:25:21 2026 +0200

    [backport camel-4.22.x] CAMEL-24409: keep binary bodies intact in rest 
client request validation (#26070)
    
    Checking that a required body is present read the body as a String and wrote
    that String back onto the message, which corrupted binary payloads such as
    application/octet-stream. extractBodyAsString already leaves the body as a
    re-readable StreamCache, so the write back was not needed to keep the body
    readable, only to lose the bytes.
    
    (cherry picked from commit cfa4c3692956969195835017fae105514464525a)
    
    Signed-off-by: Claus Ibsen <[email protected]>
    Co-authored-by: Claude Opus 5 <[email protected]>
---
 .../jetty/rest/RestJettyRequiredBodyTest.java      | 23 ++++++++++++++++++++++
 .../DefaultRestClientRequestValidator.java         |  8 ++++----
 .../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc    |  7 +++++++
 3 files changed, 34 insertions(+), 4 deletions(-)

diff --git 
a/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/rest/RestJettyRequiredBodyTest.java
 
b/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/rest/RestJettyRequiredBodyTest.java
index 093bbb58f1f7..2616e574a7e9 100644
--- 
a/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/rest/RestJettyRequiredBodyTest.java
+++ 
b/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/rest/RestJettyRequiredBodyTest.java
@@ -26,11 +26,15 @@ import org.apache.camel.model.rest.RestParamType;
 import org.junit.jupiter.api.Test;
 
 import static org.apache.camel.test.junit6.TestSupport.assertIsInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
 public class RestJettyRequiredBodyTest extends BaseJettyTest {
 
+    // bytes that are not valid UTF-8, so they would be replaced if the body 
was turned into a String
+    private static final byte[] BINARY_BODY = { 0x00, 0x01, (byte) 0xFF, 
(byte) 0xFE, (byte) 0x80, 0x7F, (byte) 0xC3, 0x28 };
+
     @Test
     public void testJettyValid() {
         String out = fluentTemplate.withHeader(Exchange.CONTENT_TYPE, 
"application/json")
@@ -70,6 +74,18 @@ public class RestJettyRequiredBodyTest extends BaseJettyTest 
{
         assertEquals("The request body is missing.", cause.getResponseBody());
     }
 
+    @Test
+    public void testJettyBinaryBodyNotCorrupted() {
+        byte[] out = fluentTemplate.withHeader(Exchange.CONTENT_TYPE, 
"application/octet-stream")
+                .withHeader("Accept", "application/octet-stream")
+                .withHeader(Exchange.HTTP_METHOD, "post")
+                .withBody(BINARY_BODY)
+                .to("http://localhost:"; + getPort() + "/users/123/upload")
+                .request(byte[].class);
+
+        assertArrayEquals(BINARY_BODY, out);
+    }
+
     @Override
     protected RouteBuilder createRouteBuilder() {
         return new RouteBuilder() {
@@ -85,6 +101,13 @@ public class RestJettyRequiredBodyTest extends 
BaseJettyTest {
                         .name("body").required(true).type(RestParamType.body)
                         .endParam().to("direct:update");
                 from("direct:update").setBody(constant("{ \"status\": \"ok\" 
}"));
+
+                // a binary service that echoes back what it received
+                
rest("/users/").post("{id}/upload").consumes("application/octet-stream")
+                        .produces("application/octet-stream").param()
+                        .name("body").required(true).type(RestParamType.body)
+                        .endParam().to("direct:upload");
+                from("direct:upload").setBody(bodyAs(byte[].class));
             }
         };
     }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/processor/DefaultRestClientRequestValidator.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/processor/DefaultRestClientRequestValidator.java
index a9add96dfeaa..af9ca20a755e 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/processor/DefaultRestClientRequestValidator.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/processor/DefaultRestClientRequestValidator.java
@@ -74,11 +74,11 @@ public class DefaultRestClientRequestValidator implements 
RestClientRequestValid
         Object body = exchange.getMessage().getBody();
         if (validationContext.requiredBody()) {
             // the body is required, so we need to know if we have a body or 
not
-            // so force reading the body as a String which we can work with
+            // so force reading the body as a String which we can work with.
+            // extractBodyAsString uses stream caching so the message body 
stays re-readable,
+            // and the body is deliberately not replaced with the String as 
that would
+            // corrupt binary payloads such as application/octet-stream
             body = MessageHelper.extractBodyAsString(exchange.getIn());
-            if (ObjectHelper.isNotEmpty(body)) {
-                exchange.getIn().setBody(body);
-            }
             if (ObjectHelper.isEmpty(body)) {
                 // this is a bad request, the client did not include a message 
body
                 return new ValidationError(400, "The request body is 
missing.");
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index 435153a8a2df..094508581eee 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -1814,3 +1814,10 @@ The `backOffMaxAttempts` option now bounds the attempts 
to start the delegated c
 The retry task previously also carried the default five second duration of its 
budget, which ended the
 task before the second attempt for any `backOffDelay` at or above the default 
of five seconds. A delegate
 that fails to start is therefore retried for longer than before, up to 
`backOffMaxAttempts` times.
+
+=== camel-core - REST client request validation and binary bodies
+
+When `clientRequestValidation` is enabled and the REST service declares a 
required body, the incoming
+message body is no longer replaced with a `String` version of itself. That 
conversion corrupted binary
+payloads such as `application/octet-stream`. The body is still read to check 
that it is present, using
+stream caching so it stays re-readable, but it now keeps its original type.

Reply via email to