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/05/17 14:42:22 UTC

[apisix] branch master updated: feat: support uri encoding in redirect (#4244)

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 b0cae10  feat: support uri encoding in redirect (#4244)
b0cae10 is described below

commit b0cae100d2a8c42b023836799265a63cebb1876a
Author: Gary-Airwallex <ga...@airwallex.com>
AuthorDate: Mon May 17 22:42:16 2021 +0800

    feat: support uri encoding in redirect (#4244)
---
 apisix/plugins/redirect.lua        | 37 +++++++++++----
 docs/en/latest/plugins/redirect.md |  3 +-
 docs/zh/latest/plugins/redirect.md |  3 +-
 t/plugin/redirect.t                | 97 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 129 insertions(+), 11 deletions(-)

diff --git a/apisix/plugins/redirect.lua b/apisix/plugins/redirect.lua
index 40db3e0..c2c2aec 100644
--- a/apisix/plugins/redirect.lua
+++ b/apisix/plugins/redirect.lua
@@ -22,6 +22,8 @@ local re_gmatch = ngx.re.gmatch
 local re_sub = ngx.re.sub
 local ipairs = ipairs
 local ngx = ngx
+local str_find = core.string.find
+local str_sub  = string.sub
 
 local lrucache = core.lrucache.new({
     ttl = 300, count = 100
@@ -49,6 +51,7 @@ local schema = {
             }
         },
         http_to_https = {type = "boolean"},
+        encode_uri = {type = "boolean", default = false}
     },
     oneOf = {
         {required = {"uri"}},
@@ -160,18 +163,18 @@ function _M.rewrite(conf, ctx)
     end
 
     if ret_code then
+        local new_uri
         if uri then
-            local new_uri, err = concat_new_uri(uri, ctx)
+            local err
+            new_uri, err = concat_new_uri(uri, ctx)
             if not new_uri then
                 core.log.error("failed to generate new uri by: " .. uri .. err)
                 return 500
             end
-
-            core.response.set_header("Location", new_uri)
-            return ret_code
         elseif regex_uri then
-            local new_uri, n, err = re_sub(ctx.var.uri, regex_uri[1],
-                                           regex_uri[2], "jo")
+            local n, err
+            new_uri, n, err = re_sub(ctx.var.uri, regex_uri[1],
+                                     regex_uri[2], "jo")
             if not new_uri then
                 local msg = string_format("failed to substitute the uri:%s (%s) with %s, error:%s",
                                           ctx.var.uri, regex_uri[1], regex_uri[2], err)
@@ -179,11 +182,27 @@ function _M.rewrite(conf, ctx)
                 return 500
             end
 
-            if n > 0 then
-                core.response.set_header("Location", new_uri)
-                return ret_code
+            if n < 1 then
+                return
             end
         end
+
+        if not new_uri then
+            return
+        end
+
+        if conf.encode_uri then
+            local index = str_find(new_uri, "?")
+            if index then
+                new_uri = core.utils.uri_safe_encode(str_sub(new_uri, 1, index-1)) ..
+                          str_sub(new_uri, index)
+            else
+                new_uri = core.utils.uri_safe_encode(new_uri)
+            end
+        end
+
+        core.response.set_header("Location", new_uri)
+        return ret_code
     end
 
 end
diff --git a/docs/en/latest/plugins/redirect.md b/docs/en/latest/plugins/redirect.md
index 4652f26..9ce47bc 100644
--- a/docs/en/latest/plugins/redirect.md
+++ b/docs/en/latest/plugins/redirect.md
@@ -41,8 +41,9 @@ URI redirect.
 | uri           | string  | optional    |         |       | New URL which can contain Nginx variable, eg: `/test/index.html`, `$uri/index.html`. You can refer to variables in a way similar to `${xxx}` to avoid ambiguity, eg: `${uri}foo/index.html`. If you just need the original `$` character, add `\` in front of it, like this one: `/\$foo/index.html`. If you refer to a variable name that does not exist, this will not produce an error, and it will be used as an empty string. |
 | regex_uri | array[string] | optional    |         |                   | Use regular expression to match URL from client, when the match is successful, the URL template will be redirected to. If the match is not successful, the URL from the client will be forwarded to the upstream. Only one of `uri` and `regex_uri` can be exist. For example: [" ^/iresty/(.*)/(.*)/(.*)", "/$1-$2-$3"], the first element represents the matching regular expression and the second element represents the URL t [...]
 | ret_code      | integer | optional    | 302     |  [200, ...]     | Response code                                                                                                                                                                                                                                                                                                                                                                                                                      |
+| encode_uri    | boolean | optional    | false   |       | When set to `true` the uri in `Location` header will be encoded  as per [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986) |
 
-Only one of `http_to_https` or `uri` can be specified.
+Only one of `http_to_https`, `uri` or `regex_uri` can be specified.
 
 ## How To Enable
 
diff --git a/docs/zh/latest/plugins/redirect.md b/docs/zh/latest/plugins/redirect.md
index 2f64a25..1408e68 100644
--- a/docs/zh/latest/plugins/redirect.md
+++ b/docs/zh/latest/plugins/redirect.md
@@ -31,8 +31,9 @@ URI 重定向插件。
 | uri           | string  | 可选        |         |            | 可以包含 Nginx 变量的 URI,例如:`/test/index.html`, `$uri/index.html`。你可以通过类似于 `$ {xxx}` 的方式引用变量,以避免产生歧义,例如:`${uri}foo/index.html`。若你需要保留 `$` 字符,那么使用如下格式:`/\$foo/index.html` |
 | regex_uri | array[string] | 可选        |         |                   | 转发到上游的新 `uri` 地址, 使用正则表达式匹配来自客户端的 `uri`,当匹配成功后使用模板替换发送重定向到客户端, 未匹配成功时将客户端请求的 `uri` 转发至上游。`uri` 和 `regex_uri` 不可以同时存在。例如:["^/iresty/(.*)/(.*)/(.*)","/$1-$2-$3"] 第一个元素代表匹配来自客户端请求的 `uri` 正则表达式,第二个元素代表匹配成功后发送重定向到客户端的 `uri` 模板。 |
 | ret_code      | integer | 可选        | 302     | [200, ...] | 请求响应码                                                                                                                                                                                                                    |
+| encode_uri    | boolean | 可选        | false   |       | 当设置为 `true` 时,对返回的 `Location` header进行编码,编码格式参考 [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986) |
 
-`http_to_https` 和 `uri` 两个中只能配置一个。
+`http_to_https`,`uri` 或 `regex_uri` 三个中只能配置一个。
 
 ### 示例
 
diff --git a/t/plugin/redirect.t b/t/plugin/redirect.t
index 56e7425..20e5b4a 100644
--- a/t/plugin/redirect.t
+++ b/t/plugin/redirect.t
@@ -813,3 +813,100 @@ GET /hello
 hello world
 --- no_error_log
 [error]
+
+
+
+=== TEST 33: add plugin with new regex_uri: encode_uri = true
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "plugins": {
+                        "redirect": {
+                            "regex_uri": ["^/test/(.*)", "http://test.com/${1}"],
+                            "ret_code": 301,
+                            "encode_uri": true
+                        }
+                    },
+                    "uri": "/test/*",
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    }
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 34: regex_uri redirect with special characters
+--- request
+GET /test/with%20space
+--- error_code: 200
+--- response_headers
+Location: http://test.com/with%20space
+--- error_code: 301
+--- no_error_log
+[error]
+
+
+
+=== TEST 35: add plugin with new uri: encode_uri = true
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "plugins": {
+                        "redirect": {
+                            "uri": "$uri",
+                            "ret_code": 301,
+                            "encode_uri": true
+                        }
+                    },
+                    "uri": "/hello*"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 36: redirect with special characters
+--- request
+GET /hello/with%20space
+--- response_headers
+Location: /hello/with%20space
+--- error_code: 301
+--- no_error_log
+[error]