Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions doc/api/http2.md
Original file line number Diff line number Diff line change
Expand Up @@ -4189,6 +4189,53 @@ removeAllHeaders(request.headers);
assert(request.url); // Fails because the :path header has been removed
```

#### `request.headersDistinct`

<!-- YAML
added:
- v18.3.0
- v16.17.0
-->

* Type: {Object}

Similar to [`request.headers`][], but there is no join logic and the values are
always arrays of strings, even for headers received just once.

The object has a null prototype and should not be accessed using the `in`
operator.

```js
// Prints something like:
//
// { 'user-agent': ['curl/7.22.0'],
// host: ['127.0.0.1:8000'],
// accept: ['*/*'] }
console.log(request.headersDistinct);
```

<!-- YAML
added: v8.4.0
-->

* Type: {Object}

Similar to `request.headers`, but preserves duplicate header values.
Each property is an array containing all values received for that header.

The object has a null prototype and should not be accessed using the `in`
operator.

```js
// Prints something like:
//
// {
// host: ['127.0.0.1:8000'],
// accept: ['text/html', 'application/json']
// }
console.log(request.headersDistinct);
```

#### `request.httpVersion`

<!-- YAML
Expand Down
25 changes: 25 additions & 0 deletions lib/internal/http2/compat.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const kRawTrailers = Symbol('rawTrailers');
const kSetHeader = Symbol('setHeader');
const kAppendHeader = Symbol('appendHeader');
const kAborted = Symbol('aborted');
const kHeadersDistinct = Symbol('headersDistinct');

let statusMessageWarned = false;
let statusConnectionHeaderWarned = false;
Expand Down Expand Up @@ -323,6 +324,7 @@ class Http2ServerRequest extends Readable {
this[kRawTrailers] = [];
this[kStream] = stream;
this[kAborted] = false;
this[kHeadersDistinct] = undefined;
stream[kProxySocket] = null;
stream[kRequest] = this;

Expand Down Expand Up @@ -356,6 +358,29 @@ class Http2ServerRequest extends Readable {
return this[kHeaders];
}

get headersDistinct() {
if (this[kHeadersDistinct] === undefined) {
const distinct = { __proto__: null };

const raw = this[kRawHeaders];

for (let i = 0; i < raw.length; i += 2) {
const name = raw[i].toLowerCase();
const value = raw[i + 1];

if (distinct[name] === undefined) {
distinct[name] = [value];
} else {
distinct[name].push(value);
}
}

this[kHeadersDistinct] = distinct;
}

return this[kHeadersDistinct];
}

get rawHeaders() {
return this[kRawHeaders];
}
Expand Down
2 changes: 1 addition & 1 deletion lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ ObjectSetPrototypeOf(Socket, stream.Duplex);

// Refresh existing timeouts.
Socket.prototype._unrefTimer = function _unrefTimer() {
for (let s = this; s !== null; s = s._parent) {
for (let s = this; s != null; s = s._parent) {
if (s[kTimeout])
s[kTimeout].refresh();
Comment thread
aduh95 marked this conversation as resolved.
}
Expand Down
19 changes: 19 additions & 0 deletions test/parallel/test-http2-compat-serverrequest-headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,25 @@ const h2 = require('http2');
assert.strictEqual(rawHeaders[position + 1], value);
}

const headersDistinct = request.headersDistinct;

assert.deepStrictEqual(
headersDistinct,
{
__proto__: null,
':path': ['/foobar'],
':method': ['GET'],
':scheme': ['http'],
':authority': [`localhost:${port}`],
'foo-bar': ['abc123'],
}
);

assert.strictEqual(
headersDistinct,
request.headersDistinct
);

request.url = '/one';
assert.strictEqual(request.url, '/one');

Expand Down
33 changes: 33 additions & 0 deletions test/parallel/test-net-unref-timer-parent-undefined.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';

const common = require('../common');
const fs = require('fs');
const path = require('path');
const tls = require('tls');

const fixtures = require('../common/fixtures');

const options = {
key: fixtures.readKey('agent1-key.pem'),
cert: fixtures.readKey('agent1-cert.pem'),
};

const server = tls.createServer(options, common.mustCall((conn) => {
setTimeout(() => conn.write('x'), 50);
}));

server.listen(0, common.mustCall(() => {
const client = tls.connect({
port: server.address().port,
rejectUnauthorized: false,
}, common.mustCall(() => {
client._parent = undefined;
}));

client.on('data', common.mustCall(() => {
server.close();
client.destroy();
}));

client.on('error', common.mustNotCall());
}));
Loading