-- LuaSocket-like layer above luxio local l = require "luxio" local realsocket = require "socket" local stream_mt, dgram_mt local function new_stream(sock, sockaddr) return setmetatable({ sock = sock, sockaddr = sockaddr, rdbytes = 0, wrbytes = 0, created = realsocket.gettime() }, stream_mt) end local function bind(address, service, backlog) backlog = backlog or 32 service = tostring(service) assert(type(address) == "string", "address string expected at #1") assert(type(service) == "string", "service string expected at #2") local r, s = l.getaddrinfo(address, service, l.AI_PASSIVE, l.AF_UNSPEC, l.SOCK_STREAM, l.IPPROTO_TCP) if r ~= 0 then return nil, ("getaddrinfo: %s"):format(l.gai_strerror(r)) end local sock, sockaddr, r, errno for _, ai in ipairs(s) do sock = l.socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol) if sock >= 0 then l.setsockopt(sock, l.SOL_SOCKET, l.SO_REUSEADDR, 1) r, errno = l.bind(sock, ai.ai_addr) if r == 0 then -- success sockaddr = ai.ai_addr break else l.close(sock) sock = nil end end end if sock == nil then -- none of them were bindable; return the error from the last -- attempt given there's not much else we can return. return nil, ("bind: %s"):format(l.strerror(errno)) end r, errno = l.listen(sock, backlog) if r < 0 then l.close(sock) return nil, ("listen: %s"):format(l.strerror(errno)) end return new_stream(sock, sockaddr) end local function connect(address, service, locaddr, locport) service = tostring(service) assert(type(address) == "string", "address string expected at #1") assert(type(service) == "string", "service string expected at #1") local r, s = l.getaddrinfo(address, service, 0, l.AF_UNSPEC, l.SOCK_STREAM, l.IPPROTO_TCP) if r ~= 0 then return nil, ("getaddrinfo: %s"):format(l.gai_strerror(r)) end local sock, sockaddr, r, errno for _, ai in ipairs(s) do sock = l.socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol) if sock >= 0 then r, errno = l.connect(sock, ai.ai_addr) if r == 0 then -- success sockaddr = ai.ai_addr break else l.close(sock) sock = nil end end end if sock == nil then -- none of them were connectable; return the error from the -- last attempt given there's not much else we can return. return nil, ("connect: %s"):format(l.strerror(errno)) end return new_stream(sock, sockaddr) end local function tcp() local sock, err = l.socket(l.AF_INET, l.SOCK_STREAM, l.IPPROTO_TCP) if sock < 0 then return nil, ("socket: %s"):format(l.strerror(errno)) end return new_stream(sock) end local function tcp6() local sock, err = l.socket(l.AF_INET6, l.SOCK_STREAM, l.IPPROTO_TCP) if sock < 0 then return nil, ("socket: %s"):format(l.strerror(errno)) end return new_stream(sock) end local function stream_accept(s) local sock, addr = l.accept(s.sock) if sock < 0 then return nil, ("accept: %s"):format(l.strerror(addr)) end return new_stream(sock, addr) end local function stream_bind(s, addr, serv) serv = tostring(serv) assert(type(addr) == "string", "address string expected at #2") assert(type(addr) == "string", "service string expected at #3") local r, s = l.getaddrinfo(address, service, l.AI_PASSIVE, l.AF_UNSPEC, l.SOCK_STREAM, l.IPPROTO_TCP) if r ~= 0 then return nil, ("getaddrinfo: %s"):format(l.gai_strerror(r)) end local sock = s.sock local errno, success l.setsockopt(sock, l.SOL_SOCKET, l.SO_REUSEADDR, 1) for _, ai in ipairs(s) do r, errno = l.bind(sock, ai.ai_addr) if r == 0 then -- success s.sockaddr = ai.ai_addr success = true break; end end if success == false then return nil, ("bind: %s"):format(l.strerror(errno)) end return 1 end local function stream_listen(s, backlog) backlog = backlog or 32 local r, errno = l.listen(s.sock, backlog) if r ~= 0 then return nil, ("listen: %s"):format(l.strerror(errno)) end return 1 end local function stream_receive(s, pattern, prefix) assert(type(pattern) == "number", "unimplemented pattern") -- FIXME local r, errno = l.read(s.sock, pattern) if #r < pattern then return nil, "timeout", r else return r end end local function stream_send(s, data, i, j) i = i or 1 j = j or -1 local r, errno = l.write(s.sock, data:sub(i, j)) return i + r end local function stream_tostring(s) return ("socket: %s"):format(tostring(s.sockaddr or "unbound")) end stream_mt = { accept = stream_accept, bind = stream_bind, close = stream_close, connect = stream_connect, listen = stream_listen, receive = stream_receive, send = stream_send, getpeername = stream_getpeername, getsockname = stream_getsockname, getstats = stream_getstats, setoption = stream_setoption, setstats = stream_setstats, settimeout = stream_settimeout, shutdown = stream_shutdown, __tostring = stream_tostring } stream_mt.__index = stream_mt dgram_mt = { } return { bind = bind, connect = connect, tcp = tcp, tcp6 = tcp6, _DEBUG = false, _VERSION = realsocket._VERSION, newtry = realsocket.newtry, protect = realsocket.protect, sink = realsocket.sink, skip = realsocket.skip, sleep = realsocket.sleep, gettime = realsocket.gettime, try = realsocket.try, }