This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch revert-14082-delete_remoting_http
in repository https://gitbox.apache.org/repos/asf/dubbo.git

commit b287491ed29d47e6cc551867ca7ca42401ea068f
Author: Albumen Kevin <jhq0...@gmail.com>
AuthorDate: Wed May 8 10:48:05 2024 +0800

    Revert "remove the remoting-http (#14082)"
    
    This reverts commit 583b57be2b9d4f0ccbc9be2bc6e4a190fd5e27c8.
---
 .artifacts                                         |   1 +
 .../dubbo-demo-native-consumer/pom.xml             |   5 +
 .../dubbo-demo-native-provider/pom.xml             |   6 +
 dubbo-distribution/dubbo-all-shaded/pom.xml        |   6 +
 dubbo-distribution/dubbo-all/pom.xml               |  14 +
 dubbo-distribution/dubbo-core-spi/pom.xml          |   6 +
 dubbo-remoting/dubbo-remoting-http/pom.xml         |  85 ++++++
 .../apache/dubbo/remoting/http/BaseRestClient.java |  41 +++
 .../org/apache/dubbo/remoting/http/HttpBinder.java |  39 +++
 .../apache/dubbo/remoting/http/HttpHandler.java    |  34 +++
 .../org/apache/dubbo/remoting/http/HttpServer.java |  71 +++++
 .../dubbo/remoting/http/RequestTemplate.java       | 299 +++++++++++++++++++++
 .../org/apache/dubbo/remoting/http/RestClient.java |  48 ++++
 .../org/apache/dubbo/remoting/http/RestResult.java |  42 +++
 .../remoting/http/config/HttpClientConfig.java     |  60 +++++
 .../http/factory/AbstractHttpClientFactory.java    |  63 +++++
 .../remoting/http/factory/RestClientFactory.java   |  35 +++
 .../http/factory/impl/ApacheHttpClientFactory.java |  35 +++
 .../http/factory/impl/OkHttpClientFactory.java     |  35 +++
 .../factory/impl/URLConnectionClientFactory.java   |  35 +++
 .../dubbo/remoting/http/jetty/JettyHttpBinder.java |  33 +++
 .../dubbo/remoting/http/jetty/JettyHttpServer.java | 113 ++++++++
 .../remoting/http/jetty/JettyLoggerAdapter.java    | 161 +++++++++++
 .../http/restclient/HttpClientRestClient.java      | 188 +++++++++++++
 .../remoting/http/restclient/OKHttpRestClient.java | 150 +++++++++++
 .../http/restclient/URLConnectionRestClient.java   | 170 ++++++++++++
 .../remoting/http/servlet/BootstrapListener.java   |  37 +++
 .../remoting/http/servlet/DispatcherServlet.java   |  65 +++++
 .../remoting/http/servlet/ServletHttpBinder.java   |  33 +++
 .../remoting/http/servlet/ServletHttpServer.java   |  30 +++
 .../remoting/http/servlet/ServletManager.java      |  50 ++++
 .../remoting/http/support/AbstractHttpServer.java  | 124 +++++++++
 .../remoting/http/tomcat/TomcatHttpBinder.java     |  30 +++
 .../remoting/http/tomcat/TomcatHttpServer.java     | 100 +++++++
 .../org.apache.dubbo.remoting.http.HttpBinder      |   3 +
 ...e.dubbo.remoting.http.factory.RestClientFactory |   4 +
 .../remoting/http/jetty/JettyHttpBinderTest.java   |  57 ++++
 .../http/jetty/JettyLoggerAdapterTest.java         | 127 +++++++++
 .../dubbo/remoting/http/rest/RestClientTest.java   | 240 +++++++++++++++++
 .../remoting/http/tomcat/TomcatHttpBinderTest.java |  60 +++++
 .../src/test/resources/log4j2-test.xml             |  29 ++
 dubbo-remoting/pom.xml                             |   1 +
 dubbo-test/dubbo-dependencies-all/pom.xml          |   5 +
 43 files changed, 2770 insertions(+)

diff --git a/.artifacts b/.artifacts
index ba6f6896e5..cd6334693d 100644
--- a/.artifacts
+++ b/.artifacts
@@ -76,6 +76,7 @@ dubbo-registry-nacos
 dubbo-registry-zookeeper
 dubbo-remoting
 dubbo-remoting-api
+dubbo-remoting-http
 dubbo-remoting-http12
 dubbo-remoting-netty
 dubbo-remoting-netty4
diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml 
b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml
index ab13f35f2d..1b2d730c85 100644
--- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml
+++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-consumer/pom.xml
@@ -133,6 +133,11 @@
       <artifactId>dubbo-filter-validation</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.dubbo</groupId>
+      <artifactId>dubbo-remoting-http</artifactId>
+      <version>${project.version}</version>
+    </dependency>
 
     <dependency>
       <groupId>com.alibaba</groupId>
diff --git a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml 
b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml
index 39471b6dbd..da5c091b11 100644
--- a/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml
+++ b/dubbo-demo/dubbo-demo-native/dubbo-demo-native-provider/pom.xml
@@ -133,6 +133,12 @@
       <artifactId>dubbo-filter-validation</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.dubbo</groupId>
+      <artifactId>dubbo-remoting-http</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
     <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>fastjson</artifactId>
diff --git a/dubbo-distribution/dubbo-all-shaded/pom.xml 
b/dubbo-distribution/dubbo-all-shaded/pom.xml
index 8fa70f3390..b059115692 100644
--- a/dubbo-distribution/dubbo-all-shaded/pom.xml
+++ b/dubbo-distribution/dubbo-all-shaded/pom.xml
@@ -729,6 +729,9 @@
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser</resource>
                 </transformer>
+                <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                  
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory</resource>
+                </transformer>
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.ChannelHandler</resource>
                 </transformer>
@@ -759,6 +762,9 @@
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger</resource>
                 </transformer>
+                <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                  
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http.HttpBinder</resource>
+                </transformer>
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageEncoderFactory</resource>
                 </transformer>
diff --git a/dubbo-distribution/dubbo-all/pom.xml 
b/dubbo-distribution/dubbo-all/pom.xml
index 6e60992f60..19fd7dbbfc 100644
--- a/dubbo-distribution/dubbo-all/pom.xml
+++ b/dubbo-distribution/dubbo-all/pom.xml
@@ -343,6 +343,13 @@
       <scope>compile</scope>
       <optional>true</optional>
     </dependency>
+    <dependency>
+      <groupId>org.apache.dubbo</groupId>
+      <artifactId>dubbo-remoting-http</artifactId>
+      <version>${project.version}</version>
+      <scope>compile</scope>
+      <optional>true</optional>
+    </dependency>
     <dependency>
       <groupId>org.apache.dubbo</groupId>
       <artifactId>dubbo-remoting-http12</artifactId>
@@ -546,6 +553,7 @@
                   <include>org.apache.dubbo:dubbo-registry-nacos</include>
                   <include>org.apache.dubbo:dubbo-registry-zookeeper</include>
                   <include>org.apache.dubbo:dubbo-remoting-api</include>
+                  <include>org.apache.dubbo:dubbo-remoting-http</include>
                   <include>org.apache.dubbo:dubbo-remoting-http12</include>
                   <include>org.apache.dubbo:dubbo-remoting-netty4</include>
                   <include>org.apache.dubbo:dubbo-remoting-netty</include>
@@ -776,6 +784,9 @@
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser</resource>
                 </transformer>
+                <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                  
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory</resource>
+                </transformer>
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.ChannelHandler</resource>
                 </transformer>
@@ -806,6 +817,9 @@
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger</resource>
                 </transformer>
+                <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                  
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http.HttpBinder</resource>
+                </transformer>
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageEncoderFactory</resource>
                 </transformer>
diff --git a/dubbo-distribution/dubbo-core-spi/pom.xml 
b/dubbo-distribution/dubbo-core-spi/pom.xml
index 9ffea1d76c..6ef9c5d928 100644
--- a/dubbo-distribution/dubbo-core-spi/pom.xml
+++ b/dubbo-distribution/dubbo-core-spi/pom.xml
@@ -345,6 +345,9 @@
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser</resource>
                 </transformer>
+                <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                  
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory</resource>
+                </transformer>
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.ChannelHandler</resource>
                 </transformer>
@@ -375,6 +378,9 @@
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger</resource>
                 </transformer>
+                <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                  
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http.HttpBinder</resource>
+                </transformer>
                 <transformer 
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                   
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageEncoderFactory</resource>
                 </transformer>
diff --git a/dubbo-remoting/dubbo-remoting-http/pom.xml 
b/dubbo-remoting/dubbo-remoting-http/pom.xml
new file mode 100644
index 0000000000..91834389bb
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-http/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.dubbo</groupId>
+    <artifactId>dubbo-remoting</artifactId>
+    <version>${revision}</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+  <artifactId>dubbo-remoting-http</artifactId>
+  <packaging>jar</packaging>
+  <name>${project.artifactId}</name>
+  <description>The http remoting module of dubbo project</description>
+  <properties>
+    <skip_maven_deploy>false</skip_maven_deploy>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.dubbo</groupId>
+      <artifactId>dubbo-common</artifactId>
+      <version>${project.parent.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.dubbo</groupId>
+      <artifactId>dubbo-remoting-api</artifactId>
+      <version>${project.parent.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-server</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.tomcat.embed</groupId>
+      <artifactId>tomcat-embed-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>fluent-hc</artifactId>
+      <version>4.5.14</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-slf4j-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>com.squareup.okhttp3</groupId>
+      <artifactId>okhttp</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.dubbo</groupId>
+      <artifactId>dubbo-rpc-api</artifactId>
+      <version>${project.parent.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+    </dependency>
+
+  </dependencies>
+</project>
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/BaseRestClient.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/BaseRestClient.java
new file mode 100644
index 0000000000..23c78419ad
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/BaseRestClient.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+
+public abstract class BaseRestClient<CLIENT> implements RestClient {
+
+    protected CLIENT client;
+
+    protected HttpClientConfig clientConfig;
+
+    public BaseRestClient(HttpClientConfig clientConfig) {
+        this.clientConfig = clientConfig;
+        client = createHttpClient(clientConfig);
+    }
+
+    protected abstract CLIENT createHttpClient(HttpClientConfig clientConfig);
+
+    public HttpClientConfig getClientConfig() {
+        return clientConfig;
+    }
+
+    public CLIENT getClient() {
+        return client;
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpBinder.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpBinder.java
new file mode 100644
index 0000000000..26db36d92c
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpBinder.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Adaptive;
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.remoting.Constants;
+
+/**
+ * HttpBinder
+ */
+@SPI(value = "jetty", scope = ExtensionScope.FRAMEWORK)
+public interface HttpBinder {
+
+    /**
+     * bind the server.
+     *
+     * @param url server url.
+     * @return server.
+     */
+    @Adaptive({Constants.SERVER_KEY})
+    HttpServer bind(URL url, HttpHandler handler);
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpHandler.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpHandler.java
new file mode 100644
index 0000000000..27085f67bc
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpHandler.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import java.io.IOException;
+
+/**
+ * http invocation handler.
+ */
+public interface HttpHandler<REQUEST, RESPONSE> {
+
+    /**
+     * invoke.
+     *
+     * @param request  request.
+     * @param response response.
+     * @throws IOException
+     */
+    void handle(REQUEST request, RESPONSE response) throws IOException;
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpServer.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpServer.java
new file mode 100644
index 0000000000..d85582eb76
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpServer.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import org.apache.dubbo.common.Resetable;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.RemotingServer;
+
+import java.net.InetSocketAddress;
+
+public interface HttpServer extends Resetable, RemotingServer {
+
+    /**
+     * get http handler.
+     *
+     * @return http handler.
+     */
+    HttpHandler getHttpHandler();
+
+    /**
+     * get url.
+     *
+     * @return url
+     */
+    URL getUrl();
+
+    /**
+     * get local address.
+     *
+     * @return local address.
+     */
+    InetSocketAddress getLocalAddress();
+
+    /**
+     * close the channel.
+     */
+    void close();
+
+    /**
+     * Graceful close the channel.
+     */
+    void close(int timeout);
+
+    /**
+     * is bound.
+     *
+     * @return bound
+     */
+    boolean isBound();
+
+    /**
+     * is closed.
+     *
+     * @return closed
+     */
+    boolean isClosed();
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java
new file mode 100644
index 0000000000..5536e6c83e
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.rpc.Invocation;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RequestTemplate implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String CONTENT_ENCODING = "Content-Encoding";
+    private static final String CONTENT_LENGTH = "Content-Length";
+    public static final String ENCODING_GZIP = "gzip";
+    public static final String ENCODING_DEFLATE = "deflate";
+    private static final List<String> EMPTY_ARRAYLIST = new ArrayList<>();
+
+    private final Map<String, Collection<String>> queries = new 
LinkedHashMap<>();
+    private final Map<String, Collection<String>> headers = new 
LinkedHashMap<>();
+    private String httpMethod;
+    private String path;
+    private String address;
+    private Object body;
+    private byte[] byteBody = new byte[0];
+    private String protocol = "http://";;
+    private final Invocation invocation;
+    private String contextPath = "";
+    private Class<?> bodyType;
+
+    public RequestTemplate(Invocation invocation, String httpMethod, String 
address) {
+        this(invocation, httpMethod, address, "");
+    }
+
+    public RequestTemplate(Invocation invocation, String httpMethod, String 
address, String contextPath) {
+        this.httpMethod = httpMethod;
+        this.address = address;
+        this.invocation = invocation;
+        this.contextPath = contextPath;
+    }
+
+    public String getURL() {
+        StringBuilder stringBuilder = new StringBuilder(getProtocol() + 
address);
+
+        stringBuilder.append(getUri());
+        return stringBuilder.toString();
+    }
+
+    public String getUri() {
+        StringBuilder stringBuilder = new StringBuilder(getContextPath() + 
path);
+        return stringBuilder.append(getQueryString()).toString();
+    }
+
+    public String getQueryString() {
+
+        if (queries.isEmpty()) {
+            return "";
+        }
+
+        StringBuilder queryBuilder = new StringBuilder("?");
+        for (String field : queries.keySet()) {
+
+            Collection<String> queryValues = queries.get(field);
+
+            if (queryValues == null || queryValues.isEmpty()) {
+                continue;
+            }
+
+            for (String value : queryValues) {
+                queryBuilder.append('&');
+                queryBuilder.append(field);
+                if (value == null) {
+                    continue;
+                }
+
+                queryBuilder.append('=');
+                queryBuilder.append(value);
+            }
+        }
+
+        return queryBuilder.toString().replace("?&", "?");
+    }
+
+    public RequestTemplate path(String path) {
+        this.path = path;
+        return this;
+    }
+
+    public String getHttpMethod() {
+        return httpMethod;
+    }
+
+    public RequestTemplate httpMethod(String httpMethod) {
+        this.httpMethod = httpMethod;
+        return this;
+    }
+
+    public byte[] getSerializedBody() {
+        return byteBody;
+    }
+
+    public void serializeBody(byte[] body) {
+        addHeader(CONTENT_LENGTH, body.length); // must header
+        this.byteBody = body;
+    }
+
+    public boolean isBodyEmpty() {
+        return getUnSerializedBody() == null;
+    }
+
+    public RequestTemplate body(Object body, Class bodyType) {
+        this.body = body;
+        setBodyType(bodyType);
+        return this;
+    }
+
+    public Object getUnSerializedBody() {
+        return body;
+    }
+
+    public Map<String, Collection<String>> getAllHeaders() {
+        return headers;
+    }
+
+    public Collection<String> getHeaders(String name) {
+        return headers.get(name);
+    }
+
+    public String getHeader(String name) {
+        if (headers.containsKey(name)) {
+
+            Collection<String> headers = getHeaders(name);
+
+            if (headers.isEmpty()) {
+                return null;
+            }
+            String[] strings = headers.toArray(new String[0]);
+            return strings[0];
+
+        } else {
+            return null;
+        }
+    }
+
+    public Collection<String> getEncodingValues() {
+        if (headers.containsKey(CONTENT_ENCODING)) {
+            return headers.get(CONTENT_ENCODING);
+        }
+        return EMPTY_ARRAYLIST;
+    }
+
+    public boolean isGzipEncodedRequest() {
+        return getEncodingValues().contains(ENCODING_GZIP);
+    }
+
+    public boolean isDeflateEncodedRequest() {
+        return getEncodingValues().contains(ENCODING_DEFLATE);
+    }
+
+    public void addHeader(String key, String value) {
+        addValueByKey(key, value, this.headers);
+    }
+
+    public void addHeader(String key, Object value) {
+        addValueByKey(key, String.valueOf(value), this.headers);
+    }
+
+    public void addKeepAliveHeader(int time) {
+        addHeader(Constants.KEEP_ALIVE_HEADER, time);
+        addHeader(Constants.CONNECTION, Constants.KEEP_ALIVE);
+    }
+
+    public void addHeaders(String key, Collection<String> values) {
+        Collection<String> header = getHeaders(key);
+
+        if (header == null) {
+            header = new HashSet<>();
+            this.headers.put(key, header);
+        }
+        header.addAll(values);
+    }
+
+    public void addParam(String key, String value) {
+        addValueByKey(key, value, this.queries);
+    }
+
+    public void addParam(String key, Object value) {
+        addParam(key, String.valueOf(value));
+    }
+
+    public Map<String, Collection<String>> getQueries() {
+        return queries;
+    }
+
+    public Collection<String> getParam(String key) {
+        return getQueries().get(key);
+    }
+
+    public void addParams(String key, Collection<String> values) {
+        Collection<String> params = getParam(key);
+
+        if (params == null) {
+            params = new HashSet<>();
+            this.queries.put(key, params);
+        }
+        params.addAll(values);
+    }
+
+    public void addValueByKey(String key, String value, Map<String, 
Collection<String>> maps) {
+
+        if (value == null) {
+            return;
+        }
+
+        Collection<String> values = null;
+        if (!maps.containsKey(key)) {
+            values = new HashSet<>();
+            maps.put(key, values);
+        }
+        values = maps.get(key);
+
+        values.add(value);
+    }
+
+    public Integer getContentLength() {
+
+        if (!getAllHeaders().containsKey(CONTENT_LENGTH)) {
+            return null;
+        }
+
+        HashSet<String> strings = (HashSet<String>) 
getAllHeaders().get(CONTENT_LENGTH);
+
+        return Integer.parseInt(new ArrayList<>(strings).get(0));
+    }
+
+    public String getAddress() {
+        return address;
+    }
+
+    public void setAddress(String address) {
+        addHeader("Host", address); // must header
+        this.address = address;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public Invocation getInvocation() {
+        return invocation;
+    }
+
+    public String getContextPath() {
+        if (contextPath == null || contextPath.length() == 0) {
+            return "";
+        }
+
+        if (contextPath.startsWith("/")) {
+            return contextPath;
+        } else {
+            return "/" + contextPath;
+        }
+    }
+
+    public void setContextPath(String contextPath) {
+        this.contextPath = contextPath;
+    }
+
+    public Class<?> getBodyType() {
+        return bodyType;
+    }
+
+    public void setBodyType(Class<?> bodyType) {
+        this.bodyType = bodyType;
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestClient.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestClient.java
new file mode 100644
index 0000000000..e5b8cecf7d
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestClient.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import org.apache.dubbo.remoting.RemotingException;
+
+import java.util.concurrent.CompletableFuture;
+
+public interface RestClient {
+    /**
+     * send message.
+     *
+     * @param message
+     * @throws RemotingException
+     */
+    CompletableFuture<RestResult> send(RequestTemplate message);
+
+    /**
+     * close the channel.
+     */
+    void close();
+
+    /**
+     * Graceful close the channel.
+     */
+    void close(int timeout);
+
+    /**
+     * is closed.
+     *
+     * @return closed
+     */
+    boolean isClosed();
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java
new file mode 100644
index 0000000000..bb057675df
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * rest response facade
+ */
+public interface RestResult {
+    String getContentType();
+
+    byte[] getBody() throws IOException;
+
+    Map<String, List<String>> headers();
+
+    byte[] getErrorResponse() throws IOException;
+
+    int getResponseCode() throws IOException;
+
+    String getMessage() throws IOException;
+
+    default String appendErrorMessage(String message, String errorInfo) {
+        return message + "\n error info is: " + errorInfo;
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/config/HttpClientConfig.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/config/HttpClientConfig.java
new file mode 100644
index 0000000000..09e0ab92a1
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/config/HttpClientConfig.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.config;
+
+public class HttpClientConfig {
+    private int readTimeout = 6 * 1000;
+    private int writeTimeout = 6 * 1000;
+    private int connectTimeout = 6 * 1000;
+    private int chunkLength = 8196;
+
+    private int HTTP_CLIENT_CONNECTION_MANAGER_MAX_PER_ROUTE = 20;
+    private int HTTP_CLIENT_CONNECTION_MANAGER_MAX_TOTAL = 20;
+    private int HTTPCLIENT_KEEP_ALIVE_DURATION = 30 * 1000;
+    private int HTTP_CLIENT_CONNECTION_MANAGER_CLOSE_WAIT_TIME_MS = 1000;
+    private int HTTP_CLIENT_CONNECTION_MANAGER_CLOSE_IDLE_TIME_S = 30;
+
+    public HttpClientConfig() {}
+
+    public int getReadTimeout() {
+        return readTimeout;
+    }
+
+    public void setReadTimeout(int readTimeout) {
+        this.readTimeout = readTimeout;
+    }
+
+    public int getWriteTimeout() {
+        return writeTimeout;
+    }
+
+    public void setWriteTimeout(int writeTimeout) {
+        this.writeTimeout = writeTimeout;
+    }
+
+    public int getConnectTimeout() {
+        return connectTimeout;
+    }
+
+    public void setConnectTimeout(int connectTimeout) {
+        this.connectTimeout = connectTimeout;
+    }
+
+    public int getChunkLength() {
+        return chunkLength;
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.java
new file mode 100644
index 0000000000..ad9e7f7821
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.factory;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+import org.apache.dubbo.rpc.RpcException;
+
+/**
+ * AbstractHttpClientFactory
+ */
+public abstract class AbstractHttpClientFactory implements RestClientFactory {
+
+    protected final ErrorTypeAwareLogger logger = 
LoggerFactory.getErrorTypeAwareLogger(getClass());
+
+    // TODO   load config
+    protected HttpClientConfig httpClientConfig = new HttpClientConfig();
+
+    //////////////////////////////////////// implements start 
///////////////////////////////////////////////
+    @Override
+    public RestClient createRestClient(URL url) throws RpcException {
+
+        beforeCreated(url);
+
+        // create a raw client
+        RestClient restClient = doCreateRestClient(url);
+
+        // postprocessor
+        afterCreated(restClient);
+
+        return restClient;
+    }
+
+    //////////////////////////////////////// implements end 
///////////////////////////////////////////////
+
+    ////////////////////////////////////////   inner methods  
///////////////////////////////////////////////
+
+    protected void beforeCreated(URL url) {}
+
+    protected abstract RestClient doCreateRestClient(URL url) throws 
RpcException;
+
+    protected void afterCreated(RestClient client) {}
+
+    ////////////////////////////////////////   inner methods  
///////////////////////////////////////////////
+
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/RestClientFactory.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/RestClientFactory.java
new file mode 100644
index 0000000000..c64453dbda
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/RestClientFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.factory;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Adaptive;
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.rpc.RpcException;
+
+/**
+ * RestClientFactory. (API/SPI, Singleton, ThreadSafe)
+ */
+@SPI(value = Constants.OK_HTTP, scope = ExtensionScope.FRAMEWORK)
+public interface RestClientFactory {
+
+    @Adaptive({Constants.CLIENT_KEY})
+    RestClient createRestClient(URL url) throws RpcException;
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/ApacheHttpClientFactory.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/ApacheHttpClientFactory.java
new file mode 100644
index 0000000000..89d9c36f6f
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/ApacheHttpClientFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.factory.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.factory.AbstractHttpClientFactory;
+import org.apache.dubbo.remoting.http.restclient.HttpClientRestClient;
+import org.apache.dubbo.rpc.RpcException;
+
+@Activate(Constants.APACHE_HTTP_CLIENT)
+public class ApacheHttpClientFactory extends AbstractHttpClientFactory {
+
+    @Override
+    protected RestClient doCreateRestClient(URL url) throws RpcException {
+
+        return new HttpClientRestClient(httpClientConfig);
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/OkHttpClientFactory.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/OkHttpClientFactory.java
new file mode 100644
index 0000000000..523df3caf8
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/OkHttpClientFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.factory.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.factory.AbstractHttpClientFactory;
+import org.apache.dubbo.remoting.http.restclient.OKHttpRestClient;
+import org.apache.dubbo.rpc.RpcException;
+
+@Activate(Constants.OK_HTTP)
+public class OkHttpClientFactory extends AbstractHttpClientFactory {
+
+    @Override
+    protected RestClient doCreateRestClient(URL url) throws RpcException {
+
+        return new OKHttpRestClient(httpClientConfig);
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/URLConnectionClientFactory.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/URLConnectionClientFactory.java
new file mode 100644
index 0000000000..40c2a82a66
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/URLConnectionClientFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.factory.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.factory.AbstractHttpClientFactory;
+import org.apache.dubbo.remoting.http.restclient.URLConnectionRestClient;
+import org.apache.dubbo.rpc.RpcException;
+
+@Activate(Constants.URL_CONNECTION)
+public class URLConnectionClientFactory extends AbstractHttpClientFactory {
+
+    @Override
+    protected RestClient doCreateRestClient(URL url) throws RpcException {
+
+        return new URLConnectionRestClient(httpClientConfig);
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinder.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinder.java
new file mode 100644
index 0000000000..05e5dc5a35
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinder.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.jetty;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.http.HttpBinder;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+/**
+ * JettyHttpTransporter
+ */
+public class JettyHttpBinder implements HttpBinder {
+
+    @Override
+    public HttpServer bind(URL url, HttpHandler handler) {
+        return new JettyHttpServer(url, handler);
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpServer.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpServer.java
new file mode 100644
index 0000000000..0139a5d79f
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpServer.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.jetty;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.servlet.DispatcherServlet;
+import org.apache.dubbo.remoting.http.servlet.ServletManager;
+import org.apache.dubbo.remoting.http.support.AbstractHttpServer;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+import static 
org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREADS;
+import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
+import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_FAILED_STOP_HTTP_SERVER;
+
+public class JettyHttpServer extends AbstractHttpServer {
+
+    private static final ErrorTypeAwareLogger logger = 
LoggerFactory.getErrorTypeAwareLogger(JettyHttpServer.class);
+
+    private Server server;
+
+    private URL url;
+
+    public JettyHttpServer(URL url, final HttpHandler handler) {
+        super(url, handler);
+        this.url = url;
+
+        // set dubbo's logger
+        System.setProperty("org.eclipse.jetty.util.log.class", 
JettyLoggerAdapter.class.getName());
+
+        
DispatcherServlet.addHttpHandler(url.getParameter(Constants.BIND_PORT_KEY, 
url.getPort()), handler);
+
+        int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS);
+        QueuedThreadPool threadPool = new QueuedThreadPool();
+        threadPool.setDaemon(true);
+        threadPool.setMaxThreads(threads);
+        threadPool.setMinThreads(threads);
+
+        server = new Server(threadPool);
+
+        ServerConnector connector = new ServerConnector(server);
+
+        String bindIp = url.getParameter(Constants.BIND_IP_KEY, url.getHost());
+        if (!url.isAnyHost() && NetUtils.isValidLocalHost(bindIp)) {
+            connector.setHost(bindIp);
+        }
+        connector.setPort(url.getParameter(Constants.BIND_PORT_KEY, 
url.getPort()));
+
+        server.addConnector(connector);
+
+        ServletHandler servletHandler = new ServletHandler();
+        ServletHolder servletHolder = 
servletHandler.addServletWithMapping(DispatcherServlet.class, "/*");
+        servletHolder.setInitOrder(2);
+
+        // dubbo's original impl can't support the use of ServletContext
+        //        server.addHandler(servletHandler);
+        // TODO Context.SESSIONS is the best option here? (In jetty 9.x, it 
becomes ServletContextHandler.SESSIONS)
+        ServletContextHandler context = new ServletContextHandler(server, "/", 
ServletContextHandler.SESSIONS);
+        context.setServletHandler(servletHandler);
+        ServletManager.getInstance()
+                .addServletContext(
+                        url.getParameter(Constants.BIND_PORT_KEY, 
url.getPort()), context.getServletContext());
+
+        try {
+            server.start();
+        } catch (Exception e) {
+            throw new IllegalStateException(
+                    "Failed to start jetty server on " + 
url.getParameter(Constants.BIND_IP_KEY) + ":"
+                            + url.getParameter(Constants.BIND_PORT_KEY) + ", 
cause: " + e.getMessage(),
+                    e);
+        }
+    }
+
+    @Override
+    public void close() {
+        super.close();
+
+        //
+        
ServletManager.getInstance().removeServletContext(url.getParameter(Constants.BIND_PORT_KEY,
 url.getPort()));
+
+        if (server != null) {
+            try {
+                server.stop();
+            } catch (Exception e) {
+                logger.warn(COMMON_FAILED_STOP_HTTP_SERVER, "", "", 
e.getMessage(), e);
+            }
+        }
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapter.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapter.java
new file mode 100644
index 0000000000..b93cb3f2ce
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapter.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.jetty;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+
+import org.eclipse.jetty.util.log.AbstractLogger;
+import org.eclipse.jetty.util.log.Logger;
+
+import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION;
+
+/**
+ * logger adapter for jetty
+ */
+public class JettyLoggerAdapter extends AbstractLogger {
+    protected String name;
+
+    private final ErrorTypeAwareLogger logger;
+
+    private static boolean debugEnabled = false;
+
+    public JettyLoggerAdapter() {
+        this("org.apache.dubbo.remoting.http.jetty");
+    }
+
+    public JettyLoggerAdapter(Class<?> clazz) {
+        this(clazz.getName());
+    }
+
+    public JettyLoggerAdapter(String name) {
+        this.name = name;
+        this.logger = LoggerFactory.getErrorTypeAwareLogger(name);
+    }
+
+    @Override
+    protected Logger newLogger(String name) {
+        return new JettyLoggerAdapter(name);
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public void warn(String msg, Object... objects) {
+        if (logger.isWarnEnabled()) {
+            logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", this.format(msg, 
objects));
+        }
+    }
+
+    @Override
+    public void warn(Throwable throwable) {
+        if (logger.isWarnEnabled()) {
+            logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", 
throwable.getMessage(), throwable);
+        }
+    }
+
+    @Override
+    public void warn(String msg, Throwable throwable) {
+        if (logger.isWarnEnabled()) {
+            logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", msg, throwable);
+        }
+    }
+
+    @Override
+    public void info(String msg, Object... objects) {
+        if (logger.isInfoEnabled()) {
+            logger.info(this.format(msg, objects));
+        }
+    }
+
+    @Override
+    public void info(Throwable throwable) {
+        if (logger.isInfoEnabled()) {
+            logger.info(throwable);
+        }
+    }
+
+    @Override
+    public void info(String msg, Throwable throwable) {
+        if (logger.isInfoEnabled()) {
+            logger.info(msg, throwable);
+        }
+    }
+
+    @Override
+    public boolean isDebugEnabled() {
+        return debugEnabled;
+    }
+
+    @Override
+    public void setDebugEnabled(boolean enabled) {
+        debugEnabled = enabled;
+    }
+
+    @Override
+    public void debug(String msg, Object... objects) {
+        if (debugEnabled && logger.isDebugEnabled()) {
+            logger.debug(this.format(msg, objects));
+        }
+    }
+
+    @Override
+    public void debug(Throwable throwable) {
+        if (debugEnabled && logger.isDebugEnabled()) {
+            logger.debug(throwable);
+        }
+    }
+
+    @Override
+    public void debug(String msg, Throwable throwable) {
+        if (debugEnabled && logger.isDebugEnabled()) {
+            logger.debug(msg, throwable);
+        }
+    }
+
+    @Override
+    public void ignore(Throwable throwable) {
+        if (logger.isWarnEnabled()) {
+            logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "IGNORED 
EXCEPTION ", throwable);
+        }
+    }
+
+    private String format(String msg, Object... args) {
+        msg = String.valueOf(msg); // Avoids NPE
+        String braces = "{}";
+        StringBuilder builder = new StringBuilder();
+        int start = 0;
+        for (Object arg : args) {
+            int bracesIndex = msg.indexOf(braces, start);
+            if (bracesIndex < 0) {
+                builder.append(msg.substring(start));
+                builder.append(' ');
+                builder.append(arg);
+                start = msg.length();
+            } else {
+                builder.append(msg, start, bracesIndex);
+                builder.append(arg);
+                start = bracesIndex + braces.length();
+            }
+        }
+        builder.append(msg.substring(start));
+        return builder.toString();
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java
new file mode 100644
index 0000000000..f09b288c5f
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.restclient;
+
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.RestResult;
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpOptions;
+import org.apache.http.client.methods.HttpPatch;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.methods.HttpTrace;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+
+public class HttpClientRestClient implements RestClient {
+    private final CloseableHttpClient closeableHttpClient;
+    private final HttpClientConfig httpClientConfig;
+
+    public HttpClientRestClient(HttpClientConfig clientConfig) {
+        closeableHttpClient = createHttpClient();
+        httpClientConfig = clientConfig;
+    }
+
+    @Override
+    public CompletableFuture<RestResult> send(RequestTemplate requestTemplate) 
{
+
+        HttpRequestBase httpRequest = null;
+        String httpMethod = requestTemplate.getHttpMethod();
+
+        httpRequest = createHttpUriRequest(httpMethod, requestTemplate);
+
+        if (httpRequest instanceof HttpEntityEnclosingRequest) {
+            ((HttpEntityEnclosingRequestBase) httpRequest)
+                    .setEntity(new 
ByteArrayEntity(requestTemplate.getSerializedBody()));
+        }
+
+        Map<String, Collection<String>> allHeaders = 
requestTemplate.getAllHeaders();
+
+        allHeaders.remove("Content-Length");
+        // header
+        for (String headerName : allHeaders.keySet()) {
+            Collection<String> headerValues = allHeaders.get(headerName);
+
+            for (String headerValue : headerValues) {
+                httpRequest.addHeader(headerName, headerValue);
+            }
+        }
+
+        httpRequest.setConfig(getRequestConfig(httpClientConfig));
+
+        CompletableFuture<RestResult> future = new CompletableFuture<>();
+        try {
+            CloseableHttpResponse response = 
closeableHttpClient.execute(httpRequest);
+            future.complete(new RestResult() {
+                @Override
+                public String getContentType() {
+                    Header header = response.getFirstHeader("Content-Type");
+                    return header == null ? null : header.getValue();
+                }
+
+                @Override
+                public byte[] getBody() throws IOException {
+                    if (response.getEntity() == null) {
+                        return new byte[0];
+                    }
+                    return 
IOUtils.toByteArray(response.getEntity().getContent());
+                }
+
+                @Override
+                public Map<String, List<String>> headers() {
+                    return Arrays.stream(response.getAllHeaders())
+                            .collect(Collectors.toMap(Header::getName, h -> 
Collections.singletonList(h.getValue())));
+                }
+
+                @Override
+                public byte[] getErrorResponse() throws IOException {
+                    return getBody();
+                }
+
+                @Override
+                public int getResponseCode() {
+                    return response.getStatusLine().getStatusCode();
+                }
+
+                @Override
+                public String getMessage() throws IOException {
+                    return appendErrorMessage(
+                            response.getStatusLine().getReasonPhrase(), new 
String(getErrorResponse()));
+                }
+            });
+        } catch (IOException e) {
+            future.completeExceptionally(e);
+        }
+        return future;
+    }
+
+    private RequestConfig getRequestConfig(HttpClientConfig clientConfig) {
+
+        // TODO config
+        return RequestConfig.custom().build();
+    }
+
+    @Override
+    public void close() {
+        try {
+            closeableHttpClient.close();
+        } catch (IOException e) {
+
+        }
+    }
+
+    @Override
+    public void close(int timeout) {}
+
+    @Override
+    public boolean isClosed() {
+        // TODO close judge
+        return true;
+    }
+
+    public CloseableHttpClient createHttpClient() {
+        PoolingHttpClientConnectionManager connectionManager = new 
PoolingHttpClientConnectionManager();
+        return 
HttpClients.custom().setConnectionManager(connectionManager).build();
+    }
+
+    protected HttpRequestBase createHttpUriRequest(String httpMethod, 
RequestTemplate requestTemplate) {
+        String uri = requestTemplate.getURL();
+        HttpRequestBase httpUriRequest = null;
+        if (HttpGet.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpGet(uri);
+        } else if (HttpHead.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpHead(uri);
+        } else if (HttpPost.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpPost(uri);
+        } else if (HttpPut.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpPut(uri);
+        } else if (HttpPatch.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpPatch(uri);
+        } else if (HttpDelete.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpDelete(uri);
+        } else if (HttpOptions.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpOptions(uri);
+        } else if (HttpTrace.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpTrace(uri);
+        } else {
+            throw new IllegalArgumentException("Invalid HTTP method: " + 
httpMethod);
+        }
+        return httpUriRequest;
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java
new file mode 100644
index 0000000000..36ba2e6d18
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.restclient;
+
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.RestResult;
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import okhttp3.internal.http.HttpMethod;
+
+// TODO add version 4.0 implements ,and default version is < 4.0,for 
dependency conflict
+public class OKHttpRestClient implements RestClient {
+    private final OkHttpClient okHttpClient;
+    private final HttpClientConfig httpClientConfig;
+
+    public OKHttpRestClient(HttpClientConfig clientConfig) {
+        this.okHttpClient = createHttpClient(clientConfig);
+        this.httpClientConfig = clientConfig;
+    }
+
+    @Override
+    public CompletableFuture<RestResult> send(RequestTemplate requestTemplate) 
{
+
+        Request.Builder builder = new Request.Builder();
+        // url
+        builder.url(requestTemplate.getURL());
+
+        Map<String, Collection<String>> allHeaders = 
requestTemplate.getAllHeaders();
+
+        boolean hasBody = false;
+        RequestBody requestBody = null;
+        // GET & HEAD body is forbidden
+        if (HttpMethod.permitsRequestBody(requestTemplate.getHttpMethod())) {
+            requestBody = RequestBody.create(null, 
requestTemplate.getSerializedBody());
+            hasBody = true;
+        }
+
+        // header
+        for (String headerName : allHeaders.keySet()) {
+            Collection<String> headerValues = allHeaders.get(headerName);
+            if (!hasBody && "Content-Length".equals(headerName)) {
+                continue;
+            }
+            for (String headerValue : headerValues) {
+
+                builder.addHeader(headerName, headerValue);
+            }
+        }
+
+        builder.method(requestTemplate.getHttpMethod(), requestBody);
+
+        CompletableFuture<RestResult> future = new CompletableFuture<>();
+
+        okHttpClient.newCall(builder.build()).enqueue(new Callback() {
+            @Override
+            public void onFailure(Call call, IOException e) {
+                future.completeExceptionally(e);
+            }
+
+            @Override
+            public void onResponse(Call call, Response response) throws 
IOException {
+                future.complete(new RestResult() {
+                    @Override
+                    public String getContentType() {
+                        return response.header("Content-Type");
+                    }
+
+                    @Override
+                    public byte[] getBody() throws IOException {
+                        ResponseBody body = response.body();
+                        return body == null ? null : body.bytes();
+                    }
+
+                    @Override
+                    public Map<String, List<String>> headers() {
+                        return response.headers().toMultimap();
+                    }
+
+                    @Override
+                    public byte[] getErrorResponse() throws IOException {
+                        return getBody();
+                    }
+
+                    @Override
+                    public int getResponseCode() throws IOException {
+                        return response.code();
+                    }
+
+                    @Override
+                    public String getMessage() throws IOException {
+                        return appendErrorMessage(response.message(), new 
String(getBody()));
+                    }
+                });
+            }
+        });
+
+        return future;
+    }
+
+    @Override
+    public void close() {
+        okHttpClient.connectionPool().evictAll();
+    }
+
+    @Override
+    public void close(int timeout) {}
+
+    @Override
+    public boolean isClosed() {
+        return okHttpClient.retryOnConnectionFailure();
+    }
+
+    public OkHttpClient createHttpClient(HttpClientConfig httpClientConfig) {
+        OkHttpClient client = new OkHttpClient.Builder()
+                .readTimeout(httpClientConfig.getReadTimeout(), 
TimeUnit.SECONDS)
+                .writeTimeout(httpClientConfig.getWriteTimeout(), 
TimeUnit.SECONDS)
+                .connectTimeout(httpClientConfig.getConnectTimeout(), 
TimeUnit.SECONDS)
+                .build();
+        return client;
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java
new file mode 100644
index 0000000000..65f20a19f2
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.restclient;
+
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.RestResult;
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.commons.io.IOUtils;
+
+public class URLConnectionRestClient implements RestClient {
+    private final HttpClientConfig clientConfig;
+
+    public URLConnectionRestClient(HttpClientConfig clientConfig) {
+        this.clientConfig = clientConfig;
+    }
+
+    @Override
+    public CompletableFuture<RestResult> send(RequestTemplate requestTemplate) 
{
+
+        CompletableFuture<RestResult> future = new CompletableFuture<>();
+
+        try {
+            HttpURLConnection connection = (HttpURLConnection) new 
URL(requestTemplate.getURL()).openConnection();
+            connection.setConnectTimeout(clientConfig.getConnectTimeout());
+            connection.setReadTimeout(clientConfig.getReadTimeout());
+            connection.setRequestMethod(requestTemplate.getHttpMethod());
+
+            prepareConnection(connection, requestTemplate.getHttpMethod());
+
+            // writeHeaders
+
+            for (String field : requestTemplate.getAllHeaders().keySet()) {
+                for (String value : requestTemplate.getHeaders(field)) {
+                    connection.addRequestProperty(field, value);
+                }
+            }
+
+            // writeBody
+
+            boolean gzipEncodedRequest = 
requestTemplate.isGzipEncodedRequest();
+            boolean deflateEncodedRequest = 
requestTemplate.isDeflateEncodedRequest();
+            if (requestTemplate.isBodyEmpty()) {
+                future.complete(getRestResultFromConnection(connection));
+                return future;
+            }
+            Integer contentLength = requestTemplate.getContentLength();
+
+            if (contentLength != null) {
+                connection.setFixedLengthStreamingMode(contentLength);
+            } else {
+                
connection.setChunkedStreamingMode(clientConfig.getChunkLength());
+            }
+
+            OutputStream out = connection.getOutputStream();
+            if (gzipEncodedRequest) {
+                out = new GZIPOutputStream(out);
+            } else if (deflateEncodedRequest) {
+                out = new DeflaterOutputStream(out);
+            }
+            try {
+                out.write(requestTemplate.getSerializedBody());
+            } finally {
+                try {
+                    out.close();
+                } catch (IOException suppressed) {
+                }
+            }
+
+            future.complete(getRestResultFromConnection(connection));
+        } catch (Exception e) {
+            future.completeExceptionally(e);
+        }
+
+        return future;
+    }
+
+    @Override
+    public void close() {}
+
+    @Override
+    public void close(int timeout) {}
+
+    @Override
+    public boolean isClosed() {
+        return true;
+    }
+
+    private RestResult getRestResultFromConnection(HttpURLConnection 
connection) {
+
+        return new RestResult() {
+            @Override
+            public String getContentType() {
+                return connection.getContentType();
+            }
+
+            @Override
+            public byte[] getBody() throws IOException {
+                return IOUtils.toByteArray(connection.getInputStream());
+            }
+
+            @Override
+            public Map<String, List<String>> headers() {
+                return connection.getHeaderFields();
+            }
+
+            @Override
+            public byte[] getErrorResponse() throws IOException {
+                return IOUtils.toByteArray(connection.getErrorStream());
+            }
+
+            @Override
+            public int getResponseCode() throws IOException {
+                return connection.getResponseCode();
+            }
+
+            @Override
+            public String getMessage() throws IOException {
+                return appendErrorMessage(connection.getResponseMessage(), new 
String(getErrorResponse()));
+            }
+        };
+    }
+
+    private void prepareConnection(HttpURLConnection connection, String 
httpMethod) throws IOException {
+
+        connection.setDoInput(true);
+
+        if ("GET".equals(httpMethod)) {
+            connection.setInstanceFollowRedirects(true);
+        } else {
+            connection.setInstanceFollowRedirects(false);
+        }
+
+        if ("POST".equals(httpMethod)
+                || "PUT".equals(httpMethod)
+                || "PATCH".equals(httpMethod)
+                || "DELETE".equals(httpMethod)) {
+            connection.setDoOutput(true);
+        } else {
+            connection.setDoOutput(false);
+        }
+
+        connection.setRequestMethod(httpMethod);
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/BootstrapListener.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/BootstrapListener.java
new file mode 100644
index 0000000000..0a902f292f
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/BootstrapListener.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.servlet;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+/**
+ * This class must be defined before something like spring's 
ContextLoaderListener in web.xml
+ */
+public class BootstrapListener implements ServletContextListener {
+
+    @Override
+    public void contextInitialized(ServletContextEvent servletContextEvent) {
+        ServletManager.getInstance()
+                .addServletContext(ServletManager.EXTERNAL_SERVER_PORT, 
servletContextEvent.getServletContext());
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent servletContextEvent) {
+        
ServletManager.getInstance().removeServletContext(ServletManager.EXTERNAL_SERVER_PORT);
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/DispatcherServlet.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/DispatcherServlet.java
new file mode 100644
index 0000000000..0f2358cee0
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/DispatcherServlet.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.servlet;
+
+import org.apache.dubbo.remoting.http.HttpHandler;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Service dispatcher Servlet.
+ */
+public class DispatcherServlet extends HttpServlet {
+
+    private static final long serialVersionUID = 5766349180380479888L;
+    private static final Map<Integer, HttpHandler> HANDLERS = new 
ConcurrentHashMap<>();
+    private static DispatcherServlet INSTANCE;
+
+    public DispatcherServlet() {
+        DispatcherServlet.INSTANCE = this;
+    }
+
+    public static void addHttpHandler(int port, HttpHandler processor) {
+        HANDLERS.put(port, processor);
+    }
+
+    public static void removeHttpHandler(int port) {
+        HANDLERS.remove(port);
+    }
+
+    public static DispatcherServlet getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected void service(HttpServletRequest request, HttpServletResponse 
response)
+            throws ServletException, IOException {
+        HttpHandler handler = HANDLERS.get(request.getLocalPort());
+        if (handler == null) { // service not found.
+            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Service not 
found.");
+        } else {
+            handler.handle(request, response);
+        }
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpBinder.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpBinder.java
new file mode 100644
index 0000000000..33aecc5aff
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpBinder.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.servlet;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.http.HttpBinder;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+/**
+ * ServletHttpTransporter
+ */
+public class ServletHttpBinder implements HttpBinder {
+
+    @Override
+    public HttpServer bind(URL url, HttpHandler handler) {
+        return new ServletHttpServer(url, handler);
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpServer.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpServer.java
new file mode 100644
index 0000000000..bf0b01457e
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpServer.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.servlet;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.support.AbstractHttpServer;
+
+public class ServletHttpServer extends AbstractHttpServer {
+
+    public ServletHttpServer(URL url, HttpHandler handler) {
+        super(url, handler);
+        
DispatcherServlet.addHttpHandler(url.getParameter(Constants.BIND_PORT_KEY, 
8080), handler);
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletManager.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletManager.java
new file mode 100644
index 0000000000..dc71cd862d
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletManager.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.servlet;
+
+import javax.servlet.ServletContext;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * TODO this may not be a pretty elegant solution,
+ */
+public class ServletManager {
+
+    public static final int EXTERNAL_SERVER_PORT = -1234;
+
+    private static final ServletManager INSTANCE = new ServletManager();
+
+    private final Map<Integer, ServletContext> contextMap = new 
ConcurrentHashMap<>();
+
+    public static ServletManager getInstance() {
+        return INSTANCE;
+    }
+
+    public void addServletContext(int port, ServletContext servletContext) {
+        contextMap.put(port, servletContext);
+    }
+
+    public void removeServletContext(int port) {
+        contextMap.remove(port);
+    }
+
+    public ServletContext getServletContext(int port) {
+        return contextMap.get(port);
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/support/AbstractHttpServer.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/support/AbstractHttpServer.java
new file mode 100644
index 0000000000..cc01b1e064
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/support/AbstractHttpServer.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.support;
+
+import org.apache.dubbo.common.Parameters;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+
+/**
+ * AbstractHttpServer
+ */
+public abstract class AbstractHttpServer implements HttpServer {
+
+    private final URL url;
+
+    private final HttpHandler handler;
+
+    private volatile boolean closed;
+
+    public AbstractHttpServer(URL url, HttpHandler handler) {
+        if (url == null) {
+            throw new IllegalArgumentException("url == null");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("handler == null");
+        }
+        this.url = url;
+        this.handler = handler;
+    }
+
+    @Override
+    public HttpHandler getHttpHandler() {
+        return handler;
+    }
+
+    @Override
+    public URL getUrl() {
+        return url;
+    }
+
+    @Override
+    public void reset(URL url) {}
+
+    @Override
+    public boolean isBound() {
+        return true;
+    }
+
+    @Override
+    public InetSocketAddress getLocalAddress() {
+        return url.toInetSocketAddress();
+    }
+
+    @Override
+    public void close() {
+        closed = true;
+    }
+
+    @Override
+    public void close(int timeout) {
+        close();
+    }
+
+    @Override
+    public boolean isClosed() {
+        return closed;
+    }
+
+    /**
+     * Following methods are extended from RemotingServer, useless for http 
servers
+     */
+    @Override
+    public boolean canHandleIdle() {
+        return false;
+    }
+
+    @Override
+    public Collection<Channel> getChannels() {
+        return null;
+    }
+
+    @Override
+    public Channel getChannel(InetSocketAddress remoteAddress) {
+        return null;
+    }
+
+    @Override
+    public void reset(Parameters parameters) {}
+
+    @Override
+    public ChannelHandler getChannelHandler() {
+        return null;
+    }
+
+    @Override
+    public void send(Object message) throws RemotingException {}
+
+    @Override
+    public void send(Object message, boolean sent) throws RemotingException {}
+
+    @Override
+    public void startClose() {}
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinder.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinder.java
new file mode 100755
index 0000000000..92b4bf1c70
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinder.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.tomcat;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.http.HttpBinder;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+public class TomcatHttpBinder implements HttpBinder {
+
+    @Override
+    public HttpServer bind(URL url, HttpHandler handler) {
+        return new TomcatHttpServer(url, handler);
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpServer.java
 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpServer.java
new file mode 100755
index 0000000000..002f963c35
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpServer.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.tomcat;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.SystemPropertyConfigUtils;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.servlet.DispatcherServlet;
+import org.apache.dubbo.remoting.http.servlet.ServletManager;
+import org.apache.dubbo.remoting.http.support.AbstractHttpServer;
+
+import java.io.File;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.startup.Tomcat;
+
+import static 
org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREADS;
+import static 
org.apache.dubbo.common.constants.CommonConstants.SystemProperty.SYSTEM_JAVA_IO_TMPDIR;
+import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
+import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_FAILED_STOP_HTTP_SERVER;
+import static org.apache.dubbo.remoting.Constants.ACCEPTS_KEY;
+
+public class TomcatHttpServer extends AbstractHttpServer {
+
+    private static final ErrorTypeAwareLogger logger = 
LoggerFactory.getErrorTypeAwareLogger(TomcatHttpServer.class);
+
+    private final Tomcat tomcat;
+
+    private final URL url;
+
+    public TomcatHttpServer(URL url, final HttpHandler handler) {
+        super(url, handler);
+
+        this.url = url;
+        DispatcherServlet.addHttpHandler(url.getPort(), handler);
+        String baseDir = new 
File(SystemPropertyConfigUtils.getSystemProperty(SYSTEM_JAVA_IO_TMPDIR)).getAbsolutePath();
+        tomcat = new Tomcat();
+
+        Connector connector = tomcat.getConnector();
+        connector.setPort(url.getPort());
+        connector.setProperty("maxThreads", 
String.valueOf(url.getParameter(THREADS_KEY, DEFAULT_THREADS)));
+        connector.setProperty("maxConnections", 
String.valueOf(url.getParameter(ACCEPTS_KEY, -1)));
+        connector.setProperty("URIEncoding", "UTF-8");
+        connector.setProperty("connectionTimeout", "60000");
+        connector.setProperty("maxKeepAliveRequests", "-1");
+
+        tomcat.setBaseDir(baseDir);
+        tomcat.setPort(url.getPort());
+
+        Context context = tomcat.addContext("/", baseDir);
+        Tomcat.addServlet(context, "dispatcher", new DispatcherServlet());
+        // Issue : https://github.com/apache/dubbo/issues/6418
+        // addServletMapping method will be removed since Tomcat 9
+        // context.addServletMapping("/*", "dispatcher");
+        context.addServletMappingDecoded("/*", "dispatcher");
+        ServletManager.getInstance().addServletContext(url.getPort(), 
context.getServletContext());
+
+        // tell tomcat to fail on startup failures.
+        System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", 
"true");
+
+        try {
+            tomcat.start();
+        } catch (LifecycleException e) {
+            throw new IllegalStateException("Failed to start tomcat server at 
" + url.getAddress(), e);
+        }
+    }
+
+    @Override
+    public void close() {
+        super.close();
+
+        ServletManager.getInstance().removeServletContext(url.getPort());
+
+        try {
+            tomcat.stop();
+            // close port by destroy()
+            tomcat.destroy();
+        } catch (Exception e) {
+            logger.warn(COMMON_FAILED_STOP_HTTP_SERVER, "", "", 
e.getMessage(), e);
+        }
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.HttpBinder
 
b/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.HttpBinder
new file mode 100644
index 0000000000..845124bb43
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.HttpBinder
@@ -0,0 +1,3 @@
+servlet=org.apache.dubbo.remoting.http.servlet.ServletHttpBinder
+jetty=org.apache.dubbo.remoting.http.jetty.JettyHttpBinder
+tomcat=org.apache.dubbo.remoting.http.tomcat.TomcatHttpBinder
\ No newline at end of file
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory
 
b/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory
new file mode 100644
index 0000000000..95dd12e072
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory
@@ -0,0 +1,4 @@
+ok-http=org.apache.dubbo.remoting.http.factory.impl.OkHttpClientFactory
+url-connection=org.apache.dubbo.remoting.http.factory.impl.URLConnectionClientFactory
+apache-http-client=org.apache.dubbo.remoting.http.factory.impl.ApacheHttpClientFactory
+
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinderTest.java
 
b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinderTest.java
new file mode 100644
index 0000000000..04fc431863
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinderTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.jetty;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+import org.apache.http.client.fluent.Request;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+class JettyHttpBinderTest {
+    @Test
+    void shouldAbleHandleRequestForJettyBinder() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] 
{Constants.BIND_PORT_KEY, String.valueOf(port)});
+        HttpServer httpServer = new JettyHttpServer(url, new 
HttpHandler<HttpServletRequest, HttpServletResponse>() {
+            @Override
+            public void handle(HttpServletRequest request, HttpServletResponse 
response) throws IOException {
+                response.getWriter().write("Jetty");
+            }
+        });
+
+        String response =
+                
Request.Get(url.toJavaURL().toURI()).execute().returnContent().asString();
+
+        assertThat(response, is("Jetty"));
+
+        httpServer.close();
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapterTest.java
 
b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapterTest.java
new file mode 100644
index 0000000000..e4cbbfd539
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapterTest.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.jetty;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.support.FailsafeErrorTypeAwareLogger;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import org.apache.http.client.fluent.Request;
+import org.eclipse.jetty.util.log.Log;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class JettyLoggerAdapterTest {
+
+    @Test
+    void testJettyUseDubboLogger() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] 
{Constants.BIND_PORT_KEY, String.valueOf(port)});
+        HttpServer httpServer = new JettyHttpServer(url, new 
HttpHandler<HttpServletRequest, HttpServletResponse>() {
+            @Override
+            public void handle(HttpServletRequest request, HttpServletResponse 
response) throws IOException {
+                response.getWriter().write("Jetty is using Dubbo's logger");
+            }
+        });
+        
Request.Get(url.toJavaURL().toURI()).execute().returnContent().asString();
+
+        
assertThat(Log.getLog().getClass().isAssignableFrom(JettyLoggerAdapter.class), 
is(true));
+
+        httpServer.close();
+    }
+
+    @Test
+    void testSuccessLogger() throws Exception {
+        Logger successLogger = mock(Logger.class);
+        Class<?> clazz = 
Class.forName("org.apache.dubbo.remoting.http.jetty.JettyLoggerAdapter");
+        JettyLoggerAdapter jettyLoggerAdapter =
+                (JettyLoggerAdapter) 
clazz.getDeclaredConstructor().newInstance();
+
+        Field loggerField = clazz.getDeclaredField("logger");
+        loggerField.setAccessible(true);
+        loggerField.set(jettyLoggerAdapter, new 
FailsafeErrorTypeAwareLogger(successLogger));
+        jettyLoggerAdapter.setDebugEnabled(true);
+
+        when(successLogger.isDebugEnabled()).thenReturn(true);
+        when(successLogger.isWarnEnabled()).thenReturn(true);
+        when(successLogger.isInfoEnabled()).thenReturn(true);
+
+        jettyLoggerAdapter.warn("warn");
+        jettyLoggerAdapter.info("info");
+        jettyLoggerAdapter.debug("debug");
+
+        verify(successLogger).warn(anyString());
+        verify(successLogger).info(anyString());
+        verify(successLogger).debug(anyString());
+
+        jettyLoggerAdapter.warn(new Exception("warn"));
+        jettyLoggerAdapter.info(new Exception("info"));
+        jettyLoggerAdapter.debug(new Exception("debug"));
+        jettyLoggerAdapter.ignore(new Exception("ignore"));
+
+        jettyLoggerAdapter.warn("warn", new Exception("warn"));
+        jettyLoggerAdapter.info("info", new Exception("info"));
+        jettyLoggerAdapter.debug("debug", new Exception("debug"));
+    }
+
+    @Test
+    void testNewLogger() {
+        JettyLoggerAdapter loggerAdapter = new JettyLoggerAdapter();
+        org.eclipse.jetty.util.log.Logger logger =
+                loggerAdapter.newLogger(this.getClass().getName());
+        
assertThat(logger.getClass().isAssignableFrom(JettyLoggerAdapter.class), 
is(true));
+    }
+
+    @Test
+    void testDebugEnabled() {
+        JettyLoggerAdapter loggerAdapter = new JettyLoggerAdapter();
+        loggerAdapter.setDebugEnabled(true);
+        assertThat(loggerAdapter.isDebugEnabled(), is(true));
+    }
+
+    @Test
+    void testLoggerFormat() throws Exception {
+        Class<?> clazz = 
Class.forName("org.apache.dubbo.remoting.http.jetty.JettyLoggerAdapter");
+        Object newInstance = clazz.getDeclaredConstructor().newInstance();
+
+        Method method = clazz.getDeclaredMethod("format", String.class, 
Object[].class);
+        method.setAccessible(true);
+
+        String print = (String) method.invoke(newInstance, "Hello,{}! I'am 
{}", new String[] {"World", "Jetty"});
+
+        assertThat(print, is("Hello,World! I'am Jetty"));
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/rest/RestClientTest.java
 
b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/rest/RestClientTest.java
new file mode 100644
index 0000000000..571861ae3d
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/rest/RestClientTest.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.rest;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.*;
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+import org.apache.dubbo.remoting.http.jetty.JettyHttpServer;
+import org.apache.dubbo.remoting.http.restclient.HttpClientRestClient;
+import org.apache.dubbo.remoting.http.restclient.OKHttpRestClient;
+import org.apache.dubbo.remoting.http.restclient.URLConnectionRestClient;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+public class RestClientTest {
+
+    @Test
+    public void testRestClient() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] 
{Constants.BIND_PORT_KEY, String.valueOf(port)});
+        HttpServer httpServer = new JettyHttpServer(url, new 
HttpHandler<HttpServletRequest, HttpServletResponse>() {
+            @Override
+            public void handle(HttpServletRequest request, HttpServletResponse 
response) throws IOException {
+                response.getWriter().write("Jetty");
+            }
+        });
+
+        RequestTemplate requestTemplate = new RequestTemplate(null, "POST", 
"localhost:" + port);
+
+        requestTemplate.addParam("p1", "value1");
+        requestTemplate.addParam("p2", "value2");
+
+        requestTemplate.addParams("p3", Arrays.asList("value3", "value3.1"));
+        requestTemplate.addHeader("test", "dubbo");
+        requestTemplate.addKeepAliveHeader(60);
+
+        requestTemplate.addHeaders("header", Arrays.asList("h1", "h2"));
+
+        requestTemplate.path("/test");
+        requestTemplate.serializeBody("test".getBytes(StandardCharsets.UTF_8));
+
+        RestClient restClient = new OKHttpRestClient(new HttpClientConfig());
+
+        CompletableFuture<RestResult> send = restClient.send(requestTemplate);
+
+        RestResult restResult = send.get();
+
+        assertThat(new String(restResult.getBody()), is("Jetty"));
+
+        restClient = new HttpClientRestClient(new HttpClientConfig());
+
+        send = restClient.send(requestTemplate);
+
+        restResult = send.get();
+
+        assertThat(new String(restResult.getBody()), is("Jetty"));
+
+        restClient = new URLConnectionRestClient(new HttpClientConfig());
+
+        send = restClient.send(requestTemplate);
+
+        restResult = send.get();
+
+        assertThat(new String(restResult.getBody()), is("Jetty"));
+
+        httpServer.close();
+    }
+
+    @Test
+    public void testError() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] 
{Constants.BIND_PORT_KEY, String.valueOf(port)});
+        HttpServer httpServer = new JettyHttpServer(url, new 
HttpHandler<HttpServletRequest, HttpServletResponse>() {
+            @Override
+            public void handle(HttpServletRequest request, HttpServletResponse 
response) throws IOException {
+                response.setStatus(500);
+                response.getWriter().write("server error");
+                response.addHeader("Content-Type", "text/html");
+            }
+        });
+
+        RequestTemplate requestTemplate = new RequestTemplate(null, null, 
null);
+
+        requestTemplate.httpMethod("POST");
+        requestTemplate.setAddress("localhost:" + port);
+        requestTemplate.setProtocol("http://";);
+        requestTemplate.addHeader("test", "dubbo");
+        requestTemplate.path("/test");
+        requestTemplate.serializeBody("test".getBytes(StandardCharsets.UTF_8));
+
+        RestClient restClient = new OKHttpRestClient(new HttpClientConfig());
+
+        CompletableFuture<RestResult> send = restClient.send(requestTemplate);
+
+        String error = "Server Error\n" + " error info is: server error";
+        RestResult restResult = send.get();
+
+        String contentType = "text/html;charset=iso-8859-1";
+
+        Assertions.assertEquals(500, restResult.getResponseCode());
+        Assertions.assertEquals(error, restResult.getMessage());
+        Assertions.assertEquals(contentType, restResult.getContentType());
+
+        Map<String, List<String>> headers = restResult.headers();
+        restClient.close();
+
+        restClient = new HttpClientRestClient(new HttpClientConfig());
+        send = restClient.send(requestTemplate);
+        restResult = send.get();
+
+        Assertions.assertEquals(500, restResult.getResponseCode());
+        Assertions.assertEquals(error, restResult.getMessage());
+        Assertions.assertEquals(contentType, restResult.getContentType());
+
+        restClient.close();
+
+        restClient = new URLConnectionRestClient(new HttpClientConfig());
+        send = restClient.send(requestTemplate);
+        restResult = send.get();
+
+        Assertions.assertEquals(500, restResult.getResponseCode());
+        Assertions.assertEquals(error, restResult.getMessage());
+        Assertions.assertEquals(contentType, restResult.getContentType());
+        restClient.close();
+
+        httpServer.close();
+    }
+
+    @Test
+    public void testMethod() {
+
+        RequestTemplate requestTemplate = new RequestTemplate(null, null, 
null);
+
+        requestTemplate.body(new Object(), Object.class);
+
+        Assertions.assertEquals(requestTemplate.getBodyType(), Object.class);
+
+        requestTemplate.addHeader("Content-Length", 1);
+
+        Integer contentLength = requestTemplate.getContentLength();
+
+        Assertions.assertEquals(1, contentLength);
+
+        List<String> strings = Arrays.asList("h1", "h2");
+
+        requestTemplate.addHeaders("header", strings);
+
+        Assertions.assertArrayEquals(
+                strings.toArray(new String[0]),
+                requestTemplate.getHeaders("header").toArray(new String[0]));
+
+        strings = Arrays.asList("p1", "p2");
+
+        requestTemplate.addParams("param", strings);
+
+        Assertions.assertArrayEquals(
+                strings.toArray(new String[0]),
+                requestTemplate.getParam("param").toArray(new String[0]));
+    }
+
+    @Test
+    void testBuildURL() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] 
{Constants.BIND_PORT_KEY, String.valueOf(port)});
+        HttpServer httpServer = new JettyHttpServer(url, new 
HttpHandler<HttpServletRequest, HttpServletResponse>() {
+            @Override
+            public void handle(HttpServletRequest request, HttpServletResponse 
response) throws IOException {
+                response.setCharacterEncoding("UTF-8");
+                response.getWriter().write(request.getQueryString());
+            }
+        });
+
+        RequestTemplate requestTemplate = new RequestTemplate(null, "POST", 
"localhost:" + port);
+
+        requestTemplate.addParam("name", "李强");
+        requestTemplate.addParam("age", "18");
+        requestTemplate.path("/hello/world");
+
+        // When using the OKHttpRestClient, parameters will be encoded with 
UTF-8 and appended to the URL
+        RestClient restClient = new OKHttpRestClient(new HttpClientConfig());
+
+        CompletableFuture<RestResult> send = restClient.send(requestTemplate);
+
+        RestResult restResult = send.get();
+
+        assertThat(new String(restResult.getBody()), 
is("name=%E6%9D%8E%E5%BC%BA&age=18"));
+
+        // When using the HttpClientRestClient, parameters will be encoded 
with UTF-8 and appended to the URL
+        restClient = new HttpClientRestClient(new HttpClientConfig());
+
+        send = restClient.send(requestTemplate);
+
+        restResult = send.get();
+
+        assertThat(new String(restResult.getBody()), 
is("name=%E6%9D%8E%E5%BC%BA&age=18"));
+
+        // When using the URLConnectionRestClient, parameters won't be encoded 
and still appended to the URL
+        restClient = new URLConnectionRestClient(new HttpClientConfig());
+
+        send = restClient.send(requestTemplate);
+
+        restResult = send.get();
+
+        assertThat(new String(restResult.getBody(), StandardCharsets.UTF_8), 
is("name=李强&age=18"));
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinderTest.java
 
b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinderTest.java
new file mode 100644
index 0000000000..75efc28b41
--- /dev/null
+++ 
b/dubbo-remoting/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinderTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.tomcat;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+import org.apache.http.client.fluent.Request;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+class TomcatHttpBinderTest {
+    @Test
+    void shouldAbleHandleRequestForTomcatBinder() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] 
{Constants.BIND_PORT_KEY, String.valueOf(port)});
+
+        HttpServer httpServer = new TomcatHttpBinder()
+                .bind(url, new HttpHandler<HttpServletRequest, 
HttpServletResponse>() {
+                    @Override
+                    public void handle(HttpServletRequest request, 
HttpServletResponse response) throws IOException {
+                        response.getWriter().write("Tomcat");
+                    }
+                });
+
+        String response =
+                
Request.Get(url.toJavaURL().toURI()).execute().returnContent().asString();
+
+        assertThat(response, is("Tomcat"));
+
+        httpServer.close();
+        assertThat(NetUtils.isPortInUsed(port), is(false));
+    }
+}
diff --git 
a/dubbo-remoting/dubbo-remoting-http/src/test/resources/log4j2-test.xml 
b/dubbo-remoting/dubbo-remoting-http/src/test/resources/log4j2-test.xml
new file mode 100644
index 0000000000..ba99f52cc2
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-http/src/test/resources/log4j2-test.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<Configuration status="WARN">
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT" follow="true">
+            <PatternLayout pattern="%d{HH:mm:ss.SSS} |-%highlight{%-5p} [%t] 
%40.40c:%-3L -| 
%m%n%rEx{filters(jdk.internal.reflect,java.lang.reflect,sun.reflect,org.junit,org.mockito)}"
 charset="UTF-8"/>
+        </Console>
+    </Appenders>
+    <Loggers>
+        <Root level="info">
+            <AppenderRef ref="Console"/>
+        </Root>
+    </Loggers>
+</Configuration>
diff --git a/dubbo-remoting/pom.xml b/dubbo-remoting/pom.xml
index e7aca3685e..1a1d0cd0c4 100644
--- a/dubbo-remoting/pom.xml
+++ b/dubbo-remoting/pom.xml
@@ -29,6 +29,7 @@
   <description>The remoting module of dubbo project</description>
   <modules>
     <module>dubbo-remoting-api</module>
+    <module>dubbo-remoting-http</module>
     <module>dubbo-remoting-netty</module>
     <module>dubbo-remoting-zookeeper-api</module>
     <module>dubbo-remoting-zookeeper</module>
diff --git a/dubbo-test/dubbo-dependencies-all/pom.xml 
b/dubbo-test/dubbo-dependencies-all/pom.xml
index 2c802671c7..8bfc74434b 100644
--- a/dubbo-test/dubbo-dependencies-all/pom.xml
+++ b/dubbo-test/dubbo-dependencies-all/pom.xml
@@ -300,6 +300,11 @@
       <artifactId>dubbo-remoting-api</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.dubbo</groupId>
+      <artifactId>dubbo-remoting-http</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.apache.dubbo</groupId>
       <artifactId>dubbo-remoting-http12</artifactId>

Reply via email to