]> granicus.if.org Git - python/commitdiff
Merged revisions 88664 via svnmerge from
authorAntoine Pitrou <solipsis@pitrou.net>
Sat, 26 Feb 2011 23:25:34 +0000 (23:25 +0000)
committerAntoine Pitrou <solipsis@pitrou.net>
Sat, 26 Feb 2011 23:25:34 +0000 (23:25 +0000)
svn+ssh://pythondev@svn.python.org/python/branches/py3k

........
  r88664 | antoine.pitrou | 2011-02-27 00:24:06 +0100 (dim., 27 févr. 2011) | 4 lines

  Issue #11326: Add the missing connect_ex() implementation for SSL sockets,
  and make it work for non-blocking connects.
........

Lib/ssl.py
Lib/test/test_ssl.py
Misc/NEWS

index f1a0e45903c74966552ac0f93bdbcf0e4696ff03..84aa6dc3bf7b8f028c6c0650c5be71eb6cde866b 100644 (file)
@@ -237,6 +237,7 @@ class SSLSocket(socket):
 
         self._closed = False
         self._sslobj = None
+        self._connected = connected
         if connected:
             # create the SSL object
             try:
@@ -430,23 +431,36 @@ class SSLSocket(socket):
         finally:
             self.settimeout(timeout)
 
-    def connect(self, addr):
-        """Connects to remote ADDR, and then wraps the connection in
-        an SSL channel."""
+    def _real_connect(self, addr, return_errno):
         if self.server_side:
             raise ValueError("can't connect in server-side mode")
         # Here we assume that the socket is client-side, and not
         # connected at the time of the call.  We connect it, then wrap it.
-        if self._sslobj:
+        if self._connected:
             raise ValueError("attempt to connect already-connected SSLSocket!")
-        socket.connect(self, addr)
         self._sslobj = self.context._wrap_socket(self, False, self.server_hostname)
         try:
+            socket.connect(self, addr)
             if self.do_handshake_on_connect:
                 self.do_handshake()
-        except:
-            self._sslobj = None
-            raise
+        except socket_error as e:
+            if return_errno:
+                return e.errno
+            else:
+                self._sslobj = None
+                raise e
+        self._connected = True
+        return 0
+
+    def connect(self, addr):
+        """Connects to remote ADDR, and then wraps the connection in
+        an SSL channel."""
+        self._real_connect(addr, False)
+
+    def connect_ex(self, addr):
+        """Connects to remote ADDR, and then wraps the connection in
+        an SSL channel."""
+        return self._real_connect(addr, True)
 
     def accept(self):
         """Accepts a new connection from a remote client, and returns
index 4ea1a63e4959c548393a7a86fde13c7bc1eea031..3347e9e6f3b5cfc4833d99b6b199d646bf36f5f7 100644 (file)
@@ -451,6 +451,49 @@ class NetworkedTests(unittest.TestCase):
             finally:
                 s.close()
 
+    def test_connect_ex(self):
+        # Issue #11326: check connect_ex() implementation
+        with support.transient_internet("svn.python.org"):
+            s = ssl.wrap_socket(socket.socket(socket.AF_INET),
+                                cert_reqs=ssl.CERT_REQUIRED,
+                                ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
+            try:
+                self.assertEqual(0, s.connect_ex(("svn.python.org", 443)))
+                self.assertTrue(s.getpeercert())
+            finally:
+                s.close()
+
+    def test_non_blocking_connect_ex(self):
+        # Issue #11326: non-blocking connect_ex() should allow handshake
+        # to proceed after the socket gets ready.
+        with support.transient_internet("svn.python.org"):
+            s = ssl.wrap_socket(socket.socket(socket.AF_INET),
+                                cert_reqs=ssl.CERT_REQUIRED,
+                                ca_certs=SVN_PYTHON_ORG_ROOT_CERT,
+                                do_handshake_on_connect=False)
+            try:
+                s.setblocking(False)
+                rc = s.connect_ex(('svn.python.org', 443))
+                self.assertIn(rc, (0, errno.EINPROGRESS))
+                # Wait for connect to finish
+                select.select([], [s], [], 5.0)
+                # Non-blocking handshake
+                while True:
+                    try:
+                        s.do_handshake()
+                        break
+                    except ssl.SSLError as err:
+                        if err.args[0] == ssl.SSL_ERROR_WANT_READ:
+                            select.select([s], [], [], 5.0)
+                        elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
+                            select.select([], [s], [], 5.0)
+                        else:
+                            raise
+                # SSL established
+                self.assertTrue(s.getpeercert())
+            finally:
+                s.close()
+
     def test_connect_with_context(self):
         with support.transient_internet("svn.python.org"):
             # Same as test_connect, but with a separately created context
index ebc9de9af76dac5d3f3e713ddec12f3b4de58130..665f6c878ff947ca5da2a6c19aa89b80cdd3a08a 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -24,6 +24,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #11326: Add the missing connect_ex() implementation for SSL sockets,
+  and make it work for non-blocking connects.
+
 - Issue #7322: Trying to read from a socket's file-like object after a timeout
   occurred now raises an error instead of silently losing data.