11cb0ef41Sopenharmony_ci'use strict'
21cb0ef41Sopenharmony_ciconst { URL } = require('url')
31cb0ef41Sopenharmony_ciconst http = require('http')
41cb0ef41Sopenharmony_ciconst https = require('https')
51cb0ef41Sopenharmony_ciconst zlib = require('minizlib')
61cb0ef41Sopenharmony_ciconst { Minipass } = require('minipass')
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ciconst Body = require('./body.js')
91cb0ef41Sopenharmony_ciconst { writeToStream, getTotalBytes } = Body
101cb0ef41Sopenharmony_ciconst Response = require('./response.js')
111cb0ef41Sopenharmony_ciconst Headers = require('./headers.js')
121cb0ef41Sopenharmony_ciconst { createHeadersLenient } = Headers
131cb0ef41Sopenharmony_ciconst Request = require('./request.js')
141cb0ef41Sopenharmony_ciconst { getNodeRequestOptions } = Request
151cb0ef41Sopenharmony_ciconst FetchError = require('./fetch-error.js')
161cb0ef41Sopenharmony_ciconst AbortError = require('./abort-error.js')
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ci// XXX this should really be split up and unit-ized for easier testing
191cb0ef41Sopenharmony_ci// and better DRY implementation of data/http request aborting
201cb0ef41Sopenharmony_ciconst fetch = async (url, opts) => {
211cb0ef41Sopenharmony_ci  if (/^data:/.test(url)) {
221cb0ef41Sopenharmony_ci    const request = new Request(url, opts)
231cb0ef41Sopenharmony_ci    // delay 1 promise tick so that the consumer can abort right away
241cb0ef41Sopenharmony_ci    return Promise.resolve().then(() => new Promise((resolve, reject) => {
251cb0ef41Sopenharmony_ci      let type, data
261cb0ef41Sopenharmony_ci      try {
271cb0ef41Sopenharmony_ci        const { pathname, search } = new URL(url)
281cb0ef41Sopenharmony_ci        const split = pathname.split(',')
291cb0ef41Sopenharmony_ci        if (split.length < 2) {
301cb0ef41Sopenharmony_ci          throw new Error('invalid data: URI')
311cb0ef41Sopenharmony_ci        }
321cb0ef41Sopenharmony_ci        const mime = split.shift()
331cb0ef41Sopenharmony_ci        const base64 = /;base64$/.test(mime)
341cb0ef41Sopenharmony_ci        type = base64 ? mime.slice(0, -1 * ';base64'.length) : mime
351cb0ef41Sopenharmony_ci        const rawData = decodeURIComponent(split.join(',') + search)
361cb0ef41Sopenharmony_ci        data = base64 ? Buffer.from(rawData, 'base64') : Buffer.from(rawData)
371cb0ef41Sopenharmony_ci      } catch (er) {
381cb0ef41Sopenharmony_ci        return reject(new FetchError(`[${request.method}] ${
391cb0ef41Sopenharmony_ci          request.url} invalid URL, ${er.message}`, 'system', er))
401cb0ef41Sopenharmony_ci      }
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_ci      const { signal } = request
431cb0ef41Sopenharmony_ci      if (signal && signal.aborted) {
441cb0ef41Sopenharmony_ci        return reject(new AbortError('The user aborted a request.'))
451cb0ef41Sopenharmony_ci      }
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci      const headers = { 'Content-Length': data.length }
481cb0ef41Sopenharmony_ci      if (type) {
491cb0ef41Sopenharmony_ci        headers['Content-Type'] = type
501cb0ef41Sopenharmony_ci      }
511cb0ef41Sopenharmony_ci      return resolve(new Response(data, { headers }))
521cb0ef41Sopenharmony_ci    }))
531cb0ef41Sopenharmony_ci  }
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci  return new Promise((resolve, reject) => {
561cb0ef41Sopenharmony_ci    // build request object
571cb0ef41Sopenharmony_ci    const request = new Request(url, opts)
581cb0ef41Sopenharmony_ci    let options
591cb0ef41Sopenharmony_ci    try {
601cb0ef41Sopenharmony_ci      options = getNodeRequestOptions(request)
611cb0ef41Sopenharmony_ci    } catch (er) {
621cb0ef41Sopenharmony_ci      return reject(er)
631cb0ef41Sopenharmony_ci    }
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ci    const send = (options.protocol === 'https:' ? https : http).request
661cb0ef41Sopenharmony_ci    const { signal } = request
671cb0ef41Sopenharmony_ci    let response = null
681cb0ef41Sopenharmony_ci    const abort = () => {
691cb0ef41Sopenharmony_ci      const error = new AbortError('The user aborted a request.')
701cb0ef41Sopenharmony_ci      reject(error)
711cb0ef41Sopenharmony_ci      if (Minipass.isStream(request.body) &&
721cb0ef41Sopenharmony_ci          typeof request.body.destroy === 'function') {
731cb0ef41Sopenharmony_ci        request.body.destroy(error)
741cb0ef41Sopenharmony_ci      }
751cb0ef41Sopenharmony_ci      if (response && response.body) {
761cb0ef41Sopenharmony_ci        response.body.emit('error', error)
771cb0ef41Sopenharmony_ci      }
781cb0ef41Sopenharmony_ci    }
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ci    if (signal && signal.aborted) {
811cb0ef41Sopenharmony_ci      return abort()
821cb0ef41Sopenharmony_ci    }
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci    const abortAndFinalize = () => {
851cb0ef41Sopenharmony_ci      abort()
861cb0ef41Sopenharmony_ci      finalize()
871cb0ef41Sopenharmony_ci    }
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci    const finalize = () => {
901cb0ef41Sopenharmony_ci      req.abort()
911cb0ef41Sopenharmony_ci      if (signal) {
921cb0ef41Sopenharmony_ci        signal.removeEventListener('abort', abortAndFinalize)
931cb0ef41Sopenharmony_ci      }
941cb0ef41Sopenharmony_ci      clearTimeout(reqTimeout)
951cb0ef41Sopenharmony_ci    }
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci    // send request
981cb0ef41Sopenharmony_ci    const req = send(options)
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci    if (signal) {
1011cb0ef41Sopenharmony_ci      signal.addEventListener('abort', abortAndFinalize)
1021cb0ef41Sopenharmony_ci    }
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci    let reqTimeout = null
1051cb0ef41Sopenharmony_ci    if (request.timeout) {
1061cb0ef41Sopenharmony_ci      req.once('socket', socket => {
1071cb0ef41Sopenharmony_ci        reqTimeout = setTimeout(() => {
1081cb0ef41Sopenharmony_ci          reject(new FetchError(`network timeout at: ${
1091cb0ef41Sopenharmony_ci            request.url}`, 'request-timeout'))
1101cb0ef41Sopenharmony_ci          finalize()
1111cb0ef41Sopenharmony_ci        }, request.timeout)
1121cb0ef41Sopenharmony_ci      })
1131cb0ef41Sopenharmony_ci    }
1141cb0ef41Sopenharmony_ci
1151cb0ef41Sopenharmony_ci    req.on('error', er => {
1161cb0ef41Sopenharmony_ci      // if a 'response' event is emitted before the 'error' event, then by the
1171cb0ef41Sopenharmony_ci      // time this handler is run it's too late to reject the Promise for the
1181cb0ef41Sopenharmony_ci      // response. instead, we forward the error event to the response stream
1191cb0ef41Sopenharmony_ci      // so that the error will surface to the user when they try to consume
1201cb0ef41Sopenharmony_ci      // the body. this is done as a side effect of aborting the request except
1211cb0ef41Sopenharmony_ci      // for in windows, where we must forward the event manually, otherwise
1221cb0ef41Sopenharmony_ci      // there is no longer a ref'd socket attached to the request and the
1231cb0ef41Sopenharmony_ci      // stream never ends so the event loop runs out of work and the process
1241cb0ef41Sopenharmony_ci      // exits without warning.
1251cb0ef41Sopenharmony_ci      // coverage skipped here due to the difficulty in testing
1261cb0ef41Sopenharmony_ci      // istanbul ignore next
1271cb0ef41Sopenharmony_ci      if (req.res) {
1281cb0ef41Sopenharmony_ci        req.res.emit('error', er)
1291cb0ef41Sopenharmony_ci      }
1301cb0ef41Sopenharmony_ci      reject(new FetchError(`request to ${request.url} failed, reason: ${
1311cb0ef41Sopenharmony_ci        er.message}`, 'system', er))
1321cb0ef41Sopenharmony_ci      finalize()
1331cb0ef41Sopenharmony_ci    })
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci    req.on('response', res => {
1361cb0ef41Sopenharmony_ci      clearTimeout(reqTimeout)
1371cb0ef41Sopenharmony_ci
1381cb0ef41Sopenharmony_ci      const headers = createHeadersLenient(res.headers)
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci      // HTTP fetch step 5
1411cb0ef41Sopenharmony_ci      if (fetch.isRedirect(res.statusCode)) {
1421cb0ef41Sopenharmony_ci        // HTTP fetch step 5.2
1431cb0ef41Sopenharmony_ci        const location = headers.get('Location')
1441cb0ef41Sopenharmony_ci
1451cb0ef41Sopenharmony_ci        // HTTP fetch step 5.3
1461cb0ef41Sopenharmony_ci        let locationURL = null
1471cb0ef41Sopenharmony_ci        try {
1481cb0ef41Sopenharmony_ci          locationURL = location === null ? null : new URL(location, request.url).toString()
1491cb0ef41Sopenharmony_ci        } catch {
1501cb0ef41Sopenharmony_ci          // error here can only be invalid URL in Location: header
1511cb0ef41Sopenharmony_ci          // do not throw when options.redirect == manual
1521cb0ef41Sopenharmony_ci          // let the user extract the errorneous redirect URL
1531cb0ef41Sopenharmony_ci          if (request.redirect !== 'manual') {
1541cb0ef41Sopenharmony_ci            /* eslint-disable-next-line max-len */
1551cb0ef41Sopenharmony_ci            reject(new FetchError(`uri requested responds with an invalid redirect URL: ${location}`, 'invalid-redirect'))
1561cb0ef41Sopenharmony_ci            finalize()
1571cb0ef41Sopenharmony_ci            return
1581cb0ef41Sopenharmony_ci          }
1591cb0ef41Sopenharmony_ci        }
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci        // HTTP fetch step 5.5
1621cb0ef41Sopenharmony_ci        if (request.redirect === 'error') {
1631cb0ef41Sopenharmony_ci          reject(new FetchError('uri requested responds with a redirect, ' +
1641cb0ef41Sopenharmony_ci            `redirect mode is set to error: ${request.url}`, 'no-redirect'))
1651cb0ef41Sopenharmony_ci          finalize()
1661cb0ef41Sopenharmony_ci          return
1671cb0ef41Sopenharmony_ci        } else if (request.redirect === 'manual') {
1681cb0ef41Sopenharmony_ci          // node-fetch-specific step: make manual redirect a bit easier to
1691cb0ef41Sopenharmony_ci          // use by setting the Location header value to the resolved URL.
1701cb0ef41Sopenharmony_ci          if (locationURL !== null) {
1711cb0ef41Sopenharmony_ci            // handle corrupted header
1721cb0ef41Sopenharmony_ci            try {
1731cb0ef41Sopenharmony_ci              headers.set('Location', locationURL)
1741cb0ef41Sopenharmony_ci            } catch (err) {
1751cb0ef41Sopenharmony_ci              /* istanbul ignore next: nodejs server prevent invalid
1761cb0ef41Sopenharmony_ci                 response headers, we can't test this through normal
1771cb0ef41Sopenharmony_ci                 request */
1781cb0ef41Sopenharmony_ci              reject(err)
1791cb0ef41Sopenharmony_ci            }
1801cb0ef41Sopenharmony_ci          }
1811cb0ef41Sopenharmony_ci        } else if (request.redirect === 'follow' && locationURL !== null) {
1821cb0ef41Sopenharmony_ci          // HTTP-redirect fetch step 5
1831cb0ef41Sopenharmony_ci          if (request.counter >= request.follow) {
1841cb0ef41Sopenharmony_ci            reject(new FetchError(`maximum redirect reached at: ${
1851cb0ef41Sopenharmony_ci              request.url}`, 'max-redirect'))
1861cb0ef41Sopenharmony_ci            finalize()
1871cb0ef41Sopenharmony_ci            return
1881cb0ef41Sopenharmony_ci          }
1891cb0ef41Sopenharmony_ci
1901cb0ef41Sopenharmony_ci          // HTTP-redirect fetch step 9
1911cb0ef41Sopenharmony_ci          if (res.statusCode !== 303 &&
1921cb0ef41Sopenharmony_ci              request.body &&
1931cb0ef41Sopenharmony_ci              getTotalBytes(request) === null) {
1941cb0ef41Sopenharmony_ci            reject(new FetchError(
1951cb0ef41Sopenharmony_ci              'Cannot follow redirect with body being a readable stream',
1961cb0ef41Sopenharmony_ci              'unsupported-redirect'
1971cb0ef41Sopenharmony_ci            ))
1981cb0ef41Sopenharmony_ci            finalize()
1991cb0ef41Sopenharmony_ci            return
2001cb0ef41Sopenharmony_ci          }
2011cb0ef41Sopenharmony_ci
2021cb0ef41Sopenharmony_ci          // Update host due to redirection
2031cb0ef41Sopenharmony_ci          request.headers.set('host', (new URL(locationURL)).host)
2041cb0ef41Sopenharmony_ci
2051cb0ef41Sopenharmony_ci          // HTTP-redirect fetch step 6 (counter increment)
2061cb0ef41Sopenharmony_ci          // Create a new Request object.
2071cb0ef41Sopenharmony_ci          const requestOpts = {
2081cb0ef41Sopenharmony_ci            headers: new Headers(request.headers),
2091cb0ef41Sopenharmony_ci            follow: request.follow,
2101cb0ef41Sopenharmony_ci            counter: request.counter + 1,
2111cb0ef41Sopenharmony_ci            agent: request.agent,
2121cb0ef41Sopenharmony_ci            compress: request.compress,
2131cb0ef41Sopenharmony_ci            method: request.method,
2141cb0ef41Sopenharmony_ci            body: request.body,
2151cb0ef41Sopenharmony_ci            signal: request.signal,
2161cb0ef41Sopenharmony_ci            timeout: request.timeout,
2171cb0ef41Sopenharmony_ci          }
2181cb0ef41Sopenharmony_ci
2191cb0ef41Sopenharmony_ci          // if the redirect is to a new hostname, strip the authorization and cookie headers
2201cb0ef41Sopenharmony_ci          const parsedOriginal = new URL(request.url)
2211cb0ef41Sopenharmony_ci          const parsedRedirect = new URL(locationURL)
2221cb0ef41Sopenharmony_ci          if (parsedOriginal.hostname !== parsedRedirect.hostname) {
2231cb0ef41Sopenharmony_ci            requestOpts.headers.delete('authorization')
2241cb0ef41Sopenharmony_ci            requestOpts.headers.delete('cookie')
2251cb0ef41Sopenharmony_ci          }
2261cb0ef41Sopenharmony_ci
2271cb0ef41Sopenharmony_ci          // HTTP-redirect fetch step 11
2281cb0ef41Sopenharmony_ci          if (res.statusCode === 303 || (
2291cb0ef41Sopenharmony_ci            (res.statusCode === 301 || res.statusCode === 302) &&
2301cb0ef41Sopenharmony_ci              request.method === 'POST'
2311cb0ef41Sopenharmony_ci          )) {
2321cb0ef41Sopenharmony_ci            requestOpts.method = 'GET'
2331cb0ef41Sopenharmony_ci            requestOpts.body = undefined
2341cb0ef41Sopenharmony_ci            requestOpts.headers.delete('content-length')
2351cb0ef41Sopenharmony_ci          }
2361cb0ef41Sopenharmony_ci
2371cb0ef41Sopenharmony_ci          // HTTP-redirect fetch step 15
2381cb0ef41Sopenharmony_ci          resolve(fetch(new Request(locationURL, requestOpts)))
2391cb0ef41Sopenharmony_ci          finalize()
2401cb0ef41Sopenharmony_ci          return
2411cb0ef41Sopenharmony_ci        }
2421cb0ef41Sopenharmony_ci      } // end if(isRedirect)
2431cb0ef41Sopenharmony_ci
2441cb0ef41Sopenharmony_ci      // prepare response
2451cb0ef41Sopenharmony_ci      res.once('end', () =>
2461cb0ef41Sopenharmony_ci        signal && signal.removeEventListener('abort', abortAndFinalize))
2471cb0ef41Sopenharmony_ci
2481cb0ef41Sopenharmony_ci      const body = new Minipass()
2491cb0ef41Sopenharmony_ci      // if an error occurs, either on the response stream itself, on one of the
2501cb0ef41Sopenharmony_ci      // decoder streams, or a response length timeout from the Body class, we
2511cb0ef41Sopenharmony_ci      // forward the error through to our internal body stream. If we see an
2521cb0ef41Sopenharmony_ci      // error event on that, we call finalize to abort the request and ensure
2531cb0ef41Sopenharmony_ci      // we don't leave a socket believing a request is in flight.
2541cb0ef41Sopenharmony_ci      // this is difficult to test, so lacks specific coverage.
2551cb0ef41Sopenharmony_ci      body.on('error', finalize)
2561cb0ef41Sopenharmony_ci      // exceedingly rare that the stream would have an error,
2571cb0ef41Sopenharmony_ci      // but just in case we proxy it to the stream in use.
2581cb0ef41Sopenharmony_ci      res.on('error', /* istanbul ignore next */ er => body.emit('error', er))
2591cb0ef41Sopenharmony_ci      res.on('data', (chunk) => body.write(chunk))
2601cb0ef41Sopenharmony_ci      res.on('end', () => body.end())
2611cb0ef41Sopenharmony_ci
2621cb0ef41Sopenharmony_ci      const responseOptions = {
2631cb0ef41Sopenharmony_ci        url: request.url,
2641cb0ef41Sopenharmony_ci        status: res.statusCode,
2651cb0ef41Sopenharmony_ci        statusText: res.statusMessage,
2661cb0ef41Sopenharmony_ci        headers: headers,
2671cb0ef41Sopenharmony_ci        size: request.size,
2681cb0ef41Sopenharmony_ci        timeout: request.timeout,
2691cb0ef41Sopenharmony_ci        counter: request.counter,
2701cb0ef41Sopenharmony_ci        trailer: new Promise(resolveTrailer =>
2711cb0ef41Sopenharmony_ci          res.on('end', () => resolveTrailer(createHeadersLenient(res.trailers)))),
2721cb0ef41Sopenharmony_ci      }
2731cb0ef41Sopenharmony_ci
2741cb0ef41Sopenharmony_ci      // HTTP-network fetch step 12.1.1.3
2751cb0ef41Sopenharmony_ci      const codings = headers.get('Content-Encoding')
2761cb0ef41Sopenharmony_ci
2771cb0ef41Sopenharmony_ci      // HTTP-network fetch step 12.1.1.4: handle content codings
2781cb0ef41Sopenharmony_ci
2791cb0ef41Sopenharmony_ci      // in following scenarios we ignore compression support
2801cb0ef41Sopenharmony_ci      // 1. compression support is disabled
2811cb0ef41Sopenharmony_ci      // 2. HEAD request
2821cb0ef41Sopenharmony_ci      // 3. no Content-Encoding header
2831cb0ef41Sopenharmony_ci      // 4. no content response (204)
2841cb0ef41Sopenharmony_ci      // 5. content not modified response (304)
2851cb0ef41Sopenharmony_ci      if (!request.compress ||
2861cb0ef41Sopenharmony_ci          request.method === 'HEAD' ||
2871cb0ef41Sopenharmony_ci          codings === null ||
2881cb0ef41Sopenharmony_ci          res.statusCode === 204 ||
2891cb0ef41Sopenharmony_ci          res.statusCode === 304) {
2901cb0ef41Sopenharmony_ci        response = new Response(body, responseOptions)
2911cb0ef41Sopenharmony_ci        resolve(response)
2921cb0ef41Sopenharmony_ci        return
2931cb0ef41Sopenharmony_ci      }
2941cb0ef41Sopenharmony_ci
2951cb0ef41Sopenharmony_ci      // Be less strict when decoding compressed responses, since sometimes
2961cb0ef41Sopenharmony_ci      // servers send slightly invalid responses that are still accepted
2971cb0ef41Sopenharmony_ci      // by common browsers.
2981cb0ef41Sopenharmony_ci      // Always using Z_SYNC_FLUSH is what cURL does.
2991cb0ef41Sopenharmony_ci      const zlibOptions = {
3001cb0ef41Sopenharmony_ci        flush: zlib.constants.Z_SYNC_FLUSH,
3011cb0ef41Sopenharmony_ci        finishFlush: zlib.constants.Z_SYNC_FLUSH,
3021cb0ef41Sopenharmony_ci      }
3031cb0ef41Sopenharmony_ci
3041cb0ef41Sopenharmony_ci      // for gzip
3051cb0ef41Sopenharmony_ci      if (codings === 'gzip' || codings === 'x-gzip') {
3061cb0ef41Sopenharmony_ci        const unzip = new zlib.Gunzip(zlibOptions)
3071cb0ef41Sopenharmony_ci        response = new Response(
3081cb0ef41Sopenharmony_ci          // exceedingly rare that the stream would have an error,
3091cb0ef41Sopenharmony_ci          // but just in case we proxy it to the stream in use.
3101cb0ef41Sopenharmony_ci          body.on('error', /* istanbul ignore next */ er => unzip.emit('error', er)).pipe(unzip),
3111cb0ef41Sopenharmony_ci          responseOptions
3121cb0ef41Sopenharmony_ci        )
3131cb0ef41Sopenharmony_ci        resolve(response)
3141cb0ef41Sopenharmony_ci        return
3151cb0ef41Sopenharmony_ci      }
3161cb0ef41Sopenharmony_ci
3171cb0ef41Sopenharmony_ci      // for deflate
3181cb0ef41Sopenharmony_ci      if (codings === 'deflate' || codings === 'x-deflate') {
3191cb0ef41Sopenharmony_ci        // handle the infamous raw deflate response from old servers
3201cb0ef41Sopenharmony_ci        // a hack for old IIS and Apache servers
3211cb0ef41Sopenharmony_ci        const raw = res.pipe(new Minipass())
3221cb0ef41Sopenharmony_ci        raw.once('data', chunk => {
3231cb0ef41Sopenharmony_ci          // see http://stackoverflow.com/questions/37519828
3241cb0ef41Sopenharmony_ci          const decoder = (chunk[0] & 0x0F) === 0x08
3251cb0ef41Sopenharmony_ci            ? new zlib.Inflate()
3261cb0ef41Sopenharmony_ci            : new zlib.InflateRaw()
3271cb0ef41Sopenharmony_ci          // exceedingly rare that the stream would have an error,
3281cb0ef41Sopenharmony_ci          // but just in case we proxy it to the stream in use.
3291cb0ef41Sopenharmony_ci          body.on('error', /* istanbul ignore next */ er => decoder.emit('error', er)).pipe(decoder)
3301cb0ef41Sopenharmony_ci          response = new Response(decoder, responseOptions)
3311cb0ef41Sopenharmony_ci          resolve(response)
3321cb0ef41Sopenharmony_ci        })
3331cb0ef41Sopenharmony_ci        return
3341cb0ef41Sopenharmony_ci      }
3351cb0ef41Sopenharmony_ci
3361cb0ef41Sopenharmony_ci      // for br
3371cb0ef41Sopenharmony_ci      if (codings === 'br') {
3381cb0ef41Sopenharmony_ci        // ignoring coverage so tests don't have to fake support (or lack of) for brotli
3391cb0ef41Sopenharmony_ci        // istanbul ignore next
3401cb0ef41Sopenharmony_ci        try {
3411cb0ef41Sopenharmony_ci          var decoder = new zlib.BrotliDecompress()
3421cb0ef41Sopenharmony_ci        } catch (err) {
3431cb0ef41Sopenharmony_ci          reject(err)
3441cb0ef41Sopenharmony_ci          finalize()
3451cb0ef41Sopenharmony_ci          return
3461cb0ef41Sopenharmony_ci        }
3471cb0ef41Sopenharmony_ci        // exceedingly rare that the stream would have an error,
3481cb0ef41Sopenharmony_ci        // but just in case we proxy it to the stream in use.
3491cb0ef41Sopenharmony_ci        body.on('error', /* istanbul ignore next */ er => decoder.emit('error', er)).pipe(decoder)
3501cb0ef41Sopenharmony_ci        response = new Response(decoder, responseOptions)
3511cb0ef41Sopenharmony_ci        resolve(response)
3521cb0ef41Sopenharmony_ci        return
3531cb0ef41Sopenharmony_ci      }
3541cb0ef41Sopenharmony_ci
3551cb0ef41Sopenharmony_ci      // otherwise, use response as-is
3561cb0ef41Sopenharmony_ci      response = new Response(body, responseOptions)
3571cb0ef41Sopenharmony_ci      resolve(response)
3581cb0ef41Sopenharmony_ci    })
3591cb0ef41Sopenharmony_ci
3601cb0ef41Sopenharmony_ci    writeToStream(req, request)
3611cb0ef41Sopenharmony_ci  })
3621cb0ef41Sopenharmony_ci}
3631cb0ef41Sopenharmony_ci
3641cb0ef41Sopenharmony_cimodule.exports = fetch
3651cb0ef41Sopenharmony_ci
3661cb0ef41Sopenharmony_cifetch.isRedirect = code =>
3671cb0ef41Sopenharmony_ci  code === 301 ||
3681cb0ef41Sopenharmony_ci  code === 302 ||
3691cb0ef41Sopenharmony_ci  code === 303 ||
3701cb0ef41Sopenharmony_ci  code === 307 ||
3711cb0ef41Sopenharmony_ci  code === 308
3721cb0ef41Sopenharmony_ci
3731cb0ef41Sopenharmony_cifetch.Headers = Headers
3741cb0ef41Sopenharmony_cifetch.Request = Request
3751cb0ef41Sopenharmony_cifetch.Response = Response
3761cb0ef41Sopenharmony_cifetch.FetchError = FetchError
3771cb0ef41Sopenharmony_cifetch.AbortError = AbortError
378