You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/09/03 08:36:54 UTC

(camel) branch backport/26024-to-camel-4.22.x created (now 566dc3f053a2)

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

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


      at 566dc3f053a2 [backport camel-4.22.x] CAMEL-24409: keep binary bodies intact in rest client request validation

This branch includes the following new commits:

     new 566dc3f053a2 [backport camel-4.22.x] CAMEL-24409: keep binary bodies intact in rest client request validation

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(camel) 01/01: [backport camel-4.22.x] CAMEL-24409: keep binary bodies intact in rest client request validation

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 566dc3f053a2ccea74a37b39a2101902b7b4eca4
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Sep 3 10:35:41 2026 +0200

    [backport camel-4.22.x] CAMEL-24409: keep binary bodies intact in rest client request validation
    
    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)
    
    Co-authored-by: Claude Opus 5 <no...@anthropic.com>
    Co-Authored-By: Claude Opus 4.8 <no...@anthropic.com>
    Signed-off-by: Claus Ibsen <cl...@gmail.com>
---
 .../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.