From 5bcf2f5800b0ed9c4113e55280a399e0af9e4482 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Tue, 29 Mar 2022 18:18:35 +0800 Subject: [PATCH 01/22] feat(response-rewrite): support filters * match pattern can be regex or fixed string * match mode can be once or global, default once * ngx.re.sub(gsub) default options jo Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 76 ++- docs/en/latest/plugins/response-rewrite.md | 26 +- docs/zh/latest/plugins/response-rewrite.md | 26 +- t/plugin/response-rewrite.t | 509 +++++++++++++++++++++ 4 files changed, 621 insertions(+), 16 deletions(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 7706bc96ed3e..6e5413b9636b 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -16,9 +16,13 @@ -- local core = require("apisix.core") local expr = require("resty.expr.v1") +local re_compile = require("resty.core.regex").re_match_compile local plugin_name = "response-rewrite" local ngx = ngx +local re_sub = ngx.re.sub +local re_gsub = ngx.re.gsub local pairs = pairs +local ipairs = ipairs local type = type @@ -48,6 +52,39 @@ local schema = { vars = { type = "array", }, + filters = { + description = "a group of filters that modify response body\ +by replacing one specified string by another", + type = "array", + items = { + description = "filter that modifies response body", + type = "object", + properties = { + regex = { + description = "match pattern on response body", + type = "string", + default = "", + }, + scope = { + description = "regex substitution range", + type = "string", + enum = {"once", "global"}, + default = "once", + }, + replace = { + description = "regex substitution content", + type = "string", + default = "", + }, + options = { + description = "regex options", + type = "string", + default = "jo", + } + }, + }, + }, + oneOf = {"body", "filters"}, }, minProperties = 1, } @@ -115,6 +152,23 @@ function _M.check_schema(conf) end end + if conf.filters then + for _, filter in ipairs(conf.filters) do + for field, value in pairs(filter) do + if type(field) ~= 'string' then + return false, 'invalid type as filter field' + end + if field ~= "replace" and value == "" then + return false, 'invalid value as filter field ' .. field + end + end + local ok, err = re_compile(filter.regex, filter.options) + if not ok then + return false, err + end + end + end + return true end @@ -126,7 +180,24 @@ function _M.body_filter(conf, ctx) return end - if conf.body then + if conf.filters then + + local body = core.response.hold_body_chunk(ctx) + if not body then + return + end + + for _, filter in ipairs(conf.filters) do + if filter.scope == "once" then + body = re_sub(body, filter.regex, filter.replace, filter.options) + else + body = re_gsub(body, filter.regex, filter.replace, filter.options) + end + end + + ngx.arg[1] = body + + elseif conf.body then if conf.body_base64 then ngx.arg[1] = ngx.decode_base64(conf.body) @@ -148,7 +219,8 @@ function _M.header_filter(conf, ctx) ngx.status = conf.status_code end - if conf.body then + -- fixme: if filters have no any match, response body won't be modified. + if conf.filters or conf.body then core.response.clear_header_as_body_modified() end diff --git a/docs/en/latest/plugins/response-rewrite.md b/docs/en/latest/plugins/response-rewrite.md index 6920e2643886..176cd2813384 100644 --- a/docs/en/latest/plugins/response-rewrite.md +++ b/docs/en/latest/plugins/response-rewrite.md @@ -32,13 +32,25 @@ response rewrite plugin, rewrite the content returned by the upstream as well as ## Attributes -| Name | Type | Requirement | Default | Valid | Description | -| ----------- | ------- | ----------- | ------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| status_code | integer | optional | | [200, 598] | New `status code` to client, keep the original response code by default. | -| body | string | optional | | | New `body` to client, and the content-length will be reset too. | -| body_base64 | boolean | optional | false | | Identify if `body` in configuration need base64 decoded before rewrite to client. | -| headers | object | optional | | | Set the new `headers` for client, can set up multiple. If it exists already from upstream, will rewrite the header, otherwise will add the header. You can set the corresponding value to an empty string to remove a header. The value can contain Nginx variables in `$var` format, like `$remote_addr $balancer_ip` | -| vars | array[] | optional | | | A DSL to evaluate with the given ngx.var. See `vars` [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list). if the `vars` is empty, then all rewrite operations will be executed unconditionally | +| Name | Type | Requirement | Default | Valid | Description | +|-------------|---------|-------------|---------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| status_code | integer | optional | | [200, 598] | New `status code` to client, keep the original response code by default. | +| body | string | optional | | | New `body` to client, and the content-length will be reset too. | +| body_base64 | boolean | optional | false | | Identify if `body` in configuration need base64 decoded before rewrite to client. | +| headers | object | optional | | | Set the new `headers` for client, can set up multiple. If it exists already from upstream, will rewrite the header, otherwise will add the header. You can set the corresponding value to an empty string to remove a header. The value can contain Nginx variables in `$var` format, like `$remote_addr $balancer_ip`. | +| vars | array[] | optional | | | A DSL to evaluate with the given ngx.var. See `vars` [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list). if the `vars` is empty, then all rewrite operations will be executed unconditionally. | +| filters | array[] | optional | | | A group of filters that modify response body by replacing one specified string by another. | + +Only one of `body`, `filters` can be specified. + +### Attributes of filters + +| Name | Type | Requirement | Default | Valid | Description | +|---------|--------|-------------|---------|----------------|------------------------------------------------------------------------------------------------| +| regex | string | required | | | match pattern on response body. | +| scope | string | required | "one" | "one","global" | substitution range. | +| replace | string | required | | | substitution content. | +| options | string | required | "jo" | | regex options, See `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`. | ## How To Enable diff --git a/docs/zh/latest/plugins/response-rewrite.md b/docs/zh/latest/plugins/response-rewrite.md index 7cee59665c8c..efdb29db0767 100644 --- a/docs/zh/latest/plugins/response-rewrite.md +++ b/docs/zh/latest/plugins/response-rewrite.md @@ -33,13 +33,25 @@ title: response-rewrite ## 属性 -| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | -| ----------- | ------- | ------ | ------ | ---------- | -------------------------------------------------------------------------------------------------------------------------------------- | -| status_code | integer | 可选 | | [200, 598] | 修改上游返回状态码,默认保留原始响应代码。 | -| body | string | 可选 | | | 修改上游返回的 `body` 内容,如果设置了新内容,header 里面的 content-length 字段也会被去掉 | -| body_base64 | boolean | 可选 | false | | 描述 `body` 字段是否需要 base64 解码之后再返回给客户端,用在某些图片和 Protobuffer 场景 | -| headers | object | 可选 | | | 返回给客户端的 `headers`,这里可以设置多个。头信息如果存在将重写,不存在则添加。想要删除某个 header 的话,把对应的值设置为空字符串即可。这个值能够以 `$var` 的格式包含 Nginx 变量,比如 `$remote_addr $balancer_ip` | -| vars | array[] | 可选 | | | `vars` 是一个表达式列表,只有满足条件的请求和响应才会修改 body 和 header 信息,来自 [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list)。如果 `vars` 字段为空,那么所有的重写动作都会被无条件的执行。 | +| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | +|-------------|---------|-----|-------|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| status_code | integer | 可选 | | [200, 598] | 修改上游返回状态码,默认保留原始响应代码。 | +| body | string | 可选 | | | 修改上游返回的 `body` 内容,如果设置了新内容,header 里面的 content-length 字段也会被去掉。 | +| body_base64 | boolean | 可选 | false | | 描述 `body` 字段是否需要 base64 解码之后再返回给客户端,用在某些图片和 Protobuffer 场景。 | +| headers | object | 可选 | | | 返回给客户端的 `headers`,这里可以设置多个。头信息如果存在将重写,不存在则添加。想要删除某个 header 的话,把对应的值设置为空字符串即可。这个值能够以 `$var` 的格式包含 Nginx 变量,比如 `$remote_addr $balancer_ip`。 | +| vars | array[] | 可选 | | | `vars` 是一个表达式列表,只有满足条件的请求和响应才会修改 body 和 header 信息,来自 [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list)。如果 `vars` 字段为空,那么所有的重写动作都会被无条件的执行。 | +| filters | array[] | 可选 | | | 一组过滤器,采用指定字符串表达式修改响应体。 | + +`body` 和 `filters`,两个只能配置其中一个。 + +### Attributes of filters + +| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | +|---------|--------|-----|-------|----------------|--------------------------------------------------------------------------------------------| +| regex | string | 必填 | | | 用于匹配响应体正则表达式。 | +| scope | string | 必填 | "one" | "one","global" | 替换范围。 | +| replace | string | 必填 | | | 替换后的内容。 | +| options | string | 必填 | "jo" | | 正则匹配有效参数,可选项见 `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`. | ## 示例 diff --git a/t/plugin/response-rewrite.t b/t/plugin/response-rewrite.t index ebde0d0580a6..cd9044df9b41 100644 --- a/t/plugin/response-rewrite.t +++ b/t/plugin/response-rewrite.t @@ -699,3 +699,512 @@ X-A: 127.0.0.1 X-B: from 127.0.0.1 to 127.0.0.1:1980 --- no_error_log [error] + + + +=== TEST 25: add plugin with valid filters +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.response-rewrite") + local ok, err = plugin.check_schema({ + filters = { + { + regex = "Hello", + scope = "global", + replace = "World", + options = "jo" + } + } + }) + if not ok then + ngx.say(err) + end + + ngx.say("done") + } + } +--- request +GET /t +--- response_body +done +--- no_error_log +[error] + + + +=== TEST 26: add plugin with invalid filter scope +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.response-rewrite") + local ok, err = plugin.check_schema({ + filters = { + { + regex = "Hello", + scope = "two", + replace = "World", + options = "jo" + } + } + }) + if not ok then + ngx.say(err) + else + ngx.say("done") + end + } + } +--- request +GET /t +--- response_body +property "filters" validation failed: failed to validate item 1: property "scope" validation failed: matches none of the enum values +--- no_error_log +[error] + + + +=== TEST 27: add plugin with invalid filter field type +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.response-rewrite") + local ok, err = plugin.check_schema({ + filters = { + { + 1 = "Hello" + } + } + }) + if not ok then + ngx.say(err) + else + ngx.say("done") + end + } + } +--- request +GET /t +--- error_code eval +500 + + + +=== TEST 28: add plugin with invalid filter empty value +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.response-rewrite") + local ok, err = plugin.check_schema({ + filters = { + { + regex = "", + replace = "world" + } + } + }) + if not ok then + ngx.say(err) + else + ngx.say("done") + end + } + } +--- request +GET /t +--- response_body +invalid value as filter field regex +--- no_error_log +[error] + + + +=== TEST 29: add plugin with invalid filter regex options +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.response-rewrite") + local ok, err = plugin.check_schema({ + filters = { + { + regex = "hello", + options = "h" + } + } + }) + if not ok then + ngx.say(err) + else + ngx.say("done") + end + } + } +--- request +GET /t +--- error_code eval +500 +--- error_log +unknown flag "h" + + + +=== TEST 30: set route with filters and vars expr +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "hello", + "replace": "test" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 31: check http body that matches filters +--- request +GET /hello +--- response_body +test world + + + +=== TEST 32: filter substitute global +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "l", + "replace": "t", + "scope": "global" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 33: check http body that substitute global +--- request +GET /hello +--- response_body +hetto wortd + + + +=== TEST 34: filter replace with empty +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "hello", + "replace": "" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 35: check http body that replace with empty +--- request +GET /hello +--- response_body + world + + + +=== TEST 36: filter replace with words +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "\\w\\S+$", + "replace": "*" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 37: check http body that replace with words +--- request +GET /hello +--- response_body +hello * + + + +=== TEST 38: set body and filters(body no 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, + [[{ + "plugins": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "body": "new body", + "filters": [ + { + "regex": "hello", + "replace": "HELLO" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 39: check http body that set body and filters +--- request +GET /hello +--- response_body +HELLO world + + + +=== TEST 40: set multiple filters +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "hello", + "replace": "HELLO" + }, + { + "regex": "L", + "replace": "T" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 41: check http body that set multiple filters +--- request +GET /hello +--- response_body +HETLO world + + + +=== TEST 42: filters no any match +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "test", + "replace": "TEST" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 43: check http body that filters no any match +--- request +GET /hello +--- response_body +hello world From 89b67f47eeb542fd8872527950e1a726b651c24d Mon Sep 17 00:00:00 2001 From: kwanhur Date: Fri, 1 Apr 2022 13:59:40 +0800 Subject: [PATCH 02/22] test(plugin): split response-rewrite test cases into two parts Signed-off-by: kwanhur --- t/plugin/response-rewrite.t | 508 -------------------------------- t/plugin/response-rewrite2.t | 542 +++++++++++++++++++++++++++++++++++ 2 files changed, 542 insertions(+), 508 deletions(-) create mode 100644 t/plugin/response-rewrite2.t diff --git a/t/plugin/response-rewrite.t b/t/plugin/response-rewrite.t index cd9044df9b41..45fabf78339e 100644 --- a/t/plugin/response-rewrite.t +++ b/t/plugin/response-rewrite.t @@ -700,511 +700,3 @@ X-B: from 127.0.0.1 to 127.0.0.1:1980 --- no_error_log [error] - - -=== TEST 25: add plugin with valid filters ---- config - location /t { - content_by_lua_block { - local plugin = require("apisix.plugins.response-rewrite") - local ok, err = plugin.check_schema({ - filters = { - { - regex = "Hello", - scope = "global", - replace = "World", - options = "jo" - } - } - }) - if not ok then - ngx.say(err) - end - - ngx.say("done") - } - } ---- request -GET /t ---- response_body -done ---- no_error_log -[error] - - - -=== TEST 26: add plugin with invalid filter scope ---- config - location /t { - content_by_lua_block { - local plugin = require("apisix.plugins.response-rewrite") - local ok, err = plugin.check_schema({ - filters = { - { - regex = "Hello", - scope = "two", - replace = "World", - options = "jo" - } - } - }) - if not ok then - ngx.say(err) - else - ngx.say("done") - end - } - } ---- request -GET /t ---- response_body -property "filters" validation failed: failed to validate item 1: property "scope" validation failed: matches none of the enum values ---- no_error_log -[error] - - - -=== TEST 27: add plugin with invalid filter field type ---- config - location /t { - content_by_lua_block { - local plugin = require("apisix.plugins.response-rewrite") - local ok, err = plugin.check_schema({ - filters = { - { - 1 = "Hello" - } - } - }) - if not ok then - ngx.say(err) - else - ngx.say("done") - end - } - } ---- request -GET /t ---- error_code eval -500 - - - -=== TEST 28: add plugin with invalid filter empty value ---- config - location /t { - content_by_lua_block { - local plugin = require("apisix.plugins.response-rewrite") - local ok, err = plugin.check_schema({ - filters = { - { - regex = "", - replace = "world" - } - } - }) - if not ok then - ngx.say(err) - else - ngx.say("done") - end - } - } ---- request -GET /t ---- response_body -invalid value as filter field regex ---- no_error_log -[error] - - - -=== TEST 29: add plugin with invalid filter regex options ---- config - location /t { - content_by_lua_block { - local plugin = require("apisix.plugins.response-rewrite") - local ok, err = plugin.check_schema({ - filters = { - { - regex = "hello", - options = "h" - } - } - }) - if not ok then - ngx.say(err) - else - ngx.say("done") - end - } - } ---- request -GET /t ---- error_code eval -500 ---- error_log -unknown flag "h" - - - -=== TEST 30: set route with filters and vars expr ---- 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": { - "response-rewrite": { - "vars": [ - ["status","==",200] - ], - "filters": [ - { - "regex": "hello", - "replace": "test" - } - ] - } - }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - }, - "uris": ["/hello"] - }]] - ) - - ngx.say(body) - } - } ---- request -GET /t ---- response_body -passed ---- no_error_log -[error] - - - -=== TEST 31: check http body that matches filters ---- request -GET /hello ---- response_body -test world - - - -=== TEST 32: filter substitute global ---- 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": { - "response-rewrite": { - "vars": [ - ["status","==",200] - ], - "filters": [ - { - "regex": "l", - "replace": "t", - "scope": "global" - } - ] - } - }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - }, - "uris": ["/hello"] - }]] - ) - - ngx.say(body) - } - } ---- request -GET /t ---- response_body -passed ---- no_error_log -[error] - - - -=== TEST 33: check http body that substitute global ---- request -GET /hello ---- response_body -hetto wortd - - - -=== TEST 34: filter replace with empty ---- 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": { - "response-rewrite": { - "vars": [ - ["status","==",200] - ], - "filters": [ - { - "regex": "hello", - "replace": "" - } - ] - } - }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - }, - "uris": ["/hello"] - }]] - ) - - ngx.say(body) - } - } ---- request -GET /t ---- response_body -passed ---- no_error_log -[error] - - - -=== TEST 35: check http body that replace with empty ---- request -GET /hello ---- response_body - world - - - -=== TEST 36: filter replace with words ---- 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": { - "response-rewrite": { - "vars": [ - ["status","==",200] - ], - "filters": [ - { - "regex": "\\w\\S+$", - "replace": "*" - } - ] - } - }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - }, - "uris": ["/hello"] - }]] - ) - - ngx.say(body) - } - } ---- request -GET /t ---- response_body -passed ---- no_error_log -[error] - - - -=== TEST 37: check http body that replace with words ---- request -GET /hello ---- response_body -hello * - - - -=== TEST 38: set body and filters(body no 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, - [[{ - "plugins": { - "response-rewrite": { - "vars": [ - ["status","==",200] - ], - "body": "new body", - "filters": [ - { - "regex": "hello", - "replace": "HELLO" - } - ] - } - }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - }, - "uris": ["/hello"] - }]] - ) - - ngx.say(body) - } - } ---- request -GET /t ---- response_body -passed ---- no_error_log -[error] - - - -=== TEST 39: check http body that set body and filters ---- request -GET /hello ---- response_body -HELLO world - - - -=== TEST 40: set multiple filters ---- 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": { - "response-rewrite": { - "vars": [ - ["status","==",200] - ], - "filters": [ - { - "regex": "hello", - "replace": "HELLO" - }, - { - "regex": "L", - "replace": "T" - } - ] - } - }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - }, - "uris": ["/hello"] - }]] - ) - - ngx.say(body) - } - } ---- request -GET /t ---- response_body -passed ---- no_error_log -[error] - - - -=== TEST 41: check http body that set multiple filters ---- request -GET /hello ---- response_body -HETLO world - - - -=== TEST 42: filters no any match ---- 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": { - "response-rewrite": { - "vars": [ - ["status","==",200] - ], - "filters": [ - { - "regex": "test", - "replace": "TEST" - } - ] - } - }, - "upstream": { - "nodes": { - "127.0.0.1:1980": 1 - }, - "type": "roundrobin" - }, - "uris": ["/hello"] - }]] - ) - - ngx.say(body) - } - } ---- request -GET /t ---- response_body -passed ---- no_error_log -[error] - - - -=== TEST 43: check http body that filters no any match ---- request -GET /hello ---- response_body -hello world diff --git a/t/plugin/response-rewrite2.t b/t/plugin/response-rewrite2.t new file mode 100644 index 000000000000..b10a963422c7 --- /dev/null +++ b/t/plugin/response-rewrite2.t @@ -0,0 +1,542 @@ +# +# 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. +# +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 25: add plugin with valid filters +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.response-rewrite") + local ok, err = plugin.check_schema({ + filters = { + { + regex = "Hello", + scope = "global", + replace = "World", + options = "jo" + } + } + }) + if not ok then + ngx.say(err) + end + + ngx.say("done") + } + } +--- request +GET /t +--- response_body +done +--- no_error_log +[error] + + + +=== TEST 26: add plugin with invalid filter scope +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.response-rewrite") + local ok, err = plugin.check_schema({ + filters = { + { + regex = "Hello", + scope = "two", + replace = "World", + options = "jo" + } + } + }) + if not ok then + ngx.say(err) + else + ngx.say("done") + end + } + } +--- request +GET /t +--- response_body +property "filters" validation failed: failed to validate item 1: property "scope" validation failed: matches none of the enum values +--- no_error_log +[error] + + + +=== TEST 27: add plugin with invalid filter field type +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.response-rewrite") + local ok, err = plugin.check_schema({ + filters = { + { + 1 = "Hello" + } + } + }) + if not ok then + ngx.say(err) + else + ngx.say("done") + end + } + } +--- request +GET /t +--- error_code eval +500 + + + +=== TEST 28: add plugin with invalid filter empty value +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.response-rewrite") + local ok, err = plugin.check_schema({ + filters = { + { + regex = "", + replace = "world" + } + } + }) + if not ok then + ngx.say(err) + else + ngx.say("done") + end + } + } +--- request +GET /t +--- response_body +invalid value as filter field regex +--- no_error_log +[error] + + + +=== TEST 29: add plugin with invalid filter regex options +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.response-rewrite") + local ok, err = plugin.check_schema({ + filters = { + { + regex = "hello", + options = "h" + } + } + }) + if not ok then + ngx.say(err) + else + ngx.say("done") + end + } + } +--- request +GET /t +--- error_code eval +500 +--- error_log +unknown flag "h" + + + +=== TEST 30: set route with filters and vars expr +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "hello", + "replace": "test" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 31: check http body that matches filters +--- request +GET /hello +--- response_body +test world + + + +=== TEST 32: filter substitute global +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "l", + "replace": "t", + "scope": "global" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 33: check http body that substitute global +--- request +GET /hello +--- response_body +hetto wortd + + + +=== TEST 34: filter replace with empty +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "hello", + "replace": "" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 35: check http body that replace with empty +--- request +GET /hello +--- response_body + world + + + +=== TEST 36: filter replace with words +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "\\w\\S+$", + "replace": "*" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 37: check http body that replace with words +--- request +GET /hello +--- response_body +hello * + + + +=== TEST 38: set body and filters(body no 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, + [[{ + "plugins": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "body": "new body", + "filters": [ + { + "regex": "hello", + "replace": "HELLO" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 39: check http body that set body and filters +--- request +GET /hello +--- response_body +HELLO world + + + +=== TEST 40: set multiple filters +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "hello", + "replace": "HELLO" + }, + { + "regex": "L", + "replace": "T" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 41: check http body that set multiple filters +--- request +GET /hello +--- response_body +HETLO world + + + +=== TEST 42: filters no any match +--- 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": { + "response-rewrite": { + "vars": [ + ["status","==",200] + ], + "filters": [ + { + "regex": "test", + "replace": "TEST" + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 43: check http body that filters no any match +--- request +GET /hello +--- response_body +hello world From 0f70eeb2b34f5e85527faa2290a1cc3e632e6f6d Mon Sep 17 00:00:00 2001 From: kwanhur Date: Fri, 1 Apr 2022 14:03:23 +0800 Subject: [PATCH 03/22] feat(response-rewrite): remove filters field type checking Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 3 --- t/plugin/response-rewrite2.t | 26 -------------------------- 2 files changed, 29 deletions(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 6e5413b9636b..460003f5ccb3 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -155,9 +155,6 @@ function _M.check_schema(conf) if conf.filters then for _, filter in ipairs(conf.filters) do for field, value in pairs(filter) do - if type(field) ~= 'string' then - return false, 'invalid type as filter field' - end if field ~= "replace" and value == "" then return false, 'invalid value as filter field ' .. field end diff --git a/t/plugin/response-rewrite2.t b/t/plugin/response-rewrite2.t index b10a963422c7..40a11ab809a4 100644 --- a/t/plugin/response-rewrite2.t +++ b/t/plugin/response-rewrite2.t @@ -96,32 +96,6 @@ property "filters" validation failed: failed to validate item 1: property "scope -=== TEST 27: add plugin with invalid filter field type ---- config - location /t { - content_by_lua_block { - local plugin = require("apisix.plugins.response-rewrite") - local ok, err = plugin.check_schema({ - filters = { - { - 1 = "Hello" - } - } - }) - if not ok then - ngx.say(err) - else - ngx.say("done") - end - } - } ---- request -GET /t ---- error_code eval -500 - - - === TEST 28: add plugin with invalid filter empty value --- config location /t { From e505e6d962c5e858447358139051e23fc29347cc Mon Sep 17 00:00:00 2001 From: kwanhur Date: Fri, 1 Apr 2022 14:06:50 +0800 Subject: [PATCH 04/22] feat(response-rewrite): return at the end of conf.filters Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 460003f5ccb3..917bd1d22680 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -193,6 +193,7 @@ function _M.body_filter(conf, ctx) end ngx.arg[1] = body + return elseif conf.body then From 9177cd40f3db817f115f2c907e9d6d9dd896ae43 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Fri, 1 Apr 2022 14:10:51 +0800 Subject: [PATCH 05/22] chore(annotation): remove improper fixme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 罗泽轩 --- apisix/plugins/response-rewrite.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 6e5413b9636b..5e78c8e7bc7f 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -219,7 +219,7 @@ function _M.header_filter(conf, ctx) ngx.status = conf.status_code end - -- fixme: if filters have no any match, response body won't be modified. + -- if filters have no any match, response body won't be modified. if conf.filters or conf.body then core.response.clear_header_as_body_modified() end From 562adb1ced28eadd98d90a3bb7a8bf2bd09999c6 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Fri, 1 Apr 2022 14:31:26 +0800 Subject: [PATCH 06/22] feat(response-rewrite): remove conf.filters field type and value checking Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 917bd1d22680..f74a6b657e2a 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -154,11 +154,6 @@ function _M.check_schema(conf) if conf.filters then for _, filter in ipairs(conf.filters) do - for field, value in pairs(filter) do - if field ~= "replace" and value == "" then - return false, 'invalid value as filter field ' .. field - end - end local ok, err = re_compile(filter.regex, filter.options) if not ok then return false, err From d3e82b705806b5f9d2e16e147948ba3ec23f4852 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Fri, 1 Apr 2022 15:09:38 +0800 Subject: [PATCH 07/22] feat(response-rewrite): check filter.regex invalid when empty Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index f74a6b657e2a..02a91f2055fd 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -154,6 +154,9 @@ function _M.check_schema(conf) if conf.filters then for _, filter in ipairs(conf.filters) do + if filter.regex == "" then + return false, "invalid value as filter field regex" + end local ok, err = re_compile(filter.regex, filter.options) if not ok then return false, err From 5d2978a05451b104f02a7bd5cc12ce44357f5376 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Fri, 1 Apr 2022 15:18:56 +0800 Subject: [PATCH 08/22] feat(response-rewrite): add prefix message at filter.regex and options valid err Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 02a91f2055fd..0a32b6ae3391 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -159,7 +159,8 @@ function _M.check_schema(conf) end local ok, err = re_compile(filter.regex, filter.options) if not ok then - return false, err + return false, "failed to validate regex '" .. filter.regex .. +"' and options '" .. filter.options .. "':" .. err end end end From 54a472e58113f3c0b82f7be5019bc99c60090ba7 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Fri, 1 Apr 2022 15:19:39 +0800 Subject: [PATCH 09/22] test(plugin): reindex response-rewrite test cases Signed-off-by: kwanhur --- t/plugin/response-rewrite.t | 1 - t/plugin/response-rewrite2.t | 36 ++++++++++++++++++------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/t/plugin/response-rewrite.t b/t/plugin/response-rewrite.t index 45fabf78339e..ebde0d0580a6 100644 --- a/t/plugin/response-rewrite.t +++ b/t/plugin/response-rewrite.t @@ -699,4 +699,3 @@ X-A: 127.0.0.1 X-B: from 127.0.0.1 to 127.0.0.1:1980 --- no_error_log [error] - diff --git a/t/plugin/response-rewrite2.t b/t/plugin/response-rewrite2.t index 40a11ab809a4..47223030df32 100644 --- a/t/plugin/response-rewrite2.t +++ b/t/plugin/response-rewrite2.t @@ -34,7 +34,7 @@ run_tests; __DATA__ -=== TEST 25: add plugin with valid filters +=== TEST 1: add plugin with valid filters --- config location /t { content_by_lua_block { @@ -65,7 +65,7 @@ done -=== TEST 26: add plugin with invalid filter scope +=== TEST 2: add plugin with invalid filter scope --- config location /t { content_by_lua_block { @@ -96,7 +96,7 @@ property "filters" validation failed: failed to validate item 1: property "scope -=== TEST 28: add plugin with invalid filter empty value +=== TEST 3: add plugin with invalid filter empty value --- config location /t { content_by_lua_block { @@ -125,7 +125,7 @@ invalid value as filter field regex -=== TEST 29: add plugin with invalid filter regex options +=== TEST 4: add plugin with invalid filter regex options --- config location /t { content_by_lua_block { @@ -154,7 +154,7 @@ unknown flag "h" -=== TEST 30: set route with filters and vars expr +=== TEST 5: set route with filters and vars expr --- config location /t { content_by_lua_block { @@ -197,7 +197,7 @@ passed -=== TEST 31: check http body that matches filters +=== TEST 6: check http body that matches filters --- request GET /hello --- response_body @@ -205,7 +205,7 @@ test world -=== TEST 32: filter substitute global +=== TEST 7: filter substitute global --- config location /t { content_by_lua_block { @@ -249,7 +249,7 @@ passed -=== TEST 33: check http body that substitute global +=== TEST 8: check http body that substitute global --- request GET /hello --- response_body @@ -257,7 +257,7 @@ hetto wortd -=== TEST 34: filter replace with empty +=== TEST 9: filter replace with empty --- config location /t { content_by_lua_block { @@ -300,7 +300,7 @@ passed -=== TEST 35: check http body that replace with empty +=== TEST 10: check http body that replace with empty --- request GET /hello --- response_body @@ -308,7 +308,7 @@ GET /hello -=== TEST 36: filter replace with words +=== TEST 11: filter replace with words --- config location /t { content_by_lua_block { @@ -351,7 +351,7 @@ passed -=== TEST 37: check http body that replace with words +=== TEST 12: check http body that replace with words --- request GET /hello --- response_body @@ -359,7 +359,7 @@ hello * -=== TEST 38: set body and filters(body no effect) +=== TEST 13: set body and filters(body no effect) --- config location /t { content_by_lua_block { @@ -403,7 +403,7 @@ passed -=== TEST 39: check http body that set body and filters +=== TEST 14: check http body that set body and filters --- request GET /hello --- response_body @@ -411,7 +411,7 @@ HELLO world -=== TEST 40: set multiple filters +=== TEST 15: set multiple filters --- config location /t { content_by_lua_block { @@ -458,7 +458,7 @@ passed -=== TEST 41: check http body that set multiple filters +=== TEST 16: check http body that set multiple filters --- request GET /hello --- response_body @@ -466,7 +466,7 @@ HETLO world -=== TEST 42: filters no any match +=== TEST 17: filters no any match --- config location /t { content_by_lua_block { @@ -509,7 +509,7 @@ passed -=== TEST 43: check http body that filters no any match +=== TEST 18: check http body that filters no any match --- request GET /hello --- response_body From cce127dea95f81d9621f3f07b706faa2c40f77d0 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Fri, 1 Apr 2022 15:39:45 +0800 Subject: [PATCH 10/22] feat(response-rewrite): add minItems 1 limitation on conf.filters Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 0a32b6ae3391..054b77165773 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -56,6 +56,7 @@ local schema = { description = "a group of filters that modify response body\ by replacing one specified string by another", type = "array", + minItems = 1, items = { description = "filter that modifies response body", type = "object", From 656124d4a693f61a0c433d17e98d85b25835a700 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Fri, 1 Apr 2022 15:57:22 +0800 Subject: [PATCH 11/22] docs(response-rewrite): merge filters nested fields Signed-off-by: kwanhur --- docs/en/latest/plugins/response-rewrite.md | 29 +++++++++------------- docs/zh/latest/plugins/response-rewrite.md | 29 +++++++++------------- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/docs/en/latest/plugins/response-rewrite.md b/docs/en/latest/plugins/response-rewrite.md index 176cd2813384..0bd131553eed 100644 --- a/docs/en/latest/plugins/response-rewrite.md +++ b/docs/en/latest/plugins/response-rewrite.md @@ -32,26 +32,21 @@ response rewrite plugin, rewrite the content returned by the upstream as well as ## Attributes -| Name | Type | Requirement | Default | Valid | Description | -|-------------|---------|-------------|---------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| status_code | integer | optional | | [200, 598] | New `status code` to client, keep the original response code by default. | -| body | string | optional | | | New `body` to client, and the content-length will be reset too. | -| body_base64 | boolean | optional | false | | Identify if `body` in configuration need base64 decoded before rewrite to client. | -| headers | object | optional | | | Set the new `headers` for client, can set up multiple. If it exists already from upstream, will rewrite the header, otherwise will add the header. You can set the corresponding value to an empty string to remove a header. The value can contain Nginx variables in `$var` format, like `$remote_addr $balancer_ip`. | -| vars | array[] | optional | | | A DSL to evaluate with the given ngx.var. See `vars` [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list). if the `vars` is empty, then all rewrite operations will be executed unconditionally. | -| filters | array[] | optional | | | A group of filters that modify response body by replacing one specified string by another. | +| Name | Type | Requirement | Default | Valid | Description | +|-----------------|---------|-------------|---------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| status_code | integer | optional | | [200, 598] | New `status code` to client, keep the original response code by default. | +| body | string | optional | | | New `body` to client, and the content-length will be reset too. | +| body_base64 | boolean | optional | false | | Identify if `body` in configuration need base64 decoded before rewrite to client. | +| headers | object | optional | | | Set the new `headers` for client, can set up multiple. If it exists already from upstream, will rewrite the header, otherwise will add the header. You can set the corresponding value to an empty string to remove a header. The value can contain Nginx variables in `$var` format, like `$remote_addr $balancer_ip`. | +| vars | array[] | optional | | | A DSL to evaluate with the given ngx.var. See `vars` [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list). if the `vars` is empty, then all rewrite operations will be executed unconditionally. | +| filters | array[] | optional | | | A group of filters that modify response body by replacing one specified string by another. | +| filters.regex | string | optional | | | match pattern on response body. | +| filters.scope | string | optional | "one" | "one","global" | substitution range. | +| filters.replace | string | optional | | | substitution content. | +| filters.options | string | optional | "jo" | | regex options, See `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`. | Only one of `body`, `filters` can be specified. -### Attributes of filters - -| Name | Type | Requirement | Default | Valid | Description | -|---------|--------|-------------|---------|----------------|------------------------------------------------------------------------------------------------| -| regex | string | required | | | match pattern on response body. | -| scope | string | required | "one" | "one","global" | substitution range. | -| replace | string | required | | | substitution content. | -| options | string | required | "jo" | | regex options, See `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`. | - ## How To Enable Here's an example, enable the `response-rewrite` plugin on the specified route: diff --git a/docs/zh/latest/plugins/response-rewrite.md b/docs/zh/latest/plugins/response-rewrite.md index efdb29db0767..83a9d5162e86 100644 --- a/docs/zh/latest/plugins/response-rewrite.md +++ b/docs/zh/latest/plugins/response-rewrite.md @@ -33,26 +33,21 @@ title: response-rewrite ## 属性 -| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | -|-------------|---------|-----|-------|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| status_code | integer | 可选 | | [200, 598] | 修改上游返回状态码,默认保留原始响应代码。 | -| body | string | 可选 | | | 修改上游返回的 `body` 内容,如果设置了新内容,header 里面的 content-length 字段也会被去掉。 | -| body_base64 | boolean | 可选 | false | | 描述 `body` 字段是否需要 base64 解码之后再返回给客户端,用在某些图片和 Protobuffer 场景。 | -| headers | object | 可选 | | | 返回给客户端的 `headers`,这里可以设置多个。头信息如果存在将重写,不存在则添加。想要删除某个 header 的话,把对应的值设置为空字符串即可。这个值能够以 `$var` 的格式包含 Nginx 变量,比如 `$remote_addr $balancer_ip`。 | -| vars | array[] | 可选 | | | `vars` 是一个表达式列表,只有满足条件的请求和响应才会修改 body 和 header 信息,来自 [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list)。如果 `vars` 字段为空,那么所有的重写动作都会被无条件的执行。 | -| filters | array[] | 可选 | | | 一组过滤器,采用指定字符串表达式修改响应体。 | +| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | +|-----------------|---------|-----|-------|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| status_code | integer | 可选 | | [200, 598] | 修改上游返回状态码,默认保留原始响应代码。 | +| body | string | 可选 | | | 修改上游返回的 `body` 内容,如果设置了新内容,header 里面的 content-length 字段也会被去掉。 | +| body_base64 | boolean | 可选 | false | | 描述 `body` 字段是否需要 base64 解码之后再返回给客户端,用在某些图片和 Protobuffer 场景。 | +| headers | object | 可选 | | | 返回给客户端的 `headers`,这里可以设置多个。头信息如果存在将重写,不存在则添加。想要删除某个 header 的话,把对应的值设置为空字符串即可。这个值能够以 `$var` 的格式包含 Nginx 变量,比如 `$remote_addr $balancer_ip`。 | +| vars | array[] | 可选 | | | `vars` 是一个表达式列表,只有满足条件的请求和响应才会修改 body 和 header 信息,来自 [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list)。如果 `vars` 字段为空,那么所有的重写动作都会被无条件的执行。 | +| filters | array[] | 可选 | | | 一组过滤器,采用指定字符串表达式修改响应体。 | +| filters.regex | string | 可选 | | | 用于匹配响应体正则表达式。 | +| filters.scope | string | 可选 | "one" | "one","global" | 替换范围。 | +| filters.replace | string | 可选 | | | 替换后的内容。 | +| filters.options | string | 可选 | "jo" | | 正则匹配有效参数,可选项见 `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`。 | `body` 和 `filters`,两个只能配置其中一个。 -### Attributes of filters - -| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | -|---------|--------|-----|-------|----------------|--------------------------------------------------------------------------------------------| -| regex | string | 必填 | | | 用于匹配响应体正则表达式。 | -| scope | string | 必填 | "one" | "one","global" | 替换范围。 | -| replace | string | 必填 | | | 替换后的内容。 | -| options | string | 必填 | "jo" | | 正则匹配有效参数,可选项见 `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`. | - ## 示例 ### 开启插件 From dc809e1b9f3f780d5e677e14f74d233e2af582b8 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Sun, 3 Apr 2022 15:37:40 +0800 Subject: [PATCH 12/22] feat(response-rewrite): filters description with string concat Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 5614165cedff..ea2f14571274 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -53,8 +53,8 @@ local schema = { type = "array", }, filters = { - description = "a group of filters that modify response body\ -by replacing one specified string by another", + description = "a group of filters that modify response body" .. + "by replacing one specified string by another", type = "array", minItems = 1, items = { From 93e6e233055fcd820e2051ffdc8c8d7ab466ee35 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Sun, 3 Apr 2022 15:40:33 +0800 Subject: [PATCH 13/22] feat(response-rewrite): revert to if on conf.body Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index ea2f14571274..e28f66ab33d0 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -194,8 +194,9 @@ function _M.body_filter(conf, ctx) ngx.arg[1] = body return + end - elseif conf.body then + if conf.body then if conf.body_base64 then ngx.arg[1] = ngx.decode_base64(conf.body) From f42d160c572d73fa7921c9f57ba9eb3b71ea02e8 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Sun, 3 Apr 2022 15:42:39 +0800 Subject: [PATCH 14/22] feat(response-rewrite): fix filters.scope one to once Signed-off-by: kwanhur --- docs/en/latest/plugins/response-rewrite.md | 24 +++++++++++----------- docs/zh/latest/plugins/response-rewrite.md | 24 +++++++++++----------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/en/latest/plugins/response-rewrite.md b/docs/en/latest/plugins/response-rewrite.md index 0bd131553eed..ad8f87278f05 100644 --- a/docs/en/latest/plugins/response-rewrite.md +++ b/docs/en/latest/plugins/response-rewrite.md @@ -32,18 +32,18 @@ response rewrite plugin, rewrite the content returned by the upstream as well as ## Attributes -| Name | Type | Requirement | Default | Valid | Description | -|-----------------|---------|-------------|---------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| status_code | integer | optional | | [200, 598] | New `status code` to client, keep the original response code by default. | -| body | string | optional | | | New `body` to client, and the content-length will be reset too. | -| body_base64 | boolean | optional | false | | Identify if `body` in configuration need base64 decoded before rewrite to client. | -| headers | object | optional | | | Set the new `headers` for client, can set up multiple. If it exists already from upstream, will rewrite the header, otherwise will add the header. You can set the corresponding value to an empty string to remove a header. The value can contain Nginx variables in `$var` format, like `$remote_addr $balancer_ip`. | -| vars | array[] | optional | | | A DSL to evaluate with the given ngx.var. See `vars` [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list). if the `vars` is empty, then all rewrite operations will be executed unconditionally. | -| filters | array[] | optional | | | A group of filters that modify response body by replacing one specified string by another. | -| filters.regex | string | optional | | | match pattern on response body. | -| filters.scope | string | optional | "one" | "one","global" | substitution range. | -| filters.replace | string | optional | | | substitution content. | -| filters.options | string | optional | "jo" | | regex options, See `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`. | +| Name | Type | Requirement | Default | Valid | Description | +|-----------------|---------|-------------|---------|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| status_code | integer | optional | | [200, 598] | New `status code` to client, keep the original response code by default. | +| body | string | optional | | | New `body` to client, and the content-length will be reset too. | +| body_base64 | boolean | optional | false | | Identify if `body` in configuration need base64 decoded before rewrite to client. | +| headers | object | optional | | | Set the new `headers` for client, can set up multiple. If it exists already from upstream, will rewrite the header, otherwise will add the header. You can set the corresponding value to an empty string to remove a header. The value can contain Nginx variables in `$var` format, like `$remote_addr $balancer_ip`. | +| vars | array[] | optional | | | A DSL to evaluate with the given ngx.var. See `vars` [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list). if the `vars` is empty, then all rewrite operations will be executed unconditionally. | +| filters | array[] | optional | | | A group of filters that modify response body by replacing one specified string by another. | +| filters.regex | string | optional | | | match pattern on response body. | +| filters.scope | string | optional | "once" | "once","global" | substitution range. | +| filters.replace | string | optional | | | substitution content. | +| filters.options | string | optional | "jo" | | regex options, See `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`. | Only one of `body`, `filters` can be specified. diff --git a/docs/zh/latest/plugins/response-rewrite.md b/docs/zh/latest/plugins/response-rewrite.md index 83a9d5162e86..24b24f8afd18 100644 --- a/docs/zh/latest/plugins/response-rewrite.md +++ b/docs/zh/latest/plugins/response-rewrite.md @@ -33,18 +33,18 @@ title: response-rewrite ## 属性 -| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | -|-----------------|---------|-----|-------|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| status_code | integer | 可选 | | [200, 598] | 修改上游返回状态码,默认保留原始响应代码。 | -| body | string | 可选 | | | 修改上游返回的 `body` 内容,如果设置了新内容,header 里面的 content-length 字段也会被去掉。 | -| body_base64 | boolean | 可选 | false | | 描述 `body` 字段是否需要 base64 解码之后再返回给客户端,用在某些图片和 Protobuffer 场景。 | -| headers | object | 可选 | | | 返回给客户端的 `headers`,这里可以设置多个。头信息如果存在将重写,不存在则添加。想要删除某个 header 的话,把对应的值设置为空字符串即可。这个值能够以 `$var` 的格式包含 Nginx 变量,比如 `$remote_addr $balancer_ip`。 | -| vars | array[] | 可选 | | | `vars` 是一个表达式列表,只有满足条件的请求和响应才会修改 body 和 header 信息,来自 [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list)。如果 `vars` 字段为空,那么所有的重写动作都会被无条件的执行。 | -| filters | array[] | 可选 | | | 一组过滤器,采用指定字符串表达式修改响应体。 | -| filters.regex | string | 可选 | | | 用于匹配响应体正则表达式。 | -| filters.scope | string | 可选 | "one" | "one","global" | 替换范围。 | -| filters.replace | string | 可选 | | | 替换后的内容。 | -| filters.options | string | 可选 | "jo" | | 正则匹配有效参数,可选项见 `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`。 | +| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | +|-----------------|---------|-----|--------|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| status_code | integer | 可选 | | [200, 598] | 修改上游返回状态码,默认保留原始响应代码。 | +| body | string | 可选 | | | 修改上游返回的 `body` 内容,如果设置了新内容,header 里面的 content-length 字段也会被去掉。 | +| body_base64 | boolean | 可选 | false | | 描述 `body` 字段是否需要 base64 解码之后再返回给客户端,用在某些图片和 Protobuffer 场景。 | +| headers | object | 可选 | | | 返回给客户端的 `headers`,这里可以设置多个。头信息如果存在将重写,不存在则添加。想要删除某个 header 的话,把对应的值设置为空字符串即可。这个值能够以 `$var` 的格式包含 Nginx 变量,比如 `$remote_addr $balancer_ip`。 | +| vars | array[] | 可选 | | | `vars` 是一个表达式列表,只有满足条件的请求和响应才会修改 body 和 header 信息,来自 [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list)。如果 `vars` 字段为空,那么所有的重写动作都会被无条件的执行。 | +| filters | array[] | 可选 | | | 一组过滤器,采用指定字符串表达式修改响应体。 | +| filters.regex | string | 可选 | | | 用于匹配响应体正则表达式。 | +| filters.scope | string | 可选 | "once" | "once","global" | 替换范围。 | +| filters.replace | string | 可选 | | | 替换后的内容。 | +| filters.options | string | 可选 | "jo" | | 正则匹配有效参数,可选项见 `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`。 | `body` 和 `filters`,两个只能配置其中一个。 From f46ca71446f6d27b9d0ad968ea7f260e4616309b Mon Sep 17 00:00:00 2001 From: kwanhur Date: Wed, 6 Apr 2022 15:01:50 +0800 Subject: [PATCH 15/22] feat(response-rewrite): add required on filter field regex and replace Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 1 + t/plugin/response-rewrite2.t | 62 +++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index e28f66ab33d0..2a01aa73cd65 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -60,6 +60,7 @@ local schema = { items = { description = "filter that modifies response body", type = "object", + required = {"regex", "replace"}, properties = { regex = { description = "match pattern on response body", diff --git a/t/plugin/response-rewrite2.t b/t/plugin/response-rewrite2.t index 47223030df32..bfca221bef96 100644 --- a/t/plugin/response-rewrite2.t +++ b/t/plugin/response-rewrite2.t @@ -65,7 +65,35 @@ done -=== TEST 2: add plugin with invalid filter scope +=== TEST 2: add plugin with invalid filter required filed +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.response-rewrite") + local ok, err = plugin.check_schema({ + filters = { + { + regex = "Hello", + } + } + }) + if not ok then + ngx.say(err) + else + ngx.say("done") + end + } + } +--- request +GET /t +--- response_body +property "filters" validation failed: failed to validate item 1: property "replace" is required +--- no_error_log +[error] + + + +=== TEST 3: add plugin with invalid filter scope --- config location /t { content_by_lua_block { @@ -96,7 +124,7 @@ property "filters" validation failed: failed to validate item 1: property "scope -=== TEST 3: add plugin with invalid filter empty value +=== TEST 4: add plugin with invalid filter empty value --- config location /t { content_by_lua_block { @@ -125,7 +153,7 @@ invalid value as filter field regex -=== TEST 4: add plugin with invalid filter regex options +=== TEST 5: add plugin with invalid filter regex options --- config location /t { content_by_lua_block { @@ -154,7 +182,7 @@ unknown flag "h" -=== TEST 5: set route with filters and vars expr +=== TEST 6: set route with filters and vars expr --- config location /t { content_by_lua_block { @@ -197,7 +225,7 @@ passed -=== TEST 6: check http body that matches filters +=== TEST 7: check http body that matches filters --- request GET /hello --- response_body @@ -205,7 +233,7 @@ test world -=== TEST 7: filter substitute global +=== TEST 8: filter substitute global --- config location /t { content_by_lua_block { @@ -249,7 +277,7 @@ passed -=== TEST 8: check http body that substitute global +=== TEST 9: check http body that substitute global --- request GET /hello --- response_body @@ -257,7 +285,7 @@ hetto wortd -=== TEST 9: filter replace with empty +=== TEST 10: filter replace with empty --- config location /t { content_by_lua_block { @@ -300,7 +328,7 @@ passed -=== TEST 10: check http body that replace with empty +=== TEST 11: check http body that replace with empty --- request GET /hello --- response_body @@ -308,7 +336,7 @@ GET /hello -=== TEST 11: filter replace with words +=== TEST 12: filter replace with words --- config location /t { content_by_lua_block { @@ -351,7 +379,7 @@ passed -=== TEST 12: check http body that replace with words +=== TEST 13: check http body that replace with words --- request GET /hello --- response_body @@ -359,7 +387,7 @@ hello * -=== TEST 13: set body and filters(body no effect) +=== TEST 14: set body and filters(body no effect) --- config location /t { content_by_lua_block { @@ -403,7 +431,7 @@ passed -=== TEST 14: check http body that set body and filters +=== TEST 15: check http body that set body and filters --- request GET /hello --- response_body @@ -411,7 +439,7 @@ HELLO world -=== TEST 15: set multiple filters +=== TEST 16: set multiple filters --- config location /t { content_by_lua_block { @@ -458,7 +486,7 @@ passed -=== TEST 16: check http body that set multiple filters +=== TEST 17: check http body that set multiple filters --- request GET /hello --- response_body @@ -466,7 +494,7 @@ HETLO world -=== TEST 17: filters no any match +=== TEST 18: filters no any match --- config location /t { content_by_lua_block { @@ -509,7 +537,7 @@ passed -=== TEST 18: check http body that filters no any match +=== TEST 19: check http body that filters no any match --- request GET /hello --- response_body From 4d6abb88139fceb92ffdcf4cd40fde46964e1903 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Wed, 6 Apr 2022 15:16:55 +0800 Subject: [PATCH 16/22] feat(response-rewrite): pcall compile invalid regex options Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 7 ++++--- t/plugin/response-rewrite2.t | 9 ++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 2a01aa73cd65..5d2584a0bf74 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -24,6 +24,7 @@ local re_gsub = ngx.re.gsub local pairs = pairs local ipairs = ipairs local type = type +local pcall = pcall local schema = { @@ -159,10 +160,10 @@ function _M.check_schema(conf) if filter.regex == "" then return false, "invalid value as filter field regex" end - local ok, err = re_compile(filter.regex, filter.options) + local ok, err = pcall(re_compile,filter.regex, filter.options) if not ok then - return false, "failed to validate regex '" .. filter.regex .. -"' and options '" .. filter.options .. "':" .. err + return false, "regex \"" .. filter.regex .. + "\" validation failed: " .. err end end end diff --git a/t/plugin/response-rewrite2.t b/t/plugin/response-rewrite2.t index bfca221bef96..37dc5a94bac9 100644 --- a/t/plugin/response-rewrite2.t +++ b/t/plugin/response-rewrite2.t @@ -162,6 +162,7 @@ invalid value as filter field regex filters = { { regex = "hello", + replace = "HELLO", options = "h" } } @@ -176,9 +177,11 @@ invalid value as filter field regex --- request GET /t --- error_code eval -500 ---- error_log -unknown flag "h" +200 +--- response_body +regex "hello" validation failed: unknown flag "h" (flags "h") +--- no_error_log +[error] From 451627bf7a48f72142b02284af90e7b2480f51cb Mon Sep 17 00:00:00 2001 From: kwanhur Date: Thu, 7 Apr 2022 14:59:15 +0800 Subject: [PATCH 17/22] feat(response-rewrite): remove default value and update docs on required filed Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 2 -- docs/en/latest/plugins/response-rewrite.md | 4 ++-- docs/zh/latest/plugins/response-rewrite.md | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 5d2584a0bf74..3dc446a320e2 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -66,7 +66,6 @@ local schema = { regex = { description = "match pattern on response body", type = "string", - default = "", }, scope = { description = "regex substitution range", @@ -77,7 +76,6 @@ local schema = { replace = { description = "regex substitution content", type = "string", - default = "", }, options = { description = "regex options", diff --git a/docs/en/latest/plugins/response-rewrite.md b/docs/en/latest/plugins/response-rewrite.md index ad8f87278f05..73578e39bf5a 100644 --- a/docs/en/latest/plugins/response-rewrite.md +++ b/docs/en/latest/plugins/response-rewrite.md @@ -40,9 +40,9 @@ response rewrite plugin, rewrite the content returned by the upstream as well as | headers | object | optional | | | Set the new `headers` for client, can set up multiple. If it exists already from upstream, will rewrite the header, otherwise will add the header. You can set the corresponding value to an empty string to remove a header. The value can contain Nginx variables in `$var` format, like `$remote_addr $balancer_ip`. | | vars | array[] | optional | | | A DSL to evaluate with the given ngx.var. See `vars` [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list). if the `vars` is empty, then all rewrite operations will be executed unconditionally. | | filters | array[] | optional | | | A group of filters that modify response body by replacing one specified string by another. | -| filters.regex | string | optional | | | match pattern on response body. | +| filters.regex | string | required | | | match pattern on response body. | | filters.scope | string | optional | "once" | "once","global" | substitution range. | -| filters.replace | string | optional | | | substitution content. | +| filters.replace | string | required | | | substitution content. | | filters.options | string | optional | "jo" | | regex options, See `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`. | Only one of `body`, `filters` can be specified. diff --git a/docs/zh/latest/plugins/response-rewrite.md b/docs/zh/latest/plugins/response-rewrite.md index 24b24f8afd18..cd32265bc132 100644 --- a/docs/zh/latest/plugins/response-rewrite.md +++ b/docs/zh/latest/plugins/response-rewrite.md @@ -41,9 +41,9 @@ title: response-rewrite | headers | object | 可选 | | | 返回给客户端的 `headers`,这里可以设置多个。头信息如果存在将重写,不存在则添加。想要删除某个 header 的话,把对应的值设置为空字符串即可。这个值能够以 `$var` 的格式包含 Nginx 变量,比如 `$remote_addr $balancer_ip`。 | | vars | array[] | 可选 | | | `vars` 是一个表达式列表,只有满足条件的请求和响应才会修改 body 和 header 信息,来自 [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list)。如果 `vars` 字段为空,那么所有的重写动作都会被无条件的执行。 | | filters | array[] | 可选 | | | 一组过滤器,采用指定字符串表达式修改响应体。 | -| filters.regex | string | 可选 | | | 用于匹配响应体正则表达式。 | +| filters.regex | string | 必选 | | | 用于匹配响应体正则表达式。 | | filters.scope | string | 可选 | "once" | "once","global" | 替换范围。 | -| filters.replace | string | 可选 | | | 替换后的内容。 | +| filters.replace | string | 必选 | | | 替换后的内容。 | | filters.options | string | 可选 | "jo" | | 正则匹配有效参数,可选项见 `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`。 | `body` 和 `filters`,两个只能配置其中一个。 From 27673089f0f9abdb24fd4f481abc1b2eb94f789a Mon Sep 17 00:00:00 2001 From: kwanhur Date: Thu, 7 Apr 2022 15:07:15 +0800 Subject: [PATCH 18/22] chore: place one space before `filter.regex` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 罗泽轩 --- apisix/plugins/response-rewrite.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 3dc446a320e2..2f8093f9ebfe 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -158,7 +158,7 @@ function _M.check_schema(conf) if filter.regex == "" then return false, "invalid value as filter field regex" end - local ok, err = pcall(re_compile,filter.regex, filter.options) + local ok, err = pcall(re_compile, filter.regex, filter.options) if not ok then return false, "regex \"" .. filter.regex .. "\" validation failed: " .. err From 3ec79ac43405d3cd80f4839d2528825b9dd316ae Mon Sep 17 00:00:00 2001 From: kwanhur Date: Fri, 8 Apr 2022 15:21:10 +0800 Subject: [PATCH 19/22] feat(response-rewrite): filter.regex minLength 1 Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 4 +--- t/plugin/response-rewrite2.t | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 2f8093f9ebfe..77c0a0f97691 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -66,6 +66,7 @@ local schema = { regex = { description = "match pattern on response body", type = "string", + minLength = 1, }, scope = { description = "regex substitution range", @@ -155,9 +156,6 @@ function _M.check_schema(conf) if conf.filters then for _, filter in ipairs(conf.filters) do - if filter.regex == "" then - return false, "invalid value as filter field regex" - end local ok, err = pcall(re_compile, filter.regex, filter.options) if not ok then return false, "regex \"" .. filter.regex .. diff --git a/t/plugin/response-rewrite2.t b/t/plugin/response-rewrite2.t index 37dc5a94bac9..88712888a28b 100644 --- a/t/plugin/response-rewrite2.t +++ b/t/plugin/response-rewrite2.t @@ -147,7 +147,7 @@ property "filters" validation failed: failed to validate item 1: property "scope --- request GET /t --- response_body -invalid value as filter field regex +property "filters" validation failed: failed to validate item 1: property "regex" validation failed: string too short, expected at least 1, got 0 --- no_error_log [error] From df3ede339e74833e1ab3b2409f116c1852d10cb6 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Sat, 9 Apr 2022 11:30:20 +0800 Subject: [PATCH 20/22] docs(response-rewrite): update filter scope option once and global usage Signed-off-by: kwanhur --- docs/en/latest/plugins/response-rewrite.md | 2 +- docs/zh/latest/plugins/response-rewrite.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/latest/plugins/response-rewrite.md b/docs/en/latest/plugins/response-rewrite.md index 73578e39bf5a..ee7048a903ad 100644 --- a/docs/en/latest/plugins/response-rewrite.md +++ b/docs/en/latest/plugins/response-rewrite.md @@ -41,7 +41,7 @@ response rewrite plugin, rewrite the content returned by the upstream as well as | vars | array[] | optional | | | A DSL to evaluate with the given ngx.var. See `vars` [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list). if the `vars` is empty, then all rewrite operations will be executed unconditionally. | | filters | array[] | optional | | | A group of filters that modify response body by replacing one specified string by another. | | filters.regex | string | required | | | match pattern on response body. | -| filters.scope | string | optional | "once" | "once","global" | substitution range. | +| filters.scope | string | optional | "once" | "once","global" | substitution range, "once" substitutes the first match of `filters.regex` on response body, "global" does global substitution. | | filters.replace | string | required | | | substitution content. | | filters.options | string | optional | "jo" | | regex options, See `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`. | diff --git a/docs/zh/latest/plugins/response-rewrite.md b/docs/zh/latest/plugins/response-rewrite.md index cd32265bc132..603e9ec8c73a 100644 --- a/docs/zh/latest/plugins/response-rewrite.md +++ b/docs/zh/latest/plugins/response-rewrite.md @@ -42,7 +42,7 @@ title: response-rewrite | vars | array[] | 可选 | | | `vars` 是一个表达式列表,只有满足条件的请求和响应才会修改 body 和 header 信息,来自 [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list)。如果 `vars` 字段为空,那么所有的重写动作都会被无条件的执行。 | | filters | array[] | 可选 | | | 一组过滤器,采用指定字符串表达式修改响应体。 | | filters.regex | string | 必选 | | | 用于匹配响应体正则表达式。 | -| filters.scope | string | 可选 | "once" | "once","global" | 替换范围。 | +| filters.scope | string | 可选 | "once" | "once","global" | 替换范围,"once" 表达式 `filters.regex` 仅替换首次匹配上响应体的内容,"global" 则进行全局替换。 | | filters.replace | string | 必选 | | | 替换后的内容。 | | filters.options | string | 可选 | "jo" | | 正则匹配有效参数,可选项见 `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`。 | From c42d70c441411aee54dc2e50e5797ceb51a28e46 Mon Sep 17 00:00:00 2001 From: kwanhur Date: Sat, 9 Apr 2022 11:34:33 +0800 Subject: [PATCH 21/22] docs(response-rewrite): fix regex options referece link Signed-off-by: kwanhur --- docs/en/latest/plugins/response-rewrite.md | 2 +- docs/zh/latest/plugins/response-rewrite.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/latest/plugins/response-rewrite.md b/docs/en/latest/plugins/response-rewrite.md index ee7048a903ad..a32ccfed3399 100644 --- a/docs/en/latest/plugins/response-rewrite.md +++ b/docs/en/latest/plugins/response-rewrite.md @@ -43,7 +43,7 @@ response rewrite plugin, rewrite the content returned by the upstream as well as | filters.regex | string | required | | | match pattern on response body. | | filters.scope | string | optional | "once" | "once","global" | substitution range, "once" substitutes the first match of `filters.regex` on response body, "global" does global substitution. | | filters.replace | string | required | | | substitution content. | -| filters.options | string | optional | "jo" | | regex options, See `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`. | +| filters.options | string | optional | "jo" | | regex options, See [ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch). | Only one of `body`, `filters` can be specified. diff --git a/docs/zh/latest/plugins/response-rewrite.md b/docs/zh/latest/plugins/response-rewrite.md index 603e9ec8c73a..15e77c9fa3ba 100644 --- a/docs/zh/latest/plugins/response-rewrite.md +++ b/docs/zh/latest/plugins/response-rewrite.md @@ -44,7 +44,7 @@ title: response-rewrite | filters.regex | string | 必选 | | | 用于匹配响应体正则表达式。 | | filters.scope | string | 可选 | "once" | "once","global" | 替换范围,"once" 表达式 `filters.regex` 仅替换首次匹配上响应体的内容,"global" 则进行全局替换。 | | filters.replace | string | 必选 | | | 替换后的内容。 | -| filters.options | string | 可选 | "jo" | | 正则匹配有效参数,可选项见 `[ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)`。 | +| filters.options | string | 可选 | "jo" | | 正则匹配有效参数,可选项见 [ngx.re.match](https://github.com/openresty/lua-nginx-module#ngxrematch)。 | `body` 和 `filters`,两个只能配置其中一个。 From cd3f883a1a13a860f4b0ce3894749cd61a3bfbba Mon Sep 17 00:00:00 2001 From: kwanhur Date: Sun, 10 Apr 2022 18:23:17 +0800 Subject: [PATCH 22/22] feat(response-rewrite): add re_sub/gsub err log Signed-off-by: kwanhur --- apisix/plugins/response-rewrite.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 77c0a0f97691..b2c94f2aff1e 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -182,11 +182,15 @@ function _M.body_filter(conf, ctx) return end + local err for _, filter in ipairs(conf.filters) do if filter.scope == "once" then - body = re_sub(body, filter.regex, filter.replace, filter.options) + body, _, err = re_sub(body, filter.regex, filter.replace, filter.options) else - body = re_gsub(body, filter.regex, filter.replace, filter.options) + body, _, err = re_gsub(body, filter.regex, filter.replace, filter.options) + end + if err ~= nil then + core.log.error("regex \"" .. filter.regex .. "\" substitutes failed:" .. err) end end