]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Make the SSL handshake non-blocking.
authorBen Darnell <ben@bendarnell.com>
Thu, 19 Aug 2010 18:25:15 +0000 (14:25 -0400)
committerBen Darnell <ben@bendarnell.com>
Thu, 19 Aug 2010 18:25:15 +0000 (14:25 -0400)
Based on work by mgenti.  See
http://github.com/mgenti/tornado/commit/197e88e62f1970b65a3807ac37160194fab7d496
and its ancestors.

tornado/httpserver.py
tornado/iostream.py

index e2975d03d106448bf8b2f005a8468d26e87c62fb..f031deb5a9c29448f46039fc7c76f627d6d6ebe6 100644 (file)
@@ -225,10 +225,24 @@ class HTTPServer(object):
                 raise
             if self.ssl_options is not None:
                 assert ssl, "Python 2.6+ and OpenSSL required for SSL"
-                connection = ssl.wrap_socket(
-                    connection, server_side=True, **self.ssl_options)
+                try:
+                    connection = ssl.wrap_socket(connection, 
+                                                 server_side=True, 
+                                                 do_handshake_on_connect=False, 
+                                                 **self.ssl_options)
+                except ssl.SSLError, err:
+                    logging.error("SSL Error in SSL wrap:", exc_info=True)
+                    if err.args[0] == ssl.SSL_ERROR_EOF:
+                        return connection.close()
+                except socket.error, err:
+                    logging.error("Socket Error in SSL wrap:", exc_info=True)
+                    if err.args[0] == errno.ECONNABORTED:
+                        return connection.close()
             try:
-                stream = iostream.IOStream(connection, io_loop=self.io_loop)
+                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)
                 HTTPConnection(stream, address, self.request_callback,
                                self.no_keep_alive, self.xheaders)
             except:
index 6ea1042796d538bb6a63e6d4ce0b44fba3d57e47..84feedd500d0b1927a6cb3531910741f998d41dd 100644 (file)
@@ -22,6 +22,11 @@ import socket
 
 from tornado import ioloop
 
+try:
+    import ssl # Python 2.6+
+except ImportError:
+    ssl = None
+
 class IOStream(object):
     """A utility class to write to and read from a non-blocking socket.
 
@@ -241,3 +246,44 @@ class IOStream(object):
         if not self._state & state:
             self._state = self._state | state
             self.io_loop.update_handler(self.socket.fileno(), self._state)
+
+
+class SSLIOStream(IOStream):
+    """Sets up an SSL connection in a non-blocking manner"""
+    def __init__(self, *args, **kwargs):
+        super(SSLIOStream, self).__init__(*args, **kwargs)
+        self._ssl_accepting = True
+        self._do_ssl_handshake()
+
+    def _do_ssl_handshake(self):
+        # Based on code from test_ssl.py in the python stdlib
+        try:
+            self.socket.do_handshake()
+        except ssl.SSLError, err:
+            if err.args[0] == ssl.SSL_ERROR_WANT_READ:
+                self._add_io_state(self.io_loop.READ)
+                return
+            elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
+                self._add_io_state(self.io_loop.WRITE)
+                return
+            elif err.args[0] in (ssl.SSL_ERROR_EOF,
+                                 ssl.SSL_ERROR_ZERO_RETURN):
+                return self.close()
+            raise
+        except socket.error, err:
+            if err.args[0] == errno.ECONNABORTED:
+                return self.close()
+        else:
+            self._ssl_accepting = False
+
+    def _handle_read(self):
+        if self._ssl_accepting:
+            self._do_ssl_handshake()
+            return
+        super(SSLIOStream, self)._handle_read()
+
+    def _handle_write(self):
+        if self._ssl_accepting:
+            self._do_ssl_handshake()
+            return
+        super(SSLIOStream, self)._handle_write()