[ 
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)

Reply via email to