File Explorer

/proc/4/root/var/lang/lib/node_modules/npm/node_modules/make-fetch-happen/lib

This explorer reads the filesystem of the server it runs on, so /workspace/user isn't present here. Browsing and the terminal still work against this server's own disk from /.

remote.js4.8 KB · 135 lines
const { Minipass } = require('minipass')const fetch = require('minipass-fetch')const { promiseRetry } = require('@gar/promise-retry')const ssri = require('ssri')const { log } = require('proc-log')const { redact: cleanUrl } = require('@npmcli/redact') const CachingMinipassPipeline = require('./pipeline.js')const { getAgent } = require('@npmcli/agent')const pkg = require('../package.json') const USER_AGENT = `${pkg.name}/${pkg.version} (+https://npm.im/${pkg.name})` const RETRY_ERRORS = [  'ECONNRESET', // remote socket closed on us  'ECONNREFUSED', // remote host refused to open connection  'EADDRINUSE', // failed to bind to a local port (proxy?)  'ETIMEDOUT', // someone in the transaction is WAY TOO SLOW  // from @npmcli/agent  'ECONNECTIONTIMEOUT',  'EIDLETIMEOUT',  'ERESPONSETIMEOUT',  'ETRANSFERTIMEOUT',  // Known codes we do NOT retry on:  // ENOTFOUND (getaddrinfo failure. Either bad hostname, or offline)  // EINVALIDPROXY // invalid protocol from @npmcli/agent  // EINVALIDRESPONSE // invalid status code from @npmcli/agent] const RETRY_TYPES = [  'request-timeout',] // make a request directly to the remote source,// retrying certain classes of errors as well as// following redirects (through the cache if necessary)// and verifying response integrityconst remoteFetch = (request, options) => {  // options.signal is intended for the fetch itself, not the agent.  Attaching it to the agent will re-use that signal across multiple requests, which prevents any connections beyond the first one.  const agent = getAgent(request.url, { ...options, signal: undefined })  if (!request.headers.has('connection')) {    request.headers.set('connection', agent ? 'keep-alive' : 'close')  }   if (!request.headers.has('user-agent')) {    request.headers.set('user-agent', USER_AGENT)  }   // keep our own options since we're overriding the agent  // and the redirect mode  const _opts = {    ...options,    agent,    redirect: 'manual',  }   return promiseRetry(async (retryHandler, attemptNum) => {    const req = new fetch.Request(request, _opts)    const url = cleanUrl(req.url)    try {      let res = await fetch(req, _opts)      if (_opts.integrity && res.status === 200) {        // we got a 200 response and the user has specified an expected        // integrity value, so wrap the response in an ssri stream to verify it        const integrityStream = ssri.integrityStream({          algorithms: _opts.algorithms,          integrity: _opts.integrity,          size: _opts.size,        })        const pipeline = new CachingMinipassPipeline({          events: ['integrity', 'size'],        }, res.body, integrityStream)        // we also propagate the integrity and size events out to the pipeline so we can use        // this new response body as an integrityEmitter for cacache        integrityStream.on('integrity', i => pipeline.emit('integrity', i))        integrityStream.on('size', s => pipeline.emit('size', s))        res = new fetch.Response(pipeline, res)        // set an explicit flag so we know if our response body will emit integrity and size        res.body.hasIntegrityEmitter = true      }       res.headers.set('x-fetch-attempts', attemptNum)       // do not retry POST requests, or requests with a streaming body      // do retry requests with a 408, 420, 429 or 500+ status in the response      const isStream = Minipass.isStream(req.body)      const isRetriable = req.method !== 'POST' &&          !isStream &&          ([408, 420, 429].includes(res.status) || res.status >= 500)       if (isRetriable) {        if (typeof options.onRetry === 'function') {          options.onRetry(res)        }         /* eslint-disable-next-line max-len */        log.http('fetch', `${req.method} ${url} attempt ${attemptNum} failed with ${res.status}`)        return retryHandler(res)      }       return res    } catch (err) {      const code = (err.code === 'EPROMISERETRY')        ? err.retried.code        : err.code       // err.retried will be the thing that was thrown from above      // if it's a response, we just got a bad status code and we      // can re-throw to allow the retry      const isRetryError = err.retried instanceof fetch.Response ||        (RETRY_ERRORS.includes(code) && RETRY_TYPES.includes(err.type))       if (req.method === 'POST' || isRetryError) {        throw err      }       if (typeof options.onRetry === 'function') {        options.onRetry(err)      }       log.http('fetch', `${req.method} ${url} attempt ${attemptNum} failed with ${err.code}`)      return retryHandler(err)    }  }, options.retry).catch((err) => {    // don't reject for http errors, just return them    if (err.status >= 400 && err.type !== 'system') {      return err    }     throw err  })} module.exports = remoteFetch