diff --git a/.mergify.yml b/.mergify.yml
new file mode 100644
index 0000000000..c89be4fb54
--- /dev/null
+++ b/.mergify.yml
@@ -0,0 +1,35 @@
+---
+pull_request_rules:
+ - name: warn on conflicts
+ conditions:
+ - conflict
+ actions:
+ comment:
+ message: This pull request is now in conflict :(
+ label:
+ add:
+ - conflict
+ - name: remove conflict label if not needed
+ conditions:
+ - -conflict
+ actions:
+ label:
+ remove:
+ - conflict
+ - name: add label needs-test-cases
+ conditions:
+ - files~=^src/
+ - -files~=^t/
+ actions:
+ label:
+ add:
+ - needs-test-cases
+ - name: remove label needs-test-cases
+ conditions:
+ - label=needs-test-cases
+ - files~=^src/
+ - files~=^t/
+ actions:
+ label:
+ remove:
+ - needs-test-cases
diff --git a/.travis.yml b/.travis.yml
index b7fa83a91a..dd56267580 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -83,10 +83,10 @@ install:
- git clone https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module
- git clone https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module
- git clone https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module
- - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core
+ - git clone -b uri_not_comp https://github.com/zhuizhuhaomeng/lua-resty-core.git ../lua-resty-core
- git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
- git clone https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql
- - git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module
+ - git clone -b uri_not_comp https://github.com/zhuizhuhaomeng/stream-lua-nginx-module.git ../stream-lua-nginx-module
- git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2
before_script:
@@ -94,8 +94,8 @@ before_script:
script:
- sudo iptables -I OUTPUT 1 -p udp --dport 10086 -j REJECT
- - sudo iptables -A OUTPUT -p tcp --dst 127.0.0.2 --dport 12345 -j DROP
- - sudo iptables -A OUTPUT -p udp --dst 127.0.0.2 --dport 12345 -j DROP
+ - sudo iptables -I OUTPUT -p tcp --dst 127.0.0.2 --dport 12345 -j DROP
+ - sudo iptables -I OUTPUT -p udp --dst 127.0.0.2 --dport 12345 -j DROP
- cd luajit2/
- make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT -msse4.2' > build.log 2>&1 || (cat build.log && exit 1)
- sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)
diff --git a/README.markdown b/README.markdown
index ee87baeee7..85ac8bb8c7 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1512,6 +1512,8 @@ This hook is often used to create per-worker reoccurring timers (via the [ngx.ti
return
end
end
+
+ -- do something in timer
end
local hdl, err = new_timer(delay, check)
@@ -1519,6 +1521,8 @@ This hook is often used to create per-worker reoccurring timers (via the [ngx.ti
log(ERR, "failed to create timer: ", err)
return
end
+
+ -- other job in init_worker_by_lua
';
```
@@ -4040,7 +4044,7 @@ in gzipped responses that cannot be handled properly in Lua code. Original reque
When the `body` option is not specified and the `always_forward_body` option is false (the default value), the `POST` and `PUT` subrequests will inherit the request bodies of the parent request (if any).
-There is a hard-coded upper limit on the number of concurrent subrequests possible for every main request. In older versions of Nginx, the limit was `50` concurrent subrequests and in more recent versions, Nginx `1.1.x` onwards, this was increased to `200` concurrent subrequests. When this limit is exceeded, the following error message is added to the `error.log` file:
+There is a hard-coded upper limit on the number of subrequests possible for every main request. In older versions of Nginx, the limit was `50` concurrent subrequests and in more recent versions, Nginx `1.9.5` onwards, the same limit is changed to limit the depth of recursive subrequests. When this limit is exceeded, the following error message is added to the `error.log` file:
[error] 13983#0: *1 subrequests cycle while processing "/uri"
@@ -4548,7 +4552,12 @@ or a Lua table holding the query arguments' key-value pairs, as in
ngx.req.set_uri_args({ a = 3, b = "hello world" })
```
-where in the latter case, this method will escape argument keys and values according to the URI escaping rule.
+In the former case, i.e., when the whole query-string is provided directly,
+the input Lua string should already be well-formed with the URI encoding.
+For security considerations, this method will automatically escape any control and
+whitespace characters (ASCII code 0x00 ~ 0x32 and 0x7F) in the Lua string.
+
+In the latter case, this method will escape argument keys and values according to the URI escaping rule.
Multi-value arguments are also supported:
@@ -4874,6 +4883,11 @@ ngx.req.set_header
Set the current request's request header named `header_name` to value `header_value`, overriding any existing ones.
+The input Lua string `header_name` and `header_value` should already be well-formed with the URI encoding.
+For security considerations, this method will automatically escape " ", """, "(", ")", ",", "/", ":", ";", "?",
+"<", "=", ">", "?", "@", "[", "]", "\", "{", "}", 0x00-0x1F, 0x7F-0xFF in `header_name` and automatically escape
+"0x00-0x08, 0x0A-0x0F, 0x7F in `header_value`.
+
By default, all the subrequests subsequently initiated by [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi) will inherit the new header.
Here is an example of setting the `Content-Type` header:
@@ -5567,11 +5581,15 @@ This method was introduced in the `0.5.0rc30` release.
ngx.escape_uri
--------------
-**syntax:** *newstr = ngx.escape_uri(str)*
+**syntax:** *newstr = ngx.escape_uri(str, type?)*
**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua**
-Escape `str` as a URI component.
+Since `v0.10.16rc6`, this function accepts an optional `type` argument.
+When `type` is set to 0, escape `str` as a URI. And these
+characters " ", "#", "%", "?", 0x00-0x1F, 0x7F-0xFF will be escaped.
+When `type` is set to 2 (which is the default), escape `str`
+as a URI component. All characters excepter ALPHA, DIGIT, "-", ".", "_", "~" will be escaped.
[Back to TOC](#nginx-api-for-lua)
@@ -6292,13 +6310,13 @@ When the `replace` is a string, then it is treated as a special template for str
```lua
local newstr, n, err = ngx.re.sub("hello, 1234", "([0-9])[0-9]", "[$0][$1]")
- if newstr then
- -- newstr == "hello, [12][1]34"
- -- n == 1
- else
+ if not newstr then
ngx.log(ngx.ERR, "error: ", err)
return
end
+
+ -- newstr == "hello, [12][1]34"
+ -- n == 1
```
where `$0` referring to the whole substring matched by the pattern and `$1` referring to the first parenthesized capturing substring.
@@ -6308,8 +6326,8 @@ Curly braces can also be used to disambiguate variable names from the background
```lua
local newstr, n, err = ngx.re.sub("hello, 1234", "[0-9]", "${0}00")
- -- newstr == "hello, 100234"
- -- n == 1
+ -- newstr == "hello, 100234"
+ -- n == 1
```
Literal dollar sign characters (`$`) in the `replace` string argument can be escaped by another dollar sign, for instance,
@@ -6317,8 +6335,8 @@ Literal dollar sign characters (`$`) in the `replace` string argument can be esc
```lua
local newstr, n, err = ngx.re.sub("hello, 1234", "[0-9]", "$$")
- -- newstr == "hello, $234"
- -- n == 1
+ -- newstr == "hello, $234"
+ -- n == 1
```
Do not use backlashes to escape dollar signs; it will not work as expected.
@@ -6330,9 +6348,10 @@ When the `replace` argument is of type "function", then it will be invoked with
local func = function (m)
return "[" .. m[0] .. "][" .. m[1] .. "]"
end
+
local newstr, n, err = ngx.re.sub("hello, 1234", "( [0-9] ) [0-9]", func, "x")
- -- newstr == "hello, [12][1]34"
- -- n == 1
+ -- newstr == "hello, [12][1]34"
+ -- n == 1
```
The dollar sign characters in the return value of the `replace` function argument are not special at all.
@@ -6357,13 +6376,13 @@ Here is some examples:
```lua
local newstr, n, err = ngx.re.gsub("hello, world", "([a-z])[a-z]+", "[$0,$1]", "i")
- if newstr then
- -- newstr == "[hello,h], [world,w]"
- -- n == 2
- else
+ if not newstr then
ngx.log(ngx.ERR, "error: ", err)
return
end
+
+ -- newstr == "[hello,h], [world,w]"
+ -- n == 2
```
```lua
@@ -6372,8 +6391,8 @@ Here is some examples:
return "[" .. m[0] .. "," .. m[1] .. "]"
end
local newstr, n, err = ngx.re.gsub("hello, world", "([a-z])[a-z]+", func, "i")
- -- newstr == "[hello,h], [world,w]"
- -- n == 2
+ -- newstr == "[hello,h], [world,w]"
+ -- n == 2
```
This method requires the PCRE library enabled in Nginx ([Known Issue With Special Escaping Sequences](#special-escaping-sequences)).
@@ -6548,6 +6567,10 @@ The optional `flags` argument specifies a user flags value associated with the e
When it fails to allocate memory for the current key-value item, then `set` will try removing existing items in the storage according to the Least-Recently Used (LRU) algorithm. Note that, LRU takes priority over expiration time here. If up to tens of existing items have been removed and the storage left is still insufficient (either due to the total capacity limit specified by [lua_shared_dict](#lua_shared_dict) or memory segmentation), then the `err` return value will be `no memory` and `success` will be `false`.
+If the sizes of items in the dictionary are not multiples or even powers of a certain value (like 2), it is easier to encounter `no memory` error because of memory fragmentation. It is recommended to use different dictionaries for different sizes of items.
+
+When you encounter `no memory` error, you can also evict more least-recently-used items by retrying this method call more times to to make room for the current item.
+
If this method succeeds in storing the current item by forcibly removing other not-yet-expired items in the dictionary via LRU, the `forcible` return value will be `true`. If it stores the item without forcibly removing other valid items, then the return value `forcible` will be `false`.
The first argument to this method must be the dictionary object itself, for example,
@@ -7074,6 +7097,9 @@ Since the `v0.7.18` release, connecting to a datagram unix domain socket file is
ngx.say("failed to connect to the datagram unix domain socket: ", err)
return
end
+
+ -- do something after connect
+ -- such as sock:send or sock:receive
```
assuming the datagram service is listening on the unix domain socket file `/tmp/some-datagram-service.sock` and the client socket will use the "autobind" feature on Linux.
@@ -7278,6 +7304,9 @@ Connecting to a Unix Domain Socket file is also possible:
ngx.say("failed to connect to the memcached unix domain socket: ", err)
return
end
+
+ -- do something after connect
+ -- such as sock:send or sock:receive
```
assuming memcached (or something else) is listening on the unix domain socket file `/tmp/memcached.sock`.
@@ -8156,6 +8185,8 @@ Here is a simple example:
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
+
+ -- other job in log_by_lua_block
}
}
```
@@ -8176,6 +8207,8 @@ One can also create infinite re-occurring timers, for instance, a timer getting
ngx.log(ngx.ERR, "failed to create the timer: ", err)
return
end
+
+ -- do something in timer
end
local ok, err = ngx.timer.at(delay, handler)
@@ -8183,6 +8216,8 @@ One can also create infinite re-occurring timers, for instance, a timer getting
ngx.log(ngx.ERR, "failed to create the timer: ", err)
return
end
+
+ -- do other jobs
```
It is recommended, however, to use the [ngx.timer.every](#ngxtimerevery) API function
diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki
index 97f7a4a0ea..0a9ee4b734 100644
--- a/doc/HttpLuaModule.wiki
+++ b/doc/HttpLuaModule.wiki
@@ -1224,6 +1224,8 @@ This hook is often used to create per-worker reoccurring timers (via the [[#ngx.
return
end
end
+
+ -- do something in timer
end
local hdl, err = new_timer(delay, check)
@@ -1231,6 +1233,8 @@ This hook is often used to create per-worker reoccurring timers (via the [[#ngx.
log(ERR, "failed to create timer: ", err)
return
end
+
+ -- other job in init_worker_by_lua
';
@@ -3344,7 +3348,7 @@ in gzipped responses that cannot be handled properly in Lua code. Original reque
When the body
option is not specified and the always_forward_body
option is false (the default value), the POST
and PUT
subrequests will inherit the request bodies of the parent request (if any).
-There is a hard-coded upper limit on the number of concurrent subrequests possible for every main request. In older versions of Nginx, the limit was 50
concurrent subrequests and in more recent versions, Nginx 1.1.x
onwards, this was increased to 200
concurrent subrequests. When this limit is exceeded, the following error message is added to the error.log
file:
+There is a hard-coded upper limit on the number of subrequests possible for every main request. In older versions of Nginx, the limit was 50
concurrent subrequests and in more recent versions, Nginx 1.9.5
onwards, the same limit is changed to limit the depth of recursive subrequests. When this limit is exceeded, the following error message is added to the error.log
file:
[error] 13983#0: *1 subrequests cycle while processing "/uri"
@@ -3787,7 +3791,12 @@ or a Lua table holding the query arguments' key-value pairs, as in
ngx.req.set_uri_args({ a = 3, b = "hello world" })
-where in the latter case, this method will escape argument keys and values according to the URI escaping rule.
+In the former case, i.e., when the whole query-string is provided directly,
+the input Lua string should already be well-formed with the URI encoding.
+For security considerations, this method will automatically escape any control and
+whitespace characters (ASCII code 0x00 ~ 0x32 and 0x7F) in the Lua string.
+
+In the latter case, this method will escape argument keys and values according to the URI escaping rule.
Multi-value arguments are also supported:
@@ -4075,6 +4084,11 @@ The __index
metamethod will not be added when the raw
Set the current request's request header named header_name
to value header_value
, overriding any existing ones.
+The input Lua string `header_name` and `header_value` should already be well-formed with the URI encoding.
+For security considerations, this method will automatically escape " ", """, "(", ")", ",", "/", ":", ";", "?",
+"<", "=", ">", "?", "@", "[", "]", "\", "{", "}", 0x00-0x1F, 0x7F-0xFF in `header_name` and automatically escape
+"0x00-0x08, 0x0A-0x0F, 0x7F in `header_value`.
+
By default, all the subrequests subsequently initiated by [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]] will inherit the new header.
Here is an example of setting the Content-Type
header:
@@ -4672,11 +4686,15 @@ This method was introduced in the 0.5.0rc30
release.
== ngx.escape_uri ==
-'''syntax:''' ''newstr = ngx.escape_uri(str)''
+'''syntax:''' ''newstr = ngx.escape_uri(str, type?)''
'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*''
-Escape str
as a URI component.
+Since `v0.10.16rc6`, this function accepts an optional type
argument.
+When type
is set to 0, escape str
as a URI. And these
+characters " ", "#", "%", "?", 0x00-0x1F, 0x7F-0xFF will be escaped.
+When type
is set to 2 (which is the default), escape str
+as a URI component. All characters excepter ALPHA, DIGIT, "-", ".", "_", "~" will be escaped.
== ngx.unescape_uri ==
@@ -5296,13 +5314,13 @@ When the replace
is a string, then it is treated as a special templ
local newstr, n, err = ngx.re.sub("hello, 1234", "([0-9])[0-9]", "[$0][$1]")
- if newstr then
- -- newstr == "hello, [12][1]34"
- -- n == 1
- else
+ if not newstr then
ngx.log(ngx.ERR, "error: ", err)
return
end
+
+ -- newstr == "hello, [12][1]34"
+ -- n == 1
where $0
referring to the whole substring matched by the pattern and $1
referring to the first parenthesized capturing substring.
@@ -5311,16 +5329,16 @@ Curly braces can also be used to disambiguate variable names from the background
local newstr, n, err = ngx.re.sub("hello, 1234", "[0-9]", "${0}00")
- -- newstr == "hello, 100234"
- -- n == 1
+ -- newstr == "hello, 100234"
+ -- n == 1
Literal dollar sign characters ($
) in the replace
string argument can be escaped by another dollar sign, for instance,
local newstr, n, err = ngx.re.sub("hello, 1234", "[0-9]", "$$")
- -- newstr == "hello, $234"
- -- n == 1
+ -- newstr == "hello, $234"
+ -- n == 1
Do not use backlashes to escape dollar signs; it will not work as expected.
@@ -5331,9 +5349,10 @@ When the replace
argument is of type "function", then it will be in
local func = function (m)
return "[" .. m[0] .. "][" .. m[1] .. "]"
end
+
local newstr, n, err = ngx.re.sub("hello, 1234", "( [0-9] ) [0-9]", func, "x")
- -- newstr == "hello, [12][1]34"
- -- n == 1
+ -- newstr == "hello, [12][1]34"
+ -- n == 1
The dollar sign characters in the return value of the replace
function argument are not special at all.
@@ -5354,13 +5373,13 @@ Here is some examples:
local newstr, n, err = ngx.re.gsub("hello, world", "([a-z])[a-z]+", "[$0,$1]", "i")
- if newstr then
- -- newstr == "[hello,h], [world,w]"
- -- n == 2
- else
+ if not newstr then
ngx.log(ngx.ERR, "error: ", err)
return
end
+
+ -- newstr == "[hello,h], [world,w]"
+ -- n == 2
@@ -5368,8 +5387,8 @@ Here is some examples:
return "[" .. m[0] .. "," .. m[1] .. "]"
end
local newstr, n, err = ngx.re.gsub("hello, world", "([a-z])[a-z]+", func, "i")
- -- newstr == "[hello,h], [world,w]"
- -- n == 2
+ -- newstr == "[hello,h], [world,w]"
+ -- n == 2
This method requires the PCRE library enabled in Nginx ([[#Special Escaping Sequences|Known Issue With Special Escaping Sequences]]).
@@ -5528,6 +5547,10 @@ The optional flags
argument specifies a user flags value associated
When it fails to allocate memory for the current key-value item, then set
will try removing existing items in the storage according to the Least-Recently Used (LRU) algorithm. Note that, LRU takes priority over expiration time here. If up to tens of existing items have been removed and the storage left is still insufficient (either due to the total capacity limit specified by [[#lua_shared_dict|lua_shared_dict]] or memory segmentation), then the err
return value will be no memory
and success
will be false
.
+If the sizes of items in the dictionary are not multiples or even powers of a certain value (like 2), it is easier to encounter no memory
error because of memory fragmentation. It is recommended to use different dictionaries for different sizes of items.
+
+When you encounter no memory
error, you can also evict more least-recently-used items by retrying this method call more times to to make room for the current item.
+
If this method succeeds in storing the current item by forcibly removing other not-yet-expired items in the dictionary via LRU, the forcible
return value will be true
. If it stores the item without forcibly removing other valid items, then the return value forcible
will be false
.
The first argument to this method must be the dictionary object itself, for example,
@@ -5984,6 +6007,9 @@ Since the v0.7.18
release, connecting to a datagram unix domain soc
ngx.say("failed to connect to the datagram unix domain socket: ", err)
return
end
+
+ -- do something after connect
+ -- such as sock:send or sock:receive
assuming the datagram service is listening on the unix domain socket file /tmp/some-datagram-service.sock
and the client socket will use the "autobind" feature on Linux.
@@ -6163,6 +6189,9 @@ Connecting to a Unix Domain Socket file is also possible:
ngx.say("failed to connect to the memcached unix domain socket: ", err)
return
end
+
+ -- do something after connect
+ -- such as sock:send or sock:receive
assuming memcached (or something else) is listening on the unix domain socket file /tmp/memcached.sock
.
@@ -6972,6 +7001,8 @@ Here is a simple example:
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
+
+ -- other job in log_by_lua_block
}
}
@@ -6991,6 +7022,8 @@ One can also create infinite re-occurring timers, for instance, a timer getting
ngx.log(ngx.ERR, "failed to create the timer: ", err)
return
end
+
+ -- do something in timer
end
local ok, err = ngx.timer.at(delay, handler)
@@ -6998,6 +7031,8 @@ One can also create infinite re-occurring timers, for instance, a timer getting
ngx.log(ngx.ERR, "failed to create the timer: ", err)
return
end
+
+ -- do other jobs
It is recommended, however, to use the [[#ngx.timer.every|ngx.timer.every]] API function
diff --git a/src/ngx_http_lua_args.c b/src/ngx_http_lua_args.c
index 6da3759212..7b7d5eae6c 100644
--- a/src/ngx_http_lua_args.c
+++ b/src/ngx_http_lua_args.c
@@ -19,6 +19,66 @@ static int ngx_http_lua_ngx_req_set_uri_args(lua_State *L);
static int ngx_http_lua_ngx_req_get_post_args(lua_State *L);
+uintptr_t
+ngx_http_lua_escape_args(u_char *dst, u_char *src, size_t size)
+{
+ ngx_uint_t n;
+ static u_char hex[] = "0123456789ABCDEF";
+
+ /* %00-%20 %7F*/
+
+ static uint32_t escape[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x00000001, /* 0000 0000 0000 0000 0000 0000 0000 0001 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ };
+
+ if (dst == NULL) {
+
+ /* find the number of the characters to be escaped */
+
+ n = 0;
+
+ while (size) {
+ if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
+ n++;
+ }
+ src++;
+ size--;
+ }
+
+ return (uintptr_t) n;
+ }
+
+ while (size) {
+ if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
+ *dst++ = '%';
+ *dst++ = hex[*src >> 4];
+ *dst++ = hex[*src & 0xf];
+ src++;
+
+ } else {
+ *dst++ = *src++;
+ }
+ size--;
+ }
+
+ return (uintptr_t) dst;
+}
+
+
static int
ngx_http_lua_ngx_req_set_uri_args(lua_State *L)
{
@@ -27,6 +87,7 @@ ngx_http_lua_ngx_req_set_uri_args(lua_State *L)
const char *msg;
size_t len;
u_char *p;
+ uintptr_t escape;
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting 1 argument but seen %d",
@@ -42,7 +103,6 @@ ngx_http_lua_ngx_req_set_uri_args(lua_State *L)
switch (lua_type(L, 1)) {
case LUA_TNUMBER:
- case LUA_TSTRING:
p = (u_char *) lua_tolstring(L, 1, &len);
args.data = ngx_palloc(r->pool, len);
@@ -55,6 +115,32 @@ ngx_http_lua_ngx_req_set_uri_args(lua_State *L)
args.len = len;
break;
+ case LUA_TSTRING:
+ p = (u_char *) lua_tolstring(L, 1, &len);
+
+ escape = ngx_http_lua_escape_args(NULL, p, len);
+ if (escape > 0) {
+ args.len = len + 2 * escape;
+ args.data = ngx_palloc(r->pool, args.len);
+ if (args.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_lua_escape_args(args.data, p, len);
+
+ } else {
+ args.data = ngx_palloc(r->pool, len);
+ if (args.data == NULL) {
+ return luaL_error(L, "no memory");
+ }
+
+ ngx_memcpy(args.data, p, len);
+
+ args.len = len;
+ }
+
+ break;
+
case LUA_TTABLE:
ngx_http_lua_process_args_option(r, L, 1, &args);
@@ -64,7 +150,7 @@ ngx_http_lua_ngx_req_set_uri_args(lua_State *L)
default:
msg = lua_pushfstring(L, "string, number, or table expected, "
- "but got %s", luaL_typename(L, 2));
+ "but got %s", luaL_typename(L, 1));
return luaL_argerror(L, 1, msg);
}
diff --git a/src/ngx_http_lua_headers_in.c b/src/ngx_http_lua_headers_in.c
index 34f2fb667f..713818efc2 100644
--- a/src/ngx_http_lua_headers_in.c
+++ b/src/ngx_http_lua_headers_in.c
@@ -653,16 +653,18 @@ ngx_http_lua_set_input_header(ngx_http_request_t *r, ngx_str_t key,
{
ngx_http_lua_header_val_t hv;
ngx_http_lua_set_header_t *handlers = ngx_http_lua_set_handlers;
-
+ ngx_int_t rc;
ngx_uint_t i;
dd("set header value: %.*s", (int) value.len, value.data);
- if (ngx_http_lua_check_unsafe_string(r, key.data, key.len,
- "header name") != NGX_OK
- || ngx_http_lua_check_unsafe_string(r, value.data, value.len,
- "header value") != NGX_OK)
- {
+ rc = ngx_http_lua_copy_escaped_header(r, &key, 1);
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ rc = ngx_http_lua_copy_escaped_header(r, &value, 0);
+ if (rc != NGX_OK) {
return NGX_ERROR;
}
diff --git a/src/ngx_http_lua_headers_out.c b/src/ngx_http_lua_headers_out.c
index a0a9012643..a3b0f9e90e 100644
--- a/src/ngx_http_lua_headers_out.c
+++ b/src/ngx_http_lua_headers_out.c
@@ -491,11 +491,11 @@ ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,
dd("set header value: %.*s", (int) value.len, value.data);
- if (ngx_http_lua_check_unsafe_string(r, key.data, key.len,
- "header name") != NGX_OK
- || ngx_http_lua_check_unsafe_string(r, value.data, value.len,
- "header value") != NGX_OK)
- {
+ if (ngx_http_lua_copy_escaped_header(r, &key, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_lua_copy_escaped_header(r, &value, 0) != NGX_OK) {
return NGX_ERROR;
}
diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c
index 5303ae76f8..9816d86441 100644
--- a/src/ngx_http_lua_module.c
+++ b/src/ngx_http_lua_module.c
@@ -230,10 +230,10 @@ static ngx_command_t ngx_http_lua_cmds[] = {
(void *) ngx_http_lua_init_worker_by_file },
#if defined(NDK) && NDK
- /* set_by_lua $res { inline Lua code } [$arg1 [$arg2 [...]]] */
+ /* set_by_lua_block $res { inline Lua code } */
{ ngx_string("set_by_lua_block"),
NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
- |NGX_CONF_1MORE|NGX_CONF_BLOCK,
+ |NGX_CONF_TAKE1|NGX_CONF_BLOCK,
ngx_http_lua_set_by_lua_block,
NGX_HTTP_LOC_CONF_OFFSET,
0,
diff --git a/src/ngx_http_lua_string.c b/src/ngx_http_lua_string.c
index 1bd4f2d99c..a94e181745 100644
--- a/src/ngx_http_lua_string.c
+++ b/src/ngx_http_lua_string.c
@@ -433,17 +433,18 @@ ngx_http_lua_ffi_unescape_uri(const u_char *src, size_t len, u_char *dst)
size_t
-ngx_http_lua_ffi_uri_escaped_length(const u_char *src, size_t len)
+ngx_http_lua_ffi_uri_escaped_length(const u_char *src, size_t len,
+ int type)
{
- return len + 2 * ngx_http_lua_escape_uri(NULL, (u_char *) src, len,
- NGX_ESCAPE_URI_COMPONENT);
+ return len + 2 * ngx_http_lua_escape_uri(NULL, (u_char *) src, len, type);
}
void
-ngx_http_lua_ffi_escape_uri(const u_char *src, size_t len, u_char *dst)
+ngx_http_lua_ffi_escape_uri(const u_char *src, size_t len, u_char *dst,
+ int type)
{
- ngx_http_lua_escape_uri(dst, (u_char *) src, len, NGX_ESCAPE_URI_COMPONENT);
+ ngx_http_lua_escape_uri(dst, (u_char *) src, len, type);
}
diff --git a/src/ngx_http_lua_uri.c b/src/ngx_http_lua_uri.c
index 569cb50270..b605bf2b89 100644
--- a/src/ngx_http_lua_uri.c
+++ b/src/ngx_http_lua_uri.c
@@ -55,10 +55,6 @@ ngx_http_lua_ngx_req_set_uri(lua_State *L)
return luaL_error(L, "attempt to use zero-length uri");
}
- if (ngx_http_lua_check_unsafe_string(r, p, len, "uri") != NGX_OK) {
- return luaL_error(L, "attempt to set unsafe uri");
- }
-
if (n == 2) {
luaL_checktype(L, 2, LUA_TBOOLEAN);
diff --git a/src/ngx_http_lua_util.c b/src/ngx_http_lua_util.c
index 983f476bb4..272906cb96 100644
--- a/src/ngx_http_lua_util.c
+++ b/src/ngx_http_lua_util.c
@@ -1892,13 +1892,13 @@ ngx_http_lua_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
- 0xfc00886d, /* 1111 1100 0000 0000 1000 1000 0110 1101 */
+ 0x80000029, /* 1000 0000 0000 0000 0000 0000 0010 1001 */
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
- 0x78000000, /* 0111 1000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
/* ~}| {zyx wvut srqp onml kjih gfed cba` */
- 0xa8000000, /* 1010 1000 0000 0000 0000 0000 0000 0000 */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
@@ -2008,8 +2008,52 @@ ngx_http_lua_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
/* mail_auth is the same as memcached */
+ /* " ", """, "(", ")", ",", "/", ":", ";", "?",
+ * "<", "=", ">", "?", "@", "[", "]", "\", "{",
+ * "}", %00-%1F, %7F-%FF
+ */
+
+ static uint32_t header_name[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0xfc009305, /* 1111 1100 0000 0000 1001 0011 0000 0101 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x38000001, /* 0011 1000 0000 0000 0000 0000 0000 0001 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0xa8000000, /* 1010 1000 0000 0000 0000 0000 0000 0000 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+ /* "%00-%08, %0A-%0F, %7F */
+
+ static uint32_t header_value[] = {
+ 0xfffffdff, /* 1111 1111 1111 1111 1111 1101 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ };
+
static uint32_t *map[] =
- { uri, args, uri_component, html, refresh, memcached, memcached };
+ { uri, args, uri_component, html, refresh, memcached, memcached,
+ header_name, header_value };
escape = map[type];
@@ -4328,4 +4372,38 @@ ngx_http_lua_escape_log(u_char *dst, u_char *src, size_t size)
}
+ngx_int_t
+ngx_http_lua_copy_escaped_header(ngx_http_request_t *r,
+ ngx_str_t *dst, int is_name)
+{
+ size_t escape;
+ size_t len;
+ u_char *data;
+ int type;
+
+ type = is_name
+ ? NGX_HTTP_LUA_ESCAPE_HEADER_NAME : NGX_HTTP_LUA_ESCAPE_HEADER_VALUE;
+
+ data = dst->data;
+ len = dst->len;
+
+ escape = ngx_http_lua_escape_uri(NULL, data, len, type);
+ if (escape > 0) {
+ /*
+ * we allocate space for the trailling '\0' char here because nginx
+ * header values must be null-terminated
+ */
+ dst->data = ngx_palloc(r->pool, len + 2 * escape + 1);
+ if (dst->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_lua_escape_uri(dst->data, data, len, type);
+ dst->len = len + 2 * escape;
+ dst->data[dst->len] = '\0';
+ }
+
+ return NGX_OK;
+}
+
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h
index a0ab2de357..92f3ba4737 100644
--- a/src/ngx_http_lua_util.h
+++ b/src/ngx_http_lua_util.h
@@ -27,6 +27,9 @@
# define NGX_HTTP_SWITCHING_PROTOCOLS 101
#endif
+#define NGX_HTTP_LUA_ESCAPE_HEADER_NAME 7
+
+#define NGX_HTTP_LUA_ESCAPE_HEADER_VALUE 8
/* key in Lua vm registry for all the "ngx.ctx" tables */
#define ngx_http_lua_ctx_tables_key "ngx_lua_ctx_tables"
@@ -169,6 +172,9 @@ void ngx_http_lua_unescape_uri(u_char **dst, u_char **src, size_t size,
uintptr_t ngx_http_lua_escape_uri(u_char *dst, u_char *src,
size_t size, ngx_uint_t type);
+ngx_int_t ngx_http_lua_copy_escaped_header(ngx_http_request_t *r,
+ ngx_str_t *dst, int is_name);
+
void ngx_http_lua_inject_req_api(ngx_log_t *log, lua_State *L);
void ngx_http_lua_process_args_option(ngx_http_request_t *r,
diff --git a/t/006-escape.t b/t/006-escape.t
index a21d3cb509..7c357eefe8 100644
--- a/t/006-escape.t
+++ b/t/006-escape.t
@@ -4,7 +4,7 @@ use Test::Nginx::Socket::Lua;
repeat_each(2);
-plan tests => repeat_each() * (blocks() * 2 + 2);
+plan tests => repeat_each() * (blocks() * 2 + 4);
no_long_string();
@@ -197,3 +197,100 @@ GET /lua
%2C%24%40%7C%60
--- no_error_log
[error]
+
+
+
+=== TEST 15: escape type argument
+--- config
+ location /lua {
+ content_by_lua_block {
+ ngx.say(ngx.escape_uri("https://www.google.com", 0))
+ ngx.say(ngx.escape_uri("https://www.google.com/query?q=test", 0))
+ ngx.say(ngx.escape_uri("https://www.google.com/query?\r\nq=test", 0))
+ ngx.say(ngx.escape_uri("-_.~!*'();:@&=+$,/?#", 0))
+ ngx.say(ngx.escape_uri("<>[]{}\\\" ", 0))
+ }
+ }
+--- request
+GET /lua
+--- response_body
+https://www.google.com
+https://www.google.com/query%3Fq=test
+https://www.google.com/query%3F%0D%0Aq=test
+-_.~!*'();:@&=+$,/%3F%23
+<>[]{}\"%20
+--- no_error_log
+[error]
+
+
+
+=== TEST 16: escape type argument
+--- config
+ location /lua {
+ content_by_lua_block {
+ ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 0))
+ ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 1))
+ ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 2))
+ ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 3))
+ ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 4))
+ ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 5))
+ ngx.say(ngx.escape_uri("https://www.google.com/?t=abc@ :", 6))
+ }
+ }
+--- request
+GET /lua
+--- response_body
+https://www.google.com/%3Ft=abc@%20:
+https://www.google.com/%3Ft=abc@%20:
+https%3A%2F%2Fwww.google.com%2F%3Ft%3Dabc%40%20%3A
+https://www.google.com/?t=abc@%20:
+https://www.google.com/?t=abc@%20:
+https://www.google.com/?t=abc@%20:
+https://www.google.com/?t=abc@%20:
+--- no_error_log
+[error]
+
+
+
+=== TEST 17: escape type out of range
+--- config
+ location /lua {
+ content_by_lua_block {
+ ngx.say(ngx.escape_uri("https://www.google.com", -1))
+ }
+ }
+--- request
+GET /lua
+--- error_code: 500
+--- error_log eval
+qr/\[error\] \d+#\d+: \*\d+ lua entry thread aborted: runtime error: "type" \-1 out of range/
+
+
+
+=== TEST 18: escape type out of range
+--- config
+ location /lua {
+ content_by_lua_block {
+ ngx.say(ngx.escape_uri("https://www.google.com", 10))
+ }
+ }
+--- request
+GET /lua
+--- error_code: 500
+--- error_log eval
+qr/\[error\] \d+#\d+: \*\d+ lua entry thread aborted: runtime error: "type" 10 out of range/
+
+
+
+=== TEST 19: escape type not integer
+--- config
+ location /lua {
+ content_by_lua_block {
+ ngx.say(ngx.escape_uri("https://www.google.com", true))
+ }
+ }
+--- request
+GET /lua
+--- error_code: 500
+--- error_log eval
+qr/\[error\] \d+#\d+: \*\d+ lua entry thread aborted: runtime error: "type" is not number/
diff --git a/t/016-resp-header.t b/t/016-resp-header.t
index 3d38d8f21b..febd276fef 100644
--- a/t/016-resp-header.t
+++ b/t/016-resp-header.t
@@ -8,7 +8,7 @@ use Test::Nginx::Socket::Lua;
repeat_each(2);
-plan tests => repeat_each() * (blocks() * 3 + 80);
+plan tests => repeat_each() * (blocks() * 3 + 77);
#no_diff();
no_long_string();
@@ -51,7 +51,7 @@ Content-Type: text/html
-=== TEST 3: set response content-type header
+=== TEST 3: set response content-length header
--- config
location /read {
content_by_lua '
@@ -1976,14 +1976,12 @@ Content-Type: application/json
}
--- request
GET /t
---- error_code: 500
--- response_headers
-header:
+header: value%0Dfoo:bar%0Abar:foo
foo:
bar:
---- error_log
-unsafe byte "0xd" in header value "value\x0Dfoo:bar\x0Abar:foo"
-failed to set header
+--- no_error_log
+[error]
@@ -1997,14 +1995,12 @@ failed to set header
}
--- request
GET /t
---- error_code: 500
--- response_headers
-header:
+header: value%0Afoo:bar%0Dbar:foo
foo:
bar:
---- error_log
-unsafe byte "0xa" in header value "value\x0Afoo:bar\x0Dbar:foo"
-failed to set header
+--- no_error_log
+[error]
@@ -2018,14 +2014,13 @@ failed to set header
}
--- request
GET /t
---- error_code: 500
--- response_headers
+header%3A%20value%0Dfoo%3Abar%0Abar%3Afoo: xx
header:
foo:
bar:
---- error_log
-unsafe byte "0xd" in header name "header: value\x0Dfoo:bar\x0Abar:foo"
-failed to set header
+--- no_error_log
+[error]
@@ -2039,14 +2034,13 @@ failed to set header
}
--- request
GET /t
---- error_code: 500
--- response_headers
+header%3A%20value%0Afoo%3Abar%0Dbar%3Afoo: xx
header:
foo:
bar:
---- error_log
-unsafe byte "0xa" in header name "header: value\x0Afoo:bar\x0Dbar:foo"
-failed to set header
+--- no_error_log
+[error]
@@ -2060,14 +2054,13 @@ failed to set header
}
--- request
GET /t
---- error_code: 500
--- response_headers
+%0Dheader%3A%20value%0Dfoo%3Abar%0Abar%3Afoo: xx
header:
foo:
bar:
---- error_log
-unsafe byte "0xd" in header name "\x0Dheader: value\x0Dfoo:bar\x0Abar:foo"
-failed to set header
+--- no_error_log
+[error]
@@ -2081,14 +2074,13 @@ failed to set header
}
--- request
GET /t
---- error_code: 500
--- response_headers
+%0Aheader%3A%20value%0Afoo%3Abar%0Dbar%3Afoo: xx
header:
foo:
bar:
---- error_log
-unsafe byte "0xa" in header name "\x0Aheader: value\x0Afoo:bar\x0Dbar:foo"
-failed to set header
+--- no_error_log
+[error]
@@ -2105,11 +2097,10 @@ failed to set header
}
--- request
GET /t
---- error_code: 500
--- response_headers
-foo:
xx:
xxx:
---- error_log
-unsafe byte "0xa" in header value "foo\x0Axx:bar"
-failed to set header
+--- raw_response_headers_like chomp
+foo: foo%0Axx:bar\r\nfoo: bar%0Dxxx:foo\r\n
+--- no_error_log
+[error]
diff --git a/t/028-req-header.t b/t/028-req-header.t
index fbd4e9eac9..c2c58eb52e 100644
--- a/t/028-req-header.t
+++ b/t/028-req-header.t
@@ -2044,10 +2044,10 @@ new
}
--- request
GET /req-header
---- error_code: 500
---- error_log
-unsafe byte "0xd" in header name "Foo\x0Dfoo"
-failed to set header
+--- response_body
+Foo:
+--- no_error_log
+[error]
@@ -2062,10 +2062,10 @@ failed to set header
}
--- request
GET /req-header
---- error_code: 500
---- error_log
-unsafe byte "0xa" in header value "new\x0Avalue"
-failed to set header
+--- response_body
+Foo: new%0Avalue
+--- no_error_log
+[error]
@@ -2082,10 +2082,10 @@ failed to set header
}
--- request
GET /req-header
---- error_code: 500
---- error_log
-unsafe byte "0xa" in header value "new\x0Avalue"
-failed to set header
+--- response_body
+new%0Avalue, foo bar.
+--- no_error_log
+[error]
@@ -2097,12 +2097,69 @@ failed to set header
}
content_by_lua_block {
- ngx.say(table.concat(ngx.req.get_headers()["foo"], ", "), ".")
+ ngx.say(ngx.req.get_headers()["foo"])
}
}
--- request
GET /req-header
---- error_code: 500
---- error_log
-unsafe byte "0xa" in header value "\x22new\x0Avalue\x5C\x22"
-failed to set header
+--- response_body
+"new%0Avalue\"
+--- no_error_log
+[error]
+
+
+
+=== TEST 66: add request headers with '\r\n'
+--- config
+ location /bar {
+ access_by_lua_block {
+ ngx.req.set_header("Foo\r", "123\r\n")
+ }
+ proxy_pass http://127.0.0.1:$server_port/foo;
+ }
+
+ location = /foo {
+ echo $echo_client_request_headers;
+ }
+--- request
+GET /bar
+--- response_body_like chomp
+\bFoo%0D: 123%0D%0A\b
+
+
+
+=== TEST 67: add request headers with '\0'
+--- config
+ location /bar {
+ access_by_lua_block {
+ ngx.req.set_header("Foo", "\0")
+ }
+ proxy_pass http://127.0.0.1:$server_port/foo;
+ }
+
+ location = /foo {
+ echo $echo_client_request_headers;
+ }
+--- request
+GET /bar
+--- response_body_like chomp
+\bFoo: %00\b
+
+
+
+=== TEST 68: add request headers with '中文'
+--- config
+ location /bar {
+ access_by_lua_block {
+ ngx.req.set_header("Foo中文", "ab中文a")
+ }
+ proxy_pass http://127.0.0.1:$server_port/foo;
+ }
+
+ location = /foo {
+ echo $echo_client_request_headers;
+ }
+--- request
+GET /bar
+--- response_body_like chomp
+\bFoo%E4%B8%AD%E6%96%87: ab中文a\r\n
diff --git a/t/030-uri-args-with-ctrl.t b/t/030-uri-args-with-ctrl.t
new file mode 100644
index 0000000000..e74da38eeb
--- /dev/null
+++ b/t/030-uri-args-with-ctrl.t
@@ -0,0 +1,137 @@
+# vim:set ft= ts=4 sw=4 et fdm=marker:
+use Test::Nginx::Socket::Lua;
+
+log_level('warn');
+
+repeat_each(2);
+
+plan tests => repeat_each() * (blocks() * 2 + 3);
+
+no_root_location();
+
+no_long_string();
+run_tests();
+
+__DATA__
+
+=== TEST 1: rewrite args (string with \r)
+--- config
+ location /foo {
+ rewrite_by_lua_block {
+ ngx.req.set_uri_args("a\rb")
+ }
+ proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo;
+ }
+ location /echo {
+ content_by_lua_block {
+ ngx.say(ngx.var.request_uri);
+ }
+ }
+--- request
+GET /foo?world
+--- error_code: 200
+--- response_body
+/echo?a%0Db
+
+
+
+=== TEST 2: rewrite args (string with \n)
+--- config
+ location /foo {
+ rewrite_by_lua_block {
+ ngx.req.set_uri_args("a\nb")
+ }
+ proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo;
+ }
+ location /echo {
+ content_by_lua_block {
+ ngx.say(ngx.var.request_uri);
+ }
+ }
+--- request
+GET /foo?world
+--- response_body
+/echo?a%0Ab
+
+
+
+=== TEST 3: rewrite args (string with \0)
+--- config
+ location /foo {
+ rewrite_by_lua_block {
+ ngx.req.set_uri_args("a\0b")
+ }
+ proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo;
+ }
+ location /echo {
+ content_by_lua_block {
+ ngx.say(ngx.var.request_uri);
+ }
+ }
+--- request
+GET /foo?world
+--- response_body
+/echo?a%00b
+
+
+
+=== TEST 4: rewrite args (string arg with 'lang=中文')
+ngx.req.set_uri_args with string argument should be carefully encoded.
+For backward compatibility, we are allowed to pass such parameters.
+--- config
+ location /foo {
+ rewrite_by_lua_block {
+ ngx.req.set_uri_args("lang=中文")
+ }
+ content_by_lua_block {
+ ngx.say(ngx.var.arg_lang)
+ }
+ }
+--- request
+GET /foo?world
+--- response_body
+中文
+--- no_error_log
+[error]
+
+
+
+=== TEST 5: rewrite args (string arg with '语言=chinese')
+ngx.req.set_uri_args with string argument should be carefully encoded.
+For backward compatibility, we are allowed to pass such parameters.
+--- config
+ location /foo {
+ rewrite_by_lua_block {
+ ngx.req.set_uri_args("语言=chinese")
+ }
+ content_by_lua_block {
+ ngx.say(ngx.var.arg_语言)
+ }
+ }
+--- request
+GET /foo?world
+--- response_body
+chinese
+--- no_error_log
+[error]
+
+
+
+=== TEST 6: rewrite args (string arg with '语言=中文')
+ngx.req.set_uri_args with string argument should be carefully encoded.
+For backward compatibility, we are allowed to pass such parameters.
+--- config
+ location /foo {
+ rewrite_by_lua_block {
+ ngx.req.set_uri_args("语言=中文")
+ }
+ content_by_lua_block {
+ ngx.say(ngx.var.arg_语言)
+ }
+ }
+--- request
+GET /foo?world
+--- response_body
+中文
+--- no_error_log
+[error]
diff --git a/t/030-uri-args.t b/t/030-uri-args.t
index 02fe6e1042..d4cb6d17ef 100644
--- a/t/030-uri-args.t
+++ b/t/030-uri-args.t
@@ -1568,10 +1568,9 @@ args: foo=%2C%24%40%7C%60&bar=-_.!~*'()
}
--- request
GET /t
---- error_code: 500
---- error_log
-unsafe byte "0x9" in uri "/foo\x09bar"
-attempt to set unsafe uri
+--- response
+/foo bar
+--- no_error_log
@@ -1586,10 +1585,9 @@ attempt to set unsafe uri
}
--- request
GET /t
---- error_code: 500
---- error_log
-unsafe byte "0x0" in uri "\x00foo"
-attempt to set unsafe uri
+--- error_code: 200
+--- response_body eval
+qr/\0foo/
@@ -1617,3 +1615,72 @@ request_uri: /foo%20bar
uri: /foo bar
--- no_error_log
[error]
+
+
+
+=== TEST 61: set_uri_args with boolean
+--- config
+ location /bar {
+ echo $query_string;
+ }
+ location /foo {
+ #set $args 'hello';
+ rewrite_by_lua_block {
+ ngx.req.set_uri_args(true)
+ ngx.req.set_uri("/bar", true)
+ }
+ proxy_pass http://127.0.0.2:12345;
+ }
+--- request
+ GET /foo?world
+--- response_body_like: 500 Internal Server Error
+--- log_level: debug
+--- error_code: 500
+--- error_log
+bad argument #1 to 'set_uri_args' (string, number, or table expected, but got boolean)
+
+
+
+=== TEST 62: set_uri_args with nil
+--- config
+ location /bar {
+ echo $query_string;
+ }
+ location /foo {
+ #set $args 'hello';
+ rewrite_by_lua_block {
+ ngx.req.set_uri_args(nil)
+ ngx.req.set_uri("/bar", true)
+ }
+ proxy_pass http://127.0.0.2:12345;
+ }
+--- request
+ GET /foo?world
+--- response_body_like: 500 Internal Server Error
+--- log_level: debug
+--- error_code: 500
+--- error_log
+bad argument #1 to 'set_uri_args' (string, number, or table expected, but got nil)
+
+
+
+=== TEST 63: set_uri_args with userdata
+--- config
+ location /bar {
+ echo $query_string;
+ }
+ location /foo {
+ #set $args 'hello';
+ rewrite_by_lua_block {
+ ngx.req.set_uri_args(ngx.null)
+ ngx.req.set_uri("/bar", true)
+ }
+ proxy_pass http://127.0.0.2:12345;
+ }
+--- request
+ GET /foo?world
+--- response_body_like: 500 Internal Server Error
+--- log_level: debug
+--- error_code: 500
+--- error_log
+bad argument #1 to 'set_uri_args' (string, number, or table expected, but got userdata)
diff --git a/t/113-req-header-cookie.t b/t/113-req-header-cookie.t
index 3baa899c3b..944549cd74 100644
--- a/t/113-req-header-cookie.t
+++ b/t/113-req-header-cookie.t
@@ -8,7 +8,7 @@ use Test::Nginx::Socket::Lua;
repeat_each(2);
-plan tests => repeat_each() * (4 * blocks());
+plan tests => repeat_each() * (3 * blocks() + 6);
#no_diff();
no_long_string();
@@ -263,9 +263,10 @@ Cookie: boo=123; foo=bar
}
--- request
GET /t
---- error_code: 500
---- error_log
-unsafe byte "0xa" in header value "boo=123\x0Afoo"
-failed to set header
+--- response_body
+Cookie foo: bar%0Dbar
+Cookie baz:
+Cookie boo: 123%0Afoo
+Cookie: boo=123%0Afoo; foo=bar%0Dbar
--- no_error_log
-[crit]
+[error]
diff --git a/t/162-static-module-location.t b/t/162-static-module-location.t
new file mode 100644
index 0000000000..1dce330e77
--- /dev/null
+++ b/t/162-static-module-location.t
@@ -0,0 +1,95 @@
+use Test::Nginx::Socket::Lua;
+
+repeat_each(2);
+
+plan tests => repeat_each() * (blocks() * 3);
+
+our $HtmlDir = html_dir;
+
+no_long_string();
+run_tests();
+
+__DATA__
+
+=== TEST 1: decoded url contains '\0' and '\r\n'
+--- config
+ server_tokens off;
+ location = /t {
+ rewrite_by_lua_block {
+ ngx.req.read_body();
+ local args, _ = ngx.req.get_post_args();
+ ngx.req.set_uri(args["url"], true);
+ }
+ }
+--- request
+POST /t
+url=%00%0a%0dset-cookie:1234567
+--- error_code: 301
+--- response_headers
+Location: %00%0A%0Dset-cookie:1234567/
+--- response_body_like
+.*301 Moved Permanently.*
+
+
+
+=== TEST 2: uri contain chinese characters
+--- config
+ server_tokens off;
+--- user_files
+>>> t/中文/foo.txt
+Hello, world
+--- request
+GET /t/中文
+--- error_code: 301
+--- response_headers
+Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/
+--- response_body_like
+.*301 Moved Permanently.*
+
+
+
+=== TEST 3: uri contain chinese characters with args
+--- config
+ server_tokens off;
+--- user_files
+>>> t/中文/foo.txt
+Hello, world
+--- request
+GET /t/中文?q=name
+--- error_code: 301
+--- response_headers
+Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/?q=name
+--- response_body_like
+.*301 Moved Permanently.*
+
+
+
+=== TEST 4: uri already encoded
+--- config
+ server_tokens off;
+--- user_files
+>>> t/中文/foo.txt
+Hello, world
+--- request
+GET /t/%E4%B8%AD%E6%96%87
+--- error_code: 301
+--- response_headers
+Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/
+--- response_body_like
+.*301 Moved Permanently.*
+
+
+
+=== TEST 5: uri already encoded with args
+--- config
+ server_tokens off;
+--- user_files
+>>> t/中文/foo.txt
+Hello, world
+--- request
+GET /t/%E4%B8%AD%E6%96%87?q=name
+--- error_code: 301
+--- response_headers
+Location: http://localhost:1984/t/%E4%B8%AD%E6%96%87/?q=name
+--- response_body_like
+.*301 Moved Permanently.*