Skip to content
Prev Previous commit
Next Next commit
http: generic join header
  • Loading branch information
marco-ippolito committed Dec 29, 2022
commit fdc2c0012b2f6717bed05d2142573c8de650938c
18 changes: 10 additions & 8 deletions doc/api/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -2451,11 +2451,12 @@ header name:
`etag`, `expires`, `from`, `host`, `if-modified-since`, `if-unmodified-since`,
`last-modified`, `location`, `max-forwards`, `proxy-authorization`, `referer`,
`retry-after`, `server`, or `user-agent` are discarded.
To allow duplicate values of the headers listed above to be joined,
Comment thread
marco-ippolito marked this conversation as resolved.
use the option `joinDuplicateHeaders` in [`http.request()`][]
and [`http.createServer()`][].
Comment thread
marco-ippolito marked this conversation as resolved.
Outdated
* `set-cookie` is always an array. Duplicates are added to the array.
* For duplicate `cookie` headers, the values are joined together with `; `.
* For all other headers, the values are joined together with `, `.
* To allow duplicate values of `authorization` see the option
`joinAuthorizationHeaders` in [`http.request()`][]

### `message.headersDistinct`

Expand Down Expand Up @@ -3184,9 +3185,9 @@ changes:
a 400 (Bad Request) status code to any HTTP/1.1 request message
that lacks a Host header (as mandated by the specification).
**Default:** `true`.
* `joinAuthorizationHeaders` {boolean} It joins the field line values of
multiple `Authorization` headers in a request
with ` ,` instead of discarding the duplicates.
* `joinDuplicateHeaders` {boolean} It joins the field line values of multiple
headers in a request with ` ,` instead of discarding the duplicates.
See [`message.headers`][] for more information.
**Default:** `false`.
* `ServerResponse` {http.ServerResponse} Specifies the `ServerResponse` class
to be used. Useful for extending the original `ServerResponse`. **Default:**
Expand Down Expand Up @@ -3443,9 +3444,9 @@ changes:
* `uniqueHeaders` {Array} A list of request headers that should be sent
only once. If the header's value is an array, the items will be joined
using `; `.
* `joinAuthorizationHeaders` {boolean} It joins the field line values of
multiple `Authorization` headers in a request
with ` ,` instead of discarding the duplicates.
* `joinDuplicateHeaders` {boolean} It joins the field line values of
multiple headers in a request with ` ,` instead of discarding
Comment thread
marco-ippolito marked this conversation as resolved.
Outdated
the duplicates. See [`message.headers`][] for more information.
* `callback` {Function}
* Returns: {http.ClientRequest}

Expand Down Expand Up @@ -3759,6 +3760,7 @@ Set the maximum number of idle HTTP parsers. **Default:** `1000`.
[`http.IncomingMessage`]: #class-httpincomingmessage
[`http.ServerResponse`]: #class-httpserverresponse
[`http.Server`]: #class-httpserver
[`http.createServer()`]: #httpcreateserveroptions-requestlistener
[`http.get()`]: #httpgetoptions-callback
[`http.globalAgent`]: #httpglobalagent
[`http.request()`]: #httprequestoptions-callback
Expand Down
8 changes: 4 additions & 4 deletions lib/_http_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,11 @@ function ClientRequest(input, options, cb) {
}
this.insecureHTTPParser = insecureHTTPParser;

if (options.joinAuthorizationHeaders !== undefined) {
validateBoolean(options.joinAuthorizationHeaders, 'options.joinAuthorizationHeaders');
if (options.joinDuplicateHeaders !== undefined) {
Comment thread
anonrig marked this conversation as resolved.
validateBoolean(options.joinDuplicateHeaders, 'options.joinDuplicateHeaders');
}

this.joinAuthorizationHeaders = options.joinAuthorizationHeaders;
this.joinDuplicateHeaders = options.joinDuplicateHeaders;

this.path = options.path || '/';
if (cb) {
Expand Down Expand Up @@ -817,7 +817,7 @@ function tickOnSocket(req, socket) {
parser.maxHeaderPairs = req.maxHeadersCount << 1;
}

parser.joinAuthorizationHeaders = req.joinAuthorizationHeaders;
parser.joinDuplicateHeaders = req.joinDuplicateHeaders;
Comment thread
mcollina marked this conversation as resolved.

parser.onIncoming = parserOnIncomingClient;
socket.on('error', socketErrorListener);
Expand Down
6 changes: 3 additions & 3 deletions lib/_http_common.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
incoming.httpVersionMajor = versionMajor;
incoming.httpVersionMinor = versionMinor;
incoming.httpVersion = `${versionMajor}.${versionMinor}`;
incoming.joinAuthorizationHeaders = (socket && socket.server &&
socket.server.joinAuthorizationHeaders) ||
parser.joinAuthorizationHeaders;
incoming.joinDuplicateHeaders = (socket && socket.server &&
Comment thread
marco-ippolito marked this conversation as resolved.
Outdated
socket.server.joinDuplicateHeaders) ||
parser.joinDuplicateHeaders;
incoming.url = url;
incoming.upgrade = upgrade;

Expand Down
4 changes: 2 additions & 2 deletions lib/_http_incoming.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ function IncomingMessage(socket) {
this[kTrailers] = null;
this[kTrailersCount] = 0;
this.rawTrailers = [];
this.joinAuthorizationHeaders = false;
this.joinDuplicateHeaders = false;
this.aborted = false;

this.upgrade = null;
Expand Down Expand Up @@ -399,7 +399,7 @@ function _addHeaderLine(field, value, dest) {
} else {
dest['set-cookie'] = [value];
}
} else if (this.joinAuthorizationHeaders && flag === 97) {
} else if (this.joinDuplicateHeaders) {
// RFC 9110 https://www.rfc-editor.org/rfc/rfc9110#section-5.2
// https://github.com/nodejs/node/issues/45699
// allow authorization multiple fields
Expand Down
8 changes: 4 additions & 4 deletions lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -482,11 +482,11 @@ function storeHTTPOptions(options) {
this.requireHostHeader = true;
}

const joinAuthorizationHeaders = options.joinAuthorizationHeaders;
if (joinAuthorizationHeaders !== undefined) {
validateBoolean(joinAuthorizationHeaders, 'options.joinAuthorizationHeaders');
const joinDuplicateHeaders = options.joinDuplicateHeaders;
if (joinDuplicateHeaders !== undefined) {
Comment thread
anonrig marked this conversation as resolved.
validateBoolean(joinDuplicateHeaders, 'options.joinDuplicateHeaders');
}
this.joinAuthorizationHeaders = joinAuthorizationHeaders;
this.joinDuplicateHeaders = joinDuplicateHeaders;
}

function setupConnectionsTracking(server) {
Expand Down
2 changes: 1 addition & 1 deletion lib/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ let maxHeaderSize;
* insecureHTTPParser?: boolean;
* maxHeaderSize?: number;
* requireHostHeader?: boolean;
* joinAuthorizationHeaders?: boolean;
* joinDuplicateHeaders?: boolean;
* }} [opts]
* @param {Function} [requestListener]
* @returns {Server}
Expand Down
4 changes: 2 additions & 2 deletions test/parallel/test-http-request-join-authorization-headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const http = require('http');
{
const server = http.createServer({
requireHostHeader: false,
joinAuthorizationHeaders: true
joinDuplicateHeaders: true
}, common.mustCall((req, res) => {
assert.strictEqual(req.headers.authorization, '1, 2');
res.writeHead(200, ['authorization', '3', 'authorization', '4']);
Expand All @@ -17,7 +17,7 @@ const http = require('http');
http.get({
port: server.address().port,
headers: ['authorization', '1', 'authorization', '2'],
joinAuthorizationHeaders: true
joinDuplicateHeaders: true
}, (res) => {
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(res.headers.authorization, '3, 4');
Expand Down