diff --git a/client/web/README.md b/client/web/README.md index 880f79384..e309edb45 100644 --- a/client/web/README.md +++ b/client/web/README.md @@ -9,45 +9,60 @@ browser's localStorage. ## How a browser reaches a game -Browsers cannot open raw TCP, so the client never talks to `netmux` -directly. Both of its transports go through **Hydra**, the connection -proxy in [`mux/proxy`](../../mux/proxy), deployed alongside the game: +Browsers cannot open raw TCP, but they do not need a middlebox either: +**netmux serves WebSocket natively on its ordinary game ports** +(`mux/src/websocket.cpp`, RFC 6455). A port is shared between telnet +and WebSocket by first-byte protocol detection: a WebSocket client +sends its HTTP `GET` immediately and is upgraded (`/wsclient` and `/` +are both accepted); a classic telnet client waits for the server to +speak and receives the banner after the `proto_detect_window` grace +period (#1074/#2193 — runtime-settable, `0` disables detection for a +telnet-only port). This works on plain and TLS ports alike, so `wss:` +direct to the game is a two-line config away. ``` -browser ── ws(s) / gRPC-Web ──> hydra ── telnet TCP ──> netmux +browser ── ws(s) ─────────────────> netmux game port (shared with telnet) +browser ── ws(s) / gRPC-Web ──> hydra ── telnet ──> netmux ``` -- **Telnet over WebSocket** (`js/connection.js`) — the simple path. - Point the client at a Hydra `websocket` / `websocket+tls` listener and - bytes flow as-is; the client's own telnet engine (`js/telnet.js`) - handles negotiation, `IAC GA` prompts, NAWS, TTYPE, and CHARSET. - Any other telnet-over-WebSocket bridge works here too; nothing in - this path is Hydra-specific. +- **Telnet over WebSocket** (`js/connection.js`) — the simple path, + and the direct one. Point the client at the game port (or at a + Hydra `websocket` listener, or any generic telnet-over-WebSocket + bridge — nothing in this path cares which) and bytes flow as-is; the + client's own telnet engine (`js/telnet.js`) handles negotiation, + `IAC GA` prompts, NAWS, TTYPE, and CHARSET. -- **Hydra GameSession** (`js/hydra_connection.js`) — the full protocol: - protobuf `ClientMessage`/`ServerMessage` over a WebSocket with the - `hydra-gamesession` subprotocol, falling back to gRPC-Web - unary/streaming where WebSocket is blocked. This is what the - `/hconnect`, `/hswitch`, `/hlinks`, `/hgames`, `/hscroll` commands - drive (`/hhelp` lists them): multiple game links in one session, - server-side scrollback fetch, and Hydra-stored credentials with - auto-login. Session resume means a dropped browser connection picks - up where it left off — the proxy holds the game link open. +- **Hydra GameSession** (`js/hydra_connection.js`) — the optional + richer protocol, via **Hydra**, the connection proxy in + [`mux/proxy`](../../mux/proxy): protobuf `ClientMessage`/ + `ServerMessage` over a WebSocket with the `hydra-gamesession` + subprotocol, falling back to gRPC-Web unary/streaming where + WebSocket is blocked. This is what the `/hconnect`, `/hswitch`, + `/hlinks`, `/hgames`, `/hscroll` commands drive (`/hhelp` lists + them): multiple game links in one session, server-side scrollback + fetch, and Hydra-stored credentials with auto-login. Session resume + means a dropped browser connection picks up where it left off — the + proxy holds the game link open across it, which the direct path + cannot do. ## Deploying -1. Build and run Hydra next to the game: `cd mux/proxy && make`, copy - `hydra.conf.example` to `hydra.conf`, and enable the listeners you - want. See that file for TLS certificates, the master key that - encrypts stored credentials, and health-check endpoints. -2. Serve this directory from any static host — nginx, a CDN, an S3 +The minimal deployment is netmux plus static files — no extra process: + +1. Serve this directory from any static host — nginx, a CDN, an S3 bucket. There is nothing to compile. -3. Two cross-origin rules to respect: - - A page served over `https` may only open `wss:`/`https:` - transports (mixed-content rule), so front the Hydra listeners with - TLS in any real deployment. - - gRPC-Web listeners deny cross-origin requests by default; add your - client's origin with `cors_origin` in `hydra.conf`. +2. Point the client at the game's host and port. A page served over + `https` may only open `wss:` (mixed-content rule), so use a TLS + game port for any real deployment. + +Add Hydra when you want what the proxy provides — session resume, +multi-game links, stored credentials, gRPC-Web for WebSocket-hostile +networks: `cd mux/proxy && make`, copy `hydra.conf.example` to +`hydra.conf`, and enable the listeners you want. See that file for +TLS certificates, the master key that encrypts stored credentials, +health-check endpoints, and `cors_origin` (gRPC-Web listeners deny +cross-origin requests by default; plain WebSocket is not subject to +CORS). ## Development @@ -66,6 +81,7 @@ it stays testable this way. ## Status -Part of the 2.14 development tree. The released 2.13 server has no -Hydra, so a 2.13 game wanting browser play needs a generic -telnet-over-WebSocket bridge and the simple transport above. +Part of the 2.14 development tree. The released 2.13 server has +neither the WebSocket listener nor Hydra, so a 2.13 game wanting +browser play needs a generic telnet-over-WebSocket bridge and the +simple transport above.