(controller, status, headers, statusText)
| 2242 | }, |
| 2243 | |
| 2244 | onResponseStart (controller, status, headers, statusText) { |
| 2245 | if (status < 200) { |
| 2246 | return |
| 2247 | } |
| 2248 | |
| 2249 | const rawHeaders = controller?.rawHeaders ?? [] |
| 2250 | const headersList = new HeadersList() |
| 2251 | appendHeadersListFromResponseHeaders(headersList, headers, rawHeaders) |
| 2252 | const location = headersList.get('location', true) |
| 2253 | |
| 2254 | this.body = new Readable({ read: () => controller.resume() }) |
| 2255 | |
| 2256 | const willFollow = location && request.redirect === 'follow' && |
| 2257 | redirectStatusSet.has(status) |
| 2258 | |
| 2259 | const decoders = [] |
| 2260 | |
| 2261 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding |
| 2262 | if (request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) { |
| 2263 | // https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1 |
| 2264 | const contentEncoding = headersList.get('content-encoding', true) |
| 2265 | // "All content-coding values are case-insensitive..." |
| 2266 | /** @type {string[]} */ |
| 2267 | const codings = contentEncoding ? contentEncoding.toLowerCase().split(',') : [] |
| 2268 | |
| 2269 | // Limit the number of content-encodings to prevent resource exhaustion. |
| 2270 | // CVE fix similar to urllib3 (GHSA-gm62-xv2j-4w53) and curl (CVE-2022-32206). |
| 2271 | const maxContentEncodings = 5 |
| 2272 | if (codings.length > maxContentEncodings) { |
| 2273 | reject(new Error(`too many content-encodings in response: ${codings.length}, maximum allowed is ${maxContentEncodings}`)) |
| 2274 | return |
| 2275 | } |
| 2276 | |
| 2277 | for (let i = codings.length - 1; i >= 0; --i) { |
| 2278 | const coding = codings[i].trim() |
| 2279 | // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2 |
| 2280 | if (coding === 'x-gzip' || coding === 'gzip') { |
| 2281 | decoders.push(zlib.createGunzip({ |
| 2282 | // Be less strict when decoding compressed responses, since sometimes |
| 2283 | // servers send slightly invalid responses that are still accepted |
| 2284 | // by common browsers. |
| 2285 | // Always using Z_SYNC_FLUSH is what cURL does. |
| 2286 | flush: zlib.constants.Z_SYNC_FLUSH, |
| 2287 | finishFlush: zlib.constants.Z_SYNC_FLUSH |
| 2288 | })) |
| 2289 | } else if (coding === 'deflate') { |
| 2290 | decoders.push(createInflate({ |
| 2291 | flush: zlib.constants.Z_SYNC_FLUSH, |
| 2292 | finishFlush: zlib.constants.Z_SYNC_FLUSH |
| 2293 | })) |
| 2294 | } else if (coding === 'br') { |
| 2295 | decoders.push(zlib.createBrotliDecompress({ |
| 2296 | flush: zlib.constants.BROTLI_OPERATION_FLUSH, |
| 2297 | finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH |
| 2298 | })) |
| 2299 | } else if (coding === 'zstd') { |
| 2300 | decoders.push(zlib.createZstdDecompress({ |
| 2301 | flush: zlib.constants.ZSTD_e_continue, |
nothing calls this directly
no test coverage detected