diff --git a/src/lua/bsdsocket.lua b/src/lua/bsdsocket.lua index ca7c1be90de9886db6e9dc64d4a47a43d2db5fda..e811602687972d093838b0ac4a6ead0dc5ddd0fc 100644 --- a/src/lua/bsdsocket.lua +++ b/src/lua/bsdsocket.lua @@ -546,7 +546,7 @@ local function readline_check(self, eols, limit) end for i, eol in pairs(eols) do if string.len(rbuf) >= string.len(eol) then - local data = string.match(rbuf, "^(.*" .. eol .. ")") + local data = string.match(rbuf, "^(.-" .. eol .. ")") if data ~= nil then if string.len(data) > limit then return string.sub(data, 1, limit) @@ -607,12 +607,8 @@ socket_methods.readline = function(self, limit, eol, timeout) local data = self:sysread(4096) if data ~= nil then self.rbuf = self.rbuf .. data - if string.len(self.rbuf) >= limit then - data = string.sub(self.rbuf, 1, limit) - self.rbuf = string.sub(self.rbuf, limit + 1) - return data - end + -- eof if string.len(data) == 0 then data = self.rbuf self.rbuf = nil @@ -624,6 +620,14 @@ socket_methods.readline = function(self, limit, eol, timeout) self.rbuf = string.sub(self.rbuf, string.len(data) + 1) return data end + + -- limit + if string.len(self.rbuf) >= limit then + data = string.sub(self.rbuf, 1, limit) + self.rbuf = string.sub(self.rbuf, limit + 1) + return data + end + elseif self:errno() ~= box.errno.EAGAIN then self._errno = box.errno() return nil @@ -840,36 +844,113 @@ socket_methods.name = function(self) self._errno = box.errno() return nil end + self._errno = nil return aka end -if type(box.socket) == nil then - box.socket = {} +socket_methods.peer = function(self) + local peer = box.socket.internal.peer(self.fh) + if peer == nil then + self._errno = box.errno() + return nil + end + self._errno = nil + return peer +end + +-- tcp connector +local function tcp_connect(host, port, timeout) + if host == 'unix/' and port ~= nil then + local s = package.loaded.socket('AF_UNIX', 'SOCK_STREAM', 'ip') + if s == nil then + return nil + end + if s:sysconnect(host, port) then + return s + else + local errno = box.errno() + s:close() + box.errno(errno) + return nil + end + end + + if timeout == nil then + timeout = TIMEOUT_INFINITY + end + local started = box.time() + local dns = box.socket.getaddrinfo(host, port, timeout, + { type = 'SOCK_STREAM', protocol = 'tcp' }) + if dns == nil then + return nil + end + + + + for i, remote in pairs(dns) do + + timeout = timeout - (box.time() - started) + if timeout <= 0 then + box.errno(box.errno.ETIMEDOUT) + return nil + end + + started = box.time() + + local s = box.socket(remote.family, remote.type, 'tcp') + if s == nil then + return nil + end + + if s:sysconnect(remote.host, remote.port) then + + timeout = timeout - (box.time() - started) + if timeout <= 0 then + s:close() + break + end + + if s:writable(timeout) then + return s + end + end + + local save_errno = box.errno() + s:close() + box.errno(save_errno) + end + + if timeout <= 0 then + box.errno(box.errno.ETIMEDOUT) + end + return nil end box.socket.internal = { socket_mt = { __index = socket_methods, __tostring = function(self) + local save_errno = self._errno local name = sprintf("fd %d", self.fh) - local aka = box.socket.internal.name(self.fh) + local aka = self:name() if aka ~= nil then name = sprintf("%s, aka %s:%s", name, aka.host, aka.port) end - local peer = box.socket.internal.peer(self.fh) + 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 }, - } setmetatable(box.socket, { __call = create_socket, __index = { getaddrinfo = getaddrinfo, + tcp_connect = tcp_connect } }) diff --git a/test/box/bsdsocket.result b/test/box/bsdsocket.result index 16456488203c374f4e955939ee3feb91658b9e1f..0417e31db8fe963e39caeac316f41cb47755fbe6 100644 --- a/test/box/bsdsocket.result +++ b/test/box/bsdsocket.result @@ -693,3 +693,38 @@ lua sc:close() --- - true ... +tcp_connect +lua s = box.socket.tcp_connect('mail.ru', 80) +--- +... +lua string.match(tostring(s), ', aka') ~= nil +--- + - true +... +lua string.match(tostring(s), ', peer') ~= nil +--- + - true +... +lua s:write('GET / HTTP/1.0\r\nHost: mail.ru\r\n\r\n') +--- + - true +... +lua header = s:readline(4000, { '\n\n', '\r\n\r\n' }, 1) +--- +... +lua string.match(header, '\r\n\r\n$') ~= nil +--- + - true +... +lua string.match(header, '200 [Oo][Kk]') ~= nil +--- + - true +... +lua s:close() +--- + - true +... +lua box.socket.tcp_connect('mail.ru', 80, 0.00000000001) +--- + - nil +... diff --git a/test/box/bsdsocket.test b/test/box/bsdsocket.test index 8b69dbbc5adceb8ba67e3ee43823b98b04861e3e..5d9b94f2bd9eecbbd9bddbf0bef47708a054b054 100644 --- a/test/box/bsdsocket.test +++ b/test/box/bsdsocket.test @@ -214,3 +214,16 @@ exec admin "lua from_r.host" exec admin "lua from_r.port == s:name().port" exec admin "lua s:close()" exec admin "lua sc:close()" + +print("tcp_connect") + +exec admin "lua s = box.socket.tcp_connect('mail.ru', 80)" +exec admin "lua string.match(tostring(s), ', aka') ~= nil" +exec admin "lua string.match(tostring(s), ', peer') ~= nil" +exec admin "lua s:write('GET / HTTP/1.0\\r\\nHost: mail.ru\\r\\n\\r\\n')" +exec admin "lua header = s:readline(4000, { '\\n\\n', '\\r\\n\\r\\n' }, 1)" +exec admin "lua string.match(header, '\\r\\n\\r\\n$') ~= nil" +exec admin "lua string.match(header, '200 [Oo][Kk]') ~= nil" +exec admin "lua s:close()" + +exec admin "lua box.socket.tcp_connect('mail.ru', 80, 0.00000000001)"