From b149c783f2753da791919b278ed19a3b1a79f212 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 20 Nov 2009 22:58:56 +0000 Subject: net.server_event: Initial commit of server_event.lua. Don't get too excited, it's not used at all yet, and is still incomplete :) --- net/server_event.lua | 759 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 759 insertions(+) create mode 100644 net/server_event.lua (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua new file mode 100644 index 00000000..15d1dea0 --- /dev/null +++ b/net/server_event.lua @@ -0,0 +1,759 @@ +--[[ + + + server.lua based on lua/libevent by blastbeat + + notes: + -- when using luaevent, never register 2 or more EV_READ at one socket, same for EV_WRITE + -- you cant even register a new EV_READ/EV_WRITE callback inside another one + -- never call eventcallback:close( ) from inside eventcallback + -- to do some of the above, use timeout events or something what will called from outside + -- dont let garbagecollect eventcallbacks, as long they are running + -- when using luasec, there are 4 cases of timeout errors: wantread or wantwrite during reading or writing + +--]] + + +local SCRIPT_NAME = "server_event.lua" +local SCRIPT_VERSION = "0.05" +local SCRIPT_AUTHOR = "blastbeat" +local LAST_MODIFIED = "2009/11/20" + +local cfg = { + MAX_CONNECTIONS = 100000, -- max per server connections (use "ulimit -n" on *nix) + MAX_HANDSHAKE_ATTEMPS = 10, -- attemps to finish ssl handshake + HANDSHAKE_TIMEOUT = 1, -- timout in seconds per handshake attemp + MAX_READ_LENGTH = 1024 * 1024 * 1024 * 1024, -- max bytes allowed to read from sockets + MAX_SEND_LENGTH = 1024 * 1024 * 1024 * 1024, -- max bytes size of write buffer (for writing on sockets) + ACCEPT_DELAY = 10, -- seconds to wait until the next attemp of a full server to accept + READ_TIMEOUT = 60 * 30, -- timeout in seconds for read data from socket + WRITE_TIMEOUT = 30, -- timeout in seconds for write data on socket + CONNECT_TIMEOUT = 10, -- timeout in seconds for connection attemps + CLEAR_DELAY = 5, -- seconds to wait for clearing interface list (and calling ondisconnect listeners) + DEBUG = true, -- show debug messages +} + +local function use(x) return rawget(_G, x); end +local print = use "print" +local pcall = use "pcall" +local ipairs = use "ipairs" +local string = use "string" +local select = use "select" +local require = use "require" +local tostring = use "tostring" +local coroutine = use "coroutine" +local setmetatable = use "setmetatable" + +local ssl = use "ssl" +local socket = use "socket" + +local log = require ("util.logger").init("socket") + +local function debug(...) + return log("debug", ("%s "):rep(select('#', ...)), ...) +end + +local bitor = ( function( ) -- thx Rici Lake + local hasbit = function( x, p ) + return x % ( p + p ) >= p + end + return function( x, y ) + local p = 1 + local z = 0 + local limit = x > y and x or y + while p <= limit do + if hasbit( x, p ) or hasbit( y, p ) then + z = z + p + end + p = p + p + end + return z + end +end )( ) + +local getid = function( ) + return function( ) + end +end + +local event = require "luaevent.core" +local base = event.new( ) +local EV_READ = event.EV_READ +local EV_WRITE = event.EV_WRITE +local EV_TIMEOUT = event.EV_TIMEOUT + +local EV_READWRITE = bitor( EV_READ, EV_WRITE ) + +local interfacelist = ( function( ) -- holds the interfaces for sockets + local array = { } + local len = 0 + return function( method, arg ) + if "add" == method then + len = len + 1 + array[ len ] = arg + arg:_position( len ) + return len + elseif "delete" == method then + if len <= 0 then + return nil, "array is already empty" + end + local position = arg:_position() -- get position in array + if position ~= len then + local interface = array[ len ] -- get last interface + array[ position ] = interface -- copy it into free position + array[ len ] = nil -- free last position + interface:_position( position ) -- set new position in array + else -- free last position + array[ len ] = nil + end + len = len - 1 + return len + else + return array + end + end +end )( ) + +-- Client interface methods +local interface_mt +do + interface_mt = {}; interface_mt.__index = interface_mt; + + local addevent = base.addevent + local coroutine_wrap, coroutine_yield = coroutine.wrap,coroutine.yield + local string_len = string.len + + -- Private methods + function interface_mt:_position(new_position) + self.position = new_position or self.position + return self.position; + end + function interface_mt:_close() -- regs event to start self:_destroy() + local callback = function( ) + self:_destroy(); + self.eventclose = nil + return -1 + end + self.eventclose = addevent( base, nil, EV_TIMEOUT, callback, 0 ) + return true + end + + function interface_mt:_start_connection(plainssl) -- should be called from addclient + local callback = function( event ) + if EV_TIMEOUT == event then -- timout during connection + self.fatalerror = "connection timeout" + self.listener.ontimeout( self ) -- call timeout listener + self:_close() + debug( "new connection failed. id:", self, "error:", self.fatalerror ) + else + if plainssl then -- start ssl session + self:_start_ssl( self.listener.onconnect ) + else -- normal connection + self:_start_session( self.listener.onconnect ) + end + debug( "new connection established. id:", self ) + end + self.eventconnect = nil + return -1 + end + self.eventconnect = addevent( base, self.conn, EV_WRITE, callback, cfg.CONNECT_TIMEOUT ) + return true + end + function interface_mt:_start_session(onconnect) -- new session, for example after startssl + if self.type == "client" then + local callback = function( ) + self:_lock( false, false, false ) + --vdebug( "start listening on client socket with id:", self ) + self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT ) -- register callback + onconnect( self ) + self.eventsession = nil + return -1 + end + self.eventsession = addevent( base, nil, EV_TIMEOUT, callback, 0 ) + else + self:_lock( false ) + --vdebug( "start listening on server socket with id:", self ) + self.eventread = addevent( base, self.conn, EV_READ, self.readcallback ) -- register callback + end + return true + end + function interface_mt:_start_ssl(arg) -- old socket will be destroyed, therefore we have to close read/write events first + --vdebug( "starting ssl session with client id:", self ) + local _ + _ = self.eventread and self.eventread:close( ) -- close events; this must be called outside of the event callbacks! + _ = self.eventwrite and self.eventwrite:close( ) + self.eventread, self.eventwrite = nil, nil + local err + self.conn, err = ssl.wrap( self.conn, self.sslctx ) + if err then + self.fatalerror = err + self.conn = nil -- cannot be used anymore + if "onconnect" == arg then + self.ondisconnect = nil -- dont call this when client isnt really connected + end + self:_close() + debug( "fatal error while ssl wrapping:", err ) + return false + end + self.conn:settimeout( 0 ) -- set non blocking + local handshakecallback = coroutine_wrap( + function( event ) + local _, err + local attempt = 0 + local maxattempt = cfg.MAX_HANDSHAKE_ATTEMPS + while attempt < 1000 do -- no endless loop + attempt = attempt + 1 + debug( "ssl handshake of client with id:", self, "attemp:", attempt ) + if attempt > maxattempt then + self.fatalerror = "max handshake attemps exceeded" + elseif EV_TIMEOUT == event then + self.fatalerror = "timeout during handshake" + else + _, err = self.conn:dohandshake( ) + if not err then + self:_lock( false, false, false ) -- unlock the interface; sending, closing etc allowed + self.send = self.conn.send -- caching table lookups with new client object + self.receive = self.conn.receive + local onsomething + if "onconnect" == arg then -- trigger listener + onsomething = self.listener.onconnect + else + onsomething = self.listener.onsslconnection + end + self:_start_session( onsomething ) + debug( "ssl handshake done" ) + self.eventhandshake = nil + return -1 + end + debug( "error during ssl handshake:", err ) + if err == "wantwrite" then + event = EV_WRITE + elseif err == "wantread" then + event = EV_READ + else + self.fatalerror = err + end + end + if self.fatalerror then + if "onconnect" == arg then + self.ondisconnect = nil -- dont call this when client isnt really connected + end + self:_close() + debug( "handshake failed because:", self.fatalerror ) + self.eventhandshake = nil + return -1 + end + event = coroutine_yield( event, cfg.HANDSHAKE_TIMEOUT ) -- yield this monster... + end + end + ) + debug "starting handshake..." + self:_lock( false, true, true ) -- unlock read/write events, but keep interface locked + self.eventhandshake = addevent( base, self.conn, EV_READWRITE, handshakecallback, cfg.HANDSHAKE_TIMEOUT ) + return true + end + function interface_mt:_destroy() -- close this interface + events and call last listener + debug( "closing client with id:", self ) + self:_lock( true, true, true ) -- first of all, lock the interface to avoid further actions + local _ + _ = self.eventread and self.eventread:close( ) -- close events; this must be called outside of the event callbacks! + if self.type == "client" then + _ = self.eventwrite and self.eventwrite:close( ) + _ = self.eventhandshake and self.eventhandshake:close( ) + _ = self.eventstarthandshake and self.eventstarthandshake:close( ) + _ = self.eventconnect and self.eventconnect:close( ) + _ = self.eventsession and self.eventsession:close( ) + _ = self.eventwritetimeout and self.eventwritetimeout:close( ) + _ = self.eventreadtimeout and self.eventreadtimeout:close( ) + _ = self.ondisconnect and self:ondisconnect( self.fatalerror ) -- call ondisconnect listener (wont be the case if handshake failed on connect) + _ = self.conn and self.conn:close( ) -- close connection, must also be called outside of any socket registered events! + self._server:counter(-1); + self.eventread, self.eventwrite = nil, nil + self.eventstarthandshake, self.eventhandshake, self.eventclose = nil, nil, nil + self.readcallback, self.writecallback = nil, nil + else + self.conn:close( ) + self.eventread, self.eventclose = nil, nil + self.interface, self.readcallback = nil, nil + end + interfacelist( "delete", self ) + return true + end + function interface_mt:_lock(nointerface, noreading, nowriting) -- lock or unlock this interface or events + self.nointerface, self.noreading, self.nowriting = nointerface, noreading, nowriting + return nointerface, noreading, nowriting + end + + function interface_mt:counter(c) + if c then + self._connections = self._connections - c + end + return self._connections + end + + -- Public methods + function interface_mt:write(data) + --vdebug( "try to send data to client, id/data:", self, data ) + data = tostring( data ) + local len = string_len( data ) + local total = len + self.writebufferlen + if total > cfg.MAX_SEND_LENGTH then -- check buffer length + local err = "send buffer exceeded" + debug( "error:", err ) -- to much, check your app + return nil, err + end + self.writebuffer = self.writebuffer .. data -- new buffer + self.writebufferlen = total + if not self.eventwrite then -- register new write event + --vdebug( "register new write event" ) + self.eventwrite = addevent( base, self.conn, EV_WRITE, self.writecallback, cfg.WRITE_TIMEOUT ) + end + return true + end + function interface_mt:close(now) + debug( "try to close client connection with id:", self ) + if self.type == "client" then + self.fatalerror = "client to close" + if ( not self.eventwrite ) or now then -- try to close immediately + self:_lock( true, true, true ) + self:_close() + return true + else -- wait for incomplete write request + self:_lock( true, true, false ) + debug "closing delayed until writebuffer is empty" + return nil, "writebuffer not empty, waiting" + end + else + debug( "try to close server with id:", self, "args:", now ) + self.fatalerror = "server to close" + self:_lock( true ) + local count = 0 + for _, item in ipairs( interfacelist( ) ) do + if ( item.type ~= "server" ) and ( item._server == self ) then -- client/server match + if item:close( now ) then -- writebuffer was empty + count = count + 1 + end + end + end + local timeout = 0 -- dont wait for unfinished writebuffers of clients... + if not now then + timeout = cfg.WRITE_TIMEOUT -- ...or wait for it + end + self:_close( timeout ) -- add new event to remove the server interface + debug( "seconds remained until server is closed:", timeout ) + return count -- returns finished clients with empty writebuffer + end + end + + function interface_mt:server() + return self._server or self; + end + + function interface_mt:port() + return self._port + end + + function interface_mt:ip() + return self._ip + end + + function interface_mt:ssl() + return self.usingssl + end + + function interface_mt:type() + return self._type or "client" + end + + function interface_mt:connections() + return self._connections + end + + function interface_mt:address() + return self.addr + end + + + + function interface_mt:starttls() + debug( "try to start ssl at client id:", self ) + local err + if not self.sslctx then -- no ssl available + err = "no ssl context available" + elseif self.usingssl then -- startssl was already called + err = "ssl already active" + end + if err then + debug( "error:", err ) + return nil, err + end + self.usingssl = true + self.startsslcallback = function( ) -- we have to start the handshake outside of a read/write event + self:_start_ssl(); + self.eventstarthandshake = nil + return -1 + end + if not self.eventwrite then + self:_lock( true, true, true ) -- lock the interface, to not disturb the handshake + self.eventstarthandshake = addevent( base, nil, EV_TIMEOUT, self.startsslcallback, 0 ) -- add event to start handshake + else -- wait until writebuffer is empty + self:_lock( true, true, false ) + debug "ssl session delayed until writebuffer is empty..." + end + return true + end + + function interface_mt.onconnect() + end +end + +-- End of client interface methods + +local handleclient; +do + local string_sub = string.sub -- caching table lookups + local string_len = string.len + local addevent = base.addevent + local coroutine_wrap = coroutine.wrap + local socket_gettime = socket.gettime + local coroutine_yield = coroutine.yield + function handleclient( client, ip, port, server, pattern, listener, _, sslctx ) -- creates an client interface + --vdebug("creating client interfacce...") + local interface = { + type = "client"; + conn = client; + currenttime = socket_gettime( ); -- safe the origin + writebuffer = ""; -- writebuffer + writebufferlen = 0; -- length of writebuffer + send = client.send; -- caching table lookups + receive = client.receive; + onconnect = listener.onconnect; -- will be called when client disconnects + ondisconnect = listener.ondisconnect; -- will be called when client disconnects + onincoming = listener.onincoming; -- will be called when client sends data + eventread = false, eventwrite = false, eventclose = false, + eventhandshake = false, eventstarthandshake = false; -- event handler + eventconnect = false, eventsession = false; -- more event handler... + eventwritetimeout = false; -- even more event handler... + eventreadtimeout = false; + fatalerror = false; -- error message + writecallback = false; -- will be called on write events + readcallback = false; -- will be called on read events + nointerface = true; -- lock/unlock parameter of this interface + noreading = false, nowriting = false; -- locks of the read/writecallback + startsslcallback = false; -- starting handshake callback + position = false; -- position of client in interfacelist + + -- Properties + _ip = ip, _port = port, _server = server, _pattern = pattern, + _sslctx = sslctx; -- parameters + _usingssl = false; -- client is using ssl; + } + interface.writecallback = function( event ) -- called on write events + --vdebug( "new client write event, id/ip/port:", interface, ip, port ) + if interface.nowriting or ( interface.fatalerror and ( "client to close" ~= interface.fatalerror ) ) then -- leave this event + --vdebug( "leaving this event because:", interface.nowriting or interface.fatalerror ) + interface.eventwrite = false + return -1 + end + if EV_TIMEOUT == event then -- took too long to write some data to socket -> disconnect + interface.fatalerror = "timeout during writing" + debug( "writing failed:", interface.fatalerror ) + interface:_close() + interface.eventwrite = false + return -1 + else -- can write :) + if interface.usingssl then -- handle luasec + if interface.eventreadtimeout then -- we have to read first + local ret = interface.readcallback( ) -- call readcallback + --vdebug( "tried to read in writecallback, result:", ret ) + end + if interface.eventwritetimeout then -- luasec only + interface.eventwritetimeout:close( ) -- first we have to close timeout event which where regged after a wantread error + interface.eventwritetimeout = false + end + end + local succ, err, byte = interface.send( interface.conn, interface.writebuffer, 1, interface.writebufferlen ) + --vdebug( "write data:", interface.writebuffer, "error:", err, "part:", byte ) + if succ then -- writing succesful + interface.writebuffer = "" + interface.writebufferlen = 0 + if interface.fatalerror then + debug "closing client after writing" + interface:_close() -- close interface if needed + elseif interface.startsslcallback then -- start ssl connection if needed + debug "starting ssl handshake after writing" + interface.eventstarthandshake = addevent( base, nil, EV_TIMEOUT, interface.startsslcallback, 0 ) + elseif interface.eventreadtimeout then + return EV_WRITE, EV_TIMEOUT + end + interface.eventwrite = nil + return -1 + elseif byte then -- want write again + --vdebug( "writebuffer is not empty:", err ) + interface.writebuffer = string_sub( interface.writebuffer, byte + 1, interface.writebufferlen ) -- new buffer + interface.writebufferlen = interface.writebufferlen - byte + if "wantread" == err then -- happens only with luasec + local callback = function( ) + interface:_close() + interface.eventwritetimeout = nil + return evreturn, evtimeout + end + interface.eventwritetimeout = addevent( base, nil, EV_TIMEOUT, callback, cfg.WRITE_TIMEOUT ) -- reg a new timeout event + debug( "wantread during write attemp, reg it in readcallback but dont know what really happens next..." ) + -- hopefully this works with luasec; its simply not possible to use 2 different write events on a socket in luaevent + return -1 + end + return EV_WRITE, cfg.WRITE_TIMEOUT + else -- connection was closed during writing or fatal error + interface.fatalerror = err or "fatal error" + debug( "connection failed in write event:", interface.fatalerror ) + interface:_close() + interface.eventwrite = nil + return -1 + end + end + end + local usingssl, receive = interface._usingssl, interface.receive; + interface.readcallback = function( event ) -- called on read events + --vdebug( "new client read event, id/ip/port:", interface, ip, port ) + if interface.noreading or interface.fatalerror then -- leave this event + --vdebug( "leaving this event because:", interface.noreading or interface.fatalerror ) + interface.eventread = nil + return -1 + end + if EV_TIMEOUT == event then -- took too long to get some data from client -> disconnect + interface.fatalerror = "timeout during receiving" + debug( "connection failed:", interface.fatalerror ) + interface:_close() + interface.eventread = nil + return -1 + else -- can read + if usingssl then -- handle luasec + if interface.eventwritetimeout then -- ok, in the past writecallback was regged + local ret = interface.writecallback( ) -- call it + --vdebug( "tried to write in readcallback, result:", ret ) + end + if interface.eventreadtimeout then + interface.eventreadtimeout:close( ) + interface.eventreadtimeout = nil + end + end + local buffer, err, part = receive( client, pattern ) -- receive buffer with "pattern" + --vdebug( "read data:", buffer, "error:", err, "part:", part ) + buffer = buffer or part or "" + local len = string_len( buffer ) + if len > cfg.MAX_READ_LENGTH then -- check buffer length + interface.fatalerror = "receive buffer exceeded" + debug( "fatal error:", interface.fatalerror ) + interface:_close() + interface.eventread = nil + return -1 + end + if err and ( "timeout" ~= err ) then + if "wantwrite" == err then -- need to read on write event + if not interface.eventwrite then -- register new write event if needed + interface.eventwrite = addevent( base, interface.conn, EV_WRITE, interface.writecallback, cfg.WRITE_TIMEOUT ) + end + interface.eventreadtimeout = addevent( base, nil, EV_TIMEOUT, + function( ) + interface:_close() + end, cfg.READ_TIMEOUT + ) + debug( "wantwrite during read attemp, reg it in writecallback but dont know what really happens next..." ) + -- to be honest i dont know what happens next, if it is allowed to first read, the write etc... + else -- connection was closed or fatal error + interface.fatalerror = err + debug( "connection failed in read event:", interface.fatalerror ) + interface:_close() + interface.eventread = nil + return -1 + end + end + interface.onincoming( interface, buffer, err ) -- send new data to listener + return EV_READ, cfg.READ_TIMEOUT + end + end + + client:settimeout( 0 ) -- set non blocking + setmetatable(interface, interface_mt) + interfacelist( "add", interface ) -- add to interfacelist + return interface + end +end + +local handleserver +do + function handleserver( server, addr, port, pattern, listener, sslctx, startssl ) -- creates an server interface + debug "creating server interface..." + local interface = { + _connections = 0; + + conn = server; + onconnect = listener.onconnect; -- will be called when new client connected + eventread = false; -- read event handler + eventclose = false; -- close event handler + readcallback = false; -- read event callback + fatalerror = false; -- error message + nointerface = true; -- lock/unlock parameter + } + interface.readcallback = function( event ) -- server handler, called on incoming connections + --vdebug( "server can accept, id/addr/port:", interface, addr, port ) + if interface.fatalerror then + --vdebug( "leaving this event because:", self.fatalerror ) + interface.eventread = nil + return -1 + end + local delay = cfg.ACCEPT_DELAY + if EV_TIMEOUT == event then + if interface._connections >= cfg.MAX_CONNECTIONS then -- check connection count + debug( "to many connections, seconds to wait for next accept:", delay ) + return EV_TIMEOUT, delay -- timeout... + else + return EV_READ -- accept again + end + end + --vdebug("max connection check ok, accepting...") + local client, err = server:accept() -- try to accept; TODO: check err + while client do + if interface._connections >= cfg.MAX_CONNECTIONS then + client:close( ) -- refuse connection + debug( "maximal connections reached, refuse client connection; accept delay:", delay ) + return EV_TIMEOUT, delay -- delay for next accept attemp + end + local ip, port = client:getpeername( ) + interface._connections = interface._connections + 1 -- increase connection count + local clientinterface = handleclient( client, ip, port, interface, pattern, listener, nil, sslctx ) + --vdebug( "client id:", clientinterface, "startssl:", startssl ) + if startssl then + clientinterface:_start_ssl( clientinterface.onconnect ) + else + clientinterface:_start_session( clientinterface.onconnect ) + end + debug( "accepted incoming client connection from:", ip, port ) + client, err = server:accept() -- try to accept again + end + return EV_READ + end + + server:settimeout( 0 ) + setmetatable(interface, interface_mt) + interfacelist( "add", interface ) + interface:_start_session() + return interface + end +end + +local addserver = ( function( ) + return function( addr, port, listener, pattern, backlog, sslcfg, startssl ) -- TODO: check arguments + debug( "creating new tcp server with following parameters:", addr or "nil", port or "nil", sslcfg or "nil", startssl or "nil") + local server, err = socket.bind( addr, port, backlog ) -- create server socket + if not server then + debug( "creating server socket failed because:", err ) + return nil, err + end + local sslctx + if sslcfg then + if not ssl then + debug "fatal error: luasec not found" + return nil, "luasec not found" + end + sslctx, err = ssl.newcontext( sslcfg ) + if err then + debug( "error while creating new ssl context for server socket:", err ) + return nil, err + end + end + local interface = handleserver( server, addr, port, pattern, listener, sslctx, startssl ) -- new server handler + debug( "new server created with id:", tostring(interface)) + return interface + end +end )( ) + +local wrapclient = ( function( ) + return function( client, addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) + debug( "try to connect to:", addr, serverport, "with parameters:", pattern, localaddr, localport, sslcfg, startssl ) + local sslctx + if sslcfg then -- handle ssl/new context + if not ssl then + debug "need luasec, but not available" + return nil, "luasec not found" + end + sslctx, err = ssl.newcontext( sslcfg ) + if err then + debug( "cannot create new ssl context:", err ) + return nil, err + end + end + end +end )( ) + +local addclient = ( function( ) + return function( addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) + local client, err = socket.tcp() -- creating new socket + if not client then + debug( "cannot create socket:", err ) + return nil, err + end + client:settimeout( 0 ) -- set nonblocking + if localaddr then + local res, err = client:bind( localaddr, localport, -1 ) + if not res then + debug( "cannot bind client:", err ) + return nil, err + end + end + local res, err = client:connect( addr, serverport ) -- connect + if res or ( err == "timeout" ) then + local ip, port = client:getsockname( ) + local server = function( ) + return nil, "this is a dummy server interface" + end + local interface = handleclient( client, ip, port, server, pattern, listener, sslctx ) + interface:_start_connection( startssl ) + debug( "new connection id:", interface ) + return interface, err + else + debug( "new connection failed:", err ) + return nil, err + end + return wrapclient( client, addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) + end +end )( ) + +local loop = function( ) -- starts the event loop + return base:loop( ) +end + +local newevent = ( function( ) + local add = base.addevent + return function( ... ) + return add( base, ... ) + end +end )( ) + +local closeallservers = function( arg ) + for _, item in ipairs( interfacelist( ) ) do + if item "type" == "server" then + item( "close", arg ) + end + end +end + +return { + + cfg = cfg, + base = base, + loop = loop, + event = event, + addevent = newevent, + addserver = addserver, + addclient = addclient, + wrapclient = wrapclient, + closeallservers = closeallservers, + + __NAME = SCRIPT_NAME, + __DATE = LAST_MODIFIED, + __AUTHOR = SCRIPT_AUTHOR, + __VERSION = SCRIPT_VERSION, + +} -- cgit v1.2.3 From 83334e87456fa517e30f683f5c0948b8c18b6c83 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 20 Nov 2009 23:24:41 +0000 Subject: net.server_event: Remove redundant getid() function --- net/server_event.lua | 5 ----- 1 file changed, 5 deletions(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 15d1dea0..fab617b7 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -71,11 +71,6 @@ local bitor = ( function( ) -- thx Rici Lake end end )( ) -local getid = function( ) - return function( ) - end -end - local event = require "luaevent.core" local base = event.new( ) local EV_READ = event.EV_READ -- cgit v1.2.3 From 7f86cfbcc015218d36347be850be0849f55413fc Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 02:36:35 +0000 Subject: net.server: Rename to net.server_select --- net/server.lua | 914 -------------------------------------------------- net/server_select.lua | 914 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 914 insertions(+), 914 deletions(-) delete mode 100644 net/server.lua create mode 100644 net/server_select.lua (limited to 'net') diff --git a/net/server.lua b/net/server.lua deleted file mode 100644 index 6ab8ce91..00000000 --- a/net/server.lua +++ /dev/null @@ -1,914 +0,0 @@ --- --- server.lua by blastbeat of the luadch project --- Re-used here under the MIT/X Consortium License --- --- Modifications (C) 2008-2009 Matthew Wild, Waqas Hussain --- - --- // wrapping luadch stuff // -- - -local use = function( what ) - return _G[ what ] -end -local clean = function( tbl ) - for i, k in pairs( tbl ) do - tbl[ i ] = nil - end -end - -local log, table_concat = require ("util.logger").init("socket"), table.concat; -local out_put = function (...) return log("debug", table_concat{...}); end -local out_error = function (...) return log("warn", table_concat{...}); end -local mem_free = collectgarbage - -----------------------------------// DECLARATION //-- - ---// constants //-- - -local STAT_UNIT = 1 -- byte - ---// lua functions //-- - -local type = use "type" -local pairs = use "pairs" -local ipairs = use "ipairs" -local tostring = use "tostring" -local collectgarbage = use "collectgarbage" - ---// lua libs //-- - -local os = use "os" -local table = use "table" -local string = use "string" -local coroutine = use "coroutine" - ---// lua lib methods //-- - -local os_time = os.time -local os_difftime = os.difftime -local table_concat = table.concat -local table_remove = table.remove -local string_len = string.len -local string_sub = string.sub -local coroutine_wrap = coroutine.wrap -local coroutine_yield = coroutine.yield - ---// extern libs //-- - -local luasec = select( 2, pcall( require, "ssl" ) ) -local luasocket = require "socket" - ---// extern lib methods //-- - -local ssl_wrap = ( luasec and luasec.wrap ) -local socket_bind = luasocket.bind -local socket_sleep = luasocket.sleep -local socket_select = luasocket.select -local ssl_newcontext = ( luasec and luasec.newcontext ) - ---// functions //-- - -local id -local loop -local stats -local idfalse -local addtimer -local closeall -local addserver -local getserver -local wrapserver -local getsettings -local closesocket -local removesocket -local removeserver -local changetimeout -local wrapconnection -local changesettings - ---// tables //-- - -local _server -local _readlist -local _timerlist -local _sendlist -local _socketlist -local _closelist -local _readtimes -local _writetimes - ---// simple data types //-- - -local _ -local _readlistlen -local _sendlistlen -local _timerlistlen - -local _sendtraffic -local _readtraffic - -local _selecttimeout -local _sleeptime - -local _starttime -local _currenttime - -local _maxsendlen -local _maxreadlen - -local _checkinterval -local _sendtimeout -local _readtimeout - -local _cleanqueue - -local _timer - -local _maxclientsperserver - -----------------------------------// DEFINITION //-- - -_server = { } -- key = port, value = table; list of listening servers -_readlist = { } -- array with sockets to read from -_sendlist = { } -- arrary with sockets to write to -_timerlist = { } -- array of timer functions -_socketlist = { } -- key = socket, value = wrapped socket (handlers) -_readtimes = { } -- key = handler, value = timestamp of last data reading -_writetimes = { } -- key = handler, value = timestamp of last data writing/sending -_closelist = { } -- handlers to close - -_readlistlen = 0 -- length of readlist -_sendlistlen = 0 -- length of sendlist -_timerlistlen = 0 -- lenght of timerlist - -_sendtraffic = 0 -- some stats -_readtraffic = 0 - -_selecttimeout = 1 -- timeout of socket.select -_sleeptime = 0 -- time to wait at the end of every loop - -_maxsendlen = 51000 * 1024 -- max len of send buffer -_maxreadlen = 25000 * 1024 -- max len of read buffer - -_checkinterval = 1200000 -- interval in secs to check idle clients -_sendtimeout = 60000 -- allowed send idle time in secs -_readtimeout = 6 * 60 * 60 -- allowed read idle time in secs - -_cleanqueue = false -- clean bufferqueue after using - -_maxclientsperserver = 1000 - -_maxsslhandshake = 30 -- max handshake round-trips -----------------------------------// PRIVATE //-- - -wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx, maxconnections, startssl ) -- this function wraps a server - - maxconnections = maxconnections or _maxclientsperserver - - local connections = 0 - - local dispatch, disconnect = listeners.incoming or listeners.listener, listeners.disconnect - - local err - - local ssl = false - - if sslctx then - ssl = true - if not ssl_newcontext then - out_error "luasec not found" - ssl = false - end - if type( sslctx ) ~= "table" then - out_error "server.lua: wrong server sslctx" - ssl = false - end - local ctx; - ctx, err = ssl_newcontext( sslctx ) - if not ctx then - err = err or "wrong sslctx parameters" - local file; - file = err:match("^error loading (.-) %("); - if file then - if file == "private key" then - file = sslctx.key or "your private key"; - elseif file == "certificate" then - file = sslctx.certificate or "your certificate file"; - end - local reason = err:match("%((.+)%)$") or "some reason"; - if reason == "Permission denied" then - reason = "Check that the permissions allow Prosody to read this file."; - elseif reason == "No such file or directory" then - reason = "Check that the path is correct, and the file exists."; - elseif reason == "system lib" then - reason = "Previous error (see logs), or other system error."; - else - reason = "Reason: "..tostring(reason or "unknown"):lower(); - end - log("error", "SSL/TLS: Failed to load %s: %s", file, reason); - else - log("error", "SSL/TLS: Error initialising for port %d: %s", serverport, err ); - end - ssl = false - end - sslctx = ctx; - end - if not ssl then - sslctx = false; - if startssl then - log("error", "Failed to listen on port %d due to SSL/TLS to SSL/TLS initialisation errors (see logs)", serverport ) - return nil, "Cannot start ssl, see log for details" - end - end - - local accept = socket.accept - - --// public methods of the object //-- - - local handler = { } - - handler.shutdown = function( ) end - - handler.ssl = function( ) - return ssl - end - handler.sslctx = function( ) - return sslctx - end - handler.remove = function( ) - connections = connections - 1 - end - handler.close = function( ) - for _, handler in pairs( _socketlist ) do - if handler.serverport == serverport then - handler.disconnect( handler, "server closed" ) - handler.close( true ) - end - end - socket:close( ) - _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) - _readlistlen = removesocket( _readlist, socket, _readlistlen ) - _socketlist[ socket ] = nil - handler = nil - socket = nil - --mem_free( ) - out_put "server.lua: closed server handler and removed sockets from list" - end - handler.ip = function( ) - return ip - end - handler.serverport = function( ) - return serverport - end - handler.socket = function( ) - return socket - end - handler.readbuffer = function( ) - if connections > maxconnections then - out_put( "server.lua: refused new client connection: server full" ) - return false - end - local client, err = accept( socket ) -- try to accept - if client then - local ip, clientport = client:getpeername( ) - client:settimeout( 0 ) - local handler, client, err = wrapconnection( handler, listeners, client, ip, serverport, clientport, pattern, sslctx, startssl ) -- wrap new client socket - if err then -- error while wrapping ssl socket - return false - end - connections = connections + 1 - out_put( "server.lua: accepted new client connection from ", tostring(ip), ":", tostring(clientport), " to ", tostring(serverport)) - return dispatch( handler ) - elseif err then -- maybe timeout or something else - out_put( "server.lua: error with new client connection: ", tostring(err) ) - return false - end - end - return handler -end - -wrapconnection = function( server, listeners, socket, ip, serverport, clientport, pattern, sslctx, startssl ) -- this function wraps a client to a handler object - - socket:settimeout( 0 ) - - --// local import of socket methods //-- - - local send - local receive - local shutdown - - --// private closures of the object //-- - - local ssl - - local dispatch = listeners.incoming or listeners.listener - local status = listeners.status - local disconnect = listeners.disconnect - - local bufferqueue = { } -- buffer array - local bufferqueuelen = 0 -- end of buffer array - - local toclose - local fatalerror - local needtls - - local bufferlen = 0 - - local noread = false - local nosend = false - - local sendtraffic, readtraffic = 0, 0 - - local maxsendlen = _maxsendlen - local maxreadlen = _maxreadlen - - --// public methods of the object //-- - - local handler = bufferqueue -- saves a table ^_^ - - handler.dispatch = function( ) - return dispatch - end - handler.disconnect = function( ) - return disconnect - end - handler.setlistener = function( listeners ) - dispatch = listeners.incoming - disconnect = listeners.disconnect - end - handler.getstats = function( ) - return readtraffic, sendtraffic - end - handler.ssl = function( ) - return ssl - end - handler.sslctx = function ( ) - return sslctx - end - handler.send = function( _, data, i, j ) - return send( socket, data, i, j ) - end - handler.receive = function( pattern, prefix ) - return receive( socket, pattern, prefix ) - end - handler.shutdown = function( pattern ) - return shutdown( socket, pattern ) - end - handler.close = function( forced ) - if not handler then return true; end - _readlistlen = removesocket( _readlist, socket, _readlistlen ) - _readtimes[ handler ] = nil - if bufferqueuelen ~= 0 then - if not ( forced or fatalerror ) then - handler.sendbuffer( ) - if bufferqueuelen ~= 0 then -- try again... - if handler then - handler.write = nil -- ... but no further writing allowed - end - toclose = true - return false - end - else - send( socket, table_concat( bufferqueue, "", 1, bufferqueuelen ), 1, bufferlen ) -- forced send - end - end - if socket then - _ = shutdown and shutdown( socket ) - socket:close( ) - _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) - _socketlist[ socket ] = nil - socket = nil - else - out_put "server.lua: socket already closed" - end - if handler then - _writetimes[ handler ] = nil - _closelist[ handler ] = nil - handler = nil - end - if server then - server.remove( ) - end - out_put "server.lua: closed client handler and removed socket from list" - return true - end - handler.ip = function( ) - return ip - end - handler.serverport = function( ) - return serverport - end - handler.clientport = function( ) - return clientport - end - local write = function( data ) - bufferlen = bufferlen + string_len( data ) - if bufferlen > maxsendlen then - _closelist[ handler ] = "send buffer exceeded" -- cannot close the client at the moment, have to wait to the end of the cycle - handler.write = idfalse -- dont write anymore - return false - elseif socket and not _sendlist[ socket ] then - _sendlistlen = addsocket(_sendlist, socket, _sendlistlen) - end - bufferqueuelen = bufferqueuelen + 1 - bufferqueue[ bufferqueuelen ] = data - if handler then - _writetimes[ handler ] = _writetimes[ handler ] or _currenttime - end - return true - end - handler.write = write - handler.bufferqueue = function( ) - return bufferqueue - end - handler.socket = function( ) - return socket - end - handler.pattern = function( new ) - pattern = new or pattern - return pattern - end - handler.setsend = function ( newsend ) - send = newsend or send - return send - end - handler.bufferlen = function( readlen, sendlen ) - maxsendlen = sendlen or maxsendlen - maxreadlen = readlen or maxreadlen - return maxreadlen, maxsendlen - end - handler.lock = function( switch ) - if switch == true then - handler.write = idfalse - local tmp = _sendlistlen - _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) - _writetimes[ handler ] = nil - if _sendlistlen ~= tmp then - nosend = true - end - tmp = _readlistlen - _readlistlen = removesocket( _readlist, socket, _readlistlen ) - _readtimes[ handler ] = nil - if _readlistlen ~= tmp then - noread = true - end - elseif switch == false then - handler.write = write - if noread then - noread = false - _readlistlen = addsocket(_readlist, socket, _readlistlen) - _readtimes[ handler ] = _currenttime - end - if nosend then - nosend = false - write( "" ) - end - end - return noread, nosend - end - local _readbuffer = function( ) -- this function reads data - local buffer, err, part = receive( socket, pattern ) -- receive buffer with "pattern" - if not err or ( err == "timeout" or err == "wantread" ) then -- received something - local buffer = buffer or part or "" - local len = string_len( buffer ) - if len > maxreadlen then - disconnect( handler, "receive buffer exceeded" ) - handler.close( true ) - return false - end - local count = len * STAT_UNIT - readtraffic = readtraffic + count - _readtraffic = _readtraffic + count - _readtimes[ handler ] = _currenttime - --out_put( "server.lua: read data '", buffer:gsub("[^%w%p ]", "."), "', error: ", err ) - return dispatch( handler, buffer, err ) - else -- connections was closed or fatal error - out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " read error: ", tostring(err) ) - fatalerror = true - disconnect( handler, err ) - _ = handler and handler.close( ) - return false - end - end - local _sendbuffer = function( ) -- this function sends data - local succ, err, byte, buffer, count; - local count; - if socket then - buffer = table_concat( bufferqueue, "", 1, bufferqueuelen ) - succ, err, byte = send( socket, buffer, 1, bufferlen ) - count = ( succ or byte or 0 ) * STAT_UNIT - sendtraffic = sendtraffic + count - _sendtraffic = _sendtraffic + count - _ = _cleanqueue and clean( bufferqueue ) - --out_put( "server.lua: sended '", buffer, "', bytes: ", tostring(succ), ", error: ", tostring(err), ", part: ", tostring(byte), ", to: ", tostring(ip), ":", tostring(clientport) ) - else - succ, err, count = false, "closed", 0; - end - if succ then -- sending succesful - bufferqueuelen = 0 - bufferlen = 0 - _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) -- delete socket from writelist - _ = needtls and handler.starttls(true) - _writetimes[ handler ] = nil - _ = toclose and handler.close( ) - return true - elseif byte and ( err == "timeout" or err == "wantwrite" ) then -- want write - buffer = string_sub( buffer, byte + 1, bufferlen ) -- new buffer - bufferqueue[ 1 ] = buffer -- insert new buffer in queue - bufferqueuelen = 1 - bufferlen = bufferlen - byte - _writetimes[ handler ] = _currenttime - return true - else -- connection was closed during sending or fatal error - out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " write error: ", tostring(err) ) - fatalerror = true - disconnect( handler, err ) - _ = handler and handler.close( ) - return false - end - end - - -- Set the sslctx - local handshake; - function handler.set_sslctx(new_sslctx) - ssl = true - sslctx = new_sslctx; - local wrote - local read - handshake = coroutine_wrap( function( client ) -- create handshake coroutine - local err - for i = 1, _maxsslhandshake do - _sendlistlen = ( wrote and removesocket( _sendlist, client, _sendlistlen ) ) or _sendlistlen - _readlistlen = ( read and removesocket( _readlist, client, _readlistlen ) ) or _readlistlen - read, wrote = nil, nil - _, err = client:dohandshake( ) - if not err then - out_put( "server.lua: ssl handshake done" ) - handler.readbuffer = _readbuffer -- when handshake is done, replace the handshake function with regular functions - handler.sendbuffer = _sendbuffer - _ = status and status( handler, "ssl-handshake-complete" ) - _readlistlen = addsocket(_readlist, client, _readlistlen) - return true - else - out_put( "server.lua: error during ssl handshake: ", tostring(err) ) - if err == "wantwrite" and not wrote then - _sendlistlen = addsocket(_sendlist, client, _sendlistlen) - wrote = true - elseif err == "wantread" and not read then - _readlistlen = addsocket(_readlist, client, _readlistlen) - read = true - else - break; - end - --coroutine_yield( handler, nil, err ) -- handshake not finished - coroutine_yield( ) - end - end - disconnect( handler, "ssl handshake failed" ) - _ = handler and handler.close( true ) -- forced disconnect - return false -- handshake failed - end - ) - end - if sslctx then -- ssl? - handler.set_sslctx(sslctx); - if startssl then -- ssl now? - --out_put("server.lua: ", "starting ssl handshake") - local err - socket, err = ssl_wrap( socket, sslctx ) -- wrap socket - if err then - out_put( "server.lua: ssl error: ", tostring(err) ) - --mem_free( ) - return nil, nil, err -- fatal error - end - socket:settimeout( 0 ) - handler.readbuffer = handshake - handler.sendbuffer = handshake - handshake( socket ) -- do handshake - if not socket then - return nil, nil, "ssl handshake failed"; - end - else - -- We're not automatically doing SSL, so we're not secure (yet) - ssl = false - handler.starttls = function( now ) - if not now then - --out_put "server.lua: we need to do tls, but delaying until later" - needtls = true - return - end - --out_put( "server.lua: attempting to start tls on " .. tostring( socket ) ) - local oldsocket, err = socket - socket, err = ssl_wrap( socket, sslctx ) -- wrap socket - --out_put( "server.lua: sslwrapped socket is " .. tostring( socket ) ) - if err then - out_put( "server.lua: error while starting tls on client: ", tostring(err) ) - return nil, err -- fatal error - end - - socket:settimeout( 0 ) - - -- add the new socket to our system - - send = socket.send - receive = socket.receive - shutdown = id - - _socketlist[ socket ] = handler - _readlistlen = addsocket(_readlist, socket, _readlistlen) - - -- remove traces of the old socket - - _readlistlen = removesocket( _readlist, oldsocket, _readlistlen ) - _sendlistlen = removesocket( _sendlist, oldsocket, _sendlistlen ) - _socketlist[ oldsocket ] = nil - - handler.starttls = nil - needtls = nil - - -- Secure now - ssl = true - - handler.readbuffer = handshake - handler.sendbuffer = handshake - handshake( socket ) -- do handshake - end - handler.readbuffer = _readbuffer - handler.sendbuffer = _sendbuffer - end - else -- normal connection - ssl = false - handler.readbuffer = _readbuffer - handler.sendbuffer = _sendbuffer - end - - send = socket.send - receive = socket.receive - shutdown = ( ssl and id ) or socket.shutdown - - _socketlist[ socket ] = handler - _readlistlen = addsocket(_readlist, socket, _readlistlen) - - return handler, socket -end - -id = function( ) -end - -idfalse = function( ) - return false -end - -addsocket = function( list, socket, len ) - if not list[ socket ] then - len = len + 1 - list[ len ] = socket - list[ socket ] = len - end - return len; -end - -removesocket = function( list, socket, len ) -- this function removes sockets from a list ( copied from copas ) - local pos = list[ socket ] - if pos then - list[ socket ] = nil - local last = list[ len ] - list[ len ] = nil - if last ~= socket then - list[ last ] = pos - list[ pos ] = last - end - return len - 1 - end - return len -end - -closesocket = function( socket ) - _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) - _readlistlen = removesocket( _readlist, socket, _readlistlen ) - _socketlist[ socket ] = nil - socket:close( ) - --mem_free( ) -end - -----------------------------------// PUBLIC //-- - -addserver = function( listeners, port, addr, pattern, sslctx, maxconnections, startssl ) -- this function provides a way for other scripts to reg a server - local err - --out_put("server.lua: autossl on ", port, " is ", startssl) - if type( listeners ) ~= "table" then - err = "invalid listener table" - end - if not type( port ) == "number" or not ( port >= 0 and port <= 65535 ) then - err = "invalid port" - elseif _server[ port ] then - err = "listeners on port '" .. port .. "' already exist" - elseif sslctx and not luasec then - err = "luasec not found" - end - if err then - out_error( "server.lua, port ", port, ": ", err ) - return nil, err - end - addr = addr or "*" - local server, err = socket_bind( addr, port ) - if err then - out_error( "server.lua, port ", port, ": ", err ) - return nil, err - end - local handler, err = wrapserver( listeners, server, addr, port, pattern, sslctx, maxconnections, startssl ) -- wrap new server socket - if not handler then - server:close( ) - return nil, err - end - server:settimeout( 0 ) - _readlistlen = addsocket(_readlist, server, _readlistlen) - _server[ port ] = handler - _socketlist[ server ] = handler - out_put( "server.lua: new server listener on '", addr, ":", port, "'" ) - return handler -end - -getserver = function ( port ) - return _server[ port ]; -end - -removeserver = function( port ) - local handler = _server[ port ] - if not handler then - return nil, "no server found on port '" .. tostring( port ) .. "'" - end - handler.close( ) - _server[ port ] = nil - return true -end - -closeall = function( ) - for _, handler in pairs( _socketlist ) do - handler.close( ) - _socketlist[ _ ] = nil - end - _readlistlen = 0 - _sendlistlen = 0 - _timerlistlen = 0 - _server = { } - _readlist = { } - _sendlist = { } - _timerlist = { } - _socketlist = { } - --mem_free( ) -end - -getsettings = function( ) - return _selecttimeout, _sleeptime, _maxsendlen, _maxreadlen, _checkinterval, _sendtimeout, _readtimeout, _cleanqueue, _maxclientsperserver, _maxsslhandshake -end - -changesettings = function( new ) - if type( new ) ~= "table" then - return nil, "invalid settings table" - end - _selecttimeout = tonumber( new.timeout ) or _selecttimeout - _sleeptime = tonumber( new.sleeptime ) or _sleeptime - _maxsendlen = tonumber( new.maxsendlen ) or _maxsendlen - _maxreadlen = tonumber( new.maxreadlen ) or _maxreadlen - _checkinterval = tonumber( new.checkinterval ) or _checkinterval - _sendtimeout = tonumber( new.sendtimeout ) or _sendtimeout - _readtimeout = tonumber( new.readtimeout ) or _readtimeout - _cleanqueue = new.cleanqueue - _maxclientsperserver = new._maxclientsperserver or _maxclientsperserver - _maxsslhandshake = new._maxsslhandshake or _maxsslhandshake - return true -end - -addtimer = function( listener ) - if type( listener ) ~= "function" then - return nil, "invalid listener function" - end - _timerlistlen = _timerlistlen + 1 - _timerlist[ _timerlistlen ] = listener - return true -end - -stats = function( ) - return _readtraffic, _sendtraffic, _readlistlen, _sendlistlen, _timerlistlen -end - -local dontstop = true; -- thinking about tomorrow, ... - -setquitting = function (quit) - dontstop = not quit; - return; -end - -loop = function( ) -- this is the main loop of the program - while dontstop do - local read, write, err = socket_select( _readlist, _sendlist, _selecttimeout ) - for i, socket in ipairs( write ) do -- send data waiting in writequeues - local handler = _socketlist[ socket ] - if handler then - handler.sendbuffer( ) - else - closesocket( socket ) - out_put "server.lua: found no handler and closed socket (writelist)" -- this should not happen - end - end - for i, socket in ipairs( read ) do -- receive data - local handler = _socketlist[ socket ] - if handler then - handler.readbuffer( ) - else - closesocket( socket ) - out_put "server.lua: found no handler and closed socket (readlist)" -- this can happen - end - end - for handler, err in pairs( _closelist ) do - handler.disconnect( )( handler, err ) - handler.close( true ) -- forced disconnect - end - clean( _closelist ) - _currenttime = os_time( ) - if os_difftime( _currenttime - _timer ) >= 1 then - for i = 1, _timerlistlen do - _timerlist[ i ]( _currenttime ) -- fire timers - end - _timer = _currenttime - end - socket_sleep( _sleeptime ) -- wait some time - --collectgarbage( ) - end - return "quitting" -end - ---// EXPERIMENTAL //-- - -local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx, startssl ) - local handler = wrapconnection( nil, listeners, socket, ip, serverport, "clientport", pattern, sslctx, startssl ) - _socketlist[ socket ] = handler - _sendlistlen = addsocket(_sendlist, socket, _sendlistlen) - return handler, socket -end - -local addclient = function( address, port, listeners, pattern, sslctx, startssl ) - local client, err = luasocket.tcp( ) - if err then - return nil, err - end - client:settimeout( 0 ) - _, err = client:connect( address, port ) - if err then -- try again - local handler = wrapclient( client, address, port, listeners ) - else - wrapconnection( nil, listeners, client, address, port, "clientport", pattern, sslctx, startssl ) - end -end - ---// EXPERIMENTAL //-- - -----------------------------------// BEGIN //-- - -use "setmetatable" ( _socketlist, { __mode = "k" } ) -use "setmetatable" ( _readtimes, { __mode = "k" } ) -use "setmetatable" ( _writetimes, { __mode = "k" } ) - -_timer = os_time( ) -_starttime = os_time( ) - -addtimer( function( ) - local difftime = os_difftime( _currenttime - _starttime ) - if difftime > _checkinterval then - _starttime = _currenttime - for handler, timestamp in pairs( _writetimes ) do - if os_difftime( _currenttime - timestamp ) > _sendtimeout then - --_writetimes[ handler ] = nil - handler.disconnect( )( handler, "send timeout" ) - handler.close( true ) -- forced disconnect - end - end - for handler, timestamp in pairs( _readtimes ) do - if os_difftime( _currenttime - timestamp ) > _readtimeout then - --_readtimes[ handler ] = nil - handler.disconnect( )( handler, "read timeout" ) - handler.close( ) -- forced disconnect? - end - end - end - end -) - -----------------------------------// PUBLIC INTERFACE //-- - -return { - - addclient = addclient, - wrapclient = wrapclient, - - loop = loop, - stats = stats, - closeall = closeall, - addtimer = addtimer, - addserver = addserver, - getserver = getserver, - getsettings = getsettings, - setquitting = setquitting, - removeserver = removeserver, - changesettings = changesettings, -} diff --git a/net/server_select.lua b/net/server_select.lua new file mode 100644 index 00000000..6ab8ce91 --- /dev/null +++ b/net/server_select.lua @@ -0,0 +1,914 @@ +-- +-- server.lua by blastbeat of the luadch project +-- Re-used here under the MIT/X Consortium License +-- +-- Modifications (C) 2008-2009 Matthew Wild, Waqas Hussain +-- + +-- // wrapping luadch stuff // -- + +local use = function( what ) + return _G[ what ] +end +local clean = function( tbl ) + for i, k in pairs( tbl ) do + tbl[ i ] = nil + end +end + +local log, table_concat = require ("util.logger").init("socket"), table.concat; +local out_put = function (...) return log("debug", table_concat{...}); end +local out_error = function (...) return log("warn", table_concat{...}); end +local mem_free = collectgarbage + +----------------------------------// DECLARATION //-- + +--// constants //-- + +local STAT_UNIT = 1 -- byte + +--// lua functions //-- + +local type = use "type" +local pairs = use "pairs" +local ipairs = use "ipairs" +local tostring = use "tostring" +local collectgarbage = use "collectgarbage" + +--// lua libs //-- + +local os = use "os" +local table = use "table" +local string = use "string" +local coroutine = use "coroutine" + +--// lua lib methods //-- + +local os_time = os.time +local os_difftime = os.difftime +local table_concat = table.concat +local table_remove = table.remove +local string_len = string.len +local string_sub = string.sub +local coroutine_wrap = coroutine.wrap +local coroutine_yield = coroutine.yield + +--// extern libs //-- + +local luasec = select( 2, pcall( require, "ssl" ) ) +local luasocket = require "socket" + +--// extern lib methods //-- + +local ssl_wrap = ( luasec and luasec.wrap ) +local socket_bind = luasocket.bind +local socket_sleep = luasocket.sleep +local socket_select = luasocket.select +local ssl_newcontext = ( luasec and luasec.newcontext ) + +--// functions //-- + +local id +local loop +local stats +local idfalse +local addtimer +local closeall +local addserver +local getserver +local wrapserver +local getsettings +local closesocket +local removesocket +local removeserver +local changetimeout +local wrapconnection +local changesettings + +--// tables //-- + +local _server +local _readlist +local _timerlist +local _sendlist +local _socketlist +local _closelist +local _readtimes +local _writetimes + +--// simple data types //-- + +local _ +local _readlistlen +local _sendlistlen +local _timerlistlen + +local _sendtraffic +local _readtraffic + +local _selecttimeout +local _sleeptime + +local _starttime +local _currenttime + +local _maxsendlen +local _maxreadlen + +local _checkinterval +local _sendtimeout +local _readtimeout + +local _cleanqueue + +local _timer + +local _maxclientsperserver + +----------------------------------// DEFINITION //-- + +_server = { } -- key = port, value = table; list of listening servers +_readlist = { } -- array with sockets to read from +_sendlist = { } -- arrary with sockets to write to +_timerlist = { } -- array of timer functions +_socketlist = { } -- key = socket, value = wrapped socket (handlers) +_readtimes = { } -- key = handler, value = timestamp of last data reading +_writetimes = { } -- key = handler, value = timestamp of last data writing/sending +_closelist = { } -- handlers to close + +_readlistlen = 0 -- length of readlist +_sendlistlen = 0 -- length of sendlist +_timerlistlen = 0 -- lenght of timerlist + +_sendtraffic = 0 -- some stats +_readtraffic = 0 + +_selecttimeout = 1 -- timeout of socket.select +_sleeptime = 0 -- time to wait at the end of every loop + +_maxsendlen = 51000 * 1024 -- max len of send buffer +_maxreadlen = 25000 * 1024 -- max len of read buffer + +_checkinterval = 1200000 -- interval in secs to check idle clients +_sendtimeout = 60000 -- allowed send idle time in secs +_readtimeout = 6 * 60 * 60 -- allowed read idle time in secs + +_cleanqueue = false -- clean bufferqueue after using + +_maxclientsperserver = 1000 + +_maxsslhandshake = 30 -- max handshake round-trips +----------------------------------// PRIVATE //-- + +wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx, maxconnections, startssl ) -- this function wraps a server + + maxconnections = maxconnections or _maxclientsperserver + + local connections = 0 + + local dispatch, disconnect = listeners.incoming or listeners.listener, listeners.disconnect + + local err + + local ssl = false + + if sslctx then + ssl = true + if not ssl_newcontext then + out_error "luasec not found" + ssl = false + end + if type( sslctx ) ~= "table" then + out_error "server.lua: wrong server sslctx" + ssl = false + end + local ctx; + ctx, err = ssl_newcontext( sslctx ) + if not ctx then + err = err or "wrong sslctx parameters" + local file; + file = err:match("^error loading (.-) %("); + if file then + if file == "private key" then + file = sslctx.key or "your private key"; + elseif file == "certificate" then + file = sslctx.certificate or "your certificate file"; + end + local reason = err:match("%((.+)%)$") or "some reason"; + if reason == "Permission denied" then + reason = "Check that the permissions allow Prosody to read this file."; + elseif reason == "No such file or directory" then + reason = "Check that the path is correct, and the file exists."; + elseif reason == "system lib" then + reason = "Previous error (see logs), or other system error."; + else + reason = "Reason: "..tostring(reason or "unknown"):lower(); + end + log("error", "SSL/TLS: Failed to load %s: %s", file, reason); + else + log("error", "SSL/TLS: Error initialising for port %d: %s", serverport, err ); + end + ssl = false + end + sslctx = ctx; + end + if not ssl then + sslctx = false; + if startssl then + log("error", "Failed to listen on port %d due to SSL/TLS to SSL/TLS initialisation errors (see logs)", serverport ) + return nil, "Cannot start ssl, see log for details" + end + end + + local accept = socket.accept + + --// public methods of the object //-- + + local handler = { } + + handler.shutdown = function( ) end + + handler.ssl = function( ) + return ssl + end + handler.sslctx = function( ) + return sslctx + end + handler.remove = function( ) + connections = connections - 1 + end + handler.close = function( ) + for _, handler in pairs( _socketlist ) do + if handler.serverport == serverport then + handler.disconnect( handler, "server closed" ) + handler.close( true ) + end + end + socket:close( ) + _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) + _readlistlen = removesocket( _readlist, socket, _readlistlen ) + _socketlist[ socket ] = nil + handler = nil + socket = nil + --mem_free( ) + out_put "server.lua: closed server handler and removed sockets from list" + end + handler.ip = function( ) + return ip + end + handler.serverport = function( ) + return serverport + end + handler.socket = function( ) + return socket + end + handler.readbuffer = function( ) + if connections > maxconnections then + out_put( "server.lua: refused new client connection: server full" ) + return false + end + local client, err = accept( socket ) -- try to accept + if client then + local ip, clientport = client:getpeername( ) + client:settimeout( 0 ) + local handler, client, err = wrapconnection( handler, listeners, client, ip, serverport, clientport, pattern, sslctx, startssl ) -- wrap new client socket + if err then -- error while wrapping ssl socket + return false + end + connections = connections + 1 + out_put( "server.lua: accepted new client connection from ", tostring(ip), ":", tostring(clientport), " to ", tostring(serverport)) + return dispatch( handler ) + elseif err then -- maybe timeout or something else + out_put( "server.lua: error with new client connection: ", tostring(err) ) + return false + end + end + return handler +end + +wrapconnection = function( server, listeners, socket, ip, serverport, clientport, pattern, sslctx, startssl ) -- this function wraps a client to a handler object + + socket:settimeout( 0 ) + + --// local import of socket methods //-- + + local send + local receive + local shutdown + + --// private closures of the object //-- + + local ssl + + local dispatch = listeners.incoming or listeners.listener + local status = listeners.status + local disconnect = listeners.disconnect + + local bufferqueue = { } -- buffer array + local bufferqueuelen = 0 -- end of buffer array + + local toclose + local fatalerror + local needtls + + local bufferlen = 0 + + local noread = false + local nosend = false + + local sendtraffic, readtraffic = 0, 0 + + local maxsendlen = _maxsendlen + local maxreadlen = _maxreadlen + + --// public methods of the object //-- + + local handler = bufferqueue -- saves a table ^_^ + + handler.dispatch = function( ) + return dispatch + end + handler.disconnect = function( ) + return disconnect + end + handler.setlistener = function( listeners ) + dispatch = listeners.incoming + disconnect = listeners.disconnect + end + handler.getstats = function( ) + return readtraffic, sendtraffic + end + handler.ssl = function( ) + return ssl + end + handler.sslctx = function ( ) + return sslctx + end + handler.send = function( _, data, i, j ) + return send( socket, data, i, j ) + end + handler.receive = function( pattern, prefix ) + return receive( socket, pattern, prefix ) + end + handler.shutdown = function( pattern ) + return shutdown( socket, pattern ) + end + handler.close = function( forced ) + if not handler then return true; end + _readlistlen = removesocket( _readlist, socket, _readlistlen ) + _readtimes[ handler ] = nil + if bufferqueuelen ~= 0 then + if not ( forced or fatalerror ) then + handler.sendbuffer( ) + if bufferqueuelen ~= 0 then -- try again... + if handler then + handler.write = nil -- ... but no further writing allowed + end + toclose = true + return false + end + else + send( socket, table_concat( bufferqueue, "", 1, bufferqueuelen ), 1, bufferlen ) -- forced send + end + end + if socket then + _ = shutdown and shutdown( socket ) + socket:close( ) + _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) + _socketlist[ socket ] = nil + socket = nil + else + out_put "server.lua: socket already closed" + end + if handler then + _writetimes[ handler ] = nil + _closelist[ handler ] = nil + handler = nil + end + if server then + server.remove( ) + end + out_put "server.lua: closed client handler and removed socket from list" + return true + end + handler.ip = function( ) + return ip + end + handler.serverport = function( ) + return serverport + end + handler.clientport = function( ) + return clientport + end + local write = function( data ) + bufferlen = bufferlen + string_len( data ) + if bufferlen > maxsendlen then + _closelist[ handler ] = "send buffer exceeded" -- cannot close the client at the moment, have to wait to the end of the cycle + handler.write = idfalse -- dont write anymore + return false + elseif socket and not _sendlist[ socket ] then + _sendlistlen = addsocket(_sendlist, socket, _sendlistlen) + end + bufferqueuelen = bufferqueuelen + 1 + bufferqueue[ bufferqueuelen ] = data + if handler then + _writetimes[ handler ] = _writetimes[ handler ] or _currenttime + end + return true + end + handler.write = write + handler.bufferqueue = function( ) + return bufferqueue + end + handler.socket = function( ) + return socket + end + handler.pattern = function( new ) + pattern = new or pattern + return pattern + end + handler.setsend = function ( newsend ) + send = newsend or send + return send + end + handler.bufferlen = function( readlen, sendlen ) + maxsendlen = sendlen or maxsendlen + maxreadlen = readlen or maxreadlen + return maxreadlen, maxsendlen + end + handler.lock = function( switch ) + if switch == true then + handler.write = idfalse + local tmp = _sendlistlen + _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) + _writetimes[ handler ] = nil + if _sendlistlen ~= tmp then + nosend = true + end + tmp = _readlistlen + _readlistlen = removesocket( _readlist, socket, _readlistlen ) + _readtimes[ handler ] = nil + if _readlistlen ~= tmp then + noread = true + end + elseif switch == false then + handler.write = write + if noread then + noread = false + _readlistlen = addsocket(_readlist, socket, _readlistlen) + _readtimes[ handler ] = _currenttime + end + if nosend then + nosend = false + write( "" ) + end + end + return noread, nosend + end + local _readbuffer = function( ) -- this function reads data + local buffer, err, part = receive( socket, pattern ) -- receive buffer with "pattern" + if not err or ( err == "timeout" or err == "wantread" ) then -- received something + local buffer = buffer or part or "" + local len = string_len( buffer ) + if len > maxreadlen then + disconnect( handler, "receive buffer exceeded" ) + handler.close( true ) + return false + end + local count = len * STAT_UNIT + readtraffic = readtraffic + count + _readtraffic = _readtraffic + count + _readtimes[ handler ] = _currenttime + --out_put( "server.lua: read data '", buffer:gsub("[^%w%p ]", "."), "', error: ", err ) + return dispatch( handler, buffer, err ) + else -- connections was closed or fatal error + out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " read error: ", tostring(err) ) + fatalerror = true + disconnect( handler, err ) + _ = handler and handler.close( ) + return false + end + end + local _sendbuffer = function( ) -- this function sends data + local succ, err, byte, buffer, count; + local count; + if socket then + buffer = table_concat( bufferqueue, "", 1, bufferqueuelen ) + succ, err, byte = send( socket, buffer, 1, bufferlen ) + count = ( succ or byte or 0 ) * STAT_UNIT + sendtraffic = sendtraffic + count + _sendtraffic = _sendtraffic + count + _ = _cleanqueue and clean( bufferqueue ) + --out_put( "server.lua: sended '", buffer, "', bytes: ", tostring(succ), ", error: ", tostring(err), ", part: ", tostring(byte), ", to: ", tostring(ip), ":", tostring(clientport) ) + else + succ, err, count = false, "closed", 0; + end + if succ then -- sending succesful + bufferqueuelen = 0 + bufferlen = 0 + _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) -- delete socket from writelist + _ = needtls and handler.starttls(true) + _writetimes[ handler ] = nil + _ = toclose and handler.close( ) + return true + elseif byte and ( err == "timeout" or err == "wantwrite" ) then -- want write + buffer = string_sub( buffer, byte + 1, bufferlen ) -- new buffer + bufferqueue[ 1 ] = buffer -- insert new buffer in queue + bufferqueuelen = 1 + bufferlen = bufferlen - byte + _writetimes[ handler ] = _currenttime + return true + else -- connection was closed during sending or fatal error + out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " write error: ", tostring(err) ) + fatalerror = true + disconnect( handler, err ) + _ = handler and handler.close( ) + return false + end + end + + -- Set the sslctx + local handshake; + function handler.set_sslctx(new_sslctx) + ssl = true + sslctx = new_sslctx; + local wrote + local read + handshake = coroutine_wrap( function( client ) -- create handshake coroutine + local err + for i = 1, _maxsslhandshake do + _sendlistlen = ( wrote and removesocket( _sendlist, client, _sendlistlen ) ) or _sendlistlen + _readlistlen = ( read and removesocket( _readlist, client, _readlistlen ) ) or _readlistlen + read, wrote = nil, nil + _, err = client:dohandshake( ) + if not err then + out_put( "server.lua: ssl handshake done" ) + handler.readbuffer = _readbuffer -- when handshake is done, replace the handshake function with regular functions + handler.sendbuffer = _sendbuffer + _ = status and status( handler, "ssl-handshake-complete" ) + _readlistlen = addsocket(_readlist, client, _readlistlen) + return true + else + out_put( "server.lua: error during ssl handshake: ", tostring(err) ) + if err == "wantwrite" and not wrote then + _sendlistlen = addsocket(_sendlist, client, _sendlistlen) + wrote = true + elseif err == "wantread" and not read then + _readlistlen = addsocket(_readlist, client, _readlistlen) + read = true + else + break; + end + --coroutine_yield( handler, nil, err ) -- handshake not finished + coroutine_yield( ) + end + end + disconnect( handler, "ssl handshake failed" ) + _ = handler and handler.close( true ) -- forced disconnect + return false -- handshake failed + end + ) + end + if sslctx then -- ssl? + handler.set_sslctx(sslctx); + if startssl then -- ssl now? + --out_put("server.lua: ", "starting ssl handshake") + local err + socket, err = ssl_wrap( socket, sslctx ) -- wrap socket + if err then + out_put( "server.lua: ssl error: ", tostring(err) ) + --mem_free( ) + return nil, nil, err -- fatal error + end + socket:settimeout( 0 ) + handler.readbuffer = handshake + handler.sendbuffer = handshake + handshake( socket ) -- do handshake + if not socket then + return nil, nil, "ssl handshake failed"; + end + else + -- We're not automatically doing SSL, so we're not secure (yet) + ssl = false + handler.starttls = function( now ) + if not now then + --out_put "server.lua: we need to do tls, but delaying until later" + needtls = true + return + end + --out_put( "server.lua: attempting to start tls on " .. tostring( socket ) ) + local oldsocket, err = socket + socket, err = ssl_wrap( socket, sslctx ) -- wrap socket + --out_put( "server.lua: sslwrapped socket is " .. tostring( socket ) ) + if err then + out_put( "server.lua: error while starting tls on client: ", tostring(err) ) + return nil, err -- fatal error + end + + socket:settimeout( 0 ) + + -- add the new socket to our system + + send = socket.send + receive = socket.receive + shutdown = id + + _socketlist[ socket ] = handler + _readlistlen = addsocket(_readlist, socket, _readlistlen) + + -- remove traces of the old socket + + _readlistlen = removesocket( _readlist, oldsocket, _readlistlen ) + _sendlistlen = removesocket( _sendlist, oldsocket, _sendlistlen ) + _socketlist[ oldsocket ] = nil + + handler.starttls = nil + needtls = nil + + -- Secure now + ssl = true + + handler.readbuffer = handshake + handler.sendbuffer = handshake + handshake( socket ) -- do handshake + end + handler.readbuffer = _readbuffer + handler.sendbuffer = _sendbuffer + end + else -- normal connection + ssl = false + handler.readbuffer = _readbuffer + handler.sendbuffer = _sendbuffer + end + + send = socket.send + receive = socket.receive + shutdown = ( ssl and id ) or socket.shutdown + + _socketlist[ socket ] = handler + _readlistlen = addsocket(_readlist, socket, _readlistlen) + + return handler, socket +end + +id = function( ) +end + +idfalse = function( ) + return false +end + +addsocket = function( list, socket, len ) + if not list[ socket ] then + len = len + 1 + list[ len ] = socket + list[ socket ] = len + end + return len; +end + +removesocket = function( list, socket, len ) -- this function removes sockets from a list ( copied from copas ) + local pos = list[ socket ] + if pos then + list[ socket ] = nil + local last = list[ len ] + list[ len ] = nil + if last ~= socket then + list[ last ] = pos + list[ pos ] = last + end + return len - 1 + end + return len +end + +closesocket = function( socket ) + _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) + _readlistlen = removesocket( _readlist, socket, _readlistlen ) + _socketlist[ socket ] = nil + socket:close( ) + --mem_free( ) +end + +----------------------------------// PUBLIC //-- + +addserver = function( listeners, port, addr, pattern, sslctx, maxconnections, startssl ) -- this function provides a way for other scripts to reg a server + local err + --out_put("server.lua: autossl on ", port, " is ", startssl) + if type( listeners ) ~= "table" then + err = "invalid listener table" + end + if not type( port ) == "number" or not ( port >= 0 and port <= 65535 ) then + err = "invalid port" + elseif _server[ port ] then + err = "listeners on port '" .. port .. "' already exist" + elseif sslctx and not luasec then + err = "luasec not found" + end + if err then + out_error( "server.lua, port ", port, ": ", err ) + return nil, err + end + addr = addr or "*" + local server, err = socket_bind( addr, port ) + if err then + out_error( "server.lua, port ", port, ": ", err ) + return nil, err + end + local handler, err = wrapserver( listeners, server, addr, port, pattern, sslctx, maxconnections, startssl ) -- wrap new server socket + if not handler then + server:close( ) + return nil, err + end + server:settimeout( 0 ) + _readlistlen = addsocket(_readlist, server, _readlistlen) + _server[ port ] = handler + _socketlist[ server ] = handler + out_put( "server.lua: new server listener on '", addr, ":", port, "'" ) + return handler +end + +getserver = function ( port ) + return _server[ port ]; +end + +removeserver = function( port ) + local handler = _server[ port ] + if not handler then + return nil, "no server found on port '" .. tostring( port ) .. "'" + end + handler.close( ) + _server[ port ] = nil + return true +end + +closeall = function( ) + for _, handler in pairs( _socketlist ) do + handler.close( ) + _socketlist[ _ ] = nil + end + _readlistlen = 0 + _sendlistlen = 0 + _timerlistlen = 0 + _server = { } + _readlist = { } + _sendlist = { } + _timerlist = { } + _socketlist = { } + --mem_free( ) +end + +getsettings = function( ) + return _selecttimeout, _sleeptime, _maxsendlen, _maxreadlen, _checkinterval, _sendtimeout, _readtimeout, _cleanqueue, _maxclientsperserver, _maxsslhandshake +end + +changesettings = function( new ) + if type( new ) ~= "table" then + return nil, "invalid settings table" + end + _selecttimeout = tonumber( new.timeout ) or _selecttimeout + _sleeptime = tonumber( new.sleeptime ) or _sleeptime + _maxsendlen = tonumber( new.maxsendlen ) or _maxsendlen + _maxreadlen = tonumber( new.maxreadlen ) or _maxreadlen + _checkinterval = tonumber( new.checkinterval ) or _checkinterval + _sendtimeout = tonumber( new.sendtimeout ) or _sendtimeout + _readtimeout = tonumber( new.readtimeout ) or _readtimeout + _cleanqueue = new.cleanqueue + _maxclientsperserver = new._maxclientsperserver or _maxclientsperserver + _maxsslhandshake = new._maxsslhandshake or _maxsslhandshake + return true +end + +addtimer = function( listener ) + if type( listener ) ~= "function" then + return nil, "invalid listener function" + end + _timerlistlen = _timerlistlen + 1 + _timerlist[ _timerlistlen ] = listener + return true +end + +stats = function( ) + return _readtraffic, _sendtraffic, _readlistlen, _sendlistlen, _timerlistlen +end + +local dontstop = true; -- thinking about tomorrow, ... + +setquitting = function (quit) + dontstop = not quit; + return; +end + +loop = function( ) -- this is the main loop of the program + while dontstop do + local read, write, err = socket_select( _readlist, _sendlist, _selecttimeout ) + for i, socket in ipairs( write ) do -- send data waiting in writequeues + local handler = _socketlist[ socket ] + if handler then + handler.sendbuffer( ) + else + closesocket( socket ) + out_put "server.lua: found no handler and closed socket (writelist)" -- this should not happen + end + end + for i, socket in ipairs( read ) do -- receive data + local handler = _socketlist[ socket ] + if handler then + handler.readbuffer( ) + else + closesocket( socket ) + out_put "server.lua: found no handler and closed socket (readlist)" -- this can happen + end + end + for handler, err in pairs( _closelist ) do + handler.disconnect( )( handler, err ) + handler.close( true ) -- forced disconnect + end + clean( _closelist ) + _currenttime = os_time( ) + if os_difftime( _currenttime - _timer ) >= 1 then + for i = 1, _timerlistlen do + _timerlist[ i ]( _currenttime ) -- fire timers + end + _timer = _currenttime + end + socket_sleep( _sleeptime ) -- wait some time + --collectgarbage( ) + end + return "quitting" +end + +--// EXPERIMENTAL //-- + +local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx, startssl ) + local handler = wrapconnection( nil, listeners, socket, ip, serverport, "clientport", pattern, sslctx, startssl ) + _socketlist[ socket ] = handler + _sendlistlen = addsocket(_sendlist, socket, _sendlistlen) + return handler, socket +end + +local addclient = function( address, port, listeners, pattern, sslctx, startssl ) + local client, err = luasocket.tcp( ) + if err then + return nil, err + end + client:settimeout( 0 ) + _, err = client:connect( address, port ) + if err then -- try again + local handler = wrapclient( client, address, port, listeners ) + else + wrapconnection( nil, listeners, client, address, port, "clientport", pattern, sslctx, startssl ) + end +end + +--// EXPERIMENTAL //-- + +----------------------------------// BEGIN //-- + +use "setmetatable" ( _socketlist, { __mode = "k" } ) +use "setmetatable" ( _readtimes, { __mode = "k" } ) +use "setmetatable" ( _writetimes, { __mode = "k" } ) + +_timer = os_time( ) +_starttime = os_time( ) + +addtimer( function( ) + local difftime = os_difftime( _currenttime - _starttime ) + if difftime > _checkinterval then + _starttime = _currenttime + for handler, timestamp in pairs( _writetimes ) do + if os_difftime( _currenttime - timestamp ) > _sendtimeout then + --_writetimes[ handler ] = nil + handler.disconnect( )( handler, "send timeout" ) + handler.close( true ) -- forced disconnect + end + end + for handler, timestamp in pairs( _readtimes ) do + if os_difftime( _currenttime - timestamp ) > _readtimeout then + --_readtimes[ handler ] = nil + handler.disconnect( )( handler, "read timeout" ) + handler.close( ) -- forced disconnect? + end + end + end + end +) + +----------------------------------// PUBLIC INTERFACE //-- + +return { + + addclient = addclient, + wrapclient = wrapclient, + + loop = loop, + stats = stats, + closeall = closeall, + addtimer = addtimer, + addserver = addserver, + getserver = getserver, + getsettings = getsettings, + setquitting = setquitting, + removeserver = removeserver, + changesettings = changesettings, +} -- cgit v1.2.3 From 2a8668dd30b4e52cd7e7f916527ae31694f01701 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 02:39:08 +0000 Subject: net.server: New net.server to choose the appropriate library from server_select/server_event based on the availability of luaevent and the use_libevent config option --- net/server.lua | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 net/server.lua (limited to 'net') diff --git a/net/server.lua b/net/server.lua new file mode 100644 index 00000000..507bd81e --- /dev/null +++ b/net/server.lua @@ -0,0 +1,21 @@ +local have_luaevent = pcall(require, "luaevent.core"); +local use_luaevent = require "core.configmanager".get("*", "core", "use_libevent"); + +local server; + +if have_luaevent and use_luaevent == true then + server = require "net.server_event"; + package.loaded["net.server"] = server; + + -- Backwards compatibility for timers, addtimer + -- called a function roughly every second + local add_task = require "util.timer"; + function server.addtimer(f) + return add_task(1, function (...) f(...); return 1; end); + end +else + server = require "net.server_select"; + package.loaded["net.server"] = server; +end + +return server; -- cgit v1.2.3 From 0c4e6e38e72dd3d447125dda36db091eb8a8214e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 02:41:09 +0000 Subject: net.server_event: Comment overly verbose log message --- net/server_event.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index fab617b7..82046d24 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -640,7 +640,7 @@ end local addserver = ( function( ) return function( addr, port, listener, pattern, backlog, sslcfg, startssl ) -- TODO: check arguments - debug( "creating new tcp server with following parameters:", addr or "nil", port or "nil", sslcfg or "nil", startssl or "nil") + --vdebug( "creating new tcp server with following parameters:", addr or "nil", port or "nil", sslcfg or "nil", startssl or "nil") local server, err = socket.bind( addr, port, backlog ) -- create server socket if not server then debug( "creating server socket failed because:", err ) -- cgit v1.2.3 From 27032e69558352d9d91c26f90894411ef64d0d0b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 03:15:59 +0000 Subject: net.server_event: Export base as event_base --- net/server_event.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 82046d24..375ab47e 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -740,6 +740,7 @@ return { base = base, loop = loop, event = event, + event_base = base, addevent = newevent, addserver = addserver, addclient = addclient, -- cgit v1.2.3 From 5f0238bc36814cd6d0beefde1a3a31d39d01b5e4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:11:59 +0000 Subject: net.connlisteners: Standardise on new syntax for addserver(), and clean up a bit --- net/connlisteners.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/connlisteners.lua b/net/connlisteners.lua index 230d92a4..a9b92a8c 100644 --- a/net/connlisteners.lua +++ b/net/connlisteners.lua @@ -61,9 +61,14 @@ function start(name, udata) end end - return server.addserver(h, - (udata and udata.port) or h.default_port or error("Can't start listener "..name.." because no port was specified, and it has no default port", 0), - (udata and udata.interface) or h.default_interface or "*", (udata and udata.mode) or h.default_mode or 1, (udata and udata.ssl) or nil, 99999999, udata and udata.type == "ssl"); + local interface = (udata and udata.interface) or h.default_interface or "*"; + local port = (udata and udata.port) or h.default_port or error("Can't start listener "..name.." because no port was specified, and it has no default port", 0); + local mode = (udata and udata.mode) or h.default_mode or 1; + local ssl = (udata and udata.ssl) or nil; + local maxclients = 99999999; + local autossl = udata and udata.type == "ssl"; + + return server.addserver(interface, port, h, mode, ssl, autossl); end return _M; -- cgit v1.2.3 From 5c624344662b7330a6f87d3b1e83865c2ac4dd62 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:13:21 +0000 Subject: net.server: Small fix for addtimer() compatibility code --- net/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/server.lua b/net/server.lua index 507bd81e..15bba603 100644 --- a/net/server.lua +++ b/net/server.lua @@ -9,7 +9,7 @@ if have_luaevent and use_luaevent == true then -- Backwards compatibility for timers, addtimer -- called a function roughly every second - local add_task = require "util.timer"; + local add_task = require "util.timer".add_task; function server.addtimer(f) return add_task(1, function (...) f(...); return 1; end); end -- cgit v1.2.3 From 6c6f24c491d6c3df9899d7a9c2734d72bec6e67c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:14:59 +0000 Subject: xmppclient_listener: Switch to .onincoming listener method, to be compatible with new server_event.lua --- net/xmppclient_listener.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua index 417dfd4a..fd4e7097 100644 --- a/net/xmppclient_listener.lua +++ b/net/xmppclient_listener.lua @@ -108,7 +108,7 @@ end -- End of session methods -- -function xmppclient.listener(conn, data) +function xmppclient.onincoming(conn, data) local session = sessions[conn]; if not session then session = sm_new_session(conn); -- cgit v1.2.3 From 2aa2de8a5bb0a37e167c4aab7cfa3a84c1444e95 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:15:59 +0000 Subject: xmppclient_listener: Use : syntax for connection methods, to be compatible with new server.lua --- net/xmppclient_listener.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua index fd4e7097..34438f3d 100644 --- a/net/xmppclient_listener.lua +++ b/net/xmppclient_listener.lua @@ -100,7 +100,7 @@ local function session_close(session, reason) end end session.send(""); - session.conn.close(); + session.conn:close(); xmppclient.disconnect(session.conn, (reason and (reason.text or reason.condition)) or reason or "session closed"); end end @@ -117,7 +117,7 @@ function xmppclient.onincoming(conn, data) session.log("info", "Client connected"); -- Client is using legacy SSL (otherwise mod_tls sets this flag) - if conn.ssl() then + if conn:ssl() then session.secure = true; end -- cgit v1.2.3 From f24860c37c3821c02a5ed91e706c6d4d6fd21bbc Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:19:57 +0000 Subject: net.server_select: Change addserver() parameters to be compatible with new standard syntax --- net/server_select.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/server_select.lua b/net/server_select.lua index 6ab8ce91..eebaeb81 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -692,7 +692,7 @@ end ----------------------------------// PUBLIC //-- -addserver = function( listeners, port, addr, pattern, sslctx, maxconnections, startssl ) -- this function provides a way for other scripts to reg a server +addserver = function( addr, port, listeners, pattern, sslctx, startssl ) -- this function provides a way for other scripts to reg a server local err --out_put("server.lua: autossl on ", port, " is ", startssl) if type( listeners ) ~= "table" then @@ -715,7 +715,7 @@ addserver = function( listeners, port, addr, pattern, sslctx, maxconnections, st out_error( "server.lua, port ", port, ": ", err ) return nil, err end - local handler, err = wrapserver( listeners, server, addr, port, pattern, sslctx, maxconnections, startssl ) -- wrap new server socket + local handler, err = wrapserver( listeners, server, addr, port, pattern, sslctx, _maxclientsperserver, startssl ) -- wrap new server socket if not handler then server:close( ) return nil, err -- cgit v1.2.3 From 3637339b38f22bdb048e72f22241bd2baafe7e4c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:23:51 +0000 Subject: net.server_event: Change to new standard addserver() syntax --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 375ab47e..99c3c83b 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -639,9 +639,9 @@ do end local addserver = ( function( ) - return function( addr, port, listener, pattern, backlog, sslcfg, startssl ) -- TODO: check arguments + return function( addr, port, listener, pattern, sslcfg, startssl ) -- TODO: check arguments --vdebug( "creating new tcp server with following parameters:", addr or "nil", port or "nil", sslcfg or "nil", startssl or "nil") - local server, err = socket.bind( addr, port, backlog ) -- create server socket + local server, err = socket.bind( addr, port, cfg.ACCEPT_QUEUE ) -- create server socket if not server then debug( "creating server socket failed because:", err ) return nil, err -- cgit v1.2.3 From d1205508582c22acda941e3853dc8ef049341f92 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:14:32 +0000 Subject: net.server_event: Define vdebug function for convenience --- net/server_event.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 99c3c83b..453a1f28 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -52,6 +52,7 @@ local log = require ("util.logger").init("socket") local function debug(...) return log("debug", ("%s "):rep(select('#', ...)), ...) end +local vdebug = debug; local bitor = ( function( ) -- thx Rici Lake local hasbit = function( x, p ) -- cgit v1.2.3 From 920c067b7ffe42f6a18a0561191f9bbc523a8864 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:20:35 +0000 Subject: net.server_event: Define id property for connection objects, to aid logging --- net/server_event.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 453a1f28..60ff5b72 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -444,6 +444,7 @@ do _sslctx = sslctx; -- parameters _usingssl = false; -- client is using ssl; } + interface.id = tostring(interface):match("%x+$"); interface.writecallback = function( event ) -- called on write events --vdebug( "new client write event, id/ip/port:", interface, ip, port ) if interface.nowriting or ( interface.fatalerror and ( "client to close" ~= interface.fatalerror ) ) then -- leave this event @@ -592,6 +593,7 @@ do fatalerror = false; -- error message nointerface = true; -- lock/unlock parameter } + interface.id = tostring(interface):match("%x+$"); interface.readcallback = function( event ) -- server handler, called on incoming connections --vdebug( "server can accept, id/addr/port:", interface, addr, port ) if interface.fatalerror then -- cgit v1.2.3 From 08ea9601ce7890df559dd8a6fd2cacce432addda Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:42:45 +0000 Subject: net.server_event: Use connection id in all relevant logging --- net/server_event.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 60ff5b72..23a8ff86 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -140,14 +140,14 @@ do self.fatalerror = "connection timeout" self.listener.ontimeout( self ) -- call timeout listener self:_close() - debug( "new connection failed. id:", self, "error:", self.fatalerror ) + debug( "new connection failed. id:", self.id, "error:", self.fatalerror ) else if plainssl then -- start ssl session self:_start_ssl( self.listener.onconnect ) else -- normal connection self:_start_session( self.listener.onconnect ) end - debug( "new connection established. id:", self ) + debug( "new connection established. id:", self.id ) end self.eventconnect = nil return -1 @@ -159,7 +159,7 @@ do if self.type == "client" then local callback = function( ) self:_lock( false, false, false ) - --vdebug( "start listening on client socket with id:", self ) + --vdebug( "start listening on client socket with id:", self.id ) self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT ) -- register callback onconnect( self ) self.eventsession = nil @@ -168,13 +168,13 @@ do self.eventsession = addevent( base, nil, EV_TIMEOUT, callback, 0 ) else self:_lock( false ) - --vdebug( "start listening on server socket with id:", self ) + --vdebug( "start listening on server socket with id:", self.id ) self.eventread = addevent( base, self.conn, EV_READ, self.readcallback ) -- register callback end return true end function interface_mt:_start_ssl(arg) -- old socket will be destroyed, therefore we have to close read/write events first - --vdebug( "starting ssl session with client id:", self ) + --vdebug( "starting ssl session with client id:", self.id ) local _ _ = self.eventread and self.eventread:close( ) -- close events; this must be called outside of the event callbacks! _ = self.eventwrite and self.eventwrite:close( ) @@ -199,7 +199,7 @@ do local maxattempt = cfg.MAX_HANDSHAKE_ATTEMPS while attempt < 1000 do -- no endless loop attempt = attempt + 1 - debug( "ssl handshake of client with id:", self, "attemp:", attempt ) + debug( "ssl handshake of client with id:"..tostring(self).."attemp:"..attempt ) if attempt > maxattempt then self.fatalerror = "max handshake attemps exceeded" elseif EV_TIMEOUT == event then @@ -249,7 +249,7 @@ do return true end function interface_mt:_destroy() -- close this interface + events and call last listener - debug( "closing client with id:", self ) + debug( "closing client with id:", self.id ) self:_lock( true, true, true ) -- first of all, lock the interface to avoid further actions local _ _ = self.eventread and self.eventread:close( ) -- close events; this must be called outside of the event callbacks! @@ -289,7 +289,7 @@ do -- Public methods function interface_mt:write(data) - --vdebug( "try to send data to client, id/data:", self, data ) + vdebug( "try to send data to client, id/data:", self.id, data ) data = tostring( data ) local len = string_len( data ) local total = len + self.writebufferlen @@ -307,7 +307,7 @@ do return true end function interface_mt:close(now) - debug( "try to close client connection with id:", self ) + debug( "try to close client connection with id:", self.id ) if self.type == "client" then self.fatalerror = "client to close" if ( not self.eventwrite ) or now then -- try to close immediately @@ -320,7 +320,7 @@ do return nil, "writebuffer not empty, waiting" end else - debug( "try to close server with id:", self, "args:", now ) + debug( "try to close server with id:", self.id, "args:", now ) self.fatalerror = "server to close" self:_lock( true ) local count = 0 @@ -372,7 +372,7 @@ do function interface_mt:starttls() - debug( "try to start ssl at client id:", self ) + debug( "try to start ssl at client id:", self.id ) local err if not self.sslctx then -- no ssl available err = "no ssl context available" -- cgit v1.2.3 From 7ae00da04478cee9fe34bde8cc016292ce2a503d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:44:15 +0000 Subject: net.server_event: Call connection's onconnect, no need to pass closures around now --- net/server_event.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 23a8ff86..879d9801 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -161,7 +161,7 @@ do self:_lock( false, false, false ) --vdebug( "start listening on client socket with id:", self.id ) self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT ) -- register callback - onconnect( self ) + self:onconnect() self.eventsession = nil return -1 end -- cgit v1.2.3 From 14cd16dda7f1b5fb53f5a88edf4bb815a47b0fde Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:48:04 +0000 Subject: net.server_event: Correct a few instances of .sslctx to ._sslctx --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 879d9801..88292e35 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -180,7 +180,7 @@ do _ = self.eventwrite and self.eventwrite:close( ) self.eventread, self.eventwrite = nil, nil local err - self.conn, err = ssl.wrap( self.conn, self.sslctx ) + self.conn, err = ssl.wrap( self.conn, self._sslctx ) if err then self.fatalerror = err self.conn = nil -- cannot be used anymore @@ -374,7 +374,7 @@ do function interface_mt:starttls() debug( "try to start ssl at client id:", self.id ) local err - if not self.sslctx then -- no ssl available + if not self._sslctx then -- no ssl available err = "no ssl context available" elseif self.usingssl then -- startssl was already called err = "ssl already active" -- cgit v1.2.3 From 9cb25f6151d046c5eba18c2c6af207a13906e3da Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:49:47 +0000 Subject: net.server_event: Small whitespace fixes --- net/server_event.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 88292e35..c35c6f53 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -221,7 +221,7 @@ do self.eventhandshake = nil return -1 end - debug( "error during ssl handshake:", err ) + debug( "error during ssl handshake:", err ) if err == "wantwrite" then event = EV_WRITE elseif err == "wantread" then @@ -275,6 +275,7 @@ do interfacelist( "delete", self ) return true end + function interface_mt:_lock(nointerface, noreading, nowriting) -- lock or unlock this interface or events self.nointerface, self.noreading, self.nowriting = nointerface, noreading, nowriting return nointerface, noreading, nowriting -- cgit v1.2.3 From d506a991daf0c2cd0dac67fd95fa3f6297eeeb0d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:51:31 +0000 Subject: net.server_event: Use correct listener callbacks --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index c35c6f53..456db8b8 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -212,9 +212,9 @@ do self.receive = self.conn.receive local onsomething if "onconnect" == arg then -- trigger listener - onsomething = self.listener.onconnect + onsomething = self.onconnect else - onsomething = self.listener.onsslconnection + onsomething = self.onsslconnection end self:_start_session( onsomething ) debug( "ssl handshake done" ) -- cgit v1.2.3 From 9ad7e92bf95639ba57df4811b44ba714c6f2402f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:53:07 +0000 Subject: net.server_event: conn.usingssl -> conn._usingssl --- net/server_event.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 456db8b8..1a77953d 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -355,7 +355,7 @@ do end function interface_mt:ssl() - return self.usingssl + return self._usingssl end function interface_mt:type() @@ -377,14 +377,14 @@ do local err if not self._sslctx then -- no ssl available err = "no ssl context available" - elseif self.usingssl then -- startssl was already called + elseif self._usingssl then -- startssl was already called err = "ssl already active" end if err then debug( "error:", err ) return nil, err end - self.usingssl = true + self._usingssl = true self.startsslcallback = function( ) -- we have to start the handshake outside of a read/write event self:_start_ssl(); self.eventstarthandshake = nil @@ -460,7 +460,7 @@ do interface.eventwrite = false return -1 else -- can write :) - if interface.usingssl then -- handle luasec + if interface._usingssl then -- handle luasec if interface.eventreadtimeout then -- we have to read first local ret = interface.readcallback( ) -- call readcallback --vdebug( "tried to read in writecallback, result:", ret ) -- cgit v1.2.3 From e6249e1a9e5f4e1cf2cc36d46402d4a529982cd0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:53:44 +0000 Subject: net.server_event: Add set_sslctx() method to connections --- net/server_event.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 1a77953d..41c5a02b 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -370,7 +370,9 @@ do return self.addr end - + function interface_mt:set_sslctx(sslctx) + self._sslctx = sslctx; + end function interface_mt:starttls() debug( "try to start ssl at client id:", self.id ) -- cgit v1.2.3 From e2d6a0c9c733449be39f5d728b4122eed428d766 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:55:48 +0000 Subject: net.server_event: Set startsslcallback property to nil when we're done handshaking, otherwise we keep trying to repeat the handshake --- net/server_event.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 41c5a02b..50538e05 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -388,6 +388,7 @@ do end self._usingssl = true self.startsslcallback = function( ) -- we have to start the handshake outside of a read/write event + self.startsslcallback = nil self:_start_ssl(); self.eventstarthandshake = nil return -1 -- cgit v1.2.3 From 4d67d89061c92629e6e942ab2851482cc077ad7d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:58:37 +0000 Subject: net.server_event: Use conn property for send/receive instead of caching, fixes trying to use original socket after SSL-wrapping --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 50538e05..75c146eb 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -473,7 +473,7 @@ do interface.eventwritetimeout = false end end - local succ, err, byte = interface.send( interface.conn, interface.writebuffer, 1, interface.writebufferlen ) + local succ, err, byte = interface.conn:send( interface.writebuffer, 1, interface.writebufferlen ) --vdebug( "write data:", interface.writebuffer, "error:", err, "part:", byte ) if succ then -- writing succesful interface.writebuffer = "" @@ -539,8 +539,8 @@ do interface.eventreadtimeout = nil end end - local buffer, err, part = receive( client, pattern ) -- receive buffer with "pattern" --vdebug( "read data:", buffer, "error:", err, "part:", part ) + local buffer, err, part = interface.conn:receive( pattern ) -- receive buffer with "pattern" buffer = buffer or part or "" local len = string_len( buffer ) if len > cfg.MAX_READ_LENGTH then -- check buffer length -- cgit v1.2.3 From 3fe8e5575403a0e9a06923dfcf62773c40cd1ec3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:04:04 +0000 Subject: net.server_event: Obey nowriting/nointerface for locks in interface methods --- net/server_event.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 75c146eb..e4653cb1 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -290,6 +290,7 @@ do -- Public methods function interface_mt:write(data) + if self.nowriting then return nil, "locked" end vdebug( "try to send data to client, id/data:", self.id, data ) data = tostring( data ) local len = string_len( data ) @@ -308,6 +309,7 @@ do return true end function interface_mt:close(now) + if self.nointerface then return nil, "locked"; end debug( "try to close client connection with id:", self.id ) if self.type == "client" then self.fatalerror = "client to close" -- cgit v1.2.3 From 07f5e5f4c81bfaaa9389bcb15302349de388c966 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:06:00 +0000 Subject: net.server_event: Fix moved line caused by Mercurial's crecord and my selective commits --- net/server_event.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index e4653cb1..5bff633d 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -541,8 +541,8 @@ do interface.eventreadtimeout = nil end end - --vdebug( "read data:", buffer, "error:", err, "part:", part ) local buffer, err, part = interface.conn:receive( pattern ) -- receive buffer with "pattern" + --vdebug( "read data:", tostring(buffer), "error:", tostring(err), "part:", tostring(part) ) buffer = buffer or part or "" local len = string_len( buffer ) if len > cfg.MAX_READ_LENGTH then -- check buffer length -- cgit v1.2.3 From 26277a79500975351326541d321a8d4ad73e9e48 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:06:40 +0000 Subject: net.server_event: Remove premature non-optimisation --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 5bff633d..5df764b4 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -516,7 +516,7 @@ do end end end - local usingssl, receive = interface._usingssl, interface.receive; + interface.readcallback = function( event ) -- called on read events --vdebug( "new client read event, id/ip/port:", interface, ip, port ) if interface.noreading or interface.fatalerror then -- leave this event @@ -531,7 +531,7 @@ do interface.eventread = nil return -1 else -- can read - if usingssl then -- handle luasec + if interface._usingssl then -- handle luasec if interface.eventwritetimeout then -- ok, in the past writecallback was regged local ret = interface.writecallback( ) -- call it --vdebug( "tried to write in readcallback, result:", ret ) -- cgit v1.2.3 From 0f840b5fe7bef3b3ebc9529ebb90c55f802eb43b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:08:11 +0000 Subject: net.server_event: Handle and ignore 'wantread' error from receive(), treat as a timeout --- net/server_event.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 5df764b4..82ad899f 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -552,7 +552,7 @@ do interface.eventread = nil return -1 end - if err and ( "timeout" ~= err ) then + if err and ( err ~= "timeout" and err ~= "wantread" ) then if "wantwrite" == err then -- need to read on write event if not interface.eventwrite then -- register new write event if needed interface.eventwrite = addevent( base, interface.conn, EV_WRITE, interface.writecallback, cfg.WRITE_TIMEOUT ) -- cgit v1.2.3 From 2b683fc8e66bd737c56635e516c65e502d879d13 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:09:57 +0000 Subject: net.server_event: Comment accidentally uncommented vdebug() --- net/server_event.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 82ad899f..b467a84d 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -291,7 +291,7 @@ do -- Public methods function interface_mt:write(data) if self.nowriting then return nil, "locked" end - vdebug( "try to send data to client, id/data:", self.id, data ) + --vdebug( "try to send data to client, id/data:", self.id, data ) data = tostring( data ) local len = string_len( data ) local total = len + self.writebufferlen -- cgit v1.2.3 From 8f2a8c9c0935ad80f10308838667295cbedcf080 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:42:47 +0000 Subject: net.adns: Update for new net.server API (doesn't work with libevent yet) --- net/adns.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/adns.lua b/net/adns.lua index b0c9a625..c9cb9476 100644 --- a/net/adns.lua +++ b/net/adns.lua @@ -45,10 +45,10 @@ end function new_async_socket(sock, resolver) local newconn, peername = {}, ""; local listener = {}; - function listener.incoming(conn, data) + function listener.onincoming(conn, data) dns.feed(sock, data); end - function listener.disconnect(conn, err) + function listener.ondisconnect(conn, err) log("warn", "DNS socket for %s disconnected: %s", peername, err); local servers = resolver.server; if resolver.socketset[newconn.handler] == resolver.best_server and resolver.best_server == #servers then @@ -68,7 +68,7 @@ function new_async_socket(sock, resolver) newconn.handler.setsockname = function (_, ...) return sock:setsockname(...); end newconn.handler.setpeername = function (_, ...) peername = (...); local ret = sock:setpeername(...); _.setsend(sock.send); return ret; end newconn.handler.connect = function (_, ...) return sock:connect(...) end - newconn.handler.send = function (_, data) _.write(data); return _.sendbuffer(); end + newconn.handler.send = function (_, data) _.write(_, data); return _.sendbuffer(); end return newconn.handler; end -- cgit v1.2.3 From 31dd61128403f0af9e2482856ec95d18c85f79d5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:43:46 +0000 Subject: *_listener: Update for new net.server API, specifically .listener -> .onincoming, .disconnect -> .ondisconnect --- net/httpclient_listener.lua | 4 ++-- net/httpserver_listener.lua | 4 ++-- net/xmppclient_listener.lua | 4 ++-- net/xmppcomponent_listener.lua | 6 +++--- net/xmppserver_listener.lua | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/httpclient_listener.lua b/net/httpclient_listener.lua index 69b7946b..6517de44 100644 --- a/net/httpclient_listener.lua +++ b/net/httpclient_listener.lua @@ -15,7 +15,7 @@ local buffers = {}; -- Buffers of partial lines local httpclient = { default_port = 80, default_mode = "*a" }; -function httpclient.listener(conn, data) +function httpclient.onincoming(conn, data) local request = requests[conn]; if not request then @@ -28,7 +28,7 @@ function httpclient.listener(conn, data) end end -function httpclient.disconnect(conn, err) +function httpclient.ondisconnect(conn, err) local request = requests[conn]; if request then request:reader(nil); diff --git a/net/httpserver_listener.lua b/net/httpserver_listener.lua index 455191fb..5a261a43 100644 --- a/net/httpserver_listener.lua +++ b/net/httpserver_listener.lua @@ -16,7 +16,7 @@ local requests = {}; -- Open requests local httpserver = { default_port = 80, default_mode = "*a" }; -function httpserver.listener(conn, data) +function httpserver.onincoming(conn, data) local request = requests[conn]; if not request then @@ -34,7 +34,7 @@ function httpserver.listener(conn, data) end end -function httpserver.disconnect(conn, err) +function httpserver.ondisconnect(conn, err) local request = requests[conn]; if request and not request.destroyed then request.conn = nil; diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua index 34438f3d..fc0dce2c 100644 --- a/net/xmppclient_listener.lua +++ b/net/xmppclient_listener.lua @@ -101,7 +101,7 @@ local function session_close(session, reason) end session.send(""); session.conn:close(); - xmppclient.disconnect(session.conn, (reason and (reason.text or reason.condition)) or reason or "session closed"); + xmppclient.ondisconnect(session.conn, (reason and (reason.text or reason.condition)) or reason or "session closed"); end end @@ -133,7 +133,7 @@ function xmppclient.onincoming(conn, data) end end -function xmppclient.disconnect(conn, err) +function xmppclient.ondisconnect(conn, err) local session = sessions[conn]; if session then (session.log or log)("info", "Client disconnected: %s", err); diff --git a/net/xmppcomponent_listener.lua b/net/xmppcomponent_listener.lua index c16f41a0..b353b1b3 100644 --- a/net/xmppcomponent_listener.lua +++ b/net/xmppcomponent_listener.lua @@ -118,12 +118,12 @@ local function session_close(session, reason) end session.send(""); session.conn.close(); - component_listener.disconnect(session.conn, "stream error"); + component_listener.ondisconnect(session.conn, "stream error"); end end --- Component connlistener -function component_listener.listener(conn, data) +function component_listener.onincoming(conn, data) local session = sessions[conn]; if not session then local _send = conn.write; @@ -157,7 +157,7 @@ function component_listener.listener(conn, data) end end -function component_listener.disconnect(conn, err) +function component_listener.ondisconnect(conn, err) local session = sessions[conn]; if session then (session.log or log)("info", "component disconnected: %s (%s)", tostring(session.host), tostring(err)); diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua index c7e02ec5..797ef183 100644 --- a/net/xmppserver_listener.lua +++ b/net/xmppserver_listener.lua @@ -104,14 +104,14 @@ local function session_close(session, reason) session.conn.close(true); -- Force FIXME: timer? end session.conn.close(); - xmppserver.disconnect(session.conn, "stream error"); + xmppserver.ondisconnect(session.conn, "stream error"); end end -- End of session methods -- -function xmppserver.listener(conn, data) +function xmppserver.onincoming(conn, data) local session = sessions[conn]; if not session then session = s2s_new_incoming(conn); @@ -148,7 +148,7 @@ function xmppserver.status(conn, status) end end -function xmppserver.disconnect(conn, err) +function xmppserver.ondisconnect(conn, err) local session = sessions[conn]; if session then if err and err ~= "closed" and session.srv_hosts then -- cgit v1.2.3 From 54d5d4f69dd41f033163828c24424a4e4ff8d25b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:44:26 +0000 Subject: net.httpserver, net.http: Update for new net.server API (untested) --- net/http.lua | 4 ++-- net/httpserver.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/http.lua b/net/http.lua index 9d2f9b96..34ec11b4 100644 --- a/net/http.lua +++ b/net/http.lua @@ -152,7 +152,7 @@ function request(u, ex, callback) end req.handler, req.conn = server.wrapclient(socket.tcp(), req.host, req.port or 80, listener, "*a"); - req.write = req.handler.write; + req.write = function (...) return req.handler:write(...); end req.conn:settimeout(0); local ok, err = req.conn:connect(req.host, req.port or 80); if not ok and err ~= "timeout" then @@ -200,7 +200,7 @@ end function destroy_request(request) if request.conn then request.handler.close() - listener.disconnect(request.conn, "closed"); + listener.ondisconnect(request.conn, "closed"); end end diff --git a/net/httpserver.lua b/net/httpserver.lua index ddb4475c..51dca166 100644 --- a/net/httpserver.lua +++ b/net/httpserver.lua @@ -209,7 +209,7 @@ end function new_request(handler) return { handler = handler, conn = handler.socket, - write = handler.write, state = "request", + write = function (...) return handler:write(...); end, state = "request", server = http_servers[handler.serverport()], send = send_response, destroy = destroy_request, @@ -230,7 +230,7 @@ function destroy_request(request) end request.handler.close() if request.conn then - listener.disconnect(request.handler, "closed"); + listener.ondisconnect(request.handler, "closed"); end end end -- cgit v1.2.3 From 24dba04dcbd28501a568528f287a577036fe863e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:45:13 +0000 Subject: net.server_select: Bring up to date to new common connection API --- net/server_select.lua | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'net') diff --git a/net/server_select.lua b/net/server_select.lua index eebaeb81..d7970296 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -166,7 +166,7 @@ wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx, maxco local connections = 0 - local dispatch, disconnect = listeners.incoming or listeners.listener, listeners.disconnect + local dispatch, disconnect = listeners.onincoming, listeners.ondisconnect local err @@ -241,7 +241,7 @@ wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx, maxco for _, handler in pairs( _socketlist ) do if handler.serverport == serverport then handler.disconnect( handler, "server closed" ) - handler.close( true ) + handler:close( true ) end end socket:close( ) @@ -300,9 +300,9 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport local ssl - local dispatch = listeners.incoming or listeners.listener + local dispatch = listeners.onincoming local status = listeners.status - local disconnect = listeners.disconnect + local disconnect = listeners.ondisconnect local bufferqueue = { } -- buffer array local bufferqueuelen = 0 -- end of buffer array @@ -331,9 +331,9 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.disconnect = function( ) return disconnect end - handler.setlistener = function( listeners ) - dispatch = listeners.incoming - disconnect = listeners.disconnect + handler.setlistener = function( self, listeners ) + dispatch = listeners.onincoming + disconnect = listeners.ondisconnect end handler.getstats = function( ) return readtraffic, sendtraffic @@ -400,7 +400,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.clientport = function( ) return clientport end - local write = function( data ) + local write = function( self, data ) bufferlen = bufferlen + string_len( data ) if bufferlen > maxsendlen then _closelist[ handler ] = "send buffer exceeded" -- cannot close the client at the moment, have to wait to the end of the cycle @@ -417,26 +417,26 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport return true end handler.write = write - handler.bufferqueue = function( ) + handler.bufferqueue = function( self ) return bufferqueue end - handler.socket = function( ) + handler.socket = function( self ) return socket end - handler.pattern = function( new ) + handler.pattern = function( self, new ) pattern = new or pattern return pattern end - handler.setsend = function ( newsend ) + handler.setsend = function ( self, newsend ) send = newsend or send return send end - handler.bufferlen = function( readlen, sendlen ) + handler.bufferlen = function( self, readlen, sendlen ) maxsendlen = sendlen or maxsendlen maxreadlen = readlen or maxreadlen return maxreadlen, maxsendlen end - handler.lock = function( switch ) + handler.lock = function( self, switch ) if switch == true then handler.write = idfalse local tmp = _sendlistlen @@ -507,7 +507,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport bufferqueuelen = 0 bufferlen = 0 _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) -- delete socket from writelist - _ = needtls and handler.starttls(true) + _ = needtls and handler:starttls(true) _writetimes[ handler ] = nil _ = toclose and handler.close( ) return true @@ -529,7 +529,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport -- Set the sslctx local handshake; - function handler.set_sslctx(new_sslctx) + function handler.set_sslctx(self, new_sslctx) ssl = true sslctx = new_sslctx; local wrote @@ -564,13 +564,13 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport end end disconnect( handler, "ssl handshake failed" ) - _ = handler and handler.close( true ) -- forced disconnect + _ = handler and handler:close( true ) -- forced disconnect return false -- handshake failed end ) end if sslctx then -- ssl? - handler.set_sslctx(sslctx); + handler:set_sslctx(sslctx); if startssl then -- ssl now? --out_put("server.lua: ", "starting ssl handshake") local err @@ -590,7 +590,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport else -- We're not automatically doing SSL, so we're not secure (yet) ssl = false - handler.starttls = function( now ) + handler.starttls = function( self, now ) if not now then --out_put "server.lua: we need to do tls, but delaying until later" needtls = true @@ -737,14 +737,14 @@ removeserver = function( port ) if not handler then return nil, "no server found on port '" .. tostring( port ) .. "'" end - handler.close( ) + handler:close( ) _server[ port ] = nil return true end closeall = function( ) for _, handler in pairs( _socketlist ) do - handler.close( ) + handler:close( ) _socketlist[ _ ] = nil end _readlistlen = 0 @@ -822,7 +822,7 @@ loop = function( ) -- this is the main loop of the program end for handler, err in pairs( _closelist ) do handler.disconnect( )( handler, err ) - handler.close( true ) -- forced disconnect + handler:close( true ) -- forced disconnect end clean( _closelist ) _currenttime = os_time( ) @@ -880,14 +880,14 @@ addtimer( function( ) if os_difftime( _currenttime - timestamp ) > _sendtimeout then --_writetimes[ handler ] = nil handler.disconnect( )( handler, "send timeout" ) - handler.close( true ) -- forced disconnect + handler:close( true ) -- forced disconnect end end for handler, timestamp in pairs( _readtimes ) do if os_difftime( _currenttime - timestamp ) > _readtimeout then --_readtimes[ handler ] = nil handler.disconnect( )( handler, "read timeout" ) - handler.close( ) -- forced disconnect? + handler:close( ) -- forced disconnect? end end end -- cgit v1.2.3 From 92e03c5a04498c2a0e5df7314f1f10a38331ef68 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 13:47:24 +0000 Subject: xmppcomponent_listener: Use new API for writing to components, fixes traceback (thanks Tobias) --- net/xmppcomponent_listener.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/xmppcomponent_listener.lua b/net/xmppcomponent_listener.lua index b353b1b3..790ab0b7 100644 --- a/net/xmppcomponent_listener.lua +++ b/net/xmppcomponent_listener.lua @@ -127,7 +127,7 @@ function component_listener.onincoming(conn, data) local session = sessions[conn]; if not session then local _send = conn.write; - session = { type = "component", conn = conn, send = function (data) return _send(tostring(data)); end }; + session = { type = "component", conn = conn, send = function (data) return _send(conn, tostring(data)); end }; sessions[conn] = session; -- Logging functions -- -- cgit v1.2.3 From 61285cd82a9689e0073fabf462795aa29b58647e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 14:42:56 +0000 Subject: net.server: Add some comments to explain to waqas how it all works :) --- net/server.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/server.lua b/net/server.lua index 15bba603..a2325735 100644 --- a/net/server.lua +++ b/net/server.lua @@ -5,6 +5,9 @@ local server; if have_luaevent and use_luaevent == true then server = require "net.server_event"; + -- util.timer requires "net.server", so instead of having + -- Lua look for, and load us again (causing a loop) - set this here + -- (usually it isn't set until we return, look down there...) package.loaded["net.server"] = server; -- Backwards compatibility for timers, addtimer @@ -18,4 +21,6 @@ else package.loaded["net.server"] = server; end -return server; +-- require "net.server" shall now forever return this, +-- ie. server_select or server_event as chosen above. +return server; -- cgit v1.2.3 From 3d08f189a3c6b83515273e99d7dca72693b8971a Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Tue, 24 Nov 2009 14:37:14 +0500 Subject: net.server: Log an error when libevent is requested, but luaevent is unavailable, and don't load luaevent when not requested. --- net/server.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/server.lua b/net/server.lua index a2325735..f77b216d 100644 --- a/net/server.lua +++ b/net/server.lua @@ -1,9 +1,16 @@ -local have_luaevent = pcall(require, "luaevent.core"); + local use_luaevent = require "core.configmanager".get("*", "core", "use_libevent"); +if use_luaevent then + use_luaevent = pcall(require, "luaevent.core"); + if not use_luaevent then + log("error", "libevent not found, falling back to select()"); + end +end + local server; -if have_luaevent and use_luaevent == true then +if use_luaevent then server = require "net.server_event"; -- util.timer requires "net.server", so instead of having -- Lua look for, and load us again (causing a loop) - set this here -- cgit v1.2.3 From 4e56a3c519bc0d46a3491ca18cb58c3e8dbb5ae9 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Tue, 24 Nov 2009 22:42:08 +0500 Subject: xmppclient_listener: Escape control characters when logging invalid XML. --- net/xmppclient_listener.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua index 417dfd4a..01d73a36 100644 --- a/net/xmppclient_listener.lua +++ b/net/xmppclient_listener.lua @@ -61,7 +61,7 @@ local function session_reset_stream(session) function session.data(conn, data) local ok, err = parser:parse(data); if ok then return; end - log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " ")); + log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); session:close("xml-not-well-formed"); end -- cgit v1.2.3 From f5afdcb5642fb5260717613cc7909e20805a957c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Nov 2009 20:34:22 +0000 Subject: core.sessionmanager, net.*_listener: Remove the evil collectgarbage() calls --- net/xmppclient_listener.lua | 1 - net/xmppcomponent_listener.lua | 1 - net/xmppserver_listener.lua | 1 - 3 files changed, 3 deletions(-) (limited to 'net') diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua index fc0dce2c..4fd51db6 100644 --- a/net/xmppclient_listener.lua +++ b/net/xmppclient_listener.lua @@ -140,7 +140,6 @@ function xmppclient.ondisconnect(conn, err) sm_destroy_session(session, err); sessions[conn] = nil; session = nil; - collectgarbage("collect"); end end diff --git a/net/xmppcomponent_listener.lua b/net/xmppcomponent_listener.lua index 790ab0b7..4920548d 100644 --- a/net/xmppcomponent_listener.lua +++ b/net/xmppcomponent_listener.lua @@ -169,7 +169,6 @@ function component_listener.ondisconnect(conn, err) sessions[conn] = nil; for k in pairs(session) do session[k] = nil; end session = nil; - collectgarbage("collect"); end end diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua index 797ef183..3c038b83 100644 --- a/net/xmppserver_listener.lua +++ b/net/xmppserver_listener.lua @@ -162,7 +162,6 @@ function xmppserver.ondisconnect(conn, err) s2s_destroy_session(session); sessions[conn] = nil; session = nil; - collectgarbage("collect"); end end -- cgit v1.2.3 From 4a3a52f7a1e1b36427a6543dab5fe70499021dd9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:08:10 +0000 Subject: net.adns: Some cleanup, happens to also make it compatible with libevent --- net/adns.lua | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/adns.lua b/net/adns.lua index c9cb9476..d6beffbb 100644 --- a/net/adns.lua +++ b/net/adns.lua @@ -43,33 +43,32 @@ function cancel(handle, call_handler) end function new_async_socket(sock, resolver) - local newconn, peername = {}, ""; + local peername = ""; local listener = {}; + local handler = {}; function listener.onincoming(conn, data) - dns.feed(sock, data); + dns.feed(handler, data); end function listener.ondisconnect(conn, err) log("warn", "DNS socket for %s disconnected: %s", peername, err); local servers = resolver.server; - if resolver.socketset[newconn.handler] == resolver.best_server and resolver.best_server == #servers then + if resolver.socketset[conn] == resolver.best_server and resolver.best_server == #servers then log("error", "Exhausted all %d configured DNS servers, next lookup will try %s again", #servers, servers[1]); end resolver:servfail(conn); -- Let the magic commence end - newconn.handler, newconn._socket = server.wrapclient(sock, "dns", 53, listener); - if not newconn.handler then + handler = server.wrapclient(sock, "dns", 53, listener); + if not handler then log("warn", "handler is nil"); end - if not newconn._socket then - log("warn", "socket is nil"); - end - newconn.handler.settimeout = function () end - newconn.handler.setsockname = function (_, ...) return sock:setsockname(...); end - newconn.handler.setpeername = function (_, ...) peername = (...); local ret = sock:setpeername(...); _.setsend(sock.send); return ret; end - newconn.handler.connect = function (_, ...) return sock:connect(...) end - newconn.handler.send = function (_, data) _.write(_, data); return _.sendbuffer(); end - return newconn.handler; + + handler.settimeout = function () end + handler.setsockname = function (_, ...) return sock:setsockname(...); end + handler.setpeername = function (_, ...) peername = (...); local ret = sock:setpeername(...); _:set_send(sock.send); return ret; end + handler.connect = function (_, ...) return sock:connect(...) end + handler.send = function (_, data) _:write(data); return _.sendbuffer and _.sendbuffer(); end + return handler; end dns:socket_wrapper_set(new_async_socket); -- cgit v1.2.3 From 9d32568d5d90efa317e4f77a94a678a653547cf0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:08:47 +0000 Subject: net.server_select: Rename server.setsend() to server.set_send() for consistency --- net/server_select.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/server_select.lua b/net/server_select.lua index d7970296..3b3f3768 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -427,7 +427,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport pattern = new or pattern return pattern end - handler.setsend = function ( self, newsend ) + handler.set_send = function ( self, newsend ) send = newsend or send return send end -- cgit v1.2.3 From df4d4cd44521d393dbe0a3ee8a62fe0add80f2ba Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:15:40 +0000 Subject: net.server_event: Fix to make ontimeout() listener callback work --- net/server_event.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index b467a84d..74dffb94 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -138,7 +138,7 @@ do local callback = function( event ) if EV_TIMEOUT == event then -- timout during connection self.fatalerror = "connection timeout" - self.listener.ontimeout( self ) -- call timeout listener + self:ontimeout() -- call timeout listener self:_close() debug( "new connection failed. id:", self.id, "error:", self.fatalerror ) else @@ -432,6 +432,7 @@ do onconnect = listener.onconnect; -- will be called when client disconnects ondisconnect = listener.ondisconnect; -- will be called when client disconnects onincoming = listener.onincoming; -- will be called when client sends data + ontimeout = listener.ontimeout; -- called when fatal socket timeout occurs eventread = false, eventwrite = false, eventclose = false, eventhandshake = false, eventstarthandshake = false; -- event handler eventconnect = false, eventsession = false; -- more event handler... -- cgit v1.2.3 From 05348c61f37e80e99544ab9f655833a06c88612c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:18:44 +0000 Subject: net.server_handler: Add stub handlers as fallbacks for those listeners don't implement --- net/server_event.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 74dffb94..f5eafaf6 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -405,7 +405,14 @@ do return true end - function interface_mt.onconnect() + -- Stub handlers + function interface_mt:onconnect() + end + function interface_mt:onincoming() + end + function interface_mt:ondisconnect() + end + function interface_mt:ontimeout() end end -- cgit v1.2.3 From e6d079f88724611385a25a1c2554b13bde9bc44a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:19:49 +0000 Subject: net.server_event: Count the number of client connections *upwards* :) --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index f5eafaf6..8187a7d0 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -263,7 +263,7 @@ do _ = self.eventreadtimeout and self.eventreadtimeout:close( ) _ = self.ondisconnect and self:ondisconnect( self.fatalerror ) -- call ondisconnect listener (wont be the case if handshake failed on connect) _ = self.conn and self.conn:close( ) -- close connection, must also be called outside of any socket registered events! - self._server:counter(-1); + _ = self._server and self._server:counter(-1); self.eventread, self.eventwrite = nil, nil self.eventstarthandshake, self.eventhandshake, self.eventclose = nil, nil, nil self.readcallback, self.writecallback = nil, nil @@ -283,7 +283,7 @@ do function interface_mt:counter(c) if c then - self._connections = self._connections - c + self._connections = self._connections + c end return self._connections end -- cgit v1.2.3 From 944ab8a84e38a7d10257fab36578e37cb571d8cf Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:21:12 +0000 Subject: net.server_event: Hide starttls function when the connection is not SSL-enabled --- net/server_event.lua | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 8187a7d0..79b2f388 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -374,6 +374,12 @@ do function interface_mt:set_sslctx(sslctx) self._sslctx = sslctx; + if sslctx then + self.starttls = nil; -- use starttls() of interface_mt + else + self.starttls = false; -- prevent starttls() + end + end end function interface_mt:starttls() @@ -458,6 +464,9 @@ do _sslctx = sslctx; -- parameters _usingssl = false; -- client is using ssl; } + if not sslctx then + interface.starttls = false -- don't allow TLS + end interface.id = tostring(interface):match("%x+$"); interface.writecallback = function( event ) -- called on write events --vdebug( "new client write event, id/ip/port:", interface, ip, port ) -- cgit v1.2.3 From 7dae521ba5a3fdc39c0bba3e1d5ade1c0812be1e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:22:03 +0000 Subject: net.server_event: Add set_send() for compatibility with server_select --- net/server_event.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 79b2f388..be006cab 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -380,6 +380,9 @@ do self.starttls = false; -- prevent starttls() end end + + function interface_mt:set_send(new_send) + -- No-op, we always use the underlying connection's send end function interface_mt:starttls() -- cgit v1.2.3 From 422a717900a10b8cd991b339bd0a540840695866 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:23:51 +0000 Subject: net.server_event: tostring() some debug logging parameters --- net/server_event.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index be006cab..5e2424ea 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -538,9 +538,9 @@ do end interface.readcallback = function( event ) -- called on read events - --vdebug( "new client read event, id/ip/port:", interface, ip, port ) + --vdebug( "new client read event, id/ip/port:", tostring(interface.id), tostring(ip), tostring(port) ) if interface.noreading or interface.fatalerror then -- leave this event - --vdebug( "leaving this event because:", interface.noreading or interface.fatalerror ) + --vdebug( "leaving this event because:", tostring(interface.noreading or interface.fatalerror) ) interface.eventread = nil return -1 end @@ -554,7 +554,7 @@ do if interface._usingssl then -- handle luasec if interface.eventwritetimeout then -- ok, in the past writecallback was regged local ret = interface.writecallback( ) -- call it - --vdebug( "tried to write in readcallback, result:", ret ) + --vdebug( "tried to write in readcallback, result:", tostring(ret) ) end if interface.eventreadtimeout then interface.eventreadtimeout:close( ) -- cgit v1.2.3 From 546954e85cee23594c84866db3c56842886ad541 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:25:13 +0000 Subject: net.server_event: Add addclient/wrapclient compatible with server_select, DNS and s2s should now work with libevent \o/ --- net/server_event.lua | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) (limited to 'net') diff --git a/net/server_event.lua b/net/server_event.lua index 5e2424ea..450bd341 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -693,26 +693,16 @@ local addserver = ( function( ) end end )( ) -local wrapclient = ( function( ) - return function( client, addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) - debug( "try to connect to:", addr, serverport, "with parameters:", pattern, localaddr, localport, sslcfg, startssl ) - local sslctx - if sslcfg then -- handle ssl/new context - if not ssl then - debug "need luasec, but not available" - return nil, "luasec not found" - end - sslctx, err = ssl.newcontext( sslcfg ) - if err then - debug( "cannot create new ssl context:", err ) - return nil, err - end - end +local addclient, wrapclient +do + function wrapclient( client, ip, port, listeners, pattern, sslctx, startssl ) + local interface = handleclient( client, ip, port, nil, pattern, listeners, sslctx ) + interface:_start_session() + return interface + --function handleclient( client, ip, port, server, pattern, listener, _, sslctx ) -- creates an client interface end -end )( ) - -local addclient = ( function( ) - return function( addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) + + function addclient( addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) local client, err = socket.tcp() -- creating new socket if not client then debug( "cannot create socket:", err ) @@ -726,23 +716,35 @@ local addclient = ( function( ) return nil, err end end + local sslctx + if sslcfg then -- handle ssl/new context + if not ssl then + debug "need luasec, but not available" + return nil, "luasec not found" + end + sslctx, err = ssl.newcontext( sslcfg ) + if err then + debug( "cannot create new ssl context:", err ) + return nil, err + end + end local res, err = client:connect( addr, serverport ) -- connect if res or ( err == "timeout" ) then local ip, port = client:getsockname( ) local server = function( ) return nil, "this is a dummy server interface" end - local interface = handleclient( client, ip, port, server, pattern, listener, sslctx ) + local interface = wrapclient( client, ip, serverport, listeners, pattern, sslctx, startssl ) interface:_start_connection( startssl ) - debug( "new connection id:", interface ) + debug( "new connection id:", interface.id ) return interface, err else debug( "new connection failed:", err ) return nil, err end - return wrapclient( client, addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) end -end )( ) +end + local loop = function( ) -- starts the event loop return base:loop( ) -- cgit v1.2.3 From fc078849d5ae5736fb9b1ec1e49ad1892cda6f4b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Nov 2009 14:09:53 +0000 Subject: net.server_select: Update conn.close() to use new connection method convention --- net/server_select.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/server_select.lua b/net/server_select.lua index 3b3f3768..ab2efcb0 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -353,7 +353,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.shutdown = function( pattern ) return shutdown( socket, pattern ) end - handler.close = function( forced ) + handler.close = function( self, forced ) if not handler then return true; end _readlistlen = removesocket( _readlist, socket, _readlistlen ) _readtimes[ handler ] = nil -- cgit v1.2.3 From 4267a24ab79792f5e8ea993f84a6902beaabe94a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Nov 2009 17:40:37 +0000 Subject: xmppserver_listener: Update for new server API, fixes traceback when closing s2s connections --- net/xmppserver_listener.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua index 3c038b83..53a9d354 100644 --- a/net/xmppserver_listener.lua +++ b/net/xmppserver_listener.lua @@ -100,10 +100,10 @@ local function session_close(session, reason) end end session.sends2s(""); - if session.notopen or not session.conn.close() then - session.conn.close(true); -- Force FIXME: timer? + if session.notopen or not session.conn:close() then + session.conn:close(true); -- Force FIXME: timer? end - session.conn.close(); + session.conn:close(); xmppserver.ondisconnect(session.conn, "stream error"); end end -- cgit v1.2.3