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 d0f88b631651 CAMEL-25195: camel-console - send dev console supports
base64 encoded bodies (#27141)
d0f88b631651 is described below
commit d0f88b631651f360f0639fa4c58e970678215e17
Author: Federico Mariani <[email protected]>
AuthorDate: Wed Sep 30 20:58:16 2026 +0200
CAMEL-25195: camel-console - send dev console supports base64 encoded
bodies (#27141)
The send dev console (and the send action of camel-cli-connector) only
accepted the body as text, so tools could not send binary payloads. A new
bodyEncoding option (base64) decodes the body and sends it as byte[]; the file:
prefix is not interpreted for encoded bodies, and unsupported values are
reported as an error. LocalCliConnector forwards bodyEncoding from the send
action.
Co-Authored-By: Claude <[email protected]>
---
.../apache/camel/catalog/dev-consoles-openapi.json | 7 +++
.../apache/camel/catalog/dev-consoles/send.json | 27 +++++++--
.../org/apache/camel/dev-console/send.json | 27 +++++++--
.../apache/camel/impl/console/SendDevConsole.java | 19 ++++++-
.../camel/impl/console/SendDevConsoleTest.java | 65 ++++++++++++++++++++++
.../camel/cli/connector/LocalCliConnector.java | 4 ++
6 files changed, 136 insertions(+), 13 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
index 2146b5bcc7fa..297bd579d51e 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
@@ -7049,6 +7049,13 @@
"type": "string",
"description": "The message body to send. Can refer to files using file: prefix"
},
+
"bodyEncoding": {
+
"type": "string",
+
"enum": [
+
"base64"
+
],
+
"description": "Encoding of the body option. Use base64 to send binary content,
which is decoded and sent as byte[]."
+ },
"bodyMaxChars": {
"type": "integer",
"default": 32768,
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/send.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/send.json
index 5813744eba48..83206b04163d 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/send.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/send.json
@@ -27,9 +27,26 @@
"secret": false,
"description": "The message body to send. Can refer to files using file:
prefix"
},
- "bodyMaxChars": {
+ "bodyEncoding": {
"index": 1,
"kind": "option",
+ "displayName": "Body Encoding",
+ "group": "query",
+ "label": "query",
+ "required": false,
+ "type": "string",
+ "javaType": "java.lang.String",
+ "enum": [
+ "base64"
+ ],
+ "deprecated": false,
+ "autowired": false,
+ "secret": false,
+ "description": "Encoding of the body option. Use base64 to send binary
content, which is decoded and sent as byte[]."
+ },
+ "bodyMaxChars": {
+ "index": 2,
+ "kind": "option",
"displayName": "Body Max Chars",
"group": "query",
"label": "query",
@@ -43,7 +60,7 @@
"description": "Maximum size of the message body to include in the dump"
},
"endpoint": {
- "index": 2,
+ "index": 3,
"kind": "option",
"displayName": "Endpoint",
"group": "query",
@@ -57,7 +74,7 @@
"description": "Endpoint for where to send messages (can also refer to a
route id, endpoint pattern)"
},
"exchangePattern": {
- "index": 3,
+ "index": 4,
"kind": "option",
"displayName": "Exchange Pattern",
"group": "query",
@@ -75,7 +92,7 @@
"description": "Exchange pattern when sending"
},
"poll": {
- "index": 4,
+ "index": 5,
"kind": "option",
"displayName": "Poll",
"group": "query",
@@ -89,7 +106,7 @@
"description": "Whether to poll message from the endpoint instead of
sending"
},
"pollTimeout": {
- "index": 5,
+ "index": 6,
"kind": "option",
"displayName": "Poll Timeout",
"group": "query",
diff --git
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/send.json
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/send.json
index 5813744eba48..83206b04163d 100644
---
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/send.json
+++
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/send.json
@@ -27,9 +27,26 @@
"secret": false,
"description": "The message body to send. Can refer to files using file:
prefix"
},
- "bodyMaxChars": {
+ "bodyEncoding": {
"index": 1,
"kind": "option",
+ "displayName": "Body Encoding",
+ "group": "query",
+ "label": "query",
+ "required": false,
+ "type": "string",
+ "javaType": "java.lang.String",
+ "enum": [
+ "base64"
+ ],
+ "deprecated": false,
+ "autowired": false,
+ "secret": false,
+ "description": "Encoding of the body option. Use base64 to send binary
content, which is decoded and sent as byte[]."
+ },
+ "bodyMaxChars": {
+ "index": 2,
+ "kind": "option",
"displayName": "Body Max Chars",
"group": "query",
"label": "query",
@@ -43,7 +60,7 @@
"description": "Maximum size of the message body to include in the dump"
},
"endpoint": {
- "index": 2,
+ "index": 3,
"kind": "option",
"displayName": "Endpoint",
"group": "query",
@@ -57,7 +74,7 @@
"description": "Endpoint for where to send messages (can also refer to a
route id, endpoint pattern)"
},
"exchangePattern": {
- "index": 3,
+ "index": 4,
"kind": "option",
"displayName": "Exchange Pattern",
"group": "query",
@@ -75,7 +92,7 @@
"description": "Exchange pattern when sending"
},
"poll": {
- "index": 4,
+ "index": 5,
"kind": "option",
"displayName": "Poll",
"group": "query",
@@ -89,7 +106,7 @@
"description": "Whether to poll message from the endpoint instead of
sending"
},
"pollTimeout": {
- "index": 5,
+ "index": 6,
"kind": "option",
"displayName": "Poll Timeout",
"group": "query",
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/SendDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/SendDevConsole.java
index 9735436acf41..b015c30b303f 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/SendDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/SendDevConsole.java
@@ -19,6 +19,7 @@ package org.apache.camel.impl.console;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
+import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -76,6 +77,11 @@ public class SendDevConsole extends AbstractDevConsole {
javaType = "java.lang.String")
public static final String BODY = "body";
+ @Metadata(label = "query",
+ description = "Encoding of the body option. Use base64 to send
binary content, which is decoded and sent as byte[].",
+ javaType = "java.lang.String", enums = "base64")
+ public static final String BODY_ENCODING = "bodyEncoding";
+
@Metadata(label = "query", description = "Whether to poll message from the
endpoint instead of sending",
javaType = "java.lang.Boolean")
public static final String POLL = "poll";
@@ -261,7 +267,7 @@ public class SendDevConsole extends AbstractDevConsole {
throws Exception {
Exchange out = null;
if (target != null) {
- final Object inputBody = prepareBody(body);
+ final Object inputBody = prepareBody(body, optionString(options,
BODY_ENCODING));
final Map<String, Object> inputHeaders = prepareHeaders(options);
if (poll) {
out = consumer.receive(target, timeout);
@@ -324,7 +330,13 @@ public class SendDevConsole extends AbstractDevConsole {
return target;
}
- private Object prepareBody(String body) throws Exception {
+ private Object prepareBody(String body, String bodyEncoding) throws
Exception {
+ if (bodyEncoding != null) {
+ if (!"base64".equalsIgnoreCase(bodyEncoding)) {
+ throw new IllegalArgumentException("Unsupported bodyEncoding:
" + bodyEncoding + " (supported: base64)");
+ }
+ return Base64.getDecoder().decode(body);
+ }
Object b = body;
if (body.startsWith("file:")) {
File file = new File(body.substring(5));
@@ -344,7 +356,8 @@ public class SendDevConsole extends AbstractDevConsole {
}
private static boolean isCustomHeader(String key) {
- return !BODY.equals(key) && !BODY_MAX_CHARS.equals(key) &&
!POLL.equals(key) && !POLL_TIMEOUT.equals(key)
+ return !BODY.equals(key) && !BODY_ENCODING.equals(key) &&
!BODY_MAX_CHARS.equals(key) && !POLL.equals(key)
+ && !POLL_TIMEOUT.equals(key)
&& !EXCHANGE_PATTERN.equals(key) && !ENDPOINT.equals(key)
&& !"CamelHttpPath".equals(key); // do not include ourself
/q/dev/send
}
diff --git
a/core/camel-console/src/test/java/org/apache/camel/impl/console/SendDevConsoleTest.java
b/core/camel-console/src/test/java/org/apache/camel/impl/console/SendDevConsoleTest.java
index e0c988c0c5b8..acbc85970291 100644
---
a/core/camel-console/src/test/java/org/apache/camel/impl/console/SendDevConsoleTest.java
+++
b/core/camel-console/src/test/java/org/apache/camel/impl/console/SendDevConsoleTest.java
@@ -16,15 +16,23 @@
*/
package org.apache.camel.impl.console;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.Message;
import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.console.DevConsole;
+import org.apache.camel.support.service.ServiceHelper;
import org.apache.camel.util.json.JsonObject;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -147,6 +155,63 @@ public class SendDevConsoleTest extends
AbstractDevConsoleTest {
assertNotNull(jsonOut.getString("status"));
}
+ @Test
+ public void testSendConsoleBase64Body() throws Exception {
+ DevConsole console = startedSendConsole();
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(1);
+
+ byte[] data = new byte[] { 0, (byte) 0xC3, (byte) 0xA8, (byte) 0xFF };
+ Map<String, Object> options = createOptions("direct:start",
Base64.getEncoder().encodeToString(data));
+ options.put(SendDevConsole.BODY_ENCODING, "base64");
+
+ JsonObject out = callJson(console, options);
+ assertEquals("success", out.getString("status"), out.toJson());
+ mock.assertIsSatisfied();
+ Message msg = mock.getReceivedExchanges().get(0).getMessage();
+ assertArrayEquals(data, msg.getBody(byte[].class));
+
assertFalse(msg.getHeaders().containsKey(SendDevConsole.BODY_ENCODING));
+ }
+
+ @Test
+ public void testSendConsoleBase64BodyIsNotAFileReference() throws
Exception {
+ DevConsole console = startedSendConsole();
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedBodiesReceived("file:pom.xml");
+
+ Map<String, Object> options = createOptions("direct:start",
+
Base64.getEncoder().encodeToString("file:pom.xml".getBytes(StandardCharsets.UTF_8)));
+ options.put(SendDevConsole.BODY_ENCODING, "base64");
+
+ callJson(console, options);
+ mock.assertIsSatisfied();
+ }
+
+ @Test
+ public void testSendConsoleInvalidBodyEncoding() {
+ DevConsole console = startedSendConsole();
+
+ Map<String, Object> options = createOptions("direct:start", "Hello");
+ options.put(SendDevConsole.BODY_ENCODING, "hex");
+ JsonObject out = callJson(console, options);
+ assertEquals("error", out.getString("status"));
+
+ options = createOptions("direct:start", "not base64!");
+ options.put(SendDevConsole.BODY_ENCODING, "base64");
+ out = callJson(console, options);
+ assertEquals("error", out.getString("status"));
+ }
+
+ /**
+ * The resolved console is not started, so it has no producer to send with.
+ */
+ private DevConsole startedSendConsole() {
+ DevConsole console = assertConsoleExists("send");
+ CamelContextAware.trySetCamelContext(console, context);
+ ServiceHelper.startService(console);
+ return console;
+ }
+
private Map<String, Object> createOptions(String endpoint, String body) {
Map<String, Object> options = new HashMap<>();
options.put(SendDevConsole.ENDPOINT, endpoint);
diff --git
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
index d059599f1b6b..51730c34a4e3 100644
---
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
+++
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
@@ -682,6 +682,10 @@ public class LocalCliConnector extends ServiceSupport
implements CliConnector, C
if (body != null) {
args.put("body", body);
}
+ String bodyEncoding = root.getString("bodyEncoding");
+ if (bodyEncoding != null) {
+ args.put("bodyEncoding", bodyEncoding);
+ }
if (exchangePattern != null) {
args.put("exchangePattern", exchangePattern);
}