diff --git a/conf/config.yaml b/conf/config.yaml index 4cd13e839fec..707a5b8f489f 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -52,6 +52,7 @@ plugins: # plugin list - serverless-pre-function - serverless-post-function - openid-connect + - proxy-rewrite stream_plugins: - mqtt-proxy diff --git a/doc/plugins/proxy-rewrite-cn.md b/doc/plugins/proxy-rewrite-cn.md new file mode 100644 index 000000000000..372e015cc5ea --- /dev/null +++ b/doc/plugins/proxy-rewrite-cn.md @@ -0,0 +1,72 @@ +[English](proxy-rewrite.md) +# proxy-rewrite + +上游代理信息重写插件。 + +#### 配置参数 +|名字 |可选|说明| +|------- |-----|------| +|scheme |可选| 转发到上游的新`schema` 协议,可以是`http`或`https`,默认`http`协议| +|uri |可选| 转发到上游的新`uri` 地址| +|host |可选| 转发到上游的新`host` 地址,例如:`iresty.com` | +|enable_websocket|可选| 是否启用`websocket`(布尔值),默认不启用| + +### 示例 + +#### 开启插件 +下面是一个示例,在指定的 route 上开启了 `proxy rewrite` 插件: + +```shell +curl http://127.0.0.1:9080/apisix/admin/routes/1 -X PUT -d ' +{ + "methods": ["GET"], + "uri": "/test/index.html", + "plugins": { + "proxy-rewrite": { + "uri": "/test/home.html", + "scheme": "http", + "host": "iresty.com", + "enable_websocket": true + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:80": 1 + } + } +}' +``` + +#### 测试插件 +基于上述配置进行测试: +```shell +curl -X GET http://127.0.0.1:9080/test/index.html +``` + +发送请求,查看上游服务`access.log`,如果输出信息与配置一致: +``` +127.0.0.1 - [26/Sep/2019:10:52:20 +0800] iresty.com GET /test/home.html HTTP/1.1 200 38 - curl/7.29.0 - 0.000 199 107 +``` + +即表示 `proxy rewrite` 插件生效了。 + +#### 禁用插件 +当你想去掉 `proxy rewrite` 插件的时候,很简单,在插件的配置中把对应的 json 配置删除即可,无须重启服务,即刻生效: + +```shell +curl http://127.0.0.1:9080/apisix/admin/routes/1 -X PUT -d ' +{ + "methods": ["GET"], + "uri": "/test/index.html", + "plugins": {}, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:80": 1 + } + } +}' +``` + +现在就已经移除了 `proxy rewrite` 插件了。其他插件的开启和移除也是同样的方法。 diff --git a/doc/plugins/proxy-rewrite.md b/doc/plugins/proxy-rewrite.md new file mode 100644 index 000000000000..75258dbdca3e --- /dev/null +++ b/doc/plugins/proxy-rewrite.md @@ -0,0 +1,73 @@ +[中文](proxy-rewrite-cn.md) +# proxy-rewrite + +upstream proxy info rewrite plugin. + +### Parameters +|Name |Required|Description| +|------- |-----|------| +|scheme |No| Upstream new `schema` forwarding protocol,options can be `http` or `https`,default `http`.| +|uri |No| Upstream new `uri` forwarding address.| +|host |No| Upstream new `host` forwarding address, example `iresty.com`. | +|enable_websocket|No| enable `websocket`(boolean), default `false`.| + +### Example + +#### Enable Plugin +Here's an example, enable the `proxy rewrite` plugin on the specified route: + +```shell +curl http://127.0.0.1:9080/apisix/admin/routes/1 -X PUT -d ' +{ + "methods": ["GET"], + "uri": "/test/index.html", + "plugins": { + "proxy-rewrite": { + "uri": "/test/home.html", + "scheme": "http", + "host": "iresty.com", + "enable_websocket": true + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:80": 1 + } + } +}' +``` + +#### Test Plugin +Testing based on the above examples : +```shell +curl -X GET http://127.0.0.1:9080/test/index.html +``` + +Send the request and see upstream `access.log', if the output information is consistent with the configuration : +``` +127.0.0.1 - [26/Sep/2019:10:52:20 +0800] iresty.com GET /test/home.html HTTP/1.1 200 38 - curl/7.29.0 - 0.000 199 107 +``` + +This means that the `proxy rewrite` plugin is in effect. + +#### Disable Plugin +When you want to disable the `proxy rewrite` plugin, 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:9080/apisix/admin/routes/1 -X PUT -d ' +{ + "methods": ["GET"], + "uri": "/test/index.html", + "plugins": {}, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:80": 1 + } + } +}' +``` + +The `proxy rewrite` plugin has been disabled now. It works for other plugins. diff --git a/lua/apisix/plugins/proxy-rewrite.lua b/lua/apisix/plugins/proxy-rewrite.lua new file mode 100644 index 000000000000..079c0a2e5aee --- /dev/null +++ b/lua/apisix/plugins/proxy-rewrite.lua @@ -0,0 +1,77 @@ +local core = require("apisix.core") +local plugin_name = "proxy-rewrite" +local pairs = pairs +local ipairs = ipairs + +local schema = { + type = "object", + properties = { + uri = { + description = "new uri for upstream", + type = "string", + minLength = 1, + maxLength = 4096 + }, + host = { + description = "new host for upstream", + type = "string", + pattern = "^\\*?[0-9a-zA-Z-.]+$", + }, + scheme = { + description = "new scheme for upstream", + type = "string", + enum = {"http", "https"} + }, + enable_websocket = { + description = "enable websocket for request", + type = "boolean", + default = false + } + }, + minProperties = 1, +} + +local _M = { + version = 0.1, + priority = 1008, + name = plugin_name, + schema = schema, +} + +function _M.check_schema(conf) + local ok, err = core.schema.check(schema, conf) + if not ok then + return false, err + end + return true +end + +do + local upstream_vars = { + uri = "upstream_uri", + scheme = "upstream_scheme", + host = "upstream_host", + upgrade = "upstream_upgrade", + connection = "upstream_connection", + } + local upstream_names = {} + for name, _ in pairs(upstream_vars) do + core.table.insert(upstream_names, name) + end + +function _M.rewrite(conf, ctx) + for _, name in ipairs(upstream_names) do + if conf[name] then + ctx.var[upstream_vars[name]] = conf[name] + end + end + + if conf.enable_websocket then + ctx.var["upstream_upgrade"] = ctx.var["http_upgrade"] + ctx.var["upstream_connection"] = ctx.var["http_connection"] + end +end + +end -- do + +return _M diff --git a/t/APISIX.pm b/t/APISIX.pm index 4b638a031765..6de38338cb11 100644 --- a/t/APISIX.pm +++ b/t/APISIX.pm @@ -154,6 +154,7 @@ _EOC_ listen 1980; listen 1981; listen 1982; + server_name apisix.iresty.com; server_tokens off; diff --git a/t/admin/plugins.t b/t/admin/plugins.t index bf67941c8494..73ecf8be3335 100644 --- a/t/admin/plugins.t +++ b/t/admin/plugins.t @@ -14,6 +14,6 @@ __DATA__ --- request GET /apisix/admin/plugins/list --- response_body_like eval -qr/\["limit-req","limit-count","limit-conn","key-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction","grpc-transcode","serverless-pre-function","serverless-post-function","openid-connect"\]/ +qr/\["limit-req","limit-count","limit-conn","key-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction","grpc-transcode","serverless-pre-function","serverless-post-function","openid-connect","proxy-rewrite"\]/ --- no_error_log [error] diff --git a/t/debug-mode.t b/t/debug-mode.t index 56677b38ecf0..78ed6a7dcf4f 100644 --- a/t/debug-mode.t +++ b/t/debug-mode.t @@ -44,6 +44,7 @@ loaded plugin and sort by priority: 3000 name: ip-restriction loaded plugin and sort by priority: 2599 name: openid-connect loaded plugin and sort by priority: 2510 name: jwt-auth loaded plugin and sort by priority: 2500 name: key-auth +loaded plugin and sort by priority: 1008 name: proxy-rewrite loaded plugin and sort by priority: 1003 name: limit-conn loaded plugin and sort by priority: 1002 name: limit-count loaded plugin and sort by priority: 1001 name: limit-req diff --git a/t/lib/server.lua b/t/lib/server.lua index d93f3b45846a..72a56c67e104 100644 --- a/t/lib/server.lua +++ b/t/lib/server.lua @@ -1,4 +1,5 @@ local json_decode = require("cjson").decode +local json_encode = require("cjson").encode local _M = {} @@ -22,6 +23,11 @@ function _M.limit_conn() ngx.say("hello world") end +function _M.plugin_proxy_rewrite() + ngx.say("uri: ", ngx.var.uri) + ngx.say("host: ", ngx.var.host) + ngx.say("scheme: ", ngx.var.scheme) +end function _M.status() ngx.say("ok") diff --git a/t/plugin/proxy-rewrite.t b/t/plugin/proxy-rewrite.t new file mode 100644 index 000000000000..08ab8c4f19ee --- /dev/null +++ b/t/plugin/proxy-rewrite.t @@ -0,0 +1,237 @@ +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'; + +repeat_each(1); +no_long_string(); +no_shuffle(); +no_root_location(); +run_tests; + +__DATA__ + +=== TEST 1: sanity +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.proxy-rewrite") + local ok, err = plugin.check_schema({ + uri = '/apisix/home', + host = 'apisix.iresty.com', + enable_websocket = true, + scheme = 'http' + }) + if not ok then + ngx.say(err) + end + + ngx.say("done") + } + } +--- request +GET /t +--- response_body +done +--- no_error_log +[error] + + +=== TEST 2: wrong value of key +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.proxy-rewrite") + local ok, err = plugin.check_schema({ + uri = '/apisix/home', + host = 'apisix.iresty.com', + enable_websocket = true, + scheme = 'tcp' + }) + if not ok then + ngx.say(err) + end + + ngx.say("done") + } + } +--- request +GET /t +--- response_body +invalid "enum" in docuement at pointer "#/scheme" +done +--- no_error_log +[error] + + +=== TEST 3: add plugin +--- 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": { + "proxy-rewrite": { + "uri": "/test/add", + "scheme": "https", + "host": "apisix.iresty.com", + "enable_websocket": true + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + +=== TEST 4: update plugin +--- 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": { + "proxy-rewrite": { + "uri": "/test/update", + "scheme": "http", + "host": "apisix.iresty.com", + "enable_websocket": false + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + +=== TEST 5: disable plugin +--- 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": { + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + +=== TEST 6: set route(id: 1) +--- 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, + [[{ + "methods": ["GET"], + "plugins": { + "proxy-rewrite": { + "uri": "/plugin_proxy_rewrite", + "scheme": "http", + "host": "apisix.iresty.com", + "enable_websocket": true + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + +=== TEST 7: rewrite +--- request +GET /hello HTTP/1.1 +--- response_body +uri: /plugin_proxy_rewrite +host: apisix.iresty.com +scheme: http +--- no_error_log +[error]