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

Reply via email to