This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch backport/26024-to-camel-4.18.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit 70ad2adeda5fe2729311e9887ac987b5e11cc53f Author: Claus Ibsen <[email protected]> AuthorDate: Thu Sep 3 10:37:49 2026 +0200 [backport camel-4.18.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 <[email protected]> Co-Authored-By: Claude Opus 4.8 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../jetty/rest/RestJettyRequiredBodyTest.java | 23 ++++++++++++++++++++++ .../DefaultRestClientRequestValidator.java | 8 ++++---- .../ROOT/pages/camel-4x-upgrade-guide-4_18.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 054ecd12090e..9dc7dccaf7ca 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.junit5.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_18.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc index bf669caf9230..755064454c32 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc @@ -1655,3 +1655,10 @@ that resolve outside the configured directory are rejected. If `downloadFileName` is configured with an expression (i.e. it contains `$`), the local path is built by that expression as before and is not subject to this check. + +=== 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.
