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

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k-runtime.git


The following commit(s) were added to refs/heads/master by this push:
     new 5b18aac  Add support for cors in platfomr http vertx
5b18aac is described below

commit 5b18aacf70bd2fcc63cb8a0f57c112758bf88a33
Author: lburgazzoli <lburgazz...@gmail.com>
AuthorDate: Thu Apr 16 17:38:13 2020 +0200

    Add support for cors in platfomr http vertx
---
 .../apache/camel/k/http/PlatformHttpServer.java    |   4 +
 .../k/http/PlatformHttpServiceConfiguration.java   |  87 +++++++++++--
 .../http/PlatformHttpServiceContextCustomizer.java |   9 ++
 .../apache/camel/k/http/support/CorsHandler.java   | 136 +++++++++++++++++++++
 .../k/http/PlatformHttpServiceCustomizerTest.java  |  43 +++++++
 5 files changed, 270 insertions(+), 9 deletions(-)

diff --git 
a/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServer.java
 
b/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServer.java
index 62cd280..8076157 100644
--- 
a/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServer.java
+++ 
b/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServer.java
@@ -83,6 +83,10 @@ public final class PlatformHttpServer extends ServiceSupport 
{
         final Router router = Router.router(vertx);
         final Router subRouter = Router.router(vertx);
 
+        if (configuration.getCors().isEnabled()) {
+            subRouter.route().handler(new 
org.apache.camel.k.http.support.CorsHandler(configuration));
+        }
+
         router.mountSubRouter(configuration.getPath(), subRouter);
 
         context.getRegistry().bind(
diff --git 
a/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServiceConfiguration.java
 
b/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServiceConfiguration.java
index 2180e7d..9001d2d 100644
--- 
a/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServiceConfiguration.java
+++ 
b/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServiceConfiguration.java
@@ -17,6 +17,8 @@
 package org.apache.camel.k.http;
 
 import java.math.BigInteger;
+import java.time.Duration;
+import java.util.List;
 
 import org.apache.camel.support.jsse.SSLContextParameters;
 
@@ -30,10 +32,12 @@ public class PlatformHttpServiceConfiguration {
     private String path = DEFAULT_PATH;
     private BigInteger maxBodySize;
 
-    private BodyHandlerConfiguration bodyHandlerConfiguration = new 
BodyHandlerConfiguration();
     private SSLContextParameters sslContextParameters;
     private boolean useGlobalSslContextParameters;
 
+    private CorsConfiguration corsConfiguration = new CorsConfiguration();
+    private BodyHandlerConfiguration bodyHandlerConfiguration = new 
BodyHandlerConfiguration();
+
     public String getBindHost() {
         return bindHost;
     }
@@ -66,14 +70,6 @@ public class PlatformHttpServiceConfiguration {
         this.maxBodySize = maxBodySize;
     }
 
-    public BodyHandlerConfiguration getBodyHandler() {
-        return bodyHandlerConfiguration;
-    }
-
-    public void setBodyHandler(BodyHandlerConfiguration bodyHandler) {
-        this.bodyHandlerConfiguration = bodyHandler;
-    }
-
     public SSLContextParameters getSslContextParameters() {
         return sslContextParameters;
     }
@@ -90,6 +86,79 @@ public class PlatformHttpServiceConfiguration {
         this.useGlobalSslContextParameters = useGlobalSslContextParameters;
     }
 
+    public CorsConfiguration getCors() {
+        return corsConfiguration;
+    }
+
+    public void setCors(CorsConfiguration corsConfiguration) {
+        this.corsConfiguration = corsConfiguration;
+    }
+
+    public BodyHandlerConfiguration getBodyHandler() {
+        return bodyHandlerConfiguration;
+    }
+
+    public void setBodyHandler(BodyHandlerConfiguration bodyHandler) {
+        this.bodyHandlerConfiguration = bodyHandler;
+    }
+
+    public static class CorsConfiguration {
+        private boolean enabled;
+        private List<String> origins;
+        private List<String> methods;
+        private List<String> headers;
+        private List<String> exposedHeaders;
+        private Duration accessControlMaxAge;
+
+        public boolean isEnabled() {
+            return enabled;
+        }
+
+        public void setEnabled(boolean enabled) {
+            this.enabled = enabled;
+        }
+
+        public List<String> getOrigins() {
+            return origins;
+        }
+
+        public void setOrigins(List<String> origins) {
+            this.origins = origins;
+        }
+
+        public List<String> getMethods() {
+            return methods;
+        }
+
+        public void setMethods(List<String> methods) {
+            this.methods = methods;
+        }
+
+        public List<String> getHeaders() {
+            return headers;
+        }
+
+        public List<String> getExposedHeaders() {
+            return exposedHeaders;
+        }
+
+        public void setExposedHeaders(List<String> exposedHeaders) {
+            this.exposedHeaders = exposedHeaders;
+        }
+
+        public void setHeaders(List<String> headers) {
+            this.headers = headers;
+        }
+
+        public Duration getAccessControlMaxAge() {
+            return accessControlMaxAge;
+        }
+
+        public void setAccessControlMaxAge(Duration accessControlMaxAge) {
+            this.accessControlMaxAge = accessControlMaxAge;
+        }
+    }
+
     public static class BodyHandlerConfiguration {
         private boolean handleFileUploads = true;
         private String uploadsDirectory = "file-uploads";
diff --git 
a/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServiceContextCustomizer.java
 
b/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServiceContextCustomizer.java
index f46139e..634e06e 100644
--- 
a/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServiceContextCustomizer.java
+++ 
b/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/PlatformHttpServiceContextCustomizer.java
@@ -29,6 +29,9 @@ import 
org.apache.camel.k.http.engine.RuntimePlatformHttpEngine;
 public class PlatformHttpServiceContextCustomizer extends 
PlatformHttpServiceConfiguration implements ContextCustomizer {
     private PlatformHttpServiceEndpoint endpoint;
 
+    public PlatformHttpServiceContextCustomizer() {
+    }
+
     @Override
     public int getOrder() {
         return Ordered.HIGHEST;
@@ -54,6 +57,12 @@ public class PlatformHttpServiceContextCustomizer extends 
PlatformHttpServiceCon
                 // TODO: remove once migrating to camel 3.2
                 parameters.remove("matchOnUriPrefix");
 
+                // the PlatformHttpComponent set this value but it is not 
handled which cause the
+                // context to fail as the property cannot be bound to the 
enpoint.
+                //
+                // TODO: fix upstream
+                parameters.remove("optionsEnabled");
+
                 // let the original component to create the endpoint
                 return super.createEndpoint(uri, remaining, parameters);
             }
diff --git 
a/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/support/CorsHandler.java
 
b/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/support/CorsHandler.java
new file mode 100644
index 0000000..f206b3f
--- /dev/null
+++ 
b/camel-k-runtime-http/src/main/java/org/apache/camel/k/http/support/CorsHandler.java
@@ -0,0 +1,136 @@
+/*
+ * 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.k.http.support;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import io.vertx.core.Handler;
+import io.vertx.core.http.HttpHeaders;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.ext.web.RoutingContext;
+import org.apache.camel.k.http.PlatformHttpServiceConfiguration;
+import org.apache.camel.util.ObjectHelper;
+
+public class CorsHandler implements Handler<RoutingContext> {
+
+    private static final Pattern COMMA_SEPARATED_SPLIT_REGEX = 
Pattern.compile("\\s*,\\s*");
+
+    // This is set in the recorder at runtime.
+    // Must be static because the filter is created(deployed) at build time 
and runtime config is still not available
+    final PlatformHttpServiceConfiguration.CorsConfiguration corsConfig;
+
+    public CorsHandler(PlatformHttpServiceConfiguration configuration) {
+        this.corsConfig = ObjectHelper.notNull(configuration.getCors(), 
"config");
+    }
+
+    private void processRequestedHeaders(HttpServerResponse response, String 
allowHeadersValue) {
+        if (ObjectHelper.isEmpty(corsConfig.getHeaders())) {
+            response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, 
allowHeadersValue);
+        } else {
+            List<String> requestedHeaders = new ArrayList<>();
+            for (String requestedHeader : 
COMMA_SEPARATED_SPLIT_REGEX.split(allowHeadersValue)) {
+                requestedHeaders.add(requestedHeader.toLowerCase());
+            }
+
+            List<String> validRequestedHeaders = new ArrayList<>();
+            for (String configHeader : corsConfig.getHeaders()) {
+                if (requestedHeaders.contains(configHeader.toLowerCase())) {
+                    validRequestedHeaders.add(configHeader);
+                }
+            }
+
+            if (!validRequestedHeaders.isEmpty()) {
+                
response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, 
String.join(",", validRequestedHeaders));
+            }
+        }
+    }
+
+    private void processMethods(HttpServerResponse response, String 
allowMethodsValue) {
+        if (ObjectHelper.isEmpty(corsConfig.getMethods())) {
+            response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, 
allowMethodsValue);
+        } else {
+            List<String> requestedMethods = new ArrayList<>();
+            for (String requestedMethod : 
COMMA_SEPARATED_SPLIT_REGEX.split(allowMethodsValue)) {
+                requestedMethods.add(requestedMethod.toLowerCase());
+            }
+
+            List<String> validRequestedMethods = new ArrayList<>();
+            for (String configMethod : corsConfig.getMethods()) {
+                if (requestedMethods.contains(configMethod.toLowerCase())) {
+                    validRequestedMethods.add(configMethod);
+                }
+            }
+
+            if (!validRequestedMethods.isEmpty()) {
+                
response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, 
String.join(",", validRequestedMethods));
+            }
+        }
+    }
+
+    @Override
+    public void handle(RoutingContext event) {
+        final HttpServerRequest request = event.request();
+        final HttpServerResponse response = event.response();
+        final String origin = request.getHeader(HttpHeaders.ORIGIN);
+
+        if (origin == null) {
+            event.next();
+        } else {
+            final String requestedMethods = 
request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD);
+
+            if (requestedMethods != null) {
+                processMethods(response, requestedMethods);
+            }
+
+            final String requestedHeaders = 
request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
+
+            if (requestedHeaders != null) {
+                processRequestedHeaders(response, requestedHeaders);
+            }
+
+            boolean allowsOrigin = 
ObjectHelper.isEmpty(corsConfig.getOrigins()) || 
corsConfig.getOrigins().contains(origin);
+
+            if (allowsOrigin) {
+                
response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
+            }
+
+            
response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
+
+
+            if (ObjectHelper.isNotEmpty(corsConfig.getExposedHeaders())) {
+                response.headers().set(
+                    HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS,
+                    String.join(",", corsConfig.getExposedHeaders()));
+            }
+
+            if (request.method().equals(HttpMethod.OPTIONS)) {
+                if ((requestedHeaders != null || requestedMethods != null) && 
corsConfig.getAccessControlMaxAge() != null) {
+                    response.putHeader(
+                        HttpHeaders.ACCESS_CONTROL_MAX_AGE,
+                        
String.valueOf(corsConfig.getAccessControlMaxAge().getSeconds()));
+                }
+                response.end();
+            } else {
+                event.next();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git 
a/camel-k-runtime-http/src/test/java/org/apache/camel/k/http/PlatformHttpServiceCustomizerTest.java
 
b/camel-k-runtime-http/src/test/java/org/apache/camel/k/http/PlatformHttpServiceCustomizerTest.java
index 044292a..421d63e 100644
--- 
a/camel-k-runtime-http/src/test/java/org/apache/camel/k/http/PlatformHttpServiceCustomizerTest.java
+++ 
b/camel-k-runtime-http/src/test/java/org/apache/camel/k/http/PlatformHttpServiceCustomizerTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.k.http;
 
+import java.util.Arrays;
+
 import io.vertx.core.http.HttpMethod;
 import io.vertx.core.json.Json;
 import io.vertx.core.json.JsonObject;
@@ -274,4 +276,45 @@ public class PlatformHttpServiceCustomizerTest {
             context.stop();
         }
     }
+
+    @Test
+    public void testPlatformHttpComponentCORS() throws Exception {
+        CamelContext context = new DefaultCamelContext();
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                fromF("platform-http:/")
+                    .transform().constant("cors");
+            }
+        });
+
+        PlatformHttpServiceContextCustomizer httpService = new 
PlatformHttpServiceContextCustomizer();
+        httpService.setBindPort(AvailablePortFinder.getNextAvailable());
+        httpService.getCors().setEnabled(true);
+        httpService.getCors().setMethods(Arrays.asList("GET", "POST"));
+        httpService.apply(context);
+
+        try {
+            context.start();
+
+            String origin = "http://custom.origin.quarkus";;
+            String methods = "GET,POST";
+            String headers = "X-Custom";
+
+            given()
+                .port(httpService.getBindPort())
+                .header("Origin", origin)
+                .header("Access-Control-Request-Method", methods)
+                .header("Access-Control-Request-Headers", headers)
+            .when()
+                .get("/")
+            .then()
+                .statusCode(200)
+                .header("Access-Control-Allow-Origin", origin)
+                .header("Access-Control-Allow-Methods", methods)
+                .header("Access-Control-Allow-Headers", headers);
+        } finally {
+            context.stop();
+        }
+    }
 }

Reply via email to