Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Add createConnection option to control client socket setup #2219

Merged
merged 1 commit into from Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/ws.md
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
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
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