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/04/22 08:32:29 UTC

[incubator-servicecomb-java-chassis] 02/09: [SCB-486] refactoring VertxHttpMethod to RestClientInvocation: move method parameters to be fields, not change any logic

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 15b85a5d0b0ae0457be40b5f93bc894b4d440bd5
Author: wujimin <wu...@huawei.com>
AuthorDate: Sat Apr 21 21:17:29 2018 +0800

    [SCB-486] refactoring VertxHttpMethod to RestClientInvocation: move method parameters to be fields, not change any logic
---
 .../test/scaffolding/log/LogCollector.java         |   6 +
 .../transport-rest/transport-rest-client/pom.xml   |  66 ++---
 .../transport/rest/client/RestTransportClient.java |  16 +-
 ...txHttpMethod.java => RestClientInvocation.java} |  97 ++++---
 .../rest/client/http/TestRestClientInvocation.java | 302 +++++++++++++++++++++
 .../rest/client/http/TestVertxHttpMethod.java      | 302 ---------------------
 6 files changed, 407 insertions(+), 382 deletions(-)

diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java
index 33e5808..53359e3 100644
--- a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java
+++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java
@@ -21,6 +21,7 @@ import java.util.List;
 
 import org.apache.log4j.Appender;
 import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
 import org.apache.log4j.spi.LoggingEvent;
 
@@ -48,6 +49,11 @@ public class LogCollector {
     Logger.getRootLogger().addAppender(appender);
   }
 
+  public LogCollector setLogLevel(String logName, Level level) {
+    Logger.getLogger(logName).setLevel(level);
+    return this;
+  }
+
   public List<LoggingEvent> getEvents() {
     return events;
   }
diff --git a/transports/transport-rest/transport-rest-client/pom.xml b/transports/transport-rest/transport-rest-client/pom.xml
index d7e2996..d7f0363 100644
--- a/transports/transport-rest/transport-rest-client/pom.xml
+++ b/transports/transport-rest/transport-rest-client/pom.xml
@@ -16,36 +16,40 @@
   ~ limitations under the License.
   -->
 
-<project
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
-	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-	<modelVersion>4.0.0</modelVersion>
-	<parent>
-		<groupId>org.apache.servicecomb</groupId>
-		<artifactId>transport-rest</artifactId>
-		<version>1.0.0-m2-SNAPSHOT</version>
-	</parent>
-	<artifactId>transport-rest-client</artifactId>
-	<name>Java Chassis::Transports::Rest::Client</name>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+  xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.servicecomb</groupId>
+    <artifactId>transport-rest</artifactId>
+    <version>1.0.0-m2-SNAPSHOT</version>
+  </parent>
+  <artifactId>transport-rest-client</artifactId>
+  <name>Java Chassis::Transports::Rest::Client</name>
 
-	<dependencies>
-		<dependency>
-			<groupId>org.apache.servicecomb</groupId>
-			<artifactId>foundation-vertx</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.servicecomb</groupId>
-			<artifactId>common-rest</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.slf4j</groupId>
-			<artifactId>slf4j-log4j12</artifactId>
-			<scope>test</scope>
-		</dependency>
-		<dependency>
-			<groupId>log4j</groupId>
-			<artifactId>log4j</artifactId>
-			<scope>test</scope>
-		</dependency>
-	</dependencies>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.servicecomb</groupId>
+      <artifactId>foundation-vertx</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.servicecomb</groupId>
+      <artifactId>common-rest</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.servicecomb</groupId>
+      <artifactId>foundation-test-scaffolding</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
 </project>
diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestTransportClient.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestTransportClient.java
index 1fed065..937097a 100644
--- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestTransportClient.java
+++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestTransportClient.java
@@ -17,7 +17,11 @@
 
 package org.apache.servicecomb.transport.rest.client;
 
+import java.util.List;
+
+import org.apache.servicecomb.common.rest.filter.HttpClientFilter;
 import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
 import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder;
 import org.apache.servicecomb.foundation.vertx.VertxUtils;
 import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager;
@@ -25,7 +29,7 @@ import org.apache.servicecomb.foundation.vertx.client.ClientVerticle;
 import org.apache.servicecomb.foundation.vertx.client.http.HttpClientPoolFactory;
 import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext;
 import org.apache.servicecomb.swagger.invocation.AsyncResponse;
-import org.apache.servicecomb.transport.rest.client.http.VertxHttpMethod;
+import org.apache.servicecomb.transport.rest.client.http.RestClientInvocation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,8 +44,11 @@ public final class RestTransportClient {
 
   private ClientPoolManager<HttpClientWithContext> clientMgr;
 
+  private List<HttpClientFilter> httpClientFilters;
 
   public void init(Vertx vertx) throws Exception {
+    httpClientFilters = SPIServiceUtils.getSortedService(HttpClientFilter.class);
+
     HttpClientOptions httpClientOptions = createHttpClientOptions();
     clientMgr = new ClientPoolManager<>(vertx, new HttpClientPoolFactory(httpClientOptions));
 
@@ -61,11 +68,12 @@ public final class RestTransportClient {
     return httpClientOptions;
   }
 
-  public void send(Invocation invocation, AsyncResponse asyncResp) throws Exception {
+  public void send(Invocation invocation, AsyncResponse asyncResp) {
     HttpClientWithContext httpClientWithContext = clientMgr.findClientPool(invocation.isSync());
+    RestClientInvocation restClientInvocation = new RestClientInvocation(httpClientWithContext, httpClientFilters);
     try {
-      VertxHttpMethod.INSTANCE.doMethod(httpClientWithContext, invocation, asyncResp);
-    } catch (Exception e) {
+      restClientInvocation.invoke(invocation, asyncResp);
+    } catch (Throwable e) {
       asyncResp.fail(invocation.getInvocationType(), e);
       LOGGER.error("vertx rest transport send error.", e);
     }
diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/http/VertxHttpMethod.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/http/RestClientInvocation.java
similarity index 69%
rename from transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/http/VertxHttpMethod.java
rename to transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/http/RestClientInvocation.java
index d4f2aa1..58789a6 100644
--- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/http/VertxHttpMethod.java
+++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/http/RestClientInvocation.java
@@ -29,7 +29,6 @@ import org.apache.servicecomb.core.transport.AbstractTransport;
 import org.apache.servicecomb.foundation.common.net.IpPort;
 import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
 import org.apache.servicecomb.foundation.common.utils.JsonUtils;
-import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
 import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext;
 import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
 import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
@@ -43,33 +42,42 @@ import org.slf4j.LoggerFactory;
 import org.springframework.util.StringUtils;
 
 import io.vertx.core.buffer.Buffer;
-import io.vertx.core.http.HttpClient;
 import io.vertx.core.http.HttpClientRequest;
 import io.vertx.core.http.HttpClientResponse;
 import io.vertx.core.http.HttpMethod;
 import io.vertx.core.http.RequestOptions;
 
-public class VertxHttpMethod {
-  private static final Logger LOGGER = LoggerFactory.getLogger(VertxHttpMethod.class);
+public class RestClientInvocation {
+  private static final Logger LOGGER = LoggerFactory.getLogger(RestClientInvocation.class);
 
-  public static final VertxHttpMethod INSTANCE = new VertxHttpMethod();
+  private HttpClientWithContext httpClientWithContext;
 
-  static List<HttpClientFilter> httpClientFilters = SPIServiceUtils.getSortedService(HttpClientFilter.class);
+  private Invocation invocation;
+
+  private AsyncResponse asyncResp;
+
+  private List<HttpClientFilter> httpClientFilters;
+
+  private HttpClientRequest clientRequest;
+
+  private HttpClientResponse clientResponse;
+
+  public RestClientInvocation(HttpClientWithContext httpClientWithContext, List<HttpClientFilter> httpClientFilters) {
+    this.httpClientWithContext = httpClientWithContext;
+    this.httpClientFilters = httpClientFilters;
+  }
+
+  public void invoke(Invocation invocation, AsyncResponse asyncResp) throws Exception {
+    this.invocation = invocation;
+    this.asyncResp = asyncResp;
 
-  public void doMethod(HttpClientWithContext httpClientWithContext, Invocation invocation,
-      AsyncResponse asyncResp) throws Exception {
     OperationMeta operationMeta = invocation.getOperationMeta();
     RestOperationMeta swaggerRestOperation = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION);
 
-    String path = this.createRequestPath(invocation, swaggerRestOperation);
+    String path = this.createRequestPath(swaggerRestOperation);
     IpPort ipPort = (IpPort) invocation.getEndpoint().getAddress();
 
-    HttpClientRequest clientRequest =
-        this.createRequest(httpClientWithContext.getHttpClient(),
-            invocation,
-            ipPort,
-            path,
-            asyncResp);
+    createRequest(ipPort, path);
     clientRequest.putHeader(org.apache.servicecomb.core.Const.TARGET_MICROSERVICE, invocation.getMicroserviceName());
     RestClientRequestImpl restClientRequest =
         new RestClientRequestImpl(clientRequest, httpClientWithContext.context().owner(), asyncResp);
@@ -88,7 +96,7 @@ public class VertxHttpMethod {
 
     // 从业务线程转移到网络线程中去发送
     httpClientWithContext.runOnContext(httpClient -> {
-      this.setCseContext(invocation, clientRequest);
+      this.setCseContext();
       clientRequest.setTimeout(AbstractTransport.getRequestTimeoutProperty().get());
       try {
         restClientRequest.end();
@@ -99,15 +107,14 @@ public class VertxHttpMethod {
     });
   }
 
-  private HttpMethod getMethod(Invocation invocation) {
+  private HttpMethod getMethod() {
     OperationMeta operationMeta = invocation.getOperationMeta();
     RestOperationMeta swaggerRestOperation = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION);
     String method = swaggerRestOperation.getHttpMethod();
     return HttpMethod.valueOf(method);
   }
 
-  HttpClientRequest createRequest(HttpClient client, Invocation invocation, IpPort ipPort, String path,
-      AsyncResponse asyncResp) {
+  void createRequest(IpPort ipPort, String path) {
     URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress();
     RequestOptions requestOptions = new RequestOptions();
     requestOptions.setHost(ipPort.getHostOrIp())
@@ -115,51 +122,51 @@ public class VertxHttpMethod {
         .setSsl(endpoint.isSslEnabled())
         .setURI(path);
 
-    HttpMethod method = getMethod(invocation);
+    HttpMethod method = getMethod();
     LOGGER.debug("Sending request by rest, method={}, qualifiedName={}, path={}, endpoint={}.",
         method,
         invocation.getMicroserviceQualifiedName(),
         path,
         invocation.getEndpoint().getEndpoint());
-    HttpClientRequest request = client.request(method, requestOptions, response -> {
-      handleResponse(invocation, response, asyncResp);
-    });
-    return request;
+    clientRequest = httpClientWithContext.getHttpClient().request(method, requestOptions, this::handleResponse);
   }
 
-  void handleResponse(Invocation invocation, HttpClientResponse clientResponse,
-      AsyncResponse asyncResp) {
+  protected void handleResponse(HttpClientResponse httpClientResponse) {
+    this.clientResponse = httpClientResponse;
+
     clientResponse.bodyHandler(responseBuf -> {
-      // 此时是在网络线程中,不应该就地处理,通过dispatcher转移线程
-      invocation.getResponseExecutor().execute(() -> {
-        try {
-          HttpServletResponseEx responseEx =
-              new VertxClientResponseToHttpServletResponse(clientResponse, responseBuf);
-          for (HttpClientFilter filter : httpClientFilters) {
-            Response response = filter.afterReceiveResponse(invocation, responseEx);
-            if (response != null) {
-              asyncResp.complete(response);
-              return;
-            }
+      processResponseBody(responseBuf);
+    });
+  }
+
+  protected void processResponseBody(Buffer responseBuf) {
+    invocation.getResponseExecutor().execute(() -> {
+      try {
+        HttpServletResponseEx responseEx =
+            new VertxClientResponseToHttpServletResponse(clientResponse, responseBuf);
+        for (HttpClientFilter filter : httpClientFilters) {
+          Response response = filter.afterReceiveResponse(invocation, responseEx);
+          if (response != null) {
+            asyncResp.complete(response);
+            return;
           }
-        } catch (Throwable e) {
-          asyncResp.fail(invocation.getInvocationType(), e);
         }
-      });
+      } catch (Throwable e) {
+        asyncResp.fail(invocation.getInvocationType(), e);
+      }
     });
   }
 
-  protected void setCseContext(Invocation invocation, HttpClientRequest request) {
+  protected void setCseContext() {
     try {
       String cseContext = JsonUtils.writeValueAsString(invocation.getContext());
-      request.putHeader(org.apache.servicecomb.core.Const.CSE_CONTEXT, cseContext);
+      clientRequest.putHeader(org.apache.servicecomb.core.Const.CSE_CONTEXT, cseContext);
     } catch (Exception e) {
-      LOGGER.debug(e.toString());
+      LOGGER.debug("Failed to encode and set cseContext.", e);
     }
   }
 
-  protected String createRequestPath(Invocation invocation,
-      RestOperationMeta swaggerRestOperation) throws Exception {
+  protected String createRequestPath(RestOperationMeta swaggerRestOperation) throws Exception {
     URIEndpointObject address = (URIEndpointObject) invocation.getEndpoint().getAddress();
     String urlPrefix = address.getFirst(Const.URL_PREFIX);
 
diff --git a/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/http/TestRestClientInvocation.java b/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/http/TestRestClientInvocation.java
new file mode 100644
index 0000000..5b5f4a1
--- /dev/null
+++ b/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/http/TestRestClientInvocation.java
@@ -0,0 +1,302 @@
+/*
+ * 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.transport.rest.client.http;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Level;
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.common.rest.definition.RestOperationMeta;
+import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder;
+import org.apache.servicecomb.common.rest.filter.HttpClientFilter;
+import org.apache.servicecomb.core.Endpoint;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.core.executor.ReactiveExecutor;
+import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
+import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector;
+import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext;
+import org.apache.servicecomb.serviceregistry.api.Const;
+import org.apache.servicecomb.swagger.invocation.AsyncResponse;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
+import org.hamcrest.Matchers;
+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.Handler;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.http.HttpClient;
+import io.vertx.core.http.HttpClientRequest;
+import io.vertx.core.http.HttpClientResponse;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.core.http.RequestOptions;
+import mockit.Deencapsulation;
+import mockit.Mock;
+import mockit.MockUp;
+import mockit.Mocked;
+
+public class TestRestClientInvocation {
+  Handler<Throwable> exceptionHandler;
+
+  Handler<Buffer> bodyHandler;
+
+  Map<String, String> headers = new HashMap<>();
+
+  HttpClientRequest request = mock(HttpClientRequest.class);
+
+  HttpClient httpClient = mock(HttpClient.class);
+
+  Context context = mock(Context.class);
+
+  HttpClientWithContext httpClientWithContext = new HttpClientWithContext(httpClient, context);
+
+  Invocation invocation = mock(Invocation.class);
+
+  Response response;
+
+  AsyncResponse asyncResp = resp -> {
+    response = resp;
+  };
+
+  OperationMeta operationMeta = mock(OperationMeta.class);
+
+  Endpoint endpoint = mock(Endpoint.class);
+
+  RestOperationMeta swaggerRestOperation = mock(RestOperationMeta.class);
+
+  URLPathBuilder urlPathBuilder = mock(URLPathBuilder.class);
+
+  URIEndpointObject address = mock(URIEndpointObject.class);
+
+  List<HttpClientFilter> httpClientFilters = new ArrayList<>();
+
+  RestClientInvocation restClientInvocation = new RestClientInvocation(httpClientWithContext, httpClientFilters);
+
+  Map<String, Object> handlerContext = new HashMap<>();
+
+  @SuppressWarnings("unchecked")
+  @Before
+  public void setup() {
+    Deencapsulation.setField(restClientInvocation, "clientRequest", request);
+    Deencapsulation.setField(restClientInvocation, "invocation", invocation);
+    Deencapsulation.setField(restClientInvocation, "asyncResp", asyncResp);
+
+    when(invocation.getOperationMeta()).thenReturn(operationMeta);
+    when(swaggerRestOperation.getPathBuilder()).thenReturn(urlPathBuilder);
+    when(swaggerRestOperation.getHttpMethod()).thenReturn("GET");
+    when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerRestOperation);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getAddress()).thenReturn(address);
+    when(invocation.getHandlerContext()).then(answer -> handlerContext);
+    when(httpClient.request((HttpMethod) Mockito.any(), (RequestOptions) Mockito.any(), Mockito.any()))
+        .thenReturn(request);
+    doAnswer(a -> {
+      exceptionHandler = (Handler<Throwable>) a.getArguments()[0];
+      return request;
+    }).when(request).exceptionHandler(any());
+    doAnswer(a -> {
+      headers.put(a.getArgumentAt(0, String.class), a.getArgumentAt(1, String.class));
+      return request;
+    }).when(request).putHeader((String) any(), (String) any());
+    doAnswer(a -> {
+      ((Handler<Void>) a.getArguments()[0]).handle(null);
+      return null;
+    }).when(context).runOnContext(any());
+  }
+
+  @Test
+  public void invoke(@Mocked Response resp) throws Exception {
+    doAnswer(a -> {
+      asyncResp.complete(resp);
+      return null;
+    }).when(request).end();
+    restClientInvocation.invoke(invocation, asyncResp);
+
+    Assert.assertSame(resp, response);
+  }
+
+  @Test
+  public void invoke_endThrow() throws Exception {
+    Mockito.doThrow(Error.class).when(request).end();
+    restClientInvocation.invoke(invocation, asyncResp);
+
+    Assert.assertThat(((InvocationException) response.getResult()).getCause(), Matchers.instanceOf(Error.class));
+  }
+
+  @Test
+  public void invoke_requestThrow() throws Exception {
+    Throwable t = new Error();
+    doAnswer(a -> {
+      exceptionHandler.handle(t);
+      return null;
+    }).when(request).end();
+    restClientInvocation.invoke(invocation, asyncResp);
+    restClientInvocation.invoke(invocation, asyncResp);
+
+    Assert.assertThat(((InvocationException) response.getResult()).getCause(), Matchers.sameInstance(t));
+  }
+
+  @Test
+  public void testSetCseContext() {
+    Map<String, String> contextMap = Collections.singletonMap("k", "v");
+    when(invocation.getContext()).thenReturn(contextMap);
+
+    restClientInvocation.setCseContext();
+
+    Assert.assertEquals("{x-cse-context={\"k\":\"v\"}}", headers.toString());
+  }
+
+  @Test
+  public void testSetCseContext_failed() {
+    LogCollector logCollector = new LogCollector();
+    logCollector.setLogLevel(RestClientInvocation.class.getName(), Level.DEBUG);
+    Deencapsulation.setField(restClientInvocation, "invocation", null);
+
+    restClientInvocation.setCseContext();
+
+    Assert.assertEquals("Failed to encode and set cseContext.", logCollector.getEvents().get(0).getMessage());
+    logCollector.teardown();
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void handleResponse() {
+    HttpClientResponse httpClientResponse = mock(HttpClientResponse.class);
+    doAnswer(a -> {
+      bodyHandler = (Handler<Buffer>) a.getArguments()[0];
+      return httpClientResponse;
+    }).when(httpClientResponse).bodyHandler(any());
+
+    Buffer buf = Buffer.buffer();
+    new MockUp<RestClientInvocation>(restClientInvocation) {
+      @Mock
+      void processResponseBody(Buffer responseBuf) {
+        asyncResp.success(buf);
+      }
+    };
+
+    restClientInvocation.handleResponse(httpClientResponse);
+    bodyHandler.handle(buf);
+
+    Assert.assertSame(buf, response.getResult());
+  }
+
+  @Test
+  public void processResponseBody() {
+    Response resp = Response.ok(null);
+
+    HttpClientResponse httpClientResponse = mock(HttpClientResponse.class);
+    Deencapsulation.setField(restClientInvocation, "clientResponse", httpClientResponse);
+
+    {
+      HttpClientFilter filter = mock(HttpClientFilter.class);
+      when(filter.afterReceiveResponse(any(), any())).thenReturn(null);
+      httpClientFilters.add(filter);
+    }
+    {
+      HttpClientFilter filter = mock(HttpClientFilter.class);
+      when(filter.afterReceiveResponse(any(), any())).thenReturn(resp);
+      httpClientFilters.add(filter);
+    }
+
+    when(invocation.getResponseExecutor()).thenReturn(new ReactiveExecutor());
+
+    restClientInvocation.processResponseBody(null);
+
+    Assert.assertSame(resp, response);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void processResponseBody_throw() {
+    HttpClientResponse httpClientResponse = mock(HttpClientResponse.class);
+    Deencapsulation.setField(restClientInvocation, "clientResponse", httpClientResponse);
+
+    {
+      HttpClientFilter filter = mock(HttpClientFilter.class);
+      when(filter.afterReceiveResponse(any(), any())).thenThrow(Error.class);
+      httpClientFilters.add(filter);
+    }
+
+    when(invocation.getResponseExecutor()).thenReturn(new ReactiveExecutor());
+
+    restClientInvocation.processResponseBody(null);
+
+    Assert.assertThat(((InvocationException) response.getResult()).getCause(), Matchers.instanceOf(Error.class));
+  }
+
+  @Test
+  public void createRequestPath_NoUrlPrefixNoPath() throws Exception {
+    when(address.getFirst(Const.URL_PREFIX)).thenReturn(null);
+
+    when(urlPathBuilder.createRequestPath(any())).thenReturn("/path");
+
+    String path = restClientInvocation.createRequestPath(swaggerRestOperation);
+    Assert.assertEquals("/path", path);
+  }
+
+  @Test
+  public void createRequestPath_noUrlPrefixHavePath() throws Exception {
+    handlerContext.put(RestConst.REST_CLIENT_REQUEST_PATH, "/client/path");
+    when(address.getFirst(Const.URL_PREFIX)).thenReturn(null);
+
+    String path = restClientInvocation.createRequestPath(swaggerRestOperation);
+    Assert.assertEquals("/client/path", path);
+  }
+
+  @Test
+  public void createRequestPath_haveUrlPrefixNoPath() throws Exception {
+    when(address.getFirst(Const.URL_PREFIX)).thenReturn("/prefix");
+
+    when(urlPathBuilder.createRequestPath(any())).thenReturn("/path");
+
+    String path = restClientInvocation.createRequestPath(swaggerRestOperation);
+    Assert.assertEquals("/prefix/path", path);
+  }
+
+  @Test
+  public void createRequestPath_haveUrlPrefixHavePath() throws Exception {
+    when(address.getFirst(Const.URL_PREFIX)).thenReturn("/prefix");
+    handlerContext.put(RestConst.REST_CLIENT_REQUEST_PATH, "/client/path");
+
+    String path = restClientInvocation.createRequestPath(swaggerRestOperation);
+    Assert.assertEquals("/prefix/client/path", path);
+  }
+
+  @Test
+  public void createRequestPath_haveUrlPrefixHavePathAndStartWith() throws Exception {
+    when(address.getFirst(Const.URL_PREFIX)).thenReturn("/prefix");
+    handlerContext.put(RestConst.REST_CLIENT_REQUEST_PATH, "/prefix/client/path");
+
+    String path = restClientInvocation.createRequestPath(swaggerRestOperation);
+    Assert.assertEquals("/prefix/client/path", path);
+  }
+}
diff --git a/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/http/TestVertxHttpMethod.java b/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/http/TestVertxHttpMethod.java
deleted file mode 100644
index 1c9dcfe..0000000
--- a/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/http/TestVertxHttpMethod.java
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * 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.transport.rest.client.http;
-
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.servicecomb.common.rest.RestConst;
-import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor;
-import org.apache.servicecomb.common.rest.definition.RestOperationMeta;
-import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder;
-import org.apache.servicecomb.core.Endpoint;
-import org.apache.servicecomb.core.Invocation;
-import org.apache.servicecomb.core.definition.OperationMeta;
-import org.apache.servicecomb.foundation.common.net.IpPort;
-import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
-import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext;
-import org.apache.servicecomb.serviceregistry.api.Const;
-import org.apache.servicecomb.swagger.invocation.AsyncResponse;
-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.Handler;
-import io.vertx.core.http.HttpClient;
-import io.vertx.core.http.HttpClientRequest;
-import io.vertx.core.http.HttpClientResponse;
-import io.vertx.core.http.HttpMethod;
-import mockit.Expectations;
-import mockit.Injectable;
-import mockit.Mock;
-import mockit.MockUp;
-import mockit.Mocked;
-
-public class TestVertxHttpMethod extends VertxHttpMethod {
-
-  HttpClientRequest request;
-
-  @Before
-  public void setup() {
-    request = mock(HttpClientRequest.class);
-  }
-
-  @Test
-  public void testDoMethodNullPointerException(@Mocked HttpClient httpClient) throws Exception {
-    Context context = new MockUp<Context>() {
-      @Mock
-      public void runOnContext(Handler<Void> action) {
-        action.handle(null);
-      }
-    }.getMockInstance();
-    HttpClientWithContext httpClientWithContext = new HttpClientWithContext(httpClient, context);
-
-    Invocation invocation = mock(Invocation.class);
-    AsyncResponse asyncResp = mock(AsyncResponse.class);
-
-    try {
-      this.doMethod(httpClientWithContext, invocation, asyncResp);
-      fail("Expect to throw NullPointerException, but got none");
-    } catch (NullPointerException e) {
-    }
-  }
-
-  @Test
-  public void testDoMethod(@Mocked HttpClient httpClient, @Injectable URIEndpointObject address) throws Exception {
-    Context context = new MockUp<Context>() {
-      @Mock
-      public void runOnContext(Handler<Void> action) {
-        action.handle(null);
-      }
-    }.getMockInstance();
-    HttpClientWithContext httpClientWithContext = new HttpClientWithContext(httpClient, context);
-
-    Invocation invocation = mock(Invocation.class);
-    AsyncResponse asyncResp = mock(AsyncResponse.class);
-    OperationMeta operationMeta = mock(OperationMeta.class);
-    RestOperationMeta swaggerRestOperation = mock(RestOperationMeta.class);
-
-    Endpoint endpoint = mock(Endpoint.class);
-    when(invocation.getOperationMeta()).thenReturn(operationMeta);
-    URLPathBuilder urlPathBuilder = mock(URLPathBuilder.class);
-    when(swaggerRestOperation.getPathBuilder()).thenReturn(urlPathBuilder);
-    operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION);
-    when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerRestOperation);
-    when(invocation.getEndpoint()).thenReturn(endpoint);
-    when(endpoint.getAddress()).thenReturn(address);
-
-    when(request.exceptionHandler(Mockito.any())).then(answer -> null);
-    Map<String, Object> map = new HashMap<>();
-    when(invocation.getHandlerContext()).then(answer -> map);;
-
-    this.doMethod(httpClientWithContext, invocation, asyncResp);
-    Assert.assertTrue(true);
-  }
-
-  @Test
-  public void testCreateRequest() {
-    HttpClient client = mock(HttpClient.class);
-    Invocation invocation = mock(Invocation.class);
-    OperationMeta operationMeta = mock(OperationMeta.class);
-    Endpoint endpoint = mock(Endpoint.class);
-    URIEndpointObject address = mock(URIEndpointObject.class);
-    when(invocation.getEndpoint()).thenReturn(endpoint);
-    when(endpoint.getAddress()).thenReturn(address);
-    when(address.isSslEnabled()).thenReturn(false);
-    when(invocation.getOperationMeta()).thenReturn(operationMeta);
-    RestOperationMeta swaggerRestOperation = mock(RestOperationMeta.class);
-    when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerRestOperation);
-    IpPort ipPort = mock(IpPort.class);
-    when(ipPort.getPort()).thenReturn(10);
-    when(ipPort.getHostOrIp()).thenReturn("ever");
-    AsyncResponse asyncResp = mock(AsyncResponse.class);
-    List<HttpMethod> methods = new ArrayList<>(
-        Arrays.asList(HttpMethod.GET, HttpMethod.PUT, HttpMethod.POST, HttpMethod.DELETE, HttpMethod.PATCH));
-    for (HttpMethod method : methods) {
-      when(swaggerRestOperation.getHttpMethod()).thenReturn(method.toString());
-      HttpClientRequest obj =
-          VertxHttpMethod.INSTANCE.createRequest(client, invocation, ipPort, "good", asyncResp);
-      Assert.assertNull(obj);
-    }
-  }
-
-  @Test
-  public void testSetCseContext() {
-    boolean status = false;
-    try {
-      Invocation invocation = mock(Invocation.class);
-      HttpClientResponse httpResponse = mock(HttpClientResponse.class);
-      OperationMeta operationMeta = mock(OperationMeta.class);
-      RestOperationMeta swaggerRestOperation = mock(RestOperationMeta.class);
-      HttpClientRequest request = mock(HttpClientRequest.class);
-
-      Endpoint endpoint = mock(Endpoint.class);
-      when(invocation.getOperationMeta()).thenReturn(operationMeta);
-      URLPathBuilder urlPathBuilder = mock(URLPathBuilder.class);
-      when(swaggerRestOperation.getPathBuilder()).thenReturn(urlPathBuilder);
-      operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION);
-      when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerRestOperation);
-      when(invocation.getEndpoint()).thenReturn(endpoint);
-      String contentType = httpResponse.getHeader("Content-Type");
-      ProduceProcessor produceProcessor = mock(ProduceProcessor.class);
-      when(swaggerRestOperation.findProduceProcessor(contentType)).thenReturn(produceProcessor);
-      this.setCseContext(invocation, request);
-    } catch (Exception ex) {
-      status = true;
-    }
-    Assert.assertFalse(status);
-  }
-
-  @Test
-  public void testHandleResponse() {
-    boolean status = false;
-    try {
-      Invocation invocation = mock(Invocation.class);
-      AsyncResponse asyncResp = mock(AsyncResponse.class);
-      HttpClientResponse httpResponse = mock(HttpClientResponse.class);
-      OperationMeta operationMeta = mock(OperationMeta.class);
-      RestOperationMeta swaggerRestOperation = mock(RestOperationMeta.class);
-
-      Endpoint endpoint = mock(Endpoint.class);
-      when(invocation.getOperationMeta()).thenReturn(operationMeta);
-      URLPathBuilder urlPathBuilder = mock(URLPathBuilder.class);
-      when(swaggerRestOperation.getPathBuilder()).thenReturn(urlPathBuilder);
-      operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION);
-      when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerRestOperation);
-      when(invocation.getEndpoint()).thenReturn(endpoint);
-
-      String contentType = httpResponse.getHeader("Content-Type");
-      ProduceProcessor produceProcessor = mock(ProduceProcessor.class);
-      when(swaggerRestOperation.findProduceProcessor(contentType)).thenReturn(produceProcessor);
-      this.handleResponse(invocation, httpResponse, asyncResp);
-    } catch (Exception ex) {
-      status = true;
-    }
-    Assert.assertFalse(status);
-  }
-
-  @Override
-  protected HttpClientRequest createRequest(HttpClient client, Invocation invocation, IpPort ipPort, String path,
-      AsyncResponse asyncResp) {
-    return request;
-  }
-
-  @Test
-  public void testCreateRequestPathNoUrlPrefixNoPath(@Injectable Invocation invocation,
-      @Injectable RestOperationMeta swaggerRestOperation, @Injectable Endpoint endpoint,
-      @Injectable URIEndpointObject address, @Injectable URLPathBuilder builder) throws Exception {
-    new Expectations() {
-      {
-        endpoint.getAddress();
-        result = address;
-        builder.createRequestPath((Object[]) any);
-        result = "/path";
-      }
-    };
-    String path = this.createRequestPath(invocation, swaggerRestOperation);
-    Assert.assertEquals("/path", path);
-  }
-
-  @Test
-  public void testCreateRequestPathNoUrlPrefixHavePath(@Injectable Invocation invocation,
-      @Injectable RestOperationMeta swaggerRestOperation, @Injectable Endpoint endpoint,
-      @Injectable URIEndpointObject address, @Injectable URLPathBuilder builder) throws Exception {
-    Map<String, Object> contextMap = new HashMap<>();
-    contextMap.put(RestConst.REST_CLIENT_REQUEST_PATH, "/client/path");
-
-    new Expectations() {
-      {
-        endpoint.getAddress();
-        result = address;
-        invocation.getHandlerContext();
-        result = contextMap;
-      }
-    };
-    String path = this.createRequestPath(invocation, swaggerRestOperation);
-    Assert.assertEquals("/client/path", path);
-  }
-
-  @Test
-  public void testCreateRequestPathHaveUrlPrefixNoPath(@Injectable Invocation invocation,
-      @Injectable RestOperationMeta swaggerRestOperation, @Injectable Endpoint endpoint,
-      @Injectable URIEndpointObject address, @Injectable URLPathBuilder builder) throws Exception {
-    new Expectations() {
-      {
-        endpoint.getAddress();
-        result = address;
-        address.getFirst(Const.URL_PREFIX);
-        result = "/root";
-        builder.createRequestPath((Object[]) any);
-        result = "/path";
-      }
-    };
-    String path = this.createRequestPath(invocation, swaggerRestOperation);
-    Assert.assertEquals("/root/path", path);
-  }
-
-  @Test
-  public void testCreateRequestPathHaveUrlPrefixHavePath(@Injectable Invocation invocation,
-      @Injectable RestOperationMeta swaggerRestOperation, @Injectable Endpoint endpoint,
-      @Injectable URIEndpointObject address, @Injectable URLPathBuilder builder) throws Exception {
-    Map<String, Object> contextMap = new HashMap<>();
-    contextMap.put(RestConst.REST_CLIENT_REQUEST_PATH, "/client/path");
-
-    new Expectations() {
-      {
-        endpoint.getAddress();
-        result = address;
-        address.getFirst(Const.URL_PREFIX);
-        result = "/root";
-        invocation.getHandlerContext();
-        result = contextMap;
-      }
-    };
-    String path = this.createRequestPath(invocation, swaggerRestOperation);
-    Assert.assertEquals("/root/client/path", path);
-  }
-
-  @Test
-  public void testCreateRequestPathHaveUrlPrefixHavePathAndStartWith(@Injectable Invocation invocation,
-      @Injectable RestOperationMeta swaggerRestOperation, @Injectable Endpoint endpoint,
-      @Injectable URIEndpointObject address, @Injectable URLPathBuilder builder) throws Exception {
-    Map<String, Object> contextMap = new HashMap<>();
-    contextMap.put(RestConst.REST_CLIENT_REQUEST_PATH, "/client/path");
-
-    new Expectations() {
-      {
-        endpoint.getAddress();
-        result = address;
-        address.getFirst(Const.URL_PREFIX);
-        result = "/client";
-        invocation.getHandlerContext();
-        result = contextMap;
-      }
-    };
-    String path = this.createRequestPath(invocation, swaggerRestOperation);
-    Assert.assertEquals("/client/path", path);
-  }
-}

-- 
To stop receiving notification emails like this one, please contact
liubao@apache.org.