[Async-sig] Cancelling SSL connection

Mark E. Haase mehaase at gmail.com
Wed Jun 21 13:50:57 EDT 2017


(I'm not sure if this is a newbie question or a bug report or something in
between. I apologize in advance if its off-topic. Let me know if I should
post this somewhere else.)

If a task is cancelled while SSL is being negotiated, then an SSLError is
raised, but there's no way (as far as I can tell) for the caller to catch
it. (The example below is pretty contrived, but in an application I'm
working on, the user can cancel downloads at any time.) Here's an example:

    import asyncio, random, ssl

    async def download(host):
        ssl_context = ssl.create_default_context()
        reader, writer = await asyncio.open_connection(host, 443,
ssl=ssl_context)
        request = f'HEAD / HTTP/1.1\r\nHost: {host}\r\n\r\n'
        writer.write(request.encode('ascii'))
        lines = list()
        while True:
            newdata = await reader.readline()
            if newdata == b'\r\n':
                break
            else:
                lines.append(newdata.decode('utf8').rstrip('\r\n'))
        return lines[0]

    async def main():
        while True:
            task = asyncio.Task(download('www.python.org'))
            await asyncio.sleep(random.uniform(0.0, 0.5))
            task.cancel()
            try:
                response = await task
                print(response)
            except asyncio.CancelledError:
                print('request cancelled!')
            except ssl.SSLError:
                print('caught SSL error')
            await asyncio.sleep(1)

    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.close()

Running this script yields the following output:

    HTTP/1.1 200 OK
    request cancelled!
    HTTP/1.1 200 OK
    HTTP/1.1 200 OK
    <asyncio.sslproto.SSLProtocol object at 0x7fe7c00e5a20>: SSL handshake
failed
    Traceback (most recent call last):
      File "/usr/lib/python3.6/asyncio/base_events.py", line 803, in
_create_connection_transport
        yield from waiter
      File "/usr/lib/python3.6/asyncio/tasks.py", line 304, in _wakeup
        future.result()
    concurrent.futures._base.CancelledError

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/usr/lib/python3.6/asyncio/sslproto.py", line 577, in
_on_handshake_complete
        raise handshake_exc
      File "/usr/lib/python3.6/asyncio/sslproto.py", line 638, in
_process_write_backlog
        ssldata = self._sslpipe.shutdown(self._finalize)
      File "/usr/lib/python3.6/asyncio/sslproto.py", line 155, in shutdown
        ssldata, appdata = self.feed_ssldata(b'')
      File "/usr/lib/python3.6/asyncio/sslproto.py", line 219, in
feed_ssldata
        self._sslobj.unwrap()
      File "/usr/lib/python3.6/ssl.py", line 692, in unwrap
        return self._sslobj.shutdown()
    ssl.SSLError: [SSL] shutdown while in init (_ssl.c:2299)

Is this a bug that I should file, or is there some reason that it's
intended to work this way? I can work around it with asyncio.shield(), but
I think I would prefer for the asyncio/sslproto.py to catch the SSLError
and ignore it. Maybe I'm being short sighted.

Thanks,
Mark
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/async-sig/attachments/20170621/18f74e36/attachment.html>


More information about the Async-sig mailing list