blob: b6e3110781b2d7fb5adba33653d5749609c5ca31 [file] [log] [blame] [raw]
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib 'lib';
use Test::Nginx::Socket::Lua;
#worker_connections(1014);
#master_on();
#workers(2);
#log_level('warn');
repeat_each(5);
plan tests => repeat_each() * (blocks() * 2 + 7);
our $HtmlDir = html_dir;
#no_diff();
#no_long_string();
run_tests();
__DATA__
=== TEST 1: gmatch
--- config
location /re {
content_by_lua '
for m in ngx.re.gmatch("hello, world", "[a-z]+") do
if m then
ngx.say(m[0])
else
ngx.say("not matched: ", m)
end
end
';
}
--- request
GET /re
--- response_body
hello
world
=== TEST 2: fail to match
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("hello, world", "[0-9]")
local m = it()
if m then ngx.say(m[0]) else ngx.say(m) end
local m = it()
if m then ngx.say(m[0]) else ngx.say(m) end
local m = it()
if m then ngx.say(m[0]) else ngx.say(m) end
';
}
--- request
GET /re
--- response_body
nil
nil
nil
=== TEST 3: match but iterate more times (not just match at the end)
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("hello, world!", "[a-z]+")
local m = it()
if m then ngx.say(m[0]) else ngx.say(m) end
local m = it()
if m then ngx.say(m[0]) else ngx.say(m) end
local m = it()
if m then ngx.say(m[0]) else ngx.say(m) end
local m = it()
if m then ngx.say(m[0]) else ngx.say(m) end
';
}
--- request
GET /re
--- response_body
hello
world
nil
nil
=== TEST 4: match but iterate more times (just matched at the end)
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("hello, world", "[a-z]+")
local m = it()
if m then ngx.say(m[0]) else ngx.say(m) end
local m = it()
if m then ngx.say(m[0]) else ngx.say(m) end
local m = it()
if m then ngx.say(m[0]) else ngx.say(m) end
local m = it()
if m then ngx.say(m[0]) else ngx.say(m) end
';
}
--- request
GET /re
--- response_body
hello
world
nil
nil
=== TEST 5: anchored match (failed)
--- config
location /re {
content_by_lua '
it = ngx.re.gmatch("hello, 1234", "([0-9]+)", "a")
ngx.say(it())
';
}
--- request
GET /re
--- response_body
nil
=== TEST 6: anchored match (succeeded)
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("12 hello 34", "[0-9]", "a")
local m = it()
ngx.say(m[0])
m = it()
ngx.say(m[0])
ngx.say(it())
';
}
--- request
GET /re
--- response_body
1
2
nil
=== TEST 7: non-anchored gmatch (without regex cache)
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("12 hello 34", "[0-9]")
local m = it()
ngx.say(m and m[0])
m = it()
ngx.say(m and m[0])
m = it()
ngx.say(m and m[0])
m = it()
ngx.say(m and m[0])
m = it()
ngx.say(m and m[0])
';
}
--- request
GET /re
--- response_body
1
2
3
4
nil
=== TEST 8: non-anchored gmatch (with regex cache)
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("12 hello 34", "[0-9]", "o")
local m = it()
ngx.say(m and m[0])
m = it()
ngx.say(m and m[0])
m = it()
ngx.say(m and m[0])
m = it()
ngx.say(m and m[0])
m = it()
ngx.say(m and m[0])
';
}
--- request
GET /re
--- response_body
1
2
3
4
nil
=== TEST 9: anchored match (succeeded)
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("12 hello 34", "[0-9]", "a")
local m = it()
ngx.say(m[0])
m = it()
ngx.say(m[0])
ngx.say(it())
';
}
--- request
GET /re
--- response_body
1
2
nil
=== TEST 10: anchored match (succeeded, set_by_lua)
--- config
location /re {
set_by_lua $res '
local it = ngx.re.gmatch("12 hello 34", "[0-9]", "a")
local m = it()
return m[0]
';
echo $res;
}
--- request
GET /re
--- response_body
1
=== TEST 11: gmatch (look-behind assertion)
--- config
location /re {
content_by_lua '
for m in ngx.re.gmatch("{foobar}, {foobaz}", "(?<=foo)ba[rz]") do
if m then
ngx.say(m[0])
else
ngx.say("not matched: ", m)
end
end
';
}
--- request
GET /re
--- response_body
bar
baz
=== TEST 12: gmatch (look-behind assertion 2)
--- config
location /re {
content_by_lua '
for m in ngx.re.gmatch("{foobarbaz}", "(?<=foo)bar|(?<=bar)baz") do
if m then
ngx.say(m[0])
else
ngx.say("not matched: ", m)
end
end
';
}
--- request
GET /re
--- response_body
bar
baz
=== TEST 13: with regex cache
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("hello, 1234", "([A-Z]+)", "io")
local m = it()
ngx.say(m and m[0])
it = ngx.re.gmatch("1234, okay", "([A-Z]+)", "io")
m = it()
ngx.say(m and m[0])
it = ngx.re.gmatch("hi, 1234", "([A-Z]+)", "o")
m = it()
ngx.say(m and m[0])
';
}
--- request
GET /re
--- stap2
F(ngx_http_lua_ngx_re_gmatch_iterator) { println("iterator") }
F(ngx_http_lua_ngx_re_gmatch_gc) { println("gc") }
F(ngx_http_lua_ngx_re_gmatch_cleanup) { println("cleanup") }
--- response_body
hello
okay
nil
=== TEST 14: exceeding regex cache max entries
--- http_config
lua_regex_cache_max_entries 2;
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("hello, 1234", "([0-9]+)", "o")
local m = it()
ngx.say(m and m[0])
it = ngx.re.gmatch("howdy, 567", "([0-9]+)", "oi")
m = it()
ngx.say(m and m[0])
it = ngx.re.gmatch("hiya, 98", "([0-9]+)", "ox")
m = it()
ngx.say(m and m[0])
';
}
--- request
GET /re
--- response_body
1234
567
98
=== TEST 15: disable regex cache completely
--- http_config
lua_regex_cache_max_entries 0;
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("hello, 1234", "([0-9]+)", "o")
local m = it()
ngx.say(m and m[0])
it = ngx.re.gmatch("howdy, 567", "([0-9]+)", "oi")
local m = it()
ngx.say(m and m[0])
it = ngx.re.gmatch("hiya, 98", "([0-9]+)", "ox")
local m = it()
ngx.say(m and m[0])
';
}
--- request
GET /re
--- response_body
1234
567
98
=== TEST 16: gmatch matched but no iterate
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("hello, world", "[a-z]+")
ngx.say("done")
';
}
--- request
GET /re
--- response_body
done
=== TEST 17: gmatch matched but only iterate once and still matches remain
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("hello, world", "[a-z]+")
local m = it()
if m then
ngx.say(m[0])
else
ngx.say("not matched")
end
';
}
--- request
GET /re
--- response_body
hello
=== TEST 18: gmatch matched but no iterate and early forced GC
--- config
location /re {
content_by_lua '
local a = {}
for i = 1, 3 do
it = ngx.re.gmatch("hello, world", "[a-z]+")
it()
collectgarbage()
table.insert(a, {"hello", "world"})
end
ngx.say("done")
';
}
--- request
GET /re
--- response_body
done
=== TEST 19: gmatch iterator used by another request
--- http_config eval
"lua_package_path '$::HtmlDir/?.lua;;';"
--- config
location /main {
content_by_lua '
package.loaded.foo = nil
local res = ngx.location.capture("/t")
if res.status == 200 then
ngx.print(res.body)
else
ngx.say("sr failed: ", res.status)
end
res = ngx.location.capture("/t")
if res.status == 200 then
ngx.print(res.body)
else
ngx.say("sr failed: ", res.status)
end
';
}
location /t {
content_by_lua '
local foo = require "foo"
local m = foo.go()
ngx.say(m and "matched" or "no")
';
}
--- user_files
>>> foo.lua
module("foo", package.seeall)
local it
function go()
if not it then
it = ngx.re.gmatch("hello, world", "[a-z]+")
end
return it()
end
--- request
GET /main
--- response_body
matched
sr failed: 500
--- error_log
attempt to use ngx.re.gmatch iterator in a request that did not create it
=== TEST 20: gmatch (empty matched string)
--- config
location /re {
content_by_lua '
for m in ngx.re.gmatch("hello", "a|") do
if m then
ngx.say("matched: [", m[0], "]")
else
ngx.say("not matched: ", m)
end
end
';
}
--- request
GET /re
--- response_body
matched: []
matched: []
matched: []
matched: []
matched: []
matched: []
=== TEST 21: gmatch with named pattern
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("1234, 1234", "(?<first>[0-9]+)")
m = it()
if m then
ngx.say(m[0])
ngx.say(m[1])
ngx.say(m["first"])
else
ngx.say("not matched!")
end
m = it()
if m then
ngx.say(m[0])
ngx.say(m[1])
ngx.say(m["first"])
else
ngx.say("not matched!")
end
';
}
--- request
GET /re
--- response_body
1234
1234
1234
1234
1234
1234
=== TEST 22: gmatch with multiple named pattern
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("1234, abcd, 1234", "(?<first>[0-9]+)|(?<second>[a-z]+)")
m = it()
if m then
ngx.say(m[0])
ngx.say(m[1])
ngx.say(m[2])
ngx.say(m["first"])
ngx.say(m["second"])
else
ngx.say("not matched!")
end
m = it()
if m then
ngx.say(m[0])
ngx.say(m[1])
ngx.say(m[2])
ngx.say(m["first"])
ngx.say(m["second"])
else
ngx.say("not matched!")
end
';
}
--- request
GET /re
--- response_body
1234
1234
nil
1234
nil
abcd
nil
abcd
nil
abcd
=== TEST 23: gmatch with duplicate named pattern w/ extraction
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("hello, 1234", "(?<first>[a-z]+), (?<first>[0-9]+)", "D")
m = it()
if m then
ngx.say(m[0])
ngx.say(m[1])
ngx.say(m[2])
ngx.say(table.concat(m.first,"-"))
else
ngx.say("not matched!")
end
m = it()
if m then
ngx.say(m[0])
ngx.say(m[1])
ngx.say(m[2])
ngx.say(table.concat(m.first,"-"))
else
ngx.say("not matched!")
end
';
}
--- request
GET /re
--- response_body
hello, 1234
hello
1234
hello-1234
not matched!
=== TEST 24: named captures are empty
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("1234", "(?<first>[a-z]*)([0-9]+)", "")
local m = it()
if m then
ngx.say(m[0])
ngx.say(m.first)
ngx.say(m[1])
ngx.say(m[2])
else
ngx.say("not matched!")
end
';
}
--- request
GET /re
--- response_body
1234
1234
=== TEST 25: named captures are empty (with regex cache)
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("1234", "(?<first>[a-z]*)([0-9]+)", "o")
local m = it()
if m then
ngx.say(m[0])
ngx.say(m.first)
ngx.say(m[1])
ngx.say(m[2])
else
ngx.say("not matched!")
end
';
}
--- request
GET /re
--- response_body
1234
1234
=== TEST 26: bad pattern
--- config
location /re {
content_by_lua '
local it, err = ngx.re.gmatch("hello\\nworld", "(abc")
if not err then
ngx.say("good")
else
ngx.say("error: ", err)
end
';
}
--- request
GET /re
--- response_body
error: pcre_compile() failed: missing ) in "(abc"
--- no_error_log
[error]
=== TEST 27: bad UTF-8
--- config
location = /t {
content_by_lua '
local target = "你好"
local regex = "你好"
-- Note the D here
local it, err = ngx.re.gmatch(string.sub(target, 1, 4), regex, "u")
if err then
ngx.say("error: ", err)
return
end
local m, err = it()
if err then
ngx.say("error: ", err)
return
end
if m then
ngx.say("matched: ", m[0])
else
ngx.say("not matched")
end
';
}
--- request
GET /t
--- response_body_like chop
error: pcre_exec\(\) failed: -10
--- no_error_log
[error]
=== TEST 28: UTF-8 mode without UTF-8 sequence checks
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("你好", ".", "U")
local m = it()
if m then
ngx.say(m[0])
else
ngx.say("not matched!")
end
';
}
--- stap
probe process("$LIBPCRE_PATH").function("pcre_compile") {
printf("compile opts: %x\n", $options)
}
probe process("$LIBPCRE_PATH").function("pcre_exec") {
printf("exec opts: %x\n", $options)
}
--- stap_out
compile opts: 800
exec opts: 2000
--- request
GET /re
--- response_body
--- no_error_log
[error]
=== TEST 29: UTF-8 mode with UTF-8 sequence checks
--- config
location /re {
content_by_lua '
local it = ngx.re.gmatch("你好", ".", "u")
local m = it()
if m then
ngx.say(m[0])
else
ngx.say("not matched!")
end
';
}
--- stap
probe process("$LIBPCRE_PATH").function("pcre_compile") {
printf("compile opts: %x\n", $options)
}
probe process("$LIBPCRE_PATH").function("pcre_exec") {
printf("exec opts: %x\n", $options)
}
--- stap_out
compile opts: 800
exec opts: 0
--- request
GET /re
--- response_body
--- no_error_log
[error]
=== TEST 30: just hit match limit
--- http_config
lua_regex_match_limit 5600;
--- config
location /re {
content_by_lua_file html/a.lua;
}
--- user_files
>>> a.lua
local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==]
s = string.rep([[ABCDEFG]], 10)
local start = ngx.now()
local it, err = ngx.re.gmatch(s, re, "o")
if not it then
ngx.say("failed to gen iterator: ", err)
return
end
local res, err = it()
--[[
ngx.update_time()
local elapsed = ngx.now() - start
ngx.say(elapsed, " sec elapsed.")
]]
if not res then
if err then
ngx.say("error: ", err)
return
end
ngx.say("failed to match")
return
end
--- request
GET /re
--- response_body
error: pcre_exec() failed: -8
=== TEST 31: just not hit match limit
--- http_config
lua_regex_match_limit 5700;
--- config
location /re {
content_by_lua_file html/a.lua;
}
--- user_files
>>> a.lua
local re = [==[(?i:([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:=|<=>|r?like|sounds\s+like|regexp)([\s'\"`´’‘\(\)]*)?\2|([\s'\"`´’‘\(\)]*)?([\d\w]+)([\s'\"`´’‘\(\)]*)?(?:!=|<=|>=|<>|<|>|\^|is\s+not|not\s+like|not\s+regexp)([\s'\"`´’‘\(\)]*)?(?!\6)([\d\w]+))]==]
s = string.rep([[ABCDEFG]], 10)
local start = ngx.now()
local it, err = ngx.re.gmatch(s, re, "o")
if not it then
ngx.say("failed to gen iterator: ", err)
return
end
res, err = it()
--[[
ngx.update_time()
local elapsed = ngx.now() - start
ngx.say(elapsed, " sec elapsed.")
]]
if not res then
if err then
ngx.say("error: ", err)
return
end
ngx.say("failed to match")
return
end
--- request
GET /re
--- response_body
failed to match