This is an automated email from the ASF dual-hosted git repository. kulagaivan 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 7f768890707 CAMEL-20638: camel-platform-http - Do not return http request headers (#15145) 7f768890707 is described below commit 7f7688907070ed443a44d78a347d49f20426ad5b Author: Ivan Kulaga <kulagaivanandreev...@gmail.com> AuthorDate: Wed Aug 14 18:10:41 2024 +0500 CAMEL-20638: camel-platform-http - Do not return http request headers (#15145) - added platfrom-http endpoint option that makes proxy of HeaderFilterStrategy in PlatformHttpEndpoint to filter common http request headers so that they are not sent in the response --- .../camel/catalog/components/platform-http.json | 17 ++--- .../http/vertx/VertxPlatformHttpEngineTest.java | 9 +-- .../http/vertx/VertxPlatformHttpSessionTest.java | 7 +- .../http/PlatformHttpEndpointConfigurer.java | 6 ++ .../http/PlatformHttpEndpointUriFactory.java | 3 +- .../component/platform/http/platform-http.json | 17 ++--- .../platform/http/PlatformHttpEndpoint.java | 66 +++++++++++++++++ .../http/JettyCustomPlatformHttpConsumer.java | 46 ++++++++++++ .../PlatformHttpReturnHttpRequestHeadersTest.java | 83 ++++++++++++++++++++++ .../dsl/PlatformHttpEndpointBuilderFactory.java | 32 +++++++++ .../camel/kotlin/components/PlatformHttpUriDsl.kt | 16 +++++ 11 files changed, 278 insertions(+), 24 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 f5608a28f73..c3cc5f4e0c0 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 @@ -42,13 +42,14 @@ "matchOnUriPrefix": { "index": 9, "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." }, "muteException": { "index": 10, "kind": "parameter", "displayName": "Mute Exception", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "If enabled and an Exchange failed processing on the consumer side the response's body won't contain the exception's stack trace." }, "produces": { "index": 11, "kind": "parameter", "displayName": "Produces", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type this endpoint produces, such as application\/xml or application\/json." }, - "useCookieHandler": { "index": 12, "kind": "parameter", "displayName": "Use Cookie Handler", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to enable the Cookie Handler that allows Cookie addition, expiry, and retrieval (currently only supported by camel-platform-http-vertx)" }, - "useStreaming": { "index": 13, "kind": "parameter", "displayName": "Use Streaming", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to use streaming for large requests and responses (currently only supported by camel-platform-http-vertx)" }, - "bridgeErrorHandler": { "index": 14, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming [...] - "exceptionHandler": { "index": 15, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By de [...] - "exchangePattern": { "index": 16, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." }, - "fileNameExtWhitelist": { "index": 17, "kind": "parameter", "displayName": "File Name Ext Whitelist", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "A comma or whitespace separated list of file extensions. Uploads having these extensions will be stored locally. Null value or asterisk () will allow all files." }, - "headerFilterStrategy": { "index": 18, "kind": "parameter", "displayName": "Header Filter Strategy", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom HeaderFilterStrategy to filter headers to and from Camel message." }, - "platformHttpEngine": { "index": 19, "kind": "parameter", "displayName": "Platform Http 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 of this endpoint." } + "returnHttpRequestHeaders": { "index": 12, "kind": "parameter", "displayName": "Return Http Request Headers", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to include HTTP request headers (Accept, User-Agent, etc.) into HTTP response produced by this endpoint." }, + "useCookieHandler": { "index": 13, "kind": "parameter", "displayName": "Use Cookie Handler", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to enable the Cookie Handler that allows Cookie addition, expiry, and retrieval (currently only supported by camel-platform-http-vertx)" }, + "useStreaming": { "index": 14, "kind": "parameter", "displayName": "Use Streaming", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to use streaming for large requests and responses (currently only supported by camel-platform-http-vertx)" }, + "bridgeErrorHandler": { "index": 15, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming [...] + "exceptionHandler": { "index": 16, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By de [...] + "exchangePattern": { "index": 17, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." }, + "fileNameExtWhitelist": { "index": 18, "kind": "parameter", "displayName": "File Name Ext Whitelist", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "A comma or whitespace separated list of file extensions. Uploads having these extensions will be stored locally. Null value or asterisk () will allow all files." }, + "headerFilterStrategy": { "index": 19, "kind": "parameter", "displayName": "Header Filter Strategy", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom HeaderFilterStrategy to filter headers to and from Camel message." }, + "platformHttpEngine": { "index": 20, "kind": "parameter", "displayName": "Platform Http 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 of this endpoint." } } } diff --git a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpEngineTest.java b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpEngineTest.java index 18b8d9070a9..cfd8a4d7d48 100644 --- a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpEngineTest.java +++ b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpEngineTest.java @@ -61,6 +61,7 @@ import org.apache.camel.support.jsse.SSLContextParameters; import org.apache.camel.support.jsse.SSLContextServerParameters; import org.apache.camel.support.jsse.TrustManagersParameters; import org.apache.camel.test.AvailablePortFinder; +import org.apache.hc.client5.http.utils.Base64; import org.hamcrest.Matcher; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -584,7 +585,8 @@ public class VertxPlatformHttpEngineTest { from("platform-http:/secure") .process(exchange -> { Message message = exchange.getMessage(); - message.setBody("Secure Route"); + message.setBody("Received message with the Authorization=" + + exchange.getMessage().getHeader("Authorization")); User user = message.getHeader(VertxPlatformHttpConstants.AUTHENTICATED_USER, User.class); assertThat(user).isNotNull(); @@ -614,9 +616,8 @@ public class VertxPlatformHttpEngineTest { .get("/secure") .then() .statusCode(200) - .header("Authorization", notNullValue()) - .body(is("Secure Route")); - + .body(is("Received message with the Authorization=Basic " + + Base64.encodeBase64String("camel:s3cr3t".getBytes()))); } finally { context.stop(); vertx.close(); diff --git a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpSessionTest.java b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpSessionTest.java index cb6165cc427..8f6dd316ff0 100644 --- a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpSessionTest.java +++ b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformHttpSessionTest.java @@ -173,7 +173,7 @@ public class VertxPlatformHttpSessionTest { @Override public void configure() { from("platform-http:/session") - .setBody().constant("session"); + .setBody().simple("Received cookie from platform-http:/session endpoint: ${header.cookie}"); from("direct:session") .toF("http://localhost:%d/session?cookieHandler=#instanceCookieHander", @@ -194,8 +194,9 @@ public class VertxPlatformHttpSessionTest { // subsequent call reuses session exchange = template.request("direct:session", null); // 'cookie' header for existing session, e.g. 'vertx-web.session=735944d69685aaf63421fb5b3c116b84' - String cookieHeader = getHeader("cookie", exchange); - assertEquals(cookieHeader, sessionCookie.substring(0, sessionCookie.indexOf(';'))); + String receivedCookie = exchange.getMessage().getBody(String.class) + .replace("Received cookie from platform-http:/session endpoint: ", ""); + assertEquals(receivedCookie, sessionCookie.substring(0, sessionCookie.indexOf(';'))); assertNull(getHeader("set-cookie", exchange)); assertEquals("2", getHeader("hitcount", exchange)); diff --git a/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpEndpointConfigurer.java b/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpEndpointConfigurer.java index 106d3947aee..35052f63db2 100644 --- a/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpEndpointConfigurer.java +++ b/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpEndpointConfigurer.java @@ -55,6 +55,8 @@ public class PlatformHttpEndpointConfigurer extends PropertyConfigurerSupport im case "platformhttpengine": case "platformHttpEngine": target.setPlatformHttpEngine(property(camelContext, org.apache.camel.component.platform.http.spi.PlatformHttpEngine.class, value)); return true; case "produces": target.setProduces(property(camelContext, java.lang.String.class, value)); return true; + case "returnhttprequestheaders": + case "returnHttpRequestHeaders": target.setReturnHttpRequestHeaders(property(camelContext, boolean.class, value)); return true; case "usecookiehandler": case "useCookieHandler": target.setUseCookieHandler(property(camelContext, boolean.class, value)); return true; case "usestreaming": @@ -98,6 +100,8 @@ public class PlatformHttpEndpointConfigurer extends PropertyConfigurerSupport im case "platformhttpengine": case "platformHttpEngine": return org.apache.camel.component.platform.http.spi.PlatformHttpEngine.class; case "produces": return java.lang.String.class; + case "returnhttprequestheaders": + case "returnHttpRequestHeaders": return boolean.class; case "usecookiehandler": case "useCookieHandler": return boolean.class; case "usestreaming": @@ -142,6 +146,8 @@ public class PlatformHttpEndpointConfigurer extends PropertyConfigurerSupport im case "platformhttpengine": case "platformHttpEngine": return target.getPlatformHttpEngine(); case "produces": return target.getProduces(); + case "returnhttprequestheaders": + case "returnHttpRequestHeaders": return target.isReturnHttpRequestHeaders(); case "usecookiehandler": case "useCookieHandler": return target.isUseCookieHandler(); case "usestreaming": diff --git a/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpEndpointUriFactory.java b/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpEndpointUriFactory.java index 38c8ca30155..481e5e0d895 100644 --- a/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpEndpointUriFactory.java +++ b/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpEndpointUriFactory.java @@ -23,7 +23,7 @@ public class PlatformHttpEndpointUriFactory extends org.apache.camel.support.com private static final Set<String> SECRET_PROPERTY_NAMES; private static final Set<String> MULTI_VALUE_PREFIXES; static { - Set<String> props = new HashSet<>(20); + Set<String> props = new HashSet<>(21); props.add("bridgeErrorHandler"); props.add("consumes"); props.add("cookieDomain"); @@ -42,6 +42,7 @@ public class PlatformHttpEndpointUriFactory extends org.apache.camel.support.com props.add("path"); props.add("platformHttpEngine"); props.add("produces"); + props.add("returnHttpRequestHeaders"); props.add("useCookieHandler"); props.add("useStreaming"); PROPERTY_NAMES = Collections.unmodifiableSet(props); diff --git a/components/camel-platform-http/src/generated/resources/META-INF/org/apache/camel/component/platform/http/platform-http.json b/components/camel-platform-http/src/generated/resources/META-INF/org/apache/camel/component/platform/http/platform-http.json index f5608a28f73..c3cc5f4e0c0 100644 --- a/components/camel-platform-http/src/generated/resources/META-INF/org/apache/camel/component/platform/http/platform-http.json +++ b/components/camel-platform-http/src/generated/resources/META-INF/org/apache/camel/component/platform/http/platform-http.json @@ -42,13 +42,14 @@ "matchOnUriPrefix": { "index": 9, "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." }, "muteException": { "index": 10, "kind": "parameter", "displayName": "Mute Exception", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "If enabled and an Exchange failed processing on the consumer side the response's body won't contain the exception's stack trace." }, "produces": { "index": 11, "kind": "parameter", "displayName": "Produces", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type this endpoint produces, such as application\/xml or application\/json." }, - "useCookieHandler": { "index": 12, "kind": "parameter", "displayName": "Use Cookie Handler", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to enable the Cookie Handler that allows Cookie addition, expiry, and retrieval (currently only supported by camel-platform-http-vertx)" }, - "useStreaming": { "index": 13, "kind": "parameter", "displayName": "Use Streaming", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to use streaming for large requests and responses (currently only supported by camel-platform-http-vertx)" }, - "bridgeErrorHandler": { "index": 14, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming [...] - "exceptionHandler": { "index": 15, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By de [...] - "exchangePattern": { "index": 16, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." }, - "fileNameExtWhitelist": { "index": 17, "kind": "parameter", "displayName": "File Name Ext Whitelist", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "A comma or whitespace separated list of file extensions. Uploads having these extensions will be stored locally. Null value or asterisk () will allow all files." }, - "headerFilterStrategy": { "index": 18, "kind": "parameter", "displayName": "Header Filter Strategy", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom HeaderFilterStrategy to filter headers to and from Camel message." }, - "platformHttpEngine": { "index": 19, "kind": "parameter", "displayName": "Platform Http 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 of this endpoint." } + "returnHttpRequestHeaders": { "index": 12, "kind": "parameter", "displayName": "Return Http Request Headers", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to include HTTP request headers (Accept, User-Agent, etc.) into HTTP response produced by this endpoint." }, + "useCookieHandler": { "index": 13, "kind": "parameter", "displayName": "Use Cookie Handler", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to enable the Cookie Handler that allows Cookie addition, expiry, and retrieval (currently only supported by camel-platform-http-vertx)" }, + "useStreaming": { "index": 14, "kind": "parameter", "displayName": "Use Streaming", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to use streaming for large requests and responses (currently only supported by camel-platform-http-vertx)" }, + "bridgeErrorHandler": { "index": 15, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming [...] + "exceptionHandler": { "index": 16, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By de [...] + "exchangePattern": { "index": 17, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." }, + "fileNameExtWhitelist": { "index": 18, "kind": "parameter", "displayName": "File Name Ext Whitelist", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "A comma or whitespace separated list of file extensions. Uploads having these extensions will be stored locally. Null value or asterisk () will allow all files." }, + "headerFilterStrategy": { "index": 19, "kind": "parameter", "displayName": "Header Filter Strategy", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom HeaderFilterStrategy to filter headers to and from Camel message." }, + "platformHttpEngine": { "index": 20, "kind": "parameter", "displayName": "Platform Http 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 of this endpoint." } } } 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 8bfe1f86bd5..99dc467c50d 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 @@ -16,9 +16,12 @@ */ package org.apache.camel.component.platform.http; +import java.util.Set; + import org.apache.camel.AsyncEndpoint; import org.apache.camel.Category; import org.apache.camel.Component; +import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.component.platform.http.cookie.CookieConfiguration; @@ -47,6 +50,36 @@ public class PlatformHttpEndpoint extends DefaultEndpoint private static final String PROXY_PATH = "proxy"; + private static final Set<String> COMMON_HTTP_REQUEST_HEADERS = Set.of( + "A-IM", + "Accept", + "Accept-Charset", + "Accept-Encoding", + "Accept-Language", + "Accept-Datetime", + "Access-Control-Request-Method", + "Access-Control-Request-Headers", + "Authorization", + "Cookie", + "Expect", + "Forwarded", + "From", + "Host", + "HTTP2-Settings", + "If-Match", + "If-Modified-Since", + "If-None-Match", + "If-Range", + "If-Unmodified-Since", + "Max-Forwards", + "Origin", + "Prefer", + "Proxy-Authorization", + "Range", + "Referer", + "TE", + "User-Agent"); + @UriPath(description = "The path under which this endpoint serves the HTTP requests, for proxy use 'proxy'") @Metadata(required = true) private final String path; @@ -87,6 +120,10 @@ public class PlatformHttpEndpoint extends DefaultEndpoint + " (currently only supported by camel-platform-http-vertx)") private boolean useCookieHandler; + @UriParam(label = "advanced,consumer", defaultValue = "false", + description = "Whether to include HTTP request headers (Accept, User-Agent, etc.) into HTTP response produced by this endpoint.") + private boolean returnHttpRequestHeaders = false; + public PlatformHttpEndpoint(String uri, String remaining, Component component) { super(uri, component); path = remaining; @@ -132,9 +169,30 @@ public class PlatformHttpEndpoint extends DefaultEndpoint @Override public HeaderFilterStrategy getHeaderFilterStrategy() { + if (!returnHttpRequestHeaders) { + return enhanceHeaderFilterStrategyToSkipHttpRequestHeaders(headerFilterStrategy); + } return headerFilterStrategy; } + private HeaderFilterStrategy enhanceHeaderFilterStrategyToSkipHttpRequestHeaders( + HeaderFilterStrategy headerFilterStrategy) { + return new HeaderFilterStrategy() { + @Override + public boolean applyFilterToCamelHeaders(String headerName, Object headerValue, Exchange exchange) { + if (COMMON_HTTP_REQUEST_HEADERS.contains(headerName)) { + return true; + } + return headerFilterStrategy.applyFilterToExternalHeaders(headerName, headerValue, exchange); + } + + @Override + public boolean applyFilterToExternalHeaders(String headerName, Object headerValue, Exchange exchange) { + return headerFilterStrategy.applyFilterToExternalHeaders(headerName, headerValue, exchange); + } + }; + } + @Override public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) { this.headerFilterStrategy = headerFilterStrategy; @@ -233,4 +291,12 @@ public class PlatformHttpEndpoint extends DefaultEndpoint public boolean isHttpProxy() { return this.path.startsWith(PROXY_PATH); } + + public boolean isReturnHttpRequestHeaders() { + return returnHttpRequestHeaders; + } + + public void setReturnHttpRequestHeaders(boolean returnHttpRequestHeaders) { + this.returnHttpRequestHeaders = returnHttpRequestHeaders; + } } 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 b5db315d861..9af330c2ec3 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 @@ -22,6 +22,10 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.StringJoiner; import jakarta.servlet.http.HttpServletResponse; @@ -29,11 +33,16 @@ import jakarta.servlet.http.HttpServletResponse; import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.Processor; +import org.apache.camel.TypeConverter; import org.apache.camel.component.platform.http.spi.PlatformHttpConsumer; +import org.apache.camel.spi.HeaderFilterStrategy; import org.apache.camel.support.CamelContextHelper; import org.apache.camel.support.DefaultConsumer; import org.apache.camel.support.DefaultMessage; +import org.apache.camel.support.ObjectHelper; +import org.apache.camel.support.http.HttpUtil; import org.apache.camel.util.IOHelper; +import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; @@ -107,6 +116,9 @@ public class JettyCustomPlatformHttpConsumer extends DefaultConsumer implements String body = JettyCustomPlatformHttpConsumer.toString(responseStream); exchange.getMessage().setBody(body); } + + copyMessageHeadersToResponse(response, exchange.getMessage(), getEndpoint().getHeaderFilterStrategy(), + exchange); response.write(true, ByteBuffer.wrap(exchange.getMessage().getBody(String.class).getBytes(StandardCharsets.UTF_8)), callback); @@ -129,6 +141,36 @@ public class JettyCustomPlatformHttpConsumer extends DefaultConsumer implements return contextHandler; } + private void copyMessageHeadersToResponse( + Response response, + Message message, + HeaderFilterStrategy headerFilterStrategy, + Exchange exchange) { + final TypeConverter tc = exchange.getContext().getTypeConverter(); + for (Map.Entry<String, Object> entry : message.getHeaders().entrySet()) { + final String key = entry.getKey(); + final Object value = entry.getValue(); + final Iterator<?> it = ObjectHelper.createIterator(value, null, true); + + Set<String> responseHeadersFilledByJetty = Set.of("content-length"); + if (responseHeadersFilledByJetty.contains(entry.getKey().toLowerCase())) { + continue; + } + + HttpUtil.applyHeader(headerFilterStrategy, exchange, it, tc, key, + (values, firstValue) -> applyHeader(response, key, values, firstValue)); + } + + } + + private void applyHeader(Response response, String key, List<String> values, String firstValue) { + if (values != null) { + response.getHeaders().put(key, values); + } else if (firstValue != null) { + response.getHeaders().put(key, firstValue); + } + } + private Exchange toExchange(Request request, String body) { final Exchange exchange = getEndpoint().createExchange(); final Message message = new DefaultMessage(exchange); @@ -139,6 +181,10 @@ public class JettyCustomPlatformHttpConsumer extends DefaultConsumer implements message.setHeader(Exchange.HTTP_CHARACTER_ENCODING, charset); } + for (HttpField header : request.getHeaders()) { + message.setHeader(header.getName(), header.getValue()); + } + message.setBody(!body.isEmpty() ? body : null); exchange.setMessage(message); return exchange; diff --git a/components/camel-platform-http/src/test/java/org/apache/camel/component/platform/http/PlatformHttpReturnHttpRequestHeadersTest.java b/components/camel-platform-http/src/test/java/org/apache/camel/component/platform/http/PlatformHttpReturnHttpRequestHeadersTest.java new file mode 100644 index 00000000000..7d1536a4371 --- /dev/null +++ b/components/camel-platform-http/src/test/java/org/apache/camel/component/platform/http/PlatformHttpReturnHttpRequestHeadersTest.java @@ -0,0 +1,83 @@ +/* + * 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 org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; + +public class PlatformHttpReturnHttpRequestHeadersTest extends AbstractPlatformHttpTest { + + @Test + void testReturnHttpRequestHeadersFalse() { + given() + .header("Accept", "application/json") + .header("User-Agent", "User-Agent-Camel") + .port(port) + .expect() + .statusCode(200) + .header("Accept", (String) null) + .header("User-Agent", (String) null) + .when() + .get("/getWithoutRequestHeadersReturn"); + } + + @Test + void testReturnHttpRequestHeadersTrue() { + given() + .header("Accept", "application/json") + .header("User-Agent", "User-Agent-Camel") + .port(port) + .expect() + .statusCode(200) + .header("Accept", "application/json") + .header("User-Agent", "User-Agent-Camel") + .when() + .get("/getWithRequestHeadersReturn"); + } + + @Test + void testReturnHttpRequestHeadersDefault() { + given() + .header("Accept", "application/json") + .header("User-Agent", "User-Agent-Camel") + .port(port) + .expect() + .statusCode(200) + .header("Accept", (String) null) + .header("User-Agent", (String) null) + .when() + .get("/get"); + } + + @Override + protected RouteBuilder routes() { + return new RouteBuilder() { + @Override + public void configure() { + from("platform-http:/getWithoutRequestHeadersReturn?returnHttpRequestHeaders=false") + .setBody().constant("getWithoutRequestHeadersReturn"); + from("platform-http:/getWithRequestHeadersReturn?returnHttpRequestHeaders=true") + .setBody().constant("getWithRequestHeadersReturn"); + from("platform-http:/get") + .setBody().constant("get"); + } + }; + } + +} 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 fd9f2e3de0e..0f2e4a9790b 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 @@ -323,6 +323,38 @@ public interface PlatformHttpEndpointBuilderFactory { return (PlatformHttpEndpointBuilder) this; } + /** + * Whether to include HTTP request headers (Accept, User-Agent, etc.) + * into HTTP response produced by this endpoint. + * + * The option is a: <code>boolean</code> type. + * + * Default: false + * Group: consumer + * + * @param returnHttpRequestHeaders the value to set + * @return the dsl builder + */ + default AdvancedPlatformHttpEndpointBuilder returnHttpRequestHeaders(boolean returnHttpRequestHeaders) { + doSetProperty("returnHttpRequestHeaders", returnHttpRequestHeaders); + return this; + } + /** + * Whether to include HTTP request headers (Accept, User-Agent, etc.) + * into HTTP response produced by this endpoint. + * + * The option will be converted to a <code>boolean</code> type. + * + * Default: false + * Group: consumer + * + * @param returnHttpRequestHeaders the value to set + * @return the dsl builder + */ + default AdvancedPlatformHttpEndpointBuilder returnHttpRequestHeaders(String returnHttpRequestHeaders) { + doSetProperty("returnHttpRequestHeaders", returnHttpRequestHeaders); + return this; + } /** * Whether to enable the Cookie Handler that allows Cookie addition, * expiry, and retrieval (currently only supported by diff --git a/dsl/camel-kotlin-api/src/generated/kotlin/org/apache/camel/kotlin/components/PlatformHttpUriDsl.kt b/dsl/camel-kotlin-api/src/generated/kotlin/org/apache/camel/kotlin/components/PlatformHttpUriDsl.kt index deac5fafa62..21c0ab1180d 100644 --- a/dsl/camel-kotlin-api/src/generated/kotlin/org/apache/camel/kotlin/components/PlatformHttpUriDsl.kt +++ b/dsl/camel-kotlin-api/src/generated/kotlin/org/apache/camel/kotlin/components/PlatformHttpUriDsl.kt @@ -169,6 +169,22 @@ public class PlatformHttpUriDsl( it.property("produces", produces) } + /** + * Whether to include HTTP request headers (Accept, User-Agent, etc.) into HTTP response produced + * by this endpoint. + */ + public fun returnHttpRequestHeaders(returnHttpRequestHeaders: String) { + it.property("returnHttpRequestHeaders", returnHttpRequestHeaders) + } + + /** + * Whether to include HTTP request headers (Accept, User-Agent, etc.) into HTTP response produced + * by this endpoint. + */ + public fun returnHttpRequestHeaders(returnHttpRequestHeaders: Boolean) { + it.property("returnHttpRequestHeaders", returnHttpRequestHeaders.toString()) + } + /** * Whether to enable the Cookie Handler that allows Cookie addition, expiry, and retrieval * (currently only supported by camel-platform-http-vertx)