diff --git a/eventlet/green/ssl.py b/eventlet/green/ssl.py index 81eae3d..828f9bf 100644 --- a/eventlet/green/ssl.py +++ b/eventlet/green/ssl.py @@ -3,16 +3,17 @@ __ssl = __import__('ssl') from eventlet.patcher import slurp_properties slurp_properties(__ssl, globals(), srckeys=dir(__ssl)) +import errno import functools import sys -import errno -time = __import__('time') +import time -from eventlet.support import get_errno, PY33, six -from eventlet.hubs import trampoline, IOClosed +from eventlet import greenio from eventlet.greenio import ( - set_nonblocking, GreenSocket, SOCKET_CLOSED, CONNECT_ERR, CONNECT_SUCCESS, + set_nonblocking, GreenSocket, CONNECT_ERR, CONNECT_SUCCESS, ) +from eventlet.hubs import trampoline, IOClosed +from eventlet.support import get_errno, PY33, six orig_socket = __import__('socket') socket = orig_socket.socket if sys.version_info >= (2, 7): @@ -181,10 +182,11 @@ class GreenSSLSocket(_original_sslsocket): except orig_socket.error as e: if self.act_non_blocking: raise - if get_errno(e) == errno.EWOULDBLOCK: + erno = get_errno(e) + if erno in greenio.SOCKET_BLOCKING: trampoline(self, write=True, timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out')) - if get_errno(e) in SOCKET_CLOSED: + elif erno in greenio.SOCKET_CLOSED: return '' raise @@ -204,14 +206,15 @@ class GreenSSLSocket(_original_sslsocket): except orig_socket.error as e: if self.act_non_blocking: raise - if get_errno(e) == errno.EWOULDBLOCK: + erno = get_errno(e) + if erno in greenio.SOCKET_BLOCKING: try: trampoline( self, read=True, timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out')) except IOClosed: return b'' - if get_errno(e) in SOCKET_CLOSED: + elif erno in greenio.SOCKET_CLOSED: return b'' raise @@ -318,7 +321,7 @@ class GreenSSLSocket(_original_sslsocket): set_nonblocking(newsock) break except orig_socket.error as e: - if get_errno(e) != errno.EWOULDBLOCK: + if get_errno(e) not in greenio.SOCKET_BLOCKING: raise trampoline(self, read=True, timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out')) @@ -331,7 +334,7 @@ class GreenSSLSocket(_original_sslsocket): cert_reqs=self.cert_reqs, ssl_version=self.ssl_version, ca_certs=self.ca_certs, - do_handshake_on_connect=self.do_handshake_on_connect, + do_handshake_on_connect=False, suppress_ragged_eofs=self.suppress_ragged_eofs) return (new_ssl, addr) diff --git a/eventlet/greenio/base.py b/eventlet/greenio/base.py index e1c4a45..60a8f29 100644 --- a/eventlet/greenio/base.py +++ b/eventlet/greenio/base.py @@ -11,7 +11,7 @@ from eventlet.support import get_errno, six __all__ = [ 'GreenSocket', '_GLOBAL_DEFAULT_TIMEOUT', 'set_nonblocking', - 'SOCKET_CLOSED', 'CONNECT_ERR', 'CONNECT_SUCCESS', + 'SOCKET_BLOCKING', 'SOCKET_CLOSED', 'CONNECT_ERR', 'CONNECT_SUCCESS', 'shutdown_safe', 'SSL', ] diff --git a/tests/ssl_test.py b/tests/ssl_test.py index 621ff47..13e090c 100644 --- a/tests/ssl_test.py +++ b/tests/ssl_test.py @@ -1,6 +1,5 @@ import socket import warnings -from unittest import main import eventlet from eventlet import greenio @@ -8,22 +7,23 @@ try: from eventlet.green import ssl except ImportError: pass -from tests import ( - LimitedTestCase, certificate_file, private_key_file, check_idle_cpu_usage, - skip_if_no_ssl -) +import tests -def listen_ssl_socket(address=('127.0.0.1', 0)): +def listen_ssl_socket(address=('localhost', 0), **kwargs): sock = ssl.wrap_socket( - socket.socket(), private_key_file, certificate_file, server_side=True) + socket.socket(), + tests.private_key_file, + tests.certificate_file, + server_side=True, + **kwargs + ) sock.bind(address) sock.listen(50) - return sock -class SSLTest(LimitedTestCase): +class SSLTest(tests.LimitedTestCase): def setUp(self): # disabling socket.ssl warnings because we're testing it here warnings.filterwarnings( @@ -33,30 +33,29 @@ class SSLTest(LimitedTestCase): super(SSLTest, self).setUp() - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_duplex_response(self): def serve(listener): sock, addr = listener.accept() - sock.read(8192) - sock.write(b'response') + sock.recv(8192) + sock.sendall(b'response') sock = listen_ssl_socket() server_coro = eventlet.spawn(serve, sock) - client = ssl.wrap_socket( - eventlet.connect(('127.0.0.1', sock.getsockname()[1]))) - client.write(b'line 1\r\nline 2\r\n\r\n') - self.assertEqual(client.read(8192), b'response') + client = ssl.wrap_socket(eventlet.connect(sock.getsockname())) + client.sendall(b'line 1\r\nline 2\r\n\r\n') + self.assertEqual(client.recv(8192), b'response') server_coro.wait() - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_ssl_close(self): def serve(listener): sock, addr = listener.accept() - sock.read(8192) + sock.recv(8192) try: - self.assertEqual(b"", sock.read(8192)) + self.assertEqual(b'', sock.recv(8192)) except greenio.SSL.ZeroReturnError: pass @@ -64,54 +63,54 @@ class SSLTest(LimitedTestCase): server_coro = eventlet.spawn(serve, sock) - raw_client = eventlet.connect(('127.0.0.1', sock.getsockname()[1])) + raw_client = eventlet.connect(sock.getsockname()) client = ssl.wrap_socket(raw_client) - client.write(b'X') + client.sendall(b'X') greenio.shutdown_safe(client) client.close() server_coro.wait() - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_ssl_connect(self): def serve(listener): sock, addr = listener.accept() - sock.read(8192) + sock.recv(8192) sock = listen_ssl_socket() server_coro = eventlet.spawn(serve, sock) raw_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssl_client = ssl.wrap_socket(raw_client) - ssl_client.connect(('127.0.0.1', sock.getsockname()[1])) - ssl_client.write(b'abc') + ssl_client.connect(sock.getsockname()) + ssl_client.sendall(b'abc') greenio.shutdown_safe(ssl_client) ssl_client.close() server_coro.wait() - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_ssl_unwrap(self): def serve(): sock, addr = listener.accept() self.assertEqual(sock.recv(6), b'before') - sock_ssl = ssl.wrap_socket(sock, private_key_file, certificate_file, + sock_ssl = ssl.wrap_socket(sock, tests.private_key_file, tests.certificate_file, server_side=True) sock_ssl.do_handshake() - self.assertEqual(sock_ssl.read(6), b'during') + self.assertEqual(sock_ssl.recv(6), b'during') sock2 = sock_ssl.unwrap() self.assertEqual(sock2.recv(5), b'after') sock2.close() listener = eventlet.listen(('127.0.0.1', 0)) server_coro = eventlet.spawn(serve) - client = eventlet.connect((listener.getsockname())) - client.send(b'before') + client = eventlet.connect(listener.getsockname()) + client.sendall(b'before') client_ssl = ssl.wrap_socket(client) client_ssl.do_handshake() - client_ssl.write(b'during') + client_ssl.sendall(b'during') client2 = client_ssl.unwrap() - client2.send(b'after') + client2.sendall(b'after') server_coro.wait() - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_sendall_cpu_usage(self): """SSL socket.sendall() busy loop @@ -132,8 +131,8 @@ class SSLTest(LimitedTestCase): def serve(listener): conn, _ = listener.accept() conn.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, BUFFER_SIZE) - self.assertEqual(conn.read(8), b'request') - conn.write(b'response') + self.assertEqual(conn.recv(8), b'request') + conn.sendall(b'response') stage_1.wait() conn.sendall(b'x' * SENDALL_SIZE) @@ -144,41 +143,73 @@ class SSLTest(LimitedTestCase): client_sock = eventlet.connect(server_sock.getsockname()) client_sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, BUFFER_SIZE) client = ssl.wrap_socket(client_sock) - client.write(b'request') - self.assertEqual(client.read(8), b'response') + client.sendall(b'request') + self.assertEqual(client.recv(8), b'response') stage_1.send() - check_idle_cpu_usage(0.2, 0.1) + tests.check_idle_cpu_usage(0.2, 0.1) server_coro.kill() - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_greensslobject(self): def serve(listener): sock, addr = listener.accept() - sock.write(b'content') + sock.sendall(b'content') greenio.shutdown_safe(sock) sock.close() - listener = listen_ssl_socket(('', 0)) + listener = listen_ssl_socket() eventlet.spawn(serve, listener) - client = ssl.wrap_socket( - eventlet.connect(('localhost', listener.getsockname()[1]))) - self.assertEqual(client.read(1024), b'content') - self.assertEqual(client.read(1024), b'') + client = ssl.wrap_socket(eventlet.connect(listener.getsockname())) + self.assertEqual(client.recv(1024), b'content') + self.assertEqual(client.recv(1024), b'') - @skip_if_no_ssl + @tests.skip_if_no_ssl def test_regression_gh_17(self): - def serve(listener): - sock, addr = listener.accept() + # https://github.com/eventlet/eventlet/issues/17 + # ssl wrapped but unconnected socket methods go special code path + # test that path at least for syntax/typo errors + sock = ssl.wrap_socket(socket.socket()) + sock.settimeout(0.01) + try: + sock.sendall(b'') + except ssl.SSLError as e: + assert 'timed out' in str(e) - # to simulate condition mentioned in GH-17 - sock._sslobj = None - sock.sendall(b'some data') - greenio.shutdown_safe(sock) - sock.close() + @tests.skip_if_no_ssl + def test_no_handshake_block_accept_loop(self): + listener = listen_ssl_socket() + listener.settimeout(0.3) - listener = listen_ssl_socket(('', 0)) - eventlet.spawn(serve, listener) - ssl.wrap_socket(eventlet.connect(('localhost', listener.getsockname()[1]))) + def serve(sock): + try: + name = sock.recv(8) + sock.sendall(b'hello ' + name) + except Exception: + # ignore evil clients + pass + finally: + greenio.shutdown_safe(sock) + sock.close() -if __name__ == '__main__': - main() + def accept_loop(): + while True: + try: + sock, _ = listener.accept() + except socket.error: + return + eventlet.spawn(serve, sock) + + loopt = eventlet.spawn(accept_loop) + + # evil no handshake + evil = eventlet.connect(listener.getsockname()) + good = ssl.wrap_socket(eventlet.connect(listener.getsockname())) + good.sendall(b'good') + response = good.recv(16) + good.close() + assert response == b'hello good' + evil.close() + + listener.close() + loopt.wait() + eventlet.sleep(0)