[Python-checkins] cpython: Issue #11183: Add finer-grained exceptions to the ssl module, so that

antoine.pitrou python-checkins at python.org
Fri Oct 28 00:01:05 CEST 2011


http://hg.python.org/cpython/rev/2c4a9c778bb3
changeset:   73150:2c4a9c778bb3
user:        Antoine Pitrou <solipsis at pitrou.net>
date:        Thu Oct 27 23:56:55 2011 +0200
summary:
  Issue #11183: Add finer-grained exceptions to the ssl module, so that
you don't have to inspect the exception's attributes in the common case.

files:
  Doc/library/ssl.rst  |  42 ++++++++++++++++++++++
  Lib/ssl.py           |   6 ++-
  Lib/test/test_ssl.py |  33 ++++++----------
  Misc/NEWS            |   3 +
  Modules/_ssl.c       |  60 ++++++++++++++++++++++++++++++-
  5 files changed, 120 insertions(+), 24 deletions(-)


diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -59,6 +59,48 @@
    .. versionchanged:: 3.3
       :exc:`SSLError` used to be a subtype of :exc:`socket.error`.
 
+.. exception:: SSLZeroReturnError
+
+   A subclass of :exc:`SSLError` raised when trying to read or write and
+   the SSL connection has been closed cleanly.  Note that this doesn't
+   mean that the underlying transport (read TCP) has been closed.
+
+   .. versionadded:: 3.3
+
+.. exception:: SSLWantReadError
+
+   A subclass of :exc:`SSLError` raised by a :ref:`non-blocking SSL socket
+   <ssl-nonblocking>` when trying to read or write data, but more data needs
+   to be received on the underlying TCP transport before the request can be
+   fulfilled.
+
+   .. versionadded:: 3.3
+
+.. exception:: SSLWantWriteError
+
+   A subclass of :exc:`SSLError` raised by a :ref:`non-blocking SSL socket
+   <ssl-nonblocking>` when trying to read or write data, but more data needs
+   to be sent on the underlying TCP transport before the request can be
+   fulfilled.
+
+   .. versionadded:: 3.3
+
+.. exception:: SSLSyscallError
+
+   A subclass of :exc:`SSLError` raised when a system error was encountered
+   while trying to fulfill an operation on a SSL socket.  Unfortunately,
+   there is no easy way to inspect the original errno number.
+
+   .. versionadded:: 3.3
+
+.. exception:: SSLEOFError
+
+   A subclass of :exc:`SSLError` raised when the SSL connection has been
+   terminated abrupted.  Generally, you shouldn't try to reuse the underlying
+   transport when this error is encountered.
+
+   .. versionadded:: 3.3
+
 .. exception:: CertificateError
 
    Raised to signal an error with a certificate (such as mismatching
diff --git a/Lib/ssl.py b/Lib/ssl.py
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -60,7 +60,11 @@
 import _ssl             # if we can't import it, let the error propagate
 
 from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
-from _ssl import _SSLContext, SSLError
+from _ssl import _SSLContext
+from _ssl import (
+    SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError,
+    SSLSyscallError, SSLEOFError,
+    )
 from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
 from _ssl import OP_ALL, OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1
 from _ssl import RAND_status, RAND_egd, RAND_add, RAND_bytes, RAND_pseudo_bytes
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
@@ -619,13 +619,10 @@
                     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
+                    except ssl.SSLWantReadError:
+                        select.select([s], [], [], 5.0)
+                    except ssl.SSLWantWriteError:
+                        select.select([], [s], [], 5.0)
                 # SSL established
                 self.assertTrue(s.getpeercert())
             finally:
@@ -745,13 +742,10 @@
                     count += 1
                     s.do_handshake()
                     break
-                except ssl.SSLError as err:
-                    if err.args[0] == ssl.SSL_ERROR_WANT_READ:
-                        select.select([s], [], [])
-                    elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
-                        select.select([], [s], [])
-                    else:
-                        raise
+                except ssl.SSLWantReadError:
+                    select.select([s], [], [])
+                except ssl.SSLWantWriteError:
+                    select.select([], [s], [])
             s.close()
             if support.verbose:
                 sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count)
@@ -1030,12 +1024,11 @@
                 def _do_ssl_handshake(self):
                     try:
                         self.socket.do_handshake()
-                    except ssl.SSLError as err:
-                        if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
-                                           ssl.SSL_ERROR_WANT_WRITE):
-                            return
-                        elif err.args[0] == ssl.SSL_ERROR_EOF:
-                            return self.handle_close()
+                    except (ssl.SSLWantReadError, ssl.SSLWantWriteError):
+                        return
+                    except ssl.SSLEOFError:
+                        return self.handle_close()
+                    except ssl.SSLError:
                         raise
                     except socket.error as err:
                         if err.args[0] == errno.ECONNABORTED:
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -341,6 +341,9 @@
 Library
 -------
 
+- Issue #11183: Add finer-grained exceptions to the ssl module, so that
+  you don't have to inspect the exception's attributes in the common case.
+
 - Issue #13216: Add cp65001 codec, the Windows UTF-8 (CP_UTF8).
 
 - Issue #13226: Add RTLD_xxx constants to the os module. These constants can be
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -99,6 +99,11 @@
 
 /* SSL error object */
 static PyObject *PySSLErrorObject;
+static PyObject *PySSLZeroReturnErrorObject;
+static PyObject *PySSLWantReadErrorObject;
+static PyObject *PySSLWantWriteErrorObject;
+static PyObject *PySSLSyscallErrorObject;
+static PyObject *PySSLEOFErrorObject;
 
 #ifdef WITH_THREAD
 
@@ -191,6 +196,7 @@
 PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
 {
     PyObject *v;
+    PyObject *type = PySSLErrorObject;
     char buf[2048];
     char *errstr;
     int err;
@@ -203,15 +209,18 @@
 
         switch (err) {
         case SSL_ERROR_ZERO_RETURN:
-            errstr = "TLS/SSL connection has been closed";
+            errstr = "TLS/SSL connection has been closed (EOF)";
+            type = PySSLZeroReturnErrorObject;
             p = PY_SSL_ERROR_ZERO_RETURN;
             break;
         case SSL_ERROR_WANT_READ:
             errstr = "The operation did not complete (read)";
+            type = PySSLWantReadErrorObject;
             p = PY_SSL_ERROR_WANT_READ;
             break;
         case SSL_ERROR_WANT_WRITE:
             p = PY_SSL_ERROR_WANT_WRITE;
+            type = PySSLWantWriteErrorObject;
             errstr = "The operation did not complete (write)";
             break;
         case SSL_ERROR_WANT_X509_LOOKUP:
@@ -230,6 +239,7 @@
                   = (PySocketSockObject *) PyWeakref_GetObject(obj->Socket);
                 if (ret == 0 || (((PyObject *)s) == Py_None)) {
                     p = PY_SSL_ERROR_EOF;
+                    type = PySSLEOFErrorObject;
                     errstr = "EOF occurred in violation of protocol";
                 } else if (ret == -1) {
                     /* underlying BIO reported an I/O error */
@@ -240,6 +250,7 @@
                     return v;
                 } else { /* possible? */
                     p = PY_SSL_ERROR_SYSCALL;
+                    type = PySSLSyscallErrorObject;
                     errstr = "Some I/O error occurred";
                 }
             } else {
@@ -272,7 +283,7 @@
     ERR_clear_error();
     v = Py_BuildValue("(is)", p, buf);
     if (v != NULL) {
-        PyErr_SetObject(PySSLErrorObject, v);
+        PyErr_SetObject(type, v);
         Py_DECREF(v);
     }
     return NULL;
@@ -2300,6 +2311,23 @@
 PyDoc_STRVAR(SSLError_doc,
 "An error occurred in the SSL implementation.");
 
+PyDoc_STRVAR(SSLZeroReturnError_doc,
+"SSL/TLS session closed cleanly.");
+
+PyDoc_STRVAR(SSLWantReadError_doc,
+"Non-blocking SSL socket needs to read more data\n"
+"before the requested operation can be completed.");
+
+PyDoc_STRVAR(SSLWantWriteError_doc,
+"Non-blocking SSL socket needs to write more data\n"
+"before the requested operation can be completed.");
+
+PyDoc_STRVAR(SSLSyscallError_doc,
+"System error when attempting SSL operation.");
+
+PyDoc_STRVAR(SSLEOFError_doc,
+"SSL/TLS connection terminated abruptly.");
+
 
 PyMODINIT_FUNC
 PyInit__ssl(void)
@@ -2343,7 +2371,33 @@
                                                  NULL);
     if (PySSLErrorObject == NULL)
         return NULL;
-    if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0)
+    PySSLZeroReturnErrorObject = PyErr_NewExceptionWithDoc(
+        "ssl.SSLZeroReturnError", SSLZeroReturnError_doc,
+        PySSLErrorObject, NULL);
+    PySSLWantReadErrorObject = PyErr_NewExceptionWithDoc(
+        "ssl.SSLWantReadError", SSLWantReadError_doc,
+        PySSLErrorObject, NULL);
+    PySSLWantWriteErrorObject = PyErr_NewExceptionWithDoc(
+        "ssl.SSLWantWriteError", SSLWantWriteError_doc,
+        PySSLErrorObject, NULL);
+    PySSLSyscallErrorObject = PyErr_NewExceptionWithDoc(
+        "ssl.SSLSyscallError", SSLSyscallError_doc,
+        PySSLErrorObject, NULL);
+    PySSLEOFErrorObject = PyErr_NewExceptionWithDoc(
+        "ssl.SSLEOFError", SSLEOFError_doc,
+        PySSLErrorObject, NULL);
+    if (PySSLZeroReturnErrorObject == NULL
+        || PySSLWantReadErrorObject == NULL
+        || PySSLWantWriteErrorObject == NULL
+        || PySSLSyscallErrorObject == NULL
+        || PySSLEOFErrorObject == NULL)
+        return NULL;
+    if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0
+        || PyDict_SetItemString(d, "SSLZeroReturnError", PySSLZeroReturnErrorObject) != 0
+        || PyDict_SetItemString(d, "SSLWantReadError", PySSLWantReadErrorObject) != 0
+        || PyDict_SetItemString(d, "SSLWantWriteError", PySSLWantWriteErrorObject) != 0
+        || PyDict_SetItemString(d, "SSLSyscallError", PySSLSyscallErrorObject) != 0
+        || PyDict_SetItemString(d, "SSLEOFError", PySSLEOFErrorObject) != 0)
         return NULL;
     if (PyDict_SetItemString(d, "_SSLContext",
                              (PyObject *)&PySSLContext_Type) != 0)

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


More information about the Python-checkins mailing list