Skip to content

Commit a915bb6

Browse files
luanjunyijstasiak
authored andcommitted
Fix compatibility with Python 3.7 ssl.SSLSocket (#531)
1 parent 05d613d commit a915bb6

File tree

2 files changed

+52
-38
lines changed

2 files changed

+52
-38
lines changed

eventlet/green/ssl.py

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,16 @@
33
from eventlet.patcher import slurp_properties
44
slurp_properties(__ssl, globals(), srckeys=dir(__ssl))
55

6-
import errno
7-
import functools
86
import sys
9-
107
from eventlet import greenio, hubs
118
from eventlet.greenio import (
129
set_nonblocking, GreenSocket, CONNECT_ERR, CONNECT_SUCCESS,
1310
)
1411
from eventlet.hubs import trampoline, IOClosed
1512
from eventlet.support import get_errno, PY33
1613
import six
14+
from contextlib import contextmanager
15+
1716
orig_socket = __import__('socket')
1817
socket = orig_socket.socket
1918
timeout_exc = SSLError
@@ -24,6 +23,21 @@
2423

2524
_original_sslsocket = __ssl.SSLSocket
2625
_original_wrap_socket = __ssl.wrap_socket
26+
_original_sslcontext = getattr(__ssl, 'SSLContext', None)
27+
_is_under_py_3_7 = sys.version_info < (3, 7)
28+
29+
30+
@contextmanager
31+
def _original_ssl_context(*args, **kwargs):
32+
tmp_sslcontext = _original_wrap_socket.__globals__.get('SSLContext', None)
33+
tmp_sslsocket = _original_sslsocket._create.__globals__.get('SSLSocket', None)
34+
_original_sslsocket._create.__globals__['SSLSocket'] = _original_sslsocket
35+
_original_wrap_socket.__globals__['SSLContext'] = _original_sslcontext
36+
try:
37+
yield
38+
finally:
39+
_original_wrap_socket.__globals__['SSLContext'] = tmp_sslcontext
40+
_original_sslsocket._create.__globals__['SSLSocket'] = tmp_sslsocket
2741

2842

2943
class GreenSSLSocket(_original_sslsocket):
@@ -40,59 +54,56 @@ class GreenSSLSocket(_original_sslsocket):
4054
settimeout(), and to close/reopen the connection when a timeout
4155
occurs at an unexpected juncture in the code.
4256
"""
57+
def __new__(cls, sock=None, keyfile=None, certfile=None,
58+
server_side=False, cert_reqs=CERT_NONE,
59+
ssl_version=PROTOCOL_SSLv23, ca_certs=None,
60+
do_handshake_on_connect=True, *args, **kw):
61+
if _is_under_py_3_7:
62+
return super(GreenSSLSocket, cls).__new__(cls)
63+
else:
64+
if not isinstance(sock, GreenSocket):
65+
sock = GreenSocket(sock)
66+
with _original_ssl_context():
67+
ret = _original_wrap_socket(
68+
sock=sock.fd,
69+
keyfile=keyfile,
70+
certfile=certfile,
71+
server_side=server_side,
72+
cert_reqs=cert_reqs,
73+
ssl_version=ssl_version,
74+
ca_certs=ca_certs,
75+
do_handshake_on_connect=False,
76+
*args, **kw
77+
)
78+
ret.keyfile = keyfile
79+
ret.certfile = certfile
80+
ret.cert_reqs = cert_reqs
81+
ret.ssl_version = ssl_version
82+
ret.ca_certs = ca_certs
83+
ret.__class__ = GreenSSLSocket
84+
return ret
85+
4386
# we are inheriting from SSLSocket because its constructor calls
4487
# do_handshake whose behavior we wish to override
45-
4688
def __init__(self, sock, keyfile=None, certfile=None,
4789
server_side=False, cert_reqs=CERT_NONE,
4890
ssl_version=PROTOCOL_SSLv23, ca_certs=None,
4991
do_handshake_on_connect=True, *args, **kw):
5092
if not isinstance(sock, GreenSocket):
5193
sock = GreenSocket(sock)
52-
5394
self.act_non_blocking = sock.act_non_blocking
5495

5596
if six.PY2:
5697
# On Python 2 SSLSocket constructor queries the timeout, it'd break without
5798
# this assignment
5899
self._timeout = sock.gettimeout()
59100

60-
if sys.version_info >= (3, 7):
61-
# Monkey-patch the sslsocket so our modified self gets
62-
# injected into its _create method.
63-
def fake_new(self, cls, *args, **kwargs):
64-
return self
65-
66-
orig_new = _original_sslsocket.__new__
67-
try:
68-
_original_sslsocket.__new__ = fake_new.__get__(self, GreenSSLSocket)
69-
70-
self = _original_wrap_socket(
71-
sock=sock.fd,
72-
keyfile=keyfile,
73-
certfile=certfile,
74-
server_side=server_side,
75-
cert_reqs=cert_reqs,
76-
ssl_version=ssl_version,
77-
ca_certs=ca_certs,
78-
do_handshake_on_connect=False,
79-
*args, **kw
80-
)
81-
self.keyfile = keyfile
82-
self.certfile = certfile
83-
self.cert_reqs = cert_reqs
84-
self.ssl_version = ssl_version
85-
self.ca_certs = ca_certs
86-
finally:
87-
# Unpatch
88-
_original_sslsocket.__new__ = orig_new
89-
else:
101+
if _is_under_py_3_7:
90102
# nonblocking socket handshaking on connect got disabled so let's pretend it's disabled
91103
# even when it's on
92104
super(GreenSSLSocket, self).__init__(
93105
sock.fd, keyfile, certfile, server_side, cert_reqs, ssl_version,
94106
ca_certs, do_handshake_on_connect and six.PY2, *args, **kw)
95-
96107
# the superclass initializer trashes the methods so we remove
97108
# the local-object versions of them and let the actual class
98109
# methods shine through
@@ -354,7 +365,7 @@ def connect(self, addr):
354365
except NameError:
355366
self._sslobj = sslobj
356367
else:
357-
if sys.version_info < (3, 7):
368+
if _is_under_py_3_7:
358369
self._sslobj = SSLObject(sslobj, owner=self)
359370
else:
360371
self._sslobj = sslobj
@@ -396,6 +407,7 @@ def accept(self):
396407
def dup(self):
397408
raise NotImplementedError("Can't dup an ssl object")
398409

410+
399411
SSLSocket = GreenSSLSocket
400412

401413

eventlet/greenio/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ def __init__(self, family=socket.AF_INET, *args, **kwargs):
145145
except AttributeError:
146146
self._timeout = socket.getdefaulttimeout()
147147

148-
if should_set_nonblocking:
148+
# Filter fd.fileno() != -1 so that won't call set non-blocking on
149+
# closed socket
150+
if should_set_nonblocking and fd.fileno() != -1:
149151
set_nonblocking(fd)
150152
self.fd = fd
151153
# when client calls setblocking(0) or settimeout(0) the socket must

0 commit comments

Comments
 (0)