Skip to content

perf: binary WebSocket transport for shell data#86

Merged
billchurch merged 3 commits intomainfrom
fix/binary-shell-transport-478
Feb 7, 2026
Merged

perf: binary WebSocket transport for shell data#86
billchurch merged 3 commits intomainfrom
fix/binary-shell-transport-478

Conversation

@billchurch
Copy link
Owner

@billchurch billchurch commented Feb 7, 2026

Summary

  • Accept binary WebSocket frames from the server for shell output instead of JSON-wrapped UTF-8 strings
  • Socket.IO delivers server-side Buffer as ArrayBuffer in the browser; client detects the type and passes Uint8Array directly to xterm.js Terminal.write()
  • String decoding (via TextDecoder) deferred to session logger, only when logging is active
  • Type signatures widened across the full write chain: events, socket callbacks, TerminalRef.write, TerminalActions.write, SolidTerminalManager.writeToTerminal
  • Updated DOCS/SERVER_API.md to document the string | ArrayBuffer payload

Companion to server-side changes on fix/socket-backpressure-478 in webssh2.

Backwards compatibility

This client is backwards compatible with older servers — the data handler checks instanceof ArrayBuffer and falls through to the string path for servers that still emit strings.

However, the server-side change (emitting raw Buffer instead of string) is not backwards compatible with older clients. Old clients pass the ArrayBuffer directly to term.write(), which expects string | Uint8Array — resulting in garbled output. Server and client must be updated together.

Test plan

  • npm run check:all passes (lint + typecheck + 157 tests)
  • npm run build produces clean output
  • Manual: normal interactive typing works as before
  • Manual: cat /dev/urandom | hexdump -c shows reduced CPU usage (~25% vs 100% before)
  • Manual: session logging captures correct text output
  • Manual: exec commands still display output correctly

Refs: billchurch/webssh2#478

Accept binary frames from the server for shell output instead of
JSON-wrapped UTF-8 strings. Socket.IO delivers server-side Buffers as
ArrayBuffer in the browser; the client now detects the type and passes
Uint8Array directly to xterm.js Terminal.write(), which has natively
accepted binary input since xterm.js 4.0.

String decoding (via TextDecoder) is deferred to the session logger and
only runs when logging is active, keeping the display hot path
allocation-free.

The exec channel continues to receive strings since it accumulates
output into a single result before emitting.

Type signatures widened across the full write chain:
- ServerToClientEvents.data: string → string | ArrayBuffer
- socket.ts callbacks: string → string | Uint8Array
- TerminalRef.write, TerminalActions.write, writeToTerminal: string → string | Uint8Array

Refs: billchurch/webssh2#478
- @xterm/addon-fit 0.10.0 → 0.11.0
- @xterm/addon-search 0.15.0 → 0.16.0
- Fix high-severity ReDoS in @isaacs/brace-expansion (GHSA-7h2j-956f-4vf2)
- Regenerate package-lock.json (npm audit fix triggered lockfile refresh)
@socket-security
Copy link

socket-security bot commented Feb 7, 2026

Major version upgrade from xterm 5.5.0 to 6.0.0. No code changes
required - all existing APIs remain compatible with our usage.

Notable changes in v6: VS Code-style scrollbar, ESM support,
synchronized output (DEC mode 2026), OSC 52 clipboard support.
@billchurch billchurch merged commit bead698 into main Feb 7, 2026
6 checks passed
@billchurch billchurch deleted the fix/binary-shell-transport-478 branch February 7, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant