http: avoid toLowerCase on the server hot path · nodejs/node@0697b10 · GitHub
Skip to content

Commit 0697b10

Browse files
anonrigaduh95
authored andcommitted
http: avoid toLowerCase on the server hot path
After #65802, Host/Expect/body checks still allocate toLowerCase() copies and Expect/HTTP/1.0 TE still read req.headers. Compare names without allocating, read those values from rawHeaders, intern parser header names, and skip Title-Case toLowerCase on common outgoing fields. Refs: #65802 Signed-off-by: Yagiz Nizipli <yagiz@nizipli.com> Assisted-by: a closed-source coding agent PR-URL: #66120 Refs: #65332 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com>
1 parent 14c5e7c commit 0697b10

4 files changed

Lines changed: 165 additions & 30 deletions

File tree

‎lib/_http_incoming.js‎

Lines changed: 44 additions & 6 deletions

‎lib/_http_outgoing.js‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,10 +652,38 @@ function storeHeader(self, state, key, value, validate, lenient) {
652652
matchHeader(self, state, key, value);
653653
}
654654

655+
function lowerOutgoingHeaderName(field) {
656+
switch (field) {
657+
case 'Connection':
658+
case 'connection':
659+
return 'connection';
660+
case 'Content-Length':
661+
case 'content-length':
662+
return 'content-length';
663+
case 'Transfer-Encoding':
664+
case 'transfer-encoding':
665+
return 'transfer-encoding';
666+
case 'Date':
667+
case 'date':
668+
return 'date';
669+
case 'Expect':
670+
case 'expect':
671+
return 'expect';
672+
case 'Trailer':
673+
case 'trailer':
674+
return 'trailer';
675+
case 'Keep-Alive':
676+
case 'keep-alive':
677+
return 'keep-alive';
678+
default:
679+
return field.toLowerCase();
680+
}
681+
}
682+
655683
function matchHeader(self, state, field, value) {
656684
if (field.length < 4 || field.length > 17)
657685
return;
658-
field = field.toLowerCase();
686+
field = lowerOutgoingHeaderName(field);
659687
switch (field) {
660688
case 'connection':
661689
state.connection = true;

‎lib/_http_server.js‎

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ const {
7474
IncomingMessage,
7575
kDetachAbortSignal,
7676
kHeadersCount,
77+
asciiEqualIgnoreCase,
78+
getRawHeader,
7779
} = require('_http_incoming');
7880
const {
7981
ConnResetException,
@@ -220,7 +222,8 @@ function ServerResponse(req, options) {
220222
this._expect_continue = false;
221223

222224
if (req.httpVersionMajor < 1 || req.httpVersionMinor < 1) {
223-
this.useChunkedEncodingByDefault = chunkExpression.test(req.headers.te);
225+
this.useChunkedEncodingByDefault =
226+
chunkExpression.test(getRawHeader(req, 'te', true));
224227
this.shouldKeepAlive = false;
225228
}
226229

@@ -1282,10 +1285,8 @@ function hasRequestHeader(req, length, lowerName) {
12821285
const count = req[kHeadersCount];
12831286
for (let i = 0; i < count; i += 2) {
12841287
const key = rawHeaders[i];
1285-
if (key.length === length &&
1286-
(key === lowerName || key.toLowerCase() === lowerName)) {
1288+
if (key.length === length && asciiEqualIgnoreCase(key, lowerName))
12871289
return true;
1288-
}
12891290
}
12901291
return false;
12911292
}
@@ -1294,12 +1295,14 @@ function hasBodyHeaders(req) {
12941295
const rawHeaders = req.rawHeaders;
12951296
const count = req[kHeadersCount];
12961297
for (let i = 0; i < count; i += 2) {
1297-
const length = rawHeaders[i].length;
1298-
if (length === 14 || length === 17) {
1299-
const key = rawHeaders[i].toLowerCase();
1300-
if (key === 'content-length' || key === 'transfer-encoding') {
1298+
const key = rawHeaders[i];
1299+
const length = key.length;
1300+
if (length === 14) {
1301+
if (asciiEqualIgnoreCase(key, 'content-length'))
1302+
return true;
1303+
} else if (length === 17) {
1304+
if (asciiEqualIgnoreCase(key, 'transfer-encoding'))
13011305
return true;
1302-
}
13031306
}
13041307
}
13051308
return false;
@@ -1434,22 +1437,25 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
14341437
server.emit('dropRequest', req, socket);
14351438
res.writeHead(503);
14361439
res.end();
1437-
} else if (hasRequestHeader(req, 6, 'expect')) {
1438-
handled = true;
1439-
1440-
if (continueExpression.test(req.headers.expect)) {
1441-
res._expect_continue = true;
1442-
if (server.listenerCount('checkContinue') > 0) {
1443-
server.emit('checkContinue', req, res);
1440+
} else {
1441+
const expect = getRawHeader(req, 'expect', true);
1442+
if (expect !== undefined) {
1443+
handled = true;
1444+
1445+
if (continueExpression.test(expect)) {
1446+
res._expect_continue = true;
1447+
if (server.listenerCount('checkContinue') > 0) {
1448+
server.emit('checkContinue', req, res);
1449+
} else {
1450+
res.writeContinue();
1451+
server.emit('request', req, res);
1452+
}
1453+
} else if (server.listenerCount('checkExpectation') > 0) {
1454+
server.emit('checkExpectation', req, res);
14441455
} else {
1445-
res.writeContinue();
1446-
server.emit('request', req, res);
1456+
res.writeHead(417);
1457+
res.end();
14471458
}
1448-
} else if (server.listenerCount('checkExpectation') > 0) {
1449-
server.emit('checkExpectation', req, res);
1450-
} else {
1451-
res.writeHead(417);
1452-
res.end();
14531459
}
14541460
}
14551461
}
Lines changed: 63 additions & 0 deletions

0 commit comments

Comments
 (0)