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