Commit b933d20c3fe for nodejs

commit b933d20c3fea1b3e324ac0fefc2fa25aa8a0d81a
Author: Matteo Collina <hello@matteocollina.com>
Date:   Wed Sep 30 15:06:51 2026 +0200

    http: avoid dictionary-mode objects in responses

    `setHeader()` stored headers in a `{ __proto__: null }` literal and
    `OutgoingMessage` let EventEmitter create the same literal for its
    listener table. V8 creates null-prototype literals in dictionary mode,
    so every response paid for a hash table allocation on the first
    `setHeader()`/`on()` call and for dictionary lookups on every header
    access, including the `for...in` in `_storeHeader()`.

    Use a fast-mode object with an empty null-prototype chain for the
    headers, and preset the listener table with the common events, as
    streams already do.

    Hello-world server (`res.setHeader()` + `res.end()`), one core:
    40.9k -> 50.0k req/s (+22%); CPU 24.4 -> 20.0 us/req; 4374 -> 3905 B
    of young-gen allocation per request. Responses are byte-identical.

    Signed-off-by: Matteo Collina <hello@matteocollina.com>
    PR-URL: https://github.com/nodejs/node/pull/66420
    Reviewed-By: Tim Perry <pimterry@gmail.com>
    Reviewed-By: Robert Nagy <ronagy@icloud.com>
    Reviewed-By: Tobias Nießen <tniessen@tnie.de>

diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js
index f6713d7b06a..2ca455eb556 100644
--- a/lib/_http_outgoing.js
+++ b/lib/_http_outgoing.js
@@ -87,6 +87,15 @@ let debug = require('internal/util/debuglog').debuglog('http', (fn) => {
 });

 const kCorked = Symbol('corked');
+
+// Container for the headers set through `setHeader()`, keyed by the
+// lower-cased header name. It has no enumerable or inherited properties, like
+// a `{ __proto__: null }` literal, but instances are fast-mode objects: V8
+// creates null-prototype literals in dictionary mode, which costs a hash table
+// allocation per response and slower property access for every header.
+class OutgoingHeaders {}
+ObjectSetPrototypeOf(OutgoingHeaders.prototype, null);
+delete OutgoingHeaders.prototype.constructor;
 const kSocket = Symbol('kSocket');
 const kChunkedBuffer = Symbol('kChunkedBuffer');
 const kChunkedLength = Symbol('kChunkedLength');
@@ -124,6 +133,18 @@ function OutgoingMessage(options) {
     return deprecateInstantiation(OutgoingMessage, 'DEP0195', options);
   }

+  // Preset the listener table with the events that are commonly listened
+  // for, mirroring streams. The presence of the properties keeps `_events`
+  // in fast mode: the first `on()` call would otherwise have to allocate a
+  // dictionary for the `{ __proto__: null }` object created by EventEmitter.
+  this._events ??= {
+    close: undefined,
+    error: undefined,
+    finish: undefined,
+    prefinish: undefined,
+    drain: undefined,
+  };
+
   Stream.call(this);

   // Queue that holds all currently pending data, until the response will be
@@ -805,7 +826,7 @@ OutgoingMessage.prototype.setHeader = function setHeader(name, value) {

   let headers = this[kOutHeaders];
   if (headers === null)
-    this[kOutHeaders] = headers = { __proto__: null };
+    this[kOutHeaders] = headers = new OutgoingHeaders();

   headers[name.toLowerCase()] = [name, value];
   return this;