Merge 0.9->0.10
authorKim Alvefur <zash@zash.se>
Tue, 19 Apr 2016 17:00:19 +0000 (19:00 +0200)
committerKim Alvefur <zash@zash.se>
Tue, 19 Apr 2016 17:00:19 +0000 (19:00 +0200)
1  2 
net/server_event.lua

index 5fd49d0a91d70f5b59facc3da261721159dc36dc,882d10ed95d0e45d3436ed663c73c84258bd4ddb..1329a43d67abc0933f234b3af829763afaf28453
  
  -- End of client interface methods
  
 -local handleclient;
 -do
 -      local string_sub = string.sub  -- caching table lookups
 -      local addevent = base.addevent
 -      local socket_gettime = socket.gettime
 -      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
 -                      ontimeout = listener.ontimeout; -- called when fatal socket timeout occurs
 -                      ondrain = listener.ondrain; -- called when writebuffer is empty
 -                      ondetach = listener.ondetach; -- called when disassociating this listener from this connection
 -                      onstatus = listener.onstatus; -- called for status changes (e.g. of SSL/TLS)
 -                      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,
 -                      _serverport = (server and server:port() or nil),
 -                      _sslctx = sslctx; -- parameters
 -                      _usingssl = false;  -- client is using ssl;
 -              }
 -              if not ssl then interface.starttls = false; 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 )
 -                      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
 +local 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
 +              ontimeout = listener.ontimeout; -- called when fatal socket timeout occurs
 +              onreadtimeout = listener.onreadtimeout; -- called when socket inactivity timeout occurs
 +              ondrain = listener.ondrain; -- called when writebuffer is empty
 +              ondetach = listener.ondetach; -- called when disassociating this listener from this connection
 +              onstatus = listener.onstatus; -- called for status changes (e.g. of SSL/TLS)
 +              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,
 +              _serverport = (server and server:port() or nil),
 +              _sslctx = sslctx; -- parameters
 +              _usingssl = false;  -- client is using ssl;
 +      }
 +      if not has_luasec then interface.starttls = false; 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 )
 +              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
 -                              interface.writebuffer = { t_concat(interface.writebuffer) }
 -                              local succ, err, byte = interface.conn:send( interface.writebuffer[1], 1, interface.writebufferlen )
 -                              --vdebug( "write data:", interface.writebuffer, "error:", err, "part:", byte )
 -                              if succ then  -- writing succesful
 -                                      interface.writebuffer[1] = nil
 -                                      interface.writebufferlen = 0
 -                                      interface:ondrain();
 -                                      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.writebufferlen ~= 0 then
 -                                              -- data possibly written from ondrain
 -                                              return EV_WRITE, cfg.WRITE_TIMEOUT
 -                                      elseif interface.eventreadtimeout then
 -                                              return EV_WRITE, cfg.WRITE_TIMEOUT
 -                                      end
 -                                      interface.eventwrite = nil
 -                                      return -1
 -                              elseif byte and (err == "timeout" or err == "wantwrite") then  -- want write again
 -                                      --vdebug( "writebuffer is not empty:", err )
 -                                      interface.writebuffer[1] = string_sub( interface.writebuffer[1], 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 -1;
 -                                              end
 -                                              interface.eventwritetimeout = addevent( base, nil, EV_TIMEOUT, callback, cfg.WRITE_TIMEOUT )  -- reg a new timeout event
 -                                              debug( "wantread during write attempt, 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
 +                              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
 -              end
 -              
 -              interface.readcallback = function( event )  -- called on read events
 -                      --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:", tostring(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 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:", tostring(ret) )
 -                                      end
 -                                      if interface.eventreadtimeout then
 -                                              interface.eventreadtimeout:close( )
 -                                              interface.eventreadtimeout = nil
 -                                      end
 -                              end
 -                              local buffer, err, part = interface.conn:receive( interface._pattern )  -- receive buffer with "pattern"
 -                              --vdebug( "read data:", tostring(buffer), "error:", tostring(err), "part:", tostring(part) )
 -                              buffer = buffer or part
 -                              if buffer and #buffer > 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
 +                      interface.writebuffer = { t_concat(interface.writebuffer) }
 +                      local succ, err, byte = interface.conn:send( interface.writebuffer[1], 1, interface.writebufferlen )
 +                      --vdebug( "write data:", interface.writebuffer, "error:", err, "part:", byte )
 +                      if succ then  -- writing succesful
 +                              interface.writebuffer[1] = nil
 +                              interface.writebufferlen = 0
 +                              interface:ondrain();
 +                              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.writebuffer ~= 0 then
++                              elseif interface.writebufferlen ~= 0 then
 +                                      -- data possibly written from ondrain
 +                                      return EV_WRITE, cfg.WRITE_TIMEOUT
 +                              elseif interface.eventreadtimeout then
 +                                      return EV_WRITE, cfg.WRITE_TIMEOUT
                                end
 -                              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 )
 -                                              end
 -                                              interface.eventreadtimeout = addevent( base, nil, EV_TIMEOUT,
 -                                                      function( )
 -                                                              interface:_close()
 -                                                      end, cfg.READ_TIMEOUT
 -                                              )
 -                                              debug( "wantwrite during read attempt, 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.eventwrite = nil
 +                              return -1
 +                      elseif byte and (err == "timeout" or err == "wantwrite") then  -- want write again
 +                              --vdebug( "writebuffer is not empty:", err )
 +                              interface.writebuffer[1] = s_sub( interface.writebuffer[1], 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.eventread = nil
 -                                              return -1
 +                                              interface.eventwritetimeout = nil
 +                                              return -1;
                                        end
 -                              else
 -                                      interface.onincoming( interface, buffer, err )  -- send new data to listener
 -                              end
 -                              if interface.noreading then
 -                                      interface.eventread = nil;
 -                                      return -1;
 +                                      interface.eventwritetimeout = addevent( base, nil, EV_TIMEOUT, callback, cfg.WRITE_TIMEOUT )  -- reg a new timeout event
 +                                      debug( "wantread during write attempt, 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_READ, cfg.READ_TIMEOUT
 +                              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
  
 -              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 )  -- 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
 -                      
 -                      _ip = addr, _port = port, _pattern = pattern,
 -                      _sslctx = sslctx;
 -              }
 -              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
 -                              --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
 +      interface.readcallback = function( event )  -- called on read events
 +              --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:", tostring(interface.noreading or interface.fatalerror) )
 +                      interface.eventread = nil
 +                      return -1
 +              end
 +              if EV_TIMEOUT == event and interface:onreadtimeout() ~= true then
 +                      return -1 -- took too long to get some data from client -> disconnect
 +              end
 +              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:", tostring(ret) )
                        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 attempt
 -                              end
 -                              local client_ip, client_port = client:getpeername( )
 -                              interface._connections = interface._connections + 1  -- increase connection count
 -                              local clientinterface = handleclient( client, client_ip, client_port, interface, pattern, listener, sslctx )
 -                              --vdebug( "client id:", clientinterface, "startssl:", startssl )
 -                              if ssl and sslctx then
 -                                      clientinterface:starttls(sslctx, true)
 -                              else
 -                                      clientinterface:_start_session( true )
 -                              end
 -                              debug( "accepted incoming client connection from:", client_ip or "<unknown IP>", client_port or "<unknown port>", "to", port or "<unknown port>");
 -                              
 -                              client, err = server:accept()    -- try to accept again
 +                      if interface.eventreadtimeout then
 +                              interface.eventreadtimeout:close( )
 +                              interface.eventreadtimeout = nil
                        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, 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, cfg.ACCEPT_QUEUE )  -- create server socket
 -              if not server then
 -                      debug( "creating server socket on "..addr.." port "..port.." failed:", err )
 -                      return nil, err
 +              local buffer, err, part = interface.conn:receive( interface._pattern )  -- receive buffer with "pattern"
 +              --vdebug( "read data:", tostring(buffer), "error:", tostring(err), "part:", tostring(part) )
 +              buffer = buffer or part
 +              if buffer and #buffer > 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
 -              local sslctx
 -              if sslcfg then
 -                      if not ssl then
 -                              debug "fatal error: luasec not found"
 -                              return nil, "luasec not found"
 -                      end
 -                      sslctx, err = sslcfg
 -                      if err then
 -                              debug( "error while creating new ssl context for server socket:", err )
 -                              return nil, err
 +              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 )
 +                              end
 +                              interface.eventreadtimeout = addevent( base, nil, EV_TIMEOUT,
 +                              function( )
 +                                      interface:_close()
 +                              end, cfg.READ_TIMEOUT
 +                              )
 +                              debug( "wantwrite during read attempt, 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
 +              else
 +                      interface.onincoming( interface, buffer, err )  -- send new data to listener
 +              end
 +              if interface.noreading then
 +                      interface.eventread = nil;
 +                      return -1;
                end
 -              local interface = handleserver( server, addr, port, pattern, listener, sslctx, startssl )  -- new server handler
 -              debug( "new server created with id:", tostring(interface))
 -              return interface
 +              return EV_READ, cfg.READ_TIMEOUT
        end
 -end )( )
  
 -local addclient, wrapclient
 -do
 -      function wrapclient( client, ip, port, listeners, pattern, sslctx )
 -              local interface = handleclient( client, ip, port, nil, pattern, listeners, sslctx )
 -              interface:_start_connection(sslctx)
 -              return interface, client
 -              --function handleclient( client, ip, port, server, pattern, listener, _, sslctx )  -- creates an client interface
 -      end
 -      
 -      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 )
 -                      return nil, err
 +      client:settimeout( 0 )  -- set non blocking
 +      setmetatable(interface, interface_mt)
 +      interfacelist[ interface ] = true  -- add to interfacelist
 +      return interface
 +end
 +
 +local function handleserver( server, addr, port, pattern, listener, sslctx )  -- creates an server interface
 +      debug "creating server interface..."
 +      local interface = {
 +              _connections = 0;
 +
 +              type = "server";
 +              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
 +
 +              _ip = addr, _port = port, _pattern = pattern,
 +              _sslctx = sslctx;
 +      }
 +      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
 +                      --vdebug( "leaving this event because:", self.fatalerror )
 +                      interface.eventread = nil
 +                      return -1
                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
 +              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
 -              local sslctx
 -              if sslcfg then  -- handle ssl/new context
 -                      if not ssl then
 -                              debug "need luasec, but not available"
 -                              return nil, "luasec not found"
 +              --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 attempt
                        end
 -                      sslctx, err = sslcfg
 -                      if err then
 -                              debug( "cannot create new ssl context:", err )
 -                              return nil, err
 +                      local client_ip, client_port = client:getpeername( )
 +                      interface._connections = interface._connections + 1  -- increase connection count
 +                      local clientinterface = handleclient( client, client_ip, client_port, interface, pattern, listener, sslctx )
 +                      --vdebug( "client id:", clientinterface, "startssl:", startssl )
 +                      if has_luasec and sslctx then
 +                              clientinterface:starttls(sslctx, true)
 +                      else
 +                              clientinterface:_start_session( true )
                        end
 +                      debug( "accepted incoming client connection from:", client_ip or "<unknown IP>", client_port or "<unknown port>", "to", port or "<unknown port>");
 +
 +                      client, err = server:accept()    -- try to accept again
                end
 -              local res, err = client:connect( addr, serverport )  -- connect
 -              if res or ( err == "timeout" ) then
 -                      local ip, port = client:getsockname( )
 -                      local interface = wrapclient( client, ip, serverport, listener, pattern, sslctx, startssl )
 -                      interface:_start_connection( startssl )
 -                      debug( "new connection id:", interface.id )
 -                      return interface, err
 +              return EV_READ
 +      end
 +
 +      server:settimeout( 0 )
 +      setmetatable(interface, interface_mt)
 +      interfacelist[ interface ] = true
 +      interface:_start_session()
 +      return interface
 +end
 +
 +local function addserver( addr, port, listener, pattern, sslctx, startssl )  -- TODO: check arguments
 +      --vdebug( "creating new tcp server with following parameters:", addr or "nil", port or "nil", sslctx or "nil", startssl or "nil")
 +      if sslctx and not has_luasec then
 +              debug "fatal error: luasec not found"
 +              return nil, "luasec not found"
 +      end
 +      local server, err = socket.bind( addr, port, cfg.ACCEPT_QUEUE )  -- create server socket
 +      if not server then
 +              debug( "creating server socket on "..addr.." port "..port.." failed:", err )
 +              return nil, err
 +      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
 +
 +local function wrapclient( client, ip, port, listeners, pattern, sslctx )
 +      local interface = handleclient( client, ip, port, nil, pattern, listeners, sslctx )
 +      interface:_start_connection(sslctx)
 +      return interface, client
 +      --function handleclient( client, ip, port, server, pattern, listener, _, sslctx )  -- creates an client interface
 +end
 +
 +local function addclient( addr, serverport, listener, pattern, sslctx, typ )
 +      if sslctx and not has_luasec then
 +              debug "need luasec, but not available"
 +              return nil, "luasec not found"
 +      end
 +      if not typ then
 +              local addrinfo, err = getaddrinfo(addr)
 +              if not addrinfo then return nil, err end
 +              if addrinfo[1] and addrinfo[1].family == "inet6" then
 +                      typ = "tcp6"
                else
 -                      debug( "new connection failed:", err )
 -                      return nil, err
 +                      typ = "tcp"
                end
        end
 +      local create = socket[typ]
 +      if type( create ) ~= "function"  then
 +              return nil, "invalid socket type"
 +      end
 +      local client, err = create()  -- creating new socket
 +      if not client then
 +              debug( "cannot create socket:", err )
 +              return nil, err
 +      end
 +      client:settimeout( 0 )  -- set nonblocking
 +      local res, err = client:connect( addr, serverport )  -- connect
 +      if res or ( err == "timeout" ) then
 +              local ip, port = client:getsockname( )
 +              local interface = wrapclient( client, ip, serverport, listener, pattern, sslctx )
 +              debug( "new connection id:", interface.id )
 +              return interface, err
 +      else
 +              debug( "new connection failed:", err )
 +              return nil, err
 +      end
  end
  
 -
 -local loop = function( )  -- starts the event loop
 +local function loop( )  -- starts the event loop
        base:loop( )
        return "quitting";
  end