[pypy-commit] pypy stdlib-2.7.9: Lots of improvement to the SSLError class.
amauryfa
noreply at buildbot.pypy.org
Sun Jan 25 19:57:05 CET 2015
Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: stdlib-2.7.9
Changeset: r75517:ad8cb0a374b6
Date: 2015-01-25 19:55 +0100
http://bitbucket.org/pypy/pypy/changeset/ad8cb0a374b6/
Log: Lots of improvement to the SSLError class.
diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py
--- a/pypy/module/_ssl/interp_ssl.py
+++ b/pypy/module/_ssl/interp_ssl.py
@@ -3,12 +3,15 @@
from rpython.rlib.ropenssl import *
from rpython.rlib.rposix import get_errno, set_errno
from rpython.rlib.rweakref import RWeakValueDictionary
+from rpython.rlib.objectmodel import specialize
from rpython.rtyper.lltypesystem import lltype, rffi
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.error import OperationError, oefmt, wrap_oserror
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty
+from pypy.module._ssl.ssl_data import (
+ LIBRARY_CODES_TO_NAMES, ERROR_CODES_TO_NAMES)
from pypy.module._socket import interp_socket
@@ -91,13 +94,32 @@
constants["OPENSSL_VERSION"] = SSLEAY_VERSION
-def ssl_error(space, msg, errno=0, exc='w_sslerror'):
- w_exception_class = get_exception_class(space, exc)
- if not errno:
- w_exception = space.call_function(w_exception_class, space.wrap(msg))
- else:
+def ssl_error(space, msg, errno=0, w_errtype=None, errcode=0):
+ reason_str = None
+ lib_str = None
+ if errcode:
+ err_lib = libssl_ERR_GET_LIB(errcode)
+ err_reason = libssl_ERR_GET_REASON(errcode)
+ reason_str = ERROR_CODES_TO_NAMES.get((err_lib, err_reason), None)
+ lib_str = LIBRARY_CODES_TO_NAMES.get(err_lib, None)
+ msg = rffi.charp2str(libssl_ERR_reason_error_string(errcode))
+ if not msg:
+ msg = "unknown error"
+ if reason_str and lib_str:
+ msg = "[%s: %s] %s" % (lib_str, reason_str, msg)
+ elif lib_str:
+ msg = "[%s] %s" % (lib_str, msg)
+
+ w_exception_class = w_errtype or get_exception_class(space, 'w_sslerror')
+ if errno or errcode:
w_exception = space.call_function(w_exception_class,
space.wrap(errno), space.wrap(msg))
+ else:
+ w_exception = space.call_function(w_exception_class, space.wrap(msg))
+ space.setattr(w_exception, space.wrap("reason"),
+ space.wrap(reason_str) if reason_str else space.w_None)
+ space.setattr(w_exception, space.wrap("library"),
+ space.wrap(lib_str) if lib_str else space.w_None)
return OperationError(w_exception_class, w_exception)
class SSLNpnProtocols(object):
@@ -794,22 +816,25 @@
if ss is None:
errval = libssl_ERR_peek_last_error()
- errstr = rffi.charp2str(libssl_ERR_error_string(errval, None))
- return ssl_error(space, errstr, errval)
+ return ssl_error(space, None, errcode=errval)
elif ss.ssl:
err = libssl_SSL_get_error(ss.ssl, ret)
else:
err = SSL_ERROR_SSL
+ w_errtype = None
errstr = ""
errval = 0
if err == SSL_ERROR_ZERO_RETURN:
+ w_errtype = get_exception_class(space, 'w_sslzeroreturnerror')
errstr = "TLS/SSL connection has been closed"
errval = PY_SSL_ERROR_ZERO_RETURN
elif err == SSL_ERROR_WANT_READ:
+ w_errtype = get_exception_class(space, 'w_sslwantreaderror')
errstr = "The operation did not complete (read)"
errval = PY_SSL_ERROR_WANT_READ
elif err == SSL_ERROR_WANT_WRITE:
+ w_errtype = get_exception_class(space, 'w_sslwantwriteerror')
errstr = "The operation did not complete (write)"
errval = PY_SSL_ERROR_WANT_WRITE
elif err == SSL_ERROR_WANT_X509_LOOKUP:
@@ -829,6 +854,7 @@
error = rsocket.last_error()
return interp_socket.converted_error(space, error)
else:
+ w_errtype = get_exception_class(space, 'w_sslsyscallerror')
errstr = "Some I/O error occurred"
errval = PY_SSL_ERROR_SYSCALL
else:
@@ -845,7 +871,13 @@
errstr = "Invalid error code"
errval = PY_SSL_ERROR_INVALID_ERROR_CODE
- return ssl_error(space, errstr, errval)
+ return ssl_error(space, errstr, errval, w_errtype=w_errtype)
+
+def SSLError_descr_str(space, w_exc):
+ w_strerror = space.getattr(w_exc, space.wrap("strerror"))
+ if not space.is_none(w_strerror):
+ return w_strerror
+ return space.str(space.getattr(w_exc, space.wrap("args")))
class Cache:
@@ -853,6 +885,8 @@
w_socketerror = interp_socket.get_error(space, "error")
self.w_sslerror = space.new_exception_class(
"_ssl.SSLError", w_socketerror)
+ space.setattr(self.w_sslerror, space.wrap('__str__'),
+ space.wrap(interp2app(SSLError_descr_str)))
self.w_sslzeroreturnerror = space.new_exception_class(
"_ssl.SSLZeroReturnError", self.w_sslerror)
self.w_sslwantreaderror = space.new_exception_class(
@@ -865,6 +899,7 @@
"_ssl.SSLEOFError", self.w_sslerror)
+ at specialize.memo()
def get_exception_class(space, name):
return getattr(space.fromcache(Cache), name)
diff --git a/pypy/module/_ssl/test/test_ssl.py b/pypy/module/_ssl/test/test_ssl.py
--- a/pypy/module/_ssl/test/test_ssl.py
+++ b/pypy/module/_ssl/test/test_ssl.py
@@ -301,6 +301,61 @@
raises(TypeError, ctx.load_dh_params, None)
raises(_ssl.SSLError, ctx.load_dh_params, self.keycert)
+
+class AppTestSSLError:
+ spaceconfig = dict(usemodules=('_ssl', '_socket', 'thread'))
+
+ def setup_class(cls):
+ tmpfile = udir / "tmpfile.pem"
+ tmpfile.write(SSL_CERTIFICATE + SSL_PRIVATE_KEY)
+ cls.w_keycert = cls.space.wrap(str(tmpfile))
+
+ def test_str(self):
+ # The str() of a SSLError doesn't include the errno
+ import _ssl
+ e = _ssl.SSLError(1, "foo")
+ assert str(e) == "foo"
+ assert e.errno == 1
+ # Same for a subclass
+ e = _ssl.SSLZeroReturnError(1, "foo")
+ assert str(e) == "foo"
+ assert e.errno == 1
+
+ def test_lib_reason(self):
+ # Test the library and reason attributes
+ import _ssl
+ ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1)
+ exc = raises(_ssl.SSLError, ctx.load_dh_params, self.keycert)
+ assert exc.value.library == 'PEM'
+ assert exc.value.reason == 'NO_START_LINE'
+ s = str(exc.value)
+ assert s.startswith("[PEM: NO_START_LINE] no start line")
+
+ def test_subclass(self):
+ # Check that the appropriate SSLError subclass is raised
+ # (this only tests one of them)
+ import _ssl, _socket
+ ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1)
+ s = _socket.socket()
+ try:
+ s.bind(("127.0.0.1", 0))
+ s.listen(5)
+ c = _socket.socket()
+ c.connect(s.getsockname())
+ c.setblocking(False)
+
+ c = ctx._wrap_socket(c, False)
+ try:
+ exc = raises(_ssl.SSLWantReadError, c.do_handshake)
+ msg= str(exc.value)
+ assert msg.startswith("The operation did not complete (read)")
+ # For compatibility
+ assert exc.value.errno == _ssl.SSL_ERROR_WANT_READ
+ finally:
+ c.shutdown()
+ finally:
+ s.close()
+
SSL_CERTIFICATE = """
-----BEGIN CERTIFICATE-----
MIICVDCCAb2gAwIBAgIJANfHOBkZr8JOMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNV
diff --git a/rpython/rlib/ropenssl.py b/rpython/rlib/ropenssl.py
--- a/rpython/rlib/ropenssl.py
+++ b/rpython/rlib/ropenssl.py
@@ -337,6 +337,7 @@
ssl_external('ERR_get_error', [], rffi.INT)
ssl_external('ERR_peek_last_error', [], rffi.INT)
ssl_external('ERR_error_string', [rffi.ULONG, rffi.CCHARP], rffi.CCHARP)
+ssl_external('ERR_reason_error_string', [rffi.ULONG], rffi.CCHARP)
ssl_external('ERR_clear_error', [], lltype.Void)
ssl_external('ERR_GET_LIB', [rffi.ULONG], rffi.INT, macro=True)
ssl_external('ERR_GET_REASON', [rffi.ULONG], rffi.INT, macro=True)
More information about the pypy-commit
mailing list