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)

Reply via email to