Skip to content
Snippets Groups Projects
Commit 14fb3845 authored by Roman Tsisyk's avatar Roman Tsisyk
Browse files

Merge branch 'stable'

Conflicts:
	src/lua/bsdsocket.cc
	src/lua/bsdsocket.lua
	test/box/bsdsocket.result
	test/box/bsdsocket.test
parents f3d1e75c 86fbbc56
No related branches found
No related tags found
No related merge requests found
-- bsdsocket.lua (internal file)
do
local TIMEOUT_INFINITY = 500 * 365 * 86400
local ffi = require('ffi')
local boxerrno = require('errno')
local internal = require('socket.internal')
local fiber = require('fiber')
package.loaded['socket.internal'] = nil
ffi.cdef[[
struct socket {
int fd;
};
typedef uint32_t socklen_t;
typedef ptrdiff_t ssize_t;
......@@ -18,9 +20,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);
......@@ -33,7 +35,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);
......@@ -48,12 +50,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
......@@ -62,6 +58,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
......@@ -86,9 +111,9 @@ local function get_iflags(table, flags)
return res
end
local socket_mt
local socket_methods = {}
socket_methods.errno = function(self)
check_socket(self)
if self['_errno'] == nil then
return 0
else
......@@ -97,6 +122,7 @@ socket_methods.errno = function(self)
end
socket_methods.error = function(self)
check_socket(self)
if self['_errno'] == nil then
return nil
else
......@@ -104,18 +130,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
......@@ -125,8 +154,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 = boxerrno()
return nil
......@@ -135,9 +165,10 @@ socket_methods.syswrite = function(self, octets)
end
socket_methods.sysread = function(self, len)
local fd = check_socket(self)
self._errno = nil
local buf = ffi.new('char[?]', len)
local res = ffi.C.read(self.fh, buf, len)
local res = ffi.C.read(fd, buf, len)
if res < 0 then
self._errno = boxerrno()
......@@ -149,16 +180,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
......@@ -174,48 +206,34 @@ socket_methods.nonblock = function(self, nb)
end
local function wait_safely(self, what, timeout)
local fd = check_socket(self)
local f = 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] = true
local res = internal.iowait(self.fh, what, timeout)
local res = internal.iowait(fd, what, timeout)
self.waiters[fid] = nil
fiber.testcancel()
if res == 0 then
self._errno = boxerrno.ETIMEDOUT
return 0
end
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 = boxerrno.ETIMEDOUT
return false
end
return true
return wait_safely(self, 0, timeout) ~= 0
end
socket_methods.wait = function(self, timeout)
self._errno = nil
if timeout == nil then
timeout = TIMEOUT_INFINITY
end
local wres = wait_safely(self, 2, timeout)
if wres == 0 then
self._errno = boxerrno.ETIMEDOUT
return
end
local res = ''
if bit.band(wres, 1) ~= 0 then
res = res .. 'R'
......@@ -227,26 +245,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 = boxerrno.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 = boxerrno()
return false
......@@ -255,15 +263,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
......@@ -274,6 +283,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 in pairs(self.waiters) do
internal.abort(fid)
......@@ -282,14 +292,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 = boxerrno()
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,
......@@ -308,7 +320,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 = boxerrno()
return false
end
......@@ -316,6 +328,8 @@ socket_methods.shutdown = function(self, how)
end
socket_methods.setsockopt = function(self, level, name, value)
local fd = check_socket(self)
local info = get_ivalue(internal.SO_OPT, name)
if info == nil then
......@@ -353,7 +367,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
......@@ -364,7 +378,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 = boxerrno()
......@@ -380,6 +394,8 @@ socket_methods.setsockopt = function(self, level, name, value)
end
socket_methods.getsockopt = function(self, level, name)
local fd = check_socket(self)
local info = get_ivalue(internal.SO_OPT, name)
if info == nil then
......@@ -407,7 +423,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 = boxerrno()
......@@ -423,7 +439,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 = boxerrno()
return nil
......@@ -438,13 +454,14 @@ socket_methods.getsockopt = function(self, level, name)
end
socket_methods.linger = function(self, active, timeout)
local fd = check_socket(self)
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,
local res = ffi.C.getsockopt(fd,
internal.SOL_SOCKET, info.iname, value, len)
if res < 0 then
self._errno = boxerrno()
......@@ -472,7 +489,7 @@ 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,
local res = ffi.C.setsockopt(fd,
internal.SOL_SOCKET, info.iname, value, len)
if res < 0 then
self._errno = boxerrno()
......@@ -483,25 +500,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
self._errno = boxerrno()
local cfd = ffi.C.accept(fd, nil, nil)
if cfd < 1 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, socket_mt)
return socket
return bless_socket(cfd)
end
local function readchunk(self, size, timeout)
......@@ -631,6 +638,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)
......@@ -651,6 +659,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
......@@ -676,10 +685,11 @@ socket_methods.write = function(self, octets, timeout)
end
socket_methods.send = function(self, octets, 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 = boxerrno()
return false
......@@ -688,6 +698,7 @@ socket_methods.send = function(self, octets, flags)
end
socket_methods.recv = function(self, size, flags)
local fd = check_socket(self)
local iflags = get_iflags(internal.SEND_FLAGS, flags)
if iflags == nil then
self._errno = boxerrno.EINVAL
......@@ -700,7 +711,7 @@ socket_methods.recv = function(self, size, flags)
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 = boxerrno()
......@@ -710,13 +721,14 @@ socket_methods.recv = function(self, size, flags)
end
socket_methods.recvfrom = function(self, size, flags)
local fd = check_socket(self)
local iflags = get_iflags(internal.SEND_FLAGS, flags)
if iflags == nil then
self._errno = boxerrno.EINVAL
return nil
end
self._errno = nil
local res, from = internal.recvfrom(self.fh, size, iflags)
local res, from = internal.recvfrom(fd, size, iflags)
if res == nil then
self._errno = boxerrno()
return nil
......@@ -725,6 +737,7 @@ socket_methods.recvfrom = function(self, size, flags)
end
socket_methods.sendto = function(self, host, port, octets, flags)
local fd = check_socket(self)
local iflags = get_iflags(internal.SEND_FLAGS, flags)
if iflags == nil then
......@@ -741,10 +754,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
......@@ -779,24 +792,11 @@ local function create_socket(domain, stype, proto)
iproto = tonumber(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 = boxerrno()
ffi.C.close(fh)
boxerrno(errno)
local fd = ffi.C.socket(idomain, itype, iproto)
if fd < 1 then
return nil
end
local socket = { fh = fh }
setmetatable(socket, socket_mt)
return socket
return bless_socket(fd)
end
local function getaddrinfo(host, port, timeout, opts)
......@@ -869,7 +869,8 @@ local soname_mt = {
}
socket_methods.name = function(self)
local aka = internal.name(self.fh)
local fd = check_socket(self)
local aka = internal.name(fd)
if aka == nil then
self._errno = boxerrno()
return nil
......@@ -880,7 +881,8 @@ socket_methods.name = function(self)
end
socket_methods.peer = function(self)
local peer = internal.peer(self.fh)
local fd = check_socket(self)
local peer = internal.peer(fd)
if peer == nil then
self._errno = boxerrno()
return nil
......@@ -890,6 +892,10 @@ socket_methods.peer = function(self)
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)
......@@ -1005,7 +1011,7 @@ local function tcp_server_remote(list, prepare, handler)
for _, s in pairs(server.s) do
fiber.create(function(s)
fiber.name(sprintf("listen_fd=%d",s.fh))
fiber.name(sprintf("listen_fd=%d",s:fd()))
while s:readable() do
......@@ -1052,8 +1058,10 @@ end
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)
......@@ -1067,18 +1075,10 @@ socket_mt = {
end
}
if package.loaded.socket == nil then
package.loaded.socket = {}
end
setmetatable(package.loaded.socket, {
__call = function(self, ...) return create_socket(...) end,
__index = {
getaddrinfo = getaddrinfo,
tcp_connect = tcp_connect,
tcp_server = tcp_server
}
package.loaded.socket = setmetatable({
getaddrinfo = getaddrinfo,
tcp_connect = tcp_connect,
tcp_server = tcp_server
}, {
__call = function(self, ...) return create_socket(...) end;
})
end
......@@ -46,6 +46,17 @@ type(s:error())
---
- nil
...
-- Invalid arguments
--# setopt delimiter ';'
for k in pairs(getmetatable(s).__index) do
local r, msg = pcall(s[k])
if not msg:match('Usage:') then
error("Arguments is not checked for "..k)
end
end;
---
...
--# setopt delimiter ''
port = string.gsub(box.cfg.listen, '^.*:', '')
---
...
......@@ -255,7 +266,7 @@ s:getsockopt('SOL_SOCKET', 'SO_DEBUG')
...
s:setsockopt('SOL_SOCKET', 'SO_ACCEPTCONN', 1)
---
- error: '[string "-- bsdsocket.lua (internal file)..."]:326: Socket option SO_ACCEPTCONN
- error: '[string "-- bsdsocket.lua (internal file)..."]:340: Socket option SO_ACCEPTCONN
is read only'
...
s:getsockopt('SOL_SOCKET', 'SO_RCVBUF') > 32
......@@ -914,6 +925,36 @@ socket.tcp_connect('unix/', path), errno() == errno.ENOENT
- null
- true
...
-- invalid fd
s = socket('AF_INET', 'SOCK_STREAM', 'tcp')
---
...
s:read(9)
---
- null
...
s:close()
---
- true
...
s.socket.fd = 512
---
...
tostring(s)
---
- fd 512
...
s:readable(0)
---
- true
...
s:writable(0)
---
- true
...
s = nil
---
...
-- close
port = 65454
---
......@@ -948,25 +989,6 @@ end, serv);
s = socket.tcp_connect('127.0.0.1', port)
---
...
s:read(9)
---
- Tarantool
...
sa = setmetatable({ fh = 512 }, getmetatable(s))
---
...
tostring(sa)
---
- fd 512
...
sa:readable(0)
---
- true
...
sa:writable(0)
---
- true
...
ch = fiber.channel()
---
...
......@@ -1260,3 +1282,35 @@ os.remove(path)
---
- true
...
-- Test that socket is closed on GC
s = socket('AF_UNIX', 'SOCK_STREAM', 'ip')
---
...
s:bind('unix/', path)
---
- true
...
s:listen()
---
- true
...
s = nil
---
...
collectgarbage('collect')
---
- 0
...
collectgarbage('collect')
---
- 0
...
socket.tcp_connect('unix/', path), errno() == errno.ECONNREFUSED
---
- null
- true
...
os.remove(path)
---
- true
...
......@@ -14,6 +14,15 @@ s:wait(.01)
type(s)
s:errno()
type(s:error())
-- Invalid arguments
--# setopt delimiter ';'
for k in pairs(getmetatable(s).__index) do
local r, msg = pcall(s[k])
if not msg:match('Usage:') then
error("Arguments is not checked for "..k)
end
end;
--# setopt delimiter ''
port = string.gsub(box.cfg.listen, '^.*:', '')
......@@ -286,6 +295,16 @@ socket.tcp_connect('unix/', path), errno() == errno.ECONNREFUSED
os.remove(path)
socket.tcp_connect('unix/', path), errno() == errno.ENOENT
-- invalid fd
s = socket('AF_INET', 'SOCK_STREAM', 'tcp')
s:read(9)
s:close()
s.socket.fd = 512
tostring(s)
s:readable(0)
s:writable(0)
s = nil
-- close
port = 65454
serv = socket('AF_INET', 'SOCK_STREAM', 'tcp')
......@@ -304,12 +323,6 @@ end, serv);
--# setopt delimiter ''
s = socket.tcp_connect('127.0.0.1', port)
s:read(9)
sa = setmetatable({ fh = 512 }, getmetatable(s))
tostring(sa)
sa:readable(0)
sa:writable(0)
ch = fiber.channel()
f = fiber.create(function() s:read(12) ch:put(true) end)
s:close()
......@@ -423,4 +436,12 @@ server:stop()
os.remove(path)
-- Test that socket is closed on GC
s = socket('AF_UNIX', 'SOCK_STREAM', 'ip')
s:bind('unix/', path)
s:listen()
s = nil
collectgarbage('collect')
collectgarbage('collect')
socket.tcp_connect('unix/', path), errno() == errno.ECONNREFUSED
os.remove(path)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment