Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bridge: call connection_lost "soon" #20881

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/cockpit/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ class ProtocolChannel(Channel, asyncio.Protocol):
Otherwise, if the subclass implements .do_open() itself, it is responsible
for setting up the connection and ensuring that .connection_made() is called.
"""
_transport: 'asyncio.Transport | None'
_transport: 'asyncio.Transport | None' = None
_send_pongs: bool = True
_last_ping: 'JsonObject | None' = None
_create_transport_task: 'asyncio.Task[asyncio.Transport] | None' = None
Expand Down
2 changes: 1 addition & 1 deletion src/cockpit/transports.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def abort(self, exc: 'Exception | None' = None) -> None:
self._closing = True
self._close_reader()
self._remove_write_queue()
self._protocol.connection_lost(exc)
self._loop.call_soon(self._protocol.connection_lost, exc)
self._close()

def can_write_eof(self) -> bool:
Expand Down
14 changes: 13 additions & 1 deletion test/pytest/test_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ async def test_cat(self) -> None:
assert transport.get_returncode() == 0
assert protocol.sent == protocol.received
transport.close()
# make sure the connection_lost handler isn't called immediately
assert protocol.transport is not None
# ...but "soon" (in the very next mainloop iteration)
await asyncio.sleep(0.01)
assert protocol.transport is None

@pytest.mark.asyncio
Expand Down Expand Up @@ -330,6 +334,10 @@ async def test_broken_pipe(self) -> None:
# Now let's write to the stdin with the other side closed.
# This should be enough to immediately disconnect us (EPIPE)
protocol.write(b'abc')
# make sure the connection_lost handler isn't called immediately
assert protocol.transport is not None
# ...but "soon" (in the very next mainloop iteration)
await asyncio.sleep(0.01)
assert protocol.transport is None
assert isinstance(protocol.exc, BrokenPipeError)

Expand Down Expand Up @@ -396,7 +404,11 @@ async def test_simple_close(self) -> None:
assert protocol.transport
assert protocol.transport.get_write_buffer_size() == 0
protocol.transport.close()
assert protocol.transport is None # make sure it closed immediately
# make sure the connection_lost handler isn't called immediately
assert protocol.transport is not None
# ...but "soon" (in the very next mainloop iteration)
await asyncio.sleep(0.01)
assert protocol.transport is None
# we have another ref on the transport
transport.close() # should be idempotent

Expand Down