Skip to content

Commit

Permalink
feat(proxy-rewrite): create use_real_request_uri_unsafe option, see a…
Browse files Browse the repository at this point in the history
  • Loading branch information
--global committed Jul 6, 2022
1 parent a8a692d commit c818328
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 22 deletions.
37 changes: 23 additions & 14 deletions apisix/plugins/proxy-rewrite.lua
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ local schema = {
type = "object",
minProperties = 1,
},
use_real_request_uri_unsafe = {
description = "use real_request_uri instead, THIS IS VERY UNSAFE.",
type = "boolean",
default = false,
},
},
minProperties = 1,
}
Expand Down Expand Up @@ -161,7 +166,9 @@ function _M.rewrite(conf, ctx)
end

local upstream_uri = ctx.var.uri
if conf.uri ~= nil then
if conf.use_real_request_uri_unsafe then
upstream_uri = core.utils.resolve_var("$real_request_uri", ctx.var)
elseif conf.uri ~= nil then
upstream_uri = core.utils.resolve_var(conf.uri, ctx.var)
elseif conf.regex_uri ~= nil then
local uri, _, err = re_sub(ctx.var.uri, conf.regex_uri[1],
Expand All @@ -177,22 +184,24 @@ function _M.rewrite(conf, ctx)
end
end

local index = str_find(upstream_uri, "?")
if index then
upstream_uri = core.utils.uri_safe_encode(sub_str(upstream_uri, 1, index-1)) ..
sub_str(upstream_uri, index)
else
upstream_uri = core.utils.uri_safe_encode(upstream_uri)
end

if ctx.var.is_args == "?" then
if not conf.use_real_request_uri_unsafe then
local index = str_find(upstream_uri, "?")
if index then
ctx.var.upstream_uri = upstream_uri .. "&" .. (ctx.var.args or "")
upstream_uri = core.utils.uri_safe_encode(sub_str(upstream_uri, 1, index-1)) ..
sub_str(upstream_uri, index)
else
upstream_uri = core.utils.uri_safe_encode(upstream_uri)
end

if ctx.var.is_args == "?" then
if index then
ctx.var.upstream_uri = upstream_uri .. "&" .. (ctx.var.args or "")
else
ctx.var.upstream_uri = upstream_uri .. "?" .. (ctx.var.args or "")
end
else
ctx.var.upstream_uri = upstream_uri .. "?" .. (ctx.var.args or "")
ctx.var.upstream_uri = upstream_uri
end
else
ctx.var.upstream_uri = upstream_uri
end

if conf.headers then
Expand Down
17 changes: 9 additions & 8 deletions docs/en/latest/plugins/proxy-rewrite.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ The `proxy-rewrite` Plugin rewrites Upstream proxy information such as `scheme`,

## Attributes

| Name | Type | Required | Default | Valid values | Description |
|-----------|---------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| scheme | string | False | "http" | ["http", "https"] | New upstream protocol scheme. This option is deprecated. Instead, it is recommended to set the `scheme` field in the Upstream. |
| uri | string | False | | | New Upstream forwarding address. Value supports [Nginx variables](https://nginx.org/en/docs/http/ngx_http_core_module.html). For example, `$arg_name`. |
| method | string | False | | ["GET", "POST", "PUT", "HEAD", "DELETE", "OPTIONS","MKCOL", "COPY", "MOVE", "PROPFIND", "PROPFIND","LOCK", "UNLOCK", "PATCH", "TRACE"] | Rewrites the HTTP method. |
| regex_uri | array[string] | False | | | New upstream forwarding address. Regular expressions can be used to match the URL from client. If it matches, the URL template is forwarded to the Upstream otherwise, the URL from the client is forwarded. When both `uri` and `regex_uri` are configured, `uri` is used first. For example, `[" ^/iresty/(.*)/(.*)/(.*)", "/$1-$2-$3"]`. Here, the first element is the regular expression to match and the second element is the URL template forwarded to the Upstream. |
| host | string | False | | | New Upstream host address. |
| headers | object | False | | | New Upstream headers. Headers are overwritten if they are already present otherwise, they are added to the present headers. To remove a header, set the header value to an empty string. The values in the header can contain Nginx variables like `$remote_addr` and `$client_addr`. |
| Name | Type | Required | Default | Valid values | Description |
|-----------------------------|---------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| scheme | string | False | "http" | ["http", "https"] | New upstream protocol scheme. This option is deprecated. Instead, it is recommended to set the `scheme` field in the Upstream. |
| uri | string | False | | | New Upstream forwarding address. Value supports [Nginx variables](https://nginx.org/en/docs/http/ngx_http_core_module.html). For example, `$arg_name`. |
| method | string | False | | ["GET", "POST", "PUT", "HEAD", "DELETE", "OPTIONS","MKCOL", "COPY", "MOVE", "PROPFIND", "PROPFIND","LOCK", "UNLOCK", "PATCH", "TRACE"] | Rewrites the HTTP method. |
| regex_uri | array[string] | False | | | New upstream forwarding address. Regular expressions can be used to match the URL from client. If it matches, the URL template is forwarded to the Upstream otherwise, the URL from the client is forwarded. When both `uri` and `regex_uri` are configured, `uri` is used first. For example, `[" ^/iresty/(.*)/(.*)/(.*)", "/$1-$2-$3"]`. Here, the first element is the regular expression to match and the second element is the URL template forwarded to the Upstream. |
| host | string | False | | | New Upstream host address. |
| headers | object | False | | | New Upstream headers. Headers are overwritten if they are already present otherwise, they are added to the present headers. To remove a header, set the header value to an empty string. The values in the header can contain Nginx variables like `$remote_addr` and `$client_addr`. |
| use_real_request_uri_unsafe | boolean | False | false | | Use real_request_uri (originally request_uri in nginx) to bypass URI normalization. Enabling this is considered **unsafe** as it bypasses _all_ URI normalization steps. |

## Enabling the Plugin

Expand Down
7 changes: 7 additions & 0 deletions t/lib/server.lua
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,13 @@ for i = 1, 100 do
_M["print_uri_" .. i] = print_uri
end

local function print_uri_detailed()
ngx.say("ngx.var.uri: ", ngx.var.uri)
ngx.say("ngx.var.request_uri: ", ngx.var.request_uri)
end
for i = 1, 100 do
_M["print_uri_detailed_" .. i] = print_uri_detailed
end

function _M.headers()
local args = ngx.req.get_uri_args()
Expand Down
1 change: 1 addition & 0 deletions t/lib/toolkit
Submodule toolkit added at c687ca
100 changes: 100 additions & 0 deletions t/plugin/proxy-rewrite3.t
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,103 @@ passed
GET /hello
--- error_log
plugin_proxy_rewrite get method: POST
=== TEST 8: set route(unsafe uri normalized at request)
--- 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": {
"use_real_request_uri_unsafe": false
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/print_uri_detailed_99"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]
=== TEST 9: unsafe uri normalized at request
--- request
GET /print%5Furi%5Fdetailed%5F99 HTTP/1.1
--- response_body
ngx.var.uri: /print_uri_detailed_99
ngx.var.request_uri: /print_uri_detailed_99
--- no_error_log
[error]
=== TEST 10: set route(unsafe uri not normalized at request)
--- 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": {
"use_real_request_uri_unsafe": true
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/print_uri_detailed_99"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]
=== TEST 11: unsafe uri not normalized at request
--- request
GET /print%5Furi%5Fdetailed%5F99 HTTP/1.1
--- response_body
ngx.var.uri: /print_uri_detailed_99
ngx.var.request_uri: /print%5Furi%5Fdetailed%5F99
--- no_error_log
[error]
1 change: 1 addition & 0 deletions toolkit
Submodule toolkit added at c687ca

0 comments on commit c818328

Please sign in to comment.