@@ -73,6 +73,7 @@ const {
7373const {
7474 IncomingMessage,
7575 kDetachAbortSignal,
76+ kHeadersCount,
7677} = require ( '_http_incoming' ) ;
7778const {
7879 ConnResetException,
@@ -117,6 +118,10 @@ const kServerResponseStatistics = Symbol('ServerResponseStatistics');
117118const kUpgradeStream = Symbol ( 'UpgradeStream' ) ;
118119
119120const kOptimizeEmptyRequests = Symbol ( 'OptimizeEmptyRequestsOption' ) ;
121+ const kConnectionState = Symbol ( 'ConnectionState' ) ;
122+ const kResponseOptions = Symbol ( 'ResponseOptions' ) ;
123+
124+ const statusLineCache = [ ] ;
120125
121126const {
122127 hasObserver,
@@ -475,10 +480,18 @@ function writeHead(statusCode, reason, obj) {
475480 headers = obj ;
476481 }
477482
478- if ( checkInvalidHeaderChar ( this . statusMessage ) )
479- throw new ERR_INVALID_CHAR ( 'statusMessage' ) ;
483+ let statusLine ;
484+ if ( this . statusMessage === STATUS_CODES [ statusCode ] ) {
485+ // Default reason phrases contain no invalid characters and are shared
486+ // across responses: cache the whole status line.
487+ statusLine = statusLineCache [ statusCode ] ??=
488+ `HTTP/1.1 ${ statusCode } ${ this . statusMessage } \r\n` ;
489+ } else {
490+ if ( checkInvalidHeaderChar ( this . statusMessage ) )
491+ throw new ERR_INVALID_CHAR ( 'statusMessage' ) ;
480492
481- const statusLine = `HTTP/1.1 ${ statusCode } ${ this . statusMessage } \r\n` ;
493+ statusLine = `HTTP/1.1 ${ statusCode } ${ this . statusMessage } \r\n` ;
494+ }
482495
483496 if ( statusCode === 204 || statusCode === 304 ||
484497 ( statusCode >= 100 && statusCode <= 199 ) ) {
@@ -827,7 +840,11 @@ function connectionListenerInternal(server, socket) {
827840 outgoingData : 0 ,
828841 requestsCount : 0 ,
829842 keepAliveTimeoutSet : false ,
843+ onPendingData : null ,
844+ server,
845+ socket,
830846 } ;
847+ state . onPendingData = updateOutgoingData . bind ( undefined , socket , state ) ;
831848 state . onData = socketOnData . bind ( undefined ,
832849 server , socket , parser , state ) ;
833850 state . onEnd = socketOnEnd . bind ( undefined ,
@@ -1264,8 +1281,61 @@ function emitCloseNT(self) {
12641281 }
12651282}
12661283
1267- function hasBodyHeaders ( headers ) {
1268- return ( 'content-length' in headers ) || ( 'transfer-encoding' in headers ) ;
1284+ // Check for the presence of a request header by scanning rawHeaders instead
1285+ // of materializing the req.headers object. Only the first req[kHeadersCount]
1286+ // entries are considered, matching what the req.headers getter exposes.
1287+ function hasRequestHeader ( req , length , lowerName ) {
1288+ const rawHeaders = req . rawHeaders ;
1289+ const count = req [ kHeadersCount ] ;
1290+ for ( let i = 0 ; i < count ; i += 2 ) {
1291+ const key = rawHeaders [ i ] ;
1292+ if ( key . length === length &&
1293+ ( key === lowerName || key . toLowerCase ( ) === lowerName ) ) {
1294+ return true ;
1295+ }
1296+ }
1297+ return false ;
1298+ }
1299+
1300+ function hasBodyHeaders ( req ) {
1301+ const rawHeaders = req . rawHeaders ;
1302+ const count = req [ kHeadersCount ] ;
1303+ for ( let i = 0 ; i < count ; i += 2 ) {
1304+ const length = rawHeaders [ i ] . length ;
1305+ if ( length === 14 || length === 17 ) {
1306+ const key = rawHeaders [ i ] . toLowerCase ( ) ;
1307+ if ( key === 'content-length' || key === 'transfer-encoding' ) {
1308+ return true ;
1309+ }
1310+ }
1311+ }
1312+ return false ;
1313+ }
1314+
1315+ // The options object passed to the ServerResponse constructor is identical
1316+ // for every request of a given server: cache it. Custom response classes get
1317+ // a fresh object since they may retain or mutate it.
1318+ function getResponseOptions ( server , socket ) {
1319+ const highWaterMark = socket . writableHighWaterMark ;
1320+ const rejectNonStandardBodyWrites = server . rejectNonStandardBodyWrites ;
1321+ if ( server [ kServerResponse ] !== ServerResponse ) {
1322+ return { highWaterMark, rejectNonStandardBodyWrites } ;
1323+ }
1324+ let options = server [ kResponseOptions ] ;
1325+ if ( options === undefined ||
1326+ options . highWaterMark !== highWaterMark ||
1327+ options . rejectNonStandardBodyWrites !== rejectNonStandardBodyWrites ) {
1328+ options = server [ kResponseOptions ] =
1329+ { highWaterMark, rejectNonStandardBodyWrites } ;
1330+ }
1331+ return options ;
1332+ }
1333+
1334+ // Shared 'finish' listener: everything resOnFinish needs is reachable from
1335+ // the response, avoiding a bound function per request.
1336+ function onResponseFinish ( ) {
1337+ const state = this [ kConnectionState ] ;
1338+ resOnFinish ( this . req , this , state . socket , state , state . server ) ;
12691339}
12701340
12711341// The following callback is issued after the headers have been read on a
@@ -1298,15 +1368,11 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
12981368 }
12991369 }
13001370
1301- const res = new server [ kServerResponse ] ( req ,
1302- {
1303- highWaterMark : socket . writableHighWaterMark ,
1304- rejectNonStandardBodyWrites : server . rejectNonStandardBodyWrites ,
1305- } ) ;
1371+ const res = new server [ kServerResponse ] ( req , getResponseOptions ( server , socket ) ) ;
13061372 res . _keepAliveTimeout = server . keepAliveTimeout ;
13071373 res . _maxRequestsPerSocket = server . maxRequestsPerSocket ;
1308- res . _onPendingData = updateOutgoingData . bind ( undefined ,
1309- socket , state ) ;
1374+ res . _onPendingData = state . onPendingData ;
1375+ res [ kConnectionState ] = state ;
13101376
13111377 res . shouldKeepAlive = keepAlive ;
13121378 res [ kUniqueHeaders ] = server [ kUniqueHeaders ] ;
@@ -1321,7 +1387,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
13211387 }
13221388
13231389 // Check if we should optimize empty requests (those without Content-Length or Transfer-Encoding headers)
1324- const shouldOptimize = server [ kOptimizeEmptyRequests ] === true && ! hasBodyHeaders ( req . headers ) ;
1390+ const shouldOptimize = server [ kOptimizeEmptyRequests ] === true && ! hasBodyHeaders ( req ) ;
13251391
13261392 if ( shouldOptimize ) {
13271393 // Fast processing where emitting 'data', 'end' and 'close' events is
@@ -1342,9 +1408,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
13421408
13431409 // When we're finished writing the response, check if this is the last
13441410 // response, if so destroy the socket.
1345- res . on ( 'finish' ,
1346- resOnFinish . bind ( undefined ,
1347- req , res , socket , state , server ) ) ;
1411+ res . on ( 'finish' , onResponseFinish ) ;
13481412
13491413 let handled = false ;
13501414
@@ -1354,7 +1418,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
13541418 // From RFC 7230 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4
13551419 // A server MUST respond with a 400 (Bad Request) status code to any
13561420 // HTTP/1.1 request message that lacks a Host header field
1357- if ( server . requireHostHeader && req . headers . host === undefined ) {
1421+ if ( server . requireHostHeader && ! hasRequestHeader ( req , 4 , 'host' ) ) {
13581422 res . writeHead ( 400 , [ 'Connection' , 'close' ] ) ;
13591423 res . end ( ) ;
13601424 return 0 ;
@@ -1377,7 +1441,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
13771441 server . emit ( 'dropRequest' , req , socket ) ;
13781442 res . writeHead ( 503 ) ;
13791443 res . end ( ) ;
1380- } else if ( req . headers . expect !== undefined ) {
1444+ } else if ( hasRequestHeader ( req , 6 , 'expect' ) ) {
13811445 handled = true ;
13821446
13831447 if ( continueExpression . test ( req . headers . expect ) ) {
0 commit comments