Skip to content

start_tls() does not call connection_lost() when the handshake is aborted #160513

Closed

Description

@gpshead

Bug report

Bug description:

When the connection is lost while a TLS handshake started by loop.start_tls() or StreamWriter.start_tls() is in progress, the protocol's connection_lost() is never called. start_tls() raises, but SSLProtocol.connection_lost() skips the application protocol whenever its own state is DO_HANDSHAKE, although with start_tls() that protocol has already had connection_made(). With streams, StreamWriter.wait_closed() then never returns.

import asyncio, socket, ssl, struct, threading

def serve(lsock):
    for _ in range(2):
        conn, _ = lsock.accept()
        conn.recv(65536)  # ClientHello
        # Reset the connection in the middle of the handshake.
        conn.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
        conn.close()

class Proto(asyncio.Protocol):
    lost = False
    def connection_lost(self, exc):
        self.lost = True

async def main():
    loop = asyncio.get_running_loop()
    ctx = ssl.create_default_context()
    lsock = socket.create_server(('127.0.0.1', 0))
    threading.Thread(target=serve, args=(lsock,), daemon=True).start()
    addr = lsock.getsockname()

    tr, proto = await loop.create_connection(Proto, *addr)
    try:
        await loop.start_tls(tr, proto, ctx, server_hostname='localhost')
    except OSError as exc:
        print('loop.start_tls():', repr(exc))
    await asyncio.sleep(1)
    print('connection_lost() called:', proto.lost)

    reader, writer = await asyncio.open_connection(*addr)
    try:
        await writer.start_tls(ctx, server_hostname='localhost')
    except OSError as exc:
        print('writer.start_tls():', repr(exc))
    writer.close()
    try:
        await asyncio.wait_for(writer.wait_closed(), 5)
    except TimeoutError:
        print('wait_closed() still pending after 5 seconds')
    except OSError as exc:
        print('wait_closed():', repr(exc))
    lsock.close()

asyncio.run(main())

Output on main:

loop.start_tls(): ConnectionResetError(104, 'Connection reset by peer')
connection_lost() called: False
writer.start_tls(): ConnectionResetError(104, 'Connection reset by peer')
wait_closed() still pending after 5 seconds

Expected: connection_lost() is called and wait_closed() completes, raising the same error. wait_closed() hangs in the same way when the handshake fails because ssl_handshake_timeout expired. The check dates back to the addition of loop.start_tls() in 3.7.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Activity

  1. self-assigned this
    on Oct 23, 2026
  2. added a commit that references this issue on Oct 25, 2026
  3. moved this from Todo to Done in asyncioon Oct 25, 2026
  4. added 2 commits that reference this issue on Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

  • Status
    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions