mirror of
https://github.com/lunarmodules/luasocket.git
synced 2024-11-08 14:28:21 +01:00
58096449c6
Implemented new distribution scheme. Select is now purely C. HTTP reimplemented seems faster dunno why. LTN12 functions that coroutines fail gracefully.
587 lines
16 KiB
Lua
587 lines
16 KiB
Lua
require"socket"
|
|
|
|
host = host or "localhost"
|
|
port = port or "8080"
|
|
|
|
function pass(...)
|
|
local s = string.format(unpack(arg))
|
|
io.stderr:write(s, "\n")
|
|
end
|
|
|
|
function fail(...)
|
|
local s = string.format(unpack(arg))
|
|
io.stderr:write("ERROR: ", s, "!\n")
|
|
socket.sleep(3)
|
|
os.exit()
|
|
end
|
|
|
|
function warn(...)
|
|
local s = string.format(unpack(arg))
|
|
io.stderr:write("WARNING: ", s, "\n")
|
|
end
|
|
|
|
function remote(...)
|
|
local s = string.format(unpack(arg))
|
|
s = string.gsub(s, "\n", ";")
|
|
s = string.gsub(s, "%s+", " ")
|
|
s = string.gsub(s, "^%s*", "")
|
|
control:send(s, "\n")
|
|
control:receive()
|
|
end
|
|
|
|
function test(test)
|
|
io.stderr:write("----------------------------------------------\n",
|
|
"testing: ", test, "\n",
|
|
"----------------------------------------------\n")
|
|
end
|
|
|
|
function check_timeout(tm, sl, elapsed, err, opp, mode, alldone)
|
|
if tm < sl then
|
|
if opp == "send" then
|
|
if not err then warn("must be buffered")
|
|
elseif err == "timeout" then pass("proper timeout")
|
|
else fail("unexpected error '%s'", err) end
|
|
else
|
|
if err ~= "timeout" then fail("should have timed out")
|
|
else pass("proper timeout") end
|
|
end
|
|
else
|
|
if mode == "total" then
|
|
if elapsed > tm then
|
|
if err ~= "timeout" then fail("should have timed out")
|
|
else pass("proper timeout") end
|
|
elseif elapsed < tm then
|
|
if err then fail(err)
|
|
else pass("ok") end
|
|
else
|
|
if alldone then
|
|
if err then fail("unexpected error '%s'", err)
|
|
else pass("ok") end
|
|
else
|
|
if err ~= "timeout" then fail(err)
|
|
else pass("proper timeoutk") end
|
|
end
|
|
end
|
|
else
|
|
if err then fail(err)
|
|
else pass("ok") end
|
|
end
|
|
end
|
|
end
|
|
|
|
if not socket.DEBUG then
|
|
fail("Please define LUASOCKET_DEBUG and recompile LuaSocket")
|
|
end
|
|
|
|
io.stderr:write("----------------------------------------------\n",
|
|
"LuaSocket Test Procedures\n",
|
|
"----------------------------------------------\n")
|
|
|
|
start = socket.time()
|
|
|
|
function reconnect()
|
|
io.stderr:write("attempting data connection... ")
|
|
if data then data:close() end
|
|
remote [[
|
|
if data then data:close() data = nil end
|
|
data = server:accept()
|
|
data:setoption("tcp-nodelay", true)
|
|
]]
|
|
data, err = socket.connect(host, port)
|
|
if not data then fail(err)
|
|
else pass("connected!") end
|
|
data:setoption("tcp-nodelay", true)
|
|
end
|
|
|
|
pass("attempting control connection...")
|
|
control, err = socket.connect(host, port)
|
|
if err then fail(err)
|
|
else pass("connected!") end
|
|
control:setoption("tcp-nodelay", true)
|
|
|
|
------------------------------------------------------------------------
|
|
function test_methods(sock, methods)
|
|
for _, v in methods do
|
|
if type(sock[v]) ~= "function" then
|
|
fail(sock.class .. " method '" .. v .. "' not registered")
|
|
end
|
|
end
|
|
pass(sock.class .. " methods are ok")
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function test_mixed(len)
|
|
reconnect()
|
|
local inter = math.ceil(len/4)
|
|
local p1 = "unix " .. string.rep("x", inter) .. "line\n"
|
|
local p2 = "dos " .. string.rep("y", inter) .. "line\r\n"
|
|
local p3 = "raw " .. string.rep("z", inter) .. "bytes"
|
|
local p4 = "end" .. string.rep("w", inter) .. "bytes"
|
|
local bp1, bp2, bp3, bp4
|
|
remote (string.format("str = data:receive(%d)",
|
|
string.len(p1)+string.len(p2)+string.len(p3)+string.len(p4)))
|
|
sent, err = data:send(p1, p2, p3, p4)
|
|
if err then fail(err) end
|
|
remote "data:send(str); data:close()"
|
|
bp1, err = data:receive()
|
|
if err then fail(err) end
|
|
bp2, err = data:receive()
|
|
if err then fail(err) end
|
|
bp3, err = data:receive(string.len(p3))
|
|
if err then fail(err) end
|
|
bp4, err = data:receive("*a")
|
|
if err then fail(err) end
|
|
if bp1.."\n" == p1 and bp2.."\r\n" == p2 and bp3 == p3 and bp4 == p4 then
|
|
pass("patterns match")
|
|
else fail("patterns don't match") end
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function test_asciiline(len)
|
|
reconnect()
|
|
local str, str10, back, err
|
|
str = string.rep("x", math.mod(len, 10))
|
|
str10 = string.rep("aZb.c#dAe?", math.floor(len/10))
|
|
str = str .. str10
|
|
remote "str = data:receive()"
|
|
sent, err = data:send(str, "\n")
|
|
if err then fail(err) end
|
|
remote "data:send(str, '\\n')"
|
|
back, err = data:receive()
|
|
if err then fail(err) end
|
|
if back == str then pass("lines match")
|
|
else fail("lines don't match") end
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function test_rawline(len)
|
|
reconnect()
|
|
local str, str10, back, err
|
|
str = string.rep(string.char(47), math.mod(len, 10))
|
|
str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100),
|
|
math.floor(len/10))
|
|
str = str .. str10
|
|
remote "str = data:receive()"
|
|
sent, err = data:send(str, "\n")
|
|
if err then fail(err) end
|
|
remote "data:send(str, '\\n')"
|
|
back, err = data:receive()
|
|
if err then fail(err) end
|
|
if back == str then pass("lines match")
|
|
else fail("lines don't match") end
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function test_raw(len)
|
|
reconnect()
|
|
local half = math.floor(len/2)
|
|
local s1, s2, back, err
|
|
s1 = string.rep("x", half)
|
|
s2 = string.rep("y", len-half)
|
|
remote (string.format("str = data:receive(%d)", len))
|
|
sent, err = data:send(s1)
|
|
if err then fail(err) end
|
|
sent, err = data:send(s2)
|
|
if err then fail(err) end
|
|
remote "data:send(str)"
|
|
back, err = data:receive(len)
|
|
if err then fail(err) end
|
|
if back == s1..s2 then pass("blocks match")
|
|
else fail("blocks don't match") end
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function test_totaltimeoutreceive(len, tm, sl)
|
|
reconnect()
|
|
local str, err, partial
|
|
pass("%d bytes, %ds total timeout, %ds pause", len, tm, sl)
|
|
remote (string.format ([[
|
|
data:settimeout(%d)
|
|
str = string.rep('a', %d)
|
|
data:send(str)
|
|
print('server: sleeping for %ds')
|
|
socket.sleep(%d)
|
|
print('server: woke up')
|
|
data:send(str)
|
|
]], 2*tm, len, sl, sl))
|
|
data:settimeout(tm, "total")
|
|
str, err, partial, elapsed = data:receive(2*len)
|
|
check_timeout(tm, sl, elapsed, err, "receive", "total",
|
|
string.len(str or partial) == 2*len)
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function test_totaltimeoutsend(len, tm, sl)
|
|
reconnect()
|
|
local str, err, total
|
|
pass("%d bytes, %ds total timeout, %ds pause", len, tm, sl)
|
|
remote (string.format ([[
|
|
data:settimeout(%d)
|
|
str = data:receive(%d)
|
|
print('server: sleeping for %ds')
|
|
socket.sleep(%d)
|
|
print('server: woke up')
|
|
str = data:receive(%d)
|
|
]], 2*tm, len, sl, sl, len))
|
|
data:settimeout(tm, "total")
|
|
str = string.rep("a", 2*len)
|
|
total, err, elapsed = data:send(str)
|
|
check_timeout(tm, sl, elapsed, err, "send", "total",
|
|
total == 2*len)
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function test_blockingtimeoutreceive(len, tm, sl)
|
|
reconnect()
|
|
local str, err, partial
|
|
pass("%d bytes, %ds blocking timeout, %ds pause", len, tm, sl)
|
|
remote (string.format ([[
|
|
data:settimeout(%d)
|
|
str = string.rep('a', %d)
|
|
data:send(str)
|
|
print('server: sleeping for %ds')
|
|
socket.sleep(%d)
|
|
print('server: woke up')
|
|
data:send(str)
|
|
]], 2*tm, len, sl, sl))
|
|
data:settimeout(tm)
|
|
str, err, partial, elapsed = data:receive(2*len)
|
|
check_timeout(tm, sl, elapsed, err, "receive", "blocking",
|
|
string.len(str or partial) == 2*len)
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function test_blockingtimeoutsend(len, tm, sl)
|
|
reconnect()
|
|
local str, err, total
|
|
pass("%d bytes, %ds blocking timeout, %ds pause", len, tm, sl)
|
|
remote (string.format ([[
|
|
data:settimeout(%d)
|
|
str = data:receive(%d)
|
|
print('server: sleeping for %ds')
|
|
socket.sleep(%d)
|
|
print('server: woke up')
|
|
str = data:receive(%d)
|
|
]], 2*tm, len, sl, sl, len))
|
|
data:settimeout(tm)
|
|
str = string.rep("a", 2*len)
|
|
total, err, elapsed = data:send(str)
|
|
check_timeout(tm, sl, elapsed, err, "send", "blocking",
|
|
total == 2*len)
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function empty_connect()
|
|
reconnect()
|
|
if data then data:close() data = nil end
|
|
remote [[
|
|
if data then data:close() data = nil end
|
|
data = server:accept()
|
|
]]
|
|
data, err = socket.connect("", port)
|
|
if not data then
|
|
pass("ok")
|
|
data = socket.connect(host, port)
|
|
else fail("should not have connected!") end
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function isclosed(c)
|
|
return c:getfd() == -1 or c:getfd() == (2^32-1)
|
|
end
|
|
|
|
function active_close()
|
|
reconnect()
|
|
if isclosed(data) then fail("should not be closed") end
|
|
data:close()
|
|
if not isclosed(data) then fail("should be closed") end
|
|
data = nil
|
|
local udp = socket.udp()
|
|
if isclosed(udp) then fail("should not be closed") end
|
|
udp:close()
|
|
if not isclosed(udp) then fail("should be closed") end
|
|
pass("ok")
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function test_closed()
|
|
local back, partial, err
|
|
local str = 'little string'
|
|
reconnect()
|
|
pass("trying read detection")
|
|
remote (string.format ([[
|
|
data:send('%s')
|
|
data:close()
|
|
data = nil
|
|
]], str))
|
|
-- try to get a line
|
|
back, err, partial = data:receive()
|
|
if not err then fail("should have gotten 'closed'.")
|
|
elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.")
|
|
elseif str ~= partial then fail("didn't receive partial result.")
|
|
else pass("graceful 'closed' received") end
|
|
reconnect()
|
|
pass("trying write detection")
|
|
remote [[
|
|
data:close()
|
|
data = nil
|
|
]]
|
|
total, err = data:send(string.rep("ugauga", 100000))
|
|
if not err then
|
|
pass("failed: output buffer is at least %d bytes long!", total)
|
|
elseif err ~= "closed" then
|
|
fail("got '"..err.."' instead of 'closed'.")
|
|
else
|
|
pass("graceful 'closed' received after %d bytes were sent", total)
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function test_selectbugs()
|
|
local r, s, e = socket.select(nil, nil, 0.1)
|
|
assert(type(r) == "table" and type(s) == "table" and e == "timeout")
|
|
pass("both nil: ok")
|
|
local udp = socket.udp()
|
|
udp:close()
|
|
r, s, e = socket.select({ udp }, { udp }, 0.1)
|
|
assert(type(r) == "table" and type(s) == "table" and e == "timeout")
|
|
pass("closed sockets: ok")
|
|
e = pcall(socket.select, "wrong", 1, 0.1)
|
|
assert(e == false)
|
|
e = pcall(socket.select, {}, 1, 0.1)
|
|
assert(e == false)
|
|
pass("invalid input: ok")
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function accept_timeout()
|
|
io.stderr:write("accept with timeout (if it hangs, it failed): ")
|
|
local s, e = socket.bind("*", 0, 0)
|
|
assert(s, e)
|
|
local t = socket.time()
|
|
s:settimeout(1)
|
|
local c, e = s:accept()
|
|
assert(not c, "should not accept")
|
|
assert(e == "timeout", string.format("wrong error message (%s)", e))
|
|
t = socket.time() - t
|
|
assert(t < 2, string.format("took to long to give up (%gs)", t))
|
|
s:close()
|
|
pass("good")
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function connect_timeout()
|
|
io.stderr:write("connect with timeout (if it hangs, it failed): ")
|
|
local t = socket.time()
|
|
local c, e = socket.tcp()
|
|
assert(c, e)
|
|
c:settimeout(0.1)
|
|
ip = socket.dns.toip("ibere.tecgraf.puc-rio.br")
|
|
if not ip then return end
|
|
local t = socket.time()
|
|
local r, e = c:connect(ip, 80)
|
|
assert(not r, "should not connect")
|
|
assert(e == "timeout", e)
|
|
assert(socket.time() - t < 2, "took too long to give up.")
|
|
c:close()
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function accept_errors()
|
|
io.stderr:write("not listening: ")
|
|
local d, e = socket.bind("*", 0)
|
|
assert(d, e);
|
|
local c, e = socket.tcp();
|
|
assert(c, e);
|
|
d:setfd(c:getfd())
|
|
d:settimeout(2)
|
|
local r, e = d:accept()
|
|
assert(not r and e == "not listening", e)
|
|
print("ok")
|
|
io.stderr:write("not supported: ")
|
|
local c, e = socket.udp()
|
|
assert(c, e);
|
|
d:setfd(c:getfd())
|
|
local r, e = d:accept()
|
|
assert(not r and e == "not supported" or e == "not listening", e)
|
|
print("ok")
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function connect_errors()
|
|
io.stderr:write("connection refused: ")
|
|
local c, e = socket.connect("localhost", 1);
|
|
assert(not c and e == "connection refused", e)
|
|
print("ok")
|
|
io.stderr:write("host not found: ")
|
|
local c, e = socket.connect("not.exist.com", 1);
|
|
assert(not c and e == "host not found", e)
|
|
print("ok")
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
function rebind_test()
|
|
local c = socket.bind("localhost", 0)
|
|
local i, p = c:getsockname()
|
|
local s, e = socket.tcp()
|
|
assert(s, e)
|
|
s:setoption("reuseaddr", false)
|
|
r, e = s:bind("localhost", p)
|
|
assert(not r, "managed to rebind!")
|
|
assert(e == "address already in use")
|
|
print("ok")
|
|
end
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
test("character line")
|
|
test_asciiline(1)
|
|
test_asciiline(17)
|
|
test_asciiline(200)
|
|
test_asciiline(4091)
|
|
test_asciiline(80199)
|
|
test_asciiline(8000000)
|
|
test_asciiline(80199)
|
|
test_asciiline(4091)
|
|
test_asciiline(200)
|
|
test_asciiline(17)
|
|
test_asciiline(1)
|
|
|
|
test("method registration")
|
|
test_methods(socket.tcp(), {
|
|
"accept",
|
|
"bind",
|
|
"close",
|
|
"connect",
|
|
"getpeername",
|
|
"getsockname",
|
|
"listen",
|
|
"receive",
|
|
"send",
|
|
"setoption",
|
|
"setpeername",
|
|
"setsockname",
|
|
"settimeout",
|
|
"shutdown",
|
|
})
|
|
|
|
test_methods(socket.udp(), {
|
|
"close",
|
|
"getpeername",
|
|
"getsockname",
|
|
"receive",
|
|
"receivefrom",
|
|
"send",
|
|
"sendto",
|
|
"setoption",
|
|
"setpeername",
|
|
"setsockname",
|
|
"settimeout",
|
|
})
|
|
|
|
test("select function")
|
|
test_selectbugs()
|
|
|
|
test("connect function")
|
|
connect_timeout()
|
|
empty_connect()
|
|
connect_errors()
|
|
|
|
test("rebinding: ")
|
|
rebind_test()
|
|
|
|
test("active close: ")
|
|
active_close()
|
|
|
|
test("closed connection detection: ")
|
|
test_closed()
|
|
|
|
test("accept function: ")
|
|
accept_timeout()
|
|
accept_errors()
|
|
|
|
|
|
|
|
test("mixed patterns")
|
|
test_mixed(1)
|
|
test_mixed(17)
|
|
test_mixed(200)
|
|
test_mixed(4091)
|
|
test_mixed(801990)
|
|
test_mixed(4091)
|
|
test_mixed(200)
|
|
test_mixed(17)
|
|
test_mixed(1)
|
|
|
|
|
|
test("binary line")
|
|
reconnect()
|
|
test_rawline(1)
|
|
test_rawline(17)
|
|
test_rawline(200)
|
|
test_rawline(4091)
|
|
test_rawline(80199)
|
|
test_rawline(8000000)
|
|
test_rawline(80199)
|
|
test_rawline(4091)
|
|
test_rawline(200)
|
|
test_rawline(17)
|
|
test_rawline(1)
|
|
|
|
test("raw transfer")
|
|
reconnect()
|
|
test_raw(1)
|
|
test_raw(17)
|
|
test_raw(200)
|
|
test_raw(4091)
|
|
test_raw(80199)
|
|
test_raw(8000000)
|
|
test_raw(80199)
|
|
test_raw(4091)
|
|
test_raw(200)
|
|
test_raw(17)
|
|
test_raw(1)
|
|
|
|
test("non-blocking transfer")
|
|
reconnect()
|
|
-- the value is not important, we only want
|
|
-- to test non-blocking I/O anyways
|
|
data:settimeout(200)
|
|
test_raw(1)
|
|
test_raw(17)
|
|
test_raw(200)
|
|
test_raw(4091)
|
|
test_raw(80199)
|
|
test_raw(8000000)
|
|
test_raw(80199)
|
|
test_raw(4091)
|
|
test_raw(200)
|
|
test_raw(17)
|
|
test_raw(1)
|
|
|
|
test("total timeout on send")
|
|
test_totaltimeoutsend(800091, 1, 3)
|
|
test_totaltimeoutsend(800091, 2, 3)
|
|
test_totaltimeoutsend(800091, 3, 2)
|
|
test_totaltimeoutsend(800091, 3, 1)
|
|
|
|
test("total timeout on receive")
|
|
test_totaltimeoutreceive(800091, 1, 3)
|
|
test_totaltimeoutreceive(800091, 2, 3)
|
|
test_totaltimeoutreceive(800091, 3, 2)
|
|
test_totaltimeoutreceive(800091, 3, 1)
|
|
|
|
test("blocking timeout on send")
|
|
test_blockingtimeoutsend(800091, 1, 3)
|
|
test_blockingtimeoutsend(800091, 2, 3)
|
|
test_blockingtimeoutsend(800091, 3, 2)
|
|
test_blockingtimeoutsend(800091, 3, 1)
|
|
|
|
test("blocking timeout on receive")
|
|
test_blockingtimeoutreceive(800091, 1, 3)
|
|
test_blockingtimeoutreceive(800091, 2, 3)
|
|
test_blockingtimeoutreceive(800091, 3, 2)
|
|
test_blockingtimeoutreceive(800091, 3, 1)
|
|
|
|
test(string.format("done in %.2fs", socket.time() - start))
|