From 28c9f4737580ae78a15e5eff6f58959f4817e2a6 Mon Sep 17 00:00:00 2001 From: Andrea Cosentino Date: Fri, 21 Aug 2026 20:55:25 +0200 Subject: [PATCH] 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 the message-supplied endpoint-URI override headers CamelHttpUri and CamelRestHttpUri through CamelContext.resolvePropertyPlaceholders(), so a {{...}} token arriving in a message was expanded against the application's property sources. Seven sites across five components: * 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 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 CAMEL-24282 applied to toD and enrich. Where the value came from the endpoint rather than from a header it had already been resolved at build time, so those routes are unaffected. Note bridgeEndpoint does not bound this: the createURL family ignores CamelHttpUri when bridging, but the createMethod family still resolves it, and CamelRestHttpUri is consumed before any bridgeEndpoint check. skipControlHeaders exists on camel-http only. Removing the calls left the RuntimeExchangeException import unused in HttpMethodHelper, HttpHelper, NettyHttpHelper and UndertowHelper; all removed. Co-Authored-By: Claude Opus 5 (1M context) --- .../apache/camel/http/common/HttpHelper.java | 18 ++--- .../http/helper/HttpMethodHelper.java | 9 +-- .../http/HttpUriHeaderPlaceholderTest.java | 70 +++++++++++++++++++ .../component/netty/http/NettyHttpHelper.java | 9 +-- .../component/undertow/UndertowHelper.java | 17 ++--- .../component/vertx/http/VertxHttpHelper.java | 4 +- .../pages/camel-4x-upgrade-guide-4_23.adoc | 31 ++++++++ 7 files changed, 116 insertions(+), 42 deletions(-) create mode 100644 components/camel-http/src/test/java/org/apache/camel/component/http/HttpUriHeaderPlaceholderTest.java 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 93195b91d95b4..809c9f00679b8 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 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 static String createURL(Exchange exchange, HttpCommonEndpoint endpoint) { 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 static HttpMethods createMethod(Exchange exchange, HttpCommonEndpoint end 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 966f1c74fdd40..5f3b94bff3b49 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.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 static HttpMethods createMethod(Exchange exchange, HttpEndpoint endpoint) 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 0000000000000..6488a35c5b73d --- /dev/null +++ b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpUriHeaderPlaceholderTest.java @@ -0,0 +1,70 @@ +/* + * 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.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 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-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 80192f05322d3..4ce470be55bde 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.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 static String createURL(Exchange exchange, NettyHttpEndpoint endpoint) { 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 56b9e57859177..460d12a30664d 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 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 static String createURL(Exchange exchange, UndertowEndpoint endpoint) { 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 static HttpString createMethod(Exchange exchange, UndertowEndpoint endpoi 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 a605edbc941de..26978592aff0e 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 static URI resolveHttpURI(Exchange exchange, VertxHttpEndpoint endpoint) 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 3445aca8a37b9..2c1ae4c2a1cb4 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 @@ -81,6 +81,37 @@ Applications that aggregate classes outside the default whitelist through the re without supplying their own `hazelcastInstance` must now provide a `Config` with a `JavaSerializationFilterConfig` covering their class names. +=== 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-mail `MimeMultipartDataFormat` now uses `MailHeaderFilterStrategy` instead of a plain