blob: 77975211c3e29bf9a752a058efa6623ee57339a7 [file] [log] [blame] [raw]
# vim:set ft= ts=4 sw=4 et fdm=marker:
use lib 'lib';
use Test::Nginx::Socket::Lua;
repeat_each(2);
plan tests => repeat_each() * (blocks() * 3 + 9);
our $HtmlDir = html_dir;
#$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;
no_long_string();
#no_diff();
#log_level 'warn';
no_shuffle();
run_tests();
__DATA__
=== TEST 1: sanity
--- config
location /t {
content_by_lua '
local sock, err = ngx.req.socket()
if sock then
ngx.say("got the request socket")
else
ngx.say("failed to get the request socket: ", err)
end
for i = 1, 3 do
local data, err, part = sock:receive(5)
if data then
ngx.say("received: ", data)
else
ngx.say("failed to receive: ", err, " [", part, "]")
end
end
';
}
--- request
POST /t
hello world
--- response_body
got the request socket
received: hello
received: worl
failed to receive: closed [d]
--- no_error_log
[error]
=== TEST 2: multipart rfc sample (just partial streaming)
--- config
location /t {
content_by_lua '
local sock, err = ngx.req.socket()
if sock then
ngx.say("got the request socket")
else
ngx.say("failed to get the request socket: ", err)
end
local boundary
local header = ngx.var.http_content_type
local m = ngx.re.match(header, [[; +boundary=(?:"(.*?)"|(\\w+))]], "jo")
if m then
boundary = m[1] or m[2]
else
ngx.say("invalid content-type header")
return
end
local read_to_boundary = sock:receiveuntil("\\r\\n--" .. boundary)
local read_line = sock:receiveuntil("\\r\\n")
local data, err, part = read_to_boundary()
if data then
ngx.say("preamble: [" .. data .. "]")
else
ngx.say("failed to read the first boundary: ", err)
return
end
local i = 1
while true do
local line, err = read_line()
if not line then
ngx.say("failed to read post-boundary line: ", err)
return
end
m = ngx.re.match(line, "--$", "jo")
if m then
ngx.say("found the end of the stream")
return
end
while true do
local line, err = read_line()
if not line then
ngx.say("failed to read part ", i, " header: ", err)
return
end
if line == "" then
-- the header part completes
break
end
ngx.say("part ", i, " header: [", line, "]")
end
local data, err, part = read_to_boundary()
if data then
ngx.say("part ", i, " body: [" .. data .. "]")
else
ngx.say("failed to read part ", i + 1, " boundary: ", err)
return
end
i = i + 1
end
';
}
--- request eval
"POST /t
This is the preamble. It is to be ignored, though it
is a handy place for mail composers to include an
explanatory note to non-MIME compliant readers.\r
--simple boundary\r
\r
This is implicitly typed plain ASCII text.
It does NOT end with a linebreak.\r
--simple boundary\r
Content-type: text/plain; charset=us-ascii\r
\r
This is explicitly typed plain ASCII text.
It DOES end with a linebreak.
\r
--simple boundary--\r
This is the epilogue. It is also to be ignored.
"
--- more_headers
Content-Type: multipart/mixed; boundary="simple boundary"
--- response_body
got the request socket
preamble: [This is the preamble. It is to be ignored, though it
is a handy place for mail composers to include an
explanatory note to non-MIME compliant readers.]
part 1 body: [This is implicitly typed plain ASCII text.
It does NOT end with a linebreak.]
part 2 header: [Content-type: text/plain; charset=us-ascii]
part 2 body: [This is explicitly typed plain ASCII text.
It DOES end with a linebreak.
]
found the end of the stream
--- no_error_log
[error]
=== TEST 3: multipart rfc sample (completely streaming)
--- config
location /t {
content_by_lua '
local sock, err = ngx.req.socket()
if sock then
ngx.say("got the request socket")
else
ngx.say("failed to get the request socket: ", err)
end
local boundary
local header = ngx.var.http_content_type
local m = ngx.re.match(header, [[; +boundary=(?:"(.*?)"|(\\w+))]], "jo")
if m then
boundary = m[1] or m[2]
else
ngx.say("invalid content-type header")
return
end
local read_to_boundary = sock:receiveuntil("\\r\\n--" .. boundary)
local read_line = sock:receiveuntil("\\r\\n")
local preamble = ""
while true do
local data, err, part = read_to_boundary(1)
if data then
preamble = preamble .. data
elseif not err then
break
else
ngx.say("failed to read the first boundary: ", err)
return
end
end
ngx.say("preamble: [" .. preamble .. "]")
local i = 1
while true do
local line, err = read_line(50)
if not line and err then
ngx.say("1: failed to read post-boundary line: ", err)
return
end
if line then
local dummy
dummy, err = read_line(1)
if err then
ngx.say("2: failed to read post-boundary line: ", err)
return
end
if dummy then
ngx.say("bad post-boundary line: ", dummy)
return
end
m = ngx.re.match(line, "--$", "jo")
if m then
ngx.say("found the end of the stream")
return
end
end
while true do
local line, err = read_line(50)
if not line and err then
ngx.say("failed to read part ", i, " header: ", err)
return
end
if line then
local line, err = read_line(1)
if line or err then
ngx.say("error")
return
end
end
if line == "" then
-- the header part completes
break
end
ngx.say("part ", i, " header: [", line, "]")
end
local body = ""
while true do
local data, err, part = read_to_boundary(1)
if data then
body = body .. data
elseif err then
ngx.say("failed to read part ", i + 1, " boundary: ", err)
return
else
break
end
end
ngx.say("part ", i, " body: [" .. body .. "]")
i = i + 1
end
';
}
--- request eval
"POST /t
This is the preamble. It is to be ignored, though it
is a handy place for mail composers to include an
explanatory note to non-MIME compliant readers.\r
--simple boundary\r
\r
This is implicitly typed plain ASCII text.
It does NOT end with a linebreak.\r
--simple boundary\r
Content-type: text/plain; charset=us-ascii\r
\r
This is explicitly typed plain ASCII text.
It DOES end with a linebreak.
\r
--simple boundary--\r
This is the epilogue. It is also to be ignored.
"
--- more_headers
Content-Type: multipart/mixed; boundary="simple boundary"
--- response_body
got the request socket
preamble: [This is the preamble. It is to be ignored, though it
is a handy place for mail composers to include an
explanatory note to non-MIME compliant readers.]
part 1 body: [This is implicitly typed plain ASCII text.
It does NOT end with a linebreak.]
part 2 header: [Content-type: text/plain; charset=us-ascii]
part 2 body: [This is explicitly typed plain ASCII text.
It DOES end with a linebreak.
]
found the end of the stream
--- no_error_log
[error]
=== TEST 4: attempt to use the req socket across request boundary
--- http_config eval
"lua_package_path '$::HtmlDir/?.lua;./?.lua';"
--- config
location /t {
content_by_lua '
local test = require "test"
test.go()
ngx.say("done")
';
}
--- user_files
>>> test.lua
module("test", package.seeall)
local sock, err
function go()
if not sock then
sock, err = ngx.req.socket()
if sock then
ngx.say("got the request socket")
else
ngx.say("failed to get the request socket: ", err)
end
else
for i = 1, 3 do
local data, err, part = sock:receive(5)
if data then
ngx.say("received: ", data)
else
ngx.say("failed to receive: ", err, " [", part, "]")
end
end
end
end
--- request
POST /t
hello world
--- response_body_like
(?:got the request socket
|failed to receive: closed [d]
)?done
--- no_error_log
[alert]
=== TEST 5: receive until on request_body - receiveuntil(1) on the last byte of the body
See https://groups.google.com/group/openresty/browse_thread/thread/43cf01da3c681aba for details
--- http_config eval
"lua_package_path '$::HtmlDir/?.lua;./?.lua';"
--- config
location /t {
content_by_lua '
local test = require "test"
test.go()
ngx.say("done")
';
}
--- user_files
>>> test.lua
module("test", package.seeall)
function go()
local sock, err = ngx.req.socket()
if sock then
ngx.say("got the request socket")
else
ngx.say("failed to get the request socket: ", err)
return
end
local data, err, part = sock:receive(56)
if data then
ngx.say("received: ", data)
else
ngx.say("failed to receive: ", err, " [", part, "]")
end
local discard_line = sock:receiveuntil('\r\n')
local data, err, part = discard_line(8192)
if data then
ngx.say("received len: ", #data)
else
ngx.say("failed to receive: ", err, " [", part, "]")
end
local data, err, part = discard_line(1)
if data then
ngx.say("received: ", data)
else
ngx.say("failed to receive: ", err, " [", part, "]")
end
end
--- request
POST /t
-----------------------------820127721219505131303151179################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################$
--- response_body
got the request socket
received: -----------------------------820127721219505131303151179
received len: 8192
received: $
done
--- no_error_log
[error]
--- timeout: 10
=== TEST 6: pipelined POST requests
--- http_config eval
"lua_package_path '$::HtmlDir/?.lua;./?.lua';"
--- config
location /t {
content_by_lua '
local test = require "test"
test.go()
ngx.say("done")
';
}
--- user_files
>>> test.lua
module("test", package.seeall)
function go()
local sock, err = ngx.req.socket()
if sock then
ngx.say("got the request socket")
else
ngx.say("failed to get the request socket: ", err)
return
end
while true do
local data, err, part = sock:receive(4)
if data then
ngx.say("received: ", data)
else
ngx.say("failed to receive: ", err, " [", part, "]")
return
end
end
end
--- pipelined_requests eval
["POST /t
hello, world",
"POST /t
hiya, world"]
--- response_body eval
["got the request socket
received: hell
received: o, w
received: orld
failed to receive: closed []
done
",
"got the request socket
received: hiya
received: , wo
failed to receive: closed [rld]
done
"]
--- no_error_log
[error]
=== TEST 7: Expect & 100 Continue
--- config
location /t {
content_by_lua '
local sock, err = ngx.req.socket()
if sock then
ngx.say("got the request socket")
else
ngx.say("failed to get the request socket: ", err)
return
end
for i = 1, 3 do
local data, err, part = sock:receive(5)
if data then
ngx.say("received: ", data)
else
ngx.say("failed to receive: ", err, " [", part, "]")
end
end
';
}
--- request
POST /t
hello world
--- more_headers
Expect: 100-Continue
--- error_code: 100
--- response_body_like chomp
\breceived: hello\b.*?\breceived: worl\b
--- no_error_log
[error]
=== TEST 8: pipelined requests, big buffer, small steps
--- config
location /t {
lua_socket_buffer_size 5;
content_by_lua '
local sock, err = ngx.req.socket()
if sock then
ngx.say("got the request socket")
else
ngx.say("failed to get the request socket: ", err)
end
for i = 1, 6 do
local data, err, part = sock:receive(2)
if data then
ngx.say("received: ", data)
else
ngx.say("failed to receive: ", err, " [", part, "]")
end
end
';
}
--- stap2
M(http-lua-req-socket-consume-preread) {
println("preread: ", user_string_n($arg2, $arg3))
}
--- pipelined_requests eval
["POST /t
hello world","POST /t
hiya globe"]
--- response_body eval
["got the request socket
received: he
received: ll
received: o
received: wo
received: rl
failed to receive: closed [d]
","got the request socket
received: hi
received: ya
received: g
received: lo
received: be
failed to receive: closed []
"]
--- no_error_log
[error]
=== TEST 9: chunked support is still a TODO
--- config
location /t {
content_by_lua '
local sock, err = ngx.req.socket()
if sock then
ngx.say("got the request socket")
else
ngx.req.read_body()
ngx.say("failed to get the request socket: ", err)
return
end
for i = 1, 3 do
local data, err, part = sock:receive(5)
if data then
ngx.say("received: ", data)
else
ngx.say("failed to receive: ", err, " [", part, "]")
end
end
';
}
--- raw_request eval
"POST /t HTTP/1.1\r
Host: localhost\r
Transfer-Encoding: chunked\r
Connection: close\r
\r
b\r
hello world\r
0\r
\r
"
--- stap2
/*
F(ngx_http_finalize_request) {
if ($r->main->count == 2) {
print_ubacktrace()
}
}
F(ngx_http_free_request) {
print_ubacktrace()
}
*/
--- response_body
failed to get the request socket: chunked request bodies not supported yet
--- no_error_log
[error]
[alert]
--- skip_nginx: 4: <1.3.9
=== TEST 10: chunked support in ngx.req.read_body
--- config
location /t {
content_by_lua '
ngx.req.read_body()
ngx.say(ngx.req.get_body_data())
';
}
--- raw_request eval
"POST /t HTTP/1.1\r
Host: localhost\r
Transfer-Encoding: chunked\r
Connection: close\r
\r
b\r
hello world\r
0\r
\r
"
--- stap2
/*
F(ngx_http_finalize_request) {
if ($r->main->count == 2) {
print_ubacktrace()
}
}
F(ngx_http_free_request) {
print_ubacktrace()
}
*/
--- response_body
hello world
--- no_error_log
[error]
[alert]
--- skip_nginx: 4: <1.3.9
=== TEST 11: downstream cosocket for GET requests (w/o request bodies)
--- config
#resolver 8.8.8.8;
location = /t {
content_by_lua '
local sock, err = ngx.req.socket()
if not sock then
ngx.say("failed to get socket: ", err)
return nil
end
while true do
local data, err, partial = sock:receive(4096)
ngx.log(ngx.INFO, "Received data")
if err then
ngx.say("err: ", err)
if partial then
ngx.print(partial)
end
break
end
if data then
ngx.print(data)
end
end
';
}
--- request
GET /t
--- response_body
failed to get socket: no body
--- no_error_log
[error]
=== TEST 12: downstream cosocket for POST requests with 0 size bodies
--- config
#resolver 8.8.8.8;
location = /t {
content_by_lua '
local sock, err = ngx.req.socket()
if not sock then
ngx.say("failed to get socket: ", err)
return nil
end
while true do
local data, err, partial = sock:receive(4096)
ngx.log(ngx.INFO, "Received data")
if err then
ngx.say("err: ", err)
if partial then
ngx.print(partial)
end
break
end
if data then
ngx.print(data)
end
end
';
}
--- request
POST /t
--- more_headers
Content-Length: 0
--- response_body
failed to get socket: no body
--- no_error_log
[error]
=== TEST 13: failing reread after reading timeout happens
--- config
location = /t {
content_by_lua '
local sock, err = ngx.req.socket()
if not sock then
ngx.say("failed to get socket: ", err)
return nil
end
sock:settimeout(100);
local data, err, partial = sock:receive(4096)
if err then
ngx.say("err: ", err, ", partial: ", partial)
end
local data, err, partial = sock:receive(4096)
if err then
ngx.say("err: ", err, ", partial: ", partial)
return
end
';
}
--- raw_request eval
"POST /t HTTP/1.0\r
Host: localhost\r
Content-Length: 10245\r
\r
hello"
--- response_body
err: timeout, partial: hello
err: timeout, partial:
--- error_log
lua tcp socket read timed out
=== TEST 14: successful reread after reading timeout happens (receive -> receive)
--- config
location = /t {
content_by_lua '
local sock = ngx.socket.tcp()
local ok, err = sock:connect("127.0.0.1", ngx.var.server_port)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local bytes, err = sock:send("POST /back HTTP/1.0\\r\\nHost: localhost\\r\\nContent-Length: 1024\\r\\n\\r\\nabc")
if not bytes then
ngx.say("failed to send: ", err)
else
ngx.say("sent: ", bytes)
end
ngx.sleep(0.2)
local bytes, err = sock:send("hello world")
if not bytes then
ngx.say("failed to send: ", err)
else
ngx.say("sent: ", bytes)
end
local reader = sock:receiveuntil("\\r\\n\\r\\n")
local header, err = reader()
if not header then
ngx.say("failed to receive header: ", err)
return
end
for i = 1, 2 do
local line, err = sock:receive()
if not line then
ngx.say("failed to receive line: ", err)
return
end
ngx.say("received: ", line)
end
';
}
location = /back {
content_by_lua '
ngx.send_headers()
ngx.flush(true)
local sock, err = ngx.req.socket()
if not sock then
ngx.say("failed to get socket: ", err)
return nil
end
sock:settimeout(100);
local data, err, partial = sock:receive(4096)
if err then
ngx.say("err: ", err, ", partial: ", partial)
else
ngx.say("received: ", data)
end
ngx.sleep(0.1)
local data, err, partial = sock:receive(11)
if err then
ngx.say("err: ", err, ", partial: ", partial)
else
ngx.say("received: ", data)
end
';
}
--- request
GET /t
--- response_body
sent: 65
sent: 11
received: err: timeout, partial: abc
received: received: hello world
--- error_log
lua tcp socket read timed out
=== TEST 15: successful reread after reading timeout happens (receive -> receiveuntil)
--- config
location = /t {
content_by_lua '
local sock = ngx.socket.tcp()
local ok, err = sock:connect("127.0.0.1", ngx.var.server_port)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local bytes, err = sock:send("POST /back HTTP/1.0\\r\\nHost: localhost\\r\\nContent-Length: 1024\\r\\n\\r\\nabc")
if not bytes then
ngx.say("failed to send: ", err)
else
ngx.say("sent: ", bytes)
end
ngx.sleep(0.2)
local bytes, err = sock:send("hello world\\n")
if not bytes then
ngx.say("failed to send: ", err)
else
ngx.say("sent: ", bytes)
end
local reader = sock:receiveuntil("\\r\\n\\r\\n")
local header, err = reader()
if not header then
ngx.say("failed to receive header: ", err)
return
end
for i = 1, 2 do
local line, err = sock:receive()
if not line then
ngx.say("failed to receive line: ", err)
return
end
ngx.say("received: ", line)
end
';
}
location = /back {
content_by_lua '
ngx.send_headers()
ngx.flush(true)
local sock, err = ngx.req.socket()
if not sock then
ngx.say("failed to get socket: ", err)
return nil
end
sock:settimeout(100);
local data, err, partial = sock:receive(4096)
if err then
ngx.say("err: ", err, ", partial: ", partial)
else
ngx.say("received: ", data)
end
ngx.sleep(0.1)
local reader = sock:receiveuntil("\\n")
local data, err, partial = reader()
if err then
ngx.say("err: ", err, ", partial: ", partial)
else
ngx.say("received: ", data)
end
';
}
--- request
GET /t
--- response_body
sent: 65
sent: 12
received: err: timeout, partial: abc
received: received: hello world
--- error_log
lua tcp socket read timed out
=== TEST 16: successful reread after reading timeout happens (receiveuntil -> receive)
--- config
location = /t {
content_by_lua '
local sock = ngx.socket.tcp()
local ok, err = sock:connect("127.0.0.1", ngx.var.server_port)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local bytes, err = sock:send("POST /back HTTP/1.0\\r\\nHost: localhost\\r\\nContent-Length: 1024\\r\\n\\r\\nabc")
if not bytes then
ngx.say("failed to send: ", err)
else
ngx.say("sent: ", bytes)
end
ngx.sleep(0.2)
local bytes, err = sock:send("hello world\\n")
if not bytes then
ngx.say("failed to send: ", err)
else
ngx.say("sent: ", bytes)
end
local reader = sock:receiveuntil("\\r\\n\\r\\n")
local header, err = reader()
if not header then
ngx.say("failed to receive header: ", err)
return
end
for i = 1, 2 do
local line, err = sock:receive()
if not line then
ngx.say("failed to receive line: ", err)
return
end
ngx.say("received: ", line)
end
';
}
location = /back {
content_by_lua '
ngx.send_headers()
ngx.flush(true)
local sock, err = ngx.req.socket()
if not sock then
ngx.say("failed to get socket: ", err)
return nil
end
sock:settimeout(100);
local reader = sock:receiveuntil("no-such-terminator")
local data, err, partial = reader()
if not data then
ngx.say("err: ", err, ", partial: ", partial)
else
ngx.say("received: ", data)
end
ngx.sleep(0.1)
local data, err, partial = sock:receive()
if err then
ngx.say("err: ", err, ", partial: ", partial)
else
ngx.say("received: ", data)
end
';
}
--- request
GET /t
--- response_body
sent: 65
sent: 12
received: err: timeout, partial: abc
received: received: hello world
--- error_log
lua tcp socket read timed out
=== TEST 17: req socket GC'd
--- config
location /t {
content_by_lua '
do
local sock, err = ngx.req.socket()
if sock then
ngx.say("got the request socket")
else
ngx.say("failed to get the request socket: ", err)
end
end
collectgarbage()
ngx.log(ngx.WARN, "GC cycle done")
ngx.say("done")
';
}
--- request
POST /t
hello world
--- response_body
got the request socket
done
--- no_error_log
[error]
--- grep_error_log eval: qr/lua finalize socket|GC cycle done/
--- grep_error_log_out
lua finalize socket
GC cycle done