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

http: join all duplicate headers #45982

Merged
merged 12 commits into from Jan 3, 2023
7 changes: 7 additions & 0 deletions doc/api/cli.md
Expand Up @@ -456,6 +456,12 @@ added: v12.3.0

Enable experimental WebAssembly module support.

### `--experimental-join-authorization-headers`

Enable experimental joining of the field line values of
multiple `Authorization` headers in a request.
The values are joined together with `, `.

### `--force-context-aware`

<!-- YAML
Expand Down Expand Up @@ -1873,6 +1879,7 @@ Node.js options that are allowed are:
* `--enable-source-maps`
* `--experimental-abortcontroller`
* `--experimental-import-meta-resolve`
* `--experimental-join-authorization-headers`
* `--experimental-json-modules`
* `--experimental-loader`
* `--experimental-modules`
Expand Down
4 changes: 4 additions & 0 deletions doc/api/http.md
Expand Up @@ -2454,6 +2454,9 @@ header name:
* `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 enable joining the value of duplicate `authorization` header,
use the experimental command line flag `--experimental-join-authorization-headers`.
See [`--experimental-join-authorization-headers`][] for more information.

### `message.headersDistinct`

Expand Down Expand Up @@ -3731,6 +3734,7 @@ Set the maximum number of idle HTTP parsers. **Default:** `1000`.
[`'request'`]: #event-request
[`'response'`]: #event-response
[`'upgrade'`]: #event-upgrade
[`--experimental-join-authorization-headers`]: cli.md#--experimental-join-authorization-headers
[`--insecure-http-parser`]: cli.md#--insecure-http-parser
[`--max-http-header-size`]: cli.md#--max-http-header-sizesize
[`Agent`]: #class-httpagent
Expand Down
13 changes: 13 additions & 0 deletions lib/_http_incoming.js
Expand Up @@ -31,6 +31,9 @@ const {
} = primordials;

const { Readable, finished } = require('stream');
const { getOptionValue } = require('internal/options');

const joinAuthorizationHeaders = getOptionValue('--experimental-join-authorization-headers');

const kHeaders = Symbol('kHeaders');
const kHeadersDistinct = Symbol('kHeadersDistinct');
Expand Down Expand Up @@ -400,6 +403,16 @@ function _addHeaderLine(field, value, dest) {
} else {
dest['set-cookie'] = [value];
}
} else if (joinAuthorizationHeaders && flag === 97) {
// RFC 9110 https://www.rfc-editor.org/rfc/rfc9110#section-5.2
// https://github.com/nodejs/node/issues/45699
// allow authorization multiple fields
// Make a delimited list
if (typeof dest[field] === 'string') {
marco-ippolito marked this conversation as resolved.
Show resolved Hide resolved
dest[field] += ', ' + value;
marco-ippolito marked this conversation as resolved.
Show resolved Hide resolved
} else {
dest[field] = value;
}
} else if (dest[field] === undefined) {
// Drop duplicates
dest[field] = value;
Expand Down
5 changes: 5 additions & 0 deletions src/node_options.cc
Expand Up @@ -381,6 +381,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"experimental ES Module support for webassembly modules",
&EnvironmentOptions::experimental_wasm_modules,
kAllowedInEnvvar);
AddOption("--experimental-join-authorization-headers",
"experimental joining of the field line values "
"of multiple Authorization headers",
&EnvironmentOptions::experimental_join_authorization_headers,
kAllowedInEnvvar);
AddOption("--experimental-import-meta-resolve",
"experimental ES Module import.meta.resolve() support",
&EnvironmentOptions::experimental_import_meta_resolve,
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Expand Up @@ -115,6 +115,7 @@ class EnvironmentOptions : public Options {
bool experimental_global_web_crypto = true;
bool experimental_https_modules = false;
bool experimental_wasm_modules = false;
bool experimental_join_authorization_headers = false;
bool experimental_import_meta_resolve = false;
std::string module_type;
std::string experimental_policy;
Expand Down
24 changes: 24 additions & 0 deletions test/parallel/test-http-request-merge-authorization-headers.js
@@ -0,0 +1,24 @@
// Flags: --experimental-join-authorization-headers

'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');

{
const server = http.createServer({ requireHostHeader: false }, common.mustCall((req, res) => {
assert.strictEqual(req.headers.authorization, '1, 2');
res.writeHead(200, ['authorization', '3', 'authorization', '4']);
res.end();
}));

server.listen(0, common.mustCall(() => {
http.get({ port: server.address().port, headers: ['authorization', '1', 'authorization', '2'] }, (res) => {
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(res.headers.authorization, '3, 4');
res.resume().on('end', common.mustCall(() => {
server.close();
}));
});
}));
}