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
6 changes: 6 additions & 0 deletions benchmark/fixtures/simple-http-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ module.exports = http.createServer((req, res) => {
else
res.writeHead(status, { 'Content-Length': len.toString() });
break;
case 'setHeaderImplicit':
// Let end() derive the message framing: no Content-Length or
// Transfer-Encoding header and no writeHead() call.
res.statusCode = status;
res.setHeader('Content-Type', 'text/plain');
break;
default:
if (chunkedEnc) {
res.writeHead(status, {
Expand Down
3 changes: 2 additions & 1 deletion benchmark/http/set-header.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
const common = require('../common.js');

const bench = common.createBenchmark(main, {
res: ['normal', 'setHeader', 'setHeaderWH'],
res: ['normal', 'setHeader', 'setHeaderWH', 'setHeaderImplicit'],
duration: 5,
});

Expand All @@ -15,6 +15,7 @@ const c = 50;
// normal: writeHead(status, {...})
// setHeader: statusCode = status, setHeader(...) x2
// setHeaderWH: setHeader(...), writeHead(status, ...)
// setHeaderImplicit: statusCode = status, setHeader(...) x1, framing left to end()
function main({ res, duration }) {
const server = require('../fixtures/simple-http-server.js')
.listen(0)
Expand Down
23 changes: 22 additions & 1 deletion lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
Loading