Skip to content

Commit

Permalink
perf(fetch): optimize call dispatch (#2493)
Browse files Browse the repository at this point in the history
* 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"
  • Loading branch information
tsctx committed Dec 8, 2023
1 parent 7ab7d2c commit 289c874
Showing 1 changed file with 38 additions and 43 deletions.
81 changes: 38 additions & 43 deletions lib/fetch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const {
filterResponse,
makeResponse
} = require('./response')
const { Headers } = require('./headers')
const { Headers, HeadersList } = require('./headers')
const { Request, makeRequest } = require('./request')
const zlib = require('zlib')
const {
Expand Down Expand Up @@ -2075,7 +2075,7 @@ async function httpNetworkFetch (
// 20. Return response.
return response

async function dispatch ({ body }) {
function dispatch ({ body }) {
const url = requestCurrentURL(request)
/** @type {import('../..').Agent} */
const agent = fetchParams.controller.dispatcher
Expand All @@ -2085,7 +2085,7 @@ async function httpNetworkFetch (
path: url.pathname + url.search,
origin: url.origin,
method: request.method,
body: fetchParams.controller.dispatcher.isMockActive ? request.body && (request.body.source || request.body.stream) : body,
body: agent.isMockActive ? request.body && (request.body.source || request.body.stream) : body,
headers: request.headersList.entries,
maxRedirections: 0,
upgrade: request.mode === 'websocket' ? 'websocket' : undefined
Expand All @@ -2106,59 +2106,57 @@ async function httpNetworkFetch (
}
},

onHeaders (status, headersList, resume, statusText) {
onHeaders (status, rawHeaders, resume, statusText) {
if (status < 200) {
return
}

/** @type {string[]} */
let codings = []
let location = ''

const headers = new Headers()
const headersList = new HeadersList()

// For H2, the headers are a plain JS object
// For H2, the rawHeaders are a plain JS object
// We distinguish between them and iterate accordingly
if (Array.isArray(headersList)) {
for (let n = 0; n < headersList.length; n += 2) {
const key = headersList[n + 0].toString('latin1')
const val = headersList[n + 1].toString('latin1')
if (key.toLowerCase() === 'content-encoding') {
// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
// "All content-coding values are case-insensitive..."
codings = val.toLowerCase().split(',').map((x) => x.trim())
} else if (key.toLowerCase() === 'location') {
location = val
}

headers[kHeadersList].append(key, val)
if (Array.isArray(rawHeaders)) {
for (let i = 0; i < rawHeaders.length; i += 2) {
headersList.append(rawHeaders[i].toString('latin1'), rawHeaders[i + 1].toString('latin1'))
}
const contentEncoding = headersList.get('content-encoding')
if (contentEncoding) {
// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
// "All content-coding values are case-insensitive..."
codings = contentEncoding.toLowerCase().split(',').map((x) => x.trim())
}
location = headersList.get('location')
} else {
const keys = Object.keys(headersList)
for (const key of keys) {
const val = headersList[key]
if (key.toLowerCase() === 'content-encoding') {
// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
// "All content-coding values are case-insensitive..."
codings = val.toLowerCase().split(',').map((x) => x.trim()).reverse()
} else if (key.toLowerCase() === 'location') {
location = val
}

headers[kHeadersList].append(key, val)
const keys = Object.keys(rawHeaders)
for (let i = 0; i < keys.length; ++i) {
headersList.append(keys[i], rawHeaders[keys[i]])
}
// For H2, The header names are already in lowercase,
// so we can avoid the `HeadersList#get` call here.
const contentEncoding = rawHeaders['content-encoding']
if (contentEncoding) {
// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1
// "All content-coding values are case-insensitive..."
codings = contentEncoding.toLowerCase().split(',').map((x) => x.trim()).reverse()
}
location = rawHeaders.location
}

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

const decoders = []

const willFollow = request.redirect === 'follow' &&
location &&
const willFollow = location && request.redirect === 'follow' &&
redirectStatusSet.has(status)

// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
if (request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) {
for (const coding of codings) {
for (let i = 0; i < codings.length; ++i) {
const coding = codings[i]
// https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2
if (coding === 'x-gzip' || coding === 'gzip') {
decoders.push(zlib.createGunzip({
Expand All @@ -2183,7 +2181,7 @@ async function httpNetworkFetch (
resolve({
status,
statusText,
headersList: headers[kHeadersList],
headersList,
body: decoders.length
? pipeline(this.body, ...decoders, () => { })
: this.body.on('error', () => {})
Expand Down Expand Up @@ -2237,24 +2235,21 @@ async function httpNetworkFetch (
reject(error)
},

onUpgrade (status, headersList, socket) {
onUpgrade (status, rawHeaders, socket) {
if (status !== 101) {
return
}

const headers = new Headers()

for (let n = 0; n < headersList.length; n += 2) {
const key = headersList[n + 0].toString('latin1')
const val = headersList[n + 1].toString('latin1')
const headersList = new HeadersList()

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

resolve({
status,
statusText: STATUS_CODES[status],
headersList: headers[kHeadersList],
headersList,
socket
})

Expand Down

0 comments on commit 289c874

Please sign in to comment.