Repository: camel
Updated Branches:
  refs/heads/master f4da8754d -> a012502d2


CAMEL-9704: rest-dsl CORS improved to be better support on the components. If 
cors is enabled then the request is not routed but response returned with the 
CORS headers. Added options to turn off OPTIONS handling in servlet/undertow 
components as we need rest-dsl to handle the CORS instead.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/a012502d
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/a012502d
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/a012502d

Branch: refs/heads/master
Commit: a012502d2676dfb743c7f5f72d64fe4aec8eb673
Parents: f4da875
Author: Claus Ibsen <davscl...@apache.org>
Authored: Mon Mar 14 15:03:50 2016 +0100
Committer: Claus Ibsen <davscl...@apache.org>
Committed: Mon Mar 14 15:20:19 2016 +0100

----------------------------------------------------------------------
 .../camel/model/rest/OptionsVerbDefinition.java |  1 +
 .../apache/camel/model/rest/RestDefinition.java |  2 +
 .../processor/binding/RestBindingProcessor.java |  8 ++
 .../org/apache/camel/coap/CoAPComponent.java    | 10 ++-
 .../apache/camel/http/common/CamelServlet.java  |  6 +-
 .../camel/http/common/HttpCommonEndpoint.java   | 35 ++++++---
 .../apache/camel/http/common/HttpConsumer.java  | 12 +++
 .../component/jetty/JettyHttpComponent.java     |  9 +++
 .../jetty/rest/RestJettyGetCorsTest.java        | 81 +++++++++++++++++++
 .../netty/http/NettyHttpComponent.java          |  9 +++
 .../netty4/http/NettyHttpComponent.java         |  6 ++
 .../http/rest/RestNettyHttpGetCorsTest.java     | 83 ++++++++++++++++++++
 .../component/restlet/RestletComponent.java     | 10 ++-
 .../component/restlet/RestRestletCorsTest.java  | 16 ++++
 .../restlet/RestRestletGetCorsTest.java         | 82 +++++++++++++++++++
 .../component/servlet/ServletComponent.java     | 35 ++++++---
 .../component/sparkrest/SparkComponent.java     |  8 +-
 .../component/sparkrest/SparkConsumer.java      | 14 ++++
 .../sparkrest/RestCamelSparkCorsTest.java       | 79 +++++++++++++++++++
 .../component/undertow/UndertowComponent.java   | 12 ++-
 .../component/undertow/UndertowEndpoint.java    | 14 ++++
 .../undertow/handlers/HttpCamelHandler.java     |  4 +-
 .../rest/RestUndertowHttpGetCorsTest.java       | 81 +++++++++++++++++++
 23 files changed, 584 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/camel-core/src/main/java/org/apache/camel/model/rest/OptionsVerbDefinition.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/model/rest/OptionsVerbDefinition.java
 
b/camel-core/src/main/java/org/apache/camel/model/rest/OptionsVerbDefinition.java
index 2c4afa3..5214202 100644
--- 
a/camel-core/src/main/java/org/apache/camel/model/rest/OptionsVerbDefinition.java
+++ 
b/camel-core/src/main/java/org/apache/camel/model/rest/OptionsVerbDefinition.java
@@ -28,6 +28,7 @@ import org.apache.camel.spi.Metadata;
 @Metadata(label = "rest")
 @XmlRootElement(name = "options")
 @XmlAccessorType(XmlAccessType.FIELD)
+@Deprecated
 public class OptionsVerbDefinition extends VerbDefinition {
 
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java 
b/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java
index f8e5891..ab8b746 100644
--- a/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java
+++ b/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java
@@ -234,10 +234,12 @@ public class RestDefinition extends 
OptionalIdentifiedDefinition<RestDefinition>
         return addVerb("head", uri);
     }
 
+    @Deprecated
     public RestDefinition options() {
         return addVerb("options", null);
     }
 
+    @Deprecated
     public RestDefinition options(String uri) {
         return addVerb("options", uri);
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/camel-core/src/main/java/org/apache/camel/processor/binding/RestBindingProcessor.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/processor/binding/RestBindingProcessor.java
 
b/camel-core/src/main/java/org/apache/camel/processor/binding/RestBindingProcessor.java
index f8d8720..d79b099 100644
--- 
a/camel-core/src/main/java/org/apache/camel/processor/binding/RestBindingProcessor.java
+++ 
b/camel-core/src/main/java/org/apache/camel/processor/binding/RestBindingProcessor.java
@@ -115,6 +115,14 @@ public class RestBindingProcessor extends ServiceSupport 
implements AsyncProcess
             exchange.addOnCompletion(new 
RestBindingCORSOnCompletion(corsHeaders));
         }
 
+        String method = exchange.getIn().getHeader(Exchange.HTTP_METHOD, 
String.class);
+        if ("OPTIONS".equalsIgnoreCase(method)) {
+            // for OPTIONS methods then we should not route at all as its part 
of CORS
+            exchange.setProperty(Exchange.ROUTE_STOP, true);
+            callback.done(true);
+            return true;
+        }
+
         boolean isXml = false;
         boolean isJson = false;
 

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPComponent.java 
b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPComponent.java
index 281d58b..b6b600e 100644
--- 
a/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPComponent.java
+++ 
b/components/camel-coap/src/main/java/org/apache/camel/coap/CoAPComponent.java
@@ -102,13 +102,15 @@ public class CoAPComponent extends UriEndpointComponent 
implements RestConsumerF
             }
         }
 
