diff --git a/.travis.yml b/.travis.yml index 1e3151c5d7cb893b474685349bc3faea2fb51e22..35e9d6357b5bac258ac40762400677a2a1b91d38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,6 @@ install: script: - mkdir ./build && cd ./build && cmake .. -DCMAKE_BUILD_TYPE=RelWithDebugInfo - make -j8 - - make test notifications: irc: diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ba9d0a620a32385c22d1891d1af5ef418285e04..7311509a6b3e9a75164c08da5358e766c308c964 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,8 @@ include(cmake/compiler.cmake) include(cmake/simd.cmake) include(cmake/profile.cmake) +option(ENABLE_VALGRIND "Enable integration with valgrind, a memory analyzing tool" OFF) + check_symbol_exists(MAP_ANON sys/mman.h HAVE_MAP_ANON) check_symbol_exists(MAP_ANONYMOUS sys/mman.h HAVE_MAP_ANONYMOUS) check_include_file(sys/time.h HAVE_SYS_TIME_H) @@ -377,6 +379,7 @@ message (STATUS "ENABLE_SSE2: ${ENABLE_SSE2}") message (STATUS "ENABLE_AVX: ${ENABLE_AVX}") message (STATUS "ENABLE_GCOV: ${ENABLE_GCOV}") message (STATUS "ENABLE_GPROF: ${ENABLE_GPROF}") +message (STATUS "ENABLE_VALGRIND: ${ENABLE_VALGRIND}") message (STATUS "ENABLE_TRACE: ${ENABLE_TRACE}") message (STATUS "ENABLE_BACKTRACE: ${ENABLE_BACKTRACE} (symbol resolve: ${HAVE_BFD})") message (STATUS "ENABLE_CLIENT: ${ENABLE_CLIENT}") diff --git a/cmake/luajit.cmake b/cmake/luajit.cmake index 3874fbcfbbfae8028be6636edf7ef7d25c5c8e41..cea484f7fd40a5af35b6d425410f49d9356c0c22 100644 --- a/cmake/luajit.cmake +++ b/cmake/luajit.cmake @@ -131,14 +131,19 @@ message (STATUS "Use LuaJIT library: ${LUAJIT_LIB}") macro(luajit_build) set (luajit_buildoptions BUILDMODE=static) set (luajit_copt "") + set (luajit_xcflags "") if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") set (luajit_buildoptions ${luajit_buildoptions} CCDEBUG=${CC_DEBUG_OPT}) set (luajit_copt ${luajit_copt} -O1) - set (luajit_buildoptions ${luajit_buildoptions} XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT') + set (luajit_xcflags ${luajit_xcflags} + -DLUA_USE_APICHECK -DLUA_USE_ASSERT) else () set (luajit_copt ${luajit_copt} -O2) endif() - set (luajit_copt ${luajit_copt} -I${PROJECT_SOURCE_DIR}/libobjc) + if (ENABLE_VALGRIND) + set (luajit_xcflags ${luajit_xcflags} + -DLUAJIT_USE_VALGRIND -DLUAJIT_USE_SYSMALLOC) + endif() set (luajit_target_cc "${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS}") # Use external unwind on all platforms. set (luajit_target_cc "${luajit_target_cc} -DLUAJIT_UNWIND_EXTERNAL=1") @@ -164,6 +169,7 @@ macro(luajit_build) set (luajit_buildoptions ${luajit_buildoptions} CFLAGS="" CXXFLAGS="" + XCFLAGS="${luajit_xcflags}" HOST_CC="${luajit_host_cc}" TARGET_CC="${luajit_target_cc}" CCOPT="${luajit_copt}") diff --git a/cmake/profile.cmake b/cmake/profile.cmake index 94de41788032ed753dca12938923e20a738f11b9..f3ae8c1155e5cb318f44a8f6ec7a9c0417471997 100644 --- a/cmake/profile.cmake +++ b/cmake/profile.cmake @@ -32,3 +32,5 @@ option(ENABLE_GPROF "Enable integration with gprof, a performance analyzing tool if (ENABLE_GPROF) add_compile_flags("C;CXX" "-pg") endif() + +option(ENABLE_VALGRIND "Enable integration with valgrind, a memory analyzing tool" OFF) diff --git a/src/box/lua/box_net.lua b/src/box/lua/box_net.lua index a2d35db1907548d740fa5359a6301e4004da937d..e30e4b35726e47a002fdc75b7426eb6d3495c9d5 100644 --- a/src/box/lua/box_net.lua +++ b/src/box/lua/box_net.lua @@ -302,16 +302,11 @@ box.net.box.new = function(host, port, reconnect_timeout) return true end - local sc = box.socket.tcp() - if sc == nil then - self:fatal("Can't create socket") - return false - end - local s = { sc:connect( self.host, self.port ) } - if s[1] == nil then - self:fatal("Can't connect to %s:%s: %s", - self.host, self.port, s[4]) + local sc = box.socket.tcp_connect(self.host, self.port) + if sc == nil then + self:fatal("Can't connect to %s:%s: %s", self.host, self.port, + box.errno.strerror()) return false end @@ -324,12 +319,14 @@ box.net.box.new = function(host, port, reconnect_timeout) if self.s == nil then return end - local res = { self.s:recv(12) } - if res[4] ~= nil then - self:fatal("Can't read socket: %s", res[3]) + + local header = self.s:read{chunk = 12} + + if header == nil then + self:fatal("Can't read socket: %s", box.errno.strerror()) return end - local header = res[1] + if string.len(header) ~= 12 then self:fatal("Unexpected eof while reading header from %s", self:title()) @@ -340,12 +337,12 @@ box.net.box.new = function(host, port, reconnect_timeout) local body = '' if blen > 0 then - res = { self.s:recv(blen) } - if res[4] ~= nil then - self:fatal("Error while reading socket: %s", res[4]) - return + body = self.s:read{chunk = blen} + if body == nil then + self:fatal("Error while reading socket: %s", + box.errno.strerror()) + return end - body = res[1] if string.len(body) ~= blen then self:fatal("Unexpected eof while reading body from %s", self:title()) @@ -402,9 +399,9 @@ box.net.box.new = function(host, port, reconnect_timeout) request = self.processing.wch:get(1) end if self.s ~= nil and request ~= nil then - local res = { self.s:send(request) } - if res[1] ~= string.len(request) then - self:fatal("Error while write socket: %s", res[4]) + if not self.s:write(request) then + self:fatal("Error while write socket: %s", + box.errno.strerror()) end request = nil end @@ -423,13 +420,16 @@ box.net.box.new = function(host, port, reconnect_timeout) self.timeouted = {} end, + close = function(self) if self.closed then errorf("box.net.box: already closed (%s)", self:title()) end self.closed = true + local message = sprintf('box.net.box: connection was closed (%s)', self:title()) + self.process = function() error(message) end @@ -438,6 +438,11 @@ box.net.box.new = function(host, port, reconnect_timeout) -- wake up write fiber self.processing.rch:put(true, 0) self.processing.wch:put(true, 0) + + if self.s ~= nil then + self.s:close() + self.s = nil + end return true end } diff --git a/src/lua/bsdsocket.cc b/src/lua/bsdsocket.cc index 9d1f4d451a5d7e64484caea15576b5fa9b420eba..fffecbe053310ebb4eb816ed65aa55d9988a30ef 100644 --- a/src/lua/bsdsocket.cc +++ b/src/lua/bsdsocket.cc @@ -335,8 +335,8 @@ bsdsocket_local_resolve(const char *host, const char *port, } /* IPv6 */ - char ipv6[16]; - if (inet_pton(AF_INET6, host, ipv6) == 1) { + struct in6_addr ipv6; + if (inet_pton(AF_INET6, host, &ipv6) == 1) { struct sockaddr_in6 *inaddr6 = (struct sockaddr_in6 *) addr; if (*socklen < sizeof(*inaddr6)) { errno = ENOBUFS; @@ -344,8 +344,8 @@ bsdsocket_local_resolve(const char *host, const char *port, } memset(inaddr6, 0, sizeof(*inaddr6)); inaddr6->sin6_family = AF_INET6; - inaddr6->sin6_port = htonl(atol(port)); - memcpy(inaddr6->sin6_addr.s6_addr, ipv6, 16); + inaddr6->sin6_port = htons(atoi(port)); + memcpy(inaddr6->sin6_addr.s6_addr, &ipv6, sizeof(ipv6)); *socklen = sizeof(*inaddr6); return 0; } @@ -802,6 +802,24 @@ lbox_bsdsocket_peername(struct lua_State *L) return 1; } +static int +lbox_bsdsocket_accept(struct lua_State *L) +{ + int fh = lua_tointeger(L, 1); + + struct sockaddr_storage fa; + socklen_t len = sizeof(fa); + + int sc = accept(fh, (struct sockaddr*)&fa, &len); + if (sc < 0) { + lua_pushnil(L); + return 1; + } + lua_pushnumber(L, sc); + lbox_bsdsocket_push_addr(L, (struct sockaddr *)&fa, len); + return 2; +} + static int lbox_bsdsocket_recvfrom(struct lua_State *L) { @@ -844,18 +862,11 @@ tarantool_lua_bsdsocket_init(struct lua_State *L) { "name", lbox_bsdsocket_soname }, { "peer", lbox_bsdsocket_peername }, { "recvfrom", lbox_bsdsocket_recvfrom }, + { "accept", lbox_bsdsocket_accept }, { NULL, NULL } }; - if (luaL_dostring(L, bsdsocket_lua)) - panic("Error loading Lua source (internal)/bsdsocket.lua: %s", - lua_tostring(L, -1)); - - lua_getfield(L, LUA_GLOBALSINDEX, "box"); - lua_getfield(L, -1, "socket"); - lua_getfield(L, -1, "internal"); - - luaL_register(L, NULL, internal_methods); + luaL_register(L, "box.socket.internal", internal_methods); /* domains table */ lua_pushliteral(L, "DOMAIN"); @@ -930,5 +941,9 @@ tarantool_lua_bsdsocket_init(struct lua_State *L) lua_pushinteger(L, SOL_SOCKET); lua_rawset(L, -3); + if (luaL_dostring(L, bsdsocket_lua)) + panic("Error loading Lua source (internal)/bsdsocket.lua: %s", + lua_tostring(L, -1)); + lua_settop(L, top); } diff --git a/src/lua/bsdsocket.lua b/src/lua/bsdsocket.lua index 25dd1f6704b747632c674e7579e7ab0265f52731..b88c0cf56179c5167a18b775879001ceeef5cdf5 100644 --- a/src/lua/bsdsocket.lua +++ b/src/lua/bsdsocket.lua @@ -5,8 +5,16 @@ do local TIMEOUT_INFINITY = 500 * 365 * 86400 local ffi = require 'ffi' +local os_remove = os.remove +local boxerrno = box.errno +local internal = box.socket.internal +box.socket.internal = nil +package.loaded['box.socket.internal'] = nil ffi.cdef[[ + struct socket { + int fd; + }; typedef uint32_t socklen_t; typedef ptrdiff_t ssize_t; @@ -15,9 +23,9 @@ ffi.cdef[[ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); - ssize_t write(int fh, const char *octets, size_t len); + ssize_t write(int fd, const char *octets, size_t len); ssize_t read(int fd, void *buf, size_t count); - int listen(int fh, int backlog); + int listen(int fd, int backlog); int socket(int domain, int type, int protocol); int close(int s); int shutdown(int s, int how); @@ -30,7 +38,7 @@ ffi.cdef[[ int bsdsocket_local_resolve(const char *host, const char *port, struct sockaddr *addr, socklen_t *socklen); - int bsdsocket_nonblock(int fh, int mode); + int bsdsocket_nonblock(int fd, int mode); int setsockopt(int s, int level, int iname, const void *opt, size_t optlen); int getsockopt(int s, int level, int iname, void *ptr, size_t *optlen); @@ -45,12 +53,6 @@ ffi.cdef[[ struct protoent *getprotobyname(const char *name); ]] -ffi.cdef([[ -struct sockaddr { - char _data[256]; /* enough to fit any address */ -}; -]]); - local function sprintf(fmt, ...) return string.format(fmt, ...) end @@ -59,6 +61,35 @@ local function printf(fmt, ...) print(sprintf(fmt, ...)) end +local socket_t = ffi.typeof('struct socket'); + +local function socket_cdata_gc(socket) + ffi.C.close(socket.fd) +end + +local function check_socket(self) + local socket = type(self) == 'table' and self.socket + if not ffi.istype(socket_t, socket) then + error('Usage: socket:method()'); + end + return socket.fd +end + +local socket_mt +local function bless_socket(fd) + -- Make socket to be non-blocked by default + if ffi.C.bsdsocket_nonblock(fd, 1) < 0 then + local errno = box.errno() + ffi.C.close(fd) + box.errno(errno) + return nil + end + + local socket = ffi.new(socket_t, fd); + ffi.gc(socket, socket_cdata_gc) + return setmetatable({ socket = socket }, socket_mt) +end + local function get_ivalue(table, key) if type(key) == 'number' then return key @@ -85,6 +116,7 @@ end local socket_methods = {} socket_methods.errno = function(self) + check_socket(self) if self['_errno'] == nil then return 0 else @@ -93,6 +125,7 @@ socket_methods.errno = function(self) end socket_methods.error = function(self) + check_socket(self) if self['_errno'] == nil then return nil else @@ -100,18 +133,21 @@ socket_methods.error = function(self) end end -local addr = ffi.new('struct sockaddr') +-- addrbuf is equivalent to struct sockaddr_storage +local addrbuf = ffi.new('char[128]') -- enough to fit any address +local addr = ffi.cast('struct sockaddr *', addrbuf) local addr_len = ffi.new('socklen_t[1]') socket_methods.sysconnect = function(self, host, port) + local fd = check_socket(self) self._errno = nil host = tostring(host) port = tostring(port) - addr_len[0] = ffi.sizeof(addr) + addr_len[0] = ffi.sizeof(addrbuf) local res = ffi.C.bsdsocket_local_resolve(host, port, addr, addr_len) if res == 0 then - res = ffi.C.connect(self.fh, addr, addr_len[0]); + res = ffi.C.connect(fd, addr, addr_len[0]); if res == 0 then return true end @@ -121,8 +157,9 @@ socket_methods.sysconnect = function(self, host, port) end socket_methods.syswrite = function(self, octets) + local fd = check_socket(self) self._errno = nil - local done = ffi.C.write(self.fh, octets, string.len(octets)) + local done = ffi.C.write(fd, octets, string.len(octets)) if done < 0 then self._errno = box.errno() return nil @@ -130,10 +167,12 @@ socket_methods.syswrite = function(self, octets) return tonumber(done) end -socket_methods.sysread = function(self, len) +socket_methods.sysread = function(self, size) + local fd = check_socket(self) + size = size or 4096 self._errno = nil - local buf = ffi.new('char[?]', len) - local res = ffi.C.read(self.fh, buf, len) + local buf = ffi.new('char[?]', size) + local res = ffi.C.read(fd, buf, size) if res < 0 then self._errno = box.errno() @@ -145,16 +184,17 @@ socket_methods.sysread = function(self, len) end socket_methods.nonblock = function(self, nb) + local fd = check_socket(self) self._errno = nil local res if nb == nil then - res = ffi.C.bsdsocket_nonblock(self.fh, 0x80) + res = ffi.C.bsdsocket_nonblock(fd, 0x80) elseif nb then - res = ffi.C.bsdsocket_nonblock(self.fh, 1) + res = ffi.C.bsdsocket_nonblock(fd, 1) else - res = ffi.C.bsdsocket_nonblock(self.fh, 0) + res = ffi.C.bsdsocket_nonblock(fd, 0) end if res < 0 then @@ -170,48 +210,38 @@ socket_methods.nonblock = function(self, nb) end local function wait_safely(self, what, timeout) + local fd = check_socket(self) local f = box.fiber.self() local fid = f:id() + self._errno = nil + timeout = timeout or TIMEOUT_INFINITY + if self.waiters == nil then self.waiters = {} end self.waiters[fid] = f - local res = box.socket.internal.iowait(self.fh, what, timeout) + local wres = internal.iowait(fd, what, timeout) self.waiters[fid] = nil box.fiber.testcancel() - return res -end - -socket_methods.readable = function(self, timeout) - self._errno = nil - if timeout == nil then - timeout = TIMEOUT_INFINITY - end - - local wres = wait_safely(self, 0, timeout) if wres == 0 then self._errno = box.errno.ETIMEDOUT - return false + return 0 end - return true + return wres end -socket_methods.wait = function(self, timeout) - self._errno = nil - if timeout == nil then - timeout = TIMEOUT_INFINITY - end +socket_methods.readable = function(self, timeout) + return wait_safely(self, 0, timeout) ~= 0 +end +socket_methods.wait = function(self, timeout) local wres = wait_safely(self, 2, timeout) - if wres == 0 then - self._errno = box.errno.ETIMEDOUT - return + return nil end - local res = '' if bit.band(wres, 1) ~= 0 then res = res .. 'R' @@ -223,26 +253,16 @@ socket_methods.wait = function(self, timeout) end socket_methods.writable = function(self, timeout) - self._errno = nil - if timeout == nil then - timeout = TIMEOUT_INFINITY - end - - local wres = wait_safely(self, 1, timeout) - - if wres == 0 then - self._errno = box.errno.ETIMEDOUT - return false - end - return true + return wait_safely(self, 1, timeout) ~= 0 end socket_methods.listen = function(self, backlog) + local fd = check_socket(self) self._errno = nil if backlog == nil then backlog = 256 end - local res = ffi.C.listen(self.fh, backlog) + local res = ffi.C.listen(fd, backlog) if res < 0 then self._errno = box.errno() return false @@ -251,15 +271,16 @@ socket_methods.listen = function(self, backlog) end socket_methods.bind = function(self, host, port) + local fd = check_socket(self) self._errno = nil host = tostring(host) port = tostring(port) - addr_len[0] = ffi.sizeof(addr) + addr_len[0] = ffi.sizeof(addrbuf) local res = ffi.C.bsdsocket_local_resolve(host, port, addr, addr_len) if res == 0 then - res = ffi.C.bind(self.fh, addr, addr_len[0]); + res = ffi.C.bind(fd, addr, addr_len[0]); end if res == 0 then return true @@ -270,6 +291,7 @@ socket_methods.bind = function(self, host, port) end socket_methods.close = function(self) + local fd = check_socket(self) if self.waiters ~= nil then for fid, fiber in pairs(self.waiters) do fiber:wakeup() @@ -278,14 +300,16 @@ socket_methods.close = function(self) end self._errno = nil - if ffi.C.close(self.fh) < 0 then + if ffi.C.close(fd) < 0 then self._errno = box.errno() return false end + ffi.gc(self.socket, nil) return true end socket_methods.shutdown = function(self, how) + local fd = check_socket(self) local hvariants = { ['R'] = 0, ['READ'] = 0, @@ -304,7 +328,7 @@ socket_methods.shutdown = function(self, how) ihow = 2 end self._errno = nil - if ffi.C.shutdown(self.fh, ihow) < 0 then + if ffi.C.shutdown(fd, ihow) < 0 then self._errno = box.errno() return false end @@ -312,7 +336,9 @@ socket_methods.shutdown = function(self, how) end socket_methods.setsockopt = function(self, level, name, value) - local info = get_ivalue(box.socket.internal.SO_OPT, name) + local fd = check_socket(self) + + local info = get_ivalue(internal.SO_OPT, name) if info == nil then error(sprintf("Unknown socket option name: %s", tostring(name))) @@ -326,7 +352,7 @@ socket_methods.setsockopt = function(self, level, name, value) if type(level) == 'string' then if level == 'SOL_SOCKET' then - level = box.socket.internal.SOL_SOCKET + level = internal.SOL_SOCKET else local p = ffi.C.getprotobyname(level) if p == nil then @@ -349,7 +375,7 @@ socket_methods.setsockopt = function(self, level, name, value) if info.type == 1 then local value = ffi.new("int[1]", value) - local res = ffi.C.setsockopt(self.fh, + local res = ffi.C.setsockopt(fd, level, info.iname, value, ffi.sizeof('int')) if res < 0 then @@ -360,7 +386,7 @@ socket_methods.setsockopt = function(self, level, name, value) end if info.type == 2 then - local res = ffi.C.setsockopt(self.fh, + local res = ffi.C.setsockopt(fd, level, info.iname, value, ffi.sizeof('size_t')) if res < 0 then self._errno = box.errno() @@ -376,7 +402,9 @@ socket_methods.setsockopt = function(self, level, name, value) end socket_methods.getsockopt = function(self, level, name) - local info = get_ivalue(box.socket.internal.SO_OPT, name) + local fd = check_socket(self) + + local info = get_ivalue(internal.SO_OPT, name) if info == nil then error(sprintf("Unknown socket option name: %s", tostring(name))) @@ -386,7 +414,7 @@ socket_methods.getsockopt = function(self, level, name) if type(level) == 'string' then if level == 'SOL_SOCKET' then - level = box.socket.internal.SOL_SOCKET + level = internal.SOL_SOCKET else local p = ffi.C.getprotobyname(level) if p == nil then @@ -403,7 +431,7 @@ socket_methods.getsockopt = function(self, level, name) if info.type == 1 then local value = ffi.new("int[1]", 0) local len = ffi.new("size_t[1]", ffi.sizeof('int')) - local res = ffi.C.getsockopt(self.fh, level, info.iname, value, len) + local res = ffi.C.getsockopt(fd, level, info.iname, value, len) if res < 0 then self._errno = box.errno() @@ -419,7 +447,7 @@ socket_methods.getsockopt = function(self, level, name) if info.type == 2 then local value = ffi.new("char[256]", { 0 }) local len = ffi.new("size_t[1]", 256) - local res = ffi.C.getsockopt(self.fh, level, info.iname, value, len) + local res = ffi.C.getsockopt(fd, level, info.iname, value, len) if res < 0 then self._errno = box.errno() return nil @@ -434,14 +462,15 @@ socket_methods.getsockopt = function(self, level, name) end socket_methods.linger = function(self, active, timeout) + local fd = check_socket(self) - local info = box.socket.internal.SO_OPT.SO_LINGER + local info = internal.SO_OPT.SO_LINGER self._errno = nil if active == nil then local value = ffi.new("linger_t[1]") local len = ffi.new("size_t[1]", 2 * ffi.sizeof('int')) - local res = ffi.C.getsockopt(self.fh, - box.socket.internal.SOL_SOCKET, info.iname, value, len) + local res = ffi.C.getsockopt(fd, + internal.SOL_SOCKET, info.iname, value, len) if res < 0 then self._errno = box.errno() return nil @@ -468,8 +497,8 @@ socket_methods.linger = function(self, active, timeout) local value = ffi.new("linger_t[1]", { { active = iactive, timeout = timeout } }) local len = 2 * ffi.sizeof('int') - local res = ffi.C.setsockopt(self.fh, - box.socket.internal.SOL_SOCKET, info.iname, value, len) + local res = ffi.C.setsockopt(fd, + internal.SOL_SOCKET, info.iname, value, len) if res < 0 then self._errno = box.errno() return nil @@ -479,25 +508,15 @@ socket_methods.linger = function(self, active, timeout) end socket_methods.accept = function(self) - + local fd = check_socket(self) self._errno = nil - local fh = ffi.C.accept(self.fh, nil, nil) - - if fh < 1 then + local cfd, from = internal.accept(fd) + if cfd == nil then self._errno = box.errno() return nil end - - fh = tonumber(fh) - - -- Make socket to be non-blocked by default - -- ignore result - ffi.C.bsdsocket_nonblock(fh, 1) - - local socket = { fh = fh } - setmetatable(socket, box.socket.internal.socket_mt) - return socket + return bless_socket(cfd), from end local function readchunk(self, size, timeout) @@ -521,7 +540,7 @@ local function readchunk(self, size, timeout) timeout = timeout - ( box.time() - started ) - local data = self:sysread(4096) + local data = self:sysread() if data ~= nil then self.rbuf = self.rbuf .. data if string.len(self.rbuf) >= size then @@ -552,18 +571,24 @@ local function readline_check(self, eols, limit) if string.len(rbuf) == 0 then return nil end + + local shortest for i, eol in pairs(eols) do if string.len(rbuf) >= string.len(eol) then local data = string.match(rbuf, "^(.-" .. eol .. ")") if data ~= nil then if string.len(data) > limit then - return string.sub(data, 1, limit) + data = string.sub(data, 1, limit) + end + if shortest == nil then + shortest = data + elseif #shortest > #data then + shortest = data end - return data end end end - return nil + return shortest end local function readline(self, limit, eol, timeout) @@ -589,7 +614,7 @@ local function readline(self, limit, eol, timeout) timeout = timeout - ( box.time() - started ) - local data = self:sysread(4096) + local data = self:sysread() if data ~= nil then self.rbuf = self.rbuf .. data @@ -621,6 +646,7 @@ local function readline(self, limit, eol, timeout) end socket_methods.read = function(self, opts, timeout) + check_socket(self) timeout = timeout and tonumber(timeout) or TIMEOUT_INFINITY if type(opts) == 'number' then return readchunk(self, opts, timeout) @@ -641,6 +667,7 @@ socket_methods.read = function(self, opts, timeout) end socket_methods.write = function(self, octets, timeout) + check_socket(self) if timeout == nil then timeout = TIMEOUT_INFINITY end @@ -666,10 +693,11 @@ socket_methods.write = function(self, octets, timeout) end socket_methods.send = function(self, octets, flags) - local iflags = get_iflags(box.socket.internal.SEND_FLAGS, flags) + local fd = check_socket(self) + local iflags = get_iflags(internal.SEND_FLAGS, flags) self._errno = nil - local res = ffi.C.send(self.fh, octets, string.len(octets), iflags) + local res = ffi.C.send(fd, octets, string.len(octets), iflags) if res == -1 then self._errno = box.errno() return false @@ -678,19 +706,18 @@ socket_methods.send = function(self, octets, flags) end socket_methods.recv = function(self, size, flags) - local iflags = get_iflags(box.socket.internal.SEND_FLAGS, flags) + local fd = check_socket(self) + local iflags = get_iflags(internal.SEND_FLAGS, flags) if iflags == nil then self._errno = box.errno.EINVAL return nil end - if size == nil then - size = 512 - end + size = size or 512 self._errno = nil local buf = ffi.new("char[?]", size) - local res = ffi.C.recv(self.fh, buf, size, iflags) + local res = ffi.C.recv(fd, buf, size, iflags) if res == -1 then self._errno = box.errno() @@ -700,13 +727,16 @@ socket_methods.recv = function(self, size, flags) end socket_methods.recvfrom = function(self, size, flags) - local iflags = get_iflags(box.socket.internal.SEND_FLAGS, flags) + local fd = check_socket(self) + local iflags = get_iflags(internal.SEND_FLAGS, flags) if iflags == nil then self._errno = box.errno.EINVAL return nil end + + size = size or 512 self._errno = nil - local res, from = box.socket.internal.recvfrom(self.fh, size, iflags) + local res, from = internal.recvfrom(fd, size, iflags) if res == nil then self._errno = box.errno() return nil @@ -715,7 +745,8 @@ socket_methods.recvfrom = function(self, size, flags) end socket_methods.sendto = function(self, host, port, octets, flags) - local iflags = get_iflags(box.socket.internal.SEND_FLAGS, flags) + local fd = check_socket(self) + local iflags = get_iflags(internal.SEND_FLAGS, flags) if iflags == nil then self._errno = box.errno.EINVAL @@ -731,10 +762,10 @@ socket_methods.sendto = function(self, host, port, octets, flags) port = tostring(port) octets = tostring(octets) - addr_len[0] = ffi.sizeof(addr) + addr_len[0] = ffi.sizeof(addrbuf) local res = ffi.C.bsdsocket_local_resolve(host, port, addr, addr_len) if res == 0 then - res = ffi.C.sendto(self.fh, octets, string.len(octets), iflags, + res = ffi.C.sendto(fd, octets, string.len(octets), iflags, addr, addr_len[0]) end if res < 0 then @@ -745,13 +776,13 @@ socket_methods.sendto = function(self, host, port, octets, flags) end local function create_socket(domain, stype, proto) - local idomain = get_ivalue(box.socket.internal.DOMAIN, domain) + local idomain = get_ivalue(internal.DOMAIN, domain) if idomain == nil then box.errno(box.errno.EINVAL) return nil end - local itype = get_ivalue(box.socket.internal.SO_TYPE, stype) + local itype = get_ivalue(internal.SO_TYPE, stype) if itype == nil then box.errno(box.errno.EINVAL) return nil @@ -771,24 +802,11 @@ local function create_socket(domain, stype, proto) iproto = p.p_proto end - local fh = ffi.C.socket(idomain, itype, iproto) - if fh < 0 then - return nil - end - - fh = tonumber(fh) - - -- Make socket to be non-blocked by default - if ffi.C.bsdsocket_nonblock(fh, 1) < 0 then - local errno = box.errno() - ffi.C.close(fh) - box.errno(errno) + local fd = ffi.C.socket(idomain, itype, iproto) + if fd < 1 then return nil end - - local socket = { fh = fh } - setmetatable(socket, box.socket.internal.socket_mt) - return socket + return bless_socket(fd) end local function getaddrinfo(host, port, timeout, opts) @@ -806,7 +824,7 @@ local function getaddrinfo(host, port, timeout, opts) local ga_opts = {} if opts ~= nil then if opts.type ~= nil then - local itype = get_ivalue(box.socket.internal.SO_TYPE, opts.type) + local itype = get_ivalue(internal.SO_TYPE, opts.type) if itype == nil then self._errno = box.errno.EINVAL return nil @@ -815,7 +833,7 @@ local function getaddrinfo(host, port, timeout, opts) end if opts.family ~= nil then - local ifamily = get_ivalue(box.socket.internal.DOMAIN, opts.family) + local ifamily = get_ivalue(internal.DOMAIN, opts.family) if ifamily == nil then self._errno = box.errno.EINVAL return nil @@ -834,7 +852,7 @@ local function getaddrinfo(host, port, timeout, opts) if opts.flags ~= nil then ga_opts.flags = - get_iflags(box.socket.internal.AI_FLAGS, opts.flags) + get_iflags(internal.AI_FLAGS, opts.flags) if ga_opts.flags == nil then self._errno = box.errno() return nil @@ -843,7 +861,7 @@ local function getaddrinfo(host, port, timeout, opts) end - local r = box.socket.internal.getaddrinfo(host, port, timeout, ga_opts) + local r = internal.getaddrinfo(host, port, timeout, ga_opts) if r == nil then self._errno = box.errno() else @@ -852,26 +870,50 @@ local function getaddrinfo(host, port, timeout, opts) return r end +local soname_mt = { + __tostring = function(si) + if si.host == nil and si.port == nil then + return '' + end + if si.host == nil then + return sprintf('%s:%s', '0', tostring(si.port)) + end + + if si.port == nil then + return sprintf('%s:%', tostring(si.host), 0) + end + return sprintf('%s:%s', tostring(si.host), tostring(si.port)) + end +} + socket_methods.name = function(self) - local aka = box.socket.internal.name(self.fh) + local fd = check_socket(self) + local aka = internal.name(fd) if aka == nil then self._errno = box.errno() return nil end self._errno = nil + setmetatable(aka, soname_mt) return aka end socket_methods.peer = function(self) - local peer = box.socket.internal.peer(self.fh) + local fd = check_socket(self) + local peer = internal.peer(fd) if peer == nil then self._errno = box.errno() return nil end self._errno = nil + setmetatable(peer, soname_mt) return peer end +socket_methods.fd = function(self) + return check_socket(self) +end + -- tcp connector local function tcp_connect_remote(remote, timeout) local s = create_socket(remote.family, remote.type, remote.protocol) @@ -924,6 +966,11 @@ local function tcp_connect(host, port, timeout) if dns == nil then return nil end + + if #dns == 0 then + box.errno(box.errno.EINVAL) + return nil + end for i, remote in pairs(dns) do timeout = stop - box.time() if timeout <= 0 then @@ -938,105 +985,122 @@ local function tcp_connect(host, port, timeout) return nil end -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 function tcp_server_handler(server, sc, from) + box.fiber.name(sprintf("%s/client/%s:%s", server.name, from.host, from.port)) + server.handler(sc, from) + sc:close() +end - 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 +local function tcp_server_loop(server, s, addr) + box.fiber.name(sprintf("%s/listen/%s:%s", server.name, addr.host, addr.port)) + while s:readable() do + local sc, from = s:accept() + if sc == nil then + break end - - -- errors - if not ok then - if s ~= nil then - s:close() - end - local save_errno = box.errno() - for _, s in pairs(slist) do - s:close() - end - box.errno(save_errno) - return nil - end - - table.insert(slist, s) + box.fiber.wrap(tcp_server_handler, server, sc, from) + end + if addr.family == 'AF_UNIX' and addr.port then + os_remove(addr.port) -- remove unix socket end - - local server = { s = slist } +end - server.stop = function() - if #server.s == 0 then - return false - end - for _, s in pairs(server.s) do - s:close() - end - server.s = {} +local function tcp_server_usage() + error('Usage: socket.tcp_server(host, port, handler | opts)') +end + +local function tcp_server_bind(s, addr) + if s:bind(addr.host, addr.port) then return true end - for _, s in pairs(server.s) do - box.fiber.wrap(function(s) - box.fiber.name(sprintf("listen_fd=%d",s.fh)) + if addr.family ~= 'AF_UNIX' then + return false + end - while s:readable() do - - local sc = s:accept() + if boxerrno() ~= boxerrno.EADDRINUSE then + return false + end - if sc == nil then - break - end + local save_errno = boxerrno() - box.fiber.wrap(function(sc) - pcall(handler, sc) - sc:close() - end, sc) - end - end, s) + local sc = tcp_connect(addr.host, addr.port) + if sc ~= nil then + sc:close() + boxerrno(save_errno) + return false end - return server + if boxerrno() ~= boxerrno.ECONNREFUSED then + boxerrno(save_errno) + return false + end + os_remove(addr.port) + return s:bind(addr.host, addr.port) 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)") +local function tcp_server(host, port, opts, timeout) + local server = {} + if type(opts) == 'function' then + server.handler = opts + elseif type(opts) == 'table' then + if type(opts.handler) ~='function' or (opts.prepare ~= nil and + type(opts.prepare) ~= 'function') then + tcp_server_usage() + end + for k, v in pairs(opts) do + server[k] = v + end + else + tcp_server_usage() end - + server.name = server.name or 'server' + timeout = timeout and tonumber(timeout) or TIMEOUT_INFINITY + local dns if host == 'unix/' then - return tcp_server_remote({{host = host, port = port, protocol = 0, - family = 'PF_UNIX', type = 'SOCK_STREAM' }}, prepare, handler) + dns = {{host = host, port = port, family = 'AF_UNIX', protocol = 0, + type = 'SOCK_STREAM' }} + else + dns = getaddrinfo(host, port, timeout, { type = 'SOCK_STREAM', + flags = 'AI_PASSIVE'}) + if dns == nil then + return nil + end end - local dns = getaddrinfo(host, port, timeout, { type = 'SOCK_STREAM', - protocol = 'tcp' }) - if dns == nil then - return nil + for _, addr in ipairs(dns) do + local s = create_socket(addr.family, addr.type, addr.protocol) + if s ~= nil then + local backlog + if server.prepare then + backlog = server.prepare(s) + else + s:setsockopt('SOL_SOCKET', 'SO_REUSEADDR', 1) -- ignore error + end + if not tcp_server_bind(s, addr) or not s:listen(backlog) then + local save_errno = box.errno() + s:close() + box.errno(save_errno) + return nil + end + box.fiber.wrap(tcp_server_loop, server, s, addr) + return s, addr + end end - return tcp_server_remote(dns, prepare, handler) + -- DNS resolved successfully, but addresss family is not supported + box.errno(box.errno.EAFNOSUPPORT) + return nil end -box.socket.internal = { - socket_mt = { +socket_mt = { __index = socket_methods, __tostring = function(self) + local fd = check_socket(self) + local save_errno = self._errno - local name = sprintf("fd %d", self.fh) + local name = sprintf("fd %d", fd) local aka = self:name() if aka ~= nil then name = sprintf("%s, aka %s:%s", name, aka.host, aka.port) @@ -1048,7 +1112,6 @@ box.socket.internal = { self._errno = save_errno return name end - }, } setmetatable(box.socket, { diff --git a/test/box/bsdsocket.result b/test/box/bsdsocket.result index d67a0334e1fadc52921b00da572f2ed446c7fea1..144966a65f22feefc86b80caedd52d2ae9b5f03f 100644 --- a/test/box/bsdsocket.result +++ b/test/box/bsdsocket.result @@ -30,6 +30,9 @@ lua type(s:error()) --- - nil ... +lua for k in pairs(getmetatable(s).__index) do local r, msg = pcall(s[k]); if not msg:match('Usage:') then print(k) end end +--- +... lua s:nonblock(false) --- - false @@ -177,7 +180,7 @@ lua sc:readable(.5) --- - true ... -lua sc:sysread(4096) +lua sc:sysread() --- - ok ... @@ -219,7 +222,7 @@ lua s:getsockopt('SOL_SOCKET', 'SO_DEBUG') ... lua s:setsockopt('SOL_SOCKET', 'SO_ACCEPTCONN', 1) --- -error: '[string "-- bsdsocket.lua (internal file)..."]:322: Socket option SO_ACCEPTCONN is read only' +error: '[string "-- bsdsocket.lua (internal file)..."]:348: Socket option SO_ACCEPTCONN is read only' ... lua s:getsockopt('SOL_SOCKET', 'SO_RCVBUF') > 32 --- @@ -303,9 +306,20 @@ lua sc:write('Hello, world') --- - true ... -lua sa = s:accept() +lua sa, addr = s:accept() +--- +... +lua addr2 = sa:name() --- ... +lua addr2.host == addr.host +--- + - true +... +lua addr2.family == addr.family +--- + - true +... lua sa:nonblock(1) --- - true @@ -458,7 +472,7 @@ lua s:bind('unix/', '/tmp/tarantool-test-socket') --- - true ... -lua string.match(tostring(sc), 'fd %d+, aka unix/:/tmp/tarantool%-test%-socket') ~= nil +lua string.match(tostring(s), 'fd %d+, aka unix/:/tmp/tarantool%-test%-socket') ~= nil --- - true ... @@ -576,6 +590,10 @@ lua box.cjson.encode(sc:name()) --- - {"host":"0.0.0.0","family":"AF_INET","type":"SOCK_STREAM","protocol":"tcp","port":0} ... +lua sc:name() +--- + - 0.0.0.0:0 +... lua sc:nonblock(true) --- - true @@ -614,7 +632,7 @@ lua s:readable(10) --- - true ... -lua local d, from = s:recvfrom(4096) print(' - ', from.port > 0) from.port = 'Random port' return box.cjson.encode{d, from} +lua local d, from = s:recvfrom() print(' - ', from.port > 0) from.port = 'Random port' return box.cjson.encode{d, from} --- - true - ["Hello, world, 2",{"host":"127.0.0.1","family":"AF_INET","port":"Random port"}] @@ -676,7 +694,7 @@ lua sc:readable(1) --- - true ... -lua data_r, from_r = sc:recvfrom(4096) +lua data_r, from_r = sc:recvfrom() --- ... lua data_r @@ -812,11 +830,19 @@ lua box.socket.tcp_connect('unix/', '/tmp/tarantool-test-socket'), box.errno() = - nil - true ... -lua s = box.socket.tcp_connect('127.0.0.1', box.cfg.primary_port) +lua sa = box.socket.tcp_connect('127.0.0.1', box.cfg.primary_port) +--- +... +lua sa:close() +--- + - true +... +lua sa.socket.fd = 512 --- ... -lua sa = { fh = 512 } setmetatable(sa, getmetatable(s)) +lua sa:fd() --- + - 512 ... lua tostring(sa) --- @@ -830,6 +856,12 @@ lua sa:writable(0) --- - true ... +lua sa = nil +--- +... +lua s = box.socket.tcp_connect('127.0.0.1', box.cfg.primary_port) +--- +... lua ch = box.ipc.channel() --- ... @@ -993,9 +1025,13 @@ lua s:close() --- - true ... -lua server = box.socket.tcp_server('unix/', path, function(s) s:write('Hello, world') end) +lua server, addr = box.socket.tcp_server('unix/', path, function(s) s:write('Hello, world') end) --- ... +lua type(addr) +--- + - table +... lua server ~= nil --- - true @@ -1014,7 +1050,140 @@ lua client:read(123) --- - Hello, world ... -lua server:stop() +lua client:close() +--- + - true +... +lua server:close() +--- + - true +... +lua box.fiber.sleep(.5) +--- +... +unix socket was removed +lua server, addr = box.socket.tcp_server('localhost', 0, { handler = function(s) s:read(2); s:write('Hello, world') end, name = 'testserv'}) +--- +... +lua type(addr) +--- + - table +... +lua server ~= nil +--- + - true +... +lua addr2 = server:name() +--- +... +lua addr.host == addr2.host +--- + - true +... +lua addr.family == addr2.family --- - true ... +lua box.fiber.sleep(.5) +--- +... +lua client = box.socket.tcp_connect(addr2.host, addr2.port) +--- +... +lua client ~= nil +--- + - true +... +lua cnt = 0 +--- +... +lua for i=100,200 do local f = box.fiber.find(i); if f and f:name():match('^testserv/') then cnt = cnt + 1; end; end +--- +... +lua cnt +--- + - 2 +... +lua client:write('hi') +--- + - true +... +lua client:read(123) +--- + - Hello, world +... +lua client:close() +--- + - true +... +lua server:close() +--- + - true +... +lua s = box.socket('AF_UNIX', 'SOCK_STREAM', 'ip') +--- +... +lua s:bind('unix/', '/tmp/tarantool-test-socket') +--- + - true +... +lua s:listen() +--- + - true +... +lua s = nil +--- +... +lua collectgarbage('collect') +--- + - 0 +... +lua collectgarbage('collect') +--- + - 0 +... +lua client, errno = box.socket.tcp_connect('unix/', '/tmp/tarantool-test-socket'), box.errno() +--- +... +lua errno == box.errno.ECONNREFUSED +--- + - true +... +test bind unix socket if old socket is exists +lua s = box.socket('AF_UNIX', 'SOCK_STREAM', 0) +--- +... +lua s:bind('unix/', '/tmp/tarantool-test-socket') +--- + - true +... +lua s:listen() +--- + - true +... +lua s:close() +--- + - true +... +lua s = box.socket('AF_UNIX', 'SOCK_STREAM', 0) +--- +... +lua s:bind('unix/', '/tmp/tarantool-test-socket') +--- + - false +... +lua s = box.socket.tcp_server('unix/', '/tmp/tarantool-test-socket', function() end) +--- +... +lua s ~= nil +--- + - true +... +lua s:close() +--- + - true +... +lua box.cjson.encode{ box.socket.tcp_connect('test:test@localhost:3303') == nil, box.errno.strerror() } +--- + - [true,"Invalid argument"] +... diff --git a/test/box/bsdsocket.test b/test/box/bsdsocket.test index 64ac95aeb20c81022fb51e048d0efb7d1312f0da..194f7e5edf76023e56dc546bc1bb997ac99ed433 100644 --- a/test/box/bsdsocket.test +++ b/test/box/bsdsocket.test @@ -13,6 +13,8 @@ exec admin "lua type(s)" exec admin "lua string.match(tostring(s), 'fd %d+, aka 0%.0%.0%.0:0') ~= nil" exec admin "lua s:errno()" exec admin "lua type(s:error())" +# Invalid arguments +exec admin "lua for k in pairs(getmetatable(s).__index) do local r, msg = pcall(s[k]); if not msg:match('Usage:') then print(k) end end" exec admin "lua s:nonblock(false)" exec admin "lua s:sysconnect('127.0.0.1', box.cfg.primary_port)" @@ -56,7 +58,7 @@ exec admin "lua sc:nonblock(false)" exec admin "lua sc:sysconnect('127.0.0.1', 3457)" exec admin "lua sc:nonblock(true)" exec admin "lua sc:readable(.5)" -exec admin "lua sc:sysread(4096)" +exec admin "lua sc:sysread()" exec admin "lua string.match(tostring(sc), ', peer') ~= nil" exec admin "lua #sevres" exec admin "lua sevres[1].host" @@ -93,7 +95,10 @@ exec admin "lua r or errno == box.errno.EINPROGRESS" exec admin "lua sc:writable(10)" exec admin "lua sc:write('Hello, world')" -exec admin "lua sa = s:accept()" +exec admin "lua sa, addr = s:accept()" +exec admin "lua addr2 = sa:name()" +exec admin "lua addr2.host == addr.host" +exec admin "lua addr2.family == addr.family" exec admin "lua sa:nonblock(1)" exec admin "lua sa:read(8)" exec admin "lua sa:read(3)" @@ -139,7 +144,7 @@ exec admin "lua s:nonblock()" if os.path.exists('/tmp/tarantool-test-socket'): os.unlink('/tmp/tarantool-test-socket') exec admin "lua s:bind('unix/', '/tmp/tarantool-test-socket')" -exec admin "lua string.match(tostring(sc), 'fd %d+, aka unix/:/tmp/tarantool%-test%-socket') ~= nil" +exec admin "lua string.match(tostring(s), 'fd %d+, aka unix/:/tmp/tarantool%-test%-socket') ~= nil" exec admin "lua s:listen(1234)" exec admin "lua sc = box.socket('PF_UNIX', 'SOCK_STREAM', 'ip')" @@ -181,6 +186,7 @@ exec admin "lua box.cjson.encode(box.socket.getaddrinfo('ya.ru', '80', { flags = exec admin "lua sc = box.socket('AF_INET', 'SOCK_STREAM', 'tcp')" exec admin "lua box.cjson.encode(sc:name())" +exec admin "lua sc:name()" exec admin "lua sc:nonblock(true)" exec admin "lua sc:close()" @@ -193,7 +199,7 @@ exec admin "lua s:recv(4096)" exec admin "lua sc:sendto('127.0.0.1', 3548, 'Hello, world, 2')" exec admin "lua s:readable(10)" -exec admin "lua local d, from = s:recvfrom(4096) print(' - ', from.port > 0) from.port = 'Random port' return box.cjson.encode{d, from}" +exec admin "lua local d, from = s:recvfrom() print(' - ', from.port > 0) from.port = 'Random port' return box.cjson.encode{d, from}" exec admin "lua s:close()" exec admin "lua sc:close()" @@ -210,7 +216,7 @@ exec admin "lua data, from = s:recvfrom(10)" exec admin "lua data" exec admin "lua s:sendto(from.host, from.port, 'Hello, hello!')" exec admin "lua sc:readable(1)" -exec admin "lua data_r, from_r = sc:recvfrom(4096)" +exec admin "lua data_r, from_r = sc:recvfrom()" exec admin "lua data_r" exec admin "lua from_r.host" exec admin "lua from_r.port == s:name().port" @@ -261,12 +267,16 @@ if os.path.exists(path): os.unlink(path) exec admin "lua box.socket.tcp_connect('unix/', '{}'), box.errno() == box.errno.ENOENT".format(path) -exec admin "lua s = box.socket.tcp_connect('127.0.0.1', box.cfg.primary_port)" -exec admin "lua sa = { fh = 512 } setmetatable(sa, getmetatable(s))" +exec admin "lua sa = box.socket.tcp_connect('127.0.0.1', box.cfg.primary_port)" +exec admin "lua sa:close()" +exec admin "lua sa.socket.fd = 512" +exec admin "lua sa:fd()" exec admin "lua tostring(sa)" exec admin "lua sa:readable(0)" exec admin "lua sa:writable(0)" +exec admin "lua sa = nil" +exec admin "lua s = box.socket.tcp_connect('127.0.0.1', box.cfg.primary_port)" exec admin "lua ch = box.ipc.channel()" exec admin "lua f = box.fiber.wrap(function() s:read(12) ch:put(true) end)" exec admin "lua box.fiber.sleep(.1)" @@ -318,12 +328,63 @@ exec admin "lua s:close()" os.unlink(path) -exec admin "lua server = box.socket.tcp_server('unix/', path, function(s) s:write('Hello, world') end)" +exec admin "lua server, addr = box.socket.tcp_server('unix/', path, function(s) s:write('Hello, world') end)" +exec admin "lua type(addr)" exec admin "lua server ~= nil" exec admin "lua box.fiber.sleep(.5)" exec admin "lua client = box.socket.tcp_connect('unix/', path)" exec admin "lua client ~= nil" exec admin "lua client:read(123)" -exec admin "lua server:stop()" +exec admin "lua client:close()" +exec admin "lua server:close()" +exec admin "lua box.fiber.sleep(.5)" + +if not os.path.exists(path): + print 'unix socket was removed' + +exec admin "lua server, addr = box.socket.tcp_server('localhost', 0, { handler = function(s) s:read(2); s:write('Hello, world') end, name = 'testserv'})" +exec admin "lua type(addr)" +exec admin "lua server ~= nil" +exec admin "lua addr2 = server:name()" +exec admin "lua addr.host == addr2.host" +exec admin "lua addr.family == addr2.family" +exec admin "lua box.fiber.sleep(.5)" +exec admin "lua client = box.socket.tcp_connect(addr2.host, addr2.port)" +exec admin "lua client ~= nil" +# Check that listen and client fibers have appropriate names +exec admin "lua cnt = 0" +exec admin "lua for i=100,200 do local f = box.fiber.find(i); if f and f:name():match('^testserv/') then cnt = cnt + 1; end; end" +exec admin "lua cnt" +exec admin "lua client:write('hi')" +exec admin "lua client:read(123)" +exec admin "lua client:close()" +exec admin "lua server:close()" +# Test that socket is closed on GC +exec admin "lua s = box.socket('AF_UNIX', 'SOCK_STREAM', 'ip')" +exec admin "lua s:bind('unix/', '{}')".format(path) +exec admin "lua s:listen()" +exec admin "lua s = nil" +exec admin "lua collectgarbage('collect')" +exec admin "lua collectgarbage('collect')" +exec admin "lua client, errno = box.socket.tcp_connect('unix/', '{}'), box.errno()".format(path) +exec admin "lua errno == box.errno.ECONNREFUSED" os.unlink(path) + +print 'test bind unix socket if old socket is exists' +exec admin "lua s = box.socket('AF_UNIX', 'SOCK_STREAM', 0)" +exec admin "lua s:bind('unix/', '{}')".format(path) +exec admin "lua s:listen()" +exec admin "lua s:close()" +if not os.path.exists(path): + print 'unix socket was removed: test failed' +exec admin "lua s = box.socket('AF_UNIX', 'SOCK_STREAM', 0)" +exec admin "lua s:bind('unix/', '{}')".format(path) +exec admin "lua s = box.socket.tcp_server('unix/', '{}', function() end)".format(path) +exec admin "lua s ~= nil" +exec admin "lua s:close()" +if os.path.exists(path): + print 'unix socket was not removed: test failed' + + +exec admin "lua box.cjson.encode{ box.socket.tcp_connect('test:test@localhost:3303') == nil, box.errno.strerror() }" diff --git a/third_party/luajit b/third_party/luajit index 880ca300e8fb7b432b9d25ed377db2102e4cb63d..41156fe1cdd6b60a5e8d9855c57699e89ccfbf97 160000 --- a/third_party/luajit +++ b/third_party/luajit @@ -1 +1 @@ -Subproject commit 880ca300e8fb7b432b9d25ed377db2102e4cb63d +Subproject commit 41156fe1cdd6b60a5e8d9855c57699e89ccfbf97