[Python-checkins] cpython: Issue #20951: SSLSocket.send() now raises either SSLWantReadError or

antoine.pitrou python-checkins at python.org
Tue Apr 29 10:03:37 CEST 2014


http://hg.python.org/cpython/rev/3cf067049211
changeset:   90506:3cf067049211
user:        Antoine Pitrou <solipsis at pitrou.net>
date:        Tue Apr 29 10:03:28 2014 +0200
summary:
  Issue #20951: SSLSocket.send() now raises either SSLWantReadError or SSLWantWriteError on a non-blocking socket if the operation would block. Previously, it would return 0.

Patch by Nikolaus Rath.

files:
  Doc/library/ssl.rst  |  21 +++++++++++++++++++--
  Lib/ssl.py           |  12 +-----------
  Lib/test/test_ssl.py |  30 ++++++++++++++++++++++++++++++
  Misc/NEWS            |   4 ++++
  4 files changed, 54 insertions(+), 13 deletions(-)


diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -1604,8 +1604,25 @@
 Notes on non-blocking sockets
 -----------------------------
 
-When working with non-blocking sockets, there are several things you need
-to be aware of:
+SSL sockets behave slightly different than regular sockets in
+non-blocking mode. When working with non-blocking sockets, there are
+thus several things you need to be aware of:
+
+- Most :class:`SSLSocket` methods will raise either
+  :exc:`SSLWantWriteError` or :exc:`SSLWantReadError` instead of
+  :exc:`BlockingIOError` if an I/O operation would
+  block. :exc:`SSLWantReadError` will be raised if a read operation on
+  the underlying socket is necessary, and :exc:`SSLWantWriteError` for
+  a write operation on the underlying socket. Note that attempts to
+  *write* to an SSL socket may require *reading* from the underlying
+  socket first, and attempts to *read* from the SSL socket may require
+  a prior *write* to the underlying socket.
+
+  .. versionchanged:: 3.5
+
+     In earlier Python versions, the :meth:`!SSLSocket.send` method
+     returned zero instead of raising :exc:`SSLWantWriteError` or
+     :exc:`SSLWantReadError`.
 
 - Calling :func:`~select.select` tells you that the OS-level socket can be
   read from (or written to), but it does not imply that there is sufficient
diff --git a/Lib/ssl.py b/Lib/ssl.py
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -664,17 +664,7 @@
                 raise ValueError(
                     "non-zero flags not allowed in calls to send() on %s" %
                     self.__class__)
-            try:
-                v = self._sslobj.write(data)
-            except SSLError as x:
-                if x.args[0] == SSL_ERROR_WANT_READ:
-                    return 0
-                elif x.args[0] == SSL_ERROR_WANT_WRITE:
-                    return 0
-                else:
-                    raise
-            else:
-                return v
+            return self._sslobj.write(data)
         else:
             return socket.send(self, data, flags)
 
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -2547,6 +2547,36 @@
                 s.write(b"over\n")
                 s.close()
 
+        def test_nonblocking_send(self):
+            server = ThreadedEchoServer(CERTFILE,
+                                        certreqs=ssl.CERT_NONE,
+                                        ssl_version=ssl.PROTOCOL_TLSv1,
+                                        cacerts=CERTFILE,
+                                        chatty=True,
+                                        connectionchatty=False)
+            with server:
+                s = ssl.wrap_socket(socket.socket(),
+                                    server_side=False,
+                                    certfile=CERTFILE,
+                                    ca_certs=CERTFILE,
+                                    cert_reqs=ssl.CERT_NONE,
+                                    ssl_version=ssl.PROTOCOL_TLSv1)
+                s.connect((HOST, server.port))
+                s.setblocking(False)
+
+                # If we keep sending data, at some point the buffers
+                # will be full and the call will block
+                buf = bytearray(8192)
+                def fill_buffer():
+                    while True:
+                        s.send(buf)
+                self.assertRaises((ssl.SSLWantWriteError,
+                                   ssl.SSLWantReadError), fill_buffer)
+
+                # Now read all the output and discard it
+                s.setblocking(True)
+                s.close()
+
         def test_handshake_timeout(self):
             # Issue #5103: SSL handshake must respect the socket timeout
             server = socket.socket(socket.AF_INET)
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -60,6 +60,10 @@
 Library
 -------
 
+- Issue #20951: SSLSocket.send() now raises either SSLWantReadError or
+  SSLWantWriteError on a non-blocking socket if the operation would block.
+  Previously, it would return 0.  Patch by Nikolaus Rath.
+
 - Issue #13248: removed previously deprecated asyncore.dispatcher __getattr__
   cheap inheritance hack.
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list