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

Reply via email to