This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch srv2 in repository https://gitbox.apache.org/repos/asf/camel.git
commit b89518d1d8fd421a688e84a37bd40707e58c5997 Author: Claus Ibsen <[email protected]> AuthorDate: Sun Dec 21 10:46:57 2025 +0100 CAMEL-22798: camel-platform-http-vertx - VertX has hardcoded content-type validation --- .../camel/catalog/components/platform-http.json | 7 +- .../http/vertx/VertxPlatformHttpConsumer.java | 20 ++-- .../VertxPlatformServerRequestValidationTest.java | 121 +++++++++++++++++++++ .../http/PlatformHttpComponentConfigurer.java | 6 + .../component/platform/http/platform-http.json | 7 +- .../platform/http/PlatformHttpComponent.java | 15 +++ .../dsl/PlatformHttpComponentBuilderFactory.java | 25 +++++ 7 files changed, 186 insertions(+), 15 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 970d47bf17ab..9de65466be58 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 @@ -28,9 +28,10 @@ "bridgeErrorHandler": { "index": 0, "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...] "handleWriteResponseError": { "index": 1, "kind": "property", "displayName": "Handle Write Response Error", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "When Camel is complete processing the message, and the HTTP server is writing response. This option controls whether Camel should catch any failure during writing response [...] "requestTimeout": { "index": 2, "kind": "property", "displayName": "Request Timeout", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "description": "The period in milliseconds after which the request should be timed out." }, - "autowiredEnabled": { "index": 3, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...] - "engine": { "index": 4, "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" }, - "headerFilterStrategy": { "index": 5, "kind": "property", "displayName": "Header Filter Strategy", "group": "filter", "label": "filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel message." } + "serverRequestValidation": { "index": 3, "kind": "property", "displayName": "Server Request Validation", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether HTTP server should do preliminary validation of incoming requests, validating if Content-Type\/Accept header, matches what is allowed according to consumes\/produces c [...] + "autowiredEnabled": { "index": 4, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...] + "engine": { "index": 5, "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" }, + "headerFilterStrategy": { "index": 6, "kind": "property", "displayName": "Header Filter Strategy", "group": "filter", "label": "filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel message." } }, "properties": { "path": { "index": 0, "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'" }, 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 324650f84605..fd088b7f2a03 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 @@ -149,16 +149,18 @@ public class VertxPlatformHttpConsumer extends DefaultConsumer methods.forEach(m -> newRoute.method(HttpMethod.valueOf(m.name()))); } - if (getEndpoint().getConsumes() != null) { - //comma separated contentTypes has to be registered one by one - for (String c : getEndpoint().getConsumes().split(",")) { - newRoute.consumes(c); + if (getEndpoint().getComponent().isServerRequestValidation()) { + if (getEndpoint().getConsumes() != null) { + //comma separated contentTypes has to be registered one by one + for (String c : getEndpoint().getConsumes().split(",")) { + newRoute.consumes(c); + } } - } - if (getEndpoint().getProduces() != null) { - //comma separated contentTypes has to be registered one by one - for (String p : getEndpoint().getProduces().split(",")) { - newRoute.produces(p); + if (getEndpoint().getProduces() != null) { + //comma separated contentTypes has to be registered one by one + for (String p : getEndpoint().getProduces().split(",")) { + newRoute.produces(p); + } } } diff --git a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformServerRequestValidationTest.java b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformServerRequestValidationTest.java new file mode 100644 index 000000000000..deca3af3c4ef --- /dev/null +++ b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/VertxPlatformServerRequestValidationTest.java @@ -0,0 +1,121 @@ +/* + * 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 org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.platform.http.PlatformHttpComponent; +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; + +public class VertxPlatformServerRequestValidationTest { + + @Test + void testServerRequestFalse() throws Exception { + final CamelContext context = VertxPlatformHttpEngineTest.createCamelContext(); + + try { + context.addRoutes(new RouteBuilder() { + @Override + public void configure() { + PlatformHttpComponent phc = context.getComponent("platform-http", PlatformHttpComponent.class); + phc.setServerRequestValidation(false); + + restConfiguration().component("platform-http") + .contextPath("/rest"); + + rest().post("/test") + .consumes("application/json") + .produces("application/json") + .to("direct:rest"); + + from("direct:rest") + .setBody(simple("Hello")); + } + }); + + context.start(); + + given() + .body("<hello>World</hello>") + .contentType("application/xml") + .post("/rest/test") + .then() + .statusCode(200) + .body(is("Hello")); + + given() + .body("{ \"name\": \"jack\" }") + .contentType("application/json") + .accept("application/xml") + .post("/rest/test") + .then() + .statusCode(200) + .body(is("Hello")); + } finally { + context.stop(); + } + } + + @Test + void testServerRequestTrue() throws Exception { + final CamelContext context = VertxPlatformHttpEngineTest.createCamelContext(); + + try { + context.addRoutes(new RouteBuilder() { + @Override + public void configure() { + PlatformHttpComponent phc = context.getComponent("platform-http", PlatformHttpComponent.class); + phc.setServerRequestValidation(true); + + restConfiguration().component("platform-http") + .contextPath("/rest"); + + rest().post("/test") + .consumes("application/json") + .produces("application/json") + .to("direct:rest"); + + from("direct:rest") + .setBody(simple("Hello")); + } + }); + + context.start(); + + given() + .body("<hello>World</hello>") + .contentType("application/xml") + .post("/rest/test") + .then() + .statusCode(415); + + given() + .body("{ \"name\": \"jack\" }") + .contentType("application/json") + .accept("application/xml") + .post("/rest/test") + .then() + .statusCode(406); + } finally { + context.stop(); + } + } + +} diff --git a/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpComponentConfigurer.java b/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpComponentConfigurer.java index 72a4a7d45dc0..c0beb64b8244 100644 --- a/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpComponentConfigurer.java +++ b/components/camel-platform-http/src/generated/java/org/apache/camel/component/platform/http/PlatformHttpComponentConfigurer.java @@ -34,6 +34,8 @@ public class PlatformHttpComponentConfigurer extends PropertyConfigurerSupport i case "headerFilterStrategy": target.setHeaderFilterStrategy(property(camelContext, org.apache.camel.spi.HeaderFilterStrategy.class, value)); return true; case "requesttimeout": case "requestTimeout": target.setRequestTimeout(property(camelContext, long.class, value)); return true; + case "serverrequestvalidation": + case "serverRequestValidation": target.setServerRequestValidation(property(camelContext, boolean.class, value)); return true; default: return false; } } @@ -52,6 +54,8 @@ public class PlatformHttpComponentConfigurer extends PropertyConfigurerSupport i case "headerFilterStrategy": return org.apache.camel.spi.HeaderFilterStrategy.class; case "requesttimeout": case "requestTimeout": return long.class; + case "serverrequestvalidation": + case "serverRequestValidation": return boolean.class; default: return null; } } @@ -71,6 +75,8 @@ public class PlatformHttpComponentConfigurer extends PropertyConfigurerSupport i case "headerFilterStrategy": return target.getHeaderFilterStrategy(); case "requesttimeout": case "requestTimeout": return target.getRequestTimeout(); + case "serverrequestvalidation": + case "serverRequestValidation": return target.isServerRequestValidation(); default: return null; } } 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 970d47bf17ab..9de65466be58 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 @@ -28,9 +28,10 @@ "bridgeErrorHandler": { "index": 0, "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...] "handleWriteResponseError": { "index": 1, "kind": "property", "displayName": "Handle Write Response Error", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "When Camel is complete processing the message, and the HTTP server is writing response. This option controls whether Camel should catch any failure during writing response [...] "requestTimeout": { "index": 2, "kind": "property", "displayName": "Request Timeout", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "description": "The period in milliseconds after which the request should be timed out." }, - "autowiredEnabled": { "index": 3, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...] - "engine": { "index": 4, "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" }, - "headerFilterStrategy": { "index": 5, "kind": "property", "displayName": "Header Filter Strategy", "group": "filter", "label": "filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel message." } + "serverRequestValidation": { "index": 3, "kind": "property", "displayName": "Server Request Validation", "group": "consumer", "label": "advanced,consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether HTTP server should do preliminary validation of incoming requests, validating if Content-Type\/Accept header, matches what is allowed according to consumes\/produces c [...] + "autowiredEnabled": { "index": 4, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...] + "engine": { "index": 5, "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" }, + "headerFilterStrategy": { "index": 6, "kind": "property", "displayName": "Header Filter Strategy", "group": "filter", "label": "filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel message." } }, "properties": { "path": { "index": 0, "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'" }, diff --git a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpComponent.java b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpComponent.java index 00abe1cf0f4b..c272ac263a08 100644 --- a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpComponent.java +++ b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpComponent.java @@ -64,6 +64,13 @@ public class PlatformHttpComponent extends HeaderFilterStrategyComponent @Metadata(label = "advanced,consumer", description = "The period in milliseconds after which the request should be timed out.") private long requestTimeout; + @Metadata(label = "advanced,consumer", defaultValue = "true", + description = "Whether HTTP server should do preliminary validation of incoming requests, validating if Content-Type/Accept header, matches what" + + " is allowed according to consumes/produces configuration (if set). If validation fails HTTP Status 415/406 is returned." + + " The HTTP server performs this validation before Camel is involved, and as such if validation fails then Camel is never activated." + + " Setting this option to false, allows Camel to process any incoming requests such as to do custom validation" + + " or all requests must be handled by Camel.") + private boolean serverRequestValidation = true; private final Set<HttpEndpointModel> httpEndpoints = new TreeSet<>(); private final Set<HttpEndpointModel> httpManagementEndpoints = new TreeSet<>(); @@ -248,6 +255,14 @@ public class PlatformHttpComponent extends HeaderFilterStrategyComponent this.requestTimeout = requestTimeout; } + public boolean isServerRequestValidation() { + return serverRequestValidation; + } + + public void setServerRequestValidation(boolean serverRequestValidation) { + this.serverRequestValidation = serverRequestValidation; + } + private Consumer doCreateConsumer( CamelContext camelContext, Processor processor, String verb, String basePath, String uriTemplate, diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/PlatformHttpComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/PlatformHttpComponentBuilderFactory.java index 0bbda8aae1a8..f981332b2d0b 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/PlatformHttpComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/PlatformHttpComponentBuilderFactory.java @@ -118,6 +118,30 @@ public interface PlatformHttpComponentBuilderFactory { } + /** + * Whether HTTP server should do preliminary validation of incoming + * requests, validating if Content-Type/Accept header, matches what is + * allowed according to consumes/produces configuration (if set). If + * validation fails HTTP Status 415/406 is returned. The HTTP server + * performs this validation before Camel is involved, and as such if + * validation fails then Camel is never activated. Setting this option + * to false, allows Camel to process any incoming requests such as to do + * custom validation or all requests must be handled by Camel. + * + * The option is a: <code>boolean</code> type. + * + * Default: true + * Group: consumer + * + * @param serverRequestValidation the value to set + * @return the dsl builder + */ + default PlatformHttpComponentBuilder serverRequestValidation(boolean serverRequestValidation) { + doSetProperty("serverRequestValidation", serverRequestValidation); + return this; + } + + /** * Whether autowiring is enabled. This is used for automatic autowiring * options (the option must be marked as autowired) by looking up in the @@ -190,6 +214,7 @@ public interface PlatformHttpComponentBuilderFactory { case "bridgeErrorHandler": ((PlatformHttpComponent) component).setBridgeErrorHandler((boolean) value); return true; case "handleWriteResponseError": ((PlatformHttpComponent) component).setHandleWriteResponseError((boolean) value); return true; case "requestTimeout": ((PlatformHttpComponent) component).setRequestTimeout((long) value); return true; + case "serverRequestValidation": ((PlatformHttpComponent) component).setServerRequestValidation((boolean) value); return true; case "autowiredEnabled": ((PlatformHttpComponent) component).setAutowiredEnabled((boolean) value); return true; case "engine": ((PlatformHttpComponent) component).setEngine((org.apache.camel.component.platform.http.spi.PlatformHttpEngine) value); return true; case "headerFilterStrategy": ((PlatformHttpComponent) component).setHeaderFilterStrategy((org.apache.camel.spi.HeaderFilterStrategy) value); return true;
