diff --git a/benchmark/http2/get-headers.js b/benchmark/http2/get-headers.js new file mode 100644 index 000000000000..a0732d92d7c9 --- /dev/null +++ b/benchmark/http2/get-headers.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common.js'); +const assert = require('node:assert'); +const http2 = require('node:http2'); + +const bench = common.createBenchmark(main, { + n: [1e6], + headers: [0, 1, 4, 16, 64], +}); + +function main({ n, headers }) { + const server = http2.createServer(); + server.on('request', (request, response) => { + const expected = { __proto__: null }; + for (let index = 0; index < headers; index++) { + const name = `x-header-${index}`; + response.setHeader(name, 'value'); + expected[name] = 'value'; + } + + for (let index = 0; index < 1e4; index++) { + response.getHeaders(); + } + + let result; + bench.start(); + for (let index = 0; index < n; index++) { + result = response.getHeaders(); + } + bench.end(n); + assert.deepStrictEqual(result, expected); + response.end(); + }); + + server.listen(0, '127.0.0.1', () => { + const client = http2.connect(`http://127.0.0.1:${server.address().port}`); + const request = client.request(); + request.resume(); + request.on('end', () => { + client.close(); + server.close(); + }); + request.end(); + }); +} diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 17cb76046ee0..85abfa714fa7 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -634,7 +634,13 @@ class Http2ServerResponse extends Stream { getHeaders() { const headers = { __proto__: null }; - return ObjectAssign(headers, this[kHeaders]); + const source = this[kHeaders]; + const keys = ObjectKeys(source); + for (let index = 0; index < keys.length; index++) { + const key = keys[index]; + headers[key] = source[key]; + } + return headers; } hasHeader(name) { diff --git a/test/parallel/test-http2-compat-get-headers.js b/test/parallel/test-http2-compat-get-headers.js new file mode 100644 index 000000000000..f98ceca1db14 --- /dev/null +++ b/test/parallel/test-http2-compat-get-headers.js @@ -0,0 +1,80 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('node:assert'); +const http2 = require('node:http2'); + +const server = http2.createServer(common.mustCall((request, response) => { + const empty = response.getHeaders(); + assert.strictEqual(Object.getPrototypeOf(empty), null); + assert.deepStrictEqual(Object.keys(empty), []); + assert.notStrictEqual(response.getHeaders(), empty); + + const cookies = ['first=value', 'second=value']; + response.setHeader('X-First', 'first'); + response.setHeader('10', 'ten'); + response.setHeader('2', 'two'); + response.setHeader('__proto__', 'proto'); + response.setHeader('Constructor', 'constructor'); + response.setHeader('Set-Cookie', cookies); + + const expected = { + '__proto__': null, + '2': 'two', + '10': 'ten', + 'x-first': 'first', + ['__proto__']: 'proto', + 'constructor': 'constructor', + 'set-cookie': cookies, + }; + const headers = response.getHeaders(); + assert.deepStrictEqual(headers, expected); + assert.deepStrictEqual(Object.keys(headers), Object.keys(expected)); + assert.strictEqual(headers['set-cookie'], cookies); + assert.notStrictEqual(response.getHeaders(), headers); + + headers['x-first'] = 'changed'; + headers['x-new'] = 'new'; + delete headers['2']; + assert.deepStrictEqual(response.getHeaders(), expected); + + headers['set-cookie'].push('third=value'); + assert.strictEqual(response.getHeader('set-cookie'), cookies); + assert.strictEqual(response.getHeader('set-cookie').length, 3); + + response.removeHeader('X-First'); + response.setHeader('X-First', 'replacement'); + delete expected['x-first']; + expected['x-first'] = 'replacement'; + assert.deepStrictEqual(response.getHeaders(), expected); + assert.deepStrictEqual(Object.keys(response.getHeaders()), Object.keys(expected)); + + for (const name of response.getHeaderNames()) { + response.removeHeader(name); + } + assert.deepStrictEqual(response.getHeaders(), empty); + assert.notStrictEqual(response.getHeaders(), empty); + + response.setHeader('X-Sent', 'value'); + response.flushHeaders(); + assert.deepStrictEqual(response.getHeaders(), { + '__proto__': null, + 'x-sent': 'value', + ':status': 200, + }); + response.end(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const request = client.request(); + request.resume(); + request.on('end', common.mustCall(() => { + client.close(); + server.close(); + })); + request.end(); +}));