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"); + } + }); + } + }; + } + +}