Add trace support when using Thrift over HTTP
#### 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.
agent-it/pom.xml plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftClientCallContext.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftConstants.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftPlugin.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftTypeProvider.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftUtils.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/client/TServiceClientSendBaseInterceptor.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/server/TBaseProcessorProcessInterceptor.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/tprotocol/server/TProtocolReadMessageEndInterceptor.java
agent-it/pom.xml plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftClientCallContext.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftConstants.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftPlugin.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftTypeProvider.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/ThriftUtils.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/field/getter/UrlFieldGetter.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/client/TServiceClientSendBaseInterceptor.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/server/TBaseProcessorProcessInterceptor.java plugins/thrift/src/main/java/com/navercorp/pinpoint/plugin/thrift/interceptor/tprotocol/server/TProtocolReadMessageEndInterceptor.java
agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/TestEnvironment.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/AsyncEchoTestClient.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/HttpEchoTestClient.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/client/SyncEchoTestClient.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/AsyncEchoTestServer.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/EchoTestServer.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/HttpEchoTestServer.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/SyncEchoTestServer.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/common/server/ThriftEchoTestServer.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/EchoTestRunner.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftHalfSyncHalfAsyncServerAsyncIT.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftNonblockingServerAsyncIT.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/asynchronous/ThriftThreadedSelectorServerAsyncIT.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/http/ThriftHttpIT.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftHalfSyncHalfAsyncServerIT.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftNonblockingServerIT.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftSimpleServerIT.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftThreadPoolServerIT.java agent-it/src/test/java/com/navercorp/pinpoint/plugin/thrift/it/synchronous/ThriftThreadedSelectorServerIT.java
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;
         }

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);
     }
