From 57f5c57d89be095dc3cb5621364ccedc065f3d47 Mon Sep 17 00:00:00 2001 From: Ben Darnell Date: Sat, 9 Jul 2011 14:35:59 -0700 Subject: [PATCH] Move socket accept loop from HTTPServer to netutil. --- tornado/httpserver.py | 67 +++++++++++++++++++------------------------ tornado/netutil.py | 23 +++++++++++++++ 2 files changed, 53 insertions(+), 37 deletions(-) diff --git a/tornado/httpserver.py b/tornado/httpserver.py index 6d58d0d2c..deb8dcbc0 100644 --- a/tornado/httpserver.py +++ b/tornado/httpserver.py @@ -167,8 +167,8 @@ class HTTPServer(object): self.io_loop = ioloop.IOLoop.instance() for sock in sockets: self._sockets[sock.fileno()] = sock - self.io_loop.add_handler(sock.fileno(), self._handle_events, - ioloop.IOLoop.READ) + netutil.add_accept_handler(sock, self._handle_connection, + io_loop=self.io_loop) def add_socket(self, socket): """Singular version of `add_sockets`. Takes a single socket object.""" @@ -238,43 +238,36 @@ class HTTPServer(object): self.io_loop.remove_handler(fd) sock.close() - def _handle_events(self, fd, events): - while True: + def _handle_connection(self, connection, address): + if self.ssl_options is not None: + assert ssl, "Python 2.6+ and OpenSSL required for SSL" try: - connection, address = self._sockets[fd].accept() - except socket.error, e: - if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN): - return - raise - if self.ssl_options is not None: - assert ssl, "Python 2.6+ and OpenSSL required for SSL" - try: - connection = ssl.wrap_socket(connection, - server_side=True, - do_handshake_on_connect=False, - **self.ssl_options) - except ssl.SSLError, err: - if err.args[0] == ssl.SSL_ERROR_EOF: - return connection.close() - else: - raise - except socket.error, err: - if err.args[0] == errno.ECONNABORTED: - return connection.close() - else: - raise - try: - if self.ssl_options is not None: - stream = iostream.SSLIOStream(connection, io_loop=self.io_loop) + connection = ssl.wrap_socket(connection, + server_side=True, + do_handshake_on_connect=False, + **self.ssl_options) + except ssl.SSLError, err: + if err.args[0] == ssl.SSL_ERROR_EOF: + return connection.close() + else: + raise + except socket.error, err: + if err.args[0] == errno.ECONNABORTED: + return connection.close() else: - stream = iostream.IOStream(connection, io_loop=self.io_loop) - if connection.family not in (socket.AF_INET, socket.AF_INET6): - # Unix (or other) socket; fake the remote address - address = ('0.0.0.0', 0) - HTTPConnection(stream, address, self.request_callback, - self.no_keep_alive, self.xheaders) - except Exception: - logging.error("Error in connection callback", exc_info=True) + raise + try: + if self.ssl_options is not None: + stream = iostream.SSLIOStream(connection, io_loop=self.io_loop) + else: + stream = iostream.IOStream(connection, io_loop=self.io_loop) + if connection.family not in (socket.AF_INET, socket.AF_INET6): + # Unix (or other) socket; fake the remote address + address = ('0.0.0.0', 0) + HTTPConnection(stream, address, self.request_callback, + self.no_keep_alive, self.xheaders) + except Exception: + logging.error("Error in connection callback", exc_info=True) class _BadRequestException(Exception): """Exception class for malformed HTTP requests.""" diff --git a/tornado/netutil.py b/tornado/netutil.py index 05921dfae..557b227ab 100644 --- a/tornado/netutil.py +++ b/tornado/netutil.py @@ -21,6 +21,7 @@ import os import socket import stat +from tornado.ioloop import IOLoop from tornado.platform.auto import set_close_exec def bind_sockets(port, address=None, family=socket.AF_UNSPEC, backlog=128): @@ -102,3 +103,25 @@ if hasattr(socket, 'AF_UNIX'): os.chmod(file, mode) sock.listen(backlog) return sock + +def add_accept_handler(sock, callback, io_loop=None): + """Adds an ``IOLoop`` event handler to accept new connections on ``sock``. + + When a connection is accepted, ``callback(connection, address)`` will + be run (``connection`` is a socket object, and ``address`` is the + address of the other end of the connection). Note that this signature + is different from the ``callback(fd, events)`` signature used for + ``IOLoop`` handlers. + """ + if io_loop is None: + io_loop = IOLoop.instance() + def accept_handler(fd, events): + while True: + try: + connection, address = sock.accept() + except socket.error, e: + if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN): + return + raise + callback(connection, address) + io_loop.add_handler(sock.fileno(), accept_handler, IOLoop.READ) -- 2.47.2