You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by fm...@apache.org on 2026/09/09 12:24:11 UTC

(camel-spring-boot-examples) branch main updated: Fix platform-http-proxy example: reverse proxy instead of self-referential CamelHttpUrl (#207)

This is an automated email from the ASF dual-hosted git repository.

Croway pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot-examples.git


The following commit(s) were added to refs/heads/main by this push:
     new 33c839e6 Fix platform-http-proxy example: reverse proxy instead of self-referential CamelHttpUrl (#207)
33c839e6 is described below

commit 33c839e60607bbab8d0d4c74f451d68e16dc3d86
Author: Federico Mariani <34...@users.noreply.github.com>
AuthorDate: Wed Sep 9 14:24:05 2026 +0200

    Fix platform-http-proxy example: reverse proxy instead of self-referential CamelHttpUrl (#207)
    
    * Fix platform-http-proxy example: real target instead of self-referential CamelHttpUrl
    
    The example was broken in three ways:
    - The README's curl --proxy command never reached the route: servlet
      containers normalize an absolute-form request line down to just the
      path, discarding the target host, so the request never matched
      platform-http:proxy/*.
    - toD("${headers.CamelHttpUrl}") used the incoming request's own URL
      (CamelHttpUrl is set from request.getRequestURL(), i.e. this
      application's own address), causing the route to call itself in an
      infinite loop until the HTTP connection pool exhausted (503s).
    - The consumer path literal "proxy" collides with a reserved marker in
      camel-platform-http that turns the endpoint into a catch-all consumer
      for a Host-header-based forward proxy - a mode only implemented by
      the Vert.x platform-http engine, not by camel-platform-http-starter
      (the servlet/Spring MVC engine this example uses). Left as-is it
      silently degrades into an unguarded catch-all matching unrelated
      paths instead of 404ing.
    
    Redesigned as a real path-based reverse proxy: requests under
    /reverse-proxy/** are forwarded to a configurable backend
    (reverse-proxy.target-base-uri) using camel-http's bridgeEndpoint mode,
    which appends CamelHttpPath/CamelHttpQuery onto the fixed target
    automatically instead of trusting a self-referential header.
    
    Verified end-to-end against httpbin.org: query strings, root path, and
    trailing-slash requests all forward correctly, and unrelated paths now
    404 instead of being swallowed by the route.
    
    Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01Tgb2b9DSh1ncG7C82rwzsE
    
    * Strip the reverse-proxy prefix with a Simple expression, not a Processor
    
    Use setHeader with a Simple/OGNL expression (${header.CamelHttpPath.substring(n)},
    which invokes the real java.lang.String.substring(int) per Camel's OGNL support)
    instead of a Java lambda Processor, keeping the route declarative.
    
    Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01Tgb2b9DSh1ncG7C82rwzsE
    
    * CAMEL-24625: use stripUriPrefix instead of manual header substring
    
    Now that camel-platform-http supports a stripUriPrefix consumer
    option, drop the manual setHeader/substring workaround for making
    CamelHttpPath relative to the /reverse-proxy consumer path.
    
    Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
    Claude-Session: https://claude.ai/code/session_014vs1VzVasQeo3LrxCCHZ7v
    
    ---------
    
    Co-authored-by: Claude Sonnet 5 <no...@anthropic.com>
---
 platform-http-proxy/README.adoc                    | 20 +++++++++++-------
 .../camel/example/springboot/CamelRouter.java      | 24 ++++++++++++++++------
 .../src/main/resources/application.properties      |  3 +++
 3 files changed, 34 insertions(+), 13 deletions(-)

diff --git a/platform-http-proxy/README.adoc b/platform-http-proxy/README.adoc
index 7ab369be..286bbf6a 100644
--- a/platform-http-proxy/README.adoc
+++ b/platform-http-proxy/README.adoc
@@ -2,9 +2,11 @@
 
 === Introduction
 
-This example illustrates how to use https://projects.spring.io/spring-boot/[Spring Boot] with http://camel.apache.org[Camel]. It implements a reverse proxy using https://camel.apache.org/components/latest/platform-http-component.html[platform-http].
+This example illustrates how to use https://projects.spring.io/spring-boot/[Spring Boot] with http://camel.apache.org[Camel]. It implements a *path-based reverse proxy* using https://camel.apache.org/components/latest/platform-http-component.html[platform-http]: requests received under `/reverse-proxy/**` are forwarded to a fixed backend, with the `/reverse-proxy` prefix stripped and the remaining path and query string preserved.
 
-The project uses `camel-platform-http-starter` component as the implementation for platform-http-engine and `camel-http-starter` to implement the producer calling the http endpoint.
+The project uses `camel-platform-http-starter` component as the implementation for platform-http-engine and `camel-http-starter` to implement the producer calling the http endpoint. The consumer enables the `stripUriPrefix` option, which makes the `CamelHttpPath` header relative to the `/reverse-proxy` consumer path instead of the full raw request path. The producer then uses the http component's `bridgeEndpoint` option, which is the idiomatic way to build a reverse proxy with `camel-htt [...]
+
+NOTE: This is a reverse proxy reachable at a known path (`/reverse-proxy/...`), not a forward/HTTP-CONNECT proxy. Servlet containers such as the one backing `platform-http` normalize an absolute-form request line (as sent by `curl --proxy`) down to just the request path, discarding the target host - so a client can't point an HTTP forward proxy at this application. Talk to it directly instead, as shown below.
 
 === Run
 
@@ -15,21 +17,25 @@ You can run this example using:
 mvn spring-boot:run
 ----
 
-After the Spring Boot application is started, you can execute the following HTTP requests:
+After the Spring Boot application is started, you can execute the following HTTP request:
 
 [source,bash]
 ----
-curl --proxy http://localhost:8080 -L  http://httpbin.org/get?arg1=val1 -H  'accept: application/json'
+curl http://localhost:8080/reverse-proxy/get?arg1=val1 -H 'accept: application/json'
 ----
 
-The command will call a test endpoint using the application as reverse proxy, you should see the Camel headers from both request and response in the log. Something similar to:
+The command calls `/reverse-proxy/get` on the application, which forwards it to `http://httpbin.org/get`, you should see the Camel headers from both request and response in the log. Something similar to:
 
 ----
-INFO 70370 --- [ad #1 - WireTap] header-request                           : {accept=application/json, arg1=val1, CamelHttpCharacterEncoding=UTF-8, CamelHttpMethod=GET, CamelHttpPath=/get, CamelHttpQuery=arg1=val1, CamelHttpServletRequest=org.apache.catalina.connector.RequestFacade@4f31b074, CamelHttpServletResponse=org.springframework.web.context.request.async.StandardServletAsyncWebRequest$LifecycleHttpServletResponse@158137c0, CamelHttpUri=/get, CamelHttpUrl=http://httpbin.org/get, Cam [...]
-INFO 70370 --- [ad #2 - WireTap] header-response                          : {accept=application/json, Access-Control-Allow-Credentials=true, Access-Control-Allow-Origin=*, arg1=val1, CamelHttpCharacterEncoding=UTF-8, CamelHttpMethod=GET, CamelHttpQuery=arg1=val1, CamelHttpResponseCode=200, CamelHttpResponseText=OK, CamelHttpServletRequest=org.apache.catalina.connector.RequestFacade@4f31b074, CamelHttpServletResponse=org.springframework.web.context.request.async.StandardServletAsyncWebReq [...]
+INFO 70370 --- [ad #1 - WireTap] header-request                           : {accept=application/json, arg1=val1, CamelHttpCharacterEncoding=UTF-8, CamelHttpMethod=GET, CamelHttpPath=/reverse-proxy/get, CamelHttpQuery=arg1=val1, CamelHttpServletRequest=org.apache.catalina.connector.RequestFacade@4f31b074, CamelHttpServletResponse=org.springframework.web.context.request.async.StandardServletAsyncWebRequest$LifecycleHttpServletResponse@158137c0, CamelHttpUri=/reverse-proxy/get, CamelHttpUrl [...]
+INFO 70370 --- [ad #2 - WireTap] header-response                          : {accept=application/json, Access-Control-Allow-Credentials=true, Access-Control-Allow-Origin=*, arg1=val1, CamelHttpCharacterEncoding=UTF-8, CamelHttpMethod=GET, CamelHttpPath=/get, CamelHttpQuery=arg1=val1, CamelHttpResponseCode=200, CamelHttpResponseText=OK, CamelHttpServletRequest=org.apache.catalina.connector.RequestFacade@4f31b074, CamelHttpServletResponse=org.springframework.web.context.request.async.Standa [...]
 
 ----
 
+Note that `CamelHttpUrl` always reflects the *incoming* request's own URL (this application's own address), not the backend - it is set by the servlet container from the inbound request and is unrelated to where the request is forwarded. Do not use it to build the forwarding target, as that leads to the proxy calling itself.
+
+CAUTION: The consumer path deliberately avoids the literal segment `proxy` (i.e. it is *not* `platform-http:proxy`). That exact path is a reserved marker in `camel-platform-http`: matching it turns the endpoint into a catch-all consumer that is meant to build a `Host`-header-based forward proxy, but that behavior is only implemented by the Vert.x platform-http engine - `camel-platform-http-starter` (the Spring MVC/servlet engine used by this example) has no such logic. Using `platform-ht [...]
+
 The Spring Boot application can be stopped pressing `[CTRL] + [C]` in the shell.
 
 === Help and contributions
diff --git a/platform-http-proxy/src/main/java/org/apache/camel/example/springboot/CamelRouter.java b/platform-http-proxy/src/main/java/org/apache/camel/example/springboot/CamelRouter.java
index 400838da..4858d518 100644
--- a/platform-http-proxy/src/main/java/org/apache/camel/example/springboot/CamelRouter.java
+++ b/platform-http-proxy/src/main/java/org/apache/camel/example/springboot/CamelRouter.java
@@ -21,7 +21,21 @@ import org.apache.camel.builder.RouteBuilder;
 import org.springframework.stereotype.Component;
 
 /**
- * Reverse proxy route with routes that logs heders around the backend call
+ * Reverse proxy route that logs headers around the backend call.
+ *
+ * Requests are accepted under the {@code /reverse-proxy} path and forwarded to the fixed backend
+ * configured via {@code reverse-proxy.target-base-uri}. The consumer's {@code stripUriPrefix} option
+ * makes {@code CamelHttpPath} relative to the {@code /reverse-proxy} consumer path, so that, for
+ * example, {@code /reverse-proxy/get} is forwarded to {@code <target-base-uri>/get}. The remaining
+ * path and query string are then appended automatically by the http producer's {@code bridgeEndpoint}
+ * mode.
+ *
+ * NOTE: the consumer path must not be the literal string "proxy" (e.g. "platform-http:proxy").
+ * That exact path is a reserved marker in camel-platform-http: it turns the endpoint into a
+ * catch-all consumer meant to build a Host-header-based forward proxy, but that mode is only
+ * implemented by the Vert.x platform-http engine, not by camel-platform-http-starter (the
+ * servlet/Spring-MVC engine used here). On this engine it silently degrades into an unguarded
+ * catch-all with no forwarding logic, so a genuinely different path is used instead.
  */
 @Component
 public class CamelRouter extends RouteBuilder {
@@ -30,13 +44,11 @@ public class CamelRouter extends RouteBuilder {
 	public void configure() throws Exception {
 
 		// @formatter:off
-        from("platform-http:proxy/*?matchOnUriPrefix=true")
+        from("platform-http:reverse-proxy?matchOnUriPrefix=true&stripUriPrefix=true")
                 .routeId("reverse-proxy")
                 .wireTap("direct:request")
-                .removeHeader(Exchange.HTTP_PATH)
-                .log("calling ${headers." + Exchange.HTTP_URL + "}")
-                .toD("${headers." + Exchange.HTTP_URL + "}"
-                        + "?throwExceptionOnFailure=false&bridgeEndpoint=true")
+                .log("calling ${properties:reverse-proxy.target-base-uri}${headers." + Exchange.HTTP_PATH + "}")
+                .to("{{reverse-proxy.target-base-uri}}?bridgeEndpoint=true&throwExceptionOnFailure=false")
                 .wireTap("direct:response");
 
         from("direct:request")
diff --git a/platform-http-proxy/src/main/resources/application.properties b/platform-http-proxy/src/main/resources/application.properties
index a4ed5c28..f78869f0 100644
--- a/platform-http-proxy/src/main/resources/application.properties
+++ b/platform-http-proxy/src/main/resources/application.properties
@@ -16,3 +16,6 @@
 ## ---------------------------------------------------------------------------
 # the name of Camel
 camel.main.name = MyCamelReverseProxy
+
+# base URI of the backend service requests under /reverse-proxy are forwarded to
+reverse-proxy.target-base-uri = http://httpbin.org