Branch: ircv3.2-upgrade @ 3868b34. Related: #97, #98.
Summary
A WebSocket upgrade request of 512 bytes or more never gets a response — the connection just hangs until the registration timeout. Real browsers send upgrade requests of roughly 500–700 bytes (User-Agent, Accept-*, Origin, Sec-WebSocket-Extensions, Sec-Fetch-*, cookies…), so no browser can currently connect, even over TLS where #97 doesn't apply. Node's WebSocket sends a ~200-byte request, which is why the CLI probes in #97/#98 worked.
Reproduction
Against wss://localhost:8443/ (ssl = yes; websocket = yes;), send a handcrafted upgrade request padded with a harmless header to a given total size:
| request size |
result |
| 500 bytes |
HTTP/1.1 101 Switching Protocols |
| 512 bytes |
no response, hang |
| 528 bytes |
no response, hang |
Chromium 13x headless with an otherwise-working page: its actual request is ~553 bytes → hang; the same page connecting through a local TLS proxy that strips User-Agent, Accept-*, Origin and Sec-WebSocket-Extensions (553 → 211 bytes) works end to end (registers, joins, chats).
Cause (likely)
The handshake path reads the request into the client's normal 512-byte input buffer (BUFSIZE) and only calls websocket_handshake() once it sees the terminating \r\n\r\n within that buffer (ircd/s_bsd.c around the IsWSNeedHandshake read path, ~:1076-1110). A request larger than the buffer never contains the terminator in a single read, so the handshake is never attempted and the pre-registration data is silently dropped/overwritten. Same family as #98 (fixed-size buffers sized for IRC lines being applied to WebSocket framing).
Suggested fix
Accumulate the upgrade request in a per-client handshake buffer (a few KB — browsers with cookies can exceed 2 KB) until \r\n\r\n arrives or a sane cap (e.g. 8 KB → 431/close) is hit, then run websocket_handshake() on the whole thing. autodetect ports need the same treatment.
Also observed (not necessarily a bug)
The TLS listener requests a client certificate during the handshake. Headless Chromium aborts with ERR_SSL_CLIENT_AUTH_CERT_NEEDED because it has no way to answer the prompt; interactive browsers may show a certificate picker on every connect when the user has any client certs installed. If the request is only there for CertFP / SASL EXTERNAL, consider making it optional per-port or per-feature for websocket listeners.
Context
Found while bringing up Seance (browser IRCv3 client, TheLounge fork) against a docker build of this branch. Happy to test a fix.
Branch:
ircv3.2-upgrade@3868b34. Related: #97, #98.Summary
A WebSocket upgrade request of 512 bytes or more never gets a response — the connection just hangs until the registration timeout. Real browsers send upgrade requests of roughly 500–700 bytes (
User-Agent,Accept-*,Origin,Sec-WebSocket-Extensions,Sec-Fetch-*, cookies…), so no browser can currently connect, even over TLS where #97 doesn't apply. Node'sWebSocketsends a ~200-byte request, which is why the CLI probes in #97/#98 worked.Reproduction
Against
wss://localhost:8443/(ssl = yes; websocket = yes;), send a handcrafted upgrade request padded with a harmless header to a given total size:HTTP/1.1 101 Switching ProtocolsChromium 13x headless with an otherwise-working page: its actual request is ~553 bytes → hang; the same page connecting through a local TLS proxy that strips
User-Agent,Accept-*,OriginandSec-WebSocket-Extensions(553 → 211 bytes) works end to end (registers, joins, chats).Cause (likely)
The handshake path reads the request into the client's normal 512-byte input buffer (
BUFSIZE) and only callswebsocket_handshake()once it sees the terminating\r\n\r\nwithin that buffer (ircd/s_bsd.caround theIsWSNeedHandshakeread path, ~:1076-1110). A request larger than the buffer never contains the terminator in a single read, so the handshake is never attempted and the pre-registration data is silently dropped/overwritten. Same family as #98 (fixed-size buffers sized for IRC lines being applied to WebSocket framing).Suggested fix
Accumulate the upgrade request in a per-client handshake buffer (a few KB — browsers with cookies can exceed 2 KB) until
\r\n\r\narrives or a sane cap (e.g. 8 KB →431/close) is hit, then runwebsocket_handshake()on the whole thing.autodetectports need the same treatment.Also observed (not necessarily a bug)
The TLS listener requests a client certificate during the handshake. Headless Chromium aborts with
ERR_SSL_CLIENT_AUTH_CERT_NEEDEDbecause it has no way to answer the prompt; interactive browsers may show a certificate picker on every connect when the user has any client certs installed. If the request is only there for CertFP / SASL EXTERNAL, consider making it optional per-port or per-feature for websocket listeners.Context
Found while bringing up Seance (browser IRCv3 client, TheLounge fork) against a
docker buildof this branch. Happy to test a fix.