Commit e2bc15692e4 for nodejs
commit e2bc15692e44687028cdc28f727e65083e3d058f
Author: Roberto Bianchi <rbianchidev@gmail.com>
Date: Mon Oct 12 11:13:52 2026 +0200
http: reduce raw header name allocations
Reuse the values array instead of allocating a second result array.
Signed-off-by: Roberto Bianchi <roberto.bianchi@spendesk.com>
PR-URL: https://github.com/nodejs/node/pull/66488
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Reviewed-By: Tim Perry <pimterry@gmail.com>
diff --git a/benchmark/http/get-raw-header-names.js b/benchmark/http/get-raw-header-names.js
new file mode 100644
index 00000000000..27c996d7ebb
--- /dev/null
+++ b/benchmark/http/get-raw-header-names.js
@@ -0,0 +1,33 @@
+'use strict';
+
+const common = require('../common.js');
+const assert = require('node:assert');
+const { OutgoingMessage } = require('node:http');
+
+const bench = common.createBenchmark(main, {
+ n: [1e6],
+ headers: [0, 1, 4, 16, 64],
+});
+
+function main({ n, headers }) {
+ const message = new OutgoingMessage();
+ const expected = [];
+ for (let index = 0; index < headers; index++) {
+ const name = `X-Header-${index}`;
+ message.setHeader(name, 'value');
+ expected.push(name);
+ }
+
+ for (let index = 0; index < 1e4; index++) {
+ message.getRawHeaderNames();
+ }
+
+ let result;
+ bench.start();
+ for (let index = 0; index < n; index++) {
+ result = message.getRawHeaderNames();
+ }
+ bench.end(n);
+
+ assert.deepStrictEqual(result, expected);
+}
diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js
index 2ca455eb556..f6ce0e5a475 100644
--- a/lib/_http_outgoing.js
+++ b/lib/_http_outgoing.js
@@ -22,7 +22,6 @@
'use strict';
const {
- Array,
ArrayIsArray,
MathFloor,
ObjectDefineProperty,
@@ -935,12 +934,11 @@ OutgoingMessage.prototype.getRawHeaderNames = function getRawHeaderNames() {
const headersMap = this[kOutHeaders];
if (headersMap === null) return [];
- const values = ObjectValues(headersMap);
- const headers = Array(values.length);
+ const headers = ObjectValues(headersMap);
// Retain for(;;) loop for performance reasons
// Refs: https://github.com/nodejs/node/pull/30958
- for (let i = 0, l = values.length; i < l; i++) {
- headers[i] = values[i][0];
+ for (let i = 0; i < headers.length; i++) {
+ headers[i] = headers[i][0];
}
return headers;
diff --git a/test/parallel/test-http-get-raw-header-names.js b/test/parallel/test-http-get-raw-header-names.js
new file mode 100644
index 00000000000..3d331c81a3d
--- /dev/null
+++ b/test/parallel/test-http-get-raw-header-names.js
@@ -0,0 +1,43 @@
+'use strict';
+
+require('../common');
+const assert = require('node:assert');
+const { OutgoingMessage } = require('node:http');
+
+const message = new OutgoingMessage();
+assert.deepStrictEqual(message.getRawHeaderNames(), []);
+assert.notStrictEqual(message.getRawHeaderNames(), message.getRawHeaderNames());
+
+message.setHeader('X-First', 'first');
+message.setHeader('10', 'ten');
+message.setHeader('2', 'two');
+message.setHeader('__proto__', 'proto');
+message.setHeader('Constructor', 'constructor');
+message.setHeader('X-Last', 'last');
+
+const expected = ['2', '10', 'X-First', '__proto__', 'Constructor', 'X-Last'];
+const names = message.getRawHeaderNames();
+assert.deepStrictEqual(names, expected);
+names[0] = 'changed';
+names.push('extra');
+assert.deepStrictEqual(message.getRawHeaderNames(), expected);
+assert.strictEqual(message.getHeader('2'), 'two');
+
+message.setHeader('x-FIRST', 'updated');
+expected[2] = 'x-FIRST';
+assert.deepStrictEqual(message.getRawHeaderNames(), expected);
+
+message.removeHeader('X-FIRST');
+message.setHeader('X-First', 'reinserted');
+assert.deepStrictEqual(message.getRawHeaderNames(), [
+ '2', '10', '__proto__', 'Constructor', 'X-Last', 'X-First',
+]);
+
+for (const name of message.getHeaderNames()) {
+ message.removeHeader(name);
+}
+assert.deepStrictEqual(message.getRawHeaderNames(), []);
+assert.notStrictEqual(message.getRawHeaderNames(), message.getRawHeaderNames());
+
+message.setHeader('X-New', 'new');
+assert.deepStrictEqual(message.getRawHeaderNames(), ['X-New']);