Skip to content

Commit af58671

Browse files
tsctxcrysmags
authored andcommitted
perf(fetch): optimize call dispatch (nodejs#2493)
* perf(fetch): optimize call `dispatch` * fixup * format * fixup * refactor * fixup * fixup * fixup * perf * fix: add missing async * add comment * fix comment * fix comment * Revert "fix: add missing async"
1 parent 4f387a6 commit af58671

File tree

1 file changed

+38
-43
lines changed

1 file changed

+38
-43
lines changed

lib/fetch/index.js

+38-43
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const {
99
filterResponse,
1010
makeResponse
1111
} = require('./response')
12-
const { Headers } = require('./headers')
12+
const { Headers, HeadersList } = require('./headers')
1313
const { Request, makeRequest } = require('./request')
1414
const zlib = require('zlib')
1515
const {
@@ -2075,7 +2075,7 @@ async function httpNetworkFetch (
20752075
// 20. Return response.
20762076
return response
20772077

2078-
async function dispatch ({ body }) {
2078+
function dispatch ({ body }) {
20792079
const url = requestCurrentURL(request)
20802080
/** @type {import('../..').Agent} */
20812081
const agent = fetchParams.controller.dispatcher
@@ -2085,7 +2085,7 @@ async function httpNetworkFetch (
20852085
path: url.pathname + url.search,
20862086
origin: url.origin,
20872087
method: request.method,
2088-
body: fetchParams.controller.dispatcher.isMockActive ? request.body && (request.body.source || request.body.stream) : body,
2088+
body: agent.isMockActive ? request.body && (request.body.source || request.body.stream) : body,
20892089
headers: request.headersList.entries,
20902090
maxRedirections: 0,
20912091
upgrade: request.mode === 'websocket' ? 'websocket' : undefined
@@ -2106,59 +2106,57 @@ async function httpNetworkFetch (
21062106
}
21072107
},
21082108

2109-
onHeaders (status, headersList, resume, statusText) {
2109+
onHeaders (status, rawHeaders, resume, statusText) {
21102110
if (status < 200) {
21112111
return
21122112
}
21132113

2114+
/** @type {string[]} */
21142115
let codings = []
21152116
let location = ''
21162117

2117-
const headers = new Headers()
2118+
const headersList = new HeadersList()
21182119

2119-
// For H2, the headers are a plain JS object
2120+
// For H2, the rawHeaders are a plain JS object
21202121
// We distinguish between them and iterate accordingly
2121-
if (Array.isArray(headersList)) {
2122-
for (let n = 0; n < headersList.length; n += 2) {
2123-
const key = headersList[n + 0].toString('latin1')
2124-
const val = headersList[n + 1].toString('latin1')
2125-
if (key.toLowerCase() === 'content-encoding') {
2126-
// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
2127-
// "All content-coding values are case-insensitive..."
2128-
codings = val.toLowerCase().split(',').map((x) => x.trim())
2129-
} else if (key.toLowerCase() === 'location') {
2130-
location = val
2131-
}
2132-
2133-
headers[kHeadersList].append(key, val)
2122+
if (Array.isArray(rawHeaders)) {
2123+
for (let i = 0; i < rawHeaders.length; i += 2) {
2124+
headersList.append(rawHeaders[i].toString('latin1'), rawHeaders[i + 1].toString('latin1'))
21342125
}
2126+
const contentEncoding = headersList.get('content-encoding')
2127+
if (contentEncoding) {
2128+
// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
2129+
// "All content-coding values are case-insensitive..."
2130+
codings = contentEncoding.toLowerCase().split(',').map((x) => x.trim())
2131+
}
2132+
location = headersList.get('location')
21352133
} else {
2136-
const keys = Object.keys(headersList)
2137-
for (const key of keys) {
2138-
const val = headersList[key]
2139-
if (key.toLowerCase() === 'content-encoding') {
2140-
// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
2141-
// "All content-coding values are case-insensitive..."
2142-
codings = val.toLowerCase().split(',').map((x) => x.trim()).reverse()
2143-
} else if (key.toLowerCase() === 'location') {
2144-
location = val
2145-
}
2146-
2147-
headers[kHeadersList].append(key, val)
2134+
const keys = Object.keys(rawHeaders)
2135+
for (let i = 0; i < keys.length; ++i) {
2136+
headersList.append(keys[i], rawHeaders[keys[i]])
2137+
}
2138+
// For H2, The header names are already in lowercase,
2139+
// so we can avoid the `HeadersList#get` call here.
2140+
const contentEncoding = rawHeaders['content-encoding']
2141+
if (contentEncoding) {
2142+
// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
2143+
// "All content-coding values are case-insensitive..."
2144+
codings = contentEncoding.toLowerCase().split(',').map((x) => x.trim()).reverse()
21482145
}
2146+
location = rawHeaders.location
21492147
}
21502148

21512149
this.body = new Readable({ read: resume })
21522150

21532151
const decoders = []
21542152

2155-
const willFollow = request.redirect === 'follow' &&
2156-
location &&
2153+
const willFollow = location && request.redirect === 'follow' &&
21572154
redirectStatusSet.has(status)
21582155

21592156
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
21602157
if (request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) {
2161-
for (const coding of codings) {
2158+
for (let i = 0; i < codings.length; ++i) {
2159+
const coding = codings[i]
21622160
// https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2
21632161
if (coding === 'x-gzip' || coding === 'gzip') {
21642162
decoders.push(zlib.createGunzip({
@@ -2183,7 +2181,7 @@ async function httpNetworkFetch (
21832181
resolve({
21842182
status,
21852183
statusText,
2186-
headersList: headers[kHeadersList],
2184+
headersList,
21872185
body: decoders.length
21882186
? pipeline(this.body, ...decoders, () => { })
21892187
: this.body.on('error', () => {})
@@ -2237,24 +2235,21 @@ async function httpNetworkFetch (
22372235
reject(error)
22382236
},
22392237

2240-
onUpgrade (status, headersList, socket) {
2238+
onUpgrade (status, rawHeaders, socket) {
22412239
if (status !== 101) {
22422240
return
22432241
}
22442242

2245-
const headers = new Headers()
2246-
2247-
for (let n = 0; n < headersList.length; n += 2) {
2248-
const key = headersList[n + 0].toString('latin1')
2249-
const val = headersList[n + 1].toString('latin1')
2243+
const headersList = new HeadersList()
22502244

2251-
headers[kHeadersList].append(key, val)
2245+
for (let i = 0; i < rawHeaders.length; i += 2) {
2246+
headersList.append(rawHeaders[i].toString('latin1'), rawHeaders[i + 1].toString('latin1'))
22522247
}
22532248

22542249
resolve({
22552250
status,
22562251
statusText: STATUS_CODES[status],
2257-
headersList: headers[kHeadersList],
2252+
headersList,
22582253
socket
22592254
})
22602255

0 commit comments

Comments
 (0)