[
https://issues.apache.org/jira/browse/CAMEL-25685?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Work on CAMEL-25685 started by shashank.
----------------------------------------
> camel-jetty - the query parameters of a request are silently not mapped to
> headers when Jetty cannot read its body parameters (a form-urlencoded body
> over 200000 bytes, or any multipart request with enableMultipartFilter=false)
> -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-25685
> URL: https://issues.apache.org/jira/browse/CAMEL-25685
> Project: Camel
> Issue Type: Bug
> Components: camel-jetty
> Reporter: shashank
> Assignee: shashank
> Priority: Major
>
> The jetty consumer maps the query parameters of a request to headers in
> {{AttachmentHttpBinding.populateRequestParameters}} (camel-jetty, package
> {{org.apache.camel.component.jetty12}}, lines 122-166 on main cd2ce899764e)
> through {{request.getParameterNames()}}. Jetty 12
> ({{ServletApiRequest.getParameters()}}, 12.1.14, read with javap) reads the
> body parameters first and the query parameters after them, and throws for the
> whole request when it cannot read the body parameters. The {{catch (Exception
> e)}} around the loop only logs at DEBUG ("Failed to populate request
> parameters, likely due to multipart parsing failure"), so the route gets the
> exchange without any query parameter and the client gets 200.
> {{CamelHttpQuery}} still holds the raw query string; the headers of the
> parameters are missing.
> Two requests that were measured take this path (any other failure of
> {{getParameterNames()}}, for example an I/O error while Jetty reads the form,
> goes the same way):
> * *A form-urlencoded POST whose Content-Length is over Jetty's
> {{maxFormContentSize}} (200000 bytes by default)*, with default endpoint
> options. Jetty throws {{BadMessageException("Unable to parse form
> content")}}. The form fields themselves are not affected: Camel maps them
> from the body in {{DefaultHttpBinding.readFormUrlEncodedBody}}. (With
> {{mapHttpMessageFormUrlEncodedBody=false}} the route gets no form field even
> for a small form, so on this path Jetty only ever supplies the query
> parameters; measured, not root-caused.) Only the query parameters are lost.
> Example: {{POST /upload?customerId=42}} with a form of 250 KB (for example a
> base64 file in a form field): {{header("customerId")}} is null in the route.
> * *Any multipart request on an endpoint with
> {{enableMultipartFilter=false}}*, whatever its size. This is the case the
> catch was written for (the comment says the failure is expected there), but
> the query parameters are dropped with the multipart body parameters.
> The number of form keys is not a trigger: a form with 5000 keys is mapped
> completely (the keys come from Camel's own form parsing).
> h3. Other consumers, same request (form of 250000 bytes with {{?q=1&q2=x}},
> default options, measured on main)
> ||consumer||form of 199000 bytes||form of 250000 bytes||form of 3 MB||form
> with 1002 / 5000 keys||
> |camel-jetty|200, all mapped|200, *query parameters missing*|200, *query
> parameters missing*|200, all mapped|
> |camel-servlet (in the module's Undertow servlet container)|200, all
> mapped|200, all mapped|413|200, all mapped|
> |camel-undertow|200, all mapped|200, all mapped|413|500|
> |camel-platform-http-vertx|400|400|400|400|
> So the other consumers either map everything or fail the request; only
> camel-jetty routes the exchange without data the client sent, without an
> error or a log line above DEBUG.
> h3. Reproduction
> New {{HttpBindingQueryParametersUnreadableBodyTest}} (camel-jetty), with a
> JDK {{HttpClient}}. On main:
> * {{POST /form?id=42&tag=a&tag=b&text=hello+world}}, form
> {{name=camel&data=<250000 x>}}: {{expected: <42> but was: <null>}} (the form
> fields {{name}} and {{data}} are mapped);
> * the same with {{?id=42&CamelHttpResponseCode=500}}: {{expected: <42> but
> was: <null>}};
> * a multipart request to an endpoint with {{enableMultipartFilter=false}} and
> {{?id=42}}: {{expected: <42> but was: <null>}}.
> h3. Proposed fix
> When {{getParameterNames()}} itself fails, decode the query string with
> Jetty's {{UrlEncoded.decodeQuery}} (UTF-8, as Jetty's default query encoding)
> and map its parameters the same way as the normal path (values sanitized with
> {{HttpHelper.sanitizeLog}}, the header filter strategy applied, repeated
> names as a list). The fallback only runs when the exception came from
> {{getParameterNames()}} itself, so nothing is mapped twice. A query that
> cannot be decoded (for example {{%E9}}) is still skipped with the DEBUG log,
> as before; that case (malformed client input) is not changed. The behaviour
> for requests that Jetty reads without error does not change.
> No option or default changes. The only difference users see is that these
> requests now get their query parameter headers, as every other request to the
> same endpoint does; for multipart requests with
> {{enableMultipartFilter=false}} these headers are missing on every branch
> that has the catch. No upgrade guide entry proposed (can be added if wanted).
> Not changed: REST DSL endpoints on jetty ({{JettyRestHttpBinding}}, which
> forces {{enableMultipartFilter=false}}) go through
> {{DefaultHttpBinding.populateRequestParameters}} without this catch; there a
> 250 KB form or a multipart request answers 500 (measured). That is a loud
> failure, not data loss, and is left for a separate discussion.
> Tests: the three above fail on main and pass with the fix; a fourth
> ({{?id=%E9}} with a large form, not mapped) passes on main and pins the
> unchanged path. camel-jetty with the fix (camel-http-base and
> camel-http-common built in the same reactor): 407 run, 23 skipped, 1 failure
> and 1 error that occur the same way without the change on this machine
> ({{JettyXsltHttpTemplateTest}}: the stylesheet is fetched from {{0.0.0.0}};
> {{JettyEmptyCharsetParameterTest}}: 500, the module was tested against a
> local camel-support snapshot older than main).
> How it was found: a follow-up of an earlier differential test of the HTTP
> consumers, which had shown that an invalid query escape makes camel-jetty
> drop all parameters through the same catch (malformed input, kept out of this
> ticket). The form limits of Jetty go through the same call; the table above
> was then measured with the same request against each consumer.
> Affected: main (cd2ce899764e; tested). camel-4.23.x (Jetty 12.1.14),
> camel-4.22.x and camel-4.18.x (Jetty 12.1.12) have the same catch (read, not
> run). camel-4.14.x (Jetty 12.0) has no catch there, so the exception is not
> swallowed (read, not run).
> Duplicate check (2026-10-15): JIRA text "maxFormContentSize", "maxFormKeys"
> (nothing), "AttachmentHttpBinding" (CAMEL-25634 and older multipart tickets,
> none about query parameters); GitHub pull requests "AttachmentHttpBinding"
> (only the open PR of CAMEL-25634, which changes the attachment branch inside
> the same {{try}}; the two changes merge without conflict),
> "maxFormContentSize", "maxFormKeys" (nothing).
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)