console: forward client terminal size over the console WebSocket

The docker_exec console defaults its exec PTY to 511x10000 (the no-NAWS
default that keeps the IOS-XR pager quiet for netmiko). A CPR-answering
client (xterm.js) on top of that tall canvas makes prompt_toolkit-based
CLIs (SR Linux sr_cli) re-emit their accumulated output on every
incremental render: ~145 KB instead of ~60 KB per command, visible in
the WebUI as full-screen clear/redraw flicker.

Let WebSocket console clients propagate their real terminal geometry:
binary frames {"cols": N, "rows": M} alongside text frames carrying
terminal data. The controller forwards binary frames (previously only
text was forwarded), and the compute side turns them into a NAWS
subnegotiation for telnet-based consoles (docker_exec included) or an
asyncssh pty size change for SSH consoles.

The docker_exec console restores the tall 511x10000 default when its
last client disconnects, so a later non-NAWS client (netmiko, bare
telnet) connecting to the still-live exec doesn't inherit a browser
geometry and hit PTY-window paging again.
This commit is contained in:
YueGuobin committed 2026-08-21 21:41:51 +08:00
1 parent 5188ae625a
commit abd0b8e274
3 files changed
+79 -5

No files matched your search

+9 -3
View File
@@ -694,13 +694,19 @@ async def ws_console(
async def ws_receive(ws_console_compute):
"""
Receive WebSocket data from client and forward to compute console WebSocket.
Text frames carry terminal data; binary frames carry client control
messages (e.g. terminal size), forwarded as-is.
"""
try:
while True:
data = await websocket.receive_text()
if data:
await ws_console_compute.send_str(data)
msg = await websocket.receive()
if msg["type"] == "websocket.disconnect":
break
if "text" in msg and msg["text"]:
await ws_console_compute.send_str(msg["text"])
elif "bytes" in msg and msg["bytes"]:
await ws_console_compute.send_bytes(msg["bytes"])
except WebSocketDisconnect:
await ws_console_compute.close()
log.info(