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 5ce186ae1564 CAMEL-25633: camel-undertow - form fields without a
declared charset are decoded as ISO-8859-1, so non-ASCII text is corrupted
(#27851)
5ce186ae1564 is described below
commit 5ce186ae156442cf3556728461e68b96e9ac8dad
Author: allthingssecurity <[email protected]>
AuthorDate: Thu Oct 15 11:17:47 2026 +0530
CAMEL-25633: camel-undertow - form fields without a declared charset are
decoded as ISO-8859-1, so non-ASCII text is corrupted (#27851)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../camel/component/undertow/UndertowConsumer.java | 8 +-
.../undertow/UndertowFormCharsetTest.java | 104 +++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 8 ++
3 files changed, 119 insertions(+), 1 deletion(-)
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 0f1d206a83e4..b55270502a84 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
@@ -21,6 +21,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.StringJoiner;
@@ -32,6 +33,7 @@ import io.undertow.server.handlers.accesslog.AccessLogHandler;
import io.undertow.server.handlers.accesslog.AccessLogReceiver;
import io.undertow.server.handlers.accesslog.JBossLoggingAccessLogReceiver;
import io.undertow.server.handlers.form.EagerFormParsingHandler;
+import io.undertow.server.handlers.form.FormParserFactory;
import io.undertow.util.AttachmentKey;
import io.undertow.util.Headers;
import io.undertow.util.HttpString;
@@ -118,7 +120,11 @@ public class UndertowConsumer extends DefaultConsumer
implements HttpHandler, Su
wrapWithAccessLogAndHandlers(this.webSocketHandler.getUpgradeHandler(),
endpoint));
} else {
// allow for HTTP 1.1 continue
- HttpHandler httpHandler = new
EagerFormParsingHandler().setNext(UndertowConsumer.this);
+ // form fields of a request that declares no charset are UTF-8 (as
browsers send them), not the
+ // ISO-8859-1 default of Undertow; a charset that the request
declares is still used
+ HttpHandler httpHandler = new EagerFormParsingHandler(
+
FormParserFactory.builder().withDefaultCharset(StandardCharsets.UTF_8.name()).build())
+ .setNext(UndertowConsumer.this);
if (endpoint.getOauthHttpSecurity() != null) {
httpHandler = new OAuthUndertowHttpHandler(
endpoint.getCamelContext(),
endpoint.getOauthHttpSecurity(), httpHandler, endpoint.isOptionsEnabled(),
diff --git
a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowFormCharsetTest.java
b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowFormCharsetTest.java
new file mode 100644
index 000000000000..0625ed9816b3
--- /dev/null
+++
b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowFormCharsetTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.undertow;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.Map;
+
+import org.apache.camel.Message;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.Test;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Form fields are decoded as UTF-8 when the request does not declare a
charset, as a browser sends them.
+ */
+class UndertowFormCharsetTest extends BaseUndertowTest {
+
+ private static final String TEXT = "Grüße";
+ private static final String BOUNDARY = "r27boundary";
+
+ @Test
+ void testUrlEncodedFormWithoutCharsetIsUtf8() throws Exception {
+ Message received = post("application/x-www-form-urlencoded",
"name=Gr%C3%BC%C3%9Fe".getBytes(UTF_8));
+
+ assertEquals(TEXT, received.getHeader("name"));
+ assertEquals(TEXT, received.getBody(Map.class).get("name"));
+ }
+
+ @Test
+ void testMultipartTextFieldWithoutCharsetIsUtf8() throws Exception {
+ String multipart = "--" + BOUNDARY + "\r\n"
+ + "Content-Disposition: form-data;
name=\"name\"\r\n\r\n"
+ + TEXT + "\r\n"
+ + "--" + BOUNDARY + "--\r\n";
+ Message received = post("multipart/form-data; boundary=" + BOUNDARY,
multipart.getBytes(UTF_8));
+
+ assertEquals(TEXT, received.getHeader("name"));
+ assertEquals(TEXT, received.getBody(Map.class).get("name"));
+ }
+
+ @Test
+ void testUrlEncodedFormKeepsDeclaredCharset() throws Exception {
+ // a charset that the request declares is still used to decode the
percent-encoded bytes
+ Message received = post("application/x-www-form-urlencoded;
charset=ISO-8859-1",
+ "name=Gr%FC%DFe".getBytes(UTF_8));
+
+ assertEquals(TEXT, received.getHeader("name"));
+ }
+
+ @Test
+ void testUrlEncodedIso88591FormWithoutCharsetGetsReplacementCharacters()
throws Exception {
+ // the caveat of the change: ISO-8859-1 data that does not declare its
charset is not valid UTF-8
+ Message received = post("application/x-www-form-urlencoded",
"name=Gr%FC%DFe".getBytes(UTF_8));
+
+ assertEquals("Gr\uFFFD\uFFFDe", received.getHeader("name"));
+ }
+
+ private Message post(String contentType, byte[] body) throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:form");
+ mock.reset();
+ mock.expectedMessageCount(1);
+
+ HttpRequest request =
HttpRequest.newBuilder(URI.create("http://localhost:" + getPort() + "/form"))
+ .header("Content-Type", contentType)
+ .POST(HttpRequest.BodyPublishers.ofByteArray(body)).build();
+ HttpResponse<String> response =
HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
+
+ assertEquals(200, response.statusCode());
+ MockEndpoint.assertIsSatisfied(context);
+ return mock.getExchanges().get(0).getIn();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("undertow:http://localhost:{{port}}/form")
+ .to("mock:form")
+ .setBody(constant("OK"));
+ }
+ };
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 5b24443dca94..78e27aeb64fc 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -474,6 +474,14 @@ before it calls the route; with stream caching, the
default, it already read the
The same applies to every consumer that reads the request body with
`DefaultHttpBinding.parseBody` of
`camel-http-common`, such as the Spring Boot `platform-http` consumer with
`useStreaming=true`.
+=== camel-undertow - form fields are decoded as UTF-8
+
+The consumer now decodes the fields of an `application/x-www-form-urlencoded`
or `multipart/form-data` request that
+declares no charset as UTF-8, the encoding browsers use. Before, Undertow's
default ISO-8859-1 was used, so `café`
+became `café` in the headers and the body map. A charset that the request or
a part declares is still used: a client
+that sends ISO-8859-1 form data without declaring a charset now gets U+FFFD
replacement characters instead of its
+text, and must declare its charset (for example
`application/x-www-form-urlencoded; charset=ISO-8859-1`).
+
=== camel-github2 - consumers and items of a poll that were not routed
The `github2` consumers (`commit`, `event`, `pullRequest`,
`pullRequestComment`, `tag`) remembered every item of a