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

+14 -2
View File
@@ -412,6 +412,17 @@ class _LazyExecTelnetServer(AsyncioTelnetServer):
return False
return True
async def _disconnect_client(self, network_writer):
await super()._disconnect_client(network_writer)
# When the last client leaves, restore the tall no-NAWS default: a
# browser client resizes the exec to its own geometry (WS terminal
# size control frames -> NAWS), and the next non-NAWS client (netmiko,
# bare telnet) connecting to the still-live exec would otherwise
# inherit it and hit PTY-window paging (the IOS-XR --More-- trap).
if self._exec_id and not await self._get_connections_snapshot():
with contextlib.suppress(Exception):
await self._on_naws(511, 10000)
async def _on_naws(self, columns, rows):
if self._exec_id:
try:
@@ -509,8 +520,9 @@ class _LazyExecTelnetServer(AsyncioTelnetServer):
# (e.g. the IOS-XR pager) park at --More-- for clients
# that never negotiate NAWS (netmiko, bare telnet).
# Width 511 matches netmiko's 'terminal width 511'.
# Real NAWS clients resize to their own geometry right
# after connecting.
# WebUI clients resize to their real geometry right after
# connecting, via WS terminal-size control frames turned
# into NAWS by start_websocket_console.
await self._on_naws(511, 10000)
except Exception:
pass