Esempio n. 1
0
    def add_sockets( self, sockets ):
        """Make the server start accepting connections using event loop on the
        given sockets.

        The ``sockets`` parameter is a list of socket objects such as
        those returned by `bind_sockets`.
        """
        self.ioloop = HTTPIOLoop( self.sett )
        for sock in sockets:
            self._sockets[ sock.fileno()] = sock
            add_accept_handler( sock, self._handle_connection, self.ioloop )
Esempio n. 2
0
class TCPServer( object ):
    """A non-blocking, single-threaded "Mixin class" implementing TCP server.

    To use `TCPServer`, define a `Plugin` subclass which overrides the 
    `handle_stream` method.

    `TCPServer` can serve SSL traffic with Python 2.6+ and OpenSSL.
    To make this server serve SSL traffic, configure the sub-class plugin with
    `ssloptions.*` settings. which is required for the `ssl.wrap_socket` 
    method, including "certfile" and "keyfile" 
    """

    def __init__( self, sett ):
        # configuration settings
        self.sett = sett
        self._sockets = {}  # fd -> socket object
        self._pending_sockets = []
        self._started = False
        self.ioloop = None

    def listen( self ):
        """Starts accepting connections on the given port.

        This method may be called more than once to listen on multiple ports.
        `listen` takes effect immediately; it is not necessary to call
        `TCPServer.start` afterwards.  It is, however, necessary to start
        the `HTTPIOLoop`.
        """
        sett = self.sett
        sockets = bind_sockets( 
                        sett['port'], sett['host'], None, sett['backlog'] )
        self.add_sockets(sockets)

    def add_sockets( self, sockets ):
        """Make the server start accepting connections using event loop on the
        given sockets.

        The ``sockets`` parameter is a list of socket objects such as
        those returned by `bind_sockets`.
        """
        self.ioloop = HTTPIOLoop( self.sett )
        for sock in sockets:
            self._sockets[ sock.fileno()] = sock
            add_accept_handler( sock, self._handle_connection, self.ioloop )

    def add_socket( self, socket ):
        """Singular version of `add_sockets`.  Takes a single socket object."""
        self.add_sockets([socket])

    def bind( self ):
        """Binds this server to the addres, port and family configured in
        server settings.

        This method may be called multiple times prior to `start` to listen
        on multiple ports or interfaces."""
        family = socket.AF_UNSPEC 
        sett = self.sett
        sockets = bind_sockets( 
                    sett['port'], sett['host'], family, sett['backlog'] )
        if self._started :
            self.add_sockets( sockets )
        else:
            self._pending_sockets.extend( sockets )

    def start( self ):
        """Starts this server using HTTPIOloop.

        By default, we run the server in this process and do not fork any
        additional child process.

        If `multiprocess` settings not configured or configured as <= 0, we 
        detect the number of cores available on this machine and fork that 
        number of child processes. If `multiprocess` settings configured as
        > 0, we fork that specific number of sub-processes.

        Since we use processes and not threads, there is no shared memory
        between any server code.

        Note that multiple processes are not compatible with the autoreload
        module (or the ``debug=True`` option to `Platform`). When using 
        multiple processes, no HTTPIOLoop can be created or referenced until
        after the call to ``TCPServer.start(n)``.
        """
        assert not self._started
        self._started = True
        sett = self.sett

        if sett['multiprocess'] <= 0:  # Single process
            #log.info("Starting server in single process mode ...")
            self.listen()
        else :                      # multi-process
            #log.info("Starting server in multi process mode ...")
            sockets = bind_sockets( 
                        sett['port'], sett['host'], None, sett['backlog'] )
            process.fork_processes( sett['multiprocess'], sett['max_restart'] )
            self.add_sockets( sockets )
        # TODO : Setup logging for multiple process ?

        self.ioloop.start() # Block !


    def stop(self):
        """Stops listening for new connections.

        Requests currently in progress may still continue after the
        server is stopped.
        """
        for fd, sock in self._sockets.items() :
            self.ioloop.remove_handler(fd)
            sock.close()

    def handle_stream(self, stream, address):
        """Override to handle a new `IOStream` from an incoming connection."""
        raise NotImplementedError()

    def _handle_connection( self, conn, address ):
        ssloptions = h.settingsfor( 'ssloptions.', self.sett )
        is_ssl = ssloptions['keyfile'] and ssloptions['certfile']
        if is_ssl :
            try:
                conn = ssl.wrap_socket( conn,
                                        server_side=True,
                                        do_handshake_on_connect=False,
                                        **ssloptions )
            except ssl.SSLError as err:
                if err.args[0] == ssl.SSL_ERROR_EOF:
                    return conn.close()
                else:
                    raise
            except socket.error as err:
                if err.args[0] == errno.ECONNABORTED:
                    return conn.close()
                else:
                    raise
        try:
            if is_ssl :
                stream = HTTPSSLIOStream( 
                            conn, address, self.ioloop, 
                            self.sett, ssloptions=ssloptions )
            else :
                stream = HTTPIOStream( conn, address, self.ioloop, self.sett ) 
            self.handle_stream( stream, address )
        except Exception:
            #log.error("Error in connection callback", exc_info=True)
            pass