This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new ba5aced7f6a1 CAMEL-24418:
camel-http/http-common/netty-http/undertow/vertx-http - do not resolve property
placeholders in HTTP URI override headers
ba5aced7f6a1 is described below
commit ba5aced7f6a1c38a963020575676ad4c76b492cf
Author: Andrea Cosentino <[email protected]>
AuthorDate: Fri Aug 28 10:51:45 2026 +0200
CAMEL-24418: camel-http/http-common/netty-http/undertow/vertx-http - do not
resolve property placeholders in HTTP URI override headers
The HTTP producers passed message-supplied endpoint-URI override headers
(CamelHttpUri, CamelRestHttpUri) through
CamelContext.resolvePropertyPlaceholders(),
so a {{...}} token arriving in a message was expanded against the
application's
property sources. Property placeholders are a route/configuration authoring
feature meant to be resolved at build time on the endpoint URI written in
the
route, not on untrusted header values at runtime — the same contract
CAMEL-24282
restored for toD and enrich.
Fixes seven call sites across camel-http, camel-http-common,
camel-netty-http,
camel-undertow, and camel-vertx-http. Adds HttpUriHeaderPlaceholderTest
covering
both the createURL and createMethod paths, and fixes an existing
camel-jetty test
that relied on the removed header-expansion behavior.
Closes #25571
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
.../org/apache/camel/http/common/HttpHelper.java | 18 +---
.../component/http/helper/HttpMethodHelper.java | 9 +-
.../http/HttpUriHeaderPlaceholderTest.java | 104 +++++++++++++++++++++
.../jetty/HttpEndpointUriEncodingIssueTest.java | 11 ++-
.../component/netty/http/NettyHttpHelper.java | 9 +-
.../camel/component/undertow/UndertowHelper.java | 17 +---
.../component/vertx/http/VertxHttpHelper.java | 4 +-
.../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 31 ++++++
8 files changed, 157 insertions(+), 46 deletions(-)
diff --git
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
index 93195b91d95b..809c9f00679b 100644
---
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
+++
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
@@ -33,7 +33,6 @@ import jakarta.servlet.http.HttpServletRequest;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
-import org.apache.camel.RuntimeExchangeException;
import org.apache.camel.converter.stream.CachedOutputStream;
import org.apache.camel.support.CamelObjectInputStream;
import org.apache.camel.support.DeserializationFilterHelper;
@@ -211,12 +210,9 @@ public final class HttpHelper {
uri = endpoint.getHttpUri().toASCIIString();
}
- // resolve placeholders in uri
- try {
- uri = exchange.getContext().resolvePropertyPlaceholders(uri);
- } catch (Exception e) {
- throw new RuntimeExchangeException("Cannot resolve property
placeholders with uri: " + uri, exchange, e);
- }
+ // NOTE: no placeholder resolution here. When uri came from the
endpoint it was already resolved at
+ // build time, and when it came from the CamelHttpUri header it
carries message content
+ // (see CAMEL-24282 / CAMEL-24418)
// append HTTP_PATH to HTTP_URI if it is provided in the header
String path = exchange.getIn().getHeader(Exchange.HTTP_PATH,
String.class);
@@ -331,12 +327,8 @@ public final class HttpHelper {
String queryString = exchange.getIn().getHeader(Exchange.HTTP_QUERY,
String.class);
// We need also check the HTTP_URI header query part
String uriString = exchange.getIn().getHeader(Exchange.HTTP_URI,
String.class);
- // resolve placeholders in uriString
- try {
- uriString =
exchange.getContext().resolvePropertyPlaceholders(uriString);
- } catch (Exception e) {
- throw new RuntimeExchangeException("Cannot resolve property
placeholders with uri: " + uriString, exchange, e);
- }
+ // NOTE: property placeholders are resolved at build time on the
endpoint uri written in the route,
+ // never on this header value, which carries message content (see
CAMEL-24282 / CAMEL-24418)
if (uriString != null) {
// in case the URI string contains unsafe characters
uriString = UnsafeUriCharactersEncoder.encodeHttpURI(uriString);
diff --git
a/components/camel-http/src/main/java/org/apache/camel/component/http/helper/HttpMethodHelper.java
b/components/camel-http/src/main/java/org/apache/camel/component/http/helper/HttpMethodHelper.java
index 966f1c74fdd4..5f3b94bff3b4 100644
---
a/components/camel-http/src/main/java/org/apache/camel/component/http/helper/HttpMethodHelper.java
+++
b/components/camel-http/src/main/java/org/apache/camel/component/http/helper/HttpMethodHelper.java
@@ -20,7 +20,6 @@ import java.net.URI;
import java.net.URISyntaxException;
import org.apache.camel.Exchange;
-import org.apache.camel.RuntimeExchangeException;
import org.apache.camel.StreamCache;
import org.apache.camel.component.http.HttpConstants;
import org.apache.camel.component.http.HttpEndpoint;
@@ -46,12 +45,8 @@ public final class HttpMethodHelper {
uriString = exchange.getIn().getHeader(HttpConstants.HTTP_URI,
String.class);
}
if (uriString != null) {
- // resolve placeholders in uriString
- try {
- uriString =
exchange.getContext().resolvePropertyPlaceholders(uriString);
- } catch (Exception e) {
- throw new RuntimeExchangeException("Cannot resolve property
placeholders with uri: " + uriString, exchange, e);
- }
+ // NOTE: property placeholders are resolved at build time on the
endpoint uri written in the route,
+ // never on this header value, which carries message content (see
CAMEL-24282 / CAMEL-24418)
// in case the URI string contains unsafe characters
uriString = UnsafeUriCharactersEncoder.encodeHttpURI(uriString);
URI uri = new URI(uriString);
diff --git
a/components/camel-http/src/test/java/org/apache/camel/component/http/HttpUriHeaderPlaceholderTest.java
b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpUriHeaderPlaceholderTest.java
new file mode 100644
index 000000000000..5573f0756953
--- /dev/null
+++
b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpUriHeaderPlaceholderTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.http;
+
+import java.util.Properties;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.http.common.HttpHelper;
+import org.apache.camel.http.common.HttpMethods;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.DefaultExchange;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Property placeholders are resolved at build time on the endpoint uri
written in the route, never on the
+ * {@code CamelHttpUri} header, which carries message content. See CAMEL-24282
/ CAMEL-24418.
+ */
+class HttpUriHeaderPlaceholderTest {
+
+ @Test
+ void placeholderInHttpUriHeaderIsNotResolved() throws Exception {
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ Properties prop = new Properties();
+ prop.setProperty("secretValue", "s3cr3t");
+ context.getPropertiesComponent().setInitialProperties(prop);
+ context.start();
+
+ HttpEndpoint endpoint =
context.getEndpoint("http://localhost/base", HttpEndpoint.class);
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setHeader(Exchange.HTTP_URI,
"http://localhost/api?k={{secretValue}}");
+
+ // the token survives as a literal (percent-encoded by
UnsafeUriCharactersEncoder, as any other
+ // unsafe character in a header-supplied uri would be) and is
never expanded
+ assertThat(HttpHelper.createURL(exchange, endpoint))
+
.isEqualTo("http://localhost/api?k=%7B%7BsecretValue%7D%7D")
+ .doesNotContain("s3cr3t");
+ }
+ }
+
+ @Test
+ void placeholderInHttpUriHeaderQueryIsNotResolvedByCreateMethod() throws
Exception {
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ context.start();
+
+ HttpEndpoint endpoint =
context.getEndpoint("http://localhost/base", HttpEndpoint.class);
+ Exchange exchange = new DefaultExchange(context);
+ // createMethod parses the query out of this header too, and
bridgeEndpoint does not bound it the
+ // way it bounds createURL. The key is deliberately NOT a defined
property: had the header been run
+ // through the placeholder resolver, an unknown key would fail
fast rather than survive as a literal.
+ exchange.getIn().setHeader(Exchange.HTTP_URI,
"http://localhost/api?k={{noSuchProperty}}");
+
+ // a query string is present, so GET is selected, and the token
never reaches the resolver
+ assertThat(HttpHelper.createMethod(exchange, endpoint,
false)).isEqualTo(HttpMethods.GET);
+ }
+ }
+
+ @Test
+ void placeholderInHttpUriHeaderIsNotResolvedByCreateUrlEither() throws
Exception {
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ context.start();
+
+ HttpEndpoint endpoint =
context.getEndpoint("http://localhost/base", HttpEndpoint.class);
+ Exchange exchange = new DefaultExchange(context);
+ exchange.getIn().setHeader(Exchange.HTTP_URI,
"http://localhost/api?k={{noSuchProperty}}");
+
+ // same guarantee on the createURL path, again with an undefined
key so that any future
+ // resolution of this header would surface as a failure rather
than silently expanding
+ assertThat(HttpHelper.createURL(exchange, endpoint))
+
.isEqualTo("http://localhost/api?k=%7B%7BnoSuchProperty%7D%7D");
+ }
+ }
+
+ @Test
+ void placeholderInEndpointUriIsResolvedAtBuildTime() throws Exception {
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ Properties prop = new Properties();
+ prop.setProperty("basePath", "resolved");
+ context.getPropertiesComponent().setInitialProperties(prop);
+ context.start();
+
+ // control: a placeholder written in the route's endpoint uri is
still resolved, as before
+ HttpEndpoint endpoint =
context.getEndpoint("http://localhost/{{basePath}}", HttpEndpoint.class);
+ Exchange exchange = new DefaultExchange(context);
+
+ assertThat(HttpHelper.createURL(exchange,
endpoint)).isEqualTo("http://localhost/resolved");
+ }
+ }
+}
diff --git
a/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/HttpEndpointUriEncodingIssueTest.java
b/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/HttpEndpointUriEncodingIssueTest.java
index 56e4650b4576..5b0d5a7eddd7 100644
---
a/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/HttpEndpointUriEncodingIssueTest.java
+++
b/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/HttpEndpointUriEncodingIssueTest.java
@@ -21,12 +21,13 @@ import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
*
*/
-public class HttpEndpointUriEncodingIssueTest extends BaseJettyTest {
+class HttpEndpointUriEncodingIssueTest extends BaseJettyTest {
@Test
public void testEndpointUriEncodingIssue() {
@@ -45,11 +46,13 @@ public class HttpEndpointUriEncodingIssueTest extends
BaseJettyTest {
}
@Test
- public void testEndpointHeaderUriEncodingIssue() {
- String uri =
"http://localhost:{{port}}/myapp/mytest?columns=totalsens,upsens&username=apiuser";
+ void testEndpointHeaderUriEncodingIssue() {
+ // the uri is supplied via the CamelHttpUri header, which is message
content and therefore not
+ // subject to property placeholder resolution, so the port must be
resolved up-front here
+ String uri = "http://localhost:" + getPort() +
"/myapp/mytest?columns=totalsens,upsens&username=apiuser";
String out = template.requestBodyAndHeader("http://localhost/dummy",
null, Exchange.HTTP_URI, uri, String.class);
- assertEquals("We got totalsens,upsens columns", out);
+ assertThat(out).isEqualTo("We got totalsens,upsens columns");
}
@Override
diff --git
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java
index 80192f05322d..4ce470be55bd 100644
---
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java
+++
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java
@@ -27,7 +27,6 @@ import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
-import org.apache.camel.RuntimeExchangeException;
import org.apache.camel.support.DeserializationFilterHelper;
import org.apache.camel.util.CollectionHelper;
import org.apache.camel.util.IOHelper;
@@ -165,12 +164,8 @@ public final class NettyHttpHelper {
uri = endpoint.getEndpointUri();
}
- // resolve placeholders in uri
- try {
- uri = exchange.getContext().resolvePropertyPlaceholders(uri);
- } catch (Exception e) {
- throw new RuntimeExchangeException("Cannot resolve property
placeholders with uri: " + uri, exchange, e);
- }
+ // NOTE: property placeholders are resolved at build time on the
endpoint uri written in the route,
+ // never on the message-supplied override headers (see CAMEL-24282 /
CAMEL-24418)
// append HTTP_PATH to HTTP_URI if it is provided in the header
String path = exchange.getIn().getHeader(NettyHttpConstants.HTTP_PATH,
String.class);
diff --git
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHelper.java
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHelper.java
index 56b9e5785917..460d12a30664 100644
---
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHelper.java
+++
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHelper.java
@@ -23,7 +23,6 @@ import java.util.Map;
import io.undertow.util.HttpString;
import io.undertow.util.Methods;
import org.apache.camel.Exchange;
-import org.apache.camel.RuntimeExchangeException;
import org.apache.camel.util.CollectionHelper;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.URISupport;
@@ -51,12 +50,8 @@ public final class UndertowHelper {
uri = endpoint.getHttpURI().toASCIIString();
}
- // resolve placeholders in uri
- try {
- uri = exchange.getContext().resolvePropertyPlaceholders(uri);
- } catch (Exception e) {
- throw new RuntimeExchangeException("Cannot resolve property
placeholders with uri: " + uri, exchange, e);
- }
+ // NOTE: property placeholders are resolved at build time on the
endpoint uri written in the route,
+ // never on the message-supplied override headers (see CAMEL-24282 /
CAMEL-24418)
// append HTTP_PATH to HTTP_URI if it is provided in the header
String path = exchange.getIn().getHeader(UndertowConstants.HTTP_PATH,
String.class);
@@ -126,12 +121,8 @@ public final class UndertowHelper {
String queryString =
exchange.getIn().getHeader(UndertowConstants.HTTP_QUERY, String.class);
// We need also check the HTTP_URI header query part
String uriString =
exchange.getIn().getHeader(UndertowConstants.HTTP_URI, String.class);
- // resolve placeholders in uriString
- try {
- uriString =
exchange.getContext().resolvePropertyPlaceholders(uriString);
- } catch (Exception e) {
- throw new RuntimeExchangeException("Cannot resolve property
placeholders with uri: " + uriString, exchange, e);
- }
+ // NOTE: property placeholders are resolved at build time on the
endpoint uri written in the route,
+ // never on this header value, which carries message content (see
CAMEL-24282 / CAMEL-24418)
if (uriString != null) {
URI uri = new URI(uriString);
queryString = uri.getQuery();
diff --git
a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java
b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java
index a605edbc941d..26978592aff0 100644
---
a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java
+++
b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java
@@ -54,8 +54,8 @@ public final class VertxHttpHelper {
uri = endpoint.getConfiguration().getHttpUri().toASCIIString();
}
- // Resolve property placeholders that may be present in the URI
- uri = exchange.getContext().resolvePropertyPlaceholders(uri);
+ // NOTE: property placeholders are resolved at build time on the
endpoint uri written in the route,
+ // never on the message-supplied override headers (see CAMEL-24282 /
CAMEL-24418)
// Append HTTP_PATH header value if is present
String path = message.getHeader(VertxHttpConstants.HTTP_PATH,
String.class);
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 57234ef7b897..2462b3cc614b 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -269,6 +269,37 @@ The same default is now also applied to the `ClientConfig`
that Camel builds for
endpoints, when neither a referenced `ClientConfig` nor `hazelcastConfigUri`
is supplied. Client mode
previously behaved differently from node mode for an otherwise identical
endpoint configuration.
+=== camel-http, camel-http-common, camel-netty-http, camel-undertow,
camel-vertx-http - property placeholders in HTTP URI override headers
+
+The HTTP producers no longer resolve property placeholders (`{{...}}`) in the
message-supplied
+endpoint-URI override headers `CamelHttpUri` and `CamelRestHttpUri`. Those
headers carry message
+content, while property placeholders are a route and configuration authoring
feature resolved at
+build time on the endpoint URI written in the route. This is the same
alignment 4.22 applied to
+`toD` and `enrich`.
+
+Placeholders written in the route's endpoint URI continue to be resolved
exactly as before:
+
+[source,java]
+----
+.to("http://localhost/{{basePath}}")
+.to("netty-http:http://localhost/{{basePath}}")
+----
+
+A `{{...}}` token arriving in `CamelHttpUri` or `CamelRestHttpUri` is now
treated as a literal part
+of the URI rather than being expanded. Routes that relied on that expansion
must resolve the value
+before it reaches the header, or keep the placeholder in the route.
+
+The affected sites, all of which resolved a header-derived or endpoint-derived
value per message:
+
+* `camel-http` - `HttpMethodHelper.createMethod`
+* `camel-http-common` - `HttpHelper.createURL`, `HttpHelper.createMethod`
+* `camel-netty-http` - `NettyHttpHelper.createURL`
+* `camel-undertow` - `UndertowHelper.createURL`, `UndertowHelper.createMethod`
+* `camel-vertx-http` - `VertxHttpHelper.resolveHttpURI`
+
+Where the value came from the endpoint rather than a header it was already
resolved at build time,
+so removing the per-message resolution does not change those routes.
+
=== camel-jbang (TUI)
`camel tui --record` is now rejected when combined with `--web`. The recording
configuration applies