Skip to content
73 changes: 33 additions & 40 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 @@ -2106,59 +2106,55 @@ 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
// 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]])
}
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 +2179,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 +2233,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