diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f812d20585cb9..7192316ce951b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -58,6 +58,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 665f66d6e7c70..ce1002a5c8db9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,6 +54,8 @@ fastcgi_temp
client_body_temp
utils/lj-releng
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 b08604509ae8e..b4ba843fb90b0 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/.travis/apisix_cli_test/test_dubbo.sh b/.travis/apisix_cli_test/test_dubbo.sh
new file mode 100755
index 0000000000000..49814a1133024
--- /dev/null
+++ b/.travis/apisix_cli_test/test_dubbo.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+#
+# 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.
+#
+
+. ./.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
+
+make init
+
+if ! grep "multi 16;" conf/nginx.conf > /dev/null; then
+ echo "failed: dubbo multiplex configuration not found in nginx.conf"
+ exit 1
+fi
+
+echo "passed: found dubbo multiplex configuration in nginx.conf"
diff --git a/.travis/linux_openresty_1_17_runner.sh b/.travis/linux_openresty_1_17_runner.sh
index e36dd8336ee98..39d83336f28ce 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_common_runner.sh b/.travis/linux_openresty_common_runner.sh
new file mode 100755
index 0000000000000..2229d6a1869e3
--- /dev/null
+++ b/.travis/linux_openresty_common_runner.sh
@@ -0,0 +1,167 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+. ./.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
diff --git a/.travis/linux_openresty_runner.sh b/.travis/linux_openresty_runner.sh
index f451bbe053f09..eb1e9858899d6 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 026d5405338f3..e8e8887ebef7c 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 f5c921c6845e3..79ef5b2e6e9f5 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/apisix/cli/ngx_tpl.lua b/apisix/cli/ngx_tpl.lua
index 9f496d22ca831..40a7a7932567d 100644
--- a/apisix/cli/ngx_tpl.lua
+++ b/apisix/cli/ngx_tpl.lua
@@ -240,6 +240,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")
@@ -398,6 +410,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 {
@@ -556,6 +575,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 a0852e5eedb71..09efc6d153764 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 bdbde96aef0de..57c891e54db48 100644
--- a/apisix/init.lua
+++ b/apisix/init.lua
@@ -24,6 +24,7 @@ local admin_init = require("apisix.admin.init")
local get_var = require("resty.ngxvar").fetch
local router = require("apisix.router")
local set_upstream = require("apisix.upstream").set_by_route
+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 ipairs = ipairs
local tostring = tostring
local type = type
local ngx_now = ngx.now
+local ngx_var = ngx.var
local str_byte = string.byte
local str_sub = string.sub
local tonumber = tonumber
@@ -543,6 +545,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 0000000000000..57a093f062a30
--- /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 a4cd24e3b48b9..ed8ec4c074021 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 0000000000000..175f3c9b28e6a
--- /dev/null
+++ b/doc/plugins/dubbo-proxy.md
@@ -0,0 +1,131 @@
+
+
+# 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 '
+{
+ "methods": ["GET"],
+ "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 1396c88123e59..ef1a6fc60b3ea 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 2634eea45b2af..220745e778ded 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 0000000000000..3087def77ca1c
--- /dev/null
+++ b/t/lib/dubbo-backend/dubbo-backend-interface/pom.xml
@@ -0,0 +1,45 @@
+
+
+ 4.0.0
+
+ org.apache.dubbo.backend
+ dubbo-backend
+ 1.0.0-SNAPSHOT
+
+ org.apache.dubbo.backend
+ dubbo-backend-interface
+ 1.0.0-SNAPSHOT
+ jar
+ ${project.artifactId}
+
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.8
+
+
+
+
+
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 0000000000000..5e319f5559588
--- /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> pass to response http
+ **/
+ Map hello(Map context);
+
+ /**
+ * test for dubbo non-200 response
+ * @param context pass http infos
+ * @return Map> pass to response http
+ **/
+ Map fail(Map context);
+
+ /**
+ * test for dubbo response timeout
+ * @param context pass http infos
+ * @return Map> pass to response http
+ **/
+ Map timeout(Map 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 0000000000000..7ceb3a18e338a
--- /dev/null
+++ b/t/lib/dubbo-backend/dubbo-backend-provider/pom.xml
@@ -0,0 +1,96 @@
+
+
+ 4.0.0
+
+ org.apache.dubbo.backend
+ dubbo-backend
+ 1.0.0-SNAPSHOT
+
+ org.apache.dubbo.backend
+ dubbo-backend-provider
+ 1.0.0-SNAPSHOT
+ jar
+ ${project.artifactId}
+
+
+ true
+ 1.7.25
+ 2.12.0
+ 2.7.7
+
+
+
+
+ org.apache.dubbo.backend
+ dubbo-backend-interface
+ 1.0.0-SNAPSHOT
+
+
+ org.apache.dubbo
+ dubbo
+ ${dubbo.version}
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.5
+
+
+
+
+ dubbo-demo-provider
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ org.apache.dubbo.backend.provider.Provider
+
+
+
+
+
+ com.jolira
+ onejar-maven-plugin
+ 1.4.4
+
+
+
+ one-jar
+
+
+
+
+
+
+
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 0000000000000..260784cccbcbb
--- /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 hello(Map context) {
+ Map ret = new HashMap();
+ ret.put("body", "dubbo success\n");
+ ret.put("status", "200");
+
+ for (Map.Entry 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 fail(Map context) {
+ Map ret = new HashMap();
+ ret.put("body", "dubbo fail\n");
+ ret.put("status", "503");
+ return ret;
+ }
+
+ @Override
+ public Map timeout(Map context) {
+ Map ret = new HashMap();
+ 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 0000000000000..28607375b0065
--- /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 0000000000000..14b5afd7613b9
--- /dev/null
+++ b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/META-INF/spring/dubbo-demo-provider.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/dubbo.properties b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/dubbo.properties
new file mode 100644
index 0000000000000..258fd3bf2628c
--- /dev/null
+++ b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/dubbo.properties
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+dubbo.application.qos.enable=false
diff --git a/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/log4j.properties b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/log4j.properties
new file mode 100644
index 0000000000000..2f4f4addf137f
--- /dev/null
+++ b/t/lib/dubbo-backend/dubbo-backend-provider/src/main/resources/log4j.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+###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 0000000000000..ed4bf0a755cd1
--- /dev/null
+++ b/t/lib/dubbo-backend/pom.xml
@@ -0,0 +1,97 @@
+
+
+ 4.0.0
+ org.apache.dubbo.backend
+ dubbo-backend
+ 1.0.0-SNAPSHOT
+ pom
+ ${project.artifactId}
+ A dubbo backend for test based on dubbo-samples-tengine
+
+ true
+ 2.7.7
+
+
+ dubbo-backend-interface
+ dubbo-backend-provider
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ 2.1.5.RELEASE
+ pom
+ import
+
+
+ org.apache.dubbo
+ dubbo-dependencies-bom
+ ${dubbo.version}
+ pom
+ import
+
+
+ org.apache.dubbo
+ dubbo
+ ${dubbo.version}
+
+
+ org.springframework
+ spring
+
+
+ javax.servlet
+ servlet-api
+
+
+ log4j
+ log4j
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+ 2.1.5.RELEASE
+
+
+ org.apache.dubbo
+ dubbo-spring-boot-starter
+ 2.7.1
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.8
+
+
+
+
+
diff --git a/t/plugin/dubbo-proxy/route.t b/t/plugin/dubbo-proxy/route.t
new file mode 100644
index 0000000000000..8e456104e5bd2
--- /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 comsumer
+--- 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 0000000000000..27590a58038f8
--- /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 0558efd274905..88c6f9f90346e 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