-
         Map<String, Object> map = new HashMap<String, Object>();
         // setup endpoint options
         if (config.getEndpointProperties() != null && 
!config.getEndpointProperties().isEmpty()) {
             map.putAll(config.getEndpointProperties());
         }
 
+        // allow HTTP Options as we want to handle CORS in rest-dsl
+        boolean cors = config.isEnableCORS();
+
         String query = URISupport.createQueryString(map);
 
         String url = (config.getScheme() == null ? "coap" : 
config.getScheme()) + "://" + host;
@@ -116,6 +118,10 @@ public class CoAPComponent extends UriEndpointComponent 
implements RestConsumerF
             url += ":" + config.getPort();
         }
         String restrict = verb.toUpperCase(Locale.US);
+        if (cors) {
+            restrict += ",OPTIONS";
+        }
+
         if (uriTemplate == null) {
             uriTemplate = "";
         }
@@ -140,7 +146,7 @@ public class CoAPComponent extends UriEndpointComponent 
implements RestConsumerF
         super.doStart();
 
         RestConfiguration config = 
getCamelContext().getRestConfiguration("coap", true);
-        // configure additional options on spark configuration
+        // configure additional options on coap configuration
         if (config.getComponentProperties() != null && 
!config.getComponentProperties().isEmpty()) {
             setProperties(this, config.getComponentProperties());
         }

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-http-common/src/main/java/org/apache/camel/http/common/CamelServlet.java
----------------------------------------------------------------------
diff --git 
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/CamelServlet.java
 
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/CamelServlet.java
index 061bf47..cd872c6 100644
--- 
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/CamelServlet.java
+++ 
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/CamelServlet.java
@@ -48,7 +48,7 @@ public class CamelServlet extends HttpServlet {
 
     private ServletResolveConsumerStrategy servletResolveConsumerStrategy = 
new HttpServletResolveConsumerStrategy();
     private final ConcurrentMap<String, HttpConsumer> consumers = new 
ConcurrentHashMap<String, HttpConsumer>();
-   
+
     @Override
     public void init(ServletConfig config) throws ServletException {
         super.init(config);
@@ -75,7 +75,7 @@ public class CamelServlet extends HttpServlet {
         }
 
         // if its an OPTIONS request then return which method is allowed
-        if ("OPTIONS".equals(request.getMethod())) {
+        if ("OPTIONS".equals(request.getMethod()) && 
!consumer.isOptionsEnabled()) {
             String s;
             if (consumer.getEndpoint().getHttpMethodRestrict() != null) {
                 s = "OPTIONS," + 
consumer.getEndpoint().getHttpMethodRestrict();
@@ -208,8 +208,6 @@ public class CamelServlet extends HttpServlet {
 
     /**
      * Override the Thread Context ClassLoader if need be.
-     *
-     * @param exchange
      * @return old classloader if overridden; otherwise returns null
      */
     protected ClassLoader overrideTccl(final Exchange exchange) {

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpCommonEndpoint.java
----------------------------------------------------------------------
diff --git 
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpCommonEndpoint.java
 
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpCommonEndpoint.java
index 2ab2d07..d6cea0a 100644
--- 
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpCommonEndpoint.java
+++ 
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpCommonEndpoint.java
@@ -49,17 +49,17 @@ public abstract class HttpCommonEndpoint extends 
DefaultEndpoint implements Head
     @UriParam(label = "consumer",
             description = "Whether or not the consumer should try to find a 
target consumer by matching the URI prefix if no exact match is found.")
     boolean matchOnUriPrefix;
-    @UriParam(defaultValue = "true", description = "If this option is false 
Jetty servlet will disable the HTTP streaming and set the content-length header 
on the response")
+    @UriParam(defaultValue = "true", description = "If this option is false 
the Servlet will disable the HTTP streaming and set the content-length header 
on the response")
     boolean chunked = true;
     @UriParam(label = "common",
-            description = "Determines whether or not the raw input stream from 
Jetty is cached or not"
+            description = "Determines whether or not the raw input stream from 
Servlet is cached or not"
                     + " (Camel will read the stream into a in memory/overflow 
to file, Stream caching) cache."
-                    + " By default Camel will cache the Jetty input stream to 
support reading it multiple times to ensure it Camel"
+                    + " By default Camel will cache the Servlet input stream 
to support reading it multiple times to ensure it Camel"
                     + " can retrieve all data from the stream. However you can 
set this option to true when you for example need"
                     + " to access the raw stream, such as streaming it 
directly to a file or other persistent store."
                     + " DefaultHttpBinding will copy the request input stream 
into a stream cache and put it into message body"
                     + " if this option is false to support reading the stream 
multiple times."
-                    + " If you use Jetty to bridge/proxy an endpoint then 
consider enabling this option to improve performance,"
+                    + " If you use Servlet to bridge/proxy an endpoint then 
consider enabling this option to improve performance,"
                     + " in case you do not need to read the message payload 
multiple times."
                     + " The http/http4 producer will by default cache the 
response body stream. If setting this option to true,"
                     + " then the producers will not cache the response body 
stream but use the response stream as-is as the message body.")
@@ -78,9 +78,12 @@ public abstract class HttpCommonEndpoint extends 
DefaultEndpoint implements Head
             + " data from the request to Java and that can be a potential 
security risk.")
     boolean transferException;
     @UriParam(label = "consumer",
-            description = "Specifies whether to enable HTTP TRACE for this 
Jetty consumer. By default TRACE is turned off.")
+            description = "Specifies whether to enable HTTP TRACE for this 
Servlet consumer. By default TRACE is turned off.")
     boolean traceEnabled;
     @UriParam(label = "consumer",
+            description = "Specifies whether to enable HTTP OPTIONS for this 
Servlet consumer. By default OPTIONS is turned off.")
+    boolean optionsEnabled;
+    @UriParam(label = "consumer",
             description = "Used to only allow consuming if the HttpMethod 
matches, such as GET/POST/PUT etc. Multiple methods can be specified separated 
by comma.")
     String httpMethodRestrict;
     @UriParam(label = "consumer",
@@ -261,14 +264,14 @@ public abstract class HttpCommonEndpoint extends 
DefaultEndpoint implements Head
     }
 
     /**
-     * Determines whether or not the raw input stream from Jetty is cached or 
not
+     * Determines whether or not the raw input stream from Servlet is cached 
or not
      * (Camel will read the stream into a in memory/overflow to file, Stream 
caching) cache.
-     * By default Camel will cache the Jetty input stream to support reading 
it multiple times to ensure it Camel
+     * By default Camel will cache the Servlet input stream to support reading 
it multiple times to ensure it Camel
      * can retrieve all data from the stream. However you can set this option 
to true when you for example need
      * to access the raw stream, such as streaming it directly to a file or 
other persistent store.
      * DefaultHttpBinding will copy the request input stream into a stream 
cache and put it into message body
      * if this option is false to support reading the stream multiple times.
-     * If you use Jetty to bridge/proxy an endpoint then consider enabling 
this option to improve performance,
+     * If you use Servlet to bridge/proxy an endpoint then consider enabling 
this option to improve performance,
      * in case you do not need to read the message payload multiple times.
      + The http/http4 producer will by default cache the response body stream. 
If setting this option to true,
      + then the producers will not cache the response body stream but use the 
response stream as-is as the message body.
@@ -282,7 +285,7 @@ public abstract class HttpCommonEndpoint extends 
DefaultEndpoint implements Head
     }
 
     /**
-     * If this option is false Jetty servlet will disable the HTTP streaming 
and set the content-length header on the response
+     * If this option is false Servlet will disable the HTTP streaming and set 
the content-length header on the response
      */
     public void setChunked(boolean chunked) {
         this.chunked = chunked;
@@ -343,12 +346,23 @@ public abstract class HttpCommonEndpoint extends 
DefaultEndpoint implements Head
     }
 
     /**
-     * Specifies whether to enable HTTP TRACE for this Jetty consumer. By 
default TRACE is turned off.
+     * Specifies whether to enable HTTP TRACE for this Servlet consumer. By 
default TRACE is turned off.
      */
     public void setTraceEnabled(boolean traceEnabled) {
         this.traceEnabled = traceEnabled;
     }
 
+    public boolean isOptionsEnabled() {
+        return optionsEnabled;
+    }
+
+    /**
+     * Specifies whether to enable HTTP OPTIONS for this Servlet consumer. By 
default OPTIONS is turned off.
+     */
+    public void setOptionsEnabled(boolean optionsEnabled) {
+        this.optionsEnabled = optionsEnabled;
+    }
+
     public String getHttpMethodRestrict() {
         return httpMethodRestrict;
     }
@@ -432,5 +446,4 @@ public abstract class HttpCommonEndpoint extends 
DefaultEndpoint implements Head
         this.okStatusCodeRange = okStatusCodeRange;
     }
 
-
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpConsumer.java
----------------------------------------------------------------------
diff --git 
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpConsumer.java
 
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpConsumer.java
index 87dfd7c..60907e1 100644
--- 
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpConsumer.java
+++ 
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpConsumer.java
@@ -23,12 +23,16 @@ import org.apache.camel.impl.DefaultConsumer;
 public class HttpConsumer extends DefaultConsumer implements Suspendable {
     private volatile boolean suspended;
     private boolean traceEnabled;
+    private boolean optionsEnabled;
 
     public HttpConsumer(HttpCommonEndpoint endpoint, Processor processor) {
         super(endpoint, processor);
         if (endpoint.isTraceEnabled()) {
             setTraceEnabled(true);
         }
+        if (endpoint.isOptionsEnabled()) {
+            setOptionsEnabled(true);
+        }
     }
 
     @Override
@@ -81,4 +85,12 @@ public class HttpConsumer extends DefaultConsumer implements 
Suspendable {
     public void setTraceEnabled(boolean traceEnabled) {
         this.traceEnabled = traceEnabled;
     }
+
+    public boolean isOptionsEnabled() {
+        return optionsEnabled;
+    }
+
+    public void setOptionsEnabled(boolean optionsEnabled) {
+        this.optionsEnabled = optionsEnabled;
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpComponent.java
 
b/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpComponent.java
index 79110fc..dc620d3 100644
--- 
a/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpComponent.java
+++ 
b/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpComponent.java
@@ -1058,6 +1058,12 @@ public abstract class JettyHttpComponent extends 
HttpCommonComponent implements
             }
         }
 
+        boolean cors = config.isEnableCORS();
+        if (cors) {
+            // allow HTTP Options as we want to handle CORS in rest-dsl
+            map.put("optionsEnabled", "true");
+        }
+
         String query = URISupport.createQueryString(map);
 
         String url;
@@ -1069,6 +1075,9 @@ public abstract class JettyHttpComponent extends 
HttpCommonComponent implements
 
         // must use upper case for restrict
         String restrict = verb.toUpperCase(Locale.US);
+        if (cors) {
+            restrict += ",OPTIONS";
+        }
         // get the endpoint
         url = String.format(url, scheme, host, port, path, restrict);
 

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyGetCorsTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyGetCorsTest.java
 
b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyGetCorsTest.java
new file mode 100644
index 0000000..5379763
--- /dev/null
+++ 
b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/rest/RestJettyGetCorsTest.java
@@ -0,0 +1,81 @@
+/**
+ * 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.jetty.rest;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.jetty.BaseJettyTest;
+import org.apache.camel.spi.RestConfiguration;
+import org.junit.Test;
+
+public class RestJettyGetCorsTest extends BaseJettyTest {
+    
+    @Test
+    public void testCors() throws Exception {
+        // send OPTIONS first which should not be routed
+        getMockEndpoint("mock:input").expectedMessageCount(0);
+
+        Exchange out = template.request("http://localhost:"; + getPort() + 
"/users/123/basic", new Processor() {
+            @Override
+            public void process(Exchange exchange) throws Exception {
+                exchange.getIn().setHeader(Exchange.HTTP_METHOD, "OPTIONS");
+            }
+        });
+
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_ORIGIN, 
out.getOut().getHeader("Access-Control-Allow-Origin"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_METHODS, 
out.getOut().getHeader("Access-Control-Allow-Methods"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_HEADERS, 
out.getOut().getHeader("Access-Control-Allow-Headers"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_MAX_AGE, 
out.getOut().getHeader("Access-Control-Max-Age"));
+
+        assertMockEndpointsSatisfied();
+
+        resetMocks();
+        getMockEndpoint("mock:input").expectedMessageCount(1);
+
+        // send GET request which should be routed
+
+        String out2 = template.requestBody("http://localhost:"; + getPort() + 
"/users/123/basic", null, String.class);
+        assertEquals("123;Donald Duck", out2);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // configure to use jetty on localhost with the given port
+                
restConfiguration().component("jetty").host("localhost").port(getPort()).enableCORS(true);
+
+                // use the rest DSL to define the rest services
+                rest("/users/")
+                        .get("{id}/basic")
+                        .route()
+                        .to("mock:input")
+                        .process(new Processor() {
+                            public void process(Exchange exchange) throws 
Exception {
+                                String id = exchange.getIn().getHeader("id", 
String.class);
+                                exchange.getOut().setBody(id + ";Donald Duck");
+                            }
+                        });
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java
 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java
index 6cf8474..029d290 100644
--- 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java
+++ 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java
@@ -335,6 +335,12 @@ public class NettyHttpComponent extends NettyComponent 
implements HeaderFilterSt
             }
         }
 
+        boolean cors = config.isEnableCORS();
+        if (cors) {
+            // allow HTTP Options as we want to handle CORS in rest-dsl
+            map.put("optionsEnabled", "true");
+        }
+
         String query = URISupport.createQueryString(map);
 
         String url;
@@ -346,6 +352,9 @@ public class NettyHttpComponent extends NettyComponent 
implements HeaderFilterSt
         
         // must use upper case for restrict
         String restrict = verb.toUpperCase(Locale.US);
+        if (cors) {
+            restrict += ",OPTIONS";
+        }
         // get the endpoint
         url = String.format(url, scheme, host, port, path, restrict);
         

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpComponent.java
 
b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpComponent.java
index 081e2d0..04ef4f3 100644
--- 
a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpComponent.java
+++ 
b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpComponent.java
@@ -342,6 +342,9 @@ public class NettyHttpComponent extends NettyComponent 
implements HeaderFilterSt
             }
         }
 
+        // allow HTTP Options as we want to handle CORS in rest-dsl
+        boolean cors = config.isEnableCORS();
+
         String query = URISupport.createQueryString(map);
 
         String url;
@@ -352,6 +355,9 @@ public class NettyHttpComponent extends NettyComponent 
implements HeaderFilterSt
         }
         // must use upper case for restrict
         String restrict = verb.toUpperCase(Locale.US);
+        if (cors) {
+            restrict += ",OPTIONS";
+        }
         // get the endpoint
         url = String.format(url, scheme, host, port, path, restrict);
         

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpGetCorsTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpGetCorsTest.java
 
b/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpGetCorsTest.java
new file mode 100644
index 0000000..49c49fa
--- /dev/null
+++ 
b/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/rest/RestNettyHttpGetCorsTest.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.netty4.http.rest;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.netty4.http.BaseNettyTest;
+import org.apache.camel.component.netty4.http.RestNettyHttpBinding;
+import org.apache.camel.impl.JndiRegistry;
+import org.apache.camel.spi.RestConfiguration;
+import org.junit.Test;
+
+public class RestNettyHttpGetCorsTest extends BaseNettyTest {
+
+    @Test
+    public void testCors() throws Exception {
+        // send OPTIONS first which should not be routed
+        getMockEndpoint("mock:input").expectedMessageCount(0);
+
+        Exchange out = template.request("http://localhost:"; + getPort() + 
"/users/123/basic", new Processor() {
+            @Override
+            public void process(Exchange exchange) throws Exception {
+                exchange.getIn().setHeader(Exchange.HTTP_METHOD, "OPTIONS");
+            }
+        });
+
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_ORIGIN, 
out.getOut().getHeader("Access-Control-Allow-Origin"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_METHODS, 
out.getOut().getHeader("Access-Control-Allow-Methods"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_HEADERS, 
out.getOut().getHeader("Access-Control-Allow-Headers"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_MAX_AGE, 
out.getOut().getHeader("Access-Control-Max-Age"));
+
+        assertMockEndpointsSatisfied();
+
+        resetMocks();
+        getMockEndpoint("mock:input").expectedMessageCount(1);
+
+        // send GET request which should be routed
+
+        String out2 = template.requestBody("http://localhost:"; + getPort() + 
"/users/123/basic", null, String.class);
+        assertEquals("123;Donald Duck", out2);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // configure to use netty4-http on localhost with the given 
port
+                
restConfiguration().component("netty4-http").host("localhost").port(getPort()).enableCORS(true);
+
+                // use the rest DSL to define the rest services
+                rest("/users/")
+                    .get("{id}/basic")
+                        .route()
+                        .to("mock:input")
+                        .process(new Processor() {
+                            public void process(Exchange exchange) throws 
Exception {
+                                String id = exchange.getIn().getHeader("id", 
String.class);
+                                exchange.getOut().setBody(id + ";Donald Duck");
+                            }
+                        });
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletComponent.java
 
b/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletComponent.java
index 8978cee..573ef9e 100644
--- 
a/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletComponent.java
+++ 
b/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletComponent.java
@@ -729,18 +729,24 @@ public class RestletComponent extends 
HeaderFilterStrategyComponent implements R
             }
         }
 
+        // allow HTTP Options as we want to handle CORS in rest-dsl
+        boolean cors = config.isEnableCORS();
+
         String query = URISupport.createQueryString(map);
 
         String url;
         // must use upper case for restrict
         String restrict = verb.toUpperCase(Locale.US);
+        if (cors) {
+            restrict += ",OPTIONS";
+        }
 
         if (port > 0) {
-            url = "restlet:%s://%s:%s/%s?restletMethod=%s";
+            url = "restlet:%s://%s:%s/%s?restletMethods=%s";
             url = String.format(url, scheme, host, port, path, restrict);
         } else {
             // It could use the restlet servlet transport
-            url = "restlet:/%s?restletMethod=%s";
+            url = "restlet:/%s?restletMethods=%s";
             url = String.format(url, path, restrict);
         }
         if (!query.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletCorsTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletCorsTest.java
 
b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletCorsTest.java
index fdd3c9e..0a66c29 100644
--- 
a/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletCorsTest.java
+++ 
b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletCorsTest.java
@@ -32,6 +32,22 @@ public class RestRestletCorsTest extends RestletTestSupport {
     Splitter headerSplitter = Splitter.on(",").trimResults();
 
     @Test
+    public void testCors() throws Exception {
+        Exchange out = template.request("http://localhost:"; + portNum + 
"/users/123/basic", new Processor() {
+            @Override
+            public void process(Exchange exchange) throws Exception {
+                exchange.getIn().setHeader(Exchange.HTTP_METHOD, "OPTIONS");
+            }
+        });
+
+        assertEquals("https://localhost:443";, 
out.getOut().getHeader("Access-Control-Allow-Origin"));
+        assertEquals("GET, POST, PUT, DELETE, OPTIONS", 
out.getOut().getHeader("Access-Control-Allow-Methods"));
+        assertEquals("Origin, Accept, Content-Type, 
Access-Control-Request-Method, Access-Control-Request-Headers",
+                out.getOut().getHeader("Access-Control-Allow-Headers"));
+        assertEquals("1234", out.getOut().getHeader("Access-Control-Max-Age"));
+    }
+
+    @Test
     public void testRestletProducerGet() throws Exception {
         Exchange exchange = template.request("http://localhost:"; + portNum + 
"/users/123/basic", null);
 

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletGetCorsTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletGetCorsTest.java
 
b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletGetCorsTest.java
new file mode 100644
index 0000000..fc8c115
--- /dev/null
+++ 
b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestRestletGetCorsTest.java
@@ -0,0 +1,82 @@
+/**
+ * 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.restlet;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.RestConfiguration;
+import org.junit.Test;
+
+/**
+ * @version 
+ */
+public class RestRestletGetCorsTest extends RestletTestSupport {
+
+    @Test
+    public void testCors() throws Exception {
+        // send OPTIONS first which should not be routed
+        getMockEndpoint("mock:input").expectedMessageCount(0);
+
+        Exchange out = template.request("http://localhost:"; + portNum + 
"/users/123/basic", new Processor() {
+            @Override
+            public void process(Exchange exchange) throws Exception {
+                exchange.getIn().setHeader(Exchange.HTTP_METHOD, "OPTIONS");
+            }
+        });
+
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_ORIGIN, 
out.getOut().getHeader("Access-Control-Allow-Origin"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_METHODS, 
out.getOut().getHeader("Access-Control-Allow-Methods"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_HEADERS, 
out.getOut().getHeader("Access-Control-Allow-Headers"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_MAX_AGE, 
out.getOut().getHeader("Access-Control-Max-Age"));
+
+        assertMockEndpointsSatisfied();
+
+        resetMocks();
+        getMockEndpoint("mock:input").expectedMessageCount(1);
+
+        // send GET request which should be routed
+
+        String out2 = template.requestBody("http://localhost:"; + portNum + 
"/users/123/basic", null, String.class);
+        assertEquals("123;Donald Duck", out2);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // configure to use restlet on localhost with the given port
+                
restConfiguration().component("restlet").host("localhost").port(portNum).enableCORS(true);
+
+                // use the rest DSL to define the rest services
+                rest("/users/")
+                    .get("{id}/basic")
+                        .route()
+                        .to("mock:input")
+                        .process(new Processor() {
+                            public void process(Exchange exchange) throws 
Exception {
+                                String id = exchange.getIn().getHeader("id", 
String.class);
+                                exchange.getOut().setBody(id + ";Donald Duck");
+                            }
+                        });
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/ServletComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/ServletComponent.java
 
b/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/ServletComponent.java
index 51cfed1..34f5039 100644
--- 
a/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/ServletComponent.java
+++ 
b/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/ServletComponent.java
@@ -208,9 +208,18 @@ public class ServletComponent extends HttpCommonComponent 
implements RestConsume
         }
 
         Map<String, Object> map = new HashMap<String, Object>();
-        // setup endpoint options
-        if (config.getEndpointProperties() != null && 
!config.getEndpointProperties().isEmpty()) {
-            map.putAll(config.getEndpointProperties());
+        // build query string, and append any endpoint configuration properties
+        if (config.getComponent() == null || 
config.getComponent().equals("servlet")) {
+            // setup endpoint options
+            if (config.getEndpointProperties() != null && 
!config.getEndpointProperties().isEmpty()) {
+                map.putAll(config.getEndpointProperties());
+            }
+        }
+
+        boolean cors = config.isEnableCORS();
+        if (cors) {
+            // allow HTTP Options as we want to handle CORS in rest-dsl
+            map.put("optionsEnabled", "true");
         }
 
         // do not append with context-path as the servlet path should be 
without context-path
@@ -223,24 +232,30 @@ public class ServletComponent extends HttpCommonComponent 
implements RestConsume
         } else {
             url = "servlet:///%s?httpMethodRestrict=%s";
         }
+
         // must use upper case for restrict
         String restrict = verb.toUpperCase(Locale.US);
-
+        if (cors) {
+            restrict += ",OPTIONS";
+        }
         // get the endpoint
         url = String.format(url, path, restrict);
         
         if (!query.isEmpty()) {
             url = url + "&" + query;
         }       
+
         ServletEndpoint endpoint = camelContext.getEndpoint(url, 
ServletEndpoint.class);
         setProperties(endpoint, parameters);
 
-        // use the rest binding
-        HttpBinding binding = new ServletRestHttpBinding();
-        binding.setHeaderFilterStrategy(endpoint.getHeaderFilterStrategy());
-        binding.setTransferException(endpoint.isTransferException());
-        
binding.setEagerCheckContentAvailable(endpoint.isEagerCheckContentAvailable());
-        endpoint.setBinding(binding);
+        if (!map.containsKey("httpBindingRef")) {
+            // use the rest binding, if not using a custom http binding
+            HttpBinding binding = new ServletRestHttpBinding();
+            
binding.setHeaderFilterStrategy(endpoint.getHeaderFilterStrategy());
+            binding.setTransferException(endpoint.isTransferException());
+            
binding.setEagerCheckContentAvailable(endpoint.isEagerCheckContentAvailable());
+            endpoint.setHttpBinding(binding);
+        }
 
         // configure consumer properties
         Consumer consumer = endpoint.createConsumer(processor);

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-spark-rest/src/main/java/org/apache/camel/component/sparkrest/SparkComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-spark-rest/src/main/java/org/apache/camel/component/sparkrest/SparkComponent.java
 
b/components/camel-spark-rest/src/main/java/org/apache/camel/component/sparkrest/SparkComponent.java
index 1fdc957..186bd03 100644
--- 
a/components/camel-spark-rest/src/main/java/org/apache/camel/component/sparkrest/SparkComponent.java
+++ 
b/components/camel-spark-rest/src/main/java/org/apache/camel/component/sparkrest/SparkComponent.java
@@ -333,10 +333,16 @@ public class SparkComponent extends UriEndpointComponent 
implements RestConsumer
 
         // configure consumer properties
         Consumer consumer = endpoint.createConsumer(processor);
+        if (config.isEnableCORS()) {
+            // if CORS is enabled then configure that on the spark consumer
+            if (config.getConsumerProperties() == null) {
+                config.setConsumerProperties(new HashMap<String, Object>());
+            }
+            config.getConsumerProperties().put("enableCors", true);
+        }
         if (config.getConsumerProperties() != null && 
!config.getConsumerProperties().isEmpty()) {
             setProperties(consumer, config.getConsumerProperties());
         }
-
         return consumer;
     }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-spark-rest/src/main/java/org/apache/camel/component/sparkrest/SparkConsumer.java
----------------------------------------------------------------------
diff --git 
a/components/camel-spark-rest/src/main/java/org/apache/camel/component/sparkrest/SparkConsumer.java
 
b/components/camel-spark-rest/src/main/java/org/apache/camel/component/sparkrest/SparkConsumer.java
index 4b41523..1505c7e 100644
--- 
a/components/camel-spark-rest/src/main/java/org/apache/camel/component/sparkrest/SparkConsumer.java
+++ 
b/components/camel-spark-rest/src/main/java/org/apache/camel/component/sparkrest/SparkConsumer.java
@@ -23,6 +23,7 @@ import org.apache.camel.impl.DefaultConsumer;
 public class SparkConsumer extends DefaultConsumer {
 
     private final CamelSparkRoute route;
+    private boolean enableCors;
 
     public SparkConsumer(Endpoint endpoint, Processor processor, 
CamelSparkRoute route) {
         super(endpoint, processor);
@@ -34,6 +35,14 @@ public class SparkConsumer extends DefaultConsumer {
         return (SparkEndpoint) super.getEndpoint();
     }
 
+    public boolean isEnableCors() {
+        return enableCors;
+    }
+
+    public void setEnableCors(boolean enableCors) {
+        this.enableCors = enableCors;
+    }
+
     @Override
     protected void doStart() throws Exception {
         super.doStart();
@@ -50,6 +59,11 @@ public class SparkConsumer extends DefaultConsumer {
         }
         CamelSpark.spark(verb, path, accept, route);
 
+        // special if cors is enabled in rest-dsl then we need a spark-route 
to trigger cors support
+        if (enableCors && !"options".equals(verb)) {
+            CamelSpark.spark("options", path, accept, route);
+        }
+
         if (matchOnUriPrefix) {
             CamelSpark.spark(verb, path + "/*", accept, route);
         }

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-spark-rest/src/test/java/org/apache/camel/component/sparkrest/RestCamelSparkCorsTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-spark-rest/src/test/java/org/apache/camel/component/sparkrest/RestCamelSparkCorsTest.java
 
b/components/camel-spark-rest/src/test/java/org/apache/camel/component/sparkrest/RestCamelSparkCorsTest.java
new file mode 100644
index 0000000..9bd88c6
--- /dev/null
+++ 
b/components/camel-spark-rest/src/test/java/org/apache/camel/component/sparkrest/RestCamelSparkCorsTest.java
@@ -0,0 +1,79 @@
+/**
+ * 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.sparkrest;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.RestConfiguration;
+import org.junit.Test;
+
+public class RestCamelSparkCorsTest extends BaseSparkTest {
+
+    @Test
+    public void testCors() throws Exception {
+        // send OPTIONS first which should not be routed
+        getMockEndpoint("mock:input").expectedMessageCount(0);
+
+        Exchange out = template.request("http://localhost:"; + getPort() + 
"/users/123/basic", new Processor() {
+            @Override
+            public void process(Exchange exchange) throws Exception {
+                exchange.getIn().setHeader(Exchange.HTTP_METHOD, "OPTIONS");
+            }
+        });
+
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_ORIGIN, 
out.getOut().getHeader("Access-Control-Allow-Origin"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_METHODS, 
out.getOut().getHeader("Access-Control-Allow-Methods"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_HEADERS, 
out.getOut().getHeader("Access-Control-Allow-Headers"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_MAX_AGE, 
out.getOut().getHeader("Access-Control-Max-Age"));
+
+        assertMockEndpointsSatisfied();
+
+        resetMocks();
+        getMockEndpoint("mock:input").expectedMessageCount(1);
+
+        // send GET request which should be routed
+
+        String out2 = template.requestBody("http://localhost:"; + getPort() + 
"/users/123/basic", null, String.class);
+        assertEquals("123;Donald Duck", out2);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // configure to use jetty on localhost with the given port
+                
restConfiguration().component("spark-rest").host("localhost").port(getPort()).enableCORS(true);
+
+                // use the rest DSL to define the rest services
+                rest("/users/")
+                        .get("{id}/basic")
+                        .route()
+                        .to("mock:input")
+                        .process(new Processor() {
+                            public void process(Exchange exchange) throws 
Exception {
+                                String id = exchange.getIn().getHeader("id", 
String.class);
+                                exchange.getOut().setBody(id + ";Donald Duck");
+                            }
+                        });
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
 
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
index 1d93fd7..4641651 100644
--- 
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
+++ 
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java
@@ -178,6 +178,12 @@ public class UndertowComponent extends 
UriEndpointComponent implements RestConsu
             }
         }
 
+        boolean cors = config.isEnableCORS();
+        if (cors) {
+            // allow HTTP Options as we want to handle CORS in rest-dsl
+            map.put("optionsEnabled", "true");
+        }
+
         String query = URISupport.createQueryString(map);
 
         String url;
@@ -187,8 +193,12 @@ public class UndertowComponent extends 
UriEndpointComponent implements RestConsu
             url = "undertow:%s://%s:%s/%s?httpMethodRestrict=%s";
         }
 
+        // must use upper case for restrict
         String restrict = verb.toUpperCase(Locale.US);
-
+        if (cors) {
+            restrict += ",OPTIONS";
+        }
+        // get the endpoint
         url = String.format(url, scheme, host, port, path, restrict);
 
         if (!query.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowEndpoint.java
----------------------------------------------------------------------
diff --git 
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowEndpoint.java
 
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowEndpoint.java
index 2466c15..d8b5a01 100644
--- 
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowEndpoint.java
+++ 
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowEndpoint.java
@@ -79,6 +79,9 @@ public class UndertowEndpoint extends DefaultEndpoint 
implements HeaderFilterStr
     private Boolean reuseAddresses = Boolean.TRUE;
     @UriParam(label = "producer", prefix = "option.", multiValue = true)
     private Map<String, Object> options;
+    @UriParam(label = "consumer",
+            description = "Specifies whether to enable HTTP OPTIONS for this 
Servlet consumer. By default OPTIONS is turned off.")
+    boolean optionsEnabled;
 
     public UndertowEndpoint(String uri, UndertowComponent component) throws 
URISyntaxException {
         super(uri, component);
@@ -269,6 +272,17 @@ public class UndertowEndpoint extends DefaultEndpoint 
implements HeaderFilterStr
         this.options = options;
     }
 
+    public boolean isOptionsEnabled() {
+        return optionsEnabled;
+    }
+
+    /**
+     * Specifies whether to enable HTTP OPTIONS for this Servlet consumer. By 
default OPTIONS is turned off.
+     */
+    public void setOptionsEnabled(boolean optionsEnabled) {
+        this.optionsEnabled = optionsEnabled;
+    }
+
     @Override
     protected void doStart() throws Exception {
         super.doStart();

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/HttpCamelHandler.java
----------------------------------------------------------------------
diff --git 
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/HttpCamelHandler.java
 
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/HttpCamelHandler.java
index 5061312..ca0cc07 100644
--- 
a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/HttpCamelHandler.java
+++ 
b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/HttpCamelHandler.java
@@ -58,10 +58,10 @@ public class HttpCamelHandler implements HttpHandler {
 
         HttpString requestMethod = httpExchange.getRequestMethod();
 
-        if (Methods.OPTIONS.equals(requestMethod)) {
+        if (Methods.OPTIONS.equals(requestMethod) && 
!consumer.getEndpoint().isOptionsEnabled()) {
             String allowedMethods;
             if (consumer.getEndpoint().getHttpMethodRestrict() != null) {
-                allowedMethods = "OPTIONS" + 
consumer.getEndpoint().getHttpMethodRestrict();
+                allowedMethods = "OPTIONS," + 
consumer.getEndpoint().getHttpMethodRestrict();
             } else {
                 allowedMethods = 
"GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH";
             }

http://git-wip-us.apache.org/repos/asf/camel/blob/a012502d/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/rest/RestUndertowHttpGetCorsTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/rest/RestUndertowHttpGetCorsTest.java
 
b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/rest/RestUndertowHttpGetCorsTest.java
new file mode 100644
index 0000000..c54fbef
--- /dev/null
+++ 
b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/rest/RestUndertowHttpGetCorsTest.java
@@ -0,0 +1,81 @@
+/**
+ * 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.undertow.rest;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.undertow.BaseUndertowTest;
+import org.apache.camel.spi.RestConfiguration;
+import org.junit.Test;
+
+public class RestUndertowHttpGetCorsTest extends BaseUndertowTest {
+
+    @Test
+    public void testCors() throws Exception {
+        // send OPTIONS first which should not be routed
+        getMockEndpoint("mock:input").expectedMessageCount(0);
+
+        Exchange out = template.request("http://localhost:"; + getPort() + 
"/users/123/basic", new Processor() {
+            @Override
+            public void process(Exchange exchange) throws Exception {
+                exchange.getIn().setHeader(Exchange.HTTP_METHOD, "OPTIONS");
+            }
+        });
+
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_ORIGIN, 
out.getOut().getHeader("Access-Control-Allow-Origin"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_METHODS, 
out.getOut().getHeader("Access-Control-Allow-Methods"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_ALLOW_HEADERS, 
out.getOut().getHeader("Access-Control-Allow-Headers"));
+        assertEquals(RestConfiguration.CORS_ACCESS_CONTROL_MAX_AGE, 
out.getOut().getHeader("Access-Control-Max-Age"));
+
+        assertMockEndpointsSatisfied();
+
+        resetMocks();
+        getMockEndpoint("mock:input").expectedMessageCount(1);
+
+        // send GET request which should be routed
+
+        String out2 = template.requestBody("http://localhost:"; + getPort() + 
"/users/123/basic", null, String.class);
+        assertEquals("123;Donald Duck", out2);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // configure to use undertow on localhost with the given port
+                
restConfiguration().component("undertow").host("localhost").port(getPort()).enableCORS(true);
+
+                // use the rest DSL to define the rest services
+                rest("/users/")
+                    .get("{id}/basic")
+                        .route()
+                        .to("mock:input")
+                        .process(new Processor() {
+                            public void process(Exchange exchange) throws 
Exception {
+                                String id = exchange.getIn().getHeader("id", 
String.class);
+                                exchange.getOut().setBody(id + ";Donald Duck");
+                            }
+                        });
+            }
+        };
+    }
+
+}

Reply via email to