local base = _G
local string = require("string")
local math = require("math")
local socket = require("socket.core")
local M = {}
function M.connect(address, port, laddress, lport)
local sock, err = socket.tcp()
if not sock then return nil, err end
if laddress then
local res, berr = sock:bind(laddress, lport, -1)
if not res then return nil, berr end
end
local res, cerr = sock:connect(address, port)
if not res then return nil, cerr end
return sock
end
function M.bind(host, port, backlog)
local sock, err = socket.tcp()
if not sock then return nil, err end
sock:setoption("reuseaddr", true)
local res, berr = sock:bind(host, port)
if not res then return nil, berr end
res, berr = sock:listen(backlog)
if not res then return nil, berr end
return sock
end
M.try = socket.newtry()
function M.choose(table)
return function(name, opt1, opt2)
if base.type(name) ~= "string" then
name, opt1, opt2 = "default", name, opt1
end
local f = table[name or "nil"]
if not f then
base.error("unknown key (" .. base.tostring(name) .. ")", 3)
else
return f(opt1, opt2)
end
end
end
M.sourcet = {}
M.sinkt = {}
M.BLOCKSIZE = 2048
M.sinkt["close-when-done"] = function(sock)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function(_, chunk, _)
if not chunk then
sock:close()
return 1
else
return sock:send(chunk)
end
end
})
end
M.sinkt["keep-open"] = function(sock)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function(_, chunk, _)
if chunk then
return sock:send(chunk)
else
return 1
end
end
})
end
M.sinkt["default"] = M.sinkt["keep-open"]
M.sink = M.choose(M.sinkt)
M.sourcet["by-length"] = function(sock, length)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function()
if length <= 0 then return nil end
local size = math.min(socket.BLOCKSIZE, length)
local chunk, err = sock:receive(size)
if err then return nil, err end
length = length - string.len(chunk)
return chunk
end
})
end
M.sourcet["until-closed"] = function(sock)
local done
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function()
if done then return nil end
local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
if not err then
return chunk
elseif err == "closed" then
sock:close()
done = 1
return partial
else
return nil, err
end
end
})
end
M.sourcet["default"] = M.sourcet["until-closed"]
M.source = M.choose(M.sourcet)
return M