You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/10/09 16:46:59 UTC
(camel) branch main updated: CAMEL-25457: camel-http-common - write a non-chunked text String response in the charset that the Content-Type declares (#27594)
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 <49...@users.noreply.github.com>
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 <no...@anthropic.com>
---
.../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