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 64e4d286224d CAMEL-25457: camel-http-common - write a non-chunked text 
String response in the charset that the Content-Type declares (#27594)
64e4d286224d is described below

commit 64e4d286224d00613f17b95dcdb904c89ee745d4
Author: allthingssecurity <[email protected]>
AuthorDate: Fri Oct 9 22:16:52 2026 +0530

    CAMEL-25457: camel-http-common - write a non-chunked text String response 
in the charset that the Content-Type declares (#27594)
    
    DefaultHttpBinding.doWriteDirectResponse wrote a non-chunked String body 
with a text
    Content-Type through its last fallback, which set the Content-Length and 
the character
    encoding with the charset of the exchange. That replaced the charset the 
route declared
    in the Content-Type, so text/plain; charset=ISO-8859-1 was sent as
    text/plain;charset=UTF-8. After CAMEL-25316 this was the only path that did 
not honour
    the declared charset.
    
    The fallback now uses the charset that the Content-Type declares when the 
body is a
    String and the charset is supported, and the charset of the exchange 
(CamelCharsetName)
    otherwise. The Content-Type parsing from CAMEL-25316 moves into 
getContentTypeCharset,
    shared by both places. ServletStringResponseCharsetTest checks the response 
charset,
    Content-Length and bytes. Upgrade guide entry added.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../camel/http/common/DefaultHttpBinding.java      | 26 ++++++++++++++++----
 .../servlet/ServletStringResponseCharsetTest.java  | 28 ++++++++++++++++++++--
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    | 11 +++++++++
 3 files changed, 59 insertions(+), 6 deletions(-)

diff --git 
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/DefaultHttpBinding.java
 
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/DefaultHttpBinding.java
index aa242fe85d65..3d8e32fcc315 100644
--- 
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/DefaultHttpBinding.java
+++ 
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/DefaultHttpBinding.java
@@ -587,10 +587,14 @@ public class DefaultHttpBinding implements HttpBinding {
             }
         } else {
             // not convertable as a stream so fallback as a String
+            // a String body is written in the charset of the content type, 
other bodies are converted to a String
+            // with the charset of the exchange and written in that charset
+            Charset contentTypeCharset = message.getBody() instanceof String ? 
getContentTypeCharset(contentType) : null;
             String data = message.getBody(String.class);
             if (data != null) {
                 // set content length and encoding before we write data
-                String charset = ExchangeHelper.getCharsetName(exchange, true);
+                String charset = contentTypeCharset != null
+                        ? contentTypeCharset.name() : 
ExchangeHelper.getCharsetName(exchange, true);
                 final int dataByteLength = data.getBytes(charset).length;
                 response.setCharacterEncoding(charset);
                 response.setContentLength(dataByteLength);
@@ -613,16 +617,30 @@ public class DefaultHttpBinding implements HttpBinding {
      * then converted with the type converter (the charset of the exchange).
      */
     private static InputStream toInputStreamWithContentTypeCharset(Message 
message, String contentType) {
-        if (contentType != null && message.getBody() instanceof String text) {
+        if (message.getBody() instanceof String text) {
+            Charset charset = getContentTypeCharset(contentType);
+            if (charset != null) {
+                return new ByteArrayInputStream(text.getBytes(charset));
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the charset that the content type declares, or <tt>null</tt> 
when it declares none or one that is not
+     * supported.
+     */
+    private static Charset getContentTypeCharset(String contentType) {
+        if (contentType != null) {
             for (String part : contentType.split(";")) {
                 part = part.trim();
                 // the parameter name is case-insensitive (RFC 9110, section 
5.6.6)
                 if (part.regionMatches(true, 0, "charset=", 0, 8)) {
                     String charset = 
IOHelper.normalizeCharset(part.substring(8));
                     try {
-                        return new 
ByteArrayInputStream(text.getBytes(Charset.forName(charset)));
+                        return Charset.forName(charset);
                     } catch (IllegalArgumentException e) {
-                        // unsupported or illegal charset name: keep the 
conversion with the exchange charset
+                        // unsupported or illegal charset name: keep the 
charset of the exchange
                         LOG.debug("Cannot use charset {} of the content type: 
{}", charset, e.getMessage());
                         return null;
                     }
diff --git 
a/components/camel-servlet/src/test/java/org/apache/camel/component/servlet/ServletStringResponseCharsetTest.java
 
b/components/camel-servlet/src/test/java/org/apache/camel/component/servlet/ServletStringResponseCharsetTest.java
index 72214c3ca195..c92f42e49c6c 100644
--- 
a/components/camel-servlet/src/test/java/org/apache/camel/component/servlet/ServletStringResponseCharsetTest.java
+++ 
b/components/camel-servlet/src/test/java/org/apache/camel/component/servlet/ServletStringResponseCharsetTest.java
@@ -18,12 +18,14 @@ package org.apache.camel.component.servlet;
 
 import java.io.InputStream;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 
 import org.apache.camel.Exchange;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.util.IOHelper;
 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.assertNotNull;
 
@@ -46,8 +48,26 @@ class ServletStringResponseCharsetTest extends 
ServletCamelRouterTestSupport {
     }
 
     @Test
-    void notChunkedTextResponse() throws Exception {
-        assertEquals(TEXT, queryAndDecode("/notChunked"));
+    void notChunkedTextResponseInDeclaredCharset() throws Exception {
+        assertResponseInCharset("/notChunked", StandardCharsets.ISO_8859_1);
+    }
+
+    @Test
+    void notChunkedTextResponseWithoutDeclaredCharset() throws Exception {
+        // no charset declared: the charset of the exchange, UTF-8 for a GET 
request
+        assertResponseInCharset("/notChunkedNoCharset", 
StandardCharsets.UTF_8);
+    }
+
+    private void assertResponseInCharset(String path, Charset expected) throws 
Exception {
+        WebResponse response = query(new GetMethodWebRequest(contextUrl + 
"/services" + path));
+        String contentType = response.getHeaderField("Content-Type");
+        String charset = IOHelper.getCharsetNameFromContentType(contentType);
+        assertEquals(expected, Charset.forName(charset), "charset of the 
response Content-Type " + contentType);
+        byte[] expectedBytes = TEXT.getBytes(expected);
+        assertEquals(String.valueOf(expectedBytes.length), 
response.getHeaderField("Content-Length"));
+        try (InputStream is = response.getInputStream()) {
+            assertArrayEquals(expectedBytes, is.readAllBytes());
+        }
     }
 
     private String queryAndDecode(String path) throws Exception {
@@ -76,6 +96,10 @@ class ServletStringResponseCharsetTest extends 
ServletCamelRouterTestSupport {
                 from("servlet:/notChunked?chunked=false")
                         .setHeader(Exchange.CONTENT_TYPE, 
constant("text/plain; charset=ISO-8859-1"))
                         .setBody(constant(TEXT));
+
+                from("servlet:/notChunkedNoCharset?chunked=false")
+                        .setHeader(Exchange.CONTENT_TYPE, 
constant("text/plain"))
+                        .setBody(constant(TEXT));
             }
         };
     }
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 c57fe1b0eac5..77e4031403e2 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
@@ -981,6 +981,17 @@ gzip-encoded responses (`Content-Encoding: gzip`) are 
written as before. A clien
 and read such a response as UTF-8 must now use the declared charset, and 
characters that the declared charset cannot
 represent are written as `?`.
 
+==== A response that is not chunked and has a text Content-Type
+
+A `String` response body that is not chunked (`chunked=false` or the 
`CamelHttpChunked` header) and has a text
+`Content-Type` (one that contains `text` or `html`) was written in the charset 
of the exchange (`CamelCharsetName`,
+which the consumer sets from the request charset, else UTF-8), which also 
replaced the charset that the `Content-Type`
+declared (`text/plain; charset=ISO-8859-1` was sent as 
`text/plain;charset=UTF-8`). Such a body is now written in the
+charset that the `Content-Type` declares, and the response keeps that charset: 
`CamelCharsetName` no longer overrides a
+charset declared in the `Content-Type` here.
+Responses without a charset in the `Content-Type`, and bodies that are not a 
`String`, are written as before.
+Characters that the declared charset cannot represent are written as `?`.
+
 === camel-platform-http-vertx - String response in the charset of the 
Content-Type
 
 A `String` response body was always written as UTF-8, even when the response 
`Content-Type` declared another

Reply via email to