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 df0a84fda895 CAMEL-25295: camel-undertow - match the charset parameter
of the Content-Type case-insensitively (#27326)
df0a84fda895 is described below
commit df0a84fda895081084132b8dded29b5ea8c91baf
Author: allthingssecurity <[email protected]>
AuthorDate: Sun Oct 4 12:33:01 2026 +0530
CAMEL-25295: camel-undertow - match the charset parameter of the
Content-Type case-insensitively (#27326)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../camel/component/undertow/UndertowConsumer.java | 4 +-
.../camel/component/undertow/UndertowHelper.java | 29 +++++++++--
.../undertow/UndertowStringBodyCharsetTest.java | 59 ++++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 7 +++
4 files changed, 92 insertions(+), 7 deletions(-)
diff --git
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java
index 7df12a1e2ce2..c42fee7986f6 100644
---
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java
+++
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java
@@ -407,9 +407,7 @@ public class UndertowConsumer extends DefaultConsumer
implements HttpHandler, Su
// By not setting the property when charset is absent, Camel's type
conversion
// system will use its own default (UTF-8), matching the behavior of
DefaultHttpBinding.
String contentType =
httpExchange.getRequestHeaders().getFirst(Headers.CONTENT_TYPE);
- String charset = contentType != null
- ? Headers.extractQuotedValueFromHeader(contentType, "charset")
- : null;
+ String charset = UndertowHelper.getCharsetFromContentType(contentType);
if (charset != null) {
exchange.setProperty(ExchangePropertyKey.CHARSET_NAME, charset);
in.setHeader(UndertowConstants.HTTP_CHARACTER_ENCODING, charset);
diff --git
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHelper.java
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHelper.java
index 81035bbe7175..914e3ccd8e74 100644
---
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHelper.java
+++
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHelper.java
@@ -22,7 +22,6 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Map;
-import io.undertow.util.Headers;
import io.undertow.util.HttpString;
import io.undertow.util.Methods;
import org.apache.camel.Exchange;
@@ -188,16 +187,38 @@ public final class UndertowHelper {
* not supported, in which case the body is converted
as before (UTF-8 by default)
*/
public static ByteBuffer toByteBuffer(String body, String contentType) {
- String name = contentType != null ?
Headers.extractQuotedValueFromHeader(contentType, "charset") : null;
- if (ObjectHelper.isEmpty(name)) {
+ String name = getCharsetFromContentType(contentType);
+ if (name == null) {
return null;
}
try {
- return
ByteBuffer.wrap(body.getBytes(Charset.forName(IOHelper.normalizeCharset(name))));
+ return ByteBuffer.wrap(body.getBytes(Charset.forName(name)));
} catch (IllegalArgumentException e) {
// unknown or unsupported charset in the Content-Type
return null;
}
}
+ /**
+ * Gets the charset parameter of a Content-Type. The parameter name is
case-insensitive (RFC 9110), as in the other
+ * HTTP components. Unlike {@link
IOHelper#getCharsetNameFromContentType(String)}, there is no default: a
+ * Content-Type without a charset gives <tt>null</tt>.
+ *
+ * @param contentType the Content-Type, may be <tt>null</tt>
+ * @return the charset name, or <tt>null</tt> when the
Content-Type declares no charset
+ */
+ public static String getCharsetFromContentType(String contentType) {
+ if (contentType == null) {
+ return null;
+ }
+ for (String parameter : contentType.split(";")) {
+ parameter = parameter.trim();
+ if (parameter.regionMatches(true, 0, "charset=", 0, 8)) {
+ String name =
IOHelper.normalizeCharset(parameter.substring(8));
+ return ObjectHelper.isEmpty(name) ? null : name;
+ }
+ }
+ return null;
+ }
+
}
diff --git
a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowStringBodyCharsetTest.java
b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowStringBodyCharsetTest.java
index 0700b891588b..f4470cdacc4e 100644
---
a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowStringBodyCharsetTest.java
+++
b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowStringBodyCharsetTest.java
@@ -22,6 +22,7 @@ import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePropertyKey;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.junit.jupiter.api.Test;
@@ -30,6 +31,7 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
/**
* A String body is written in the charset that the Content-Type declares,
otherwise as UTF-8.
@@ -38,6 +40,8 @@ public class UndertowStringBodyCharsetTest extends
BaseUndertowTest {
private static final String TEXT = "Grüße aus Köln";
private static final String LATIN1 = "text/plain; charset=ISO-8859-1";
+ // parameter names are case-insensitive (RFC 9110)
+ private static final String LATIN1_UPPER_CASE = "text/plain;
Charset=ISO-8859-1";
@Test
public void testResponseUsesCharsetOfContentType() throws Exception {
@@ -85,6 +89,52 @@ public class UndertowStringBodyCharsetTest extends
BaseUndertowTest {
assertArrayEquals(TEXT.getBytes(ISO_8859_1),
mock.getExchanges().get(0).getIn().getBody(byte[].class));
}
+ @Test
+ public void testResponseUsesCharsetParameterInAnyCase() throws Exception {
+ HttpResponse<byte[]> response =
send(HttpRequest.newBuilder(uri("latin1UpperCase")).GET().build());
+
+ assertEquals(LATIN1_UPPER_CASE,
response.headers().firstValue("Content-Type").orElse(null));
+ assertArrayEquals(TEXT.getBytes(ISO_8859_1), response.body());
+ }
+
+ @Test
+ public void testRequestUsesCharsetParameterInAnyCase() throws Exception {
+ HttpResponse<byte[]> response =
send(HttpRequest.newBuilder(uri("decode"))
+ .header("Content-Type", LATIN1_UPPER_CASE)
+
.POST(HttpRequest.BodyPublishers.ofByteArray(TEXT.getBytes(ISO_8859_1))).build());
+
+ assertArrayEquals(TEXT.getBytes(UTF_8), response.body());
+ }
+
+ @Test
+ public void testProducerUsesCharsetParameterInAnyCase() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:received");
+ mock.expectedMessageCount(1);
+
+
template.sendBodyAndHeader("undertow:http://localhost:{{port}}/received", TEXT,
Exchange.CONTENT_TYPE,
+ LATIN1_UPPER_CASE);
+
+ MockEndpoint.assertIsSatisfied(context);
+ assertArrayEquals(TEXT.getBytes(ISO_8859_1),
mock.getExchanges().get(0).getIn().getBody(byte[].class));
+ }
+
+ @Test
+ public void testRequestWithoutCharsetDoesNotSetCharset() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:received");
+ mock.expectedMessageCount(1);
+
+ HttpResponse<byte[]> response =
send(HttpRequest.newBuilder(uri("received"))
+ .header("Content-Type", "text/plain")
+
.POST(HttpRequest.BodyPublishers.ofByteArray(TEXT.getBytes(UTF_8))).build());
+
+ // only a charset that the request declares is set, otherwise Camel
uses its default
+ assertEquals(200, response.statusCode());
+ MockEndpoint.assertIsSatisfied(context);
+ Exchange received = mock.getExchanges().get(0);
+ assertNull(received.getProperty(ExchangePropertyKey.CHARSET_NAME));
+
assertNull(received.getIn().getHeader(UndertowConstants.HTTP_CHARACTER_ENCODING));
+ }
+
private URI uri(String path) {
return URI.create("http://localhost:" + getPort() + "/" + path);
}
@@ -102,6 +152,15 @@ public class UndertowStringBodyCharsetTest extends
BaseUndertowTest {
.setHeader(Exchange.CONTENT_TYPE, constant(LATIN1))
.setBody(constant(TEXT));
+ from("undertow:http://localhost:{{port}}/latin1UpperCase")
+ .setHeader(Exchange.CONTENT_TYPE,
constant(LATIN1_UPPER_CASE))
+ .setBody(constant(TEXT));
+
+ // decodes the request in its charset and answers in UTF-8
+ from("undertow:http://localhost:{{port}}/decode")
+ .convertBodyTo(String.class)
+ .setHeader(Exchange.CONTENT_TYPE,
constant("text/plain; charset=UTF-8"));
+
from("undertow:http://localhost:{{port}}/echo")
.convertBodyTo(String.class);
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index b0d4eeb66d52..4f6537cc7a39 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -764,6 +764,13 @@ written in the charset that the `Content-Type` declares.
Bodies that are not a `
read such a message as UTF-8 must now use the declared charset, and characters
that the declared charset cannot
represent are written as `?`.
+==== The charset parameter of the Content-Type in any case
+
+The `charset` parameter of a `Content-Type` was only recognized in lower case.
A request with
+`Content-Type: text/plain; Charset=ISO-8859-1` did not set `CamelCharsetName`
(so its body was read as UTF-8), and a
+`String` body with such a `Content-Type` was written as UTF-8. The parameter
name is now matched case-insensitively
+(RFC 9110), as in the other HTTP components, on the consumer (request and
response) and on the producer.
+
=== camel-snmp - GET_NEXT (walk)
A `GET_NEXT` walk now ends at the end of the agent's MIB view (`endOfMibView`,
or `noSuchName` for SNMPv1) and at