KAFKA-3578: Allow cross origin HTTP requests on all HTTP methods Author: Liquan Pei <[email protected]>
Reviewers: Ewen Cheslack-Postava <[email protected]> Closes #1288 from Ishiihara/kip-56 Project: http://git-wip-us.apache.org/repos/asf/kafka/repo Commit: http://git-wip-us.apache.org/repos/asf/kafka/commit/e5032733 Tree: http://git-wip-us.apache.org/repos/asf/kafka/tree/e5032733 Diff: http://git-wip-us.apache.org/repos/asf/kafka/diff/e5032733 Branch: refs/heads/0.10.0 Commit: e50327331d7d4e2854297a377f6701d737599672 Parents: e29eac4 Author: Liquan Pei <[email protected]> Authored: Fri Apr 29 10:52:42 2016 -0700 Committer: Ewen Cheslack-Postava <[email protected]> Committed: Fri Apr 29 10:52:42 2016 -0700 ---------------------------------------------------------------------- .../kafka/connect/runtime/WorkerConfig.java | 10 ++++++- .../kafka/connect/runtime/rest/RestServer.java | 14 +++++++--- .../connect/runtime/rest/RestServerTest.java | 28 ++++++++++++++------ 3 files changed, 39 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kafka/blob/e5032733/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/WorkerConfig.java ---------------------------------------------------------------------- diff --git a/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/WorkerConfig.java b/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/WorkerConfig.java index 471e4a5..7ede130 100644 --- a/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/WorkerConfig.java +++ b/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/WorkerConfig.java @@ -109,6 +109,11 @@ public class WorkerConfig extends AbstractConfig { " from the domain of the REST API."; protected static final String ACCESS_CONTROL_ALLOW_ORIGIN_DEFAULT = ""; + public static final String ACCESS_CONTROL_ALLOW_METHODS_CONFIG = "access.control.allow.methods"; + protected static final String ACCESS_CONTROL_ALLOW_METHODS_DOC = + "Sets the methods supported for cross origin requests by setting the Access-Control-Allow-Methods header. " + + "The default value of the Access-Control-Allow-Methods header allows cross origin requests for GET, POST and HEAD."; + protected static final String ACCESS_CONTROL_ALLOW_METHODS_DEFAULT = ""; /** * Get a basic ConfigDef for a WorkerConfig. This includes all the common settings. Subclasses can use this to @@ -141,7 +146,10 @@ public class WorkerConfig extends AbstractConfig { .define(REST_ADVERTISED_PORT_CONFIG, Type.INT, null, Importance.LOW, REST_ADVERTISED_PORT_DOC) .define(ACCESS_CONTROL_ALLOW_ORIGIN_CONFIG, Type.STRING, ACCESS_CONTROL_ALLOW_ORIGIN_DEFAULT, Importance.LOW, - ACCESS_CONTROL_ALLOW_ORIGIN_DOC); + ACCESS_CONTROL_ALLOW_ORIGIN_DOC) + .define(ACCESS_CONTROL_ALLOW_METHODS_CONFIG, Type.STRING, + ACCESS_CONTROL_ALLOW_METHODS_DEFAULT, Importance.LOW, + ACCESS_CONTROL_ALLOW_METHODS_DOC); } public WorkerConfig(ConfigDef definition, Map<String, String> props) { http://git-wip-us.apache.org/repos/asf/kafka/blob/e5032733/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/rest/RestServer.java ---------------------------------------------------------------------- diff --git a/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/rest/RestServer.java b/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/rest/RestServer.java index 3475e1c..a878fb0 100644 --- a/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/rest/RestServer.java +++ b/connect/runtime/src/main/java/org/apache/kafka/connect/runtime/rest/RestServer.java @@ -20,6 +20,7 @@ package org.apache.kafka.connect.runtime.rest; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; + import org.apache.kafka.connect.errors.ConnectException; import org.apache.kafka.connect.runtime.Herder; import org.apache.kafka.connect.runtime.WorkerConfig; @@ -47,9 +48,6 @@ import org.glassfish.jersey.servlet.ServletContainer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.servlet.DispatcherType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -60,6 +58,10 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; +import javax.servlet.DispatcherType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; + /** * Embedded server for the REST API that provides the control plane for Kafka Connect workers. */ @@ -115,7 +117,11 @@ public class RestServer { if (allowedOrigins != null && !allowedOrigins.trim().isEmpty()) { FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter()); filterHolder.setName("cross-origin"); - filterHolder.setInitParameter("allowedOrigins", allowedOrigins); + filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, allowedOrigins); + String allowedMethods = config.getString(WorkerConfig.ACCESS_CONTROL_ALLOW_METHODS_CONFIG); + if (allowedMethods != null && !allowedOrigins.trim().isEmpty()) { + filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, allowedMethods); + } context.addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST)); } http://git-wip-us.apache.org/repos/asf/kafka/blob/e5032733/connect/runtime/src/test/java/org/apache/kafka/connect/runtime/rest/RestServerTest.java ---------------------------------------------------------------------- diff --git a/connect/runtime/src/test/java/org/apache/kafka/connect/runtime/rest/RestServerTest.java b/connect/runtime/src/test/java/org/apache/kafka/connect/runtime/rest/RestServerTest.java index 8e9d52b..64d5b5e 100644 --- a/connect/runtime/src/test/java/org/apache/kafka/connect/runtime/rest/RestServerTest.java +++ b/connect/runtime/src/test/java/org/apache/kafka/connect/runtime/rest/RestServerTest.java @@ -31,11 +31,6 @@ import org.powermock.api.easymock.PowerMock; import org.powermock.api.easymock.annotation.MockStrict; import org.powermock.modules.junit4.PowerMockRunner; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.Response; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; @@ -43,6 +38,12 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; + import static org.junit.Assert.assertEquals; @RunWith(PowerMockRunner.class) @@ -71,15 +72,15 @@ public class RestServerTest { @Test public void testCORSEnabled() { - checkCORSRequest("*", "http://bar.com", "http://bar.com"); + checkCORSRequest("*", "http://bar.com", "http://bar.com", "PUT"); } @Test public void testCORSDisabled() { - checkCORSRequest("", "http://bar.com", null); + checkCORSRequest("", "http://bar.com", null, null); } - public void checkCORSRequest(String corsDomain, String origin, String expectedHeader) { + public void checkCORSRequest(String corsDomain, String origin, String expectedHeader, String method) { // To be able to set the Origin, we need to toggle this flag System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); @@ -92,10 +93,12 @@ public class RestServerTest { return null; } }); + PowerMock.replayAll(); Map<String, String> workerProps = baseWorkerProps(); workerProps.put(WorkerConfig.ACCESS_CONTROL_ALLOW_ORIGIN_CONFIG, corsDomain); + workerProps.put(WorkerConfig.ACCESS_CONTROL_ALLOW_METHODS_CONFIG, method); WorkerConfig workerConfig = new StandaloneConfig(workerProps); server = new RestServer(workerConfig); server.start(herder); @@ -107,6 +110,15 @@ public class RestServerTest { assertEquals(200, response.getStatus()); assertEquals(expectedHeader, response.getHeaderString("Access-Control-Allow-Origin")); + + response = request("/connector-plugins/FileStreamSource/validate") + .header("Referer", origin + "/page") + .header("Origin", origin) + .header("Access-Control-Request-Method", method) + .options(); + assertEquals(404, response.getStatus()); + assertEquals(expectedHeader, response.getHeaderString("Access-Control-Allow-Origin")); + assertEquals(method, response.getHeaderString("Access-Control-Allow-Methods")); PowerMock.verifyAll(); }
