[pypy-commit] pypy py3.3: Fix exception handling in _ssl: it now use distinct Exception classes.
amauryfa
noreply at buildbot.pypy.org
Sun Jun 22 00:11:23 CEST 2014
Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: py3.3
Changeset: r72128:18dd3240202b
Date: 2014-06-22 00:07 +0200
http://bitbucket.org/pypy/pypy/changeset/18dd3240202b/
Log: Fix exception handling in _ssl: it now use distinct Exception
classes. + Small hacks until "import ssl" succeeds.
diff --git a/pypy/module/_ssl/__init__.py b/pypy/module/_ssl/__init__.py
--- a/pypy/module/_ssl/__init__.py
+++ b/pypy/module/_ssl/__init__.py
@@ -5,7 +5,12 @@
See the socket module for documentation."""
interpleveldefs = {
- 'SSLError': 'interp_ssl.get_error(space)',
+ 'SSLError': 'interp_ssl.get_error(space).w_error',
+ 'SSLZeroReturnError': 'interp_ssl.get_error(space).w_ZeroReturnError',
+ 'SSLWantReadError': 'interp_ssl.get_error(space).w_WantReadError',
+ 'SSLWantWriteError': 'interp_ssl.get_error(space).w_WantWriteError',
+ 'SSLEOFError': 'interp_ssl.get_error(space).w_EOFError',
+ 'SSLSyscallError': 'interp_ssl.get_error(space).w_SyscallError',
'_SSLSocket': 'interp_ssl.SSLSocket',
'_SSLContext': 'interp_ssl.SSLContext',
'_test_decode_cert': 'interp_ssl._test_decode_cert',
@@ -24,6 +29,8 @@
if HAVE_OPENSSL_RAND:
Module.interpleveldefs['RAND_add'] = "interp_ssl.RAND_add"
+ Module.interpleveldefs['RAND_bytes'] = "space.w_None" # so far
+ Module.interpleveldefs['RAND_pseudo_bytes'] = "space.w_None" # so far
Module.interpleveldefs['RAND_status'] = "interp_ssl.RAND_status"
Module.interpleveldefs['RAND_egd'] = "interp_ssl.RAND_egd"
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
@@ -11,6 +11,7 @@
from rpython.rlib.rposix import get_errno, set_errno
from pypy.module._socket import interp_socket
+from pypy.module.exceptions import interp_exceptions
import weakref
@@ -62,10 +63,15 @@
# protocol options
constants["OP_ALL"] = SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+constants["OP_CIPHER_SERVER_PREFERENCE"] = SSL_OP_CIPHER_SERVER_PREFERENCE
+constants["OP_SINGLE_DH_USE"] = SSL_OP_SINGLE_DH_USE
constants["OP_NO_SSLv2"] = SSL_OP_NO_SSLv2
constants["OP_NO_SSLv3"] = SSL_OP_NO_SSLv3
constants["OP_NO_TLSv1"] = SSL_OP_NO_TLSv1
constants["HAS_SNI"] = HAS_SNI
+constants["HAS_ECDH"] = True # To break the test suite
+constants["HAS_NPN"] = True # To break the test suite
+constants["HAS_TLS_UNIQUE"] = True # To break the test suite
# OpenSSL version
def _parse_version(ver):
@@ -83,11 +89,12 @@
constants["OPENSSL_VERSION"] = SSLEAY_VERSION
constants["_OPENSSL_API_VERSION"] = _parse_version(libver)
-def ssl_error(space, msg, errno=0):
- w_exception_class = get_error(space)
- w_exception = space.call_function(w_exception_class,
+def ssl_error(space, msg, errno=0, w_errtype=None):
+ if w_errtype is None:
+ w_errtype = get_error(space).w_error
+ w_exception = space.call_function(w_errtype,
space.wrap(errno), space.wrap(msg))
- return OperationError(w_exception_class, w_exception)
+ return OperationError(w_errtype, w_exception)
class SSLContext(W_Root):
@@ -958,16 +965,20 @@
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_error(space).w_ZeroReturnError
errstr = "TLS/SSL connection has been closed"
errval = PY_SSL_ERROR_ZERO_RETURN
elif err == SSL_ERROR_WANT_READ:
+ w_errtype = get_error(space).w_WantReadError
errstr = "The operation did not complete (read)"
errval = PY_SSL_ERROR_WANT_READ
elif err == SSL_ERROR_WANT_WRITE:
+ w_errtype = get_error(space).w_WantWriteError
errstr = "The operation did not complete (write)"
errval = PY_SSL_ERROR_WANT_WRITE
elif err == SSL_ERROR_WANT_X509_LOOKUP:
@@ -980,6 +991,7 @@
e = libssl_ERR_get_error()
if e == 0:
if ret == 0 or ss.w_socket() is None:
+ w_errtype = get_error(space).w_EOFError
errstr = "EOF occurred in violation of protocol"
errval = PY_SSL_ERROR_EOF
elif ret == -1:
@@ -987,6 +999,7 @@
error = rsocket.last_error()
return interp_socket.converted_error(space, error)
else:
+ w_errtype = get_error(space).w_SyscallError
errstr = "Some I/O error occurred"
errval = PY_SSL_ERROR_SYSCALL
else:
@@ -1003,17 +1016,44 @@
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)
-class Cache:
+class W_Error(interp_exceptions.W_OSError):
+ "An error occurred in the SSL implementation."
+
+ def descr_str(self, space):
+ if space.isinstance_w(self.w_strerror, space.w_unicode):
+ return self.w_strerror
+ else:
+ return space.str(space.newtuple(self.args_w))
+
+W_Error.typedef = TypeDef(
+ "ssl.SSLError",
+ interp_exceptions.W_OSError.typedef,
+ __new__ = interp_exceptions._new(W_Error),
+ __doc__ = W_Error.__doc__,
+ __str__ = interp2app(W_Error.descr_str),
+ )
+
+
+class ErrorCache:
def __init__(self, space):
- w_socketerror = interp_socket.get_error(space, "error")
- self.w_error = space.new_exception_class(
- "_ssl.SSLError", w_socketerror)
+ self.w_error = space.gettypefor(W_Error)
+ self.w_ZeroReturnError = space.new_exception_class(
+ "ssl.SSLZeroReturnError", self.w_error)
+ self.w_WantReadError = space.new_exception_class(
+ "ssl.SSLWantReadError", self.w_error)
+ self.w_WantWriteError = space.new_exception_class(
+ "ssl.SSLWantWriteError", self.w_error)
+ self.w_EOFError = space.new_exception_class(
+ "ssl.SSLEOFError", self.w_error)
+ self.w_SyscallError = space.new_exception_class(
+ "ssl.SSLSyscallError", self.w_error)
def get_error(space):
- return space.fromcache(Cache).w_error
+ return space.fromcache(ErrorCache)
+
@unwrap_spec(filename=str, verbose=bool)
def _test_decode_cert(space, filename, verbose=True):
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
@@ -241,6 +241,37 @@
ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1)
assert _ssl.OP_ALL | _ssl.OP_NO_SSLv2 == ctx.options
+class AppTestSSLError:
+ spaceconfig = dict(usemodules=('_ssl', '_socket', 'binascii', 'thread'))
+
+ def test_str(self):
+ import _ssl
+ # The str() of a SSLError doesn't include the errno
+ 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_subclass(self):
+ import ssl
+ import socket
+ # Check that the appropriate SSLError subclass is raised
+ # (this only tests one of them)
+ ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+ with socket.socket() as s:
+ s.bind(("127.0.0.1", 0))
+ s.listen(5)
+ c = socket.socket()
+ c.connect(s.getsockname())
+ c.setblocking(False)
+ with ctx.wrap_socket(c, False, do_handshake_on_connect=False) as c:
+ exc = raises(ssl.SSLWantReadError, c.do_handshake)
+ assert str(exc.value).startswith("The operation did not complete (read)"), s
+ # For compatibility
+ assert exc.value.errno == ssl.SSL_ERROR_WANT_READ
SSL_CERTIFICATE = """
diff --git a/rpython/rlib/ropenssl.py b/rpython/rlib/ropenssl.py
--- a/rpython/rlib/ropenssl.py
+++ b/rpython/rlib/ropenssl.py
@@ -82,6 +82,10 @@
SSL_OP_NO_TLSv1 = rffi_platform.ConstantInteger("SSL_OP_NO_TLSv1")
SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = rffi_platform.ConstantInteger(
"SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS")
+ SSL_OP_CIPHER_SERVER_PREFERENCE = rffi_platform.ConstantInteger(
+ "SSL_OP_CIPHER_SERVER_PREFERENCE")
+ SSL_OP_SINGLE_DH_USE = rffi_platform.ConstantInteger(
+ "SSL_OP_SINGLE_DH_USE")
HAS_SNI = rffi_platform.Defined("SSL_CTRL_SET_TLSEXT_HOSTNAME")
SSL_VERIFY_NONE = rffi_platform.ConstantInteger("SSL_VERIFY_NONE")
SSL_VERIFY_PEER = rffi_platform.ConstantInteger("SSL_VERIFY_PEER")
More information about the pypy-commit
mailing list