Skip to content

Commit 8cf5ae0

Browse files
committed
http: refactor to use more primordials
PR-URL: #36194 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent e57d8af commit 8cf5ae0

8 files changed

+147
-88
lines changed

lib/_http_agent.js

+28-17
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,22 @@
2222
'use strict';
2323

2424
const {
25+
ArrayPrototypeIncludes,
26+
ArrayPrototypeIndexOf,
27+
ArrayPrototypePop,
28+
ArrayPrototypePush,
29+
ArrayPrototypeShift,
30+
ArrayPrototypeSplice,
31+
FunctionPrototypeCall,
2532
NumberIsNaN,
2633
ObjectCreate,
2734
ObjectKeys,
2835
ObjectSetPrototypeOf,
2936
ObjectValues,
37+
StringPrototypeIndexOf,
38+
StringPrototypeSplit,
39+
StringPrototypeStartsWith,
40+
StringPrototypeSubstr,
3041
Symbol,
3142
} = primordials;
3243

@@ -79,7 +90,7 @@ function Agent(options) {
7990
if (!(this instanceof Agent))
8091
return new Agent(options);
8192

82-
EventEmitter.call(this);
93+
FunctionPrototypeCall(EventEmitter, this);
8394

8495
this.defaultPort = 80;
8596
this.protocol = 'http:';
@@ -126,7 +137,7 @@ function Agent(options) {
126137

127138
const requests = this.requests[name];
128139
if (requests && requests.length) {
129-
const req = requests.shift();
140+
const req = ArrayPrototypeShift(requests);
130141
const reqAsyncRes = req[kRequestAsyncResource];
131142
if (reqAsyncRes) {
132143
// Run request within the original async context.
@@ -172,7 +183,7 @@ function Agent(options) {
172183
this.removeSocket(socket, options);
173184

174185
socket.once('error', freeSocketErrorListener);
175-
freeSockets.push(socket);
186+
ArrayPrototypePush(freeSockets, socket);
176187
});
177188

178189
// Don't emit keylog events unless there is a listener for them.
@@ -251,11 +262,11 @@ Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */,
251262
let socket;
252263
if (freeSockets) {
253264
while (freeSockets.length && freeSockets[0].destroyed) {
254-
freeSockets.shift();
265+
ArrayPrototypeShift(freeSockets);
255266
}
256267
socket = this.scheduling === 'fifo' ?
257-
freeSockets.shift() :
258-
freeSockets.pop();
268+
ArrayPrototypeShift(freeSockets) :
269+
ArrayPrototypePop(freeSockets);
259270
if (!freeSockets.length)
260271
delete this.freeSockets[name];
261272
}
@@ -267,7 +278,7 @@ Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */,
267278
asyncResetHandle(socket);
268279
this.reuseSocket(socket, req);
269280
setRequestSocket(this, req, socket);
270-
this.sockets[name].push(socket);
281+
ArrayPrototypePush(this.sockets[name], socket);
271282
this.totalSocketCount++;
272283
} else if (sockLen < this.maxSockets &&
273284
this.totalSocketCount < this.maxTotalSockets) {
@@ -291,7 +302,7 @@ Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */,
291302
// Used to capture the original async context.
292303
req[kRequestAsyncResource] = new AsyncResource('QueuedRequest');
293304

294-
this.requests[name].push(req);
305+
ArrayPrototypePush(this.requests[name], req);
295306
}
296307
};
297308

@@ -315,7 +326,7 @@ Agent.prototype.createSocket = function createSocket(req, options, cb) {
315326
if (!this.sockets[name]) {
316327
this.sockets[name] = [];
317328
}
318-
this.sockets[name].push(s);
329+
ArrayPrototypePush(this.sockets[name], s);
319330
this.totalSocketCount++;
320331
debug('sockets', name, this.sockets[name].length, this.totalSocketCount);
321332
installListeners(this, s, options);
@@ -340,16 +351,16 @@ function calculateServerName(options, req) {
340351
// abc:123 => abc
341352
// [::1] => ::1
342353
// [::1]:123 => ::1
343-
if (hostHeader.startsWith('[')) {
344-
const index = hostHeader.indexOf(']');
354+
if (StringPrototypeStartsWith(hostHeader, '[')) {
355+
const index = StringPrototypeIndexOf(hostHeader, ']');
345356
if (index === -1) {
346357
// Leading '[', but no ']'. Need to do something...
347358
servername = hostHeader;
348359
} else {
349-
servername = hostHeader.substr(1, index - 1);
360+
servername = StringPrototypeSubstr(hostHeader, 1, index - 1);
350361
}
351362
} else {
352-
servername = hostHeader.split(':', 1)[0];
363+
servername = StringPrototypeSplit(hostHeader, ':', 1)[0];
353364
}
354365
}
355366
// Don't implicitly set invalid (IP) servernames.
@@ -381,7 +392,7 @@ function installListeners(agent, s, options) {
381392
// TODO(ronag): Always destroy, even if not in free list.
382393
const sockets = agent.freeSockets;
383394
for (const name of ObjectKeys(sockets)) {
384-
if (sockets[name].includes(s)) {
395+
if (ArrayPrototypeIncludes(sockets[name], s)) {
385396
return s.destroy();
386397
}
387398
}
@@ -413,13 +424,13 @@ Agent.prototype.removeSocket = function removeSocket(s, options) {
413424

414425
// If the socket was destroyed, remove it from the free buffers too.
415426
if (!s.writable)
416-
sets.push(this.freeSockets);
427+
ArrayPrototypePush(sets, this.freeSockets);
417428

418429
for (const sockets of sets) {
419430
if (sockets[name]) {
420-
const index = sockets[name].indexOf(s);
431+
const index = ArrayPrototypeIndexOf(sockets[name], s);
421432
if (index !== -1) {
422-
sockets[name].splice(index, 1);
433+
ArrayPrototypeSplice(sockets[name], index, 1);
423434
// Don't leak
424435
if (sockets[name].length === 0)
425436
delete sockets[name];

lib/_http_client.js

+18-10
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,20 @@ const {
2525
ArrayIsArray,
2626
Boolean,
2727
Error,
28+
FunctionPrototypeCall,
2829
NumberIsFinite,
2930
ObjectAssign,
3031
ObjectKeys,
3132
ObjectSetPrototypeOf,
33+
ReflectApply,
34+
RegExpPrototypeTest,
3235
String,
33-
Symbol
36+
StringPrototypeCharCodeAt,
37+
StringPrototypeIncludes,
38+
StringPrototypeIndexOf,
39+
StringPrototypeToUpperCase,
40+
Symbol,
41+
TypedArrayPrototypeSlice,
3442
} = primordials;
3543

3644
const net = require('net');
@@ -91,7 +99,7 @@ class HTTPClientAsyncResource {
9199

92100
let urlWarningEmitted = false;
93101
function ClientRequest(input, options, cb) {
94-
OutgoingMessage.call(this);
102+
FunctionPrototypeCall(OutgoingMessage, this);
95103

96104
if (typeof input === 'string') {
97105
const urlStr = input;
@@ -151,7 +159,7 @@ function ClientRequest(input, options, cb) {
151159

152160
if (options.path) {
153161
const path = String(options.path);
154-
if (INVALID_PATH_REGEX.test(path))
162+
if (RegExpPrototypeTest(INVALID_PATH_REGEX, path))
155163
throw new ERR_UNESCAPED_CHARACTERS('Request path');
156164
}
157165

@@ -187,7 +195,7 @@ function ClientRequest(input, options, cb) {
187195
if (!checkIsHttpToken(method)) {
188196
throw new ERR_INVALID_HTTP_TOKEN('Method', method);
189197
}
190-
method = this.method = method.toUpperCase();
198+
method = this.method = StringPrototypeToUpperCase(method);
191199
} else {
192200
method = this.method = 'GET';
193201
}
@@ -266,10 +274,10 @@ function ClientRequest(input, options, cb) {
266274
// For the Host header, ensure that IPv6 addresses are enclosed
267275
// in square brackets, as defined by URI formatting
268276
// https://tools.ietf.org/html/rfc3986#section-3.2.2
269-
const posColon = hostHeader.indexOf(':');
277+
const posColon = StringPrototypeIndexOf(hostHeader, ':');
270278
if (posColon !== -1 &&
271-
hostHeader.includes(':', posColon + 1) &&
272-
hostHeader.charCodeAt(0) !== 91/* '[' */) {
279+
StringPrototypeIncludes(hostHeader, ':', posColon + 1) &&
280+
StringPrototypeCharCodeAt(hostHeader, 0) !== 91/* '[' */) {
273281
hostHeader = `[${hostHeader}]`;
274282
}
275283

@@ -337,7 +345,7 @@ ObjectSetPrototypeOf(ClientRequest, OutgoingMessage);
337345

338346
ClientRequest.prototype._finish = function _finish() {
339347
DTRACE_HTTP_CLIENT_REQUEST(this, this.socket);
340-
OutgoingMessage.prototype._finish.call(this);
348+
FunctionPrototypeCall(OutgoingMessage.prototype._finish, this);
341349
};
342350

343351
ClientRequest.prototype._implicitHeader = function _implicitHeader() {
@@ -535,7 +543,7 @@ function socketOnData(d) {
535543
parser.finish();
536544
freeParser(parser, req, socket);
537545

538-
const bodyHead = d.slice(bytesParsed, d.length);
546+
const bodyHead = TypedArrayPrototypeSlice(d, bytesParsed, d.length);
539547

540548
const eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
541549
if (req.listenerCount(eventName) > 0) {
@@ -831,7 +839,7 @@ function _deferToConnect(method, arguments_, cb) {
831839

832840
const callSocketMethod = () => {
833841
if (method)
834-
this.socket[method].apply(this.socket, arguments_);
842+
ReflectApply(this.socket[method], this.socket, arguments_);
835843

836844
if (typeof cb === 'function')
837845
cb();

lib/_http_common.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222
'use strict';
2323

2424
const {
25+
ArrayPrototypeConcat,
2526
MathMin,
2627
Symbol,
28+
RegExpPrototypeTest,
29+
TypedArrayPrototypeSlice,
2730
} = primordials;
2831
const { setImmediate } = require('timers');
2932

@@ -63,7 +66,7 @@ function parserOnHeaders(headers, url) {
6366
// Once we exceeded headers limit - stop collecting them
6467
if (this.maxHeaderPairs <= 0 ||
6568
this._headers.length < this.maxHeaderPairs) {
66-
this._headers = this._headers.concat(headers);
69+
this._headers = ArrayPrototypeConcat(this._headers, headers);
6770
}
6871
this._url += url;
6972
}
@@ -135,7 +138,7 @@ function parserOnBody(b, start, len) {
135138

136139
// Pretend this was the result of a stream._read call.
137140
if (len > 0 && !stream._dumped) {
138-
const slice = b.slice(start, start + len);
141+
const slice = TypedArrayPrototypeSlice(b, start, start + len);
139142
const ret = stream.push(slice);
140143
if (!ret)
141144
readStop(this.socket);
@@ -217,7 +220,7 @@ const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;
217220
* See https://tools.ietf.org/html/rfc7230#section-3.2.6
218221
*/
219222
function checkIsHttpToken(val) {
220-
return tokenRegExp.test(val);
223+
return RegExpPrototypeTest(tokenRegExp, val);
221224
}
222225

223226
const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
@@ -228,7 +231,7 @@ const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
228231
* field-vchar = VCHAR / obs-text
229232
*/
230233
function checkInvalidHeaderChar(val) {
231-
return headerCharRegex.test(val);
234+
return RegExpPrototypeTest(headerCharRegex, val);
232235
}
233236

234237
function cleanParser(parser) {

lib/_http_incoming.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,13 @@
2222
'use strict';
2323

2424
const {
25+
ArrayPrototypePush,
26+
FunctionPrototypeCall,
2527
ObjectDefineProperty,
2628
ObjectSetPrototypeOf,
29+
StringPrototypeCharCodeAt,
30+
StringPrototypeSlice,
31+
StringPrototypeToLowerCase,
2732
Symbol
2833
} = primordials;
2934

@@ -54,7 +59,7 @@ function IncomingMessage(socket) {
5459
};
5560
}
5661

57-
Readable.call(this, streamOptions);
62+
FunctionPrototypeCall(Readable, this, streamOptions);
5863

5964
this._readableState.readingMore = true;
6065

@@ -312,7 +317,7 @@ function matchKnownFields(field, lowercased) {
312317
if (lowercased) {
313318
return '\u0000' + field;
314319
}
315-
return matchKnownFields(field.toLowerCase(), true);
320+
return matchKnownFields(StringPrototypeToLowerCase(field), true);
316321
}
317322
// Add the given (field, value) pair to the message
318323
//
@@ -326,9 +331,9 @@ function matchKnownFields(field, lowercased) {
326331
IncomingMessage.prototype._addHeaderLine = _addHeaderLine;
327332
function _addHeaderLine(field, value, dest) {
328333
field = matchKnownFields(field);
329-
const flag = field.charCodeAt(0);
334+
const flag = StringPrototypeCharCodeAt(field, 0);
330335
if (flag === 0 || flag === 2) {
331-
field = field.slice(1);
336+
field = StringPrototypeSlice(field, 1);
332337
// Make a delimited list
333338
if (typeof dest[field] === 'string') {
334339
dest[field] += (flag === 0 ? ', ' : '; ') + value;
@@ -338,7 +343,7 @@ function _addHeaderLine(field, value, dest) {
338343
} else if (flag === 1) {
339344
// Array header -- only Set-Cookie at the moment
340345
if (dest['set-cookie'] !== undefined) {
341-
dest['set-cookie'].push(value);
346+
ArrayPrototypePush(dest['set-cookie'], value);
342347
} else {
343348
dest['set-cookie'] = [value];
344349
}

0 commit comments

Comments
 (0)