X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;f=net%2Fserver_select.lua;h=7ac4152314cca2068f711b3f8865fbe5371b73d3;hb=refs%2Fheads%2Fnohangouts-0.9.7;hp=250438c38b764a104c32169114a72b50f1142c10;hpb=b2de510a78bdd7a10639aaa25db52215ab983dbc;p=prosody.git diff --git a/net/server_select.lua b/net/server_select.lua index 250438c3..7ac41523 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -10,11 +10,6 @@ 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 @@ -47,7 +42,6 @@ local os_difftime = os.difftime local math_min = math.min local math_huge = math.huge local table_concat = table.concat -local string_len = string.len local string_sub = string.sub local coroutine_wrap = coroutine.wrap local coroutine_yield = coroutine.yield @@ -107,6 +101,7 @@ local _readtraffic local _selecttimeout local _sleeptime +local _tcpbacklog local _starttime local _currenttime @@ -118,8 +113,6 @@ local _checkinterval local _sendtimeout local _readtimeout -local _cleanqueue - local _timer local _maxselectlen @@ -147,6 +140,7 @@ _readtraffic = 0 _selecttimeout = 1 -- timeout of socket.select _sleeptime = 0 -- time to wait at the end of every loop +_tcpbacklog = 128 -- some kind of hint to the OS _maxsendlen = 51000 * 1024 -- max len of send buffer _maxreadlen = 25000 * 1024 -- max len of read buffer @@ -155,9 +149,8 @@ _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 - -_maxfd = luasocket._SETSIZE or 1024 -- We should ignore this on Windows. Perhaps by simply setting it to math.huge or something. +local is_windows = package.config:sub(1,1) == "\\" -- check the directory separator, to detemine whether this is Windows +_maxfd = (is_windows and math.huge) or luasocket._SETSIZE or 1024 -- max fd number, limit to 1024 by default to prevent glibc buffer overflow, but not on Windows _maxselectlen = luasocket._SETSIZE or 1024 -- But this still applies on Windows _maxsslhandshake = 30 -- max handshake round-trips @@ -221,7 +214,7 @@ wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx ) -- t handler.resume = function( ) if handler.paused then if not socket then - socket = socket_bind( ip, serverport ); + socket = socket_bind( ip, serverport, _tcpbacklog ); socket:settimeout( 0 ) end _readlistlen = addsocket(_readlist, socket, _readlistlen) @@ -253,7 +246,7 @@ wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx ) -- t end connections = connections + 1 out_put( "server.lua: accepted new client connection from ", tostring(ip), ":", tostring(clientport), " to ", tostring(serverport)) - if dispatch then + if dispatch and not sslctx then -- SSL connections will notify onconnect when handshake completes return dispatch( handler ); end return; @@ -270,7 +263,9 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport if socket:getfd() >= _maxfd then out_error("server.lua: Disallowed FD number: "..socket:getfd()) -- PROTIP: Switch to libevent socket:close( ) -- Should we send some kind of error here? - server.pause( ) + if server then + server.pause( ) + end return nil, nil, "fd-too-large" end socket:settimeout( 0 ) @@ -289,6 +284,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport local status = listeners.onstatus local disconnect = listeners.ondisconnect local drain = listeners.ondrain + local detach = listeners.ondetach local bufferqueue = { } -- buffer array local bufferqueuelen = 0 -- end of buffer array @@ -318,10 +314,14 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport return disconnect end handler.setlistener = function( self, listeners ) + if detach then + detach(self) -- Notify listener that it is no longer responsible for this connection + end dispatch = listeners.onincoming disconnect = listeners.ondisconnect status = listeners.onstatus drain = listeners.ondrain + detach = listeners.ondetach end handler.getstats = function( ) return readtraffic, sendtraffic @@ -350,9 +350,6 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.force_close = function ( self, err ) if bufferqueuelen ~= 0 then out_put("server.lua: discarding unwritten data for ", tostring(ip), ":", tostring(clientport)) - for i = bufferqueuelen, 1, -1 do - bufferqueue[i] = nil; - end bufferqueuelen = 0; end return self:close(err); @@ -405,8 +402,9 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.clientport = function( ) return clientport end + handler.port = handler.clientport -- COMPAT server_event local write = function( self, data ) - bufferlen = bufferlen + string_len( data ) + bufferlen = bufferlen + #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 @@ -488,7 +486,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport local buffer, err, part = receive( socket, pattern ) -- receive buffer with "pattern" if not err or (err == "wantread" or err == "timeout") then -- received something local buffer = buffer or part or "" - local len = string_len( buffer ) + local len = #buffer if len > maxreadlen then handler:close( "receive buffer exceeded" ) return false @@ -514,7 +512,9 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport count = ( succ or byte or 0 ) * STAT_UNIT sendtraffic = sendtraffic + count _sendtraffic = _sendtraffic + count - _ = _cleanqueue and clean( bufferqueue ) + for i = bufferqueuelen,1,-1 do + bufferqueue[ i ] = nil + end --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, "unexpected close", 0; @@ -583,7 +583,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport end out_put( "server.lua: ssl handshake error: ", tostring(err or "handshake too long") ) _ = handler and handler:force_close("ssl handshake failed") - return false, err -- handshake failed + return false, err -- handshake failed end ) end @@ -627,7 +627,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.readbuffer = handshake handler.sendbuffer = handshake - return handshake( socket ) -- do handshake + return handshake( socket ) -- do handshake end end @@ -643,10 +643,10 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport if sslctx and luasec then out_put "server.lua: auto-starting ssl negotiation..." handler.autostart_ssl = true; - local ok, err = handler:starttls(sslctx); - if ok == false then - return nil, nil, err - end + local ok, err = handler:starttls(sslctx); + if ok == false then + return nil, nil, err + end end return handler, socket @@ -710,6 +710,7 @@ local function link(sender, receiver, buffersize) sender:lock_read(true); end end + sender:set_mode("*a"); end ----------------------------------// PUBLIC //-- @@ -731,7 +732,7 @@ addserver = function( addr, port, listeners, pattern, sslctx ) -- this function return nil, err end addr = addr or "*" - local server, err = socket_bind( addr, port ) + local server, err = socket_bind( addr, port, _tcpbacklog ) if err then out_error( "server.lua, [", addr, "]:", port, ": ", err ) return nil, err @@ -780,7 +781,19 @@ closeall = function( ) end getsettings = function( ) - return _selecttimeout, _sleeptime, _maxsendlen, _maxreadlen, _checkinterval, _sendtimeout, _readtimeout, _cleanqueue, _maxselectlen, _maxsslhandshake, _maxfd + return { + select_timeout = _selecttimeout; + select_sleep_time = _sleeptime; + tcp_backlog = _tcpbacklog; + max_send_buffer_size = _maxsendlen; + max_receive_buffer_size = _maxreadlen; + select_idle_check_interval = _checkinterval; + send_timeout = _sendtimeout; + read_timeout = _readtimeout; + max_connections = _maxselectlen; + max_ssl_handshake_roundtrips = _maxsslhandshake; + highest_allowed_fd = _maxfd; + } end changesettings = function( new ) @@ -792,9 +805,9 @@ changesettings = function( new ) _maxsendlen = tonumber( new.max_send_buffer_size ) or _maxsendlen _maxreadlen = tonumber( new.max_receive_buffer_size ) or _maxreadlen _checkinterval = tonumber( new.select_idle_check_interval ) or _checkinterval + _tcpbacklog = tonumber( new.tcp_backlog ) or _tcpbacklog _sendtimeout = tonumber( new.send_timeout ) or _sendtimeout _readtimeout = tonumber( new.read_timeout ) or _readtimeout - _cleanqueue = new.select_clean_queue _maxselectlen = new.max_connections or _maxselectlen _maxsslhandshake = new.max_ssl_handshake_roundtrips or _maxsslhandshake _maxfd = new.highest_allowed_fd or _maxfd @@ -847,9 +860,31 @@ loop = function(once) -- this is the main loop of the program for handler, err in pairs( _closelist ) do handler.disconnect( )( handler, err ) handler:force_close() -- forced disconnect + _closelist[ handler ] = nil; end - clean( _closelist ) _currenttime = luasocket_gettime( ) + + -- Check for socket timeouts + 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:force_close() -- 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 + + -- Fire timers if _currenttime - _timer >= math_min(next_timer_time, 1) then next_timer_time = math_huge; for i = 1, _timerlistlen do @@ -860,8 +895,9 @@ loop = function(once) -- this is the main loop of the program else next_timer_time = next_timer_time - (_currenttime - _timer); end - socket_sleep( _sleeptime ) -- wait some time - --collectgarbage( ) + + -- wait some time (0 by default) + socket_sleep( _sleeptime ) until quitting; if once and quitting == "once" then quitting = nil; return; end return "quitting" @@ -887,13 +923,9 @@ local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx -- When socket is writeable, call onconnect local _sendbuffer = handler.sendbuffer; handler.sendbuffer = function () - _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ); handler.sendbuffer = _sendbuffer; listeners.onconnect(handler); - -- If there was data with the incoming packet, handle it now. - if #handler:bufferqueue() > 0 then - return _sendbuffer(); - end + return _sendbuffer(); -- Send any queued outgoing data end end end @@ -925,28 +957,6 @@ use "setmetatable" ( _writetimes, { __mode = "k" } ) _timer = luasocket_gettime( ) _starttime = luasocket_gettime( ) -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:force_close() -- 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 -) - local function setlogger(new_logger) local old_logger = log; if new_logger then