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

spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new e3601e6  feat: support dubbo (#3224)
e3601e6 is described below

commit e3601e6876e13a44fa8102fa19a41b4bc1a080e2
Author: 罗泽轩 <spacewander...@gmail.com>
AuthorDate: Tue Jan 19 19:47:09 2021 -0600

    feat: support dubbo (#3224)
    
    Fix #89
---
 .github/workflows/build.yml                        |   9 +
 .gitignore                                         |   2 +
 .travis/ASF-Release.cfg                            |   5 +-
 .../apisix_cli_test/test_dubbo.sh                  |  41 ++-
 .travis/linux_openresty_1_15_runner.sh             |   2 +-
 .travis/linux_openresty_1_17_runner.sh             |   2 +-
 ..._runner.sh => linux_openresty_common_runner.sh} |   1 +
 .travis/linux_openresty_runner.sh                  | 149 +---------
 Makefile                                           |   2 +-
 README.md                                          |   1 +
 README_CN.md                                       |   1 +
 apisix/cli/ngx_tpl.lua                             |  43 +++
 apisix/cli/ops.lua                                 |   9 +
 apisix/init.lua                                    |  12 +
 apisix/plugins/dubbo-proxy.lua                     |  69 +++++
 conf/config-default.yaml                           |   3 +
 doc/plugins/dubbo-proxy.md                         | 130 +++++++++
 rockspec/apisix-master-0.rockspec                  |   1 +
 t/APISIX.pm                                        |  53 ++++
 .../dubbo-backend/dubbo-backend-interface/pom.xml  |  45 +++
 .../java/org/apache/dubbo/backend/DemoService.java |  43 +++
 t/lib/dubbo-backend/dubbo-backend-provider/pom.xml |  96 +++++++
 .../dubbo/backend/provider/DemoServiceImpl.java    |  61 ++++
 .../apache/dubbo/backend/provider/Provider.java    |  40 +++
 .../META-INF/spring/dubbo-demo-provider.xml        |  39 +++
 .../src/main/resources/dubbo.properties            |   6 +-
 .../src/main/resources/log4j.properties            |  12 +-
 t/lib/dubbo-backend/pom.xml                        |  97 +++++++
 t/plugin/dubbo-proxy/route.t                       | 314 +++++++++++++++++++++
 t/plugin/dubbo-proxy/upstream.t                    | 137 +++++++++
 utils/linux-install-openresty.sh                   |  52 ++++
 31 files changed, 1305 insertions(+), 172 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fabc113..d0aae55 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -59,6 +59,15 @@ jobs:
       - name: Linux Before install
         run: sudo ./.travis/${{ matrix.os_name }}_runner.sh before_install
 
+      - name: Start Dubbo Backend
+        if: matrix.os_name == 'linux_openresty'
+        run: |
+          sudo apt install -y maven
+          cd t/lib/dubbo-backend
+          mvn package
+          cd dubbo-backend-provider/target
+          java -Djava.net.preferIPv4Stack=true -jar 
dubbo-demo-provider.one-jar.jar > /tmp/java.log &
+
       - name: Install Redis Cluster
         if: startsWith(matrix.os_name, 'linux_openresty')
         run: |
diff --git a/.gitignore b/.gitignore
index ee5e22a..f073b53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,6 +55,8 @@ client_body_temp
 utils/lj-releng
 utils/reindex
 default.etcd/
+t/lib/dubbo-backend/dubbo-backend-interface/target/
+t/lib/dubbo-backend/dubbo-backend-provider/target/
 .idea/
 *.iml
 \.*
diff --git a/.travis/ASF-Release.cfg b/.travis/ASF-Release.cfg
index b086045..b4ba843 100644
--- a/.travis/ASF-Release.cfg
+++ b/.travis/ASF-Release.cfg
@@ -79,14 +79,15 @@ DISCLAIMER.txt
 LICENSE*.txt
 NOTICE.txt
 
-# Exclude travis CI files
+# Exclude CI files
 build-cache
 deps
 test-nginx
 grpc_server_example
-.travis.yml
 grpcurl
 t/servroot
+t/lib/dubbo-backend/dubbo-backend-provider/target
+t/lib/dubbo-backend/dubbo-backend-interface/target
 
 conf
 .travis/openwhisk-utilities
diff --git a/utils/linux-install-openresty.sh 
b/.travis/apisix_cli_test/test_dubbo.sh
similarity index 54%
copy from utils/linux-install-openresty.sh
copy to .travis/apisix_cli_test/test_dubbo.sh
index b8847ef..49814a1 100755
--- a/utils/linux-install-openresty.sh
+++ b/.travis/apisix_cli_test/test_dubbo.sh
@@ -1,4 +1,5 @@
 #!/usr/bin/env bash
+
 #
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
@@ -16,17 +17,37 @@
 # limitations under the License.
 #
 
-wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
-sudo apt-get -y update --fix-missing
-sudo apt-get -y install software-properties-common
-sudo add-apt-repository -y "deb https://openresty.org/package/ubuntu 
$(lsb_release -sc) main"
+. ./.travis/apisix_cli_test/common.sh
+
+# enable dubbo
+echo '
+plugins:
+    - dubbo-proxy
+' > conf/config.yaml
+
+make init
+
+if ! grep "location @dubbo_pass " conf/nginx.conf > /dev/null; then
+    echo "failed: dubbo location not found in nginx.conf"
+    exit 1
+fi
+
+echo "passed: found dubbo location in nginx.conf"
+
+# dubbo multiplex configuration
+echo '
+plugins:
+    - dubbo-proxy
+plugin_attr:
+    dubbo-proxy:
+        upstream_multiplex_count: 16
+' > conf/config.yaml
 
-sudo apt-get update
+make init
 
-if [ "$OPENRESTY_VERSION" == "default" ]; then
-    openresty='openresty-debug'
-else
-    openresty="openresty-debug=$OPENRESTY_VERSION*"
+if ! grep "multi 16;" conf/nginx.conf > /dev/null; then
+    echo "failed: dubbo multiplex configuration not found in nginx.conf"
+    exit 1
 fi
 
-sudo apt-get install "$openresty" lua5.1 liblua5.1-0-dev
+echo "passed: found dubbo multiplex configuration in nginx.conf"
diff --git a/.travis/linux_openresty_1_15_runner.sh 
b/.travis/linux_openresty_1_15_runner.sh
index f845042..cc619ba 100755
--- a/.travis/linux_openresty_1_15_runner.sh
+++ b/.travis/linux_openresty_1_15_runner.sh
@@ -18,4 +18,4 @@
 
 
 export OPENRESTY_VERSION=1.15.8.2
-. ./.travis/linux_openresty_runner.sh
+. ./.travis/linux_openresty_common_runner.sh
diff --git a/.travis/linux_openresty_1_17_runner.sh 
b/.travis/linux_openresty_1_17_runner.sh
index e36dd83..39d8333 100755
--- a/.travis/linux_openresty_1_17_runner.sh
+++ b/.travis/linux_openresty_1_17_runner.sh
@@ -18,4 +18,4 @@
 
 
 export OPENRESTY_VERSION=1.17.8.2
-. ./.travis/linux_openresty_runner.sh
+. ./.travis/linux_openresty_common_runner.sh
diff --git a/.travis/linux_openresty_runner.sh 
b/.travis/linux_openresty_common_runner.sh
similarity index 99%
copy from .travis/linux_openresty_runner.sh
copy to .travis/linux_openresty_common_runner.sh
index f451bbe..2229d6a 100755
--- a/.travis/linux_openresty_runner.sh
+++ b/.travis/linux_openresty_common_runner.sh
@@ -137,6 +137,7 @@ script() {
     sudo bash ./utils/check-plugins-code.sh
 
     make lint && make license-check || exit 1
+
     # APISIX_ENABLE_LUACOV=1 PERL5LIB=.:$PERL5LIB prove -Itest-nginx/lib -r t
     PERL5LIB=.:$PERL5LIB prove -Itest-nginx/lib -r t
 }
diff --git a/.travis/linux_openresty_runner.sh 
b/.travis/linux_openresty_runner.sh
index f451bbe..eb1e985 100755
--- a/.travis/linux_openresty_runner.sh
+++ b/.travis/linux_openresty_runner.sh
@@ -16,151 +16,6 @@
 # limitations under the License.
 #
 
-. ./.travis/common.sh
 
-before_install() {
-    sudo cpanm --notest Test::Nginx >build.log 2>&1 || (cat build.log && exit 
1)
-    docker pull redis:3.0-alpine
-    docker run --rm -itd -p 6379:6379 --name apisix_redis redis:3.0-alpine
-    docker run --rm -itd -e HTTP_PORT=8888 -e HTTPS_PORT=9999 -p 8888:8888 -p 
9999:9999 mendhak/http-https-echo
-    # Runs Keycloak version 10.0.2 with inbuilt policies for unit tests
-    docker run --rm -itd -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=123456 -p 
8090:8080 -p 8443:8443 sshniro/keycloak-apisix
-    # spin up kafka cluster for tests (1 zookeper and 1 kafka instance)
-    docker pull bitnami/zookeeper:3.6.0
-    docker pull bitnami/kafka:latest
-    docker network create kafka-net --driver bridge
-    docker run --name zookeeper-server -d -p 2181:2181 --network kafka-net -e 
ALLOW_ANONYMOUS_LOGIN=yes bitnami/zookeeper:3.6.0
-    docker run --name kafka-server1 -d --network kafka-net -e 
ALLOW_PLAINTEXT_LISTENER=yes -e 
KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper-server:2181 -e 
KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:9092 -p 9092:9092 -e 
KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true bitnami/kafka:latest
-    docker pull bitinit/eureka
-    docker run --name eureka -d -p 8761:8761 --env ENVIRONMENT=apisix --env 
spring.application.name=apisix-eureka --env server.port=8761 --env 
eureka.instance.ip-address=127.0.0.1 --env 
eureka.client.registerWithEureka=true --env eureka.client.fetchRegistry=false 
--env eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8761/eureka/ 
bitinit/eureka
-    sleep 5
-    docker exec -i kafka-server1 /opt/bitnami/kafka/bin/kafka-topics.sh 
--create --zookeeper zookeeper-server:2181 --replication-factor 1 --partitions 
1 --topic test2
-
-    # start skywalking
-    docker run --rm --name skywalking -d -p 1234:1234 -p 11800:11800 -p 
12800:12800 apache/skywalking-oap-server
-}
-
-do_install() {
-    export_or_prefix
-
-    ./utils/linux-install-openresty.sh
-
-    ./utils/linux-install-luarocks.sh
-    sudo luarocks install luacheck > build.log 2>&1 || (cat build.log && exit 
1)
-
-    ./utils/linux-install-etcd-client.sh
-
-    if [ ! -f "build-cache/apisix-master-0.rockspec" ]; then
-        create_lua_deps
-
-    else
-        src=`md5sum rockspec/apisix-master-0.rockspec | awk '{print $1}'`
-        src_cp=`md5sum build-cache/apisix-master-0.rockspec | awk '{print $1}'`
-        if [ "$src" = "$src_cp" ]; then
-            echo "Use lua deps cache"
-            sudo cp -r build-cache/deps ./
-        else
-            create_lua_deps
-        fi
-    fi
-
-    # sudo apt-get install tree -y
-    # tree deps
-
-    git clone https://github.com/iresty/test-nginx.git test-nginx
-    make utils
-
-    git clone https://github.com/apache/openwhisk-utilities.git 
.travis/openwhisk-utilities
-    cp .travis/ASF* .travis/openwhisk-utilities/scancode/
-
-    ls -l ./
-    if [ ! -f "build-cache/grpc_server_example" ]; then
-        wget 
https://github.com/iresty/grpc_server_example/releases/download/20200901/grpc_server_example-amd64.tar.gz
-        tar -xvf grpc_server_example-amd64.tar.gz
-        mv grpc_server_example build-cache/
-    fi
-
-    if [ ! -f "build-cache/proto/helloworld.proto" ]; then
-        if [ ! -f "grpc_server_example/main.go" ]; then
-            git clone https://github.com/iresty/grpc_server_example.git 
grpc_server_example
-        fi
-
-        cd grpc_server_example/
-        mv proto/ ../build-cache/
-        cd ..
-    fi
-
-    if [ ! -f "build-cache/grpcurl" ]; then
-        wget 
https://github.com/api7/grpcurl/releases/download/20200314/grpcurl-amd64.tar.gz
-        tar -xvf grpcurl-amd64.tar.gz
-        mv grpcurl build-cache/
-    fi
-}
-
-script() {
-    export_or_prefix
-    openresty -V
-
-    ./build-cache/grpc_server_example &
-
-    ./bin/apisix help
-    ./bin/apisix init
-    ./bin/apisix init_etcd
-    ./bin/apisix start
-
-    #start again  --> fial
-    res=`./bin/apisix start`
-    if [ "$res" != "APISIX is running..." ]; then
-        echo "failed: APISIX runs repeatedly"
-        exit 1
-    fi
-
-    #kill apisix
-    sudo kill -9 `ps aux | grep apisix | grep nginx | awk '{print $2}'`
-
-    #start -> ok
-    res=`./bin/apisix start`
-    if [ "$res" == "APISIX is running..." ]; then
-        echo "failed: shouldn't stop APISIX running after kill the old 
process."
-        exit 1
-    fi
-
-    sleep 1
-    cat logs/error.log
-
-    sudo sh ./t/grpc-proxy-test.sh
-    sleep 1
-
-    ./bin/apisix stop
-    sleep 1
-
-    sudo bash ./utils/check-plugins-code.sh
-
-    make lint && make license-check || exit 1
-    # APISIX_ENABLE_LUACOV=1 PERL5LIB=.:$PERL5LIB prove -Itest-nginx/lib -r t
-    PERL5LIB=.:$PERL5LIB prove -Itest-nginx/lib -r t
-}
-
-after_success() {
-    # cat luacov.stats.out
-    # luacov-coveralls
-    echo "done"
-}
-
-case_opt=$1
-shift
-
-case ${case_opt} in
-before_install)
-    before_install "$@"
-    ;;
-do_install)
-    do_install "$@"
-    ;;
-script)
-    script "$@"
-    ;;
-after_success)
-    after_success "$@"
-    ;;
-esac
+export OPENRESTY_VERSION=source
+. ./.travis/linux_openresty_common_runner.sh
diff --git a/Makefile b/Makefile
index e9d7d0b..618e01a 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@ INST_LUADIR ?= $(INST_PREFIX)/share/lua/5.1
 INST_BINDIR ?= /usr/bin
 INSTALL ?= install
 UNAME ?= $(shell uname)
-OR_EXEC ?= $(shell which openresty)
+OR_EXEC ?= $(shell which openresty || which nginx)
 LUAROCKS_VER ?= $(shell luarocks --version | grep -E -o  "luarocks [0-9]+.")
 
 SHELL := /bin/bash -o pipefail
diff --git a/README.md b/README.md
index 2210aa8..b12e6a6 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,7 @@ A/B testing, canary release, blue-green deployment, limit 
rate, defense against
 - **Multi protocols**
 
   - [TCP/UDP Proxy](doc/stream-proxy.md): Dynamic TCP/UDP proxy.
+  - [Dubbo Proxy](doc/plugins/dubbo-proxy.md): Dynamic HTTP to Dubbo proxy.
   - [Dynamic MQTT Proxy](doc/plugins/mqtt-proxy.md): Supports to load balance 
MQTT by `client_id`, both support MQTT 
[3.1.\*](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html), 
[5.0](https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html).
   - [gRPC proxy](doc/grpc-proxy.md): Proxying gRPC traffic.
   - [gRPC transcoding](doc/plugins/grpc-transcode.md): Supports protocol 
transcoding so that clients can access your gRPC API by using HTTP/JSON.
diff --git a/README_CN.md b/README_CN.md
index e653b82..1d432b3 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -77,6 +77,7 @@ A/B 测试、金丝雀发布(灰度发布)、蓝绿部署、限流限速、抵
 - **多协议**
 
   - [TCP/UDP 代理](doc/zh-cn/stream-proxy.md): 动态 TCP/UDP 代理。
+  - [Dubbo 代理](doc/plugins/dubbo-proxy.md): 动态代理 HTTP 请求到 Dubbo 后端。
   - [动态 MQTT 代理](doc/zh-cn/plugins/mqtt-proxy.md): 支持用 `client_id` 对 MQTT 
进行负载均衡,同时支持 MQTT 
[3.1.\*](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html) 和 
[5.0](https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html) 两个协议标准。
   - [gRPC 代理](doc/zh-cn/grpc-proxy.md):通过 APISIX 代理 gRPC 连接,并使用 APISIX 
的大部分特性管理你的 gRPC 服务。
   - [gRPC 协议转换](doc/zh-cn/plugins/grpc-transcode.md):支持协议的转换,这样客户端可以通过 
HTTP/JSON 来访问你的 gRPC API。
diff --git a/apisix/cli/ngx_tpl.lua b/apisix/cli/ngx_tpl.lua
index 3790f46..dab32d0 100644
--- a/apisix/cli/ngx_tpl.lua
+++ b/apisix/cli/ngx_tpl.lua
@@ -242,6 +242,18 @@ http {
         keepalive 320;
     }
 
+    {% if enabled_plugins["dubbo-proxy"] then %}
+    upstream apisix_dubbo_backend {
+        server 0.0.0.1;
+        balancer_by_lua_block {
+            apisix.http_balancer_phase()
+        }
+
+        multi {* dubbo_upstream_multiplex_count *};
+        keepalive 320;
+    }
+    {% end %}
+
     init_by_lua_block {
         require "resty.core"
         apisix = require("apisix")
@@ -400,6 +412,13 @@ http {
         set $upstream_scheme             'http';
         set $upstream_host               $host;
         set $upstream_uri                '';
+        set $ctx_ref                     '';
+
+        {% if enabled_plugins["dubbo-proxy"] then %}
+        set $dubbo_service_name          '';
+        set $dubbo_service_version       '';
+        set $dubbo_method                '';
+        {% end %}
 
         {% if with_module_status then %}
         location = /apisix/nginx_status {
@@ -558,6 +577,30 @@ http {
             }
         }
 
+        {% if enabled_plugins["dubbo-proxy"] then %}
+        location @dubbo_pass {
+            access_by_lua_block {
+                apisix.dubbo_access_phase()
+            }
+
+            dubbo_pass_all_headers on;
+            dubbo_pass_body on;
+            dubbo_pass $dubbo_service_name $dubbo_service_version 
$dubbo_method apisix_dubbo_backend;
+
+            header_filter_by_lua_block {
+                apisix.http_header_filter_phase()
+            }
+
+            body_filter_by_lua_block {
+                apisix.http_body_filter_phase()
+            }
+
+            log_by_lua_block {
+                apisix.http_log_phase()
+            }
+        }
+        {% end %}
+
         {% if enabled_plugins["proxy-mirror"] then %}
         location = /proxy_mirror {
             internal;
diff --git a/apisix/cli/ops.lua b/apisix/cli/ops.lua
index 2980a07..d6ebab1 100644
--- a/apisix/cli/ops.lua
+++ b/apisix/cli/ops.lua
@@ -235,6 +235,14 @@ Please modify "admin_key" in conf/config.yaml .
     yaml_conf.apisix.ssl.ssl_cert = "cert/ssl_PLACE_HOLDER.crt"
     yaml_conf.apisix.ssl.ssl_cert_key = "cert/ssl_PLACE_HOLDER.key"
 
+    local dubbo_upstream_multiplex_count = 32
+    if yaml_conf.plugin_attr and yaml_conf.plugin_attr["dubbo-proxy"] then
+        local dubbo_conf = yaml_conf.plugin_attr["dubbo-proxy"]
+        if tonumber(dubbo_conf.upstream_multiplex_count) >= 1 then
+            dubbo_upstream_multiplex_count = 
dubbo_conf.upstream_multiplex_count
+        end
+    end
+
     -- Using template.render
     local sys_conf = {
         lua_path = env.pkg_path_org,
@@ -244,6 +252,7 @@ Please modify "admin_key" in conf/config.yaml .
         with_module_status = with_module_status,
         error_log = {level = "warn"},
         enabled_plugins = enabled_plugins,
+        dubbo_upstream_multiplex_count = dubbo_upstream_multiplex_count,
     }
 
     if not yaml_conf.apisix then
diff --git a/apisix/init.lua b/apisix/init.lua
index af46588..7188d2a 100644
--- a/apisix/init.lua
+++ b/apisix/init.lua
@@ -25,6 +25,7 @@ local get_var       = require("resty.ngxvar").fetch
 local router        = require("apisix.router")
 local set_upstream  = require("apisix.upstream").set_by_route
 local upstream_util = require("apisix.utils.upstream")
+local ctxdump       = require("resty.ctxdump")
 local ipmatcher     = require("resty.ipmatcher")
 local ngx           = ngx
 local get_method    = ngx.req.get_method
@@ -34,6 +35,7 @@ local error         = error
 local ipairs        = ipairs
 local tostring      = tostring
 local ngx_now       = ngx.now
+local ngx_var       = ngx.var
 local str_byte      = string.byte
 local str_sub       = string.sub
 local tonumber      = tonumber
@@ -520,6 +522,16 @@ function _M.http_access_phase()
     end
 
     set_upstream_host(api_ctx)
+
+    if api_ctx.dubbo_proxy_enabled then
+        ngx_var.ctx_ref = ctxdump.stash_ngx_ctx()
+        return ngx.exec("@dubbo_pass")
+    end
+end
+
+
+function _M.dubbo_access_phase()
+    ngx.ctx = ctxdump.apply_ngx_ctx(ngx_var.ctx_ref)
 end
 
 
diff --git a/apisix/plugins/dubbo-proxy.lua b/apisix/plugins/dubbo-proxy.lua
new file mode 100644
index 0000000..57a093f
--- /dev/null
+++ b/apisix/plugins/dubbo-proxy.lua
@@ -0,0 +1,69 @@
+--
+-- 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.
+--
+local core = require("apisix.core")
+local ngx_var = ngx.var
+
+
+local plugin_name = "dubbo-proxy"
+
+local schema = {
+    type = "object",
+    properties = {
+        service_name = {
+            type = "string",
+            minLength = 1,
+        },
+        service_version = {
+            type = "string",
+            pattern = [[^\d+\.\d+\.\d+]],
+        },
+        method = {
+            type = "string",
+            minLength = 1,
+        },
+    },
+    required = { "service_name", "service_version"},
+}
+
+local _M = {
+    version = 0.1,
+    priority = 507,
+    name = plugin_name,
+    schema = schema,
+}
+
+
+function _M.check_schema(conf)
+    return core.schema.check(schema, conf)
+end
+
+
+function _M.access(conf, ctx)
+    ctx.dubbo_proxy_enabled = true
+
+    ngx_var.dubbo_service_name = conf.service_name
+    ngx_var.dubbo_service_version = conf.service_version
+    if not conf.method then
+        -- remove the prefix '/' from $uri
+        ngx_var.dubbo_method = core.string.sub(ngx_var.uri, 2)
+    else
+        ngx_var.dubbo_method = conf.method
+    end
+end
+
+
+return _M
diff --git a/conf/config-default.yaml b/conf/config-default.yaml
index a4cd24e..ed8ec4c 100644
--- a/conf/config-default.yaml
+++ b/conf/config-default.yaml
@@ -215,6 +215,7 @@ plugins:                          # plugin list (sorted in 
alphabetical order)
   - batch-requests
   - consumer-restriction
   - cors
+  # - dubbo-proxy
   - echo
   # - error-log-logger
   # - example-plugin
@@ -270,3 +271,5 @@ plugin_attr:
   server-info:
     report_interval: 60  # server info report interval (unit: second)
     report_ttl: 3600     # live time for server info in etcd (unit: second)
+  dubbo-proxy:
+    upstream_multiplex_count: 32
diff --git a/doc/plugins/dubbo-proxy.md b/doc/plugins/dubbo-proxy.md
new file mode 100644
index 0000000..20339d9
--- /dev/null
+++ b/doc/plugins/dubbo-proxy.md
@@ -0,0 +1,130 @@
+<!--
+#
+# 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.
+#
+-->
+
+# Summary
+
+- [**Name**](#Name)
+- [**Requirement**](#Requirement)
+- [**Runtime Attributes**](#Runtime-Attributes)
+- [**Static Attributes**](#Static-Attributes)
+- [**How To Enable**](#How-To-Enable)
+- [**Test Plugin**](#Test-Plugin)
+- [**Disable Plugin**](#Disable-Plugin)
+
+## Name
+
+dubbo-proxy plugin allows you proxy HTTP request to 
[**dubbo**](http://dubbo.apache.org).
+
+## Requirement
+
+If you are using OpenResty, you need to build it with dubbo support, see [How 
to build](https://github.com/api7/mod_dubbo#how-to-build).
+
+To make http2dubbo work in APISIX, we enhance the dubbo module based on 
Tengine's `mod_dubbo`. The modifications are contributed back to Tengine, but 
they are not included in the latest release version (Tengine-2.3.2) yet. So 
Tengine itself is unsupported.
+
+## Runtime Attributes
+
+| Name         | Type   | Requirement | Default  | Valid        | Description  
                                                        |
+| ------------ | ------ | ----------- | -------- | ------------ | 
-------------------------------------------------------------------- |
+| service_name    | string | required    |          |              | dubbo 
provider service name|
+| service_version | string | required    |          |              | dubbo 
provider service version|
+| method          | string | optional    | the path of uri   |     | dubbo 
provider service method|
+
+## Static Attributes
+
+| Name         | Type   | Requirement | Default  | Valid        | Description  
                                                        |
+| ------------ | ------ | ----------- | -------- | ------------ | 
-------------------------------------------------------------------- |
+| upstream_multiplex_count | number | required    | 32        | >= 1 | the 
maximum number of multiplex requests in an upstream connection |
+
+## How To Enable
+
+First of all, enable the dubbo-proxy plugin in the `config.yaml`:
+
+```
+# Add this in config.yaml
+plugins:
+  - ... # plugin you need
+  - dubbo-proxy
+```
+
+Then reload APISIX.
+
+Here's an example, enable the dubbo-proxy plugin on the specified route:
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/upstream/1  -H 'X-API-KEY: 
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+    "nodes": {
+        "127.0.0.1:20880": 1
+    },
+    "type": "roundrobin"
+}'
+
+curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: 
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+    "uris": [
+        "/hello"
+    ],
+    "plugins": {
+        "dubbo-proxy": {
+            "service_name": "org.apache.dubbo.sample.tengine.DemoService",
+            "service_version": "0.0.0",
+            "method": "tengineDubbo"
+        }
+    },
+    "upstream_id": 1
+}'
+```
+
+## Test Plugin
+
+You can follow the [Quick 
Start](https://github.com/alibaba/tengine/tree/master/modules/mod_dubbo#quick-start)
 example in Tengine and use the configuration above to test it.
+They should provide the same result.
+
+## Disable Plugin
+
+When you want to disable the dubbo-proxy plugin on a route/service, it is very 
simple,
+ you can delete the corresponding json configuration in the plugin 
configuration,
+  no need to restart the service, it will take effect immediately:
+
+```shell
+$ curl http://127.0.0.1:2379/v2/keys/apisix/routes/1  -H 'X-API-KEY: 
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d value='
+{
+    "methods": ["GET"],
+    "uris": [
+        "/hello"
+    ],
+    "plugins": {
+    },
+    "upstream_id": 1
+    }
+}'
+```
+
+The dubbo-proxy plugin has been disabled now. It works for other plugins.
+
+If you want to disable dubbo-proxy plugin totally,
+you need to comment out in the `config.yaml`:
+
+```yaml
+plugins:
+  - ... # plugin you need
+  #- dubbo-proxy
+```
+
+And then reload APISIX.
diff --git a/rockspec/apisix-master-0.rockspec 
b/rockspec/apisix-master-0.rockspec
index 1744525..78b74bc 100644
--- a/rockspec/apisix-master-0.rockspec
+++ b/rockspec/apisix-master-0.rockspec
@@ -31,6 +31,7 @@ description = {
 }
 
 dependencies = {
+    "lua-resty-ctxdump = 0.1-0",
     "lua-resty-template = 1.9",
     "lua-resty-etcd = 1.4.3",
     "lua-resty-balancer = 0.02rc5",
diff --git a/t/APISIX.pm b/t/APISIX.pm
index 2634eea..220745e 100644
--- a/t/APISIX.pm
+++ b/t/APISIX.pm
@@ -28,6 +28,7 @@ no_root_location(); # avoid generated duplicate 'location /'
 worker_connections(128);
 
 my $apisix_home = $ENV{APISIX_HOME} || cwd();
+my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
 
 sub read_file($) {
     my $infile = shift;
@@ -131,6 +132,50 @@ if ($profile) {
 }
 
 
+my $dubbo_upstream = "";
+my $dubbo_location = "";
+my $version = eval { `$nginx_binary -V 2>&1` };
+if ($version =~ m/\/mod_dubbo/) {
+    $dubbo_upstream = <<_EOC_;
+    upstream apisix_dubbo_backend {
+        server 0.0.0.1;
+        balancer_by_lua_block {
+            apisix.http_balancer_phase()
+        }
+
+        multi 1;
+        keepalive 320;
+    }
+
+_EOC_
+
+    $dubbo_location = <<_EOC_;
+        location \@dubbo_pass {
+            access_by_lua_block {
+                apisix.dubbo_access_phase()
+            }
+
+            dubbo_pass_all_headers on;
+            dubbo_pass_body on;
+            dubbo_pass \$dubbo_service_name \$dubbo_service_version 
\$dubbo_method apisix_dubbo_backend;
+
+            header_filter_by_lua_block {
+                apisix.http_header_filter_phase()
+            }
+
+            body_filter_by_lua_block {
+                apisix.http_body_filter_phase()
+            }
+
+            log_by_lua_block {
+                apisix.http_log_phase()
+            }
+        }
+
+_EOC_
+}
+
+
 add_block_preprocessor(sub {
     my ($block) = @_;
     my $wait_etcd_sync = $block->wait_etcd_sync // 0.1;
@@ -269,6 +314,8 @@ _EOC_
         keepalive 32;
     }
 
+    $dubbo_upstream
+
     init_by_lua_block {
         $init_by_lua_block
     }
@@ -362,6 +409,10 @@ _EOC_
         set \$upstream_scheme             'http';
         set \$upstream_host               \$host;
         set \$upstream_uri                '';
+        set \$ctx_ref                     '';
+        set \$dubbo_service_name          '';
+        set \$dubbo_service_version       '';
+        set \$dubbo_method                '';
 
         location = /apisix/nginx_status {
             allow 127.0.0.0/24;
@@ -459,6 +510,8 @@ _EOC_
             }
         }
 
+        $dubbo_location
+
         location = /proxy_mirror {
             internal;
 
diff --git a/t/lib/dubbo-backend/dubbo-backend-interface/pom.xml 
b/t/lib/dubbo-backend/dubbo-backend-interface/pom.xml
new file mode 100644
index 0000000..3087def
--- /dev/null
+++ b/t/lib/dubbo-backend/dubbo-backend-interface/pom.xml
@@ -0,0 +1,45 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<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.backend</groupId>
+        <artifactId>dubbo-backend</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <groupId>org.apache.dubbo.backend</groupId>
+    <artifactId>dubbo-backend-interface</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description></description>
+    <properties>
+        <skip_maven_deploy>true</skip_maven_deploy>
+    </properties>
+    <build>
+    <plugins>
+        <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-compiler-plugin</artifactId>
+            <configuration>
+                <source>1.8</source>
+                <target>1.8</target>
+            </configuration>
+        </plugin>
+    </plugins>
+    </build>
+</project>
diff --git 
a/t/lib/dubbo-backend/dubbo-backend-interface/src/main/java/org/apache/dubbo/backend/DemoService.java
 
b/t/lib/dubbo-backend/dubbo-backend-interface/src/main/java/org/apache/dubbo/backend/DemoService.java
new file mode 100644
index 0000000..5e319f5
--- /dev/null
+++ 
b/t/lib/dubbo-backend/dubbo-backend-interface/src/main/java/org/apache/dubbo/backend/DemoService.java
@@ -0,0 +1,43 @@
+/*
+ * 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.backend;
+
+import java.util.Map;
+
+public interface DemoService {;
+
+    /**
+     * standard samples tengine dubbo infterace demo
+     * @param context pass http infos
+     * @return Map<String, Object></> pass to response http
+     **/
+    Map<String, Object> hello(Map<String, Object> context);
+
+    /**
+     * test for dubbo non-200 response
+     * @param context pass http infos
+     * @return Map<String, Object></> pass to response http
+     **/
+    Map<String, Object> fail(Map<String, Object> context);
+
+    /**
+     * test for dubbo response timeout
+     * @param context pass http infos
+     * @return Map<String, Object></> pass to response http
+     **/
+    Map<String, Object> timeout(Map<String, Object> context);
+}
diff --git a/t/lib/dubbo-backend/dubbo-backend-provider/pom.xml 
b/t/lib/dubbo-backend/dubbo-backend-provider/pom.xml
new file mode 100644
index 0000000..7ceb3a1
--- /dev/null
+++ b/t/lib/dubbo-backend/dubbo-backend-provider/pom.xml
@@ -0,0 +1,96 @@
+<!--
+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.backend</groupId>
+        <artifactId>dubbo-backend</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <groupId>org.apache.dubbo.backend</groupId>
+    <artifactId>dubbo-backend-provider</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description></description>
+    <properties>
+        <skip_maven_deploy>true</skip_maven_deploy>
+        <slf4j-log4j12.version>1.7.25</slf4j-log4j12.version>
+        <curator.version>2.12.0</curator.version>
+        <dubbo.version>2.7.7</dubbo.version>
+    </properties>
+    
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo.backend</groupId>
+            <artifactId>dubbo-backend-interface</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo</artifactId>
+            <version>${dubbo.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.5</version>
+        </dependency>
+    </dependencies>
+    
+    <build>
+        <finalName>dubbo-demo-provider</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            
<mainClass>org.apache.dubbo.backend.provider.Provider</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>com.jolira</groupId>
+                <artifactId>onejar-maven-plugin</artifactId>
+                <version>1.4.4</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>one-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git 
a/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/DemoServiceImpl.java
 
b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/DemoServiceImpl.java
new file mode 100644
index 0000000..260784c
--- /dev/null
+++ 
b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/DemoServiceImpl.java
@@ -0,0 +1,61 @@
+/*
+ * 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.backend.provider;
+
+import org.apache.dubbo.backend.DemoService;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.lang.InterruptedException;
+
+public class DemoServiceImpl implements DemoService {
+    @Override
+    public Map<String, Object> hello(Map<String, Object> context) {
+        Map<String, Object> ret = new HashMap<String, Object>();
+        ret.put("body", "dubbo success\n");
+        ret.put("status", "200");
+
+        for (Map.Entry<String, Object> entry : context.entrySet()) {
+            System.out.println("Key = " + entry.getKey() + ", Value = " + 
entry.getValue());
+            if (entry.getKey().startsWith("extra-arg")) {
+                ret.put("Got-" + entry.getKey(), entry.getValue());
+            }
+        }
+
+        return ret;
+    }
+
+    @Override
+    public Map<String, Object> fail(Map<String, Object> context) {
+        Map<String, Object> ret = new HashMap<String, Object>();
+        ret.put("body", "dubbo fail\n");
+        ret.put("status", "503");
+        return ret;
+    }
+
+    @Override
+    public Map<String, Object> timeout(Map<String, Object> context) {
+        Map<String, Object> ret = new HashMap<String, Object>();
+        try {
+            TimeUnit.MILLISECONDS.sleep(500);
+        } catch (InterruptedException ex) {}
+        ret.put("body", "dubbo fail\n");
+        ret.put("status", "503");
+        return ret;
+    }
+}
diff --git 
a/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/Provider.java
 
b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/Provider.java
new file mode 100644
index 0000000..2860737
--- /dev/null
+++ 
b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/java/org/apache/dubbo/backend/provider/Provider.java
@@ -0,0 +1,40 @@
+/*
+ * 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.backend.provider;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.util.concurrent.TimeUnit;
+import java.lang.InterruptedException;
+
+public class Provider {
+
+    /**
+     * To get ipv6 address to work, add
+     * System.setProperty("java.net.preferIPv6Addresses", "true");
+     * before running your application.
+     */
+    public static void main(String[] args) throws Exception {
+        ClassPathXmlApplicationContext context = new 
ClassPathXmlApplicationContext(new 
String[]{"META-INF/spring/dubbo-demo-provider.xml"});
+        context.start();
+        while (true) {
+            try {
+                TimeUnit.MINUTES.sleep(1);
+            } catch (InterruptedException ex) {}
+        }
+    }
+}
diff --git 
a/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml
 
b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml
new file mode 100644
index 0000000..14b5afd
--- /dev/null
+++ 
b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml
@@ -0,0 +1,39 @@
+<?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.
+  -->
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo";
+       xmlns="http://www.springframework.org/schema/beans";
+       xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
+       http://dubbo.apache.org/schema/dubbo 
http://dubbo.apache.org/schema/dubbo/dubbo.xsd";>
+
+    <!-- provider's application name, used for tracing dependency relationship 
-->
+    <dubbo:application name="demo-provider"/>
+
+    <!-- use multicast registry center to export service -->
+    <dubbo:registry address="multicast://224.5.6.7:1234"/>
+
+    <!-- use dubbo protocol to export service on port 20880 -->
+    <dubbo:protocol name="dubbo" port="20880" threads="1024"/>
+    
+    <!-- service implementation, as same as regular local bean -->
+    <bean id="demoService" 
class="org.apache.dubbo.backend.provider.DemoServiceImpl"/>
+
+    <!-- declare the service interface to be exported -->
+    <dubbo:service interface="org.apache.dubbo.backend.DemoService" 
ref="demoService"/>
+
+</beans>
\ No newline at end of file
diff --git a/.travis/linux_openresty_1_17_runner.sh 
b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/dubbo.properties
old mode 100755
new mode 100644
similarity index 89%
copy from .travis/linux_openresty_1_17_runner.sh
copy to 
t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/dubbo.properties
index e36dd83..258fd3b
--- a/.travis/linux_openresty_1_17_runner.sh
+++ 
b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/dubbo.properties
@@ -1,4 +1,3 @@
-#!/usr/bin/env bash
 #
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
-
-export OPENRESTY_VERSION=1.17.8.2
-. ./.travis/linux_openresty_runner.sh
+dubbo.application.qos.enable=false
diff --git a/.travis/linux_openresty_1_17_runner.sh 
b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/log4j.properties
old mode 100755
new mode 100644
similarity index 70%
copy from .travis/linux_openresty_1_17_runner.sh
copy to 
t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/log4j.properties
index e36dd83..2f4f4ad
--- a/.travis/linux_openresty_1_17_runner.sh
+++ 
b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/log4j.properties
@@ -1,4 +1,3 @@
-#!/usr/bin/env bash
 #
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
-
-export OPENRESTY_VERSION=1.17.8.2
-. ./.travis/linux_openresty_runner.sh
+###set log levels###
+log4j.rootLogger=info, stdout
+###output to the console###
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] 
%t %5p %c{2}: %m%n
diff --git a/t/lib/dubbo-backend/pom.xml b/t/lib/dubbo-backend/pom.xml
new file mode 100644
index 0000000..ed4bf0a
--- /dev/null
+++ b/t/lib/dubbo-backend/pom.xml
@@ -0,0 +1,97 @@
+<!--
+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>
+    <groupId>org.apache.dubbo.backend</groupId>
+    <artifactId>dubbo-backend</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>${project.artifactId}</name>
+    <description>A dubbo backend for test based on 
dubbo-samples-tengine</description>
+    <properties>
+        <skip_maven_deploy>true</skip_maven_deploy>
+        <dubbo.version>2.7.7</dubbo.version>
+    </properties>
+    <modules>
+        <module>dubbo-backend-interface</module>
+        <module>dubbo-backend-provider</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- Spring Boot -->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>2.1.5.RELEASE</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.dubbo</groupId>
+                <artifactId>dubbo-dependencies-bom</artifactId>
+                <version>${dubbo.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.dubbo</groupId>
+                <artifactId>dubbo</artifactId>
+                <version>${dubbo.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.springframework</groupId>
+                        <artifactId>spring</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>javax.servlet</groupId>
+                        <artifactId>servlet-api</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>log4j</groupId>
+                        <artifactId>log4j</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+    <dependencies>
+        <!-- Spring Boot dependencies -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+            <version>2.1.5.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-spring-boot-starter</artifactId>
+            <version>2.7.1</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <configuration>
+                        <source>1.8</source>
+                        <target>1.8</target>
+                    </configuration>
+                </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/t/plugin/dubbo-proxy/route.t b/t/plugin/dubbo-proxy/route.t
new file mode 100644
index 0000000..7feea03
--- /dev/null
+++ b/t/plugin/dubbo-proxy/route.t
@@ -0,0 +1,314 @@
+#
+# 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.
+#
+use t::APISIX;
+
+my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
+my $version = eval { `$nginx_binary -V 2>&1` };
+
+if ($version !~ m/\/mod_dubbo/) {
+    plan(skip_all => "mod_dubbo not installed");
+} else {
+    plan('no_plan');
+}
+
+repeat_each(1);
+log_level('info');
+no_root_location();
+no_shuffle();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (!defined $block->disable_dubbo) {
+        my $extra_yaml_config = <<_EOC_;
+plugins:
+    - dubbo-proxy
+    - response-rewrite
+    - proxy-rewrite
+_EOC_
+
+        $block->set_value("extra_yaml_config", $extra_yaml_config);
+    }
+
+    if (!$block->yaml_config) {
+        my $yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    config_center: yaml
+    enable_admin: false
+_EOC_
+
+        $block->set_value("yaml_config", $yaml_config);
+    }
+
+    if ($block->apisix_yaml) {
+        my $upstream = <<_EOC_;
+upstreams:
+  - nodes:
+        "127.0.0.1:20880": 1
+    type: roundrobin
+    id: 1
+#END
+_EOC_
+
+        $block->set_value("apisix_yaml", $block->apisix_yaml . $upstream);
+    }
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /hello");
+    }
+
+    if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+        $block->set_value("no_error_log", "[error]");
+    }
+});
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: ignore route's dubbo configuration if dubbo is disable globally
+--- disable_dubbo
+--- apisix_yaml
+routes:
+  -
+    uri: /hello
+    plugins:
+        dubbo-proxy:
+            service_name: org.apache.dubbo.backend.DemoService
+            service_version: 0.0.0
+            method: hello
+    upstream:
+        nodes:
+            "127.0.0.1:1980": 1
+        type: roundrobin
+--- response_body
+hello world
+
+
+
+=== TEST 2: check schema
+--- apisix_yaml
+routes:
+  -
+    uri: /hello
+    plugins:
+        dubbo-proxy:
+            service_name: org.apache.dubbo.backend.DemoService
+            method: hello
+    upstream_id: 1
+--- error_log
+property "service_version" is required
+--- error_code: 404
+
+
+
+=== TEST 3: sanity
+--- apisix_yaml
+routes:
+  -
+    uri: /hello
+    plugins:
+        dubbo-proxy:
+            service_name: org.apache.dubbo.backend.DemoService
+            service_version: 0.0.0
+            method: hello
+    upstream_id: 1
+--- more_headers
+Extra-Arg-K: V
+--- response_headers
+Got-extra-arg-k: V
+--- response_body
+dubbo success
+
+
+
+=== TEST 4: enabled in service
+--- apisix_yaml
+routes:
+  - uri: /hello
+    service_id: 1
+
+services:
+    -
+        plugins:
+            dubbo-proxy:
+                service_name: org.apache.dubbo.backend.DemoService
+                service_version: 0.0.0
+                method: hello
+        id: 1
+        upstream_id: 1
+--- response_body
+dubbo success
+
+
+
+=== TEST 5: work with consumer
+--- yaml_config
+apisix:
+    node_listen: 1984
+    enable_admin: true
+    admin_key: null
+plugins:
+    - key-auth
+    - dubbo-proxy
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+
+            local code, message = t('/apisix/admin/consumers',
+                ngx.HTTP_PUT,
+                [[{
+                     "username":"jack",
+                     "plugins": {
+                        "key-auth": {
+                            "key": "jack"
+                        }
+                     }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(message)
+                return
+            end
+
+            local code, message = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "upstream":{
+                        "nodes": {
+                            "127.0.0.1:20880": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "plugins": {
+                        "dubbo-proxy": {
+                            "service_name": 
"org.apache.dubbo.backend.DemoService",
+                            "service_version": "0.0.0",
+                            "method": "hello"
+                        },
+                        "key-auth": {}
+                    },
+                    "uris": ["/hello"] 
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+
+            ngx.say(message)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 6: blocked
+--- yaml_config
+apisix:
+    node_listen: 1984
+    enable_admin: true
+    admin_key: null
+plugins:
+    - key-auth
+    - dubbo-proxy
+--- error_code: 401
+
+
+
+=== TEST 7: passed
+--- yaml_config
+apisix:
+    node_listen: 1984
+    enable_admin: true
+    admin_key: null
+plugins:
+    - key-auth
+    - dubbo-proxy
+--- more_headers
+apikey: jack
+--- response_body
+dubbo success
+
+
+
+=== TEST 8: rewrite response
+--- apisix_yaml
+routes:
+  -
+    uri: /hello
+    plugins:
+        response-rewrite:
+            headers:
+                fruit: banana
+            body: "hello world\n"
+        dubbo-proxy:
+            service_name: org.apache.dubbo.backend.DemoService
+            service_version: 0.0.0
+            method: hello
+    upstream_id: 1
+
+--- response_body
+hello world
+--- response_headers
+fruit: banana
+
+
+
+=== TEST 9: rewrite request
+--- apisix_yaml
+routes:
+  -
+    uri: /hello
+    plugins:
+        proxy-rewrite:
+            headers:
+                extra-arg-fruit: banana
+        dubbo-proxy:
+            service_name: org.apache.dubbo.backend.DemoService
+            service_version: 0.0.0
+            method: hello
+    upstream_id: 1
+
+--- response_body
+dubbo success
+--- response_headers
+Got-extra-arg-fruit: banana
+
+
+
+=== TEST 10: use uri as default method
+--- apisix_yaml
+routes:
+  -
+    uri: /hello
+    plugins:
+        dubbo-proxy:
+            service_name: org.apache.dubbo.backend.DemoService
+            service_version: 0.0.0
+    upstream_id: 1
+
+--- response_body
+dubbo success
diff --git a/t/plugin/dubbo-proxy/upstream.t b/t/plugin/dubbo-proxy/upstream.t
new file mode 100644
index 0000000..27590a5
--- /dev/null
+++ b/t/plugin/dubbo-proxy/upstream.t
@@ -0,0 +1,137 @@
+#
+# 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.
+#
+use t::APISIX;
+
+my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
+my $version = eval { `$nginx_binary -V 2>&1` };
+
+if ($version !~ m/\/mod_dubbo/) {
+    plan(skip_all => "mod_dubbo not installed");
+} else {
+    plan('no_plan');
+}
+
+repeat_each(1);
+log_level('info');
+no_root_location();
+no_shuffle();
+worker_connections(256);
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /hello");
+    }
+
+    if (!defined $block->disable_dubbo) {
+        my $extra_yaml_config = <<_EOC_;
+plugins:
+    - dubbo-proxy
+_EOC_
+
+        $block->set_value("extra_yaml_config", $extra_yaml_config);
+    }
+
+    my $yaml_config = $block->yaml_config // <<_EOC_;
+apisix:
+    node_listen: 1984
+    config_center: yaml
+    enable_admin: false
+_EOC_
+
+    $block->set_value("yaml_config", $yaml_config);
+});
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: retry
+--- apisix_yaml
+upstreams:
+    - nodes:
+        - host: 127.0.0.1
+          port: 20881
+          weight: 1
+        - host: 127.0.0.1
+          port: 20880
+          weight: 1
+      type: roundrobin
+      id: 1
+routes:
+  -
+    uri: /hello
+    plugins:
+        dubbo-proxy:
+            service_name: org.apache.dubbo.backend.DemoService
+            service_version: 0.0.0
+            method: hello
+    upstream_id: 1
+#END
+--- response_body
+dubbo success
+
+
+
+=== TEST 2: upstream return error
+--- apisix_yaml
+routes:
+  -
+    uri: /hello
+    plugins:
+        dubbo-proxy:
+            service_name: org.apache.dubbo.backend.DemoService
+            service_version: 0.0.0
+            method: fail
+    upstream_id: 1
+upstreams:
+  - nodes:
+        "127.0.0.1:20880": 1
+    type: roundrobin
+    id: 1
+#END
+--- response_body
+dubbo fail
+--- error_code: 503
+
+
+
+=== TEST 3: upstream timeout
+--- apisix_yaml
+routes:
+  -
+    uri: /hello
+    plugins:
+        dubbo-proxy:
+            service_name: org.apache.dubbo.backend.DemoService
+            service_version: 0.0.0
+            method: timeout
+    upstream_id: 1
+upstreams:
+  - nodes:
+        "127.0.0.1:20880": 1
+    type: roundrobin
+    timeout:
+        connect: 0.1
+        read: 0.1
+        send: 0.1
+    id: 1
+#END
+--- error_log
+upstream timed out
+--- error_code: 504
diff --git a/utils/linux-install-openresty.sh b/utils/linux-install-openresty.sh
index b8847ef..4b4a6a2 100755
--- a/utils/linux-install-openresty.sh
+++ b/utils/linux-install-openresty.sh
@@ -15,6 +15,58 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+set -euo pipefail
+
+if [ "$OPENRESTY_VERSION" == "source" ]; then
+    cd ..
+
+    wget https://openresty.org/download/openresty-1.19.3.1.tar.gz
+    tar -zxvpf openresty-1.19.3.1.tar.gz
+
+    git clone --depth=1 https://github.com/api7/ngx_multi_upstream_module.git
+    cd ngx_multi_upstream_module || exit 1
+    ./patch.sh ../openresty-1.19.3.1
+
+    git clone --depth=1 https://github.com/api7/mod_dubbo.git ../mod_dubbo
+
+    cd ../openresty-1.19.3.1 || exit 1
+    ./configure --prefix="/usr/local/openresty-debug" \
+        --add-module=../mod_dubbo --add-module=../ngx_multi_upstream_module \
+        --with-debug \
+        --with-poll_module \
+        --with-pcre-jit \
+        --without-http_rds_json_module \
+        --without-http_rds_csv_module \
+        --without-lua_rds_parser \
+        --with-stream \
+        --with-stream_ssl_module \
+        --with-stream_ssl_preread_module \
+        --with-http_v2_module \
+        --without-mail_pop3_module \
+        --without-mail_imap_module \
+        --without-mail_smtp_module \
+        --with-http_stub_status_module \
+        --with-http_realip_module \
+        --with-http_addition_module \
+        --with-http_auth_request_module \
+        --with-http_secure_link_module \
+        --with-http_random_index_module \
+        --with-http_gzip_static_module \
+        --with-http_sub_module \
+        --with-http_dav_module \
+        --with-http_flv_module \
+        --with-http_mp4_module \
+        --with-http_gunzip_module \
+        --with-threads \
+        --with-compat \
+        --with-luajit-xcflags='-DLUAJIT_NUMMODE=2 -DLUAJIT_ENABLE_LUA52COMPAT'
+    make
+    sudo make install
+
+    sudo apt-get install lua5.1 liblua5.1-0-dev
+
+    exit 0
+fi
 
 wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
 sudo apt-get -y update --fix-missing

Reply via email to