From 515954256dbe8743a8d1b4a1d2bad0b2d7aacfbd Mon Sep 17 00:00:00 2001 From: Ron Frederick Date: Mon, 3 Jun 2024 22:13:57 -0700 Subject: [PATCH] Handle close on redirected pipe, without getting an EOF first This commit handles getting a close on a pipe used as a read redirect target on a pipe without ever receiving an EOF. This seems to happen on Linux when redirecting to a pseudo-TTY. When the slave TTY is closed, the master half gets an I/O error, triggering connection_lost to be called with an exception rather than eof_received. This prevented calls to drain() from properly unblocking, since the redirect was not properly cleaned up. Thanks go to GitHub user xuoguoto who helped to provide sample scripts showing the problem and ran multiple tests collecting data to help track this down! --- asyncssh/process.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/asyncssh/process.py b/asyncssh/process.py index bc3496d..3510e33 100644 --- a/asyncssh/process.py +++ b/asyncssh/process.py @@ -365,6 +365,12 @@ def connection_made(self, transport: asyncio.BaseTransport) -> None: self._transport = cast(asyncio.ReadTransport, transport) + def connection_lost(self, exc: Optional[Exception]) -> None: + """Handle closing of the pipe""" + + self._process.feed_close(self._datatype) + self.close() + def data_received(self, data: bytes) -> None: """Forward data from the pipe""" @@ -1048,6 +1054,12 @@ def feed_eof(self, datatype: DataType) -> None: self._readers[datatype].close() self.clear_reader(datatype) + def feed_close(self, datatype: DataType) -> None: + """Feed pipe close to the channel""" + + if datatype in self._readers: + self.feed_eof(datatype) + def feed_recv_buf(self, datatype: DataType, writer: _WriterProtocol[AnyStr]) -> None: """Feed current receive buffer to a newly set writer"""