Federico Mariani created CAMEL-24147:
----------------------------------------
Summary: camel-lra: participant callback URLs are not URL-encoded
and parseQuery is lossy - compensation breaks for URIs with query parameters or
options with special characters
Key: CAMEL-24147
URL: https://issues.apache.org/jira/browse/CAMEL-24147
Project: Camel
Issue Type: Bug
Components: camel-lra
Reporter: Federico Mariani
Attachments: LRAUrlEncodingIssueTest.java
h3. Problem
Two related defects:
# {{LRAUrlBuilder.query()}} concatenates keys and values raw
(components/camel-lra/src/main/java/org/apache/camel/service/lra/LRAUrlBuilder.java:89-98)
- no {{URLEncoder}}. {{LRAClient.join()}} embeds the compensation/completion
endpoint URI and all evaluated saga option values as query parameters of the
participant callback URL registered with the LRA coordinator (Link header). Any
value containing {{&}}, {{=}}, {{#}}, {{+}} or a space corrupts the callback
URL.
# {{LRASagaRoutes.parseQuery}} splits with {{element.split("=")}} and takes
index 1 (LRASagaRoutes.java:71-73), so even a single embedded {{=}} drops
everything after the second {{=}}. Should be {{split("=", 2)}} with symmetric
encoding/decoding. Valueless parameters also trigger the noisy
{{saveArrayAccess}} WARN with a stack trace.
h3. Consequence
For a saga step with e.g.
{{compensation("seda:cancelOrder?concurrentConsumers=2&size=1000")}} the
reconstructed URI does not match the registered one, {{verifyRequest()}} throws
"URI ... is not allowed", and *compensation never runs while the LRA
coordinator believes it succeeded*. Option values are corrupted the same way
({{ACME®ion=EU}} arrives as {{ACME}}; {{a+b}} arrives as {{a b}} because
{{URLDecoder}} is applied to text that was never encoded).
This goes unnoticed today only because the documented examples all use plain
{{direct:}} URIs without parameters.
h3. Reproducer
Attached {{LRAUrlEncodingIssueTest.java}} (goes into
{{components/camel-lra/src/test/java/org/apache/camel/service/lra/}}, no LRA
coordinator needed, both tests fail on current main):
* {{testCallbackUrlRoundTrip}}: builds the compensate callback URL exactly like
{{LRAClient.join()}} and parses it back exactly like
{{LRASagaRoutes.verifyRequest()}} - values do not round-trip.
* {{testVerifyRequestAcceptsRegisteredUriWithQueryParams}}: simulates the
coordinator invoking the compensate callback (CamelHttpQuery fallback path of
CAMEL-17751) - {{verifyRequest}} rejects the registered compensation URI.
h3. Suggested fix
URL-encode key and value in {{LRAUrlBuilder.query()}}, and parse with
{{split("&")}} / {{split("=", 2)}} plus matching decoding in {{parseQuery}}.
Found during a broader Saga EIP / camel-lra code review (July 2026).
_Claude Code on behalf of Croway_
--
This message was sent by Atlassian Jira
(v8.20.10#820010)