You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/10/19 12:46:46 UTC

[camel] branch main updated: CAMEL-18593: Platform-http : add reverse proxy feature (#8574)

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 c34a9b6be2d CAMEL-18593: Platform-http : add reverse proxy feature (#8574)
c34a9b6be2d is described below

commit c34a9b6be2d38c6307b6a878c7bc8e38597c4b04
Author: Zineb BENDHIBA <be...@gmail.com>
AuthorDate: Wed Oct 19 14:46:40 2022 +0200

    CAMEL-18593: Platform-http : add reverse proxy feature (#8574)
---
 .../camel/catalog/components/platform-http.json    |   2 +-
 components/camel-platform-http-vertx/pom.xml       |   6 +
 .../src/main/docs/platform-http-vertx.adoc         |  14 +++
 .../http/vertx/VertxPlatformHttpConsumer.java      |   8 ++
 .../http/vertx/VertxPlatformHttpProxyTest.java     |  91 ++++++++++++++
 .../http/vertx/VertxPlatformHttpsProxyTest.java    | 135 +++++++++++++++++++++
 .../src/test/resources/proxy/keystore.p12          | Bin 0 -> 2716 bytes
 components/camel-platform-http/pom.xml             |   6 +-
 .../component/platform/http/platform-http.json     |   2 +-
 .../src/main/docs/platform-http-component.adoc     |   6 +
 .../platform/http/PlatformHttpEndpoint.java        |  10 +-
 .../http/JettyCustomPlatformHttpConsumer.java      |  37 +++++-
 .../platform/http/PlatformHttpProxyTest.java       |  54 +++++++++
 .../builder/endpoint/StaticEndpointBuilders.java   |   6 +-
 .../dsl/PlatformHttpEndpointBuilderFactory.java    |   6 +-
 15 files changed, 372 insertions(+), 11 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/platform-http.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/platform-http.json
index 805e0c10109..b48a5f2fa93 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/platform-http.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/platform-http.json
@@ -27,7 +27,7 @@
     "engine": { "kind": "property", "displayName": "Engine", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.platform.http.spi.PlatformHttpEngine", "deprecated": false, "autowired": false, "secret": false, "description": "An HTTP Server engine implementation to serve the requests" }
   },
   "properties": {
-    "path": { "kind": "path", "displayName": "Path", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The path under which this endpoint serves the HTTP requests" },
+    "path": { "kind": "path", "displayName": "Path", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The path under which this endpoint serves the HTTP requests, for proxy use 'proxy'" },
     "consumes": { "kind": "parameter", "displayName": "Consumes", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type this endpoint accepts as an input, such as application\/xml or application\/json. null or &#42;\/&#42; mean no restriction." },
     "httpMethodRestrict": { "kind": "parameter", "displayName": "Http Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "A comma separated list of HTTP methods to serve, e.g. GET,POST . If no methods are specified, all methods will be served." },
     "matchOnUriPrefix": { "kind": "parameter", "displayName": "Match On Uri Prefix", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether or not the consumer should try to find a target consumer by matching the URI prefix if no exact match is found." },
diff --git a/components/camel-platform-http-vertx/pom.xml b/components/camel-platform-http-vertx/pom.xml
index c48d0f6a7f1..80f5ae8b084 100644
--- a/components/camel-platform-http-vertx/pom.xml
+++ b/components/camel-platform-http-vertx/pom.xml
@@ -100,6 +100,12 @@
             <artifactId>log4j-slf4j-impl</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.github.tomakehurst</groupId>
+            <artifactId>wiremock-jre8</artifactId>
+            <version>${wiremock-version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/components/camel-platform-http-vertx/src/main/docs/platform-http-vertx.adoc b/components/camel-platform-http-vertx/src/main/docs/platform-http-vertx.adoc
index ae087c7e51a..34c2cccfc53 100644
--- a/components/camel-platform-http-vertx/src/main/docs/platform-http-vertx.adoc
+++ b/components/camel-platform-http-vertx/src/main/docs/platform-http-vertx.adoc
@@ -60,3 +60,17 @@ context.addRoutes(new RouteBuilder() {
 
 context.start();
 ----
+
+== Implementing a reverse proxy
+
+Platform HTTP component can act as a reverse proxy, in that case `Exchange.HTTP_URI`, `Exchange.HTTP_HOST` headers are populated from the absolute URL received on the request line of the HTTP request.
+
+Here's an example of a HTTP proxy that simply redirects the Exchange to the origin server.
+
+[source,java]
+------------------------------------------------------------------------------------------
+from("platform-http:proxy")
+    .toD("http://"
+        + "${headers." + Exchange.HTTP_HOST + "}");
+
+-------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/components/camel-platform-http-vertx/src/main/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpConsumer.java b/components/camel-platform-http-vertx/src/main/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpConsumer.java
index 4a8072f8764..01d373d4e8a 100644
--- a/components/camel-platform-http-vertx/src/main/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpConsumer.java
+++ b/components/camel-platform-http-vertx/src/main/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpConsumer.java
@@ -40,6 +40,7 @@ import io.vertx.ext.web.impl.RouteImpl;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePattern;
 import org.apache.camel.ExchangePropertyKey;
+import org.apache.camel.ExtendedExchange;
 import org.apache.camel.Message;
 import org.apache.camel.Processor;
 import org.apache.camel.attachment.AttachmentMessage;
@@ -181,6 +182,13 @@ public class VertxPlatformHttpConsumer extends DefaultConsumer {
         //             .removeHeaders("*", "CamelHttpPath")
         //             .to("rest:get:?bridgeEndpoint=true");
         //
+
+        if (getEndpoint().isHttpProxy()) {
+            exchange.adapt(ExtendedExchange.class).setStreamCacheDisabled(true);
+            final MultiMap httpHeaders = ctx.request().headers();
+            exchange.getMessage().setHeader(Exchange.HTTP_HOST, httpHeaders.get("Host"));
+            exchange.getMessage().removeHeader("Proxy-Connection");
+        }
         vertx.executeBlocking(
                 promise -> {
                     try {
diff --git a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpProxyTest.java b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpProxyTest.java
new file mode 100644
index 00000000000..df2a47cdc9d
--- /dev/null
+++ b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpProxyTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.platform.http.vertx;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import io.restassured.RestAssured;
+import io.restassured.http.ContentType;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.AvailablePortFinder;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.containsString;
+
+public class VertxPlatformHttpProxyTest {
+    private final int port = AvailablePortFinder.getNextAvailable();
+    private WireMockServer wireMockServer = new WireMockServer(options().port(port));
+
+    @BeforeEach
+    void before() {
+        wireMockServer.stubFor(get(urlPathEqualTo("/"))
+                .willReturn(aResponse()
+                        .withBody(
+                                "{\"message\": \"Hello World\"}")));
+
+        wireMockServer.start();
+    }
+
+    @AfterEach
+    void after() {
+        if (wireMockServer != null) {
+            wireMockServer.stop();
+        }
+    }
+
+    @Test
+    void testProxy() throws Exception {
+        final CamelContext context = VertxPlatformHttpEngineTest.createCamelContext();
+
+        try {
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from("platform-http:proxy")
+                            .toD("${headers." + Exchange.HTTP_URI + "}?bridgeEndpoint=true");
+                }
+            });
+
+            context.start();
+
+            // URI of proxy created with platform HTTP component
+            final var proxyURI = "http://localhost:" + RestAssured.port;
+
+            final var originURI = "http://localhost:" + wireMockServer.port();
+
+            given()
+                    .proxy(proxyURI)
+                    .contentType(ContentType.JSON)
+                    .when().get(originURI)
+                    .then()
+                    .statusCode(200)
+                    .body(containsString("{\"message\": \"Hello World\"}"));
+
+        } finally {
+            context.stop();
+        }
+    }
+
+}
diff --git a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpsProxyTest.java b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpsProxyTest.java
new file mode 100644
index 00000000000..79053d62d17
--- /dev/null
+++ b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpsProxyTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.platform.http.vertx;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import io.restassured.RestAssured;
+import io.restassured.http.ContentType;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.support.jsse.KeyManagersParameters;
+import org.apache.camel.support.jsse.KeyStoreParameters;
+import org.apache.camel.support.jsse.SSLContextParameters;
+import org.apache.camel.support.jsse.TrustManagersParameters;
+import org.apache.camel.test.AvailablePortFinder;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.containsString;
+
+public class VertxPlatformHttpsProxyTest {
+
+    private final int port = AvailablePortFinder.getNextAvailable();
+    private WireMockServer wireMockServer = new WireMockServer(
+            options().httpsPort(port)
+                    .httpDisabled(true)
+                    .keystorePath("proxy/keystore.p12")
+                    .keystorePassword("changeit")
+                    .keyManagerPassword("changeit"));
+
+    @BeforeEach
+    void before() {
+        wireMockServer.stubFor(get(urlPathEqualTo("/"))
+                .willReturn(aResponse()
+                        .withBody(
+                                "{\"message\": \"Hello World\"}")));
+
+        wireMockServer.start();
+    }
+
+    @AfterEach
+    void after() {
+        if (wireMockServer != null) {
+            wireMockServer.stop();
+        }
+    }
+
+    @Test
+    void testProxy() throws Exception {
+        final CamelContext context = VertxPlatformHttpEngineTest.createCamelContext();
+
+        try {
+
+            context.getRegistry().bind("sslContextParameters", sslContextParameters());
+            context.getRegistry().bind("x509HostnameVerifier", x509HostnameVerifier());
+
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from("platform-http:proxy")
+                            .toD("https://${headers." + Exchange.HTTP_HOST
+                                 + "}?bridgeEndpoint=true&sslContextParameters=#sslContextParameters&x509HostnameVerifier=#x509HostnameVerifier");
+                }
+            });
+
+            context.start();
+
+            // URI of proxy created with platform HTTP component
+            final var proxyURI = "http://localhost:" + RestAssured.port;
+
+            // In order to make sure that RestAssured don't perform a CONNECT instead of a GET, we do trick with http
+            // if we want to do test manually from a terminal we use the real HTTPS address
+            final var originURI = "http://localhost:" + port;
+
+            given()
+                    .proxy(proxyURI)
+                    .contentType(ContentType.JSON)
+                    .when().get(originURI)
+                    .then()
+                    .statusCode(200)
+                    .body(containsString("{\"message\": \"Hello World\"}"));
+
+        } finally {
+            context.stop();
+        }
+    }
+
+    public SSLContextParameters sslContextParameters() {
+        SSLContextParameters sslContextParameters = new SSLContextParameters();
+
+        KeyManagersParameters keyManagersParameters = new KeyManagersParameters();
+        KeyStoreParameters keyStore = new KeyStoreParameters();
+        keyStore.setPassword("changeit");
+        keyStore.setResource("proxy/keystore.p12");
+        keyManagersParameters.setKeyPassword("changeit");
+        keyManagersParameters.setKeyStore(keyStore);
+        sslContextParameters.setKeyManagers(keyManagersParameters);
+
+        KeyStoreParameters truststoreParameters = new KeyStoreParameters();
+        truststoreParameters.setResource("proxy/keystore.p12");
+        truststoreParameters.setPassword("changeit");
+
+        TrustManagersParameters trustManagersParameters = new TrustManagersParameters();
+        trustManagersParameters.setKeyStore(truststoreParameters);
+        sslContextParameters.setTrustManagers(trustManagersParameters);
+
+        return sslContextParameters;
+    }
+
+    public NoopHostnameVerifier x509HostnameVerifier() {
+        return NoopHostnameVerifier.INSTANCE;
+    }
+
+}
diff --git a/components/camel-platform-http-vertx/src/test/resources/proxy/keystore.p12 b/components/camel-platform-http-vertx/src/test/resources/proxy/keystore.p12
new file mode 100644
index 00000000000..fadac3cb35b
Binary files /dev/null and b/components/camel-platform-http-vertx/src/test/resources/proxy/keystore.p12 differ
diff --git a/components/camel-platform-http/pom.xml b/components/camel-platform-http/pom.xml
index d9d2af60983..bfdb73f8ed8 100644
--- a/components/camel-platform-http/pom.xml
+++ b/components/camel-platform-http/pom.xml
@@ -67,6 +67,10 @@
             <artifactId>log4j-slf4j-impl</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-http</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
-
 </project>
diff --git a/components/camel-platform-http/src/generated/resources/org/apache/camel/component/platform/http/platform-http.json b/components/camel-platform-http/src/generated/resources/org/apache/camel/component/platform/http/platform-http.json
index 805e0c10109..b48a5f2fa93 100644
--- a/components/camel-platform-http/src/generated/resources/org/apache/camel/component/platform/http/platform-http.json
+++ b/components/camel-platform-http/src/generated/resources/org/apache/camel/component/platform/http/platform-http.json
@@ -27,7 +27,7 @@
     "engine": { "kind": "property", "displayName": "Engine", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.platform.http.spi.PlatformHttpEngine", "deprecated": false, "autowired": false, "secret": false, "description": "An HTTP Server engine implementation to serve the requests" }
   },
   "properties": {
-    "path": { "kind": "path", "displayName": "Path", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The path under which this endpoint serves the HTTP requests" },
+    "path": { "kind": "path", "displayName": "Path", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The path under which this endpoint serves the HTTP requests, for proxy use 'proxy'" },
     "consumes": { "kind": "parameter", "displayName": "Consumes", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type this endpoint accepts as an input, such as application\/xml or application\/json. null or &#42;\/&#42; mean no restriction." },
     "httpMethodRestrict": { "kind": "parameter", "displayName": "Http Method Restrict", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "A comma separated list of HTTP methods to serve, e.g. GET,POST . If no methods are specified, all methods will be served." },
     "matchOnUriPrefix": { "kind": "parameter", "displayName": "Match On Uri Prefix", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether or not the consumer should try to find a target consumer by matching the URI prefix if no exact match is found." },
diff --git a/components/camel-platform-http/src/main/docs/platform-http-component.adoc b/components/camel-platform-http/src/main/docs/platform-http-component.adoc
index ae8b2041126..a73fed9d7be 100644
--- a/components/camel-platform-http/src/main/docs/platform-http-component.adoc
+++ b/components/camel-platform-http/src/main/docs/platform-http-component.adoc
@@ -61,3 +61,9 @@ include::partial$component-endpoint-options.adoc[]
 // endpoint options: END
 
 include::spring-boot:partial$starter.adoc[]
+
+== Implementing a reverse proxy
+
+Platform HTTP component can act as a reverse proxy, in that case some headers are populated from the absolute URL received on the request line of the HTTP request. Those headers are specific to the underlining platform.
+
+At this moment, this feature is only supported for Vert.x in `camel-platform-http-vertx` component.
\ No newline at end of file
diff --git a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java
index db5ca28e7fe..34db08833ef 100644
--- a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java
+++ b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java
@@ -40,7 +40,9 @@ import org.apache.camel.support.service.ServiceHelper;
              category = { Category.HTTP }, consumerOnly = true)
 public class PlatformHttpEndpoint extends DefaultEndpoint implements AsyncEndpoint, HeaderFilterStrategyAware {
 
-    @UriPath(description = "The path under which this endpoint serves the HTTP requests")
+    private static final String PROXY_PATH = "proxy";
+
+    @UriPath(description = "The path under which this endpoint serves the HTTP requests, for proxy use 'proxy'")
     @Metadata(required = true)
     private final String path;
     @UriParam(label = "consumer", defaultValue = "false",
@@ -143,7 +145,7 @@ public class PlatformHttpEndpoint extends DefaultEndpoint implements AsyncEndpoi
     }
 
     public String getPath() {
-        return path;
+        return isHttpProxy() ? "/" : path;
     }
 
     public PlatformHttpEngine getPlatformHttpEngine() {
@@ -207,4 +209,8 @@ public class PlatformHttpEndpoint extends DefaultEndpoint implements AsyncEndpoi
                 ? platformHttpEngine
                 : getComponent().getOrCreateEngine();
     }
+
+    public boolean isHttpProxy() {
+        return this.path.startsWith(PROXY_PATH);
+    }
 }
diff --git a/components/camel-platform-http/src/test/java/org/apache/camel/component/platform/http/JettyCustomPlatformHttpConsumer.java b/components/camel-platform-http/src/test/java/org/apache/camel/component/platform/http/JettyCustomPlatformHttpConsumer.java
index f1bb46af1ff..cc8accecc5d 100644
--- a/components/camel-platform-http/src/test/java/org/apache/camel/component/platform/http/JettyCustomPlatformHttpConsumer.java
+++ b/components/camel-platform-http/src/test/java/org/apache/camel/component/platform/http/JettyCustomPlatformHttpConsumer.java
@@ -17,17 +17,23 @@
 package org.apache.camel.component.platform.http;
 
 import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.StringJoiner;
 import java.util.regex.Pattern;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.camel.Exchange;
+import org.apache.camel.ExtendedExchange;
 import org.apache.camel.Message;
 import org.apache.camel.Processor;
 import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.support.DefaultConsumer;
 import org.apache.camel.support.DefaultMessage;
+import org.apache.camel.util.IOHelper;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.eclipse.jetty.server.handler.ContextHandler;
@@ -74,11 +80,26 @@ public class JettyCustomPlatformHttpConsumer extends DefaultConsumer {
                         bodyRequest += strCurrentLine;
                     }
                     final Exchange exchange = exchg = toExchange(request, bodyRequest);
+                    if (getEndpoint().isHttpProxy()) {
+                        exchange.getMessage().removeHeader("Proxy-Connection");
+                    }
+                    exchange.getMessage().setHeader(Exchange.HTTP_SCHEME, httpServletRequest.getScheme());
+                    exchange.getMessage().setHeader(Exchange.HTTP_HOST, httpServletRequest.getServerName());
+                    exchange.getMessage().setHeader(Exchange.HTTP_PORT, httpServletRequest.getServerPort());
+                    exchange.getMessage().setHeader(Exchange.HTTP_PATH, httpServletRequest.getPathInfo());
+                    if (getEndpoint().isHttpProxy()) {
+                        exchange.adapt(ExtendedExchange.class).setStreamCacheDisabled(true);
+                    }
                     createUoW(exchange);
-                    getProcessor().process(
-                            exchange);
+                    getProcessor().process(exchange);
                     httpServletResponse.setStatus(HttpServletResponse.SC_OK);
                     request.setHandled(true);
+                    if (getEndpoint().isHttpProxy()) {
+                        // extract response
+                        InputStream response = exchange.getMessage().getBody(InputStream.class);
+                        String body = JettyCustomPlatformHttpConsumer.toString(response);
+                        exchange.getMessage().setBody(body);
+                    }
                     httpServletResponse.getWriter().println(exchange.getMessage().getBody());
                 } catch (Exception e) {
                     getExceptionHandler().handleException("Failed handling platform-http endpoint " + endpoint.getPath(), exchg,
@@ -113,4 +134,16 @@ public class JettyCustomPlatformHttpConsumer extends DefaultConsumer {
         return (PlatformHttpEndpoint) super.getEndpoint();
     }
 
+    private static String toString(InputStream input) throws IOException {
+        BufferedReader reader = IOHelper.buffered(new InputStreamReader(input));
+        StringJoiner builder = new StringJoiner(" ");
+        while (true) {
+            String line = reader.readLine();
+            if (line == null) {
+                return builder.toString();
+            }
+            builder.add(line);
+        }
+    }
+
 }
diff --git a/components/camel-platform-http/src/test/java/org/apache/camel/component/platform/http/PlatformHttpProxyTest.java b/components/camel-platform-http/src/test/java/org/apache/camel/component/platform/http/PlatformHttpProxyTest.java
new file mode 100644
index 00000000000..c25da3058cd
--- /dev/null
+++ b/components/camel-platform-http/src/test/java/org/apache/camel/component/platform/http/PlatformHttpProxyTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.platform.http;
+
+import io.restassured.http.ContentType;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.containsString;
+
+public class PlatformHttpProxyTest extends AbstractPlatformHttpTest {
+    @Test
+    void testProxy() {
+        given()
+                .body("hello")
+                .proxy("http://localhost:" + AbstractPlatformHttpTest.port)
+                .contentType(ContentType.HTML)
+                .when().get("http://neverssl.com:80")
+                .then()
+                .statusCode(200)
+                .body(containsString("<html>"));
+    }
+
+    @Override
+    protected RouteBuilder routes() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("platform-http:proxy")
+                        .toD("${headers." + Exchange.HTTP_SCHEME + "}://" +
+                             "${headers." + Exchange.HTTP_HOST + "}:" +
+                             "${headers." + Exchange.HTTP_PORT + "}" +
+                             "${headers." + Exchange.HTTP_PATH + "}?bridgeEndpoint=true");
+            }
+        };
+    }
+
+}
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
index 45a7d986cda..e219af622d8 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
@@ -11889,7 +11889,8 @@ public class StaticEndpointBuilders {
      * Syntax: <code>platform-http:path</code>
      * 
      * Path parameter: path (required)
-     * The path under which this endpoint serves the HTTP requests
+     * The path under which this endpoint serves the HTTP requests, for proxy
+     * use 'proxy'
      * 
      * @param path path
      * @return the dsl builder
@@ -11910,7 +11911,8 @@ public class StaticEndpointBuilders {
      * Syntax: <code>platform-http:path</code>
      * 
      * Path parameter: path (required)
-     * The path under which this endpoint serves the HTTP requests
+     * The path under which this endpoint serves the HTTP requests, for proxy
+     * use 'proxy'
      * 
      * @param componentName to use a custom component name for the endpoint
      * instead of the default name
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/PlatformHttpEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/PlatformHttpEndpointBuilderFactory.java
index 222b8b34eb1..4160c2aae3f 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/PlatformHttpEndpointBuilderFactory.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/PlatformHttpEndpointBuilderFactory.java
@@ -387,7 +387,8 @@ public interface PlatformHttpEndpointBuilderFactory {
          * Syntax: <code>platform-http:path</code>
          * 
          * Path parameter: path (required)
-         * The path under which this endpoint serves the HTTP requests
+         * The path under which this endpoint serves the HTTP requests, for
+         * proxy use 'proxy'
          * 
          * @param path path
          * @return the dsl builder
@@ -407,7 +408,8 @@ public interface PlatformHttpEndpointBuilderFactory {
          * Syntax: <code>platform-http:path</code>
          * 
          * Path parameter: path (required)
-         * The path under which this endpoint serves the HTTP requests
+         * The path under which this endpoint serves the HTTP requests, for
+         * proxy use 'proxy'
          * 
          * @param componentName to use a custom component name for the endpoint
          * instead of the default name