diff --git a/apisix/plugins/proxy-rewrite.lua b/apisix/plugins/proxy-rewrite.lua index c1609c612dfa..6bddfa39a4a9 100644 --- a/apisix/plugins/proxy-rewrite.lua +++ b/apisix/plugins/proxy-rewrite.lua @@ -284,13 +284,28 @@ function _M.rewrite(conf, ctx) local upstream_uri = ctx.var.uri local separator_escaped = false + + -- resolve_var() below drops the query string, so keep the original one from + -- real_request_uri to re-append it when conf.uri is set. + local query_string = "" if conf.use_real_request_uri_unsafe then upstream_uri = ctx.var.real_request_uri + local index = str_find(upstream_uri, "?") + query_string = index and sub_str(upstream_uri, index) or "" end if conf.uri ~= nil then separator_escaped = true upstream_uri = core.utils.resolve_var(conf.uri, ctx.var, escape_separator) + if query_string ~= "" then + -- merge with '&' when conf.uri already carries its own query string, + -- otherwise append the original query string as-is + if str_find(upstream_uri, "?") then + upstream_uri = upstream_uri .. "&" .. sub_str(query_string, 2) + else + upstream_uri = upstream_uri .. query_string + end + end elseif conf.regex_uri ~= nil then if not str_find(upstream_uri, "?") then diff --git a/t/plugin/proxy-rewrite.t b/t/plugin/proxy-rewrite.t index 276dd02ed1ea..6b75fbe7c08b 100644 --- a/t/plugin/proxy-rewrite.t +++ b/t/plugin/proxy-rewrite.t @@ -1231,3 +1231,288 @@ GET /hello uri: /uri host: test.com:6443 x-real-ip: 127.0.0.1 + + + +=== TEST 45: set route(rewrite uri args with unsafe allowed) +--- 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": "/plugin_proxy_rewrite_args", + "use_real_request_uri_unsafe": 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 + + + +=== TEST 46: rewrite uri args +--- request +GET /hello?q=apisix&a=iresty HTTP/1.1 +--- response_body +uri: /plugin_proxy_rewrite_args +a: iresty +q: apisix + + + +=== TEST 47: set route(rewrite uri empty args with unsafe allowed) +--- 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": "/plugin_proxy_rewrite_args", + "use_real_request_uri_unsafe": 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 + + + +=== TEST 48: rewrite uri empty args +--- request +GET /hello HTTP/1.1 +--- response_body +uri: /plugin_proxy_rewrite_args + + + +=== TEST 49: set route(conf.uri carries its own query, use_real_request_uri_unsafe) +--- 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": "/plugin_proxy_rewrite_args?x=1", + "use_real_request_uri_unsafe": 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 + + + +=== TEST 50: merge conf.uri query with incoming query (use '&', not double '?') +--- request +GET /hello?q=apisix HTTP/1.1 +--- response_body +uri: /plugin_proxy_rewrite_args +q: apisix +x: 1 + + + +=== TEST 51: set route(use_real_request_uri_unsafe only, no conf.uri) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "proxy-rewrite": { + "use_real_request_uri_unsafe": true + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/plugin_proxy_rewrite_args" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 52: query string reaches upstream exactly once without conf.uri +--- request +GET /plugin_proxy_rewrite_args?q=apisix HTTP/1.1 +--- response_body +uri: /plugin_proxy_rewrite_args +q: apisix + + + +=== TEST 53: set route(conf.uri echoing raw request_uri, use_real_request_uri_unsafe) +--- 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": "/print_uri_detailed", + "use_real_request_uri_unsafe": 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 + + + +=== TEST 54: query with special characters passed through unchanged (unsafe) +--- request +GET /hello?name=a%20b&x=%E4%B8%AD HTTP/1.1 +--- response_body +ngx.var.uri: /print_uri_detailed +ngx.var.request_uri: /print_uri_detailed?name=a%20b&x=%E4%B8%AD + + + +=== TEST 55: query with multiple '?' passed through unchanged (unsafe) +--- request +GET /hello?a=1?b=2 HTTP/1.1 +--- response_body +ngx.var.uri: /print_uri_detailed +ngx.var.request_uri: /print_uri_detailed?a=1?b=2 + + + +=== TEST 56: set route(same conf.uri with use_real_request_uri_unsafe disabled) +--- 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": "/print_uri_detailed", + "use_real_request_uri_unsafe": 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 + + + +=== TEST 57: query preserved with use_real_request_uri_unsafe disabled (both variants consistent) +--- request +GET /hello?name=a%20b&x=1 HTTP/1.1 +--- response_body +ngx.var.uri: /print_uri_detailed +ngx.var.request_uri: /print_uri_detailed?name=a%20b&x=1 diff --git a/t/plugin/proxy-rewrite3.t b/t/plugin/proxy-rewrite3.t index 55afe14cc738..f16a038109da 100644 --- a/t/plugin/proxy-rewrite3.t +++ b/t/plugin/proxy-rewrite3.t @@ -199,7 +199,7 @@ plugin_proxy_rewrite get method: POST -=== TEST 8: set route(unsafe uri not normalized at request) +=== TEST 8: set route(unsafe uri not normalized at request with unsafe allowed) --- config location /t { content_by_lua_block { @@ -243,7 +243,7 @@ ngx.var.request_uri: /print%5Furi%5Fdetailed -=== TEST 10: set route(safe uri not normalized at request) +=== TEST 10: set route(safe uri not normalized at request with unsafe allowed) --- config location /t { content_by_lua_block { @@ -999,3 +999,47 @@ GET /hello/%ED%85%8C%EC%8A%A4%ED%8A%B8 HTTP/1.1 } --- response_body /hello?unsafe_variable=%ED%85%8C%EC%8A%A4%ED%8A%B8 + + + +=== TEST 42: set route(unsafe uri normalized at request with unsafe not allowed) +--- 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" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 43: unsafe uri normalized at request +--- request +GET /print%5Furi%5Fdetailed HTTP/1.1 +--- response_body +ngx.var.uri: /print_uri_detailed +ngx.var.request_uri: /print_uri_detailed