Let Google Hangouts contacts appear offline
[prosody.git] / net / server_select.lua
index 86c9daef24ae7e8fd63a130a28177b95c9ddde05..7ac4152314cca2068f711b3f8865fbe5371b73d3 100644 (file)
@@ -101,6 +101,7 @@ local _readtraffic
 
 local _selecttimeout
 local _sleeptime
+local _tcpbacklog
 
 local _starttime
 local _currenttime
@@ -139,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
@@ -147,7 +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
 
-_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
@@ -211,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)
@@ -243,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;
@@ -260,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 )
@@ -279,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
@@ -308,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
@@ -392,6 +402,7 @@ 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 + #data
                if bufferlen > maxsendlen then
@@ -551,6 +562,9 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport
                                                handler.readbuffer = _readbuffer        -- when handshake is done, replace the handshake function with regular functions
                                                handler.sendbuffer = _sendbuffer
                                                _ = status and status( handler, "ssl-handshake-complete" )
+                                               if self.autostart_ssl and listeners.onconnect then
+                                                       listeners.onconnect(self);
+                                               end
                                                _readlistlen = addsocket(_readlist, client, _readlistlen)
                                                return true
                                        else
@@ -696,6 +710,7 @@ local function link(sender, receiver, buffersize)
                        sender:lock_read(true);
                end
        end
+       sender:set_mode("*a");
 end
 
 ----------------------------------// PUBLIC //--
@@ -717,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
@@ -769,6 +784,7 @@ getsettings = function( )
        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;
@@ -789,6 +805,7 @@ 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
        _maxselectlen = new.max_connections or _maxselectlen
@@ -847,6 +864,7 @@ loop = function(once) -- this is the main loop of the program
                end
                _currenttime = luasocket_gettime( )
 
+               -- Check for socket timeouts
                local difftime = os_difftime( _currenttime - _starttime )
                if difftime > _checkinterval then
                        _starttime = _currenttime
@@ -866,6 +884,7 @@ loop = function(once) -- this is the main loop of the program
                        end
                end
 
+               -- Fire timers
                if _currenttime - _timer >= math_min(next_timer_time, 1) then
                        next_timer_time = math_huge;
                        for i = 1, _timerlistlen do
@@ -876,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"
@@ -903,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