From 5d72de3d03ffa4687bd13e90afe3056faa57afda Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Tue, 24 Nov 2009 12:59:42 -0500 Subject: [PATCH] Moved GreenSSLObject into eventlet.green.socket, cleaned up imports slightly, removed PyOpenSSL dependency -- it will warn you if you need to install it. --- eventlet/green/socket.py | 76 +++++++++++++++++++++++++++++++++++++--- eventlet/green/ssl.py | 3 +- eventlet/greenio.py | 66 ---------------------------------- eventlet/util.py | 38 ++++++-------------- setup.py | 3 +- tests/greenio_test.py | 8 +++-- 6 files changed, 91 insertions(+), 103 deletions(-) diff --git a/eventlet/green/socket.py b/eventlet/green/socket.py index 87966c5..1b8b535 100644 --- a/eventlet/green/socket.py +++ b/eventlet/green/socket.py @@ -9,10 +9,8 @@ except AttributeError: pass from eventlet.api import get_hub -from eventlet.util import wrap_ssl_obj from eventlet.greenio import GreenSocket as socket -from eventlet.greenio import GreenSSL as _GreenSSL -from eventlet.greenio import GreenSSLObject as _GreenSSLObject +from eventlet.greenio import SSL as _SSL def fromfd(*args): return socket(__socket.fromfd(*args)) @@ -78,5 +76,73 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT): raise error, msg -def ssl(sock, certificate=None, private_key=None): - return wrap_ssl_obj(sock, certificate, private_key) +def _convert_to_sslerror(ex): + """ Transliterates SSL.SysCallErrors to socket.sslerrors""" + return socket.sslerror((ex[0], ex[1])) + + +class GreenSSLObject(object): + """ Wrapper object around the SSLObjects returned by socket.ssl, which have a + slightly different interface from SSL.Connection objects. """ + def __init__(self, green_ssl_obj): + """ Should only be called by a 'green' socket.ssl """ + self.connection = green_ssl_obj + try: + # if it's already connected, do the handshake + self.connection.getpeername() + except: + pass + else: + try: + self.connection.do_handshake() + except _SSL.SysCallError, e: + raise _convert_to_sslerror(e) + + def read(self, n=None): + """If n is provided, read n bytes from the SSL connection, otherwise read + until EOF. The return value is a string of the bytes read.""" + if n is None: + # don't support this until someone needs it + raise NotImplementedError("GreenSSLObject does not support "\ + " unlimited reads until we hear of someone needing to use them.") + else: + try: + return self.connection.read(n) + except _SSL.ZeroReturnError: + return '' + except _SSL.SysCallError, e: + raise _convert_to_sslerror(e) + + def write(self, s): + """Writes the string s to the on the object's SSL connection. + The return value is the number of bytes written. """ + try: + return self.connection.write(s) + except _SSL.SysCallError, e: + raise _convert_to_sslerror(e) + + def server(self): + """ Returns a string describing the server's certificate. Useful for debugging + purposes; do not parse the content of this string because its format can't be + parsed unambiguously. """ + return str(self.connection.get_peer_certificate().get_subject()) + + def issuer(self): + """Returns a string describing the issuer of the server's certificate. Useful + for debugging purposes; do not parse the content of this string because its + format can't be parsed unambiguously.""" + return str(self.connection.get_peer_certificate().get_issuer()) + + +try: + from eventlet.green import ssl + def ssl(sock, certificate=None, private_key=None): + warnings.warn("socket.ssl() is deprecated. Use ssl.wrap_socket() instead.", + DeprecationWarning, stacklevel=2) + return ssl.sslwrap_simple(sock, keyfile, certfile) +except ImportError: + def ssl(sock, certificate=None, private_key=None): + from eventlet import util + wrapped = util.wrap_ssl(sock, certificate, private_key) + return GreenSSLObject(wrapped) + diff --git a/eventlet/green/ssl.py b/eventlet/green/ssl.py index 969c564..f5a2a61 100644 --- a/eventlet/green/ssl.py +++ b/eventlet/green/ssl.py @@ -8,7 +8,7 @@ import time from eventlet.api import trampoline, getcurrent from thread import get_ident -from eventlet.greenio import set_nonblocking, GreenSocket, GreenSSLObject, SOCKET_CLOSED, CONNECT_ERR, CONNECT_SUCCESS +from eventlet.greenio import set_nonblocking, GreenSocket, SOCKET_CLOSED, CONNECT_ERR, CONNECT_SUCCESS orig_socket = __import__('socket') socket = orig_socket.socket @@ -290,6 +290,7 @@ def sslwrap_simple(sock, keyfile=None, certfile=None): """A replacement for the old socket.ssl function. Designed for compability with Python 2.5 and earlier. Will disappear in Python 3.0.""" + from eventlet.green.socket import GreenSSLObject ssl_sock = GreenSSLSocket(sock, 0, keyfile, certfile, CERT_NONE, PROTOCOL_SSLv23, None) return GreenSSLObject(ssl_sock) diff --git a/eventlet/greenio.py b/eventlet/greenio.py index 27858eb..71eab06 100644 --- a/eventlet/greenio.py +++ b/eventlet/greenio.py @@ -1,6 +1,4 @@ from eventlet.api import trampoline, get_hub -from eventlet import util - BUFFER_SIZE = 4096 @@ -712,67 +710,3 @@ def shutdown_safe(sock): if e[0] != errno.ENOTCONN: raise - -def _convert_to_sslerror(ex): - """ Transliterates SSL.SysCallErrors to socket.sslerrors""" - return socket.sslerror((ex[0], ex[1])) - - -class GreenSSLObject(object): - """ Wrapper object around the SSLObjects returned by socket.ssl, which have a - slightly different interface from SSL.Connection objects. """ - def __init__(self, green_ssl_obj): - """ Should only be called by a 'green' socket.ssl """ - try: - from eventlet.green.ssl import GreenSSLSocket - except ImportError: - class GreenSSLSocket(object): - pass - - assert isinstance(green_ssl_obj, (GreenSSL, GreenSSLSocket)) - self.connection = green_ssl_obj - try: - # if it's already connected, do the handshake - self.connection.getpeername() - except: - pass - else: - try: - self.connection.do_handshake() - except SSL.SysCallError, e: - raise _convert_to_sslerror(e) - - def read(self, n=None): - """If n is provided, read n bytes from the SSL connection, otherwise read - until EOF. The return value is a string of the bytes read.""" - if n is None: - # don't support this until someone needs it - raise NotImplementedError("GreenSSLObject does not support "\ - " unlimited reads until we hear of someone needing to use them.") - else: - try: - return self.connection.read(n) - except SSL.ZeroReturnError: - return '' - except SSL.SysCallError, e: - raise _convert_to_sslerror(e) - - def write(self, s): - """Writes the string s to the on the object's SSL connection. - The return value is the number of bytes written. """ - try: - return self.connection.write(s) - except SSL.SysCallError, e: - raise _convert_to_sslerror(e) - - def server(self): - """ Returns a string describing the server's certificate. Useful for debugging - purposes; do not parse the content of this string because its format can't be - parsed unambiguously. """ - return str(self.connection.get_peer_certificate().get_subject()) - - def issuer(self): - """Returns a string describing the issuer of the server's certificate. Useful - for debugging purposes; do not parse the content of this string because its - format can't be parsed unambiguously.""" - return str(self.connection.get_peer_certificate().get_issuer()) diff --git a/eventlet/util.py b/eventlet/util.py index 86d34d1..1070fe6 100644 --- a/eventlet/util.py +++ b/eventlet/util.py @@ -3,6 +3,8 @@ import select import socket import errno +from eventlet import greenio + def g_log(*args): import sys from eventlet.support import greenlets as greenlet @@ -37,27 +39,21 @@ def tcp_socket(): try: # if ssl is available, use eventlet.green.ssl for our ssl implementation - import ssl as _ssl + from eventlet.green import ssl def wrap_ssl(sock, certificate=None, private_key=None, server_side=False): - from eventlet.green import ssl return ssl.wrap_socket(sock, keyfile=private_key, certfile=certificate, server_side=server_side, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23, ca_certs=None, do_handshake_on_connect=True, - suppress_ragged_eofs=True) - - def wrap_ssl_obj(sock, certificate=None, private_key=None): - from eventlet import ssl - warnings.warn("socket.ssl() is deprecated. Use ssl.wrap_socket() instead.", - DeprecationWarning, stacklevel=2) - return ssl.sslwrap_simple(sock, keyfile, certfile) - + suppress_ragged_eofs=True) except ImportError: # if ssl is not available, use PyOpenSSL def wrap_ssl(sock, certificate=None, private_key=None, server_side=False): - from OpenSSL import SSL - from eventlet import greenio + try: + from OpenSSL import SSL + except ImportError: + raise ImportError("To use SSL with Eventlet, you must install PyOpenSSL or use Python 2.6 or later.") context = SSL.Context(SSL.SSLv23_METHOD) if certificate is not None: context.use_certificate_file(certificate) @@ -71,13 +67,6 @@ except ImportError: else: connection.set_connect_state() return greenio.GreenSSL(connection) - - def wrap_ssl_obj(sock, certificate=None, private_key=None): - """ For 100% compatibility with the socket module, this wraps and handshakes an - open connection, returning a SSLObject.""" - from eventlet import greenio - wrapped = wrap_ssl(sock, certificate, private_key) - return greenio.GreenSSLObject(wrapped) socket_already_wrapped = False def wrap_socket_with_coroutine_socket(use_thread_pool=True): @@ -85,12 +74,9 @@ def wrap_socket_with_coroutine_socket(use_thread_pool=True): if socket_already_wrapped: return - def new_socket(*args, **kw): - from eventlet import greenio - return greenio.GreenSocket(__original_socket__(*args, **kw)) - socket.socket = new_socket - - socket.ssl = wrap_ssl_obj + import eventlet.green.socket + socket.socket = eventlet.green.socket.socket + socket.ssl = eventlet.green.socket.ssl try: import ssl as _ssl from eventlet.green import ssl @@ -115,7 +101,6 @@ def wrap_socket_with_coroutine_socket(use_thread_pool=True): if __original_fromfd__ is not None: def new_fromfd(*args, **kw): - from eventlet import greenio return greenio.GreenSocket(__original_fromfd__(*args, **kw)) socket.fromfd = new_fromfd @@ -136,7 +121,6 @@ def wrap_pipes_with_coroutine_pipes(): if pipes_already_wrapped: return def new_fdopen(*args, **kw): - from eventlet import greenio return greenio.GreenPipe(__original_fdopen__(*args, **kw)) def new_read(fd, *args, **kw): from eventlet import api diff --git a/setup.py b/setup.py index d876a9b..2f69fe4 100644 --- a/setup.py +++ b/setup.py @@ -6,8 +6,7 @@ from eventlet import __version__ import sys requirements = [] -for flag, req in [('--without-greenlet','greenlet >= 0.2'), - ('--without-pyopenssl', 'pyopenssl')]: +for flag, req in [('--without-greenlet','greenlet >= 0.2')]: if flag in sys.argv: sys.argv.remove(flag) else: diff --git a/tests/greenio_test.py b/tests/greenio_test.py index 38777c3..1476129 100644 --- a/tests/greenio_test.py +++ b/tests/greenio_test.py @@ -1,6 +1,7 @@ from tests import skipped, LimitedTestCase, skip_with_libevent, TestIsTakingTooLong from unittest import main from eventlet import api, util, coros, proc, greenio +from eventlet.green.socket import GreenSSLObject import os import socket import sys @@ -282,7 +283,7 @@ class SSLTest(LimitedTestCase): self.private_key_file) killer = api.spawn(serve, listener) client = util.wrap_ssl(api.connect_tcp(('localhost', listener.getsockname()[1]))) - client = greenio.GreenSSLObject(client) + client = GreenSSLObject(client) self.assertEquals(client.read(1024), 'content') self.assertEquals(client.read(1024), '') @@ -290,7 +291,10 @@ class SSLTest(LimitedTestCase): def serve(listener): sock, addr = listener.accept() stuff = sock.read(8192) - empt = sock.read(8192) + try: + self.assertEquals("", sock.read(8192)) + except greenio.SSL.ZeroReturnError: + pass sock = api.ssl_listener(('127.0.0.1', 0), self.certificate_file, self.private_key_file) server_coro = coros.execute(serve, sock)