1 __ssl = __import__('ssl')
3 from eventlet.patcher import slurp_properties
4 slurp_properties(__ssl, globals(), srckeys=dir(__ssl))
8 time = __import__('time')
10 from eventlet.support import get_errno
11 from eventlet.hubs import trampoline, IOClosed
12 from eventlet.greenio import set_nonblocking, GreenSocket, SOCKET_CLOSED, CONNECT_ERR, CONNECT_SUCCESS
13 orig_socket = __import__('socket')
14 socket = orig_socket.socket
15 if sys.version_info >= (2, 7):
17 timeout_exc = SSLError
20 timeout_exc = orig_socket.timeout
22 __patched__ = ['SSLSocket', 'wrap_socket', 'sslwrap_simple']
25 class GreenSSLSocket(__ssl.SSLSocket):
27 """ This is a green version of the SSLSocket class from the ssl module added
28 in 2.6. For documentation on it, please see the Python standard
31 Python nonblocking ssl objects don't give errors when the other end
32 of the socket is closed (they do notice when the other end is shutdown,
33 though). Any write/read operations will simply hang if the socket is
34 closed from the other end. There is no obvious fix for this problem;
35 it appears to be a limitation of Python's ssl object implementation.
36 A workaround is to set a reasonable timeout on the socket using
37 settimeout(), and to close/reopen the connection when a timeout
38 occurs at an unexpected juncture in the code.
40 # we are inheriting from SSLSocket because its constructor calls
41 # do_handshake whose behavior we wish to override
43 def __init__(self, sock, *args, **kw):
44 if not isinstance(sock, GreenSocket):
45 sock = GreenSocket(sock)
47 self.act_non_blocking = sock.act_non_blocking
48 self._timeout = sock.gettimeout()
49 super(GreenSSLSocket, self).__init__(sock.fd, *args, **kw)
51 # the superclass initializer trashes the methods so we remove
52 # the local-object versions of them and let the actual class
53 # methods shine through
55 for fn in orig_socket._delegate_methods:
57 except AttributeError:
60 def settimeout(self, timeout):
61 self._timeout = timeout
66 def setblocking(self, flag):
68 self.act_non_blocking = False
71 self.act_non_blocking = True
74 def _call_trampolining(self, func, *a, **kw):
75 if self.act_non_blocking:
81 except SSLError as exc:
82 if get_errno(exc) == SSL_ERROR_WANT_READ:
85 timeout=self.gettimeout(),
86 timeout_exc=timeout_exc('timed out'))
87 elif get_errno(exc) == SSL_ERROR_WANT_WRITE:
90 timeout=self.gettimeout(),
91 timeout_exc=timeout_exc('timed out'))
95 def write(self, data):
96 """Write DATA to the underlying SSL channel. Returns
97 number of bytes of DATA actually transmitted."""
98 return self._call_trampolining(
99 super(GreenSSLSocket, self).write, data)
101 def read(self, len=1024):
102 """Read up to LEN bytes and return them.
103 Return zero-length string on EOF."""
105 return self._call_trampolining(
106 super(GreenSSLSocket, self).read, len)
110 def send(self, data, flags=0):
112 return self._call_trampolining(
113 super(GreenSSLSocket, self).send, data, flags)
115 trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
116 return socket.send(self, data, flags)
118 def sendto(self, data, addr, flags=0):
119 # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
121 raise ValueError("sendto not allowed on instances of %s" %
124 trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
125 return socket.sendto(self, data, addr, flags)
127 def sendall(self, data, flags=0):
128 # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
132 "non-zero flags not allowed in calls to sendall() on %s" %
136 while (count < amount):
137 v = self.send(data[count:])
140 trampoline(self, write=True, timeout_exc=timeout_exc('timed out'))
145 return socket.sendall(self, data, flags)
146 except orig_socket.error as e:
147 if self.act_non_blocking:
149 if get_errno(e) == errno.EWOULDBLOCK:
150 trampoline(self, write=True,
151 timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
152 if get_errno(e) in SOCKET_CLOSED:
156 def recv(self, buflen=1024, flags=0):
157 # *NOTE: gross, copied code from ssl.py becase it's not factored well enough to be used as-is
161 "non-zero flags not allowed in calls to recv() on %s" %
163 read = self.read(buflen)
168 return socket.recv(self, buflen, flags)
169 except orig_socket.error as e:
170 if self.act_non_blocking:
172 if get_errno(e) == errno.EWOULDBLOCK:
174 trampoline(self, read=True,
175 timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
178 if get_errno(e) in SOCKET_CLOSED:
182 def recv_into(self, buffer, nbytes=None, flags=0):
183 if not self.act_non_blocking:
184 trampoline(self, read=True, timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
185 return super(GreenSSLSocket, self).recv_into(buffer, nbytes, flags)
187 def recvfrom(self, addr, buflen=1024, flags=0):
188 if not self.act_non_blocking:
189 trampoline(self, read=True, timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
190 return super(GreenSSLSocket, self).recvfrom(addr, buflen, flags)
192 def recvfrom_into(self, buffer, nbytes=None, flags=0):
193 if not self.act_non_blocking:
194 trampoline(self, read=True, timeout=self.gettimeout(), timeout_exc=timeout_exc('timed out'))
195 return super(GreenSSLSocket, self).recvfrom_into(buffer, nbytes, flags)
198 return GreenSocket(self._call_trampolining(
199 super(GreenSSLSocket, self).unwrap))
201 def do_handshake(self):
202 """Perform a TLS/SSL handshake."""
203 return self._call_trampolining(
204 super(GreenSSLSocket, self).do_handshake)
206 def _socket_connect(self, addr):
207 real_connect = socket.connect
208 if self.act_non_blocking:
209 return real_connect(self, addr)
211 # *NOTE: gross, copied code from greenio because it's not factored
212 # well enough to reuse
213 if self.gettimeout() is None:
216 return real_connect(self, addr)
217 except orig_socket.error as exc:
218 if get_errno(exc) in CONNECT_ERR:
219 trampoline(self, write=True)
220 elif get_errno(exc) in CONNECT_SUCCESS:
225 end = time.time() + self.gettimeout()
228 real_connect(self, addr)
229 except orig_socket.error as exc:
230 if get_errno(exc) in CONNECT_ERR:
231 trampoline(self, write=True,
232 timeout=end - time.time(), timeout_exc=timeout_exc('timed out'))
233 elif get_errno(exc) in CONNECT_SUCCESS:
237 if time.time() >= end:
238 raise timeout_exc('timed out')
240 def connect(self, addr):
241 """Connects to remote ADDR, and then wraps the connection in
243 # *NOTE: grrrrr copied this code from ssl.py because of the reference
244 # to socket.connect which we don't want to call directly
246 raise ValueError("attempt to connect already-connected SSLSocket!")
247 self._socket_connect(addr)
249 self._sslobj = _ssl.sslwrap(self._sock, False, self.keyfile, self.certfile,
250 self.cert_reqs, self.ssl_version,
251 self.ca_certs, self.ciphers)
253 self._sslobj = _ssl.sslwrap(self._sock, False, self.keyfile, self.certfile,
254 self.cert_reqs, self.ssl_version,
256 if self.do_handshake_on_connect:
260 """Accepts a new connection from a remote client, and returns
261 a tuple containing that new connection wrapped with a server-side
262 SSL channel, and the address of the remote client."""
263 # RDW grr duplication of code from greenio
264 if self.act_non_blocking:
265 newsock, addr = socket.accept(self)
269 newsock, addr = socket.accept(self)
270 set_nonblocking(newsock)
272 except orig_socket.error as e:
273 if get_errno(e) != errno.EWOULDBLOCK:
275 trampoline(self, read=True, timeout=self.gettimeout(),
276 timeout_exc=timeout_exc('timed out'))
278 new_ssl = type(self)(
280 keyfile=self.keyfile,
281 certfile=self.certfile,
283 cert_reqs=self.cert_reqs,
284 ssl_version=self.ssl_version,
285 ca_certs=self.ca_certs,
286 do_handshake_on_connect=self.do_handshake_on_connect,
287 suppress_ragged_eofs=self.suppress_ragged_eofs)
288 return (new_ssl, addr)
291 raise NotImplementedError("Can't dup an ssl object")
293 SSLSocket = GreenSSLSocket
296 def wrap_socket(sock, *a, **kw):
297 return GreenSSLSocket(sock, *a, **kw)
300 if hasattr(__ssl, 'sslwrap_simple'):
301 def sslwrap_simple(sock, keyfile=None, certfile=None):
302 """A replacement for the old socket.ssl function. Designed
303 for compability with Python 2.5 and earlier. Will disappear in
305 ssl_sock = GreenSSLSocket(sock, keyfile=keyfile, certfile=certfile,
308 ssl_version=PROTOCOL_SSLv23,