Repository: pinpoint-apm/pinpoint
Issue: Add trace support when using Thrift over HTTP
Labels: enhancement
module:plugin

#### Description
When using Thrift over HTTP (using **THttpClient** and **TServlet**), jdk/apache http client is used internally to send HTTP request client side. Similarly, **TServlet** operates inside a servlet container, and the container will process these requests first.
We have plugins for both of these, so the Thrift plugin should not be the one doing trace propagation.

Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/agent-it/pom.xml b/agent-it/pom.xml
index 12c67cd20112..618c0d830418 100644
--- a/agent-it/pom.xml
+++ b/agent-it/pom.xml
@@ -365,6 +365,13 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>9.2.11.v20150529</version>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
 
     <build>
diff --git a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftClientCallContext.java b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftClientCallContext.java
index 482a87550770..2a3217372795 100644
--- a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftClientCallContext.java
+++ b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftClientCallContext.java
@@ -25,6 +25,8 @@ public class ThriftClientCallContext {
     public static final ThriftHeader NONE = null;
     
     private final String methodName;
+
+    private boolean isEntryPoint;
     
     private ThriftHeader traceHeaderToBeRead;
     
@@ -37,7 +39,15 @@ public ThriftClientCallContext(String methodName) {
     public String getMethodName() {
         return methodName;
     }
-    
+
+    public boolean isEntryPoint() {
+        return isEntryPoint;
+    }
+
+    public void setEntryPoint(boolean entryPoint) {
+        isEntryPoint = entryPoint;
+    }
+
     public ThriftHeader getTraceHeaderToBeRead() {
         return traceHeaderToBeRead;
     }
@@ -56,14 +66,12 @@ public void setTraceHeader(ThriftRequestProperty traceHeader) {
 
     @Override
     public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("ThriftClientCallContext={methodName=").append(this.methodName);
-        sb.append(", traceHeaderToBeRead=").append(this.traceHeaderToBeRead.name());
-        sb.append(", traceHeader=").append(this.traceHeader.toString());
-        sb.append("}");
+        final StringBuilder sb = new StringBuilder("ThriftClientCallContext{");
+        sb.append("methodName='").append(methodName).append('\'');
+        sb.append(", isEntryPoint=").append(isEntryPoint);
+        sb.append(", traceHeaderToBeRead=").append(traceHeaderToBeRead);
+        sb.append(", traceHeader=").append(traceHeader);
+        sb.append('}');
         return sb.toString();
     }
-    
-    
-
 }
diff --git a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftConstants.java b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftConstants.java
index ca70c0df91b0..d52d92d1c708 100644
--- a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftConstants.java
+++ b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftConstants.java
@@ -55,6 +55,7 @@ private ThriftConstants() {
     public static final String T_ASYNC_METHOD_CALL_FIELD_TRANSPORT = "transport";
     public static final String FRAME_BUFFER_FIELD_TRANS_ = "trans_";
     public static final String FRAME_BUFFER_FIELD_IN_TRANS_ = "inTrans_";
+    public static final String T_HTTP_CLIENT_FIELD_URL_ = "url_";
 
     // custom field injector (accessor) FQCN
     private static final String FIELD_ACCESSOR_BASE = "com.navercorp.pinpoint.plugin.thrift.field.accessor.";
@@ -68,4 +69,5 @@ private ThriftConstants() {
     public static final String FIELD_GETTER_T_NON_BLOCKING_TRANSPORT = FIELD_GETTER_BASE + "TNonblockingTransportFieldGetter";
     public static final String FIELD_GETTER_T_TRANSPORT = FIELD_GETTER_BASE + "TTransportFieldGetter";
     public static final String FIELD_GETTER_T_PROTOCOL = FIELD_GETTER_BASE + "TProtocolFieldGetter";
+    public static final String FIELD_GETTER_URL = FIELD_GETTER_BASE + "UrlFieldGetter";
 }
diff --git a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftPlugin.java b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftPlugin.java
index 6260d49bdbfd..c310113801f6 100644
--- a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftPlugin.java
+++ b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftPlugin.java
@@ -54,6 +54,7 @@ public void setup(ProfilerPluginSetupContext context) {
 
         if (traceClient) {
             addInterceptorsForSynchronousClients(config);
+            addTHttpClientEditor();
             if (traceClientAsync) {
                 addInterceptorsForAsynchronousClients();
             }
@@ -274,6 +275,19 @@ public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, Strin
         });
     }
 
+    // THttpClient
+
+    private void addTHttpClientEditor() {
+        transformTemplate.transform("org.apache.thrift.transport.THttpClient", new TransformCallback() {
+            @Override
+            public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
+                final InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer);
+                target.addGetter(ThriftConstants.FIELD_GETTER_URL, ThriftConstants.T_HTTP_CLIENT_FIELD_URL_);
+                return target.toBytecode();
+            }
+        });
+    }
+
     // Common
 
     private void addInterceptorsForRetrievingSocketAddresses() {
diff --git a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftTypeProvider.java b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftTypeProvider.java
index 96ba3d159592..c61e2ba68f0e 100644
--- a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftTypeProvider.java
+++ b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftTypeProvider.java
@@ -29,8 +29,8 @@ public class ThriftTypeProvider implements TraceMetadataProvider {
     public void setup(TraceMetadataSetupContext context) {
         context.addServiceType(ThriftConstants.THRIFT_SERVER);
         context.addServiceType(ThriftConstants.THRIFT_CLIENT, AnnotationKeyMatchers.exact(ThriftConstants.THRIFT_URL));
-        context.addServiceType(ThriftConstants.THRIFT_SERVER_INTERNAL, AnnotationKeyMatchers.ARGS_MATCHER);
-        context.addServiceType(ThriftConstants.THRIFT_CLIENT_INTERNAL, AnnotationKeyMatchers.ARGS_MATCHER);
+        context.addServiceType(ThriftConstants.THRIFT_SERVER_INTERNAL, AnnotationKeyMatchers.exact(ThriftConstants.THRIFT_URL));
+        context.addServiceType(ThriftConstants.THRIFT_CLIENT_INTERNAL, AnnotationKeyMatchers.exact(ThriftConstants.THRIFT_URL));
         context.addAnnotationKey(ThriftConstants.THRIFT_URL);
         context.addAnnotationKey(ThriftConstants.THRIFT_ARGS);
         context.addAnnotationKey(ThriftConstants.THRIFT_RESULT);
diff --git a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftUtils.java b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftUtils.java
index 6276cf4e46cf..1a6afb7bd2e3 100644
--- a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftUtils.java
+++ b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftUtils.java
@@ -76,7 +76,14 @@ public static String getClientServiceName(TServiceClient client) {
      */
     public static String getAsyncMethodCallName(TAsyncMethodCall<?> asyncMethodCall) {
         String asyncMethodCallClassName = asyncMethodCall.getClass().getName();
-        return convertDotPathToUriPath(ThriftConstants.ASYNC_METHOD_CALL_PATTERN.matcher(asyncMethodCallClassName).replaceAll("."));
+        String convertedMethodCallName = convertDotPathToUriPath(ThriftConstants.ASYNC_METHOD_CALL_PATTERN.matcher(asyncMethodCallClassName).replaceAll("."));
+        // thrift java generator appends "_call" to the method name when naming the function class
+        // https://github.com/apache/thrift/blob/master/compiler/cpp/src/thrift/generate/t_java_generator.cc#L3151
+        final String callSuffix = "_call";
+        if (convertedMethodCallName.endsWith(callSuffix)) {
+            return convertedMethodCallName.substring(0, convertedMethodCallName.length() - callSuffix.length());
+        }
+        return convertedMethodCallName;
     }
 
     /**
diff --git a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/field/getter/UrlFieldGetter.java b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/field/getter/UrlFieldGetter.java
new file mode 100644
index 000000000000..5109fb7fe843
--- /dev/null
+++ b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/field/getter/UrlFieldGetter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2018 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.plugin.thrift.field.getter;
+
+import java.net.URL;
+
+/**
+ * @author HyunGil Jeong
+ */
+public interface UrlFieldGetter {
+    URL _$PINPOINT$_getUrl();
+}
diff --git a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/client/TServiceClientSendBaseInterceptor.java b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/client/TServiceClientSendBaseInterceptor.java
index a421db1c6546..61370df0384e 100644
--- a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/client/TServiceClientSendBaseInterceptor.java
+++ b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/client/TServiceClientSendBaseInterceptor.java
@@ -17,12 +17,16 @@
 package com.navercorp.pinpoint.plugin.thrift.interceptor.client;
 
 import java.net.Socket;
+import java.net.URL;
 
 import com.navercorp.pinpoint.bootstrap.interceptor.scope.InterceptorScope;
+import com.navercorp.pinpoint.common.plugin.util.HostAndPort;
 import com.navercorp.pinpoint.common.util.StringUtils;
+import com.navercorp.pinpoint.plugin.thrift.field.getter.UrlFieldGetter;
 import org.apache.thrift.TBase;
 import org.apache.thrift.TServiceClient;
 import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.THttpClient;
 import org.apache.thrift.transport.TTransport;
 
 import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor;
@@ -84,43 +88,39 @@ public void before(Object target, Object[] args) {
             if (trace == null) {
                 return;
             }
-            ThriftRequestProperty parentTraceInfo = new ThriftRequestProperty();
+
             final boolean shouldSample = trace.canSampled();
             if (!shouldSample) {
+                if (transport instanceof THttpClient) {
+                    return;
+                }
+                ThriftRequestProperty parentTraceInfo = new ThriftRequestProperty();
                 if (isDebug) {
                     logger.debug("set Sampling flag=false");
                 }
-                parentTraceInfo.setShouldSample(shouldSample);
+                parentTraceInfo.setShouldSample(false);
+                InterceptorScopeInvocation currentTransaction = this.scope.getCurrentInvocation();
+                currentTransaction.setAttachment(parentTraceInfo);
+                return;
+            }
+
+            SpanEventRecorder recorder = trace.traceBlockBegin();
+            String remoteAddress = ThriftConstants.UNKNOWN_ADDRESS;
+
+            // If we're writing to THttpClient, http client plugin will handle trace propagation.
+            // We simply record as basic method.
+            if (transport instanceof THttpClient) {
+                recorder.recordServiceType(ThriftConstants.THRIFT_CLIENT_INTERNAL);
+                remoteAddress = getRemoteAddressForTHttpClient((THttpClient) transport);
             } else {
-                SpanEventRecorder recorder = trace.traceBlockBegin();
                 recorder.recordServiceType(ThriftConstants.THRIFT_CLIENT);
-
-                // retrieve connection information
-                String remoteAddress = ThriftConstants.UNKNOWN_ADDRESS;
-                if (transport instanceof SocketFieldAccessor) {
-                    Socket socket = ((SocketFieldAccessor)transport)._$PINPOINT$_getSocket();
-                    if (socket != null) {
-                        remoteAddress = ThriftUtils.getHostPort(socket.getRemoteSocketAddress());
-                    }
-                } else {
-                    if (isDebug) {
-                        logger.debug("Invalid target object. Need field accessor({}).", SocketFieldAccessor.class.getName());
-                    }
-                }
+                remoteAddress = getRemoteAddress(transport);
                 recorder.recordDestinationId(remoteAddress);
 
-                String methodName = ThriftConstants.UNKNOWN_METHOD_NAME;
-                if (args[0] instanceof String) {
-                    methodName = (String)args[0];
-                }
-                String serviceName = ThriftUtils.getClientServiceName(client);
-
-                String thriftUrl = getServiceUrl(remoteAddress, serviceName, methodName);
-                recorder.recordAttribute(ThriftConstants.THRIFT_URL, thriftUrl);
-
                 TraceId nextId = trace.getTraceId().getNextTraceId();
                 recorder.recordNextSpanId(nextId.getSpanId());
 
+                ThriftRequestProperty parentTraceInfo = new ThriftRequestProperty();
                 parentTraceInfo.setTraceId(nextId.getTransactionId());
                 parentTraceInfo.setSpanId(nextId.getSpanId());
                 parentTraceInfo.setParentSpanId(nextId.getParentSpanId());
@@ -129,10 +129,49 @@ public void before(Object target, Object[] args) {
                 parentTraceInfo.setParentApplicationName(traceContext.getApplicationName());
                 parentTraceInfo.setParentApplicationType(traceContext.getServerTypeCode());
                 parentTraceInfo.setAcceptorHost(remoteAddress);
+
+                InterceptorScopeInvocation currentTransaction = this.scope.getCurrentInvocation();
+                currentTransaction.setAttachment(parentTraceInfo);
+
+            }
+
+            String methodName = ThriftConstants.UNKNOWN_METHOD_NAME;
+            if (args[0] instanceof String) {
+                methodName = (String)args[0];
+            }
+            String serviceName = ThriftUtils.getClientServiceName(client);
+            String thriftUrl = getServiceUrl(remoteAddress, serviceName, methodName);
+            recorder.recordAttribute(ThriftConstants.THRIFT_URL, thriftUrl);
+        }
+    }
+
+    private String getRemoteAddressForTHttpClient(THttpClient tHttpClient) {
+        if (tHttpClient instanceof UrlFieldGetter) {
+            URL url = ((UrlFieldGetter) tHttpClient)._$PINPOINT$_getUrl();
+            if (url == null) {
+                return ThriftConstants.UNKNOWN_ADDRESS;
+            }
+            return HostAndPort.toHostAndPortString(url.getHost(), url.getPort());
+        }
+        if (isDebug) {
+            logger.debug("Invalid oprot transport object. Need field getter({}).", UrlFieldGetter.class.getName());
+        }
+        return ThriftConstants.UNKNOWN_ADDRESS;
+    }
+
+    private String getRemoteAddress(TTransport transport) {
+        if (transport instanceof SocketFieldAccessor) {
+            Socket socket = ((SocketFieldAccessor)transport)._$PINPOINT$_getSocket();
+            if (socket == null) {
+                return ThriftConstants.UNKNOWN_ADDRESS;
+            }
+            return ThriftUtils.getHostPort(socket.getRemoteSocketAddress());
+        } else {
+            if (isDebug) {
+                logger.debug("Invalid oprot transport object. Need field accessor({}).", SocketFieldAccessor.class.getName());
             }
-            InterceptorScopeInvocation currentTransaction = this.scope.getCurrentInvocation();
-            currentTransaction.setAttachment(parentTraceInfo);
         }
+        return ThriftConstants.UNKNOWN_ADDRESS;
     }
 
     private String getServiceUrl(String url, String serviceName, String methodName) {
diff --git a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/server/TBaseProcessorProcessInterceptor.java b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/server/TBaseProcessorProcessInterceptor.java
index a29a9710d1ac..634b51683f83 100644
--- a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/server/TBaseProcessorProcessInterceptor.java
+++ b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/server/TBaseProcessorProcessInterceptor.java
@@ -94,41 +94,61 @@ public void after(Object target, Object[] args, Object result, Throwable throwab
         if (trace == null) {
             return;
         }
+
+        InterceptorScopeInvocation currentTransaction = this.scope.getCurrentInvocation();
+        Object attachment = currentTransaction.getAttachment();
+        if (!(attachment instanceof ThriftClientCallContext)) {
+            return;
+        }
+
         // logging here as some Thrift servers depend on TTransportException being thrown for normal operations.
         // log only when current transaction is being traced.
         if (isDebug) {
             logger.afterInterceptor(target, args, result, throwable);
         }
-        this.traceContext.removeTraceObject();
+
+        ThriftClientCallContext clientCallContext = (ThriftClientCallContext) attachment;
+        if (clientCallContext.isEntryPoint()) {
+            traceContext.removeTraceObject();
+        }
+
         if (trace.canSampled()) {
-            try {
-                processTraceObject(trace, target, args, throwable);
-            } catch (Throwable t) {
-                logger.warn("Error processing trace object. Cause:{}", t.getMessage(), t);
-            } finally {
-                trace.close();
+            String methodUri = getMethodUri(target);
+            if (clientCallContext.isEntryPoint()) {
+                finalizeSpan(trace, methodUri, throwable);
+            } else {
+                finalizeSpanEvent(trace, methodUri, throwable);
             }
         }
     }
 
-    private void processTraceObject(final Trace trace, Object target, Object[] args, Throwable throwable) {
-        // end spanEvent
+    private void finalizeSpan(final Trace trace, String methodUri, Throwable throwable) {
+        try {
+            finalizeSpanEvent(trace, null, throwable);
+            SpanRecorder recorder = trace.getSpanRecorder();
+            recorder.recordRpcName(methodUri);
+        } catch (Throwable t) {
+            logger.warn("Error processing trace object. Cause:{}", t.getMessage(), t);
+        } finally {
+            trace.close();
+        }
+    }
+
+    private void finalizeSpanEvent(final Trace trace, String methodUri, Throwable throwable) {
         try {
             SpanEventRecorder recorder = trace.currentSpanEventRecorder();
             // TODO Might need a way to collect and record method arguments
             // trace.recordAttribute(...);
             recorder.recordException(throwable);
             recorder.recordApi(this.descriptor);
+            if (methodUri != null) {
+                recorder.recordAttribute(ThriftConstants.THRIFT_URL, methodUri);
+            }
         } catch (Throwable t) {
             logger.warn("Error processing trace object. Cause:{}", t.getMessage(), t);
         } finally {
             trace.traceBlockEnd();
         }
-
-        // end root span
-        SpanRecorder recorder = trace.getSpanRecorder();
-        String methodUri = getMethodUri(target);
-        recorder.recordRpcName(methodUri);
     }
 
     private String getMethodUri(Object target) {
diff --git a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/tprotocol/server/TProtocolReadMessageEndInterceptor.java b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/tprotocol/server/TProtocolReadMessageEndInterceptor.java
index 4e2d43fed7bd..54a3148beae6 100644
--- a/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/tprotocol/server/TProtocolReadMessageEndInterceptor.java
+++ b/plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/tprotocol/server/TProtocolReadMessageEndInterceptor.java
@@ -99,12 +99,9 @@ public void after(Object target, Object[] args, Object result, Throwable throwab
             InterceptorScopeInvocation currentTransaction = this.scope.getCurrentInvocation();
             Object attachment = currentTransaction.getAttachment();
             if (attachment instanceof ThriftClientCallContext) {
-                ThriftClientCallContext clientCallContext = (ThriftClientCallContext)attachment;
-                String methodName = clientCallContext.getMethodName();
-                ThriftRequestProperty parentTraceInfo = clientCallContext.getTraceHeader();
+                ThriftClientCallContext clientCallContext = (ThriftClientCallContext) attachment;
                 try {
-                    this.logger.debug("parentTraceInfo : {}", parentTraceInfo);
-                    recordTrace(target, parentTraceInfo, methodName);
+                    recordTrace(target, clientCallContext);
                 } catch (Throwable t) {
                     logger.warn("Error creating trace object. Cause:{}", t.getMessage(), t);
                 }
@@ -132,8 +129,17 @@ private boolean validate(Object target) {
         return true;
     }
 
-    private void recordTrace(Object target, ThriftRequestProperty parentTraceInfo, String methodName) {
-        final Trace trace = createTrace(target, parentTraceInfo, methodName);
+    private void recordTrace(Object target, ThriftClientCallContext clientCallContext) {
+        String methodName = clientCallContext.getMethodName();
+        // Check if there is an active trace first for cases such as TServlet, which one of the WAS plugins would
+        // have created one already.
+        Trace trace = traceContext.currentRawTraceObject();
+        if (trace == null) {
+            ThriftRequestProperty parentTraceInfo = clientCallContext.getTraceHeader();
+            this.logger.debug("parentTraceInfo : {}", parentTraceInfo);
+            trace = createTrace(target, parentTraceInfo, methodName);
+            clientCallContext.setEntryPoint(true);
+        }
         if (trace == null) {
             return;
         }


Test patch:
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/TestEnvironment.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/TestEnvironment.java
index 21b2c1274117..80ec15f2ecdc 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/TestEnvironment.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/TestEnvironment.java
@@ -29,14 +29,23 @@ public class TestEnvironment {
     private static final int MIN_SERVER_PORT = 9091;
     private static final int MAX_SERVER_PORT = 9099;
 
+    private static final String SERVER_HOST = "localhost";
     private static final String SERVER_IP = "127.0.0.1";
     private static final TProtocolFactory PROTOCOL_FACTORY = new TBinaryProtocol.Factory();
+    private static final String HTTP_PATH = "/thrift";
 
+    private final String serverHost = SERVER_HOST;
     private final String serverIp = SERVER_IP;
     private final int port = MIN_SERVER_PORT + (int)(Math.random() * (MAX_SERVER_PORT - MIN_SERVER_PORT) + 1);
+    private final String httpPath = HTTP_PATH;
+    private final String httpUrl = "http://" + serverHost + ":" + port + httpPath;
     private final InetSocketAddress serverAddress = new InetSocketAddress(SERVER_IP, this.port);
     private final TProtocolFactory protocolFactory = PROTOCOL_FACTORY;
 
+    public String getServerHost() {
+        return this.serverHost;
+    }
+
     public String getServerIp() {
         return this.serverIp;
     }
@@ -45,6 +54,14 @@ public int getPort() {
         return this.port;
     }
 
+    public String getHttpPath() {
+        return this.httpPath;
+    }
+
+    public String getHttpUrl() {
+        return this.httpUrl;
+    }
+
     public InetSocketAddress getServerAddress() {
         return this.serverAddress;
     }
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/AsyncEchoTestClient.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/AsyncEchoTestClient.java
index 5349eb6f4014..90dcdf13370f 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/AsyncEchoTestClient.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/AsyncEchoTestClient.java
@@ -107,7 +107,7 @@ public void verifyTraces(PluginTestVerifier verifier, String expectedMessage) th
         // SpanEvent - TAsyncClientManager.call
         Method call = TAsyncClientManager.class.getDeclaredMethod("call", TAsyncMethodCall.class);
         ExpectedAnnotation thriftUrl = Expectations.annotation("thrift.url",
-                remoteAddress + "/com/navercorp/pinpoint/plugin/thrift/dto/EchoService/echo_call");
+                remoteAddress + "/com/navercorp/pinpoint/plugin/thrift/dto/EchoService/echo");
         ExpectedTrace callTrace = event("THRIFT_CLIENT", // ServiceType
                 call, // Method
                 null, // rpc
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/HttpEchoTestClient.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/HttpEchoTestClient.java
new file mode 100644
index 000000000000..18e9b62680bb
--- /dev/null
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/HttpEchoTestClient.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2018 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.plugin.thrift.common.client;
+
+import com.navercorp.pinpoint.bootstrap.plugin.test.Expectations;
+import com.navercorp.pinpoint.bootstrap.plugin.test.ExpectedAnnotation;
+import com.navercorp.pinpoint.bootstrap.plugin.test.ExpectedTrace;
+import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifier;
+import com.navercorp.pinpoint.common.plugin.util.HostAndPort;
+import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
+import com.navercorp.pinpoint.plugin.thrift.dto.EchoService;
+import org.apache.thrift.TBase;
+import org.apache.thrift.TException;
+import org.apache.thrift.TServiceClient;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.THttpClient;
+import org.apache.thrift.transport.TTransportException;
+
+import java.lang.reflect.Method;
+import java.net.URL;
+
+import static com.navercorp.pinpoint.bootstrap.plugin.test.Expectations.event;
+
+/**
+ * @author HyunGil Jeong
+ */
+public class HttpEchoTestClient implements EchoTestClient {
+
+    private final TestEnvironment environment;
+    private final THttpClient httpClient;
+
+    private HttpEchoTestClient(TestEnvironment environment, THttpClient httpClient) throws TTransportException {
+        this.environment = environment;
+        this.httpClient = httpClient;
+    }
+
+    @Override
+    public String echo(String message) throws TException {
+        TProtocol protocol = environment.getProtocolFactory().getProtocol(httpClient);
+        EchoService.Client client = new EchoService.Client(protocol);
+        return client.echo(message);
+    }
+
+    @Override
+    public void verifyTraces(PluginTestVerifier verifier, String expectedMessage) throws Exception {
+        // ignore jdk http url connector traces
+        verifier.ignoreServiceType("JDK_HTTPURLCONNECTOR");
+
+        // refer to TServiceClientSendBaseInterceptor.getRemoteAddressForTHttpClient(...)
+        URL url = new URL(environment.getHttpUrl());
+        String hostAndPort = HostAndPort.toHostAndPortString(url.getHost(), url.getPort());
+
+        // SpanEvent - TServiceClient.sendBase
+        Method sendBaseMethod = TServiceClient.class.getDeclaredMethod("sendBase", String.class, TBase.class);
+        ExpectedAnnotation thriftUrl = Expectations.annotation("thrift.url",
+                hostAndPort + "/com/navercorp/pinpoint/plugin/thrift/dto/EchoService/echo");
+        ExpectedAnnotation thriftArgs = Expectations.annotation("thrift.args",
+                "echo_args(message:" + expectedMessage + ")");
+        ExpectedTrace tServiceClientSendBaseTrace = event(
+                "THRIFT_CLIENT_INTERNAL",
+                sendBaseMethod,
+                thriftUrl, thriftArgs);
+
+        // SpanEvent - HttpURLConnection.connect (ignore)
+
+        // SpanEvent - TServiceClient.receiveBase
+        Method receiveBaseMethod = TServiceClient.class.getDeclaredMethod("receiveBase", TBase.class, String.class);
+        ExpectedAnnotation thriftResult = Expectations.annotation(
+                "thrift.result", "echo_result(success:" + expectedMessage + ")");
+        ExpectedTrace tServiceClientReceiveBaseTrace = event(
+                "THRIFT_CLIENT_INTERNAL",
+                receiveBaseMethod,
+                thriftResult);
+
+        verifier.verifyDiscreteTrace(
+                tServiceClientSendBaseTrace,
+                tServiceClientReceiveBaseTrace);
+    }
+
+    @Override
+    public void close() {
+        httpClient.close();
+    }
+
+    public static HttpEchoTestClient create(TestEnvironment environment) throws TTransportException {
+        return new HttpEchoTestClient(environment, new THttpClient(environment.getHttpUrl()));
+    }
+}
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/SyncEchoTestClient.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/SyncEchoTestClient.java
index 5749e6a3287a..8f9734da0dc1 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/SyncEchoTestClient.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/SyncEchoTestClient.java
@@ -61,13 +61,14 @@ public final String echo(String message) throws TException {
 
     @Override
     public void verifyTraces(PluginTestVerifier verifier, String expectedMessage) throws Exception {
+        // refer to TServiceClientSendBaseInterceptor.getRemoteAddress(...)
         final InetSocketAddress socketAddress = this.environment.getServerAddress();
         final String hostName = SocketAddressUtils.getHostNameFirst(socketAddress);
-        // refer to com.navercorp.pinpoint.plugin.thrift.ThriftUtils#getHostPort
         final String remoteAddress = HostAndPort.toHostAndPortString(hostName, socketAddress.getPort());
+
         // SpanEvent - TServiceClient.sendBase
         Method sendBase = TServiceClient.class.getDeclaredMethod("sendBase", String.class, TBase.class);
-        // refer to com.navercorp.pinpoint.plugin.thrift.ThriftUtils#getClientServiceName
+
         ExpectedAnnotation thriftUrl = Expectations.annotation("thrift.url",
                 remoteAddress + "/com/navercorp/pinpoint/plugin/thrift/dto/EchoService/echo");
         ExpectedAnnotation thriftArgs = Expectations.annotation("thrift.args",
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/AsyncEchoTestServer.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/AsyncEchoTestServer.java
index 68ab6d616ebd..25240f7e2fb0 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/AsyncEchoTestServer.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/AsyncEchoTestServer.java
@@ -25,7 +25,9 @@
 import com.navercorp.pinpoint.bootstrap.plugin.util.SocketAddressUtils;
 import com.navercorp.pinpoint.common.plugin.util.HostAndPort;
 import org.apache.thrift.TBaseAsyncProcessor;
+import org.apache.thrift.TException;
 import org.apache.thrift.TProcessor;
+import org.apache.thrift.async.AsyncMethodCallback;
 import org.apache.thrift.server.AbstractNonblockingServer;
 import org.apache.thrift.server.AbstractNonblockingServer.AsyncFrameBuffer;
 import org.apache.thrift.server.THsHaServer;
@@ -43,9 +45,9 @@
 /**
  * @author HyunGil Jeong
  */
-public abstract class AsyncEchoTestServer<T extends AbstractNonblockingServer> extends EchoTestServer<T> {
+public abstract class AsyncEchoTestServer<T extends AbstractNonblockingServer> extends ThriftEchoTestServer<T> {
 
-    protected AsyncEchoTestServer(T server, TestEnvironment environment) throws TTransportException {
+    protected AsyncEchoTestServer(T server, TestEnvironment environment) {
         super(server, environment);
     }
 
@@ -64,13 +66,17 @@ public void verifyServerTraces(PluginTestVerifier verifier) throws Exception {
         ));
         // SpanEvent - TBaseAsyncProcessor.process
         verifier.verifyTrace(event("THRIFT_SERVER_INTERNAL", process));
-        verifier.verifyTraceCount(0);
     }
 
     public static class AsyncEchoTestServerFactory {
 
         private static TProcessor getAsyncProcessor() {
-            return new EchoService.AsyncProcessor<EchoService.AsyncIface>(new EchoServiceAsyncHandler());
+            return new EchoService.AsyncProcessor<EchoService.AsyncIface>(new EchoService.AsyncIface() {
+                @Override
+                public void echo(String message, AsyncMethodCallback<String> resultHandler) throws TException {
+                    resultHandler.onComplete(message);
+                }
+            });
         }
 
         public static AsyncEchoTestServer<TThreadedSelectorServer> threadedSelectorServer(
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/EchoTestServer.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/EchoTestServer.java
index 8f461aea02a0..4bb87e17a8a9 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/EchoTestServer.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/EchoTestServer.java
@@ -5,7 +5,7 @@
  * 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
+ *     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,
@@ -17,123 +17,17 @@
 package com.navercorp.pinpoint.plugin.thrift.common.server;
 
 import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifier;
-import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
-import com.navercorp.pinpoint.plugin.thrift.common.client.AsyncEchoTestClient;
-import com.navercorp.pinpoint.plugin.thrift.common.client.SyncEchoTestClient;
-import com.navercorp.pinpoint.plugin.thrift.dto.EchoService;
-import org.apache.thrift.TException;
-import org.apache.thrift.async.AsyncMethodCallback;
-import org.apache.thrift.protocol.TProtocol;
-import org.apache.thrift.server.ServerContext;
-import org.apache.thrift.server.TServer;
-import org.apache.thrift.server.TServerEventHandler;
-import org.apache.thrift.transport.TTransport;
-import org.apache.thrift.transport.TTransportException;
 
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 
 /**
  * @author HyunGil Jeong
  */
-public abstract class EchoTestServer<T extends TServer> {
+public interface EchoTestServer {
 
-    protected static class EchoServiceHandler implements EchoService.Iface {
-        @Override
-        public String echo(String message) throws TException {
-            return message;
-        }
-    }
+    void start(ExecutorService executorService);
 
-    protected static class EchoServiceAsyncHandler implements EchoService.AsyncIface {
-
-        private final EchoServiceHandler syncHandler = new EchoServiceHandler();
-
-        @SuppressWarnings("unchecked")
-        @Override
-        public void echo(String message, AsyncMethodCallback resultHandler) throws TException {
-            try {
-                final String echo = this.syncHandler.echo(message);
-                resultHandler.onComplete(echo);
-            } catch (Exception e) {
-                resultHandler.onError(e);
-            }
-        }
-    }
-
-    private final T server;
-
-    protected final TestEnvironment environment;
-
-    protected EchoTestServer(T server, TestEnvironment environment) throws TTransportException {
-        if (server == null) {
-            throw new IllegalArgumentException("server cannot be null");
-        }
-        this.server = server;
-        this.environment = environment;
-    }
-
-    public void start(ExecutorService executor) throws TTransportException, InterruptedException {
-        if (this.server.isServing()) {
-            return;
-        }
-
-        CountDownLatch waitToServeLatch = new CountDownLatch(1);
-        server.setServerEventHandler(new WaitToServeHandler(waitToServeLatch));
-
-        executor.execute(new Runnable() {
-            @Override
-            public void run() {
-                server.serve();
-            }
-        });
-
-        waitToServeLatch.await();
-    }
-
-    public void stop() {
-        this.server.stop();
-    }
-
-    public void verifyTraces(PluginTestVerifier verifier) throws Exception {
-        this.verifyServerTraces(verifier);
-    }
-
-    protected abstract void verifyServerTraces(PluginTestVerifier verifier) throws Exception;
-
-    public abstract SyncEchoTestClient getSynchronousClient() throws TTransportException;
-
-    public abstract AsyncEchoTestClient getAsynchronousClient() throws IOException;
-
-
-    private class WaitToServeHandler implements TServerEventHandler {
-
-        private final CountDownLatch waitToServeLatch;
-
-        public WaitToServeHandler(CountDownLatch waitToServeLatch) {
-            this.waitToServeLatch = waitToServeLatch;
-        }
-
-        @Override
-        public void preServe() {
-            waitToServeLatch.countDown();
-        }
-
-        @Override
-        public ServerContext createContext(TProtocol tProtocol, TProtocol tProtocol1) {
-            return null;
-        }
-
-        @Override
-        public void deleteContext(ServerContext serverContext, TProtocol tProtocol, TProtocol tProtocol1) {
-
-        }
-
-        @Override
-        public void processContext(ServerContext serverContext, TTransport tTransport, TTransport tTransport1) {
-
-        }
-    }
+    void stop();
 
+    void verifyTraces(PluginTestVerifier verifier) throws Exception;
 }
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/HttpEchoTestServer.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/HttpEchoTestServer.java
new file mode 100644
index 000000000000..a3b9f8fdd6c8
--- /dev/null
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/HttpEchoTestServer.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2018 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.plugin.thrift.common.server;
+
+import com.navercorp.pinpoint.bootstrap.plugin.test.Expectations;
+import com.navercorp.pinpoint.bootstrap.plugin.test.ExpectedAnnotation;
+import com.navercorp.pinpoint.bootstrap.plugin.test.ExpectedTrace;
+import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifier;
+import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
+import com.navercorp.pinpoint.plugin.thrift.common.client.HttpEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.dto.EchoService;
+import org.apache.thrift.TBaseProcessor;
+import org.apache.thrift.TException;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.server.TServlet;
+import org.apache.thrift.transport.TTransportException;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.concurrent.ExecutorService;
+
+import static com.navercorp.pinpoint.bootstrap.plugin.test.Expectations.event;
+
+/**
+ * @author HyunGil Jeong
+ */
+public abstract class HttpEchoTestServer implements EchoTestServer {
+
+    private final TestEnvironment environment;
+    private final Server server;
+
+    protected HttpEchoTestServer(TestEnvironment environment, TProcessor processor) {
+        this.environment = environment;
+        int port = this.environment.getPort();
+        String path = this.environment.getHttpPath();
+        TProtocolFactory protocolFactory = this.environment.getProtocolFactory();
+        this.server = new Server(port);
+        this.server.setHandler(new EchoHttpServerHandler(path, processor, protocolFactory));
+    }
+
+    public abstract HttpEchoTestClient getHttpClient() throws TTransportException;
+
+    @Override
+    public void start(ExecutorService executorService) {
+        try {
+            server.start();
+        } catch (Exception e) {
+            throw new IllegalStateException("Failed to start EchoHttpServer.", e);
+        }
+    }
+
+    @Override
+    public void stop() {
+        if (server.isStarted()) {
+            try {
+                server.stop();
+            } catch (Exception e) {
+                throw new IllegalStateException("Failed to stop EchoHttpServer.", e);
+            }
+        }
+    }
+
+    @Override
+    public void verifyTraces(PluginTestVerifier verifier) throws Exception {
+        // ignore Jetty traces
+        verifier.ignoreServiceType("JETTY", "JETTY_METHOD");
+
+        // RootSpan - Jetty Servlet Process (ignore)
+
+        // SpanEvent - (Jetty) Server.handle (ignore)
+
+        // SpanEvent - TBaseProcessor.process
+        // refer to TBaseProcessorProcessInterceptor.finalizeSpanEvent(...)
+        Method processMethod = TBaseProcessor.class.getDeclaredMethod("process", TProtocol.class, TProtocol.class);
+        ExpectedAnnotation thriftUrl = Expectations.annotation(
+                "thrift.url", "com/navercorp/pinpoint/plugin/thrift/dto/EchoService/echo");
+        ExpectedTrace tBaseProcessorProcessTrace = event(
+                "THRIFT_SERVER_INTERNAL",
+                processMethod,
+                thriftUrl);
+
+        verifier.verifyDiscreteTrace(tBaseProcessorProcessTrace);
+    }
+
+    public static HttpEchoTestServer createServer(final TestEnvironment environment) {
+        TProcessor processor = new EchoService.Processor<EchoService.Iface>(new EchoService.Iface() {
+            @Override
+            public String echo(String message) throws TException {
+                return message;
+            }
+        });
+        return new HttpEchoTestServer(environment, processor) {
+            @Override
+            public HttpEchoTestClient getHttpClient() throws TTransportException {
+                return HttpEchoTestClient.create(environment);
+            }
+        };
+    }
+
+    private class EchoHttpServerHandler extends AbstractHandler {
+
+        private final String path;
+        private final TServlet servlet;
+
+        private EchoHttpServerHandler(String path, TProcessor processor, TProtocolFactory protocolFactory) {
+            this.path = path;
+            this.servlet = new TServlet(processor, protocolFactory) {
+                @Override
+                protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+                    super.service(req, resp);
+                }
+            };
+        }
+
+        @Override
+        public void handle(String target, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException {
+            if (path.equals(target)) {
+                servlet.service(httpServletRequest, httpServletResponse);
+            } else {
+                httpServletResponse.sendError(HttpStatus.NOT_FOUND_404, target + " is not available.");
+            }
+        }
+    }
+}
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/SyncEchoTestServer.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/SyncEchoTestServer.java
index bc258236a01b..224e6fb7ade3 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/SyncEchoTestServer.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/SyncEchoTestServer.java
@@ -25,6 +25,7 @@
 import com.navercorp.pinpoint.bootstrap.plugin.util.SocketAddressUtils;
 import com.navercorp.pinpoint.common.plugin.util.HostAndPort;
 import org.apache.thrift.TBaseProcessor;
+import org.apache.thrift.TException;
 import org.apache.thrift.TProcessor;
 import org.apache.thrift.protocol.TProtocol;
 import org.apache.thrift.server.THsHaServer;
@@ -46,9 +47,9 @@
 /**
  * @author HyunGil Jeong
  */
-public abstract class SyncEchoTestServer<T extends TServer> extends EchoTestServer<T> {
+public abstract class SyncEchoTestServer<T extends TServer> extends ThriftEchoTestServer<T> {
 
-    protected SyncEchoTestServer(T server, TestEnvironment environment) throws TTransportException {
+    protected SyncEchoTestServer(T server, TestEnvironment environment) {
         super(server, environment);
     }
 
@@ -59,7 +60,8 @@ public void verifyServerTraces(PluginTestVerifier verifier) throws Exception {
         verifier.verifyTraceCount(2);
         Method process = TBaseProcessor.class.getDeclaredMethod("process", TProtocol.class, TProtocol.class);
         verifier.verifyDiscreteTrace(
-        // RootSpan - Thrift Server Invocation
+                // RootSpan - Thrift Server Invocation
+                // refer to TBaseProcessorProcessInterceptor.finalizeSpan(...)
                 root("THRIFT_SERVER", // ServiceType,
                         "Thrift Server Invocation", // Method
                         "com/navercorp/pinpoint/plugin/thrift/dto/EchoService/echo", // rpc
@@ -67,13 +69,17 @@ public void verifyServerTraces(PluginTestVerifier verifier) throws Exception {
                         address), // remoteAddress
                 // SpanEvent - TBaseProcessor.process
                 event("THRIFT_SERVER_INTERNAL", process));
-        verifier.verifyTraceCount(0);
     }
 
     public static class SyncEchoTestServerFactory {
 
         private static TProcessor getProcessor() {
-            return new EchoService.Processor<EchoService.Iface>(new EchoServiceHandler());
+            return new EchoService.Processor<EchoService.Iface>(new EchoService.Iface() {
+                @Override
+                public String echo(String message) throws TException {
+                    return message;
+                }
+            });
         }
 
         public static SyncEchoTestServer<TSimpleServer> simpleServer(final TestEnvironment environment)
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/ThriftEchoTestServer.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/ThriftEchoTestServer.java
new file mode 100644
index 000000000000..d5d63bd8496f
--- /dev/null
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/ThriftEchoTestServer.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2018 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.plugin.thrift.common.server;
+
+import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifier;
+import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
+import com.navercorp.pinpoint.plugin.thrift.common.client.AsyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.client.SyncEchoTestClient;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.server.ServerContext;
+import org.apache.thrift.server.TServer;
+import org.apache.thrift.server.TServerEventHandler;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author HyunGil Jeong
+ */
+public abstract class ThriftEchoTestServer<T extends TServer> implements EchoTestServer {
+
+    private final T server;
+
+    protected final TestEnvironment environment;
+
+    protected ThriftEchoTestServer(T server, TestEnvironment environment) {
+        if (server == null) {
+            throw new IllegalArgumentException("server cannot be null");
+        }
+        this.server = server;
+        this.environment = environment;
+    }
+
+    @Override
+    public void start(ExecutorService executor) {
+        if (this.server.isServing()) {
+            return;
+        }
+
+        CountDownLatch waitToServeLatch = new CountDownLatch(1);
+        server.setServerEventHandler(new WaitToServeHandler(waitToServeLatch));
+
+        executor.execute(new Runnable() {
+            @Override
+            public void run() {
+                server.serve();
+            }
+        });
+
+        boolean started = false;
+        try {
+            started = waitToServeLatch.await(10, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+        if (!started) {
+            throw new IllegalStateException("Failed to start EchoThriftServer.");
+        }
+    }
+
+    @Override
+    public void stop() {
+        this.server.stop();
+    }
+
+    @Override
+    public void verifyTraces(PluginTestVerifier verifier) throws Exception {
+        this.verifyServerTraces(verifier);
+    }
+
+    protected abstract void verifyServerTraces(PluginTestVerifier verifier) throws Exception;
+
+    public abstract SyncEchoTestClient getSynchronousClient() throws TTransportException;
+
+    public abstract AsyncEchoTestClient getAsynchronousClient() throws IOException;
+
+
+    private class WaitToServeHandler implements TServerEventHandler {
+
+        private final CountDownLatch waitToServeLatch;
+
+        public WaitToServeHandler(CountDownLatch waitToServeLatch) {
+            this.waitToServeLatch = waitToServeLatch;
+        }
+
+        @Override
+        public void preServe() {
+            waitToServeLatch.countDown();
+        }
+
+        @Override
+        public ServerContext createContext(TProtocol tProtocol, TProtocol tProtocol1) {
+            return null;
+        }
+
+        @Override
+        public void deleteContext(ServerContext serverContext, TProtocol tProtocol, TProtocol tProtocol1) {
+
+        }
+
+        @Override
+        public void processContext(ServerContext serverContext, TTransport tTransport, TTransport tTransport1) {
+
+        }
+    }
+
+}
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/EchoTestRunner.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/EchoTestRunner.java
index 9a293cd0bf4c..0863134e5c4e 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/EchoTestRunner.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/EchoTestRunner.java
@@ -19,7 +19,6 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-import org.apache.thrift.server.TServer;
 import org.apache.thrift.transport.TTransportException;
 import org.junit.After;
 import org.junit.AfterClass;
@@ -35,21 +34,21 @@
 /**
  * @author HyunGil Jeong
  */
-public abstract class EchoTestRunner<T extends TServer> {
+public abstract class EchoTestRunner<T extends EchoTestServer> {
 
     private static ExecutorService SERVER_EXECUTOR;
 
-    private EchoTestServer<T> echoServer;
+    private T echoServer;
 
     PluginTestVerifier verifier;
 
     @BeforeClass
-    public static void setUpBeforeClass() throws InterruptedException {
+    public static void setUpBeforeClass() {
         SERVER_EXECUTOR = Executors.newSingleThreadExecutor();
     }
 
     @Before
-    public void setUp() throws TTransportException, InterruptedException {
+    public void setUp() throws TTransportException {
         this.echoServer = createEchoServer(new TestEnvironment());
         this.verifier = PluginTestVerifierHolder.getInstance();
         this.echoServer.start(SERVER_EXECUTOR);
@@ -67,17 +66,11 @@ public static void tearDownAfterClass() {
         SERVER_EXECUTOR.shutdown();
     }
 
-    protected String invokeEcho(String message) throws Exception {
-        final EchoTestClient echoClient = this.echoServer.getSynchronousClient();
-        return invokeAndVerify(echoClient, message);
+    protected T getServer() {
+        return echoServer;
     }
 
-    protected String invokeEchoAsync(String message) throws Exception {
-        final EchoTestClient echoClient = this.echoServer.getAsynchronousClient();
-        return invokeAndVerify(echoClient, message);
-    }
-
-    private String invokeAndVerify(EchoTestClient echoClient, String message) throws Exception {
+    protected String invokeAndVerify(EchoTestClient echoClient, String message) throws Exception {
         try {
             return echoClient.echo(message);
         } finally {
@@ -88,12 +81,13 @@ private String invokeAndVerify(EchoTestClient echoClient, String message) throws
         }
     }
 
-    protected abstract EchoTestServer<T> createEchoServer(TestEnvironment environment) throws TTransportException;
+    protected abstract T createEchoServer(TestEnvironment environment) throws TTransportException;
 
     private void verifyTraces(EchoTestClient echoClient, String expectedMessage) throws Exception {
         this.verifier.printCache(System.out);
         echoClient.verifyTraces(this.verifier, expectedMessage);
         this.echoServer.verifyTraces(this.verifier);
+        this.verifier.verifyTraceCount(0);
     }
 
 }
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftHalfSyncHalfAsyncServerAsyncIT.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftHalfSyncHalfAsyncServerAsyncIT.java
index 99a8078aecc3..b206fd80b5e4 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftHalfSyncHalfAsyncServerAsyncIT.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftHalfSyncHalfAsyncServerAsyncIT.java
@@ -19,13 +19,15 @@
 import static org.junit.Assert.assertEquals;
 
 import com.navercorp.pinpoint.plugin.AgentPath;
+import com.navercorp.pinpoint.plugin.thrift.common.client.AsyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.client.SyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.server.ThriftEchoTestServer;
 import org.apache.thrift.server.THsHaServer;
 import org.apache.thrift.transport.TTransportException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
-import com.navercorp.pinpoint.plugin.thrift.common.server.EchoTestServer;
 import com.navercorp.pinpoint.plugin.thrift.common.server.AsyncEchoTestServer.AsyncEchoTestServerFactory;
 import com.navercorp.pinpoint.plugin.thrift.it.EchoTestRunner;
 import com.navercorp.pinpoint.test.plugin.Dependency;
@@ -42,11 +44,12 @@
  */
 @RunWith(PinpointPluginTestSuite.class)
 @PinpointAgent(AgentPath.PATH)
-@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)", "log4j:log4j:1.2.16", "org.slf4j:slf4j-log4j12:1.7.22" })
-public class ThriftHalfSyncHalfAsyncServerAsyncIT extends EchoTestRunner<THsHaServer> {
+@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)",
+        "org.slf4j:slf4j-simple:1.6.6", "org.slf4j:log4j-over-slf4j:1.6.6", "org.slf4j:slf4j-api:1.6.6" })
+public class ThriftHalfSyncHalfAsyncServerAsyncIT extends EchoTestRunner<ThriftEchoTestServer<THsHaServer>> {
 
     @Override
-    protected EchoTestServer<THsHaServer> createEchoServer(TestEnvironment environment) throws TTransportException {
+    protected ThriftEchoTestServer<THsHaServer> createEchoServer(TestEnvironment environment) throws TTransportException {
         return AsyncEchoTestServerFactory.halfSyncHalfAsyncServer(environment);
     }
 
@@ -55,7 +58,8 @@ public void testSynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEcho(expectedMessage);
+        final SyncEchoTestClient client = getServer().getSynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
@@ -65,7 +69,8 @@ public void testAsynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEchoAsync(expectedMessage);
+        final AsyncEchoTestClient client = getServer().getAsynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftNonblockingServerAsyncIT.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftNonblockingServerAsyncIT.java
index aa1ebdbd3583..f7cb6165ed6c 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftNonblockingServerAsyncIT.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftNonblockingServerAsyncIT.java
@@ -19,13 +19,15 @@
 import static org.junit.Assert.assertEquals;
 
 import com.navercorp.pinpoint.plugin.AgentPath;
+import com.navercorp.pinpoint.plugin.thrift.common.client.AsyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.client.SyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.server.ThriftEchoTestServer;
 import org.apache.thrift.server.TNonblockingServer;
 import org.apache.thrift.transport.TTransportException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
-import com.navercorp.pinpoint.plugin.thrift.common.server.EchoTestServer;
 import com.navercorp.pinpoint.plugin.thrift.common.server.AsyncEchoTestServer.AsyncEchoTestServerFactory;
 import com.navercorp.pinpoint.plugin.thrift.it.EchoTestRunner;
 import com.navercorp.pinpoint.test.plugin.Dependency;
@@ -42,11 +44,12 @@
  */
 @RunWith(PinpointPluginTestSuite.class)
 @PinpointAgent(AgentPath.PATH)
-@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)", "log4j:log4j:1.2.16", "org.slf4j:slf4j-log4j12:1.7.22" })
-public class ThriftNonblockingServerAsyncIT extends EchoTestRunner<TNonblockingServer> {
+@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)",
+        "org.slf4j:slf4j-simple:1.6.6", "org.slf4j:log4j-over-slf4j:1.6.6", "org.slf4j:slf4j-api:1.6.6" })
+public class ThriftNonblockingServerAsyncIT extends EchoTestRunner<ThriftEchoTestServer<TNonblockingServer>> {
 
     @Override
-    protected EchoTestServer<TNonblockingServer> createEchoServer(TestEnvironment environment)
+    protected ThriftEchoTestServer<TNonblockingServer> createEchoServer(TestEnvironment environment)
             throws TTransportException {
         return AsyncEchoTestServerFactory.nonblockingServer(environment);
     }
@@ -56,7 +59,8 @@ public void testSynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEcho(expectedMessage);
+        final SyncEchoTestClient client = getServer().getSynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
@@ -66,7 +70,8 @@ public void testAsynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEchoAsync(expectedMessage);
+        final AsyncEchoTestClient client = getServer().getAsynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftThreadedSelectorServerAsyncIT.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftThreadedSelectorServerAsyncIT.java
index 2dd4088a3b08..188ac280845e 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftThreadedSelectorServerAsyncIT.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftThreadedSelectorServerAsyncIT.java
@@ -19,13 +19,15 @@
 import static org.junit.Assert.assertEquals;
 
 import com.navercorp.pinpoint.plugin.AgentPath;
+import com.navercorp.pinpoint.plugin.thrift.common.client.AsyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.client.SyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.server.ThriftEchoTestServer;
 import org.apache.thrift.server.TThreadedSelectorServer;
 import org.apache.thrift.transport.TTransportException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
-import com.navercorp.pinpoint.plugin.thrift.common.server.EchoTestServer;
 import com.navercorp.pinpoint.plugin.thrift.common.server.AsyncEchoTestServer.AsyncEchoTestServerFactory;
 import com.navercorp.pinpoint.plugin.thrift.it.EchoTestRunner;
 import com.navercorp.pinpoint.test.plugin.Dependency;
@@ -42,11 +44,12 @@
  */
 @RunWith(PinpointPluginTestSuite.class)
 @PinpointAgent(AgentPath.PATH)
-@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)", "log4j:log4j:1.2.16", "org.slf4j:slf4j-log4j12:1.7.22" })
-public class ThriftThreadedSelectorServerAsyncIT extends EchoTestRunner<TThreadedSelectorServer> {
+@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)",
+        "org.slf4j:slf4j-simple:1.6.6", "org.slf4j:log4j-over-slf4j:1.6.6", "org.slf4j:slf4j-api:1.6.6" })
+public class ThriftThreadedSelectorServerAsyncIT extends EchoTestRunner<ThriftEchoTestServer<TThreadedSelectorServer>> {
 
     @Override
-    protected EchoTestServer<TThreadedSelectorServer> createEchoServer(TestEnvironment environment)
+    protected ThriftEchoTestServer<TThreadedSelectorServer> createEchoServer(TestEnvironment environment)
             throws TTransportException {
         return AsyncEchoTestServerFactory.threadedSelectorServer(environment);
     }
@@ -56,7 +59,8 @@ public void testSynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEcho(expectedMessage);
+        final SyncEchoTestClient client = getServer().getSynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
@@ -66,7 +70,8 @@ public void testAsynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEchoAsync(expectedMessage);
+        final AsyncEchoTestClient client = getServer().getAsynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/http/ThriftHttpIT.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/http/ThriftHttpIT.java
new file mode 100644
index 000000000000..df3b3b738eb9
--- /dev/null
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/http/ThriftHttpIT.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2018 NAVER Corp.
+ *
+ * Licensed 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 com.navercorp.pinpoint.plugin.thrift.it.http;
+
+import com.navercorp.pinpoint.plugin.AgentPath;
+import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
+import com.navercorp.pinpoint.plugin.thrift.common.client.HttpEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.server.HttpEchoTestServer;
+import com.navercorp.pinpoint.plugin.thrift.it.EchoTestRunner;
+import com.navercorp.pinpoint.test.plugin.Dependency;
+import com.navercorp.pinpoint.test.plugin.JvmVersion;
+import com.navercorp.pinpoint.test.plugin.PinpointAgent;
+import com.navercorp.pinpoint.test.plugin.PinpointPluginTestSuite;
+import org.apache.thrift.transport.TTransportException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author HyunGil Jeong
+ */
+@RunWith(PinpointPluginTestSuite.class)
+@PinpointAgent(AgentPath.PATH)
+@JvmVersion(8)
+@Dependency({ "org.apache.thrift:libthrift:[0.9.1,)", "org.eclipse.jetty:jetty-server:9.2.11.v20150529",
+        "org.slf4j:slf4j-simple:1.6.6", "org.slf4j:log4j-over-slf4j:1.6.6", "org.slf4j:slf4j-api:1.6.6" })
+public class ThriftHttpIT extends EchoTestRunner<HttpEchoTestServer> {
+
+    @Override
+    protected HttpEchoTestServer createEchoServer(TestEnvironment environment) throws TTransportException {
+        return HttpEchoTestServer.createServer(environment);
+    }
+
+    @Test
+    public void testThriftHttpCall() throws Exception {
+        // Given
+        final String expectedMessage = "TEST_MESSAGE";
+        // When
+        final HttpEchoTestClient client = getServer().getHttpClient();
+        final String result = invokeAndVerify(client, expectedMessage);
+        // Then
+        assertEquals(expectedMessage, result);
+    }
+}
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftHalfSyncHalfAsyncServerIT.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftHalfSyncHalfAsyncServerIT.java
index ccf410aa0ae3..56016c3ed036 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftHalfSyncHalfAsyncServerIT.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftHalfSyncHalfAsyncServerIT.java
@@ -19,13 +19,15 @@
 import static org.junit.Assert.assertEquals;
 
 import com.navercorp.pinpoint.plugin.AgentPath;
+import com.navercorp.pinpoint.plugin.thrift.common.client.AsyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.client.SyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.server.ThriftEchoTestServer;
 import org.apache.thrift.server.THsHaServer;
 import org.apache.thrift.transport.TTransportException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
-import com.navercorp.pinpoint.plugin.thrift.common.server.EchoTestServer;
 import com.navercorp.pinpoint.plugin.thrift.common.server.SyncEchoTestServer.SyncEchoTestServerFactory;
 import com.navercorp.pinpoint.plugin.thrift.it.EchoTestRunner;
 import com.navercorp.pinpoint.test.plugin.Dependency;
@@ -42,11 +44,12 @@
  */
 @RunWith(PinpointPluginTestSuite.class)
 @PinpointAgent(AgentPath.PATH)
-@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)", "log4j:log4j:1.2.16", "org.slf4j:slf4j-log4j12:1.7.22" })
-public class ThriftHalfSyncHalfAsyncServerIT extends EchoTestRunner<THsHaServer> {
+@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)",
+        "org.slf4j:slf4j-simple:1.6.6", "org.slf4j:log4j-over-slf4j:1.6.6", "org.slf4j:slf4j-api:1.6.6" })
+public class ThriftHalfSyncHalfAsyncServerIT extends EchoTestRunner<ThriftEchoTestServer<THsHaServer>> {
 
     @Override
-    protected EchoTestServer<THsHaServer> createEchoServer(TestEnvironment environment) throws TTransportException {
+    protected ThriftEchoTestServer<THsHaServer> createEchoServer(TestEnvironment environment) throws TTransportException {
         return SyncEchoTestServerFactory.halfSyncHalfAsyncServer(environment);
     }
 
@@ -55,7 +58,8 @@ public void testSynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEcho(expectedMessage);
+        final SyncEchoTestClient client = getServer().getSynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
@@ -65,7 +69,8 @@ public void testAsynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEchoAsync(expectedMessage);
+        final AsyncEchoTestClient client = getServer().getAsynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftNonblockingServerIT.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftNonblockingServerIT.java
index 1843c26f8ec4..4cdd9a152167 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftNonblockingServerIT.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftNonblockingServerIT.java
@@ -19,13 +19,15 @@
 import static org.junit.Assert.assertEquals;
 
 import com.navercorp.pinpoint.plugin.AgentPath;
+import com.navercorp.pinpoint.plugin.thrift.common.client.AsyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.client.SyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.server.ThriftEchoTestServer;
 import org.apache.thrift.server.TNonblockingServer;
 import org.apache.thrift.transport.TTransportException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
-import com.navercorp.pinpoint.plugin.thrift.common.server.EchoTestServer;
 import com.navercorp.pinpoint.plugin.thrift.common.server.SyncEchoTestServer.SyncEchoTestServerFactory;
 import com.navercorp.pinpoint.plugin.thrift.it.EchoTestRunner;
 import com.navercorp.pinpoint.test.plugin.Dependency;
@@ -42,11 +44,12 @@
  */
 @RunWith(PinpointPluginTestSuite.class)
 @PinpointAgent(AgentPath.PATH)
-@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)", "log4j:log4j:1.2.16", "org.slf4j:slf4j-log4j12:1.7.22" })
-public class ThriftNonblockingServerIT extends EchoTestRunner<TNonblockingServer> {
+@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)",
+        "org.slf4j:slf4j-simple:1.6.6", "org.slf4j:log4j-over-slf4j:1.6.6", "org.slf4j:slf4j-api:1.6.6" })
+public class ThriftNonblockingServerIT extends EchoTestRunner<ThriftEchoTestServer<TNonblockingServer>> {
 
     @Override
-    protected EchoTestServer<TNonblockingServer> createEchoServer(TestEnvironment environment)
+    protected ThriftEchoTestServer<TNonblockingServer> createEchoServer(TestEnvironment environment)
             throws TTransportException {
         return SyncEchoTestServerFactory.nonblockingServer(environment);
     }
@@ -56,7 +59,8 @@ public void testSynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEcho(expectedMessage);
+        final SyncEchoTestClient client = getServer().getSynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
@@ -66,7 +70,8 @@ public void testAsynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEchoAsync(expectedMessage);
+        final AsyncEchoTestClient client = getServer().getAsynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftSimpleServerIT.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftSimpleServerIT.java
index 3fb02bce8c51..a71aef4ed0d6 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftSimpleServerIT.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftSimpleServerIT.java
@@ -19,13 +19,14 @@
 import static org.junit.Assert.*;
 
 import com.navercorp.pinpoint.plugin.AgentPath;
+import com.navercorp.pinpoint.plugin.thrift.common.client.SyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.server.ThriftEchoTestServer;
 import org.apache.thrift.server.TSimpleServer;
 import org.apache.thrift.transport.TTransportException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
-import com.navercorp.pinpoint.plugin.thrift.common.server.EchoTestServer;
 import com.navercorp.pinpoint.plugin.thrift.common.server.SyncEchoTestServer.SyncEchoTestServerFactory;
 import com.navercorp.pinpoint.plugin.thrift.it.EchoTestRunner;
 import com.navercorp.pinpoint.test.plugin.Dependency;
@@ -41,11 +42,12 @@
  */
 @RunWith(PinpointPluginTestSuite.class)
 @PinpointAgent(AgentPath.PATH)
-@Dependency({ "org.apache.thrift:libthrift:[0.9.1,)", "log4j:log4j:1.2.16", "org.slf4j:slf4j-log4j12:1.7.22" })
-public class ThriftSimpleServerIT extends EchoTestRunner<TSimpleServer> {
+@Dependency({ "org.apache.thrift:libthrift:[0.9.1,)",
+        "org.slf4j:slf4j-simple:1.6.6", "org.slf4j:log4j-over-slf4j:1.6.6", "org.slf4j:slf4j-api:1.6.6" })
+public class ThriftSimpleServerIT extends EchoTestRunner<ThriftEchoTestServer<TSimpleServer>> {
 
     @Override
-    protected EchoTestServer<TSimpleServer> createEchoServer(TestEnvironment environment) throws TTransportException {
+    protected ThriftEchoTestServer<TSimpleServer> createEchoServer(TestEnvironment environment) throws TTransportException {
         return SyncEchoTestServerFactory.simpleServer(environment);
     }
 
@@ -54,7 +56,8 @@ public void testSynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEcho(expectedMessage);
+        final SyncEchoTestClient client = getServer().getSynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftThreadPoolServerIT.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftThreadPoolServerIT.java
index d7117679a19d..7216ea4073d8 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftThreadPoolServerIT.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftThreadPoolServerIT.java
@@ -19,13 +19,14 @@
 import static org.junit.Assert.assertEquals;
 
 import com.navercorp.pinpoint.plugin.AgentPath;
+import com.navercorp.pinpoint.plugin.thrift.common.client.SyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.server.ThriftEchoTestServer;
 import org.apache.thrift.server.TThreadPoolServer;
 import org.apache.thrift.transport.TTransportException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
-import com.navercorp.pinpoint.plugin.thrift.common.server.EchoTestServer;
 import com.navercorp.pinpoint.plugin.thrift.common.server.SyncEchoTestServer.SyncEchoTestServerFactory;
 import com.navercorp.pinpoint.plugin.thrift.it.EchoTestRunner;
 import com.navercorp.pinpoint.test.plugin.Dependency;
@@ -41,11 +42,12 @@
  */
 @RunWith(PinpointPluginTestSuite.class)
 @PinpointAgent(AgentPath.PATH)
-@Dependency({ "org.apache.thrift:libthrift:[0.9.1,)", "log4j:log4j:1.2.16", "org.slf4j:slf4j-log4j12:1.7.22" })
-public class ThriftThreadPoolServerIT extends EchoTestRunner<TThreadPoolServer> {
+@Dependency({ "org.apache.thrift:libthrift:[0.9.1,)",
+        "org.slf4j:slf4j-simple:1.6.6", "org.slf4j:log4j-over-slf4j:1.6.6", "org.slf4j:slf4j-api:1.6.6" })
+public class ThriftThreadPoolServerIT extends EchoTestRunner<ThriftEchoTestServer<TThreadPoolServer>> {
 
     @Override
-    protected EchoTestServer<TThreadPoolServer> createEchoServer(TestEnvironment environment)
+    protected ThriftEchoTestServer<TThreadPoolServer> createEchoServer(TestEnvironment environment)
             throws TTransportException {
         return SyncEchoTestServerFactory.threadedPoolServer(environment);
     }
@@ -55,7 +57,8 @@ public void testSynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEcho(expectedMessage);
+        final SyncEchoTestClient client = getServer().getSynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
diff --git a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftThreadedSelectorServerIT.java b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftThreadedSelectorServerIT.java
index 652722c620ec..c08c3dae00b7 100644
--- a/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftThreadedSelectorServerIT.java
+++ b/agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftThreadedSelectorServerIT.java
@@ -19,13 +19,15 @@
 import static org.junit.Assert.assertEquals;
 
 import com.navercorp.pinpoint.plugin.AgentPath;
+import com.navercorp.pinpoint.plugin.thrift.common.client.AsyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.client.SyncEchoTestClient;
+import com.navercorp.pinpoint.plugin.thrift.common.server.ThriftEchoTestServer;
 import org.apache.thrift.server.TThreadedSelectorServer;
 import org.apache.thrift.transport.TTransportException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
-import com.navercorp.pinpoint.plugin.thrift.common.server.EchoTestServer;
 import com.navercorp.pinpoint.plugin.thrift.common.server.SyncEchoTestServer.SyncEchoTestServerFactory;
 import com.navercorp.pinpoint.plugin.thrift.it.EchoTestRunner;
 import com.navercorp.pinpoint.test.plugin.Dependency;
@@ -42,11 +44,12 @@
  */
 @RunWith(PinpointPluginTestSuite.class)
 @PinpointAgent(AgentPath.PATH)
-@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)", "log4j:log4j:1.2.16", "org.slf4j:slf4j-log4j12:1.7.22" })
-public class ThriftThreadedSelectorServerIT extends EchoTestRunner<TThreadedSelectorServer> {
+@Dependency({ "org.apache.thrift:libthrift:[0.10.0,)",
+        "org.slf4j:slf4j-simple:1.6.6", "org.slf4j:log4j-over-slf4j:1.6.6", "org.slf4j:slf4j-api:1.6.6" })
+public class ThriftThreadedSelectorServerIT extends EchoTestRunner<ThriftEchoTestServer<TThreadedSelectorServer>> {
 
     @Override
-    protected EchoTestServer<TThreadedSelectorServer> createEchoServer(TestEnvironment environment)
+    protected ThriftEchoTestServer<TThreadedSelectorServer> createEchoServer(TestEnvironment environment)
             throws TTransportException {
         return SyncEchoTestServerFactory.threadedSelectorServer(environment);
     }
@@ -56,7 +59,8 @@ public void testSynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEcho(expectedMessage);
+        final SyncEchoTestClient client = getServer().getSynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }
@@ -66,7 +70,8 @@ public void testAsynchronousRpcCall() throws Exception {
         // Given
         final String expectedMessage = "TEST_MESSAGE";
         // When
-        final String result = super.invokeEchoAsync(expectedMessage);
+        final AsyncEchoTestClient client = getServer().getAsynchronousClient();
+        final String result = invokeAndVerify(client, expectedMessage);
         // Then
         assertEquals(expectedMessage, result);
     }