Description
Environment
Node v22.13.1
[email protected]
Reproduction
https://github.com/eltigerchino/crossws-duplicate-headers-repro
Reproduction instructions are included in the repository.
Describe the bug
I'm trying to select a WebSocket protocol during an upgrade request by returning the Sec-WebSocket-Protocol
header with a specific value. However, the ws
, uNetworking/uWebSockets.js
, and Deno libraries, also return this header if the client sends it. This causes the response to be invalid, since only one instance of the protocol header should exist in the response.
handleProtocols takes two arguments:
protocols {Set} The list of WebSocket subprotocols indicated by the client in the Sec-WebSocket-Protocol header.
request {http.IncomingMessage} The client HTTP GET request.
The returned value sets the value of the Sec-WebSocket-Protocol header in the HTTP 101 response. If returned value is false, the header is not added in the response.If handleProtocols is not set, then the first of the client's requested subprotocols is used.
https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback
Fortunately, this behaviour can be turned off by setting the Node adapter's option serverOptions.handleProtocols
. However, it might be better to standardise this behaviour in crossws
itself. The other adapters do not automatically return the protocol header. Thus, the upgrade
hook can be responsible for selecting the protocol by returning the header or omitting the header.
Alternatively, we could go the opposite direction and make all adapters always return the first protocol the client sends but allow overwriting the header from the upgrade hook.
Additional context
Warning: The server can't send more than one Sec-WebSocket-Protocol header. If the server doesn't want to use any subprotocol, it shouldn't send any Sec-WebSocket-Protocol header. Sending a blank header is incorrect. The client may close the connection if it doesn't get the subprotocol it wants.
Either a single value representing the subprotocol the server is ready to use or null. The value chosen MUST be derived from the client's handshake, specifically by selecting one of the values from the |Sec-WebSocket-Protocol| field that the server is willing to use for this connection (if any). If the client's handshake did not contain such a header field or if the server does not agree to any of the client's requested subprotocols, the only acceptable value is null. The absence of such a field is equivalent to the null value (meaning that if the server does not wish to agree to one of the suggested subprotocols, it MUST NOT send back a |Sec-WebSocket-Protocol| header field in its response). The empty string is not the same as the null value for these purposes and is not a legal value for this field. The ABNF for the value of this header field is (token), where the definitions of constructs and rules are as given in [RFC2616].
https://datatracker.ietf.org/doc/html/rfc6455#section-4.2.2
Logs
WebSocket connection to 'ws://localhost:8000/' failed: