Issue
connectd/websocketd.c matches HTTP header names byte-exactly (get_http_hdr uses memstarts) and matches the Upgrade/Connection values with case-sensitive strstr. RFC 9110 section 5.1 makes field names case-insensitive, and the RFC 6455 excerpt quoted in the file itself says the Upgrade and Connection values are "treated as an ASCII case-insensitive value".
Practical impact: Node.js's built-in WebSocket (undici) sends lowercased request headers, so it can never connect to a bind-addr=ws: listener. Any other client that normalizes header casing fails the same way.
Steps to reproduce
Against v26.06.1 with bind-addr=ws:0.0.0.0:19847:
printf 'GET / HTTP/1.1\r\nhost: x\r\nupgrade: websocket\r\nconnection: Upgrade\r\nsec-websocket-key: dGhlIHNhbXBsZSBub25jZQ==\r\nsec-websocket-version: 13\r\n\r\n' | nc <host> 19847
returns:
HTTP/1.1 400 I only speak websocket
Upgrade: websocket missing
The identical request with RFC-cased header names (Upgrade, Connection, Sec-WebSocket-Key, Sec-WebSocket-Version) succeeds with 101 Switching Protocols. new WebSocket('ws://<host>:19847') from Node 22 fails the same way, while browsers happen to send RFC casing and work.
PR with a fix to follow shortly.
Issue
connectd/websocketd.c matches HTTP header names byte-exactly (get_http_hdr uses memstarts) and matches the Upgrade/Connection values with case-sensitive strstr. RFC 9110 section 5.1 makes field names case-insensitive, and the RFC 6455 excerpt quoted in the file itself says the Upgrade and Connection values are "treated as an ASCII case-insensitive value".
Practical impact: Node.js's built-in WebSocket (undici) sends lowercased request headers, so it can never connect to a bind-addr=ws: listener. Any other client that normalizes header casing fails the same way.
Steps to reproduce
Against v26.06.1 with bind-addr=ws:0.0.0.0:19847:
returns:
The identical request with RFC-cased header names (Upgrade, Connection, Sec-WebSocket-Key, Sec-WebSocket-Version) succeeds with 101 Switching Protocols.
new WebSocket('ws://<host>:19847')from Node 22 fails the same way, while browsers happen to send RFC casing and work.PR with a fix to follow shortly.