You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2021/09/06 00:58:03 UTC

[apisix] branch master updated: feat: allow route to inherit hosts from service (#4977)

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 0ba89d5  feat: allow route to inherit hosts from service (#4977)
0ba89d5 is described below

commit 0ba89d5c0f9dc2863329a8fa6552c380453627cf
Author: 罗泽轩 <sp...@gmail.com>
AuthorDate: Mon Sep 6 08:57:55 2021 +0800

    feat: allow route to inherit hosts from service (#4977)
---
 apisix/http/route.lua                              |  15 +-
 apisix/http/router/radixtree_host_uri.lua          |  32 ++-
 apisix/http/router/radixtree_uri.lua               |  12 +-
 .../http/router/radixtree_uri_with_parameter.lua   |  12 +-
 apisix/plugin.lua                                  |   7 +
 apisix/schema_def.lua                              |   7 +-
 docs/en/latest/admin-api.md                        |   4 +-
 docs/zh/latest/admin-api.md                        |   4 +-
 t/router/radixtree-host-uri2.t                     |  36 +++
 t/router/radixtree-host-uri3.t                     | 138 ++++++++++
 t/router/radixtree-uri-host.t                      | 289 +++++++++++++++++++++
 t/router/radixtree-uri-with-parameter.t            |  87 +++++++
 12 files changed, 628 insertions(+), 15 deletions(-)

diff --git a/apisix/http/route.lua b/apisix/http/route.lua
index c5a5358..2a8a12b 100644
--- a/apisix/http/route.lua
+++ b/apisix/http/route.lua
@@ -17,6 +17,7 @@
 local require = require
 local radixtree = require("resty.radixtree")
 local router = require("apisix.utils.router")
+local service_fetch = require("apisix.http.service").get
 local core = require("apisix.core")
 local expr = require("resty.expr.v1")
 local plugin_checker = require("apisix.plugin").plugin_checker
@@ -56,13 +57,25 @@ function _M.create_radixtree_uri_router(routes, uri_routes, with_parameter)
                 filter_fun = filter_fun()
             end
 
+            local hosts = route.value.hosts or route.value.host
+            if not hosts and route.value.service_id then
+                local service = service_fetch(route.value.service_id)
+                if not service then
+                    core.log.error("failed to fetch service configuration by ",
+                                   "id: ", route.value.service_id)
+                    -- we keep the behavior that missing service won't affect the route matching
+                else
+                    hosts = service.value.hosts
+                end
+            end
+
             core.log.info("insert uri route: ",
                           core.json.delay_encode(route.value, true))
             core.table.insert(uri_routes, {
                 paths = route.value.uris or route.value.uri,
                 methods = route.value.methods,
                 priority = route.value.priority,
-                hosts = route.value.hosts or route.value.host,
+                hosts = hosts,
                 remote_addrs = route.value.remote_addrs
                                or route.value.remote_addr,
                 vars = route.value.vars,
diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua
index 08e0655..250dc27 100644
--- a/apisix/http/router/radixtree_host_uri.lua
+++ b/apisix/http/router/radixtree_host_uri.lua
@@ -17,12 +17,15 @@
 local require = require
 local router = require("apisix.utils.router")
 local core = require("apisix.core")
+local get_services = require("apisix.http.service").services
+local service_fetch = require("apisix.http.service").get
 local ipairs = ipairs
 local type = type
 local tab_insert = table.insert
 local loadstring = loadstring
 local pairs = pairs
-local cached_version
+local cached_router_version
+local cached_service_version
 local host_router
 local only_uri_router
 
@@ -49,7 +52,21 @@ local function push_host_router(route, host_routes, only_uri_routes)
         filter_fun = filter_fun()
     end
 
-    local hosts = route.value.hosts or {route.value.host}
+    local hosts = route.value.hosts
+    if not hosts then
+        if route.value.host then
+            hosts = {route.value.host}
+        elseif route.value.service_id then
+            local service = service_fetch(route.value.service_id)
+            if not service then
+                core.log.error("failed to fetch service configuration by ",
+                                "id: ", route.value.service_id)
+                -- we keep the behavior that missing service won't affect the route matching
+            else
+                hosts = service.value.hosts
+            end
+        end
+    end
 
     local radixtree_route = {
         paths = route.value.uris or route.value.uri,
@@ -66,7 +83,7 @@ local function push_host_router(route, host_routes, only_uri_routes)
         end
     }
 
-    if #hosts == 0 then
+    if hosts == nil then
         core.table.insert(only_uri_routes, radixtree_route)
         return
     end
@@ -124,11 +141,16 @@ end
     local match_opts = {}
 function _M.match(api_ctx)
     local user_routes = _M.user_routes
-    if not cached_version or cached_version ~= user_routes.conf_version then
+    local _, service_version = get_services()
+    if not cached_router_version or cached_router_version ~= user_routes.conf_version
+        or not cached_service_version or cached_service_version ~= service_version
+    then
         create_radixtree_router(user_routes.values)
-        cached_version = user_routes.conf_version
+        cached_router_version = user_routes.conf_version
+        cached_service_version = service_version
     end
 
+
     core.table.clear(match_opts)
     match_opts.method = api_ctx.var.request_method
     match_opts.remote_addr = api_ctx.var.remote_addr
diff --git a/apisix/http/router/radixtree_uri.lua b/apisix/http/router/radixtree_uri.lua
index f3245fd..b438ed8 100644
--- a/apisix/http/router/radixtree_uri.lua
+++ b/apisix/http/router/radixtree_uri.lua
@@ -17,7 +17,9 @@
 local require = require
 local core = require("apisix.core")
 local base_router = require("apisix.http.route")
-local cached_version
+local get_services = require("apisix.http.service").services
+local cached_router_version
+local cached_service_version
 
 
 local _M = {version = 0.2}
@@ -28,10 +30,14 @@ local _M = {version = 0.2}
     local match_opts = {}
 function _M.match(api_ctx)
     local user_routes = _M.user_routes
-    if not cached_version or cached_version ~= user_routes.conf_version then
+    local _, service_version = get_services()
+    if not cached_router_version or cached_router_version ~= user_routes.conf_version
+        or not cached_service_version or cached_service_version ~= service_version
+    then
         uri_router = base_router.create_radixtree_uri_router(user_routes.values,
                                                              uri_routes, false)
-        cached_version = user_routes.conf_version
+        cached_router_version = user_routes.conf_version
+        cached_service_version = service_version
     end
 
     if not uri_router then
diff --git a/apisix/http/router/radixtree_uri_with_parameter.lua b/apisix/http/router/radixtree_uri_with_parameter.lua
index f7b6a22..2570171 100644
--- a/apisix/http/router/radixtree_uri_with_parameter.lua
+++ b/apisix/http/router/radixtree_uri_with_parameter.lua
@@ -17,7 +17,9 @@
 local require = require
 local core = require("apisix.core")
 local base_router = require("apisix.http.route")
-local cached_version
+local get_services = require("apisix.http.service").services
+local cached_router_version
+local cached_service_version
 
 
 local _M = {}
@@ -28,10 +30,14 @@ local _M = {}
     local match_opts = {}
 function _M.match(api_ctx)
     local user_routes = _M.user_routes
-    if not cached_version or cached_version ~= user_routes.conf_version then
+    local _, service_version = get_services()
+    if not cached_router_version or cached_router_version ~= user_routes.conf_version
+        or not cached_service_version or cached_service_version ~= service_version
+    then
         uri_router = base_router.create_radixtree_uri_router(user_routes.values,
                                                              uri_routes, true)
-        cached_version = user_routes.conf_version
+        cached_router_version = user_routes.conf_version
+        cached_service_version = service_version
     end
 
     if not uri_router then
diff --git a/apisix/plugin.lua b/apisix/plugin.lua
index 973c3ea..307e102 100644
--- a/apisix/plugin.lua
+++ b/apisix/plugin.lua
@@ -416,6 +416,13 @@ local function merge_service_route(service_conf, route_conf)
         new_conf.value.name = nil
     end
 
+    if route_conf.value.hosts then
+        new_conf.value.hosts = route_conf.value.hosts
+    end
+    if not new_conf.value.hosts and route_conf.value.host then
+        new_conf.value.host = route_conf.value.host
+    end
+
     -- core.log.info("merged conf : ", core.json.delay_encode(new_conf))
     return new_conf
 end
diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua
index c8f4ad5..adddf15 100644
--- a/apisix/schema_def.lua
+++ b/apisix/schema_def.lua
@@ -661,7 +661,12 @@ _M.service = {
             description = "enable websocket for request",
             type        = "boolean",
         },
-
+        hosts = {
+            type = "array",
+            items = host_def,
+            minItems = 1,
+            uniqueItems = true,
+        },
     },
 }
 
diff --git a/docs/en/latest/admin-api.md b/docs/en/latest/admin-api.md
index e106919..5534c84 100644
--- a/docs/en/latest/admin-api.md
+++ b/docs/en/latest/admin-api.md
@@ -323,12 +323,13 @@ Return response from etcd currently.
 | desc             | False    | Auxiliary   | service usage scenarios, and more.                                                       | service xxxx                                   |
 | labels           | False    | Match Rules | Key/value pairs to specify attributes                                                    | {"version":"v2","build":"16","env":"production"} |
 | enable_websocket | False    | Auxiliary   | enable `websocket`(boolean), default `false`.                                            |                                                  |
+| hosts            | False    | Match Rules | The `host` in the form of a non-empty list means that multiple different hosts are allowed, and match any one of them.| ["foo.com", "*.bar.com"]                             |
 | create_time      | False    | Auxiliary   | epoch timestamp in second, will be created automatically if missing                      | 1602883670                                       |
 | update_time      | False    | Auxiliary   | epoch timestamp in second, will be created automatically if missing                      | 1602883670                                       |
 
 Config Example:
 
-```shell
+```json
 {
     "id": "1",          # id
     "plugins": {},      # Bound plugin
@@ -337,6 +338,7 @@ Config Example:
     "name": "service-test",
     "desc": "hello world",
     "enable_websocket": true,
+    "hosts": ["foo.com"]
 }
 ```
 
diff --git a/docs/zh/latest/admin-api.md b/docs/zh/latest/admin-api.md
index b3454ce..90da553 100644
--- a/docs/zh/latest/admin-api.md
+++ b/docs/zh/latest/admin-api.md
@@ -325,12 +325,13 @@ HTTP/1.1 200 OK
 | desc             | 可选                               | 辅助     | 服务描述、使用场景等。                                                 |                                                  |
 | labels           | 可选                               | 匹配规则 | 标识附加属性的键值对                                                   | {"version":"v2","build":"16","env":"production"} |
 | enable_websocket | 可选                               | 辅助     | 是否启用 `websocket`(boolean), 缺省 `false`.                           |                                                  |
+| hosts            | 可选                              | 匹配规则 | 非空列表形态的 `host`,表示允许有多个不同 `host`,匹配其中任意一个即可。| ["foo.com", "\*.bar.com"]                            |
 | create_time      | 可选                               | 辅助     | 单位为秒的 epoch 时间戳,如果不指定则自动创建                          | 1602883670                                       |
 | update_time      | 可选                               | 辅助     | 单位为秒的 epoch 时间戳,如果不指定则自动创建                          | 1602883670                                       |
 
 service 对象 json 配置内容:
 
-```shell
+```json
 {
     "id": "1",              # id
     "plugins": {},          # 指定 service 绑定的插件
@@ -339,6 +340,7 @@ service 对象 json 配置内容:
     "name": "测试svc",  # service 名称
     "desc": "hello world",  # service 描述
     "enable_websocket": true, #启动 websocket 功能
+    "hosts": ["foo.com"]
 }
 ```
 
diff --git a/t/router/radixtree-host-uri2.t b/t/router/radixtree-host-uri2.t
index 20f034b..313bc6a 100644
--- a/t/router/radixtree-host-uri2.t
+++ b/t/router/radixtree-host-uri2.t
@@ -376,3 +376,39 @@ GET /server_port
 Host: test.com
 --- no_error_log
 [error]
+
+
+
+=== TEST 14: inherit hosts from services
+--- yaml_config eval: $::yaml_config
+--- apisix_yaml
+services:
+  - id: 1
+    hosts:
+      - bar.com
+upstreams:
+  - id: 1
+    nodes:
+      "127.0.0.1:1980": 1
+    type: roundrobin
+routes:
+  -
+    service_id: 1
+    upstream_id: 1
+    uri: /hello
+    plugins:
+        proxy-rewrite:
+            uri: /hello1
+  -
+    upstream_id: 1
+    uri: /hello
+    priority: -1
+#END
+--- more_headers
+Host: www.foo.com
+--- request
+GET /hello
+--- response_body
+hello world
+--- no_error_log
+[error]
diff --git a/t/router/radixtree-host-uri3.t b/t/router/radixtree-host-uri3.t
new file mode 100644
index 0000000..42c6faf
--- /dev/null
+++ b/t/router/radixtree-host-uri3.t
@@ -0,0 +1,138 @@
+#
+# 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 'no_plan';
+
+our $yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    router:
+        http: 'radixtree_host_uri'
+    admin_key: null
+_EOC_
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    $block->set_value("yaml_config", $yaml_config);
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if (!$block->error_log && !$block->no_error_log &&
+        (defined $block->error_code && $block->error_code != 502))
+    {
+        $block->set_value("no_error_log", "[error]");
+    }
+
+    $block;
+});
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: change hosts in services
+--- config
+    location /t {
+        content_by_lua_block {
+            local http = require "resty.http"
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port
+                        .. "/hello"
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/services/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "hosts": ["foo.com"]
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "service_id": "1",
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            for _, h in ipairs({"foo.com", "bar.com"}) do
+                local httpc = http.new()
+                local res, err = httpc:request_uri(uri, {headers = {Host = h}})
+                if not res then
+                    ngx.say(err)
+                    return
+                end
+                if res.status == 404 then
+                    ngx.say(res.status)
+                else
+                    ngx.print(res.body)
+                end
+            end
+
+            local code, body = t('/apisix/admin/services/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "hosts": ["bar.com"]
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            for _, h in ipairs({"foo.com", "bar.com"}) do
+                local httpc = http.new()
+                local res, err = httpc:request_uri(uri, {headers = {Host = h}})
+                if not res then
+                    ngx.say(err)
+                    return
+                end
+                if res.status == 404 then
+                    ngx.say(res.status)
+                else
+                    ngx.print(res.body)
+                end
+            end
+        }
+    }
+--- response_body
+hello world
+404
+404
+hello world
diff --git a/t/router/radixtree-uri-host.t b/t/router/radixtree-uri-host.t
index a0d1141..6e93ae4 100644
--- a/t/router/radixtree-uri-host.t
+++ b/t/router/radixtree-uri-host.t
@@ -299,3 +299,292 @@ GET /file:xx
 --- error_code: 404
 --- no_error_log
 [error]
+
+
+
+=== TEST 18: inherit hosts from services
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/services/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "hosts": ["bar.com"]
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "plugins": {
+                            "proxy-rewrite":{"uri":"/hello1"}
+                        },
+                        "service_id": "1",
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+
+            local code, body = t('/apisix/admin/routes/2',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello",
+                        "priority": -1
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 19: hit
+--- more_headers
+Host: www.foo.com
+--- request
+GET /hello
+--- response_body
+hello world
+--- no_error_log
+[error]
+
+
+
+=== TEST 20: change hosts in services
+--- config
+    location /t {
+        content_by_lua_block {
+            local http = require "resty.http"
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port
+                        .. "/hello"
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/services/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "hosts": ["foo.com"]
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            local httpc = http.new()
+            local res, err = httpc:request_uri(uri, {headers = {Host = "foo.com"}})
+            if not res then
+                ngx.say(err)
+                return
+            end
+            ngx.print(res.body)
+
+            local code, body = t('/apisix/admin/services/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "hosts": ["bar.com"]
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            local httpc = http.new()
+            local res, err = httpc:request_uri(uri, {headers = {Host = "foo.com"}})
+            if not res then
+                ngx.say(err)
+                return
+            end
+            ngx.print(res.body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+hello1 world
+hello world
+--- no_error_log
+[error]
+
+
+
+=== TEST 21: unbind services
+--- config
+    location /t {
+        content_by_lua_block {
+            local http = require "resty.http"
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port
+                        .. "/hello"
+            local t = require("lib.test_admin").test
+
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "plugins": {
+                            "proxy-rewrite":{"uri":"/hello1"}
+                        },
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            local httpc = http.new()
+            local res, err = httpc:request_uri(uri, {headers = {Host = "foo.com"}})
+            if not res then
+                ngx.say(err)
+                return
+            end
+            ngx.print(res.body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+hello1 world
+--- no_error_log
+[error]
+
+
+
+=== TEST 22: host from route is preferred
+--- config
+    location /t {
+        content_by_lua_block {
+            local http = require "resty.http"
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port
+                        .. "/hello"
+            local t = require("lib.test_admin").test
+
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "hosts": ["foo.com"],
+                        "plugins": {
+                            "proxy-rewrite":{"uri":"/hello1"}
+                        },
+                        "service_id": "1",
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            for _, h in ipairs({"foo.com", "bar.com"}) do
+                local httpc = http.new()
+                local res, err = httpc:request_uri(uri, {headers = {Host = h}})
+                if not res then
+                    ngx.say(err)
+                    return
+                end
+                ngx.print(res.body)
+            end
+
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "host": "foo.com",
+                        "plugins": {
+                            "proxy-rewrite":{"uri":"/hello1"}
+                        },
+                        "service_id": "1",
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.1)
+
+            for _, h in ipairs({"foo.com", "bar.com"}) do
+                local httpc = http.new()
+                local res, err = httpc:request_uri(uri, {headers = {Host = h}})
+                if not res then
+                    ngx.say(err)
+                    return
+                end
+                ngx.print(res.body)
+            end
+        }
+    }
+--- request
+GET /t
+--- response_body
+hello1 world
+hello world
+hello1 world
+hello world
+--- no_error_log
+[error]
diff --git a/t/router/radixtree-uri-with-parameter.t b/t/router/radixtree-uri-with-parameter.t
index 70fd59a..d8fa095 100644
--- a/t/router/radixtree-uri-with-parameter.t
+++ b/t/router/radixtree-uri-with-parameter.t
@@ -212,3 +212,90 @@ GET /json/bbb/foo
 {"error_msg":"404 Route Not Found"}
 --- no_error_log
 [error]
+
+
+
+=== TEST 10: inherit hosts from services
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/services/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "hosts": ["bar.com"]
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "plugins": {
+                            "proxy-rewrite":{"uri":"/hello1"}
+                        },
+                        "service_id": "1",
+                        "uri": "/:name/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+
+            local code, body = t('/apisix/admin/routes/2',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "plugins": {
+                            "proxy-rewrite":{"uri":"/hello"}
+                        },
+                        "uri": "/:name/hello",
+                        "priority": -1
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 11: hit
+--- more_headers
+Host: www.foo.com
+--- request
+GET /john/hello
+--- response_body
+hello world
+--- no_error_log
+[error]