fix: WebSSH terminal echo delay — replace readline() with read()

asyncssh's async-for on shell.stdout internally calls readline()
which buffers until a newline is found. For interactive terminal
use, single-character echo never contains a newline, so all
typed characters were buffered until Enter was pressed.

Fix: use shell.stdout.read(4096) which returns data as soon as
any is available, and set bufsize=4096 on create_process() to
reduce the internal read buffer from 128KB to 4KB for low-latency
interactive echo.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-05-26 21:54:40 +08:00
parent 8b10f9d64a
commit a2d392dae7
+16 -6
View File
@@ -180,6 +180,7 @@ async def terminal_ws(
shell_proc = conn.create_process(
term_type="xterm-256color",
term_size=(24, 80),
bufsize=4096, # Small buffer for low-latency interactive echo
)
except Exception as e:
# Stale pooled connection — force-close and retry with fresh connection
@@ -190,6 +191,7 @@ async def terminal_ws(
shell_proc = conn.create_process(
term_type="xterm-256color",
term_size=(24, 80),
bufsize=4096, # Small buffer for low-latency interactive echo
)
except Exception as e2:
logger.error(f"WebSSH shell creation failed on fresh connection (server={server.id}): {type(e2).__name__}: {e2!r}")
@@ -218,13 +220,21 @@ async def terminal_ws(
command_buffer = "" # Buffer for command logging
async def _read_shell_output():
"""Read SSH shell output -> send to WebSocket client"""
"""Read SSH shell output -> send to WebSocket client
Uses read() instead of async-for to avoid readline() buffering.
async-for calls readline() internally, which waits for newline
characters before yielding — causing echo delay in interactive
terminal use. read(n) returns data as soon as any is available.
"""
try:
async for data in shell.stdout:
await websocket.send_json({
"type": MSG_DATA,
"data": data,
})
while not shell.stdout.at_eof():
data = await shell.stdout.read(4096)
if data:
await websocket.send_json({
"type": MSG_DATA,
"data": data,
})
except Exception:
pass