Skip to content

Commit ef06841

Browse files
committed
telnet server: prompt app not cancelled when client close connection
If client closes the telnet connection (either due to user typing "Ctrl-] then 'quit'" or due to network shutdown) in middle of the prompt application, the application will hang forever and resources maintained in TelnetServer.connections and TelnetServer._application_tasks are not released.
1 parent 51ccb92 commit ef06841

File tree

1 file changed

+14
-2
lines changed

1 file changed

+14
-2
lines changed

prompt_toolkit/contrib/telnet/server.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ def __init__(self, connection: socket.socket, encoding: str) -> None:
8787
self._connection = connection
8888
self._errors = "strict"
8989
self._buffer: List[bytes] = []
90+
self._closed = False
9091

9192
def write(self, data: str) -> None:
9293
data = data.replace("\n", "\r\n")
@@ -98,12 +99,16 @@ def isatty(self) -> bool:
9899

99100
def flush(self) -> None:
100101
try:
101-
self._connection.send(b"".join(self._buffer))
102+
if not self._closed:
103+
self._connection.send(b"".join(self._buffer))
102104
except OSError as e:
103105
logger.warning("Couldn't send data over socket: %s" % e)
104106

105107
self._buffer = []
106108

109+
def close(self) -> None:
110+
self._closed = True
111+
107112
@property
108113
def encoding(self) -> str:
109114
return self._encoding
@@ -137,6 +142,7 @@ def __init__(
137142
self._closed = False
138143
self._ready = asyncio.Event()
139144
self.vt100_output = None
145+
self._task = None
140146

141147
# Create "Output" object.
142148
self.size = Size(rows=40, columns=79)
@@ -186,6 +192,9 @@ def handle_incoming_data() -> None:
186192
# Connection closed by client.
187193
logger.info("Connection closed by client. %r %r" % self.addr)
188194
self.close()
195+
# to abort the prompt app
196+
if self._task:
197+
self._task.cancel()
189198

190199
# Add reader.
191200
loop = get_event_loop()
@@ -196,7 +205,9 @@ def handle_incoming_data() -> None:
196205
await self._ready.wait()
197206
with create_app_session(input=self.vt100_input, output=self.vt100_output):
198207
self.context = contextvars.copy_context()
199-
await self.interact(self)
208+
self._task = get_event_loop().create_task(self.interact(self))
209+
await self._task
210+
self._task = None
200211
except Exception as e:
201212
print("Got %s" % type(e).__name__, e)
202213
import traceback
@@ -222,6 +233,7 @@ def close(self) -> None:
222233
self.vt100_input.close()
223234
get_event_loop().remove_reader(self.conn)
224235
self.conn.close()
236+
self.stdout.close()
225237

226238
def send(self, formatted_text: AnyFormattedText) -> None:
227239
"""

0 commit comments

Comments
 (0)