summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/make-fetch-happen/lib/cache/policy.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/make-fetch-happen/lib/cache/policy.js')
-rw-r--r--deps/npm/node_modules/make-fetch-happen/lib/cache/policy.js161
1 files changed, 161 insertions, 0 deletions
diff --git a/deps/npm/node_modules/make-fetch-happen/lib/cache/policy.js b/deps/npm/node_modules/make-fetch-happen/lib/cache/policy.js
new file mode 100644
index 0000000000..189dce80ee
--- /dev/null
+++ b/deps/npm/node_modules/make-fetch-happen/lib/cache/policy.js
@@ -0,0 +1,161 @@
+const CacheSemantics = require('http-cache-semantics')
+const Negotiator = require('negotiator')
+const ssri = require('ssri')
+
+// HACK: negotiator lazy loads several of its own modules
+// as a micro optimization. we need to be sure that they're
+// in memory as soon as possible at startup so that we do
+// not try to lazy load them after the directory has been
+// retired during a self update of the npm CLI, we do this
+// by calling all of the methods that trigger a lazy load
+// on a fake instance.
+const preloadNegotiator = new Negotiator({ headers: {} })
+preloadNegotiator.charsets()
+preloadNegotiator.encodings()
+preloadNegotiator.languages()
+preloadNegotiator.mediaTypes()
+
+// options passed to http-cache-semantics constructor
+const policyOptions = {
+ shared: false,
+ ignoreCargoCult: true,
+}
+
+// a fake empty response, used when only testing the
+// request for storability
+const emptyResponse = { status: 200, headers: {} }
+
+// returns a plain object representation of the Request
+const requestObject = (request) => {
+ const _obj = {
+ method: request.method,
+ url: request.url,
+ headers: {},
+ }
+
+ request.headers.forEach((value, key) => {
+ _obj.headers[key] = value
+ })
+
+ return _obj
+}
+
+// returns a plain object representation of the Response
+const responseObject = (response) => {
+ const _obj = {
+ status: response.status,
+ headers: {},
+ }
+
+ response.headers.forEach((value, key) => {
+ _obj.headers[key] = value
+ })
+
+ return _obj
+}
+
+class CachePolicy {
+ constructor ({ entry, request, response, options }) {
+ this.entry = entry
+ this.request = requestObject(request)
+ this.response = responseObject(response)
+ this.options = options
+ this.policy = new CacheSemantics(this.request, this.response, policyOptions)
+
+ if (this.entry) {
+ // if we have an entry, copy the timestamp to the _responseTime
+ // this is necessary because the CacheSemantics constructor forces
+ // the value to Date.now() which means a policy created from a
+ // cache entry is likely to always identify itself as stale
+ this.policy._responseTime = this.entry.time
+ }
+ }
+
+ // static method to quickly determine if a request alone is storable
+ static storable (request, options) {
+ // no cachePath means no caching
+ if (!options.cachePath)
+ return false
+
+ // user explicitly asked not to cache
+ if (options.cache === 'no-store')
+ return false
+
+ // we only cache GET and HEAD requests
+ if (!['GET', 'HEAD'].includes(request.method))
+ return false
+
+ // otherwise, let http-cache-semantics make the decision
+ // based on the request's headers
+ const policy = new CacheSemantics(requestObject(request), emptyResponse, policyOptions)
+ return policy.storable()
+ }
+
+ // returns true if the policy satisfies the request
+ satisfies (request) {
+ const _req = requestObject(request)
+ if (this.request.headers.host !== _req.headers.host)
+ return false
+
+ const negotiatorA = new Negotiator(this.request)
+ const negotiatorB = new Negotiator(_req)
+
+ if (JSON.stringify(negotiatorA.mediaTypes()) !== JSON.stringify(negotiatorB.mediaTypes()))
+ return false
+
+ if (JSON.stringify(negotiatorA.languages()) !== JSON.stringify(negotiatorB.languages()))
+ return false
+
+ if (JSON.stringify(negotiatorA.encodings()) !== JSON.stringify(negotiatorB.encodings()))
+ return false
+
+ if (this.options.integrity)
+ return ssri.parse(this.options.integrity).match(this.entry.integrity)
+
+ return true
+ }
+
+ // returns true if the request and response allow caching
+ storable () {
+ return this.policy.storable()
+ }
+
+ // NOTE: this is a hack to avoid parsing the cache-control
+ // header ourselves, it returns true if the response's
+ // cache-control contains must-revalidate
+ get mustRevalidate () {
+ return !!this.policy._rescc['must-revalidate']
+ }
+
+ // returns true if the cached response requires revalidation
+ // for the given request
+ needsRevalidation (request) {
+ const _req = requestObject(request)
+ // force method to GET because we only cache GETs
+ // but can serve a HEAD from a cached GET
+ _req.method = 'GET'
+ return !this.policy.satisfiesWithoutRevalidation(_req)
+ }
+
+ responseHeaders () {
+ return this.policy.responseHeaders()
+ }
+
+ // returns a new object containing the appropriate headers
+ // to send a revalidation request
+ revalidationHeaders (request) {
+ const _req = requestObject(request)
+ return this.policy.revalidationHeaders(_req)
+ }
+
+ // returns true if the request/response was revalidated
+ // successfully. returns false if a new response was received
+ revalidated (request, response) {
+ const _req = requestObject(request)
+ const _res = responseObject(response)
+ const policy = this.policy.revalidatedPolicy(_req, _res)
+ return !policy.modified
+ }
+}
+
+module.exports = CachePolicy