summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/npm-profile/node_modules/make-fetch-happen/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/npm-profile/node_modules/make-fetch-happen/index.js')
-rw-r--r--deps/npm/node_modules/npm-profile/node_modules/make-fetch-happen/index.js468
1 files changed, 468 insertions, 0 deletions
diff --git a/deps/npm/node_modules/npm-profile/node_modules/make-fetch-happen/index.js b/deps/npm/node_modules/npm-profile/node_modules/make-fetch-happen/index.js
new file mode 100644
index 0000000000..d82811b63d
--- /dev/null
+++ b/deps/npm/node_modules/npm-profile/node_modules/make-fetch-happen/index.js
@@ -0,0 +1,468 @@
+'use strict'
+
+let Cache
+const url = require('url')
+const CachePolicy = require('http-cache-semantics')
+const fetch = require('node-fetch-npm')
+const pkg = require('./package.json')
+const retry = require('promise-retry')
+let ssri
+const Stream = require('stream')
+const getAgent = require('./agent')
+const setWarning = require('./warning')
+
+const isURL = /^https?:/
+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
+ // Known codes we do NOT retry on:
+ // ENOTFOUND (getaddrinfo failure. Either bad hostname, or offline)
+]
+
+const RETRY_TYPES = [
+ 'request-timeout'
+]
+
+// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
+module.exports = cachingFetch
+cachingFetch.defaults = function (_uri, _opts) {
+ const fetch = this
+ if (typeof _uri === 'object') {
+ _opts = _uri
+ _uri = null
+ }
+
+ function defaultedFetch (uri, opts) {
+ const finalOpts = Object.assign({}, _opts || {}, opts || {})
+ return fetch(uri || _uri, finalOpts)
+ }
+
+ defaultedFetch.defaults = fetch.defaults
+ defaultedFetch.delete = fetch.delete
+ return defaultedFetch
+}
+
+cachingFetch.delete = cacheDelete
+function cacheDelete (uri, opts) {
+ opts = configureOptions(opts)
+ if (opts.cacheManager) {
+ const req = new fetch.Request(uri, {
+ method: opts.method,
+ headers: opts.headers
+ })
+ return opts.cacheManager.delete(req, opts)
+ }
+}
+
+function initializeCache (opts) {
+ if (typeof opts.cacheManager === 'string') {
+ if (!Cache) {
+ // Default cacache-based cache
+ Cache = require('./cache')
+ }
+
+ opts.cacheManager = new Cache(opts.cacheManager, opts)
+ }
+
+ opts.cache = opts.cache || 'default'
+
+ if (opts.cache === 'default' && isHeaderConditional(opts.headers)) {
+ // If header list contains `If-Modified-Since`, `If-None-Match`,
+ // `If-Unmodified-Since`, `If-Match`, or `If-Range`, fetch will set cache
+ // mode to "no-store" if it is "default".
+ opts.cache = 'no-store'
+ }
+}
+
+function configureOptions (_opts) {
+ const opts = Object.assign({}, _opts || {})
+ opts.method = (opts.method || 'GET').toUpperCase()
+
+ if (opts.retry && typeof opts.retry === 'number') {
+ opts.retry = { retries: opts.retry }
+ }
+
+ if (opts.retry === false) {
+ opts.retry = { retries: 0 }
+ }
+
+ if (opts.cacheManager) {
+ initializeCache(opts)
+ }
+
+ return opts
+}
+
+function initializeSsri () {
+ if (!ssri) {
+ ssri = require('ssri')
+ }
+}
+
+function cachingFetch (uri, _opts) {
+ const opts = configureOptions(_opts)
+
+ if (opts.integrity) {
+ initializeSsri()
+ }
+
+ const isCachable = (opts.method === 'GET' || opts.method === 'HEAD') &&
+ opts.cacheManager &&
+ opts.cache !== 'no-store' &&
+ opts.cache !== 'reload'
+
+ if (isCachable) {
+ const req = new fetch.Request(uri, {
+ method: opts.method,
+ headers: opts.headers
+ })
+
+ return opts.cacheManager.match(req, opts).then(res => {
+ if (res) {
+ const warningCode = (res.headers.get('Warning') || '').match(/^\d+/)
+ if (warningCode && +warningCode >= 100 && +warningCode < 200) {
+ // https://tools.ietf.org/html/rfc7234#section-4.3.4
+ //
+ // If a stored response is selected for update, the cache MUST:
+ //
+ // * delete any Warning header fields in the stored response with
+ // warn-code 1xx (see Section 5.5);
+ //
+ // * retain any Warning header fields in the stored response with
+ // warn-code 2xx;
+ //
+ res.headers.delete('Warning')
+ }
+
+ if (opts.cache === 'default' && !isStale(req, res)) {
+ return res
+ }
+
+ if (opts.cache === 'default' || opts.cache === 'no-cache') {
+ return conditionalFetch(req, res, opts)
+ }
+
+ if (opts.cache === 'force-cache' || opts.cache === 'only-if-cached') {
+ // 112 Disconnected operation
+ // SHOULD be included if the cache is intentionally disconnected from
+ // the rest of the network for a period of time.
+ // (https://tools.ietf.org/html/rfc2616#section-14.46)
+ setWarning(res, 112, 'Disconnected operation')
+ return res
+ }
+ }
+
+ if (!res && opts.cache === 'only-if-cached') {
+ const errorMsg = `request to ${
+ uri
+ } failed: cache mode is 'only-if-cached' but no cached response available.`
+
+ const err = new Error(errorMsg)
+ err.code = 'ENOTCACHED'
+ throw err
+ }
+
+ // Missing cache entry, or mode is default (if stale), reload, no-store
+ return remoteFetch(req.url, opts)
+ })
+ }
+
+ return remoteFetch(uri, opts)
+}
+
+function iterableToObject (iter) {
+ const obj = {}
+ for (let k of iter.keys()) {
+ obj[k] = iter.get(k)
+ }
+ return obj
+}
+
+function makePolicy (req, res) {
+ const _req = {
+ url: req.url,
+ method: req.method,
+ headers: iterableToObject(req.headers)
+ }
+ const _res = {
+ status: res.status,
+ headers: iterableToObject(res.headers)
+ }
+
+ return new CachePolicy(_req, _res, { shared: false })
+}
+
+// https://tools.ietf.org/html/rfc7234#section-4.2
+function isStale (req, res) {
+ if (!res) {
+ return null
+ }
+
+ const _req = {
+ url: req.url,
+ method: req.method,
+ headers: iterableToObject(req.headers)
+ }
+
+ const policy = makePolicy(req, res)
+
+ const responseTime = res.headers.get('x-local-cache-time') ||
+ res.headers.get('date') ||
+ 0
+
+ policy._responseTime = new Date(responseTime)
+
+ const bool = !policy.satisfiesWithoutRevalidation(_req)
+ return bool
+}
+
+function mustRevalidate (res) {
+ return (res.headers.get('cache-control') || '').match(/must-revalidate/i)
+}
+
+function conditionalFetch (req, cachedRes, opts) {
+ const _req = {
+ url: req.url,
+ method: req.method,
+ headers: Object.assign({}, opts.headers || {})
+ }
+
+ const policy = makePolicy(req, cachedRes)
+ opts.headers = policy.revalidationHeaders(_req)
+
+ return remoteFetch(req.url, opts)
+ .then(condRes => {
+ const revalidatedPolicy = policy.revalidatedPolicy(_req, {
+ status: condRes.status,
+ headers: iterableToObject(condRes.headers)
+ })
+
+ if (condRes.status >= 500 && !mustRevalidate(cachedRes)) {
+ // 111 Revalidation failed
+ // MUST be included if a cache returns a stale response because an
+ // attempt to revalidate the response failed, due to an inability to
+ // reach the server.
+ // (https://tools.ietf.org/html/rfc2616#section-14.46)
+ setWarning(cachedRes, 111, 'Revalidation failed')
+ return cachedRes
+ }
+
+ if (condRes.status === 304) { // 304 Not Modified
+ condRes.body = cachedRes.body
+ return opts.cacheManager.put(req, condRes, opts)
+ .then(newRes => {
+ newRes.headers = new fetch.Headers(revalidatedPolicy.policy.responseHeaders())
+ return newRes
+ })
+ }
+
+ return condRes
+ })
+ .then(res => res)
+ .catch(err => {
+ if (mustRevalidate(cachedRes)) {
+ throw err
+ } else {
+ // 111 Revalidation failed
+ // MUST be included if a cache returns a stale response because an
+ // attempt to revalidate the response failed, due to an inability to
+ // reach the server.
+ // (https://tools.ietf.org/html/rfc2616#section-14.46)
+ setWarning(cachedRes, 111, 'Revalidation failed')
+ // 199 Miscellaneous warning
+ // The warning text MAY include arbitrary information to be presented to
+ // a human user, or logged. A system receiving this warning MUST NOT take
+ // any automated action, besides presenting the warning to the user.
+ // (https://tools.ietf.org/html/rfc2616#section-14.46)
+ setWarning(
+ cachedRes,
+ 199,
+ `Miscellaneous Warning ${err.code}: ${err.message}`
+ )
+
+ return cachedRes
+ }
+ })
+}
+
+function remoteFetchHandleIntegrity (res, integrity) {
+ const oldBod = res.body
+ const newBod = ssri.integrityStream({
+ integrity
+ })
+ oldBod.pipe(newBod)
+ res.body = newBod
+ oldBod.once('error', err => {
+ newBod.emit('error', err)
+ })
+ newBod.once('error', err => {
+ oldBod.emit('error', err)
+ })
+}
+
+function remoteFetch (uri, opts) {
+ const agent = getAgent(uri, opts)
+ const headers = Object.assign({
+ 'connection': agent ? 'keep-alive' : 'close',
+ 'user-agent': USER_AGENT
+ }, opts.headers || {})
+
+ const reqOpts = {
+ agent,
+ body: opts.body,
+ compress: opts.compress,
+ follow: opts.follow,
+ headers: new fetch.Headers(headers),
+ method: opts.method,
+ redirect: 'manual',
+ size: opts.size,
+ counter: opts.counter,
+ timeout: opts.timeout
+ }
+
+ return retry(
+ (retryHandler, attemptNum) => {
+ const req = new fetch.Request(uri, reqOpts)
+ return fetch(req)
+ .then(res => {
+ res.headers.set('x-fetch-attempts', attemptNum)
+
+ if (opts.integrity) {
+ remoteFetchHandleIntegrity(res, opts.integrity)
+ }
+
+ const isStream = req.body instanceof Stream
+
+ if (opts.cacheManager) {
+ const isMethodGetHead = req.method === 'GET' ||
+ req.method === 'HEAD'
+
+ const isCachable = opts.cache !== 'no-store' &&
+ isMethodGetHead &&
+ makePolicy(req, res).storable() &&
+ res.status === 200 // No other statuses should be stored!
+
+ if (isCachable) {
+ return opts.cacheManager.put(req, res, opts)
+ }
+
+ if (!isMethodGetHead) {
+ return opts.cacheManager.delete(req).then(() => {
+ if (res.status >= 500 && req.method !== 'POST' && !isStream) {
+ return retryHandler(res)
+ }
+
+ return res
+ })
+ }
+ }
+
+ const isRetriable = req.method !== 'POST' &&
+ !isStream && (
+ res.status === 408 || // Request Timeout
+ res.status === 420 || // Enhance Your Calm (usually Twitter rate-limit)
+ res.status === 429 || // Too Many Requests ("standard" rate-limiting)
+ res.status >= 500 // Assume server errors are momentary hiccups
+ )
+
+ if (isRetriable) {
+ return retryHandler(res)
+ }
+
+ if (!fetch.isRedirect(res.status) || opts.redirect === 'manual') {
+ return res
+ }
+
+ // handle redirects - matches behavior of npm-fetch: https://github.com/bitinn/node-fetch
+ if (opts.redirect === 'error') {
+ const err = new Error(`redirect mode is set to error: ${uri}`)
+ err.code = 'ENOREDIRECT'
+ throw err
+ }
+
+ if (!res.headers.get('location')) {
+ const err = new Error(`redirect location header missing at: ${uri}`)
+ err.code = 'EINVALIDREDIRECT'
+ throw err
+ }
+
+ if (req.counter >= req.follow) {
+ const err = new Error(`maximum redirect reached at: ${uri}`)
+ err.code = 'EMAXREDIRECT'
+ throw err
+ }
+
+ const resolvedUrl = url.resolve(req.url, res.headers.get('location'))
+ let redirectURL = url.parse(resolvedUrl)
+
+ if (isURL.test(res.headers.get('location'))) {
+ redirectURL = url.parse(res.headers.get('location'))
+ }
+
+ // Remove authorization if changing hostnames (but not if just
+ // changing ports or protocols). This matches the behavior of request:
+ // https://github.com/request/request/blob/b12a6245/lib/redirect.js#L134-L138
+ if (url.parse(req.url).hostname !== redirectURL.hostname) {
+ req.headers.delete('authorization')
+ }
+
+ // for POST request with 301/302 response, or any request with 303 response,
+ // use GET when following redirect
+ if (res.status === 303 ||
+ ((res.status === 301 || res.status === 302) && req.method === 'POST')) {
+ opts.method = 'GET'
+ opts.body = null
+ req.headers.delete('content-length')
+ }
+
+ opts.headers = {}
+ req.headers.forEach((value, name) => {
+ opts.headers[name] = value
+ })
+
+ opts.counter = ++req.counter
+ return cachingFetch(resolvedUrl, opts)
+ })
+ .catch(err => {
+ const code = err.code === 'EPROMISERETRY' ? err.retried.code : err.code
+
+ const isRetryError = RETRY_ERRORS.indexOf(code) === -1 &&
+ RETRY_TYPES.indexOf(err.type) === -1
+
+ if (req.method === 'POST' || isRetryError) {
+ throw err
+ }
+
+ return retryHandler(err)
+ })
+ },
+ opts.retry
+ ).catch(err => {
+ if (err.status >= 400) {
+ return err
+ }
+
+ throw err
+ })
+}
+
+function isHeaderConditional (headers) {
+ if (!headers || typeof headers !== 'object') {
+ return false
+ }
+
+ const modifiers = [
+ 'if-modified-since',
+ 'if-none-match',
+ 'if-unmodified-since',
+ 'if-match',
+ 'if-range'
+ ]
+
+ return Object.keys(headers)
+ .some(h => modifiers.indexOf(h.toLowerCase()) !== -1)
+}