diff --git a/src/lua/bsdsocket.lua b/src/lua/bsdsocket.lua index c074fe063e7e4fa553a301be76de7b107b982619..bfab039e3a6c4a0859545da2869ef4e46c73d776 100644 --- a/src/lua/bsdsocket.lua +++ b/src/lua/bsdsocket.lua @@ -933,35 +933,127 @@ local function tcp_connect(host, port, timeout) return nil end -socket_mt = { - __index = socket_methods, - __tostring = function(self) - local save_errno = self._errno - local name = sprintf("fd %d", self.fh) - local aka = self:name() - if aka ~= nil then - name = sprintf("%s, aka %s:%s", name, aka.host, aka.port) +local function tcp_server_remote(list, prepare, handler) + local slist = {} + + -- bind/create sockets + for _, addr in pairs(list) do + local s = create_socket(addr.family, addr.type, addr.protocol) + + local ok = false + if s ~= nil then + if s:bind(addr.host, addr.port) then + local prepared, backlog = pcall(prepare, s) + if prepared and s:listen(backlog) then + ok = true + end + end + end + + -- errors + if not ok then + if s ~= nil then + s:close() end - local peer = self:peer(self) - if peer ~= nil then - name = sprintf("%s, peer %s:%s", name, peer.host, peer.port) + local save_errno = boxerrno() + for _, s in pairs(slist) do + s:close() end - self._errno = save_errno - return name + boxerrno(save_errno) + return nil + end + + table.insert(slist, s) + end + + local server = { s = slist } + + server.stop = function() + if #server.s == 0 then + return false end + for _, s in pairs(server.s) do + s:close() + end + server.s = {} + return true + end + + for _, s in pairs(server.s) do + fiber.create(function(s) + fiber.name(sprintf("listen_fd=%d",s.fh)) + + while s:readable() do + + local sc = s:accept() + + if sc == nil then + break + end + + fiber.create(function(sc) + pcall(handler, sc) + sc:close() + end, sc) + end + end, s) + end + + return server +end + +local function tcp_server(host, port, prepare, handler, timeout) + if handler == nil then + handler = prepare + prepare = function() end + end + + if type(prepare) ~= 'function' or type(handler) ~= 'function' then + error("Usage: socket.tcp_server(host, port[, prepare], handler)") + end + + if host == 'unix/' then + return tcp_server_remote({{host = host, port = port, protocol = 0, + family = 'PF_UNIX', type = 'SOCK_STREAM' }}, prepare, handler) + end + + local dns = getaddrinfo(host, port, timeout, { type = 'SOCK_STREAM', + protocol = 'tcp' }) + if dns == nil then + return nil + end + return tcp_server_remote(dns, prepare, handler) +end + +socket_mt = { + __index = socket_methods, + __tostring = function(self) + local save_errno = self._errno + local name = sprintf("fd %d", self.fh) + local aka = self:name() + if aka ~= nil then + name = sprintf("%s, aka %s:%s", name, aka.host, aka.port) + end + local peer = self:peer(self) + if peer ~= nil then + name = sprintf("%s, peer %s:%s", name, peer.host, peer.port) + end + self._errno = save_errno + return name + end } if package.loaded.socket == nil then package.loaded.socket = {} end -package.loaded.socket.tcp_connect = tcp_connect --- package.loaded.socket.tcp_server = tcp_server setmetatable(package.loaded.socket, { __call = function(self, ...) return create_socket(...) end, __index = { getaddrinfo = getaddrinfo, + tcp_connect = tcp_connect, + tcp_server = tcp_server } }) diff --git a/test/box/bsdsocket.result b/test/box/bsdsocket.result index 419e84a62149b4b4c1ae7f8184eda548e893ed6d..a1443a1f7c75967785d3aede83d33a1525b9dead 100644 --- a/test/box/bsdsocket.result +++ b/test/box/bsdsocket.result @@ -1178,3 +1178,32 @@ os.remove(path) --- - true ... +server = socket.tcp_server('unix/', path, function(s) s:write('Hello, world') end) +--- +... +server ~= nil +--- +- true +... +fiber.sleep(.5) +--- +... +client = socket.tcp_connect('unix/', path) +--- +... +client ~= nil +--- +- true +... +client:read(123) +--- +- Hello, world +... +server:stop() +--- +- true +... +os.remove(path) +--- +- true +... diff --git a/test/box/bsdsocket.test.lua b/test/box/bsdsocket.test.lua index ef953214eba84277d6fce1f437f0041afc821004..1d51a54a7a2b01499f2bde878b73478597871901 100644 --- a/test/box/bsdsocket.test.lua +++ b/test/box/bsdsocket.test.lua @@ -387,3 +387,20 @@ c:close() s:close() os.remove(path) + + +server = socket.tcp_server('unix/', path, function(s) s:write('Hello, world') end) + +server ~= nil + +fiber.sleep(.5) + +client = socket.tcp_connect('unix/', path) + +client ~= nil + +client:read(123) + +server:stop() + +os.remove(path)