You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2018/06/22 10:28:24 UTC
[incubator-servicecomb-java-chassis] 01/03: [SCB-679] support
mounting CorsHandler
This is an automated email from the ASF dual-hosted git repository.
liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git
commit 47490a6d8ce8bef48b5558969d9ce74e9b63d7b6
Author: yaohaishi <ya...@huawei.com>
AuthorDate: Wed Jun 20 23:40:24 2018 +0800
[SCB-679] support mounting CorsHandler
---
.../transport/rest/vertx/RestServerVerticle.java | 38 ++++++++
.../transport/rest/vertx/TransportConfig.java | 63 +++++++++++++
.../rest/vertx/TestRestServerVerticle.java | 87 ++++++++++++++++-
.../transport/rest/vertx/TestTransportConfig.java | 105 +++++++++++++++++----
4 files changed, 271 insertions(+), 22 deletions(-)
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java
index f2ac2fb..3780347 100644
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java
@@ -18,6 +18,7 @@
package org.apache.servicecomb.transport.rest.vertx;
import java.util.List;
+import java.util.Set;
import org.apache.servicecomb.core.Endpoint;
import org.apache.servicecomb.core.transport.AbstractTransport;
@@ -36,9 +37,11 @@ import io.vertx.core.AbstractVerticle;
import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
+import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.ext.web.Router;
+import io.vertx.ext.web.handler.CorsHandler;
public class RestServerVerticle extends AbstractVerticle {
private static final Logger LOGGER = LoggerFactory.getLogger(RestServerVerticle.class);
@@ -68,6 +71,7 @@ public class RestServerVerticle extends AbstractVerticle {
}
Router mainRouter = Router.router(vertx);
mountAccessLogHandler(mainRouter);
+ mountCorsHandler(mainRouter);
initDispatcher(mainRouter);
HttpServer httpServer = createHttpServer();
httpServer.requestHandler(mainRouter::accept);
@@ -90,6 +94,40 @@ public class RestServerVerticle extends AbstractVerticle {
}
}
+ /**
+ * Support CORS
+ */
+ void mountCorsHandler(Router mainRouter) {
+ if (!TransportConfig.isCorsEnabled()) {
+ return;
+ }
+
+ CorsHandler corsHandler = getCorsHandler(TransportConfig.getCorsAllowedOrigin());
+ // Access-Control-Allow-Credentials
+ corsHandler.allowCredentials(TransportConfig.isCorsAllowCredentials());
+ // Access-Control-Allow-Headers
+ corsHandler.allowedHeaders(TransportConfig.getCorsAllowedHeaders());
+ // Access-Control-Allow-Methods
+ Set<String> allowedMethods = TransportConfig.getCorsAllowedMethods();
+ for (String method : allowedMethods) {
+ corsHandler.allowedMethod(HttpMethod.valueOf(method));
+ }
+ // Access-Control-Expose-Headers
+ corsHandler.exposedHeaders(TransportConfig.getCorsExposedHeaders());
+ // Access-Control-Max-Age
+ int maxAge = TransportConfig.getCorsMaxAge();
+ if (maxAge >= 0) {
+ corsHandler.maxAgeSeconds(maxAge);
+ }
+
+ LOGGER.info("mount CorsHandler");
+ mainRouter.route().handler(corsHandler);
+ }
+
+ private CorsHandler getCorsHandler(String corsAllowedOrigin) {
+ return CorsHandler.create(corsAllowedOrigin);
+ }
+
private void initDispatcher(Router mainRouter) {
List<VertxHttpDispatcher> dispatchers = SPIServiceUtils.getSortedService(VertxHttpDispatcher.class);
for (VertxHttpDispatcher dispatcher : dispatchers) {
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java
index 01f13c8..ae08b0c 100644
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java
@@ -17,6 +17,12 @@
package org.apache.servicecomb.transport.rest.vertx;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.springframework.util.StringUtils;
+
import com.netflix.config.DynamicIntProperty;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;
@@ -34,6 +40,8 @@ public final class TransportConfig {
// 32K
public static final int DEFAULT_SERVER_MAX_HEADER_SIZE = 32 * 1024;
+ public static final String SERVICECOMB_CORS_CONFIG_BASE = "servicecomb.cors";
+
private static Class<? extends Verticle> restServerVerticle = RestServerVerticle.class;
private TransportConfig() {
@@ -78,4 +86,59 @@ public final class TransportConfig {
.getIntProperty("cse.rest.server.maxHeaderSize", DEFAULT_SERVER_MAX_HEADER_SIZE)
.get();
}
+
+ public static boolean isCorsEnabled() {
+ return DynamicPropertyFactory.getInstance()
+ .getBooleanProperty(SERVICECOMB_CORS_CONFIG_BASE + ".enabled", false)
+ .get();
+ }
+
+ public static String getCorsAllowedOrigin() {
+ return DynamicPropertyFactory.getInstance()
+ .getStringProperty(SERVICECOMB_CORS_CONFIG_BASE + ".origin", "*")
+ .get();
+ }
+
+ public static boolean isCorsAllowCredentials() {
+ return DynamicPropertyFactory.getInstance()
+ .getBooleanProperty(SERVICECOMB_CORS_CONFIG_BASE + ".allowCredentials", false)
+ .get();
+ }
+
+ public static Set<String> getCorsAllowedHeaders() {
+ String allowedHeaders = DynamicPropertyFactory.getInstance()
+ .getStringProperty(SERVICECOMB_CORS_CONFIG_BASE + ".allowedHeader", null)
+ .get();
+ return convertToSet(allowedHeaders);
+ }
+
+ public static Set<String> getCorsAllowedMethods() {
+ String allowedMethods = DynamicPropertyFactory.getInstance()
+ .getStringProperty(SERVICECOMB_CORS_CONFIG_BASE + ".allowedMethod", null)
+ .get();
+ return convertToSet(allowedMethods);
+ }
+
+ public static Set<String> getCorsExposedHeaders() {
+ String exposedHeaders = DynamicPropertyFactory.getInstance()
+ .getStringProperty(SERVICECOMB_CORS_CONFIG_BASE + ".exposedHeader", null)
+ .get();
+ return convertToSet(exposedHeaders);
+ }
+
+ public static int getCorsMaxAge() {
+ return DynamicPropertyFactory.getInstance()
+ .getIntProperty(SERVICECOMB_CORS_CONFIG_BASE + ".maxAge", -1)
+ .get();
+ }
+
+ private static Set<String> convertToSet(String setString) {
+ Set<String> resultSet = new HashSet<>();
+ if (!StringUtils.isEmpty(setString)) {
+ String[] arrString = setString.split(",");
+ Stream.of(arrString).map(String::trim).filter(str -> !StringUtils.isEmpty(str))
+ .forEach(resultSet::add);
+ }
+ return resultSet;
+ }
}
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java
index 146c561..4b65435 100644
--- a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java
@@ -17,22 +17,36 @@
package org.apache.servicecomb.transport.rest.vertx;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
import org.apache.servicecomb.core.CseContext;
import org.apache.servicecomb.core.Endpoint;
import org.apache.servicecomb.core.Transport;
import org.apache.servicecomb.core.transport.AbstractTransport;
import org.apache.servicecomb.core.transport.TransportManager;
import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
+import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
+import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mockito;
import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
+import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.Route;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.handler.CorsHandler;
+import mockit.Deencapsulation;
import mockit.Expectations;
+import mockit.Mock;
+import mockit.MockUp;
import mockit.Mocked;
public class TestRestServerVerticle {
@@ -42,7 +56,7 @@ public class TestRestServerVerticle {
Future<Void> startFuture = null;
@Before
- public void setUp() throws Exception {
+ public void setUp() {
instance = new RestServerVerticle();
startFuture = Future.future();
@@ -50,7 +64,7 @@ public class TestRestServerVerticle {
}
@After
- public void tearDown() throws Exception {
+ public void tearDown() {
instance = null;
startFuture = null;
}
@@ -112,7 +126,7 @@ public class TestRestServerVerticle {
@Test
public void testRestServerVerticleWithHttp2(@Mocked Transport transport, @Mocked Vertx vertx,
@Mocked Context context,
- @Mocked JsonObject jsonObject, @Mocked Future<Void> startFuture) throws Exception {
+ @Mocked JsonObject jsonObject, @Mocked Future<Void> startFuture) {
URIEndpointObject endpointObject = new URIEndpointObject("http://127.0.0.1:8080?protocol=http2");
new Expectations() {
{
@@ -164,4 +178,71 @@ public class TestRestServerVerticle {
}
Assert.assertFalse(status);
}
+
+ @Test
+ public void testMountCorsHandler() {
+ ArchaiusUtils.setProperty("servicecomb.cors.enabled", true);
+ ArchaiusUtils.setProperty("servicecomb.cors.allowedMethod", "GET,PUT,POST");
+ ArchaiusUtils.setProperty("servicecomb.cors.allowedHeader", "abc,def");
+ ArchaiusUtils.setProperty("servicecomb.cors.exposedHeader", "abc2,def2");
+ ArchaiusUtils.setProperty("servicecomb.cors.maxAge", 1);
+ Set<HttpMethod> methodSet = new HashSet<>(3);
+ methodSet.add(HttpMethod.GET);
+ methodSet.add(HttpMethod.PUT);
+ methodSet.add(HttpMethod.POST);
+ AtomicInteger counter = new AtomicInteger(0);
+
+ CorsHandler corsHandler = new MockUp<CorsHandler>() {
+ @Mock
+ CorsHandler allowCredentials(boolean allow) {
+ Assert.assertFalse(allow);
+ counter.incrementAndGet();
+ return null;
+ }
+
+ @Mock
+ CorsHandler allowedHeaders(Set<String> headerNames) {
+ Assert.assertThat(headerNames, Matchers.containsInAnyOrder("abc", "def"));
+ counter.incrementAndGet();
+ return null;
+ }
+
+ @Mock
+ CorsHandler exposedHeaders(Set<String> headerNames) {
+ Assert.assertThat(headerNames, Matchers.containsInAnyOrder("abc2", "def2"));
+ counter.incrementAndGet();
+ return null;
+ }
+
+ @Mock
+ CorsHandler allowedMethod(HttpMethod method) {
+ Assert.assertTrue(methodSet.contains(method));
+ counter.incrementAndGet();
+ methodSet.remove(method);
+ return null;
+ }
+
+ @Mock
+ CorsHandler maxAgeSeconds(int maxAgeSeconds) {
+ Assert.assertEquals(1, maxAgeSeconds);
+ counter.incrementAndGet();
+ return null;
+ }
+ }.getMockInstance();
+
+ new MockUp<RestServerVerticle>() {
+ @Mock
+ CorsHandler getCorsHandler(String corsAllowedOrigin) {
+ Assert.assertEquals("*", corsAllowedOrigin);
+ return corsHandler;
+ }
+ };
+ Router router = Mockito.mock(Router.class);
+ Mockito.when(router.route()).thenReturn(Mockito.mock(Route.class));
+
+ RestServerVerticle server = new RestServerVerticle();
+
+ Deencapsulation.invoke(server, "mountCorsHandler", router);
+ Assert.assertEquals(7, counter.get());
+ }
}
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestTransportConfig.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestTransportConfig.java
index e63dcd5..a3e84f8 100644
--- a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestTransportConfig.java
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestTransportConfig.java
@@ -17,26 +17,22 @@
package org.apache.servicecomb.transport.rest.vertx;
-import org.apache.commons.configuration.Configuration;
import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
-import org.junit.AfterClass;
+import org.hamcrest.Matchers;
+import org.junit.After;
import org.junit.Assert;
-import org.junit.BeforeClass;
+import org.junit.Before;
import org.junit.Test;
-import com.netflix.config.DynamicPropertyFactory;
-
public class TestTransportConfig {
- Configuration config = (Configuration) DynamicPropertyFactory.getBackingConfigurationSource();
- @BeforeClass
- public static void setup() {
+ @Before
+ public void before() {
ArchaiusUtils.resetConfig();
}
-
- @AfterClass
- public static void teardown() {
+ @After
+ public void after() {
ArchaiusUtils.resetConfig();
}
@@ -47,9 +43,8 @@ public class TestTransportConfig {
@Test
public void testGetAddressNormal() {
- config.addProperty("cse.rest.address", "1.1.1.1");
+ ArchaiusUtils.setProperty("cse.rest.address", "1.1.1.1");
Assert.assertEquals("1.1.1.1", TransportConfig.getAddress());
- config.clearProperty("cse.rest.address");
}
@Test
@@ -59,18 +54,90 @@ public class TestTransportConfig {
@Test
public void testGetThreadCountNormal() {
- config.addProperty("cse.rest.server.thread-count", 10);
+ ArchaiusUtils.setProperty("cse.rest.server.thread-count", 10);
Assert.assertEquals(10, TransportConfig.getThreadCount());
- config.clearProperty("cse.rest.server.thread-count");
}
@Test
public void testGetCompressedAndHeaderSize() {
- config.addProperty("cse.rest.server.compression", true);
+ ArchaiusUtils.setProperty("cse.rest.server.compression", true);
Assert.assertEquals(true, TransportConfig.getCompressed());
- config.addProperty("cse.rest.server.maxHeaderSize", 2048);
+ ArchaiusUtils.setProperty("cse.rest.server.maxHeaderSize", 2048);
Assert.assertEquals(2048, TransportConfig.getMaxHeaderSize());
- config.clearProperty("cse.rest.server.compression");
- config.clearProperty("cse.rest.server.maxHeaderSize");
+ }
+
+ @Test
+ public void testIsCorsEnabled() {
+ Assert.assertFalse(TransportConfig.isCorsEnabled());
+ ArchaiusUtils.setProperty("servicecomb.cors.enabled", true);
+ Assert.assertTrue(TransportConfig.isCorsEnabled());
+ ArchaiusUtils.setProperty("servicecomb.cors.enabled", false);
+ Assert.assertFalse(TransportConfig.isCorsEnabled());
+ }
+
+ @Test
+ public void testGetCorsAllowedOrigin() {
+ Assert.assertEquals("*", TransportConfig.getCorsAllowedOrigin());
+ String origin = "http://localhost:8080";
+ ArchaiusUtils.setProperty("servicecomb.cors.origin", origin);
+ Assert.assertEquals(origin, TransportConfig.getCorsAllowedOrigin());
+ }
+
+ @Test
+ public void testIsCorsAllowCredentials() {
+ Assert.assertFalse(TransportConfig.isCorsAllowCredentials());
+ ArchaiusUtils.setProperty("servicecomb.cors.allowCredentials", true);
+ Assert.assertTrue(TransportConfig.isCorsAllowCredentials());
+ ArchaiusUtils.setProperty("servicecomb.cors.allowCredentials", false);
+ Assert.assertFalse(TransportConfig.isCorsAllowCredentials());
+ }
+
+ @Test
+ public void testGetCorsAllowedHeaders() {
+ String configKey = "servicecomb.cors.allowedHeader";
+ Assert.assertTrue(TransportConfig.getCorsAllowedHeaders().isEmpty());
+ ArchaiusUtils.setProperty(configKey, "abc");
+ Assert.assertThat(TransportConfig.getCorsAllowedHeaders(), Matchers.containsInAnyOrder("abc"));
+ ArchaiusUtils.setProperty(configKey, "abc, def");
+ Assert.assertThat(TransportConfig.getCorsAllowedHeaders(), Matchers.containsInAnyOrder("abc", "def"));
+ ArchaiusUtils.setProperty(configKey, "abc ,, def");
+ Assert.assertThat(TransportConfig.getCorsAllowedHeaders(), Matchers.containsInAnyOrder("abc", "def"));
+ ArchaiusUtils.setProperty(configKey, "");
+ Assert.assertTrue(TransportConfig.getCorsAllowedHeaders().isEmpty());
+ }
+
+ @Test
+ public void testGetCorsAllowedMethods() {
+ String configKey = "servicecomb.cors.allowedMethod";
+ Assert.assertTrue(TransportConfig.getCorsAllowedMethods().isEmpty());
+ ArchaiusUtils.setProperty(configKey, "GET");
+ Assert.assertThat(TransportConfig.getCorsAllowedMethods(), Matchers.containsInAnyOrder("GET"));
+ ArchaiusUtils.setProperty(configKey, "GET, POST");
+ Assert.assertThat(TransportConfig.getCorsAllowedMethods(), Matchers.containsInAnyOrder("GET", "POST"));
+ ArchaiusUtils.setProperty(configKey, "GET,,POST");
+ Assert.assertThat(TransportConfig.getCorsAllowedMethods(), Matchers.containsInAnyOrder("GET", "POST"));
+ ArchaiusUtils.setProperty(configKey, "");
+ Assert.assertTrue(TransportConfig.getCorsAllowedMethods().isEmpty());
+ }
+
+ @Test
+ public void testGetCorsExposedHeaders() {
+ String configKey = "servicecomb.cors.exposedHeader";
+ Assert.assertTrue(TransportConfig.getCorsExposedHeaders().isEmpty());
+ ArchaiusUtils.setProperty(configKey, "abc");
+ Assert.assertThat(TransportConfig.getCorsExposedHeaders(), Matchers.containsInAnyOrder("abc"));
+ ArchaiusUtils.setProperty(configKey, "abc, def");
+ Assert.assertThat(TransportConfig.getCorsExposedHeaders(), Matchers.containsInAnyOrder("abc", "def"));
+ ArchaiusUtils.setProperty(configKey, "abc ,, def");
+ Assert.assertThat(TransportConfig.getCorsExposedHeaders(), Matchers.containsInAnyOrder("abc", "def"));
+ ArchaiusUtils.setProperty(configKey, "");
+ Assert.assertTrue(TransportConfig.getCorsExposedHeaders().isEmpty());
+ }
+
+ @Test
+ public void testGetCorsMaxAge() {
+ Assert.assertEquals(-1, TransportConfig.getCorsMaxAge());
+ ArchaiusUtils.setProperty("servicecomb.cors.maxAge", 3600);
+ Assert.assertEquals(3600, TransportConfig.getCorsMaxAge());
}
}