Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib: revert primordials in a hot path #38248

Closed
wants to merge 2 commits into from
Closed
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: 2 additions & 4 deletions lib/_http_common.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@
'use strict';

const {
ArrayPrototypePushApply,
MathMin,
Symbol,
RegExpPrototypeTest,
TypedArrayPrototypeSlice,
} = primordials;
const { setImmediate } = require('timers');

Expand Down Expand Up @@ -66,7 +64,7 @@ function parserOnHeaders(headers, url) {
// Once we exceeded headers limit - stop collecting them
if (this.maxHeaderPairs <= 0 ||
this._headers.length < this.maxHeaderPairs) {
ArrayPrototypePushApply(this._headers, headers);
this._headers.push(...headers);
}
this._url += url;
}
Expand Down Expand Up @@ -138,7 +136,7 @@ function parserOnBody(b, start, len) {

// Pretend this was the result of a stream._read call.
if (len > 0 && !stream._dumped) {
const slice = TypedArrayPrototypeSlice(b, start, start + len);
const slice = b.slice(start, start + len);
const ret = stream.push(slice);
if (!ret)
readStop(this.socket);
Expand Down
6 changes: 2 additions & 4 deletions lib/_http_incoming.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
'use strict';

const {
ArrayPrototypePush,
FunctionPrototypeCall,
ObjectDefineProperty,
ObjectSetPrototypeOf,
StringPrototypeCharCodeAt,
Expand Down Expand Up @@ -59,7 +57,7 @@ function IncomingMessage(socket) {
};
}

FunctionPrototypeCall(Readable, this, streamOptions);
Readable.call(this, streamOptions);

this._readableState.readingMore = true;

Expand Down Expand Up @@ -350,7 +348,7 @@ function _addHeaderLine(field, value, dest) {
} else if (flag === 1) {
// Array header -- only Set-Cookie at the moment
if (dest['set-cookie'] !== undefined) {
ArrayPrototypePush(dest['set-cookie'], value);
dest['set-cookie'].push(value);
} else {
dest['set-cookie'] = [value];
}
Expand Down
23 changes: 9 additions & 14 deletions lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,7 @@
const {
Array,
ArrayIsArray,
ArrayPrototypeForEach,
ArrayPrototypeJoin,
ArrayPrototypePush,
ArrayPrototypeUnshift,
FunctionPrototype,
FunctionPrototypeBind,
FunctionPrototypeCall,
MathFloor,
NumberPrototypeToString,
ObjectCreate,
Expand Down Expand Up @@ -88,7 +82,7 @@ const { CRLF } = common;

const kCorked = Symbol('corked');

const nop = FunctionPrototype;
const nop = () => {};

const RE_CONN_CLOSE = /(?:^|\W)close(?:$|\W)/i;
const RE_TE_CHUNKED = common.chunkExpression;
Expand All @@ -101,7 +95,7 @@ function isCookieField(s) {
}

function OutgoingMessage() {
FunctionPrototypeCall(Stream, this);
Stream.call(this);

// Queue that holds all currently pending data, until the response will be
// assigned to the socket (until it will its turn in the HTTP pipeline).
Expand Down Expand Up @@ -331,7 +325,7 @@ OutgoingMessage.prototype._send = function _send(data, encoding, callback) {
data = this._header + data;
} else {
const header = this._header;
ArrayPrototypeUnshift(this.outputData, {
this.outputData.unshift({
data: header,
encoding: 'latin1',
callback: null
Expand Down Expand Up @@ -368,7 +362,7 @@ function _writeRaw(data, encoding, callback) {
return conn.write(data, encoding, callback);
}
// Buffer, as long as we're not destroyed.
ArrayPrototypePush(this.outputData, { data, encoding, callback });
this.outputData.push({ data, encoding, callback });
this.outputSize += data.length;
this._onPendingData(data.length);
return this.outputSize < HIGH_WATER_MARK;
Expand Down Expand Up @@ -397,9 +391,10 @@ function _storeHeader(firstLine, headers) {
}
} else if (ArrayIsArray(headers)) {
if (headers.length && ArrayIsArray(headers[0])) {
ArrayPrototypeForEach(headers, (entry) =>
processHeader(this, state, entry[0], entry[1], true)
);
for (let i = 0; i < headers.length; i++) {
const entry = headers[i];
processHeader(this, state, entry[0], entry[1], true);
}
} else {
if (headers.length % 2 !== 0) {
throw new ERR_INVALID_ARG_VALUE('headers', headers);
Expand Down Expand Up @@ -877,7 +872,7 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
if (typeof callback === 'function')
this.once('finish', callback);

const finish = FunctionPrototypeBind(onFinish, undefined, this);
const finish = onFinish.bind(undefined, this);

if (this._hasBody && this.chunkedEncoding) {
this._send('0\r\n' + this._trailer + '\r\n', 'latin1', finish);
Expand Down
88 changes: 41 additions & 47 deletions lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,12 @@

const {
ArrayIsArray,
ArrayPrototypeForEach,
ArrayPrototypePush,
ArrayPrototypeShift,
Error,
FunctionPrototype,
FunctionPrototypeBind,
FunctionPrototypeCall,
ObjectKeys,
ObjectSetPrototypeOf,
ReflectApply,
RegExpPrototypeTest,
Symbol,
SymbolFor,
TypedArrayPrototypeSlice,
} = primordials;

const net = require('net');
Expand Down Expand Up @@ -185,7 +177,7 @@ class HTTPServerAsyncResource {
}

function ServerResponse(req) {
FunctionPrototypeCall(OutgoingMessage, this);
OutgoingMessage.call(this);

if (req.method === 'HEAD') this._hasBody = false;

Expand All @@ -212,7 +204,7 @@ ObjectSetPrototypeOf(ServerResponse, OutgoingMessage);
ServerResponse.prototype._finish = function _finish() {
DTRACE_HTTP_SERVER_RESPONSE(this.socket);
emitStatistics(this[kServerResponseStatistics]);
FunctionPrototypeCall(OutgoingMessage.prototype._finish, this);
OutgoingMessage.prototype._finish.call(this);
};


Expand Down Expand Up @@ -386,7 +378,7 @@ function Server(options, requestListener) {
validateBoolean(insecureHTTPParser, 'options.insecureHTTPParser');
this.insecureHTTPParser = insecureHTTPParser;

FunctionPrototypeCall(net.Server, this, { allowHalfOpen: true });
net.Server.call(this, { allowHalfOpen: true });

if (requestListener) {
this.on('request', requestListener);
Expand Down Expand Up @@ -422,17 +414,19 @@ Server.prototype[EE.captureRejectionSymbol] = function(err, event, ...args) {
const { 1: res } = args;
if (!res.headersSent && !res.writableEnded) {
// Don't leak headers.
ArrayPrototypeForEach(res.getHeaderNames(),
(name) => res.removeHeader(name));
const names = res.getHeaderNames();
for (let i = 0; i < names.length; i++) {
res.removeHeader(names[i]);
}
res.statusCode = 500;
res.end(STATUS_CODES[500]);
} else {
res.destroy();
}
break;
default:
ReflectApply(net.Server.prototype[SymbolFor('nodejs.rejection')],
this, arguments);
net.Server.prototype[SymbolFor('nodejs.rejection')]
.apply(this, arguments);
}
};

Expand Down Expand Up @@ -493,20 +487,20 @@ function connectionListenerInternal(server, socket) {
outgoingData: 0,
keepAliveTimeoutSet: false
};
state.onData = FunctionPrototypeBind(socketOnData, undefined,
server, socket, parser, state);
state.onEnd = FunctionPrototypeBind(socketOnEnd, undefined,
server, socket, parser, state);
state.onClose = FunctionPrototypeBind(socketOnClose, undefined,
socket, state);
state.onDrain = FunctionPrototypeBind(socketOnDrain, undefined,
socket, state);
state.onData = socketOnData.bind(undefined,
server, socket, parser, state);
state.onEnd = socketOnEnd.bind(undefined,
server, socket, parser, state);
state.onClose = socketOnClose.bind(undefined,
socket, state);
state.onDrain = socketOnDrain.bind(undefined,
socket, state);
socket.on('data', state.onData);
socket.on('error', socketOnError);
socket.on('end', state.onEnd);
socket.on('close', state.onClose);
socket.on('drain', state.onDrain);
parser.onIncoming = FunctionPrototypeBind(parserOnIncoming, undefined,
parser.onIncoming = parserOnIncoming.bind(undefined,
server, socket, state);

// We are consuming socket, so it won't get any actual data
Expand All @@ -527,18 +521,18 @@ function connectionListenerInternal(server, socket) {
parser.consume(socket._handle);
}
parser[kOnExecute] =
FunctionPrototypeBind(onParserExecute, undefined,
server, socket, parser, state);
onParserExecute.bind(undefined,
server, socket, parser, state);

parser[kOnTimeout] =
FunctionPrototypeBind(onParserTimeout, undefined,
server, socket);
onParserTimeout.bind(undefined,
server, socket);

// When receiving new requests on the same socket (pipelining or keep alive)
// make sure the requestTimeout is active.
parser[kOnMessageBegin] =
FunctionPrototypeBind(setRequestTimeout, undefined,
server, socket);
setRequestTimeout.bind(undefined,
server, socket);

// This protects from DOS attack where an attacker establish the connection
// without sending any data on applications where server.timeout is left to
Expand Down Expand Up @@ -594,7 +588,7 @@ function socketOnClose(socket, state) {

function abortIncoming(incoming) {
while (incoming.length) {
const req = ArrayPrototypeShift(incoming);
const req = incoming.shift();
req.destroy(connResetException('aborted'));
}
// Abort socket._httpMessage ?
Expand All @@ -606,7 +600,7 @@ function socketOnEnd(server, socket, parser, state) {
if (ret instanceof Error) {
debug('parse error');
// socketOnError has additional logic and will call socket.destroy(err).
FunctionPrototypeCall(socketOnError, socket, ret);
socketOnError.call(socket, ret);
} else if (!server.httpAllowHalfOpen) {
socket.end();
} else if (state.outgoing.length) {
Expand All @@ -629,7 +623,7 @@ function socketOnData(server, socket, parser, state, d) {
function onRequestTimeout(socket) {
socket[kRequestTimeout] = undefined;
// socketOnError has additional logic and will call socket.destroy(err).
ReflectApply(socketOnError, socket, [new ERR_HTTP_REQUEST_TIMEOUT()]);
socketOnError.call(socket, new ERR_HTTP_REQUEST_TIMEOUT());
}

function onParserExecute(server, socket, parser, state, ret) {
Expand All @@ -649,7 +643,7 @@ function onParserTimeout(server, socket) {
socket.destroy();
}

const noop = FunctionPrototype;
const noop = () => {};
const badRequestResponse = Buffer.from(
`HTTP/1.1 400 ${STATUS_CODES[400]}${CRLF}` +
`Connection: close${CRLF}${CRLF}`, 'ascii'
Expand Down Expand Up @@ -696,7 +690,7 @@ function onParserExecuteCommon(server, socket, parser, state, ret, d) {
prepareError(ret, parser, d);
ret.rawPacket = d || parser.getCurrentBuffer();
debug('parse error', ret);
FunctionPrototypeCall(socketOnError, socket, ret);
socketOnError.call(socket, ret);
} else if (parser.incoming && parser.incoming.upgrade) {
// Upgrade or CONNECT
const req = parser.incoming;
Expand All @@ -719,7 +713,7 @@ function onParserExecuteCommon(server, socket, parser, state, ret, d) {
const eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
if (eventName === 'upgrade' || server.listenerCount(eventName) > 0) {
debug('SERVER have listener for %s', eventName);
const bodyHead = TypedArrayPrototypeSlice(d, ret, d.length);
const bodyHead = d.slice(ret, d.length);

socket.readableFlowing = null;

Expand All @@ -738,7 +732,7 @@ function onParserExecuteCommon(server, socket, parser, state, ret, d) {
// When receiving new requests on the same socket (pipelining or keep alive)
// make sure the requestTimeout is active.
parser[kOnMessageBegin] =
FunctionPrototypeBind(setRequestTimeout, undefined, server, socket);
setRequestTimeout.bind(undefined, server, socket);
}

if (socket._paused && socket.parser) {
Expand Down Expand Up @@ -802,7 +796,7 @@ function resOnFinish(req, res, socket, state, server) {
// array will be empty.
assert(state.incoming.length === 0 || state.incoming[0] === req);

ArrayPrototypeShift(state.incoming);
state.incoming.shift();

// If the user never called req.read(), and didn't pipe() or
// .resume() or .on('data'), then we call req._dump() so that the
Expand Down Expand Up @@ -835,7 +829,7 @@ function resOnFinish(req, res, socket, state, server) {
}
} else {
// Start sending the next message
const m = ArrayPrototypeShift(state.outgoing);
const m = state.outgoing.shift();
if (m) {
m.assignSocket(socket);
}
Expand All @@ -861,7 +855,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
return 2;
}

ArrayPrototypePush(state.incoming, req);
state.incoming.push(req);

// If the writable end isn't consuming, then stop reading
// so that we don't become overwhelmed by a flood of
Expand All @@ -879,8 +873,8 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {

const res = new server[kServerResponse](req);
res._keepAliveTimeout = server.keepAliveTimeout;
res._onPendingData = FunctionPrototypeBind(updateOutgoingData, undefined,
socket, state);
res._onPendingData = updateOutgoingData.bind(undefined,
socket, state);

res.shouldKeepAlive = keepAlive;
DTRACE_HTTP_SERVER_REQUEST(req, socket);
Expand All @@ -896,16 +890,16 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {

if (socket._httpMessage) {
// There are already pending outgoing res, append.
ArrayPrototypePush(state.outgoing, res);
state.outgoing.push(res);
} else {
res.assignSocket(socket);
}

// When we're finished writing the response, check if this is the last
// response, if so destroy the socket.
res.on('finish',
FunctionPrototypeBind(resOnFinish, undefined,
req, res, socket, state, server));
resOnFinish.bind(undefined,
req, res, socket, state, server));

if (req.headers.expect !== undefined &&
(req.httpVersionMajor === 1 && req.httpVersionMinor === 1)) {
Expand Down Expand Up @@ -977,8 +971,8 @@ function unconsume(parser, socket) {

function generateSocketListenerWrapper(originalFnName) {
return function socketListenerWrap(ev, fn) {
const res = ReflectApply(net.Socket.prototype[originalFnName], this,
[ev, fn]);
const res = net.Socket.prototype[originalFnName].call(this,
ev, fn);
if (!this.parser) {
this.on = net.Socket.prototype.on;
this.addListener = net.Socket.prototype.addListener;
Expand Down
Loading