SSL support in asynchat.async_chat
A while back I needed to be able to use SSL connections in async_chat, but I found it to be horribly incompatible. After quite a bit of investigation I found a suitable solution.
import asynchat import socket import ssl import errno class async_chat_ssl(asynchat.async_chat): """ Asynchronous connection with SSL support. """ def connect(self, host, use_ssl=False): self.use_ssl = use_ssl if use_ssl: self.send = self._ssl_send self.recv = self._ssl_recv asynchat.async_chat.connect(self, host) def handle_connect(self): """ Initializes SSL support after the connection has been made. """ if self.use_ssl: self.ssl = ssl.wrap_socket(self.socket) self.set_socket(self.ssl) def _ssl_send(self, data): """ Replacement for self.send() during SSL connections. """ try: result = self.write(data) return result except ssl.SSLError, why: if why[0] in (asyncore.EWOULDBLOCK, errno.ESRCH): return 0 else: raise ssl.SSLError, why return 0 def _ssl_recv(self, buffer_size): """ Replacement for self.recv() during SSL connections. """ try: data = self.read(buffer_size) if not data: self.handle_close() return '' return data except ssl.SSLError, why: if why[0] in (asyncore.ECONNRESET, asyncore.ENOTCONN, asyncore.ESHUTDOWN): self.handle_close() return '' elif why[0] == errno.ENOENT: # Required in order to keep it non-blocking return '' else: raise
It should fit in place of typical use of asynchat.async_chat. In order to specify that you’re wanting to use SSL, just set the flag in:
connect(host, use_ssl=True)
It would be nice if SSL support with asynchat.async_chat worked by default. Hopefully I’m not the only one who finds the above solution useful.
And as always, if you see any errors above, I encourage you to post a comment explaining the it!

Jean-Paul Calderone wrote,
Unfortunately, you may not find this as useful as you would have expected. The ssl module does not support non-blocking reads:
>>> import socket
>>> import time
>>> import ssl
>>> s = ssl.wrap_socket(socket.socket())
>>> s.connect((‘localhost’, 8443))
>>> s.send(‘GET /async.rpy HTTP/1.1\r\n\r\n’)
27
>>> s.setblocking(False)
>>> a = time.time(); s.recv(1024); b = time.time()
‘HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nDate: Thu, 02 Sep 2010 11:51:03 GMT\r\nContent-Type: text/html\r\nServer: TwistedWeb/10.1.0+r29954\r\n\r\n4c\r\n<html><body>Sorry to keep you waiting.</body></html>\r\n0\r\n\r\n’
>>> print b – a
4.13403391838
>>>
Even worse than the fact that it blocks is the fact that it is busy looping while it does so. Expect to see 100% CPU when using this approach any time there’s a network hiccup or someone decides to DoS your service. :(
Giampaolo RodolĂ wrote,
@jp: what python version are you using?
Python 2.6.6 should already include a fix for that: http://bugs.python.org/issue3890
Jean-Paul Calderone wrote,
@Giampaolo: It seems that bug was deemed important enough to include twice. ;) The one in ssl.py does appear to be fixed, but the one in _ssl.c is still there: see the do loop in PySSL_SSLRead at http://svn.python.org/view/*checkout*/python/trunk/Modules/_ssl.c?revision=82210&content-type=text/plain
Erdal wrote,
ok, so which version of Python works with the non-blocking socket?
buy fat loss wrote,
Glad to be one of several visitants on this awful web site : D.