Skip to content

Commit

Permalink
[feature] Add option to support late addition of headers
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 8, 2023
1 parent 41dc56a commit c764eff
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 2 deletions.
16 changes: 15 additions & 1 deletion doc/ws.md
Original file line number Diff line number Diff line change
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
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
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,11 @@ function initAsClient(websocket, address, protocols, options) {
});
});

req.end();
if (typeof opts.finishRequest === 'function') {
opts.finishRequest(req, websocket);
} else {
req.end();
}
}

/**
Expand Down
25 changes: 25 additions & 0 deletions test/websocket.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3857,6 +3857,31 @@ describe('WebSocket', () => {
agent
});
});

it('supports late headers using finishRequest', (done) => {
const wss = new WebSocket.Server({ port: 0 }, () => {
const ws = new WebSocket(`ws://localhost:${wss.address().port}`, {
finishRequest(req, ws_arg) {
setTimeout( () => {
assert.strictEqual(ws_arg, ws);
assert.strictEqual(req, ws._req);
req.setHeader('Cookie', 'foo=bar');
req.end();
}, 150);
}
});

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 c764eff

Please sign in to comment.