You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by we...@apache.org on 2020/06/03 09:47:06 UTC
[incubator-apisix] branch master updated: feature: support
http_to_https in redirect plugin. (#1642)
This is an automated email from the ASF dual-hosted git repository.
wenming pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-apisix.git
The following commit(s) were added to refs/heads/master by this push:
new f615cf7 feature: support http_to_https in redirect plugin. (#1642)
f615cf7 is described below
commit f615cf7134f47110270e879627730b7b6f9525b2
Author: Wen Ming <mo...@gmail.com>
AuthorDate: Wed Jun 3 17:46:58 2020 +0800
feature: support http_to_https in redirect plugin. (#1642)
---
FAQ.md | 18 ++-
FAQ_CN.md | 18 ++-
apisix/plugins/redirect.lua | 37 ++++--
doc/plugins/redirect-cn.md | 20 ++-
doc/plugins/redirect.md | 18 ++-
t/plugin/redirect.t | 297 ++++++++++++++++++++++++++++++++++++++++++--
6 files changed, 378 insertions(+), 30 deletions(-)
diff --git a/FAQ.md b/FAQ.md
index 55c1590..2335456 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -116,7 +116,21 @@ https://github.com/iresty/lua-resty-radixtree#operator-list
An example, redirect `http://foo.com` to `https://foo.com`
There are several different ways to do this.
-1. `redirect` plugin:
+1. Directly use the `http_to_https` in `redirect` plugin:
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "uri": "/hello",
+ "host": "foo.com",
+ "plugins": {
+ "redirect": {
+ "http_to_https": true
+ }
+ }
+}'
+```
+
+2. Use with advanced routing rule `vars` with `redirect` plugin:
```shell
curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
@@ -139,7 +153,7 @@ curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f03433
}'
```
-2. `serverless` plugin:
+3. `serverless` plugin:
```shell
curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
diff --git a/FAQ_CN.md b/FAQ_CN.md
index 2a576a6..b954db9 100644
--- a/FAQ_CN.md
+++ b/FAQ_CN.md
@@ -119,7 +119,21 @@ https://github.com/iresty/lua-resty-radixtree#operator-list
比如,将 `http://foo.com` 重定向到 `https://foo.com`
有几种不同的方法来实现:
-1. 使用`redirect`插件:
+1. 直接使用 `redirect` 插件的 `http_to_https` 功能:
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "uri": "/hello",
+ "host": "foo.com",
+ "plugins": {
+ "redirect": {
+ "http_to_https": true
+ }
+ }
+}'
+```
+
+2. 结合高级路由规则 `vars` 和 `redirect` 插件一起使用:
```shell
curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
@@ -142,7 +156,7 @@ curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f03433
}'
```
-2. 使用`serverless`插件:
+3. 使用`serverless`插件:
```shell
curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
diff --git a/apisix/plugins/redirect.lua b/apisix/plugins/redirect.lua
index 6cc28ac..a9df21f 100644
--- a/apisix/plugins/redirect.lua
+++ b/apisix/plugins/redirect.lua
@@ -30,8 +30,12 @@ local schema = {
properties = {
ret_code = {type = "integer", minimum = 200, default = 302},
uri = {type = "string", minLength = 2},
+ http_to_https = {type = "boolean"}, -- default is false
},
- required = {"uri"},
+ oneOf = {
+ {required = {"uri"}},
+ {required = {"http_to_https"}}
+ }
}
@@ -80,11 +84,13 @@ function _M.check_schema(conf)
return false, err
end
- local uri_segs, err = parse_uri(conf.uri)
- if not uri_segs then
- return false, err
+ if conf.uri then
+ local uri_segs, err = parse_uri(conf.uri)
+ if not uri_segs then
+ return false, err
+ end
+ core.log.info(core.json.delay_encode(uri_segs))
end
- core.log.info(core.json.delay_encode(uri_segs))
return true
end
@@ -120,15 +126,22 @@ end
function _M.rewrite(conf, ctx)
core.log.info("plugin rewrite phase, conf: ", core.json.delay_encode(conf))
- local new_uri, err = concat_new_uri(conf.uri, ctx)
- if not new_uri then
- core.log.error("failed to generate new uri by: ", conf.uri, " error: ",
- err)
- core.response.exit(500)
+ if conf.http_to_https and ctx.var.scheme == "http" then
+ conf.uri = "https://$host$request_uri"
+ conf.ret_code = 301
end
- core.response.set_header("Location", new_uri)
- core.response.exit(conf.ret_code)
+ if conf.uri and conf.ret_code then
+ local new_uri, err = concat_new_uri(conf.uri, ctx)
+ if not new_uri then
+ core.log.error("failed to generate new uri by: ", conf.uri, " error: ",
+ err)
+ core.response.exit(500)
+ end
+
+ core.response.set_header("Location", new_uri)
+ core.response.exit(conf.ret_code)
+ end
end
diff --git a/doc/plugins/redirect-cn.md b/doc/plugins/redirect-cn.md
index ba7d94c..b32b88c 100644
--- a/doc/plugins/redirect-cn.md
+++ b/doc/plugins/redirect-cn.md
@@ -27,8 +27,9 @@ URI 重定向插件。
|名称 |必须|描述|
|------- |-----|------|
-|uri |是| 可以包含 Nginx 变量的 URI,例如:`/test/index.html`, `$uri/index.html`。你可以通过类似于 `$ {xxx}` 的方式引用变量,以避免产生歧义,例如:`${uri}foo/index.html`。若你需要保留 `$` 字符,那么使用如下格式:`/\$foo/index.html`。|
-|ret_code|否|请求响应码,默认值为 `302`。|
+|uri |是,与 `http_to_https` 二选一| 可以包含 Nginx 变量的 URI,例如:`/test/index.html`, `$uri/index.html`。你可以通过类似于 `$ {xxx}` 的方式引用变量,以避免产生歧义,例如:`${uri}foo/index.html`。若你需要保留 `$` 字符,那么使用如下格式:`/\$foo/index.html`。|
+|ret_code|否,只和 `uri` 配置使用。|请求响应码,默认值为 `302`。|
+|http_to_https|是,与 `uri` 二选一|布尔值,默认是 `false`。当设置为 `ture` 并且请求是 http 时,会自动 301 重定向为 https,uri 保持不变|
### 示例
@@ -94,6 +95,21 @@ Location: /test/default.html
我们可以检查响应码和响应头中的 `Location` 参数,它表示该插件已启用。
+```
+
+下面是一个实现 http 到 https 跳转的示例:
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "uri": "/hello",
+ "plugins": {
+ "redirect": {
+ "http_to_https": true
+ }
+ }
+}'
+```
+
#### 禁用插件
移除插件配置中相应的 JSON 配置可立即禁用该插件,无需重启服务:
diff --git a/doc/plugins/redirect.md b/doc/plugins/redirect.md
index 187cdc9..6c7da6f 100644
--- a/doc/plugins/redirect.md
+++ b/doc/plugins/redirect.md
@@ -34,8 +34,9 @@ URI redirect.
|Name |Requirement|Description|
|------- |-----|------|
-|uri |required| New uri 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.|
-|ret_code|optional|Response code, the default value is `302`.|
+|uri |required, need pick one from `uri` and `http_to_https`| New uri 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.|
+|ret_code|optional, only works with `uri`|Response code, the default value is `302`.|
+|http_to_https|required, need pick one from `uri` and `http_to_https`|Boolean value. The default value is `false`. When it is set to `ture` and the request is HTTP, will be automatically redirected to HTTPS with 301 response code, and the URI will keep the same as client request.|
## How To Enable
@@ -101,6 +102,19 @@ We can check the response code and the response header `Location`.
It shows that the `redirect` plugin is in effect.
+ Here is an example of redirect HTTP to HTTPS:
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "uri": "/hello",
+ "plugins": {
+ "redirect": {
+ "http_to_https": true
+ }
+ }
+}'
+```
+
## Disable Plugin
When you want to disable the `redirect` plugin, it is very simple,
diff --git a/t/plugin/redirect.t b/t/plugin/redirect.t
index 2ff80ec..1415db2 100644
--- a/t/plugin/redirect.t
+++ b/t/plugin/redirect.t
@@ -14,18 +14,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-BEGIN {
- if ($ENV{TEST_NGINX_CHECK_LEAK}) {
- $SkipReason = "unavailable for the hup tests";
-
- } else {
- $ENV{TEST_NGINX_USE_HUP} = 1;
- undef $ENV{TEST_NGINX_USE_STAP};
- }
-}
-
use t::APISIX 'no_plan';
+$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
+
repeat_each(1);
no_long_string();
no_shuffle();
@@ -398,3 +390,288 @@ Host: foo.com
--- error_code: 301
--- response_headers
Location: https://foo.com/hello
+
+
+
+=== TEST 17: enable http_to_https
+--- 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,
+ [[{
+ "uri": "/hello",
+ "host": "foo.com",
+ "plugins": {
+ "redirect": {
+ "http_to_https": true
+ }
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 18: redirect
+--- request
+GET /hello
+--- more_headers
+Host: foo.com
+--- error_code: 301
+--- response_headers
+Location: https://foo.com/hello
+
+
+
+=== TEST 19: enable http_to_https with ret_code(not take effect)
+--- 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,
+ [[{
+ "uri": "/hello",
+ "host": "foo.com",
+ "plugins": {
+ "redirect": {
+ "http_to_https": true,
+ "ret_code": 302
+ }
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 20: redirect
+--- request
+GET /hello
+--- more_headers
+Host: foo.com
+--- error_code: 301
+--- response_headers
+Location: https://foo.com/hello
+
+
+
+=== TEST 21: wrong configure, enable http_to_https with uri
+--- 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,
+ [[{
+ "uri": "/hello",
+ "host": "foo.com",
+ "plugins": {
+ "redirect": {
+ "http_to_https": true,
+ "uri": "/hello"
+ }
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/error_msg":"failed to check the configuration of plugin redirect err: value should match only one schema, but matches both schemas 1 and 2/
+--- no_error_log
+[error]
+
+
+
+=== TEST 22: enable http_to_https with upstream
+--- 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,
+ [[{
+ "uri": "/hello",
+ "host": "test.com",
+ "plugins": {
+ "redirect": {
+ "http_to_https": true
+ }
+ },
+ "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 23: redirect
+--- request
+GET /hello
+--- more_headers
+Host: test.com
+--- error_code: 301
+--- response_headers
+Location: https://test.com/hello
+
+
+
+=== TEST 24: set ssl(sni: test.com)
+--- config
+location /t {
+ content_by_lua_block {
+ local core = require("apisix.core")
+ local t = require("lib.test_admin")
+
+ local ssl_cert = t.read_file("conf/cert/apisix.crt")
+ local ssl_key = t.read_file("conf/cert/apisix.key")
+ local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
+
+ local code, body = t.test('/apisix/admin/ssl/1',
+ ngx.HTTP_PUT,
+ core.json.encode(data),
+ [[{
+ "node": {
+ "value": {
+ "sni": "test.com"
+ },
+ "key": "/apisix/ssl/1"
+ },
+ "action": "set"
+ }]]
+ )
+
+ ngx.status = code
+ ngx.say(body)
+ }
+}
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 25: client https request
+--- config
+listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+location /t {
+ content_by_lua_block {
+ -- etcd sync
+ ngx.sleep(0.2)
+
+ do
+ local sock = ngx.socket.tcp()
+
+ sock:settimeout(2000)
+
+ local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock")
+ if not ok then
+ ngx.say("failed to connect: ", err)
+ return
+ end
+
+ ngx.say("connected: ", ok)
+
+ local sess, err = sock:sslhandshake(nil, "test.com", false)
+ if not sess then
+ ngx.say("failed to do SSL handshake: ", err)
+ return
+ end
+
+ ngx.say("ssl handshake: ", type(sess))
+
+ local req = "GET /hello HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n"
+ local bytes, err = sock:send(req)
+ if not bytes then
+ ngx.say("failed to send http request: ", err)
+ return
+ end
+
+ ngx.say("sent http request: ", bytes, " bytes.")
+
+ while true do
+ local line, err = sock:receive()
+ if not line then
+ -- ngx.say("failed to receive response status line: ", err)
+ break
+ end
+
+ ngx.say("received: ", line)
+ end
+
+ local ok, err = sock:close()
+ ngx.say("close: ", ok, " ", err)
+ end -- do
+ -- collectgarbage()
+ }
+}
+--- request
+GET /t
+--- response_body eval
+qr{connected: 1
+ssl handshake: userdata
+sent http request: 58 bytes.
+received: HTTP/1.1 200 OK
+received: Content-Type: text/plain
+received: Connection: close
+received: Server: \w+
+received: \nreceived: hello world
+close: 1 nil}
+--- no_error_log
+[error]
+[alert]