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.