Skip to content

Commit 69e6cec

Browse files
committed
[feature] Introduce the requireProtocolSelection option
To comply with the WHATWG specification, the connection must fail if the server does not return the `Sec-WebSocket-Protocol` header when a client requests specific subprotocols. However, some servers omit this header, as such behavior is permitted by RFC 6455. This new option adds the ability to skip that validation, enabling the client to work with servers that are not WHATWG-compliant. Refs: #1862 (comment)
1 parent 91707b4 commit 69e6cec

3 files changed

Lines changed: 31 additions & 1 deletion

File tree

doc/ws.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@ This class represents a WebSocket. It extends the `EventEmitter`.
328328
depending on the `protocolVersion`.
329329
- `perMessageDeflate` {Boolean|Object} Enable/disable permessage-deflate.
330330
- `protocolVersion` {Number} Value of the `Sec-WebSocket-Version` header.
331+
- `requireProtocolSelection` {Boolean} Specifies whether to treat a missing
332+
`Sec-WebSocket-Protocol` header in the server response as an error when
333+
subprotocols are requested. Defaults to `true`.
331334
- `skipUTF8Validation` {Boolean} Specifies whether or not to skip UTF-8
332335
validation for text and close messages. Defaults to `false`. Set to `true`
333336
only if the server is trusted.

lib/websocket.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,9 @@ module.exports = WebSocket;
650650
* permessage-deflate
651651
* @param {Number} [options.protocolVersion=13] Value of the
652652
* `Sec-WebSocket-Version` header
653+
* @param {Boolean} [options.requireProtocolSelection=false] Specifies whether
654+
* to treat a missing `Sec-WebSocket-Protocol` header in the server response
655+
* as an error when subprotocols are requested.
653656
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
654657
* not to skip UTF-8 validation for text and close messages
655658
* @private
@@ -663,6 +666,7 @@ function initAsClient(websocket, address, protocols, options) {
663666
maxPayload: 100 * 1024 * 1024,
664667
skipUTF8Validation: false,
665668
perMessageDeflate: true,
669+
requireProtocolSelection: true,
666670
followRedirects: false,
667671
maxRedirects: 10,
668672
...options,
@@ -959,7 +963,7 @@ function initAsClient(websocket, address, protocols, options) {
959963
} else if (!protocolSet.has(serverProt)) {
960964
protError = 'Server sent an invalid subprotocol';
961965
}
962-
} else if (protocolSet.size) {
966+
} else if (protocolSet.size && opts.requireProtocolSelection) {
963967
protError = 'Server sent no subprotocol';
964968
}
965969

test/websocket.test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,6 +1402,29 @@ describe('WebSocket', () => {
14021402
});
14031403
});
14041404

1405+
it('honors the `requireProtocolSelection` option', (done) => {
1406+
const wss = new WebSocket.Server({
1407+
handleProtocols() {},
1408+
server
1409+
});
1410+
1411+
wss.on('connection', (ws) => {
1412+
assert.strictEqual(ws.protocol, '');
1413+
ws.on('close', () => wss.close(done));
1414+
});
1415+
1416+
const ws = new WebSocket(
1417+
`ws://localhost:${server.address().port}`,
1418+
'foo',
1419+
{ requireProtocolSelection: false }
1420+
);
1421+
1422+
ws.on('open', () => {
1423+
assert.strictEqual(ws.protocol, '');
1424+
ws.close();
1425+
});
1426+
});
1427+
14051428
it('honors the `createConnection` option', (done) => {
14061429
const wss = new WebSocket.Server({ noServer: true, path: '/foo' });
14071430

0 commit comments

Comments
 (0)