Skip to content

Commit

Permalink
[feature] Add option to support late addition of headers (#2123)
Browse files Browse the repository at this point in the history
This supports the use-case where headers need to be added that depend on
the socket connection (e.g. for TLS channel binding).
  • Loading branch information
mvduin committed Mar 10, 2023
1 parent b4b9d5a commit cd89e07
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 2 deletions.
16 changes: 15 additions & 1 deletion doc/ws.md
Expand Up @@ -293,6 +293,8 @@ This class represents a WebSocket. It extends the `EventEmitter`.
- `address` {String|url.URL} The URL to which to connect.
- `protocols` {String|Array} The list of subprotocols.
- `options` {Object}
- `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
`false`.
- `generateMask` {Function} The function used to generate the masking key. It
Expand All @@ -316,12 +318,24 @@ This class represents a WebSocket. It extends the `EventEmitter`.
Options given do not have any effect if parsed from the URL given with the
`address` parameter.

Create a new WebSocket instance.

`perMessageDeflate` default value is `true`. When using an object, parameters
are the same of the server. The only difference is the direction of requests.
For example, `serverNoContextTakeover` can be used to ask the server to disable
context takeover.

Create a new WebSocket instance.
`finishRequest` is called with arguments

- `request` {http.ClientRequest}
- `websocket` {WebSocket}

for each HTTP GET request (the initial one and any caused by redirects) when it
is ready to be sent, to allow for last minute customization of the headers. If
`finishRequest` is set then it has the responsibility to call `request.end()`
once it is done setting request headers. This is intended for niche use-cases
where some headers can't be provided in advance e.g. because they depend on the
underlying socket.

#### IPC connections

Expand Down
6 changes: 5 additions & 1 deletion lib/websocket.js
Expand Up @@ -989,7 +989,11 @@ function initAsClient(websocket, address, protocols, options) {
});
});

req.end();
if (opts.finishRequest) {
opts.finishRequest(req, websocket);
} else {
req.end();
}
}

/**
Expand Down
29 changes: 29 additions & 0 deletions test/websocket.test.js
Expand Up @@ -3857,6 +3857,35 @@ describe('WebSocket', () => {
agent
});
});

it('honors the `finishRequest` option', (done) => {
const wss = new WebSocket.Server({ port: 0 }, () => {
const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
finishRequest(request, websocket) {
process.nextTick(() => {
assert.strictEqual(request, ws._req);
assert.strictEqual(websocket, ws);
});
request.on('socket', (socket) => {
socket.on('connect', () => {
request.setHeader('Cookie', 'foo=bar');
request.end();
});
});
}
});

ws.on('close', (code) => {
assert.strictEqual(code, 1005);
wss.close(done);
});
});

wss.on('connection', (ws, req) => {
assert.strictEqual(req.headers.cookie, 'foo=bar');
ws.close();
});
});
});

describe('permessage-deflate', () => {
Expand Down

0 comments on commit cd89e07

Please sign in to comment.