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

fmariani pushed a commit to branch camel-spring-boot-4.8.x
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git

commit dab7b32697e925da1524c3468acdb598411e3057
Author: Rinaldo Pitzer JĂșnior <[email protected]>
AuthorDate: Fri Dec 6 05:27:17 2024 -0300

    CAMEL-21461: platform-http - add CORS support (#1299)
---
 .../springboot/CamelRequestHandlerMapping.java     | 107 ++++++++++-
 .../SpringBootPlatformHttpCorsCredentialsTest.java | 201 +++++++++++++++++++++
 .../springboot/SpringBootPlatformHttpCorsTest.java | 173 ++++++++++++++++++
 .../SpringBootPlatformHttpOptionsTest.java         | 124 +++++++++++++
 .../src/test/resources/application.properties      |  22 ++-
 5 files changed, 621 insertions(+), 6 deletions(-)

diff --git 
a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java
 
b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java
index 91d84e4c4d8..380d69ce232 100644
--- 
a/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java
+++ 
b/components-starter/camel-platform-http-starter/src/main/java/org/apache/camel/component/platform/http/springboot/CamelRequestHandlerMapping.java
@@ -23,8 +23,10 @@ import 
org.apache.camel.component.platform.http.PlatformHttpComponent;
 import org.apache.camel.component.platform.http.PlatformHttpEndpoint;
 import org.apache.camel.component.platform.http.PlatformHttpListener;
 import org.apache.camel.component.platform.http.spi.PlatformHttpEngine;
+import org.apache.camel.spi.RestConfiguration;
 import org.apache.camel.util.ReflectionHelper;
 import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
 import 
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@@ -32,13 +34,20 @@ import org.springframework.web.util.ServletRequestPathUtils;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 public class CamelRequestHandlerMapping extends RequestMappingHandlerMapping 
implements PlatformHttpListener {
 
     private final PlatformHttpComponent component;
     private final PlatformHttpEngine engine;
 
+    private CorsConfiguration corsConfiguration;
+
     public CamelRequestHandlerMapping(PlatformHttpComponent component, 
PlatformHttpEngine engine) {
         this.component = component;
         this.engine = engine;
@@ -67,6 +76,55 @@ public class CamelRequestHandlerMapping extends 
RequestMappingHandlerMapping imp
         return null;
     }
 
+    @Override
+    protected CorsConfiguration initCorsConfiguration(Object handler, Method 
method, RequestMappingInfo mappingInfo) {
+        RestConfiguration restConfiguration = 
component.getCamelContext().getRestConfiguration();
+
+        if (!restConfiguration.isEnableCORS()) {
+            // CORS disabled for camel
+            return null;
+        }
+
+        if (corsConfiguration == null) {
+            Map<String, String> corsHeaders = 
restConfiguration.getCorsHeaders();
+            corsConfiguration = createCorsConfiguration(corsHeaders != null ? 
corsHeaders : Collections.emptyMap());
+        }
+        return corsConfiguration;
+    }
+
+    @Override
+    protected CorsConfiguration getCorsConfiguration(Object handler, 
HttpServletRequest request) {
+        return super.getCorsConfiguration(handler, request);
+    }
+
+    private CorsConfiguration createCorsConfiguration(Map<String, String> 
corsHeaders) {
+        CorsConfiguration config = new CorsConfiguration();
+
+        String allowedOrigin = corsHeaders.get("Access-Control-Allow-Origin");
+        config.addAllowedOrigin(allowedOrigin != null ? allowedOrigin : 
RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_ORIGIN);
+
+        String allowMethodsStr = 
corsHeaders.get("Access-Control-Allow-Methods");
+        allowMethodsStr = allowMethodsStr != null ? allowMethodsStr : 
RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_METHODS;
+        for (String allowMethod : allowMethodsStr.split(",")) {
+            config.addAllowedMethod(allowMethod.trim());
+        }
+
+        String allowHeadersStr = 
corsHeaders.get("Access-Control-Allow-Headers");
+        allowHeadersStr = allowHeadersStr != null ? allowHeadersStr : 
RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_HEADERS;
+        for (String allowHeader : allowHeadersStr.split(",")) {
+            config.addAllowedHeader(allowHeader.trim());
+        }
+
+        String maxAgeStr = corsHeaders.get("Access-Control-Max-Age");
+        Long maxAge = maxAgeStr != null ? Long.parseLong(maxAgeStr) : 
Long.parseLong(RestConfiguration.CORS_ACCESS_CONTROL_MAX_AGE);
+        config.setMaxAge(maxAge);
+
+        String allowCredentials = 
corsHeaders.get("Access-Control-Allow-Credentials");
+        config.setAllowCredentials(Boolean.parseBoolean(allowCredentials));
+
+        return config;
+    }
+
     @Override
     protected HandlerMethod getHandlerInternal(HttpServletRequest request) 
throws Exception {
         ServletRequestPathUtils.parseAndCache(request);
@@ -78,7 +136,11 @@ public class CamelRequestHandlerMapping extends 
RequestMappingHandlerMapping imp
         RequestMappingInfo info = asRequestMappingInfo(model);
         Method m = 
ReflectionHelper.findMethod(SpringBootPlatformHttpConsumer.class, "service",
                 HttpServletRequest.class, HttpServletResponse.class);
-        registerMapping(info, model.getConsumer(), m);
+        for (RequestMappingInfo info : requestMappingInfos) {
+            // Needed in case of context reload
+            unregisterMapping(info);
+            registerMapping(info, model.getConsumer(), m);
+        }
     }
 
     @Override
@@ -93,6 +155,9 @@ public class CamelRequestHandlerMapping extends 
RequestMappingHandlerMapping imp
         if (verbs == null && model.getConsumer() != null) {
             PlatformHttpEndpoint endpoint = (PlatformHttpEndpoint) 
model.getConsumer().getEndpoint();
             verbs = endpoint.getHttpMethodRestrict();
+            if (verbs == null) {
+                Collections.addAll(methods, RequestMethod.values());
+            }
         }
         if (verbs != null) {
             for (String v : model.getVerbs().split(",")) {
@@ -101,9 +166,43 @@ public class CamelRequestHandlerMapping extends 
RequestMappingHandlerMapping imp
             }
         }
 
-        RequestMappingInfo info = RequestMappingInfo.paths(model.getUri())
-                .methods(methods.toArray(new 
RequestMethod[0])).options(this.getBuilderConfiguration()).build();
-        return info;
+        if (component.getCamelContext().getRestConfiguration().isEnableCORS()) 
{
+            // when CORS is enabled Camel adds OPTIONS by default in 
httpMethodsRestrict
+            // which causes multiple registration of OPTIONS endpoints in 
spring.
+            // this causes issues when unregistering endpoints because others 
share
+            // the same handler (the consumer) so they get unregistered as 
well.
+            // removing the OPTIONS mappings maintains the intended behavior
+            // of this verb while avoiding this issue.
+            methods.remove(RequestMethod.OPTIONS);
+        }
+
+        for (RequestMethod rm : methods) {
+            createRequestMappingInfo(model, rm, result);
+        }
+
+        return result;
+    }
+
+    private void createRequestMappingInfo(HttpEndpointModel model, 
RequestMethod rm, List<RequestMappingInfo> result) {
+        RequestMethod[] methods = new RequestMethod[]{};
+        if (rm != null) {
+            methods = new RequestMethod[]{rm};
+        }
+
+        RequestMappingInfo.Builder info = RequestMappingInfo
+                .paths(model.getUri())
+                .methods(methods)
+                .options(this.getBuilderConfiguration());
+
+        if (model.getConsumes() != null
+                && (RequestMethod.POST.name().equals(rm.name()) || 
RequestMethod.PUT.name().equals(rm.name()) || 
RequestMethod.PATCH.name().equals(rm.name()))) {
+            info.consumes(model.getConsumes().split(","));
+        }
+        if (model.getProduces() != null) {
+            info.produces(model.getProduces().split(","));
+        }
+
+        result.add(info.build());
     }
 
 }
diff --git 
a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCorsCredentialsTest.java
 
b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCorsCredentialsTest.java
new file mode 100644
index 00000000000..b7a60072a3e
--- /dev/null
+++ 
b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCorsCredentialsTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.springboot;
+
+import io.restassured.RestAssured;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import 
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
+import 
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.*;
+
+@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, 
SecurityAutoConfiguration.class})
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 
classes = { CamelAutoConfiguration.class,
+        SpringBootPlatformHttpCorsCredentialsTest.class, 
SpringBootPlatformHttpCorsCredentialsTest.TestConfiguration.class,
+        PlatformHttpComponentAutoConfiguration.class, 
SpringBootPlatformHttpAutoConfiguration.class, })
+public class SpringBootPlatformHttpCorsCredentialsTest {
+
+    @LocalServerPort
+    private Integer port;
+
+    @BeforeEach
+    void setUp() {
+        RestAssured.port = port;
+        RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
+    }
+
+    @Configuration
+    public static class TestConfiguration {
+        @Bean
+        public RouteBuilder springBootPlatformHttpRestDSLRouteBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    restConfiguration().component("platform-http")
+                        .enableCORS(true)
+                        .corsAllowCredentials(true)
+                        .corsHeaderProperty("Access-Control-Allow-Origin", 
"http://custom.origin.springboot";);
+
+                    rest("/rest")
+                        .get().to("direct:restGet")
+                        
.post().consumes("application/json").to("direct:restPost");
+
+                    from("direct:restPost")
+                        .transform().constant("corsPost");
+
+                    from("direct:restGet")
+                        .transform().constant("corsGet");
+                }
+            };
+        }
+    }
+
+    @Test
+    public void get() {
+        given()
+            .when()
+            .get("/rest")
+            .then()
+            .statusCode(200)
+            .body(equalTo("corsGet"));
+    }
+
+    @Test
+    public void post() {
+        given()
+            .header("Content-type","application/json")
+            .when()
+            .post("/rest")
+            .then()
+            .statusCode(200)
+            .body(equalTo("corsPost"));
+    }
+
+    @Test
+    public void options() {
+        final String origin = "http://custom.origin.springboot";;
+        final String method = "POST";
+        final String headers = "X-Requested-With";
+
+        given()
+            .header("Origin", origin)
+            .header("Access-Control-Request-Method", method)
+            .header("Access-Control-Request-Headers", headers)
+            .when()
+            .options("/rest")
+            .then()
+            .statusCode(200)
+            .header("Access-Control-Allow-Origin", origin)
+            .header("Access-Control-Allow-Methods", containsString(method))
+            .header("Access-Control-Allow-Headers", containsString(headers))
+            .header("Access-Control-Allow-Credentials", "true")
+            .header("Access-Control-Max-Age", 
RestConfiguration.CORS_ACCESS_CONTROL_MAX_AGE)
+            .body(emptyString());
+    }
+
+    @Test
+    public void getForbiddenOrigin() {
+        final String origin = "http://custom2.origin.springboot";;
+
+        given()
+            .header("Origin", origin)
+            .when()
+            .get("/rest")
+            .then()
+            .statusCode(403);
+    }
+
+    @Test
+    public void postForbiddenOrigin() {
+        final String origin = "http://custom2.origin.springboot";;
+
+        given()
+            .header("Origin", origin)
+            .header("Content-type","application/json")
+            .when()
+            .post("/rest")
+            .then()
+            .statusCode(403);
+    }
+
+    @Test
+    public void optionsForbiddenOrigin() {
+        final String origin = "http://custom2.origin.springboot";;
+
+        given()
+            .header("Origin", origin)
+            .when()
+            .options("/rest")
+            .then()
+            .header("Access-Control-Allow-Origin", 
not(containsString(origin)));
+    }
+
+    @Test
+    public void optionsForbiddenMethod() {
+        String method = "DELETE";
+
+        given()
+            .header("Access-Control-Request-Method", method)
+            .when()
+            .options("/rest")
+            .then()
+            .header("Access-Control-Allow-Methods", 
not(containsString(method)));
+    }
+
+    @Test
+    public void optionsForbiddenHeader() {
+        String header = "X-Custom-Header";
+
+        given()
+            .header("Access-Control-Request-Headers", header)
+            .when()
+            .options("/rest")
+            .then()
+            .header("Access-Control-Allow-Headers", 
not(containsString(header)));
+    }
+
+    @Test
+    public void optionNonCors() {
+        given()
+            .when()
+            .options("/rest")
+            .then()
+            .statusCode(200)
+            .header("Allow", containsString("OPTIONS"))
+            .header("Allow", containsString("GET"))
+            .header("Allow", containsString("POST"))
+            .header("Allow", containsString("HEAD"))
+            .header("Allow", not(containsString("PATCH")))
+            .header("Allow", not(containsString("PUT")))
+            .header("Allow", not(containsString("DELETE")));
+    }
+    
+}
diff --git 
a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCorsTest.java
 
b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCorsTest.java
new file mode 100644
index 00000000000..fd7e3cd7f35
--- /dev/null
+++ 
b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpCorsTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.springboot;
+
+import io.restassured.RestAssured;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import 
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
+import 
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.emptyString;
+import static org.hamcrest.Matchers.not;
+
+@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, 
SecurityAutoConfiguration.class})
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 
classes = { CamelAutoConfiguration.class,
+        SpringBootPlatformHttpCorsTest.class, 
SpringBootPlatformHttpCorsTest.TestConfiguration.class,
+        PlatformHttpComponentAutoConfiguration.class, 
SpringBootPlatformHttpAutoConfiguration.class, })
+public class SpringBootPlatformHttpCorsTest {
+
+    @LocalServerPort
+    private Integer port;
+
+    @BeforeEach
+    void setUp() {
+        RestAssured.port = port;
+        RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
+    }
+
+    @Configuration
+    public static class TestConfiguration {
+        @Bean
+        public RouteBuilder springBootPlatformHttpRestDSLRouteBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    
restConfiguration().component("platform-http").enableCORS(true);
+
+                    rest("/rest")
+                            
.post().consumes("application/json").to("direct:rest");
+
+                    from("direct:rest")
+                            .setBody(simple("Hello ${body}"));
+
+                    from("platform-http:/cors?httpMethodRestrict=GET,POST")
+                            .transform().constant("cors");
+                }
+            };
+        }
+    }
+
+    @Test
+    public void get() {
+        given()
+            .when()
+            .get("/cors")
+            .then()
+            .statusCode(200);
+    }
+
+    @Test
+    public void post() {
+        given()
+            .header("Content-type","application/json")
+            .when()
+            .post("/rest")
+            .then()
+            .statusCode(200);
+    }
+
+    @Test
+    public void options() {
+        final String origin = "http://custom.origin.springboot";;
+        final String method = "POST";
+        final String headers = "X-Requested-With";
+
+        given()
+            .header("Origin", origin)
+            .header("Access-Control-Request-Method", method)
+            .header("Access-Control-Request-Headers", headers)
+            .when()
+            .options("/rest")
+            .then()
+            .statusCode(200)
+            .header("Access-Control-Allow-Origin", "*")
+            .header("Access-Control-Allow-Methods", containsString(method))
+            .header("Access-Control-Allow-Headers", containsString(headers))
+            .header("Access-Control-Max-Age", 
RestConfiguration.CORS_ACCESS_CONTROL_MAX_AGE)
+            .body(emptyString());
+    }
+
+    @Test
+    public void optionsSecondEndPoint() {
+        final String origin = "http://custom.origin.springboot";;
+        final String method = "POST";
+        final String headers = "X-Requested-With";
+
+        given()
+            .header("Origin", origin)
+            .header("Access-Control-Request-Method", method)
+            .header("Access-Control-Request-Headers", headers)
+            .when()
+            .options("/cors")
+            .then()
+            .statusCode(200)
+            .header("Access-Control-Allow-Origin", "*")
+            .header("Access-Control-Allow-Methods", containsString(method))
+            .header("Access-Control-Allow-Headers", containsString(headers))
+            .header("Access-Control-Max-Age", 
RestConfiguration.CORS_ACCESS_CONTROL_MAX_AGE)
+            .body(emptyString());
+    }
+
+    @Test
+    public void optionNonCORS() {
+        given()
+            .when()
+            .options("/rest")
+            .then()
+            .statusCode(200)
+            .header("Allow", containsString("OPTIONS"))
+            .header("Allow", not(containsString("GET")))
+            .header("Allow", containsString("POST"))
+            .header("Allow", not(containsString("HEAD")))
+            .header("Allow", not(containsString("PATCH")))
+            .header("Allow", not(containsString("PUT")))
+            .header("Allow", not(containsString("DELETE")));
+    }
+
+    @Test
+    public void optionNonCorsSecondEndpoint() {
+        given()
+            .when()
+            .options("/cors")
+            .then()
+            .statusCode(200)
+            .header("Allow", containsString("OPTIONS"))
+            .header("Allow", containsString("GET"))
+            .header("Allow", containsString("POST"))
+            .header("Allow", containsString("HEAD"))
+            .header("Allow", not(containsString("PATCH")))
+            .header("Allow", not(containsString("PUT")))
+            .header("Allow", not(containsString("DELETE")));
+    }
+
+}
diff --git 
a/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpOptionsTest.java
 
b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpOptionsTest.java
new file mode 100644
index 00000000000..036dd359cf1
--- /dev/null
+++ 
b/components-starter/camel-platform-http-starter/src/test/java/org/apache/camel/component/platform/http/springboot/SpringBootPlatformHttpOptionsTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.springboot;
+
+import io.restassured.RestAssured;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import 
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
+import 
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.*;
+
+@EnableAutoConfiguration(exclude = {OAuth2ClientAutoConfiguration.class, 
SecurityAutoConfiguration.class})
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 
classes = { CamelAutoConfiguration.class,
+        SpringBootPlatformHttpOptionsTest.class, 
SpringBootPlatformHttpOptionsTest.TestConfiguration.class,
+        PlatformHttpComponentAutoConfiguration.class, 
SpringBootPlatformHttpAutoConfiguration.class, })
+public class SpringBootPlatformHttpOptionsTest {
+
+    @LocalServerPort
+    private Integer port;
+
+    @BeforeEach
+    void setUp() {
+        RestAssured.port = port;
+        RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
+    }
+
+    @Configuration
+    public static class TestConfiguration {
+        @Bean
+        public RouteBuilder springBootPlatformHttpRestDSLRouteBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    
restConfiguration().component("platform-http").enableCORS(false);
+
+                    rest("/rest")
+                        .get().to("direct:get")
+                        .post().to("direct:post");
+                    from("direct:get").transform().constant("get");
+                    from("direct:post").transform().constant("post");
+
+                    
from("platform-http:/restRestricted?httpMethodRestrict=GET,PUT").transform().constant("restricted");
+                    
from("platform-http:/restDefault").transform().constant("default");
+                }
+            };
+        }
+    }
+
+    @Test
+    public void optionsRest() {
+        given()
+            .when()
+            .options("/rest")
+            .then()
+            .statusCode(200)
+            .header("Allow", containsString("OPTIONS"))
+            .header("Allow", containsString("GET"))
+            .header("Allow", containsString("POST"))
+            .header("Allow", containsString("HEAD"))
+            .header("Allow", not(containsString("PATCH")))
+            .header("Allow", not(containsString("PUT")))
+            .header("Allow", not(containsString("DELETE")));
+    }
+
+    @Test
+    public void optionsRestRestricted() {
+        given()
+            .when()
+            .options("/restRestricted")
+            .then()
+            .statusCode(200)
+            .header("Allow", containsString("OPTIONS"))
+            .header("Allow", containsString("GET"))
+            .header("Allow", not(containsString("POST")))
+            .header("Allow", containsString("HEAD"))
+            .header("Allow", not(containsString("PATCH")))
+            .header("Allow", containsString("PUT"))
+            .header("Allow", not(containsString("DELETE")));
+    }
+
+    @Test
+    public void optionsRestDefault() {
+        given()
+            .when()
+            .options("/restDefault")
+            .then()
+            .statusCode(200)
+            .header("Allow", containsString("OPTIONS"))
+            .header("Allow", containsString("GET"))
+            .header("Allow", containsString("POST"))
+            .header("Allow", containsString("HEAD"))
+            .header("Allow", containsString("PATCH"))
+            .header("Allow", containsString("PUT"))
+            .header("Allow", containsString("DELETE"));
+    }
+
+}
diff --git 
a/components-starter/camel-platform-http-starter/src/test/resources/application.properties
 
b/components-starter/camel-platform-http-starter/src/test/resources/application.properties
index 2a1a48d10d4..f3183eb5542 100644
--- 
a/components-starter/camel-platform-http-starter/src/test/resources/application.properties
+++ 
b/components-starter/camel-platform-http-starter/src/test/resources/application.properties
@@ -1,2 +1,20 @@
-#logging.level.org.springframework.web: DEBUG
-#logging.level.org.springframework.boot.web: DEBUG
\ No newline at end of file
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+#logging.level.org.springframework.web=TRACE
+#logging.level.org.springframework.boot.web=TRACE
+#spring.mvc.log-request-details=true

Reply via email to