Serdar Gökay created CAMEL-25306:
------------------------------------
Summary: camel-rest-openapi - contract-first path parameter
headers are not percent-decoded
Key: CAMEL-25306
URL: https://issues.apache.org/jira/browse/CAMEL-25306
Project: Camel
Issue Type: Improvement
Components: camel-rest-openapi
Affects Versions: 4.22.1
Reporter: Serdar Gökay
{{RestOpenApiProcessor.process()}} maps the operation's path placeholders from
{{Exchange.HTTP_PATH}}:
{code:java}
String path = exchange.getMessage().getHeader(Exchange.HTTP_PATH, String.class);
...
// map path-parameters from operation to camel headers
HttpHelper.evalPlaceholders(exchange.getMessage().getHeaders(), path,
consumerPath);
{code}
{{HTTP_PATH}} is the raw request path ({{RoutingContext.normalizedPath()}} in
camel-platform-http-vertx, {{getRawPath()}} in camel-http-common), so a
contract-first operation receives its path parameters still percent-encoded.
The Rest DSL bindings that call the same helper pass a decoded path
({{getPathInfo()}} in camel-servlet, {{getRequestPath()}} in camel-undertow,
{{URI.getPath()}} in camel-netty-http), and camel-platform-http-vertx uses the
decoded {{ctx.pathParams()}}. CAMEL-24577 aligned the Spring Boot platform-http
engine with the same behaviour.
*Reproducer* (Camel JBang 4.22.1, {{camel run openapi.json routes.camel.yaml
application.properties}}):
{code:title=openapi.json}
{"openapi":"3.0.3","info":{"title":"t","version":"1"},"servers":[{"url":"/api"}],
"paths":{"/items/{id}":{"get":{"operationId":"getItem","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}}}}
{code}
{code:title=routes.camel.yaml}
- route:
from:
uri: rest-openapi:openapi.json
steps:
- log: "contract-first done"
- route:
from:
uri: direct:getItem
steps:
- setBody:
simple: "id=[${header.id}]"
- rest:
path: /dsl
get:
- path: /items/{id}
to: direct:getItem
{code}
{code:title=application.properties}
camel.server.enabled=true
camel.server.port=8080
camel.rest.component=platform-http
camel.rest.bindingMode=off
camel.rest.inlineRoutes=false
{code}
Both consumers dispatch to the same route:
||Request||Response||
|{{/api/items/A%221}}|{{id=[A%221]}}|
|{{/dsl/items/A%221}}|{{id=[A"1]}}|
|{{/api/items/X%20Y}}|{{id=[X%20Y]}}|
|{{/dsl/items/X%20Y}}|{{id=[X Y]}}|
|{{/api/items/caf%C3%A9}}|{{id=[caf%C3%A9]}}|
|{{/dsl/items/caf%C3%A9}}|{{id=[café]}}|
With {{clientRequestValidation=true}}, the request validator works on the
decoded value (a {{pattern: '^\\S+$'}} parameter rejects {{X%20Y}}), while the
route receives the encoded text.
*Proposed change*: decode each value after the path has been split, so that an
encoded {{/}} stays inside its parameter and {{+}} is kept as is (path-segment
decoding, not form decoding). {{RestOpenApiProcessor}} could pass a
{{BiConsumer}} to the existing {{evalPlaceholders(BiConsumer, String, String)}}
overload that decodes the value before setting the header.
{{RestOpenApiProcessor}} is unchanged on main.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)