shashank created CAMEL-25316:
--------------------------------
Summary: camel-http-common - a String response is written in the
exchange charset while the Content-Type declares another charset (servlet and
Jetty consumers, chunked by default)
Key: CAMEL-25316
URL: https://issues.apache.org/jira/browse/CAMEL-25316
Project: Camel
Issue Type: Bug
Components: camel-http-common, camel-servlet, camel-jetty
Reporter: shashank
{{DefaultHttpBinding.doWriteDirectResponse}} sets the response {{Content-Type}}
as the route declares it, and then writes the body through an input stream:
{code:java}
if (checkChunked(message, exchange)) { // chunked=true is the
default
is = message.getBody(InputStream.class);
} else if (!isText(contentType)) { // for example
application/json
is =
exchange.getContext().getTypeConverter().tryConvertTo(InputStream.class,
message.getBody());
}
{code}
A {{String}} body is converted with the charset of the exchange, which for a
servlet or Jetty consumer is the charset of the request, UTF-8 when the request
names none (every GET). So a route that answers with {{text/plain;
charset=ISO-8859-1}} (or {{application/json; charset=ISO-8859-1}} without
chunking) sends UTF-8 bytes under an ISO-8859-1 header, and the client reads
{{GrüÃe}} instead of {{Grüße}}. Only the non-chunked text path writes the body
and the header in one charset (it replaces the declared charset with the
exchange charset).
h3. Reproduction
New {{ServletStringResponseCharsetTest}} (camel-servlet, embedded Undertow): a
chunked {{text/plain; charset=ISO-8859-1}} response and a non-chunked
{{application/json; charset=ISO-8859-1}} response with {{Grüße aus Köln}},
decoded with the charset of the response header: both fail on main ({{expected:
<Grüße aus Köln> but was: <GrüÃe aus Köln>}}), in two runs; the non-chunked
{{text/plain}} response is the control and passes.
h3. Proposed fix
When the body is a {{String}} and the Content-Type declares a supported charset
(parameter name matched case-insensitively, RFC 9110 5.6.6), convert it with
that charset in the two stream paths. Responses without a declared charset,
bodies that are not a {{String}}, and the non-chunked text path are unchanged.
Not changed either: a gzip-encoded response ({{Content-Encoding: gzip}},
{{doWriteGZIPResponse}}) still converts a {{String}} body with the exchange
charset; it can follow the same way if wanted. Upgrade guide entry, as for
CAMEL-25217 and CAMEL-25248. With the fix: camel-http-common, camel-servlet
(105 tests) and camel-jetty (395 tests; {{JettyXsltHttpTemplateTest}} fails on
this machine with and without the change, {{0.0.0.0}} binding) pass.
Found with a Lean 4 model of the four write paths: the property "the bytes are
in the charset that the response header names" fails on main for the chunked
and the non-text paths for every text with a character outside US-ASCII
(declared ISO-8859-1, UTF-8 exchange), and holds for the fix in all four paths;
the fix equals main when the declared charset is the exchange charset and in
the non-chunked text path. The model abstracts the parsing of the Content-Type
(case, quotes) and the fallback for an unsupported charset name; those are
covered by reading the code only.
Affected: 4.14.x, 4.18.x and main (same code).
Priority Minor, as for CAMEL-25217 and CAMEL-25248: the default (chunked)
configuration is affected, but only a route that declares a charset other than
the exchange charset (UTF-8 for a GET) sees it, and the client gets the right
text in the wrong charset (wrong characters, no lost or truncated body).
Duplicate check (2026-10-04): JIRA components camel-servlet / camel-jetty /
camel-http-common with charset or encoding (36 issues, CAMEL-3443 and
CAMEL-12424 are about the request), "DefaultHttpBinding" with charset or
chunked: none on this. GitHub pull requests "DefaultHttpBinding charset",
"servlet response charset": none.
_Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)