1 __ssl = __import__('ssl')
3 from eventlet.patcher import slurp_properties
4 slurp_properties(__ssl, globals(), srckeys=dir(__ssl))
9 time = __import__('time')
11 from eventlet.support import get_errno, PY33, six
12 from eventlet.hubs import trampoline, IOClosed
13 from eventlet.greenio import (
14 set_nonblocking, GreenSocket, SOCKET_CLOSED, CONNECT_ERR, CONNECT_SUCCESS,
16 orig_socket = __import__('socket')
17 socket = orig_socket.socket
18 if sys.version_info >= (2, 7):
20 timeout_exc = SSLError
23 timeout_exc = orig_socket.timeout
26 'SSLSocket', 'SSLContext', 'wrap_socket', 'sslwrap_simple',
27 'create_default_context', '_create_default_https_context']
29 _original_sslsocket = __ssl.SSLSocket
32 class GreenSSLSocket(_original_sslsocket):
33 """ This is a green version of the SSLSocket class from the ssl module added
34 in 2.6. For documentation on it, please see the Python standard
37 Python nonblocking ssl objects don't give errors when the other end
38 of the socket is closed (they do notice when the other end is shutdown,
39 though). Any write/read operations will simply hang if the socket is
40 closed from the other end. There is no obvious fix for this problem;
41 it appears to be a limitation of Python's ssl object implementation.
42 A workaround is to set a reasonable timeout on the socket using
43 settimeout(), and to close/reopen the connection when a timeout
44 occurs at an unexpected juncture in the code.
46 # we are inheriting from SSLSocket because its constructor calls
47 # do_handshake whose behavior we wish to override
49 def __init__(self, sock, keyfile=None, certfile=None,
50 server_side=False, cert_reqs=CERT_NONE,
51 ssl_version=PROTOCOL_SSLv23, ca_certs=None,
52 do_handshake_on_connect=True, *args, **kw):
53 if not isinstance(sock, GreenSocket):
54 sock = GreenSocket(sock)
56 self.act_non_blocking = sock.act_non_blocking
59 # On Python 2 SSLSocket constructor queries the timeout, it'd break without
61 self._timeout = sock.gettimeout()
63 # nonblocking socket handshaking on connect got disabled so let's pretend it's disabled
65 super(GreenSSLSocket, self).__init__(
66 sock.fd, keyfile, certfile, server_side, cert_reqs, ssl_version,
67 ca_certs, do_handshake_on_connect and six.PY2, *args, **kw)
69 # the superclass initializer trashes the methods so we remove
70 # the local-object versions of them and let the actual class
71 # methods shine through
72 # Note: This for Python 2
74 for fn in orig_socket._delegate_methods:
76 except AttributeError:
80 # Python 3 SSLSocket construction process overwrites the timeout so restore it
81 self._timeout = sock.gettimeout()
83 # it also sets timeout to None internally apparently (tested with 3.4.2)
84 _original_sslsocket.settimeout(self, 0.0)
85 assert _original_sslsocket.gettimeout(self) == 0.0
87 # see note above about handshaking
88 self.do_handshake_on_connect = do_handshake_on_connect
89 if do_handshake_on_connect and self._connected:
92 def settimeout(self, timeout):
93 self._timeout = timeout
98 def setblocking(self, flag):
100 self.act_non_blocking = False
103 self.act_non_blocking = True
106 def _call_trampolining(self, func, *a, **kw):
107 if self.act_non_blocking:
108 return func(*a, **kw)
112 return func(*a, **kw)
113 except SSLError as exc:
114 if get_errno(exc) == SSL_ERROR_WANT_READ:
117 timeout=self.gettimeout(),
118 timeout_exc=timeout_exc('timed out'))
119 elif get_errno(exc) == SSL_ERROR_WANT_WRITE:
122 timeout=self.gettimeout(),
123 timeout_exc=timeout_exc('timed out'))
127 def write(self, data):
128 """Write DATA to the underlying SSL channel. Returns
129 number of bytes of DATA actually transmitted."""
130 return self._call_trampolining(
131 super(GreenSSLSocket, self).write, data)
133 def read(self, *args, **kwargs):
134 """Read up to LEN bytes and return them.
135 Return zero-length string on EOF."""
137 return self._call_trampolining(
138 super(GreenSSLSocket, self).read, *args, **kwargs)
142 def send(self, data, flags=0):
144 return self._call_trampolining(
145 super(GreenSSLSocket, self).send, data, flags)
147 trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
148 return socket.send(self, data, flags)
150 def sendto(self, data, addr, flags=0):
151 # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
153 raise ValueError("sendto not allowed on instances of %s" %
156 trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
157 return socket.sendto(self, data, addr, flags)
159 def sendall(self, data, flags=0):
160 # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
164 "non-zero flags not allowed in calls to sendall() on %s" %
169 while (count < amount):
170 v = self.send(data_to_send)
173 trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
175 data_to_send = data[count:]
180 return socket.sendall(self, data, flags)
181 except orig_socket.error as e:
182 if self.act_non_blocking:
184 if get_errno(e) == errno.EWOULDBLOCK:
185 trampoline(self, write=True,
186 timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
187 if get_errno(e) in SOCKET_CLOSED:
191 def recv(self, buflen=1024, flags=0):
192 # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
196 "non-zero flags not allowed in calls to recv() on %s" %
198 read = self.read(buflen)
203 return socket.recv(self, buflen, flags)
204 except orig_socket.error as e:
205 if self.act_non_blocking:
207 if get_errno(e) == errno.EWOULDBLOCK:
211 timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
214 if get_errno(e) in SOCKET_CLOSED:
218 def recv_into(self, buffer, nbytes=None, flags=0):
219 if not self.act_non_blocking:
220 trampoline(self, read=True, timeout=self.gettimeout(),
221 timeout_exc=timeout_exc('timed out'))
222 return super(GreenSSLSocket, self).recv_into(buffer, nbytes, flags)
224 def recvfrom(self, addr, buflen=1024, flags=0):
225 if not self.act_non_blocking:
226 trampoline(self, read=True, timeout=self.gettimeout(),
227 timeout_exc=timeout_exc('timed out'))
228 return super(GreenSSLSocket, self).recvfrom(addr, buflen, flags)
230 def recvfrom_into(self, buffer, nbytes=None, flags=0):
231 if not self.act_non_blocking:
232 trampoline(self, read=True, timeout=self.gettimeout(),
233 timeout_exc=timeout_exc('timed out'))
234 return super(GreenSSLSocket, self).recvfrom_into(buffer, nbytes, flags)
237 return GreenSocket(self._call_trampolining(
238 super(GreenSSLSocket, self).unwrap))
240 def do_handshake(self):
241 """Perform a TLS/SSL handshake."""
242 return self._call_trampolining(
243 super(GreenSSLSocket, self).do_handshake)
245 def _socket_connect(self, addr):
246 real_connect = socket.connect
247 if self.act_non_blocking:
248 return real_connect(self, addr)
250 # *NOTE: gross, copied code from greenio because it's not factored
251 # well enough to reuse
252 if self.gettimeout() is None:
255 return real_connect(self, addr)
256 except orig_socket.error as exc:
257 if get_errno(exc) in CONNECT_ERR:
258 trampoline(self, write=True)
259 elif get_errno(exc) in CONNECT_SUCCESS:
264 end = time.time() + self.gettimeout()
267 real_connect(self, addr)
268 except orig_socket.error as exc:
269 if get_errno(exc) in CONNECT_ERR:
272 timeout=end - time.time(), timeout_exc=timeout_exc('timed out'))
273 elif get_errno(exc) in CONNECT_SUCCESS:
277 if time.time() >= end:
278 raise timeout_exc('timed out')
280 def connect(self, addr):
281 """Connects to remote ADDR, and then wraps the connection in
283 # *NOTE: grrrrr copied this code from ssl.py because of the reference
284 # to socket.connect which we don't want to call directly
286 raise ValueError("attempt to connect already-connected SSLSocket!")
287 self._socket_connect(addr)
290 sslwrap = _ssl.sslwrap
291 except AttributeError:
292 # sslwrap was removed in 3.x and later in 2.7.9
294 sslobj = self._context._wrap_socket(self._sock, server_side, ssl_sock=self)
296 context = self.context if PY33 else self._context
297 sslobj = context._wrap_socket(self, server_side)
299 sslobj = sslwrap(self._sock, server_side, self.keyfile, self.certfile,
300 self.cert_reqs, self.ssl_version,
301 self.ca_certs, *([self.ciphers] if has_ciphers else []))
303 self._sslobj = sslobj
304 if self.do_handshake_on_connect:
308 """Accepts a new connection from a remote client, and returns
309 a tuple containing that new connection wrapped with a server-side
310 SSL channel, and the address of the remote client."""
311 # RDW grr duplication of code from greenio
312 if self.act_non_blocking:
313 newsock, addr = socket.accept(self)
317 newsock, addr = socket.accept(self)
318 set_nonblocking(newsock)
320 except orig_socket.error as e:
321 if get_errno(e) != errno.EWOULDBLOCK:
323 trampoline(self, read=True, timeout=self.gettimeout(),
324 timeout_exc=timeout_exc('timed out'))
326 new_ssl = type(self)(
328 keyfile=self.keyfile,
329 certfile=self.certfile,
331 cert_reqs=self.cert_reqs,
332 ssl_version=self.ssl_version,
333 ca_certs=self.ca_certs,
334 do_handshake_on_connect=self.do_handshake_on_connect,
335 suppress_ragged_eofs=self.suppress_ragged_eofs)
336 return (new_ssl, addr)
339 raise NotImplementedError("Can't dup an ssl object")
341 SSLSocket = GreenSSLSocket
344 def wrap_socket(sock, *a, **kw):
345 return GreenSSLSocket(sock, *a, **kw)
348 if hasattr(__ssl, 'sslwrap_simple'):
349 def sslwrap_simple(sock, keyfile=None, certfile=None):
350 """A replacement for the old socket.ssl function. Designed
351 for compatibility with Python 2.5 and earlier. Will disappear in
353 ssl_sock = GreenSSLSocket(sock, keyfile=keyfile, certfile=certfile,
356 ssl_version=PROTOCOL_SSLv23,
361 if hasattr(__ssl, 'SSLContext'):
362 _original_sslcontext = __ssl.SSLContext
364 class GreenSSLContext(_original_sslcontext):
367 def wrap_socket(self, sock, *a, **kw):
368 return GreenSSLSocket(sock, *a, _context=self, **kw)
370 SSLContext = GreenSSLContext
372 if hasattr(__ssl, 'create_default_context'):
373 _original_create_default_context = __ssl.create_default_context
375 def green_create_default_context(*a, **kw):
376 # We can't just monkey-patch on the green version of `wrap_socket`
377 # on to SSLContext instances, but SSLContext.create_default_context
378 # does a bunch of work. Rather than re-implementing it all, just
379 # switch out the __class__ to get our `wrap_socket` implementation
380 context = _original_create_default_context(*a, **kw)
381 context.__class__ = GreenSSLContext
384 create_default_context = green_create_default_context
385 _create_default_https_context = green_create_default_context