Skip to content

Commit

Permalink
[feature] Add createConnection option, to control client socket setup
Browse files Browse the repository at this point in the history
This notably makes it possible to create a WebSocket over _any_ duplex
stream, allowing use of WS in all sorts of other weird environments,
eventually including the use of WebSockets over HTTP/2 streams.
  • Loading branch information
pimterry committed Apr 12, 2024
1 parent b119b41 commit 3ec5fe4
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 2 deletions.
4 changes: 4 additions & 0 deletions doc/ws.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ This class represents a WebSocket. It extends the `EventEmitter`.
`'ping'`, and `'pong'` events can be emitted multiple times in the same
tick. To improve compatibility with the WHATWG standard, the default value
is `false`. Setting it to `true` improves performance slightly.
- `createConnection` {Function} An alternative function to use in place of
`tls.createConnection` or `net.createConnection`. This can be used to
manually control exactly how the connection to the server is made, or to
make a connection over an existing Duplex stream obtained elsewhere.
- `finishRequest` {Function} A function which can be used to customize the
headers of each HTTP request before it is sent. See description below.
- `followRedirects` {Boolean} Whether or not to follow redirects. Defaults to
Expand Down
7 changes: 5 additions & 2 deletions lib/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,8 @@ module.exports = WebSocket;
* times in the same tick
* @param {Boolean} [options.autoPong=true] Specifies whether or not to
* automatically send a pong in response to a ping
* @param {Function} [options.createConnection] An alternative function to use
* in place of `tls.createConnection` or `net.createConnection`.
* @param {Function} [options.finishRequest] A function which can be used to
* customize the headers of each http request before it is sent
* @param {Boolean} [options.followRedirects=false] Whether or not to follow
Expand Down Expand Up @@ -660,8 +662,8 @@ function initAsClient(websocket, address, protocols, options) {
perMessageDeflate: true,
followRedirects: false,
maxRedirects: 10,
...options,
createConnection: undefined,
...options,
socketPath: undefined,
hostname: undefined,
protocol: undefined,
Expand Down Expand Up @@ -732,7 +734,8 @@ function initAsClient(websocket, address, protocols, options) {
const protocolSet = new Set();
let perMessageDeflate;

opts.createConnection = isSecure ? tlsConnect : netConnect;
opts.createConnection =
opts.createConnection || (isSecure ? tlsConnect : netConnect);
opts.defaultPort = opts.defaultPort || defaultPort;
opts.port = parsedUrl.port || defaultPort;
opts.host = parsedUrl.hostname.startsWith('[')
Expand Down
28 changes: 28 additions & 0 deletions test/websocket.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,34 @@ describe('WebSocket', () => {
});
});

it('honors the `createConnection` option', (done) => {
const wss = new WebSocket.Server({ noServer: true, path: '/foo' });

server.once('upgrade', (req, socket, head) => {
assert.strictEqual(req.headers.host, 'google.com:22');
wss.handleUpgrade(req, socket, head, NOOP);
});

const ws = new WebSocket('ws://google.com:22/foo', {
createConnection: (options) => {
assert.strictEqual(options.host, 'google.com');
assert.strictEqual(options.port, '22');

// Ignore the invalid host address, and connect to the server manually:
return net.createConnection({
host: 'localhost',
port: server.address().port
});
}
});

ws.on('open', () => {
assert.strictEqual(ws.url, 'ws://google.com:22/foo');
ws.on('close', () => done());
ws.close();
});
});

it('emits an error if the redirect URL is invalid (1/2)', (done) => {
server.once('upgrade', (req, socket) => {
socket.end('HTTP/1.1 302 Found\r\nLocation: ws://\r\n\r\n');
Expand Down

0 comments on commit 3ec5fe4

Please sign in to comment.