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/servicecomb-java-chassis.git
The following commit(s) were added to refs/heads/master by this push:
new cbc614f [SCB-1847] create a new edge dispatcher to forward http
requests to provider
cbc614f is described below
commit cbc614f668339b0f993374e509494e484e52fd19
Author: liubao <[email protected]>
AuthorDate: Thu Apr 2 20:21:44 2020 +0800
[SCB-1847] create a new edge dispatcher to forward http requests to provider
---
.../org/apache/servicecomb/core/Invocation.java | 2 +-
...gumentExceptionToProducerResponseConverter.java | 46 +++++
...owableExceptionToProducerResponseConverter.java | 45 +++++
.../handler/impl/ProducerOperationHandler.java | 26 +--
....exception.ExceptionToProducerResponseConverter | 4 +-
.../servicecomb/demo/edge/consumer/Consumer.java | 6 +-
.../demo/edge/consumer/ConsumerMain.java | 2 +
.../src/main/resources/microservice.yaml | 18 ++
.../jaxrs/client/MultiErrorCodeServiceClient.java | 6 +-
edge/edge-core/pom.xml | 5 +-
.../edge/core/CommonHttpEdgeDispatcher.java | 192 +++++++++++++++++++++
.../edge/core/URLMappedEdgeDispatcher.java | 28 ++-
...cecomb.transport.rest.vertx.VertxHttpDispatcher | 3 +-
.../edge/core/TestURLMappedEdgeDispatcher.java | 9 +
.../tests/SpringMvcIntegrationTestBase.java | 7 +-
.../ExceptionToProducerResponseConverter.java | 6 +
.../transport/rest/vertx/RestBodyHandler.java | 31 ++--
17 files changed, 387 insertions(+), 49 deletions(-)
diff --git a/core/src/main/java/org/apache/servicecomb/core/Invocation.java
b/core/src/main/java/org/apache/servicecomb/core/Invocation.java
index 049316c..8a36c66 100644
--- a/core/src/main/java/org/apache/servicecomb/core/Invocation.java
+++ b/core/src/main/java/org/apache/servicecomb/core/Invocation.java
@@ -144,7 +144,7 @@ public class Invocation extends SwaggerInvocation {
public Invocation() {
// An empty invocation, used to mock or some other scenario do not need
operation information.
- traceIdLogger = new TraceIdLogger(this);
+ traceIdLogger = new TraceIdLogger(this);
}
public Invocation(ReferenceConfig referenceConfig, OperationMeta
operationMeta,
diff --git
a/core/src/main/java/org/apache/servicecomb/core/exception/IllegalArgumentExceptionToProducerResponseConverter.java
b/core/src/main/java/org/apache/servicecomb/core/exception/IllegalArgumentExceptionToProducerResponseConverter.java
new file mode 100644
index 0000000..c3c8d2e
--- /dev/null
+++
b/core/src/main/java/org/apache/servicecomb/core/exception/IllegalArgumentExceptionToProducerResponseConverter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.servicecomb.core.exception;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.apache.servicecomb.swagger.invocation.SwaggerInvocation;
+import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
+import
org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
+
+public class IllegalArgumentExceptionToProducerResponseConverter implements
+ ExceptionToProducerResponseConverter<IllegalArgumentException> {
+ @Override
+ public Class<IllegalArgumentException> getExceptionClass() {
+ return IllegalArgumentException.class;
+ }
+
+ @Override
+ public int getOrder() {
+ return 10000;
+ }
+
+ @Override
+ public Response convert(SwaggerInvocation swaggerInvocation,
IllegalArgumentException e) {
+ InvocationException invocationException = new
InvocationException(Status.BAD_REQUEST.getStatusCode(), "",
+ new CommonExceptionData("Parameters not valid or types not match."),
e);
+ return Response.failResp(invocationException);
+ }
+}
diff --git
a/core/src/main/java/org/apache/servicecomb/core/exception/ThrowableExceptionToProducerResponseConverter.java
b/core/src/main/java/org/apache/servicecomb/core/exception/ThrowableExceptionToProducerResponseConverter.java
new file mode 100644
index 0000000..4ffa2ec
--- /dev/null
+++
b/core/src/main/java/org/apache/servicecomb/core/exception/ThrowableExceptionToProducerResponseConverter.java
@@ -0,0 +1,45 @@
+/*
+ * 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.servicecomb.core.exception;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.apache.servicecomb.swagger.invocation.SwaggerInvocation;
+import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
+import
org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
+
+public class ThrowableExceptionToProducerResponseConverter implements
ExceptionToProducerResponseConverter<Throwable> {
+ @Override
+ public Class<Throwable> getExceptionClass() {
+ return Throwable.class;
+ }
+
+ @Override
+ public int getOrder() {
+ return 20000;
+ }
+
+ @Override
+ public Response convert(SwaggerInvocation swaggerInvocation, Throwable e) {
+ InvocationException invocationException = new
InvocationException(Status.INTERNAL_SERVER_ERROR.getStatusCode(), "",
+ new CommonExceptionData("Unexpected exception when processing the
request."), e);
+ return Response.failResp(invocationException);
+ }
+}
diff --git
a/core/src/main/java/org/apache/servicecomb/core/handler/impl/ProducerOperationHandler.java
b/core/src/main/java/org/apache/servicecomb/core/handler/impl/ProducerOperationHandler.java
index 22df2f9..3845e6b 100644
---
a/core/src/main/java/org/apache/servicecomb/core/handler/impl/ProducerOperationHandler.java
+++
b/core/src/main/java/org/apache/servicecomb/core/handler/impl/ProducerOperationHandler.java
@@ -20,8 +20,6 @@ package org.apache.servicecomb.core.handler.impl;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.CompletableFuture;
-import javax.ws.rs.core.Response.Status;
-
import org.apache.servicecomb.core.Handler;
import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.core.exception.ExceptionUtils;
@@ -30,7 +28,6 @@ import
org.apache.servicecomb.swagger.invocation.AsyncResponse;
import org.apache.servicecomb.swagger.invocation.Response;
import org.apache.servicecomb.swagger.invocation.SwaggerInvocation;
import org.apache.servicecomb.swagger.invocation.context.ContextUtils;
-import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory;
import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
import
org.apache.servicecomb.swagger.invocation.extension.ProducerInvokeExtension;
@@ -93,17 +90,11 @@ public class ProducerOperationHandler implements Handler {
asyncResp.handle(processException(invocation, ex));
});
- } catch (IllegalArgumentException ae) {
- invocation.getTraceIdLogger().error(LOGGER, "Parameters not valid or
types not match {},",
- invocation.getInvocationQualifiedName(), ae);
- invocation.onBusinessMethodFinish();
- invocation.onBusinessFinish();
- asyncResp.handle(processException(invocation,
- new InvocationException(Status.BAD_REQUEST.getStatusCode(), "",
- new CommonExceptionData("Parameters not valid or types not
match."), ae)));
} catch (Throwable e) {
- invocation.getTraceIdLogger().error(LOGGER, "unexpected error {},",
- invocation.getInvocationQualifiedName(), e);
+ if (shouldPrintErrorLog(e)) {
+ invocation.getTraceIdLogger().error(LOGGER, "unexpected error {},",
+ invocation.getInvocationQualifiedName(), e);
+ }
invocation.onBusinessMethodFinish();
invocation.onBusinessFinish();
asyncResp.handle(processException(invocation, e));
@@ -132,15 +123,6 @@ public class ProducerOperationHandler implements Handler {
invocation.onBusinessMethodFinish();
invocation.onBusinessFinish();
- } catch (IllegalArgumentException ae) {
- invocation.getTraceIdLogger().error(LOGGER, "Parameters not valid or
types not match {},",
- invocation.getInvocationQualifiedName(), ae);
- invocation.onBusinessMethodFinish();
- invocation.onBusinessFinish();
- // ae.getMessage() is always null. Give a custom error message.
- response = processException(invocation,
- new InvocationException(Status.BAD_REQUEST.getStatusCode(), "",
- new CommonExceptionData("Parameters not valid or types not
match."), ae));
} catch (Throwable e) {
if (shouldPrintErrorLog(e)) {
invocation.getTraceIdLogger().error(LOGGER, "unexpected error {},",
diff --git
a/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter
similarity index 81%
copy from
edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
copy to
core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter
index d81bb22..f703b90 100644
---
a/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
+++
b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter
@@ -15,5 +15,5 @@
# limitations under the License.
#
-org.apache.servicecomb.edge.core.DefaultEdgeDispatcher
-org.apache.servicecomb.edge.core.URLMappedEdgeDispatcher
\ No newline at end of file
+org.apache.servicecomb.core.exception.IllegalArgumentExceptionToProducerResponseConverter
+org.apache.servicecomb.core.exception.ThrowableExceptionToProducerResponseConverter
\ No newline at end of file
diff --git
a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java
b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java
index 7d5d567..973e556 100644
---
a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java
+++
b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java
@@ -168,8 +168,12 @@ public class Consumer {
int response = template.getForObject(url + "?x=2&y=3", Integer.class);
Assert.isTrue(response == 5, "not get 5.");
+ try {
Map raw = template.getForObject(url + "?x=99&y=3", Map.class);
- Assert.isTrue(raw.get("message").equals("Cse Internal Server Error"),
"x99");
+ } catch (HttpServerErrorException e) {
+ Assert.isTrue(e.getRawStatusCode() == 500, "x99");
+ Assert.isTrue(e.getResponseBodyAsString().contains("Unexpected exception
when processing the request"), "x99");
+ }
try {
template.getForObject(url + "?x=88&y=3", Map.class);
diff --git
a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java
b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java
index b4378b5..1bcfc30 100644
---
a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java
+++
b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java
@@ -33,6 +33,8 @@ public class ConsumerMain {
new Consumer().run("rest");
System.out.println("Running url dispatcher.");
new Consumer().run("url");
+ System.out.println("Running http dispatcher.");
+ new Consumer().run("http");
System.out.println("All test case finished.");
}
diff --git a/demo/demo-edge/edge-service/src/main/resources/microservice.yaml
b/demo/demo-edge/edge-service/src/main/resources/microservice.yaml
index beaa93b..f4f2489 100644
--- a/demo/demo-edge/edge-service/src/main/resources/microservice.yaml
+++ b/demo/demo-edge/edge-service/src/main/resources/microservice.yaml
@@ -56,3 +56,21 @@ servicecomb:
path: "/url/business/v2/.*"
microserviceName: business
versionRule: 2.0.0-3.0.0
+ http:
+ enabled: true
+ mappings:
+ businessV2:
+ prefixSegmentCount: 1
+ path: "/http/business/v2/.*"
+ microserviceName: business
+ versionRule: 2.0.0
+ businessV1:
+ prefixSegmentCount: 1
+ path: "/http/business/v1/add.*"
+ microserviceName: business
+ versionRule: 1.0.0-1.2.0
+ businessV1_1:
+ prefixSegmentCount: 1
+ path: "/http/business/v1/dec.*"
+ microserviceName: business
+ versionRule: 1.1.0
\ No newline at end of file
diff --git
a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java
b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java
index 3a7ec70..2ff8d0a 100644
---
a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java
+++
b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java
@@ -41,8 +41,8 @@ import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;
-import org.springframework.web.client.UnknownHttpStatusCodeException;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
@@ -110,8 +110,8 @@ public class MultiErrorCodeServiceClient implements
CategorizedTestCase {
result = template
.postForEntity(serverDirectURL + "/MultiErrorCodeService/errorCode",
entity, MultiResponse200.class);
TestMgr.check(590, 200);
- } catch (UnknownHttpStatusCodeException e) {
- TestMgr.check(e.getRawStatusCode(), 590);
+ } catch (HttpServerErrorException e) {
+ TestMgr.check(e.getRawStatusCode(), 500);
}
// not recommend
diff --git a/edge/edge-core/pom.xml b/edge/edge-core/pom.xml
index e50c5c1..86454f5 100644
--- a/edge/edge-core/pom.xml
+++ b/edge/edge-core/pom.xml
@@ -31,7 +31,10 @@
<groupId>org.apache.servicecomb</groupId>
<artifactId>transport-rest-vertx</artifactId>
</dependency>
-
+ <dependency>
+ <groupId>org.apache.servicecomb</groupId>
+ <artifactId>handler-loadbalance</artifactId>
+ </dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-codegen</artifactId>
diff --git
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java
new file mode 100644
index 0000000..ac8f30d
--- /dev/null
+++
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java
@@ -0,0 +1,192 @@
+/*
+ * 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.servicecomb.edge.core;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.foundation.common.cache.VersionedCache;
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
+import org.apache.servicecomb.foundation.vertx.client.http.HttpClients;
+import org.apache.servicecomb.loadbalance.ExtensionsManager;
+import org.apache.servicecomb.loadbalance.LoadBalancer;
+import org.apache.servicecomb.loadbalance.LoadbalanceHandler;
+import org.apache.servicecomb.loadbalance.RuleExt;
+import org.apache.servicecomb.loadbalance.ServiceCombServer;
+import org.apache.servicecomb.loadbalance.filter.ServerDiscoveryFilter;
+import org.apache.servicecomb.serviceregistry.RegistryUtils;
+import org.apache.servicecomb.serviceregistry.discovery.DiscoveryContext;
+import org.apache.servicecomb.serviceregistry.discovery.DiscoveryTree;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.netflix.config.ConcurrentCompositeConfiguration;
+import com.netflix.config.DynamicPropertyFactory;
+
+import io.vertx.core.http.HttpClient;
+import io.vertx.core.http.HttpClientRequest;
+import io.vertx.core.http.RequestOptions;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.RoutingContext;
+
+/**
+ * This dispatcher forwards requests to any http servers, includes
java-chassis providers and other,
+ * provided the server is registered to service center.
+ *
+ * This dispatcher using loadbalance handler to choose the target server. So
any functions
+ * provided by loadbalancer handler is available, excluding retrying.
+ */
+public class CommonHttpEdgeDispatcher extends AbstractEdgeDispatcher {
+ private static final Logger LOG =
LoggerFactory.getLogger(CommonHttpEdgeDispatcher.class);
+
+ private static final String KEY_ENABLED =
"servicecomb.http.dispatcher.edge.http.enabled";
+
+ private static final String KEY_ORDER =
"servicecomb.http.dispatcher.edge.http.order";
+
+ private static final String KEY_PATTERN =
"servicecomb.http.dispatcher.edge.http.pattern";
+
+ private static final String PATTERN_ANY = "/(.*)";
+
+ private static final String KEY_MAPPING_PREFIX =
"servicecomb.http.dispatcher.edge.http.mappings";
+
+ private Map<String, LoadBalancer> loadBalancerMap = new
ConcurrentHashMapEx<>();
+
+ private Map<String, URLMappedConfigurationItem> configurations = new
HashMap<>();
+
+ private DiscoveryTree discoveryTree;
+
+ public CommonHttpEdgeDispatcher() {
+ if (this.enabled()) {
+ loadConfigurations();
+ discoveryTree = new DiscoveryTree();
+ discoveryTree.addFilter(new ServerDiscoveryFilter());
+ }
+ }
+
+ @Override
+ public int getOrder() {
+ return DynamicPropertyFactory.getInstance().getIntProperty(KEY_ORDER,
40_000).get();
+ }
+
+ @Override
+ public boolean enabled() {
+ return
DynamicPropertyFactory.getInstance().getBooleanProperty(KEY_ENABLED,
false).get();
+ }
+
+ @Override
+ public void init(Router router) {
+ String pattern =
DynamicPropertyFactory.getInstance().getStringProperty(KEY_PATTERN,
PATTERN_ANY).get();
+
router.routeWithRegex(pattern).failureHandler(this::onFailure).handler(this::onRequest);
+ }
+
+ private void loadConfigurations() {
+ ConcurrentCompositeConfiguration config =
(ConcurrentCompositeConfiguration) DynamicPropertyFactory
+ .getBackingConfigurationSource();
+ configurations = URLMappedConfigurationLoader.loadConfigurations(config,
KEY_MAPPING_PREFIX);
+ config.addConfigurationListener(event -> {
+ if (event.getPropertyName().startsWith(KEY_MAPPING_PREFIX)) {
+ LOG.info("Map rule have been changed. Reload configurations. Event=" +
event.getType());
+ configurations =
URLMappedConfigurationLoader.loadConfigurations(config, KEY_MAPPING_PREFIX);
+ }
+ });
+ }
+
+ @SuppressWarnings("deprecation")
+ protected void onRequest(RoutingContext context) {
+ URLMappedConfigurationItem configurationItem =
findConfigurationItem(context.request().uri());
+ if (configurationItem == null) {
+ context.next();
+ return;
+ }
+
+ String uri = Utils.findActualPath(context.request().uri(),
configurationItem.getPrefixSegmentCount());
+
+ Invocation invocation = new Invocation() {
+ @Override
+ public String getConfigTransportName() {
+ return "rest";
+ }
+ };
+
+ LoadBalancer loadBalancer = getOrCreateLoadBalancer(invocation,
configurationItem.getMicroserviceName(),
+ configurationItem.getVersionRule());
+ ServiceCombServer server = loadBalancer.chooseServer(invocation);
+ URIEndpointObject endpointObject = new
URIEndpointObject(server.getEndpoint().getEndpoint());
+
+ RequestOptions requestOptions = new RequestOptions();
+ requestOptions.setHost(endpointObject.getHostOrIp())
+ .setPort(endpointObject.getPort())
+ .setSsl(endpointObject.isSslEnabled())
+ .setURI(uri);
+
+ // TODO: now use registry client, after next PR transport client is fixed,
using that.
+ HttpClient httpClient = HttpClients.getClient("registry").getHttpClient();
+ HttpClientRequest httpClientRequest = httpClient
+ .request(context.request().method(), requestOptions,
httpClientResponse -> {
+ context.response().setStatusCode(httpClientResponse.statusCode());
+ httpClientResponse.headers().forEach((header) -> {
+ // any headers need to exclude can add here
+// if ("Content-Length".equalsIgnoreCase(header.getKey())) {
+// return;
+// }
+ context.response().headers().set(header.getKey(),
header.getValue());
+ });
+ httpClientResponse.handler(data -> {
+ context.response().write(data);
+ });
+ httpClientResponse.endHandler((v) -> context.response().end());
+ });
+ context.request().headers().forEach((header) -> {
+ // any headers need to exclude can add here
+// if ("Content-Length".equalsIgnoreCase(header.getKey())) {
+// return;
+// }
+ httpClientRequest.headers().set(header.getKey(), header.getValue());
+ });
+ context.request().handler(data -> httpClientRequest.write(data));
+ context.request().endHandler((v) -> httpClientRequest.end());
+ }
+
+ protected LoadBalancer getOrCreateLoadBalancer(Invocation invocation, String
microserviceName, String versionRule) {
+ DiscoveryContext context = new DiscoveryContext();
+ context.setInputParameters(invocation);
+ VersionedCache serversVersionedCache = discoveryTree.discovery(context,
+ RegistryUtils.getAppId(),
+ microserviceName,
+ versionRule);
+ invocation.addLocalContext(LoadbalanceHandler.CONTEXT_KEY_SERVER_LIST,
serversVersionedCache.data());
+ return loadBalancerMap
+ .computeIfAbsent(microserviceName, name ->
createLoadBalancer(microserviceName));
+ }
+
+ private LoadBalancer createLoadBalancer(String microserviceName) {
+ RuleExt rule = ExtensionsManager.createLoadBalancerRule(microserviceName);
+ return new LoadBalancer(rule, microserviceName);
+ }
+
+ private URLMappedConfigurationItem findConfigurationItem(String path) {
+ for (URLMappedConfigurationItem item : configurations.values()) {
+ if (item.getPattern().matcher(path).matches()) {
+ return item;
+ }
+ }
+ return null;
+ }
+}
diff --git
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java
index 7bb1a88..014c1e9 100644
---
a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java
+++
b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java
@@ -20,6 +20,7 @@ package org.apache.servicecomb.edge.core;
import java.util.HashMap;
import java.util.Map;
+import org.apache.servicecomb.transport.rest.vertx.RestBodyHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,6 +36,8 @@ import io.vertx.ext.web.RoutingContext;
public class URLMappedEdgeDispatcher extends AbstractEdgeDispatcher {
private static final Logger LOG =
LoggerFactory.getLogger(URLMappedEdgeDispatcher.class);
+ public static final String CONFIGURATION_ITEM = "URLMappedConfigurationItem";
+
private static final String PATTERN_ANY = "/(.*)";
private static final String KEY_ORDER =
"servicecomb.http.dispatcher.edge.url.order";
@@ -67,8 +70,10 @@ public class URLMappedEdgeDispatcher extends
AbstractEdgeDispatcher {
public void init(Router router) {
// cookies handler are enabled by default start from 3.8.3
String pattern =
DynamicPropertyFactory.getInstance().getStringProperty(KEY_PATTERN,
PATTERN_ANY).get();
- router.routeWithRegex(pattern).handler(createBodyHandler());
-
router.routeWithRegex(pattern).failureHandler(this::onFailure).handler(this::onRequest);
+ router.routeWithRegex(pattern).failureHandler(this::onFailure)
+ .handler(this::preCheck)
+ .handler(createBodyHandler())
+ .handler(this::onRequest);
}
private void loadConfigurations() {
@@ -83,14 +88,29 @@ public class URLMappedEdgeDispatcher extends
AbstractEdgeDispatcher {
});
}
-
- protected void onRequest(RoutingContext context) {
+ protected void preCheck(RoutingContext context) {
URLMappedConfigurationItem configurationItem =
findConfigurationItem(context.request().path());
if (configurationItem == null) {
+ // by pass body handler flag
+ context.put(RestBodyHandler.BYPASS_BODY_HANDLER, Boolean.TRUE);
+ context.next();
+ return;
+ }
+ context.put(CONFIGURATION_ITEM, configurationItem);
+ context.next();
+ }
+
+ protected void onRequest(RoutingContext context) {
+ Boolean bypass = context.get(RestBodyHandler.BYPASS_BODY_HANDLER);
+ if (Boolean.TRUE.equals(bypass)) {
+ // clear flag
+ context.put(RestBodyHandler.BYPASS_BODY_HANDLER, Boolean.FALSE);
context.next();
return;
}
+ URLMappedConfigurationItem configurationItem =
context.get(CONFIGURATION_ITEM);
+
String path = Utils.findActualPath(context.request().path(),
configurationItem.getPrefixSegmentCount());
EdgeInvocation edgeInvocation = createEdgeInvocation();
diff --git
a/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
b/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
index d81bb22..0f1d5d6 100644
---
a/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
+++
b/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher
@@ -16,4 +16,5 @@
#
org.apache.servicecomb.edge.core.DefaultEdgeDispatcher
-org.apache.servicecomb.edge.core.URLMappedEdgeDispatcher
\ No newline at end of file
+org.apache.servicecomb.edge.core.URLMappedEdgeDispatcher
+org.apache.servicecomb.edge.core.CommonHttpEdgeDispatcher
\ No newline at end of file
diff --git
a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java
b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java
index b575c2d..a209a40 100644
---
a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java
+++
b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java
@@ -20,6 +20,7 @@ package org.apache.servicecomb.edge.core;
import java.util.Map;
import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
+import org.apache.servicecomb.transport.rest.vertx.RestBodyHandler;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -54,6 +55,8 @@ public class TestURLMappedEdgeDispatcher {
new Expectations() {
{
+ context.get(RestBodyHandler.BYPASS_BODY_HANDLER);
+ result = Boolean.TRUE;
context.next();
}
};
@@ -81,8 +84,14 @@ public class TestURLMappedEdgeDispatcher {
Assert.assertEquals(item.getStringPattern(), "/a/b/c/.*");
Assert.assertEquals(item.getVersionRule(), "2.0.0+");
+ URLMappedConfigurationItem finalItem = item;
new Expectations() {
{
+ context.get(RestBodyHandler.BYPASS_BODY_HANDLER);
+ result = Boolean.FALSE;
+ context.get(URLMappedEdgeDispatcher.CONFIGURATION_ITEM);
+ result = finalItem;
+
context.request();
result = requst;
requst.path();
diff --git
a/integration-tests/springmvc-tests/common/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcIntegrationTestBase.java
b/integration-tests/springmvc-tests/common/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcIntegrationTestBase.java
index 12a1356..e2a4efb 100644
---
a/integration-tests/springmvc-tests/common/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcIntegrationTestBase.java
+++
b/integration-tests/springmvc-tests/common/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcIntegrationTestBase.java
@@ -68,6 +68,7 @@ import org.springframework.util.MultiValueMap;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.UnknownHttpStatusCodeException;
@@ -639,9 +640,9 @@ public class SpringMvcIntegrationTestBase {
restTemplate
.getForEntity(controllerUrl + "sayhi?name=throwexception",
String.class);
assertFalse(true);
- } catch (UnknownHttpStatusCodeException e) {
- assertThat(e.getRawStatusCode(), is(590));
- assertThat(e.getResponseBodyAsString(), is("{\"message\":\"Cse Internal
Server Error\"}"));
+ } catch (HttpServerErrorException e) {
+ assertThat(e.getRawStatusCode(), is(500));
+ assertThat(e.getResponseBodyAsString(), is("{\"message\":\"Unexpected
exception when processing the request.\"}"));
}
}
diff --git
a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionToProducerResponseConverter.java
b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionToProducerResponseConverter.java
index a74b471..5bb9382 100644
---
a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionToProducerResponseConverter.java
+++
b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionToProducerResponseConverter.java
@@ -19,6 +19,12 @@ package org.apache.servicecomb.swagger.invocation.exception;
import org.apache.servicecomb.swagger.invocation.Response;
import org.apache.servicecomb.swagger.invocation.SwaggerInvocation;
+/**
+ * ExceptionToProducerResponseConverter are used to convert provider
Exceptions to user friendly messages.
+ * They are called when :
+ * 1. exception happens executing business logic
+ * 2. exception happens in ProducerInvokeExtension.beforeMethodInvoke(e.g.
parameter validation)
+ */
public interface ExceptionToProducerResponseConverter<T extends Throwable> {
Class<T> getExceptionClass();
diff --git
a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java
b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java
index 257d95d..be4e774 100644
---
a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java
+++
b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java
@@ -60,15 +60,21 @@ public class RestBodyHandler implements BodyHandler {
private static final String BODY_HANDLED = "__body-handled";
private long bodyLimit = DEFAULT_BODY_LIMIT;
+
private boolean handleFileUploads;
+
private String uploadsDir;
private boolean mergeFormAttributes = DEFAULT_MERGE_FORM_ATTRIBUTES;
private boolean deleteUploadedFilesOnEnd =
DEFAULT_DELETE_UPLOADED_FILES_ON_END;
+
private boolean isPreallocateBodyBuffer = DEFAULT_PREALLOCATE_BODY_BUFFER;
+
private static final int DEFAULT_INITIAL_BODY_BUFFER_SIZE = 1024; //bytes
+ public static final String BYPASS_BODY_HANDLER = "__bypass_body_handler";
+
public RestBodyHandler() {
this(true, DEFAULT_UPLOADS_DIRECTORY);
}
@@ -94,6 +100,12 @@ public class RestBodyHandler implements BodyHandler {
return;
}
+ Boolean bypass = context.get(BYPASS_BODY_HANDLER);
+ if (Boolean.TRUE.equals(bypass)) {
+ context.next();
+ return;
+ }
+
// we need to keep state since we can be called again on reroute
Boolean handled = context.get(BODY_HANDLED);
if (handled == null || !handled) {
@@ -150,14 +162,13 @@ public class RestBodyHandler implements BodyHandler {
private long parseContentLengthHeader(HttpServerRequest request) {
String contentLength = request.getHeader(HttpHeaders.CONTENT_LENGTH);
- if(contentLength == null || contentLength.isEmpty()) {
+ if (contentLength == null || contentLength.isEmpty()) {
return -1;
}
try {
long parsedContentLength = Long.parseLong(contentLength);
- return parsedContentLength < 0 ? null : parsedContentLength;
- }
- catch (NumberFormatException ex) {
+ return parsedContentLength < 0 ? null : parsedContentLength;
+ } catch (NumberFormatException ex) {
return -1;
}
}
@@ -246,18 +257,16 @@ public class RestBodyHandler implements BodyHandler {
private void initBodyBuffer(long contentLength) {
int initialBodyBufferSize;
- if(contentLength < 0) {
+ if (contentLength < 0) {
initialBodyBufferSize = DEFAULT_INITIAL_BODY_BUFFER_SIZE;
- }
- else if(contentLength > MAX_PREALLOCATED_BODY_BUFFER_BYTES) {
+ } else if (contentLength > MAX_PREALLOCATED_BODY_BUFFER_BYTES) {
initialBodyBufferSize = MAX_PREALLOCATED_BODY_BUFFER_BYTES;
- }
- else {
+ } else {
initialBodyBufferSize = (int) contentLength;
}
- if(bodyLimit != -1) {
- initialBodyBufferSize = (int)Math.min(initialBodyBufferSize,
bodyLimit);
+ if (bodyLimit != -1) {
+ initialBodyBufferSize = (int) Math.min(initialBodyBufferSize,
bodyLimit);
}
this.body = Buffer.buffer(initialBodyBufferSize);