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