http-cache-semantics
Advanced tools
Comparing version 3.1.0 to 3.2.0
125
index.js
@@ -25,33 +25,40 @@ 'use strict'; | ||
function CachePolicy(req, res, {shared, cacheHeuristic} = {}) { | ||
if (!res || !res.headers) { | ||
throw Error("Response headers missing"); | ||
} | ||
if (!req || !req.headers) { | ||
throw Error("Request headers missing"); | ||
} | ||
module.exports = class CachePolicy { | ||
constructor(req, res, {shared, cacheHeuristic, _fromObject} = {}) { | ||
if (_fromObject) { | ||
this._fromObject(_fromObject); | ||
return; | ||
} | ||
this._responseTime = this.now(); | ||
this._isShared = shared !== false; | ||
this._cacheHeuristic = undefined !== cacheHeuristic ? cacheHeuristic : 0.1; // 10% matches IE | ||
if (!res || !res.headers) { | ||
throw Error("Response headers missing"); | ||
} | ||
if (!req || !req.headers) { | ||
throw Error("Request headers missing"); | ||
} | ||
this._status = 'status' in res ? res.status : 200; | ||
this._resHeaders = res.headers; | ||
this._rescc = parseCacheControl(res.headers['cache-control']); | ||
this._method = 'method' in req ? req.method : 'GET'; | ||
this._url = req.url; | ||
this._reqHeaders = req.headers; | ||
this._reqcc = parseCacheControl(req.headers['cache-control']); | ||
this._responseTime = this.now(); | ||
this._isShared = shared !== false; | ||
this._cacheHeuristic = undefined !== cacheHeuristic ? cacheHeuristic : 0.1; // 10% matches IE | ||
// When the Cache-Control header field is not present in a request, caches MUST consider the no-cache request pragma-directive | ||
// as having the same effect as if "Cache-Control: no-cache" were present (see Section 5.2.1). | ||
if (!res.headers['cache-control'] && /no-cache/.test(res.headers.pragma)) { | ||
this._rescc['no-cache'] = true; | ||
this._status = 'status' in res ? res.status : 200; | ||
this._resHeaders = res.headers; | ||
this._rescc = parseCacheControl(res.headers['cache-control']); | ||
this._method = 'method' in req ? req.method : 'GET'; | ||
this._url = req.url; | ||
this._host = req.headers.host; | ||
this._noAuthorization = !req.headers.authorization; | ||
this._reqHeaders = res.headers.vary ? req.headers : null; // Don't keep all request headers if they won't be used | ||
this._reqcc = parseCacheControl(req.headers['cache-control']); | ||
// When the Cache-Control header field is not present in a request, caches MUST consider the no-cache request pragma-directive | ||
// as having the same effect as if "Cache-Control: no-cache" were present (see Section 5.2.1). | ||
if (!res.headers['cache-control'] && /no-cache/.test(res.headers.pragma)) { | ||
this._rescc['no-cache'] = true; | ||
} | ||
} | ||
} | ||
CachePolicy.prototype = { | ||
now() { | ||
return Date.now(); | ||
}, | ||
} | ||
@@ -71,3 +78,3 @@ storable() { | ||
// the Authorization header field does not appear in the request, if the cache is shared, | ||
(!this._isShared || !this._reqHeaders.authorization || this._allowsStoringAuthenticated()) && | ||
(!this._isShared || this._noAuthorization || this._allowsStoringAuthenticated()) && | ||
// the response either: | ||
@@ -84,3 +91,3 @@ ( | ||
); | ||
}, | ||
} | ||
@@ -92,3 +99,3 @@ _hasExplicitExpiration() { | ||
this._resHeaders.expires; | ||
}, | ||
} | ||
@@ -110,3 +117,3 @@ satisfiesWithoutRevalidation(req) { | ||
return (!this._url || this._url === req.url) && | ||
(this._reqHeaders.host === req.headers.host) && | ||
(this._host === req.headers.host) && | ||
// the request method associated with the stored response allows it to be used for the presented request, and | ||
@@ -119,3 +126,3 @@ (!req.method || this._method === req.method) && | ||
!this.stale() // TODO: allow stale | ||
}, | ||
} | ||
@@ -125,3 +132,3 @@ _allowsStoringAuthenticated() { | ||
return this._rescc['must-revalidate'] || this._rescc.public || this._rescc['s-maxage']; | ||
}, | ||
} | ||
@@ -134,3 +141,3 @@ _varyMatches(req) { | ||
// A Vary header field-value of "*" always fails to match | ||
if (this._reqHeaders.vary === '*') { | ||
if (this._resHeaders.vary === '*') { | ||
return false; | ||
@@ -144,3 +151,3 @@ } | ||
return true; | ||
}, | ||
} | ||
@@ -162,3 +169,3 @@ responseHeaders() { | ||
return headers; | ||
}, | ||
} | ||
@@ -176,3 +183,3 @@ /** | ||
return dateValue; | ||
}, | ||
} | ||
@@ -194,3 +201,3 @@ /** | ||
return age + residentTime; | ||
}, | ||
} | ||
@@ -244,13 +251,51 @@ maxAge() { | ||
return 0; | ||
}, | ||
} | ||
timeToLive() { | ||
return Math.max(0, this.maxAge() - this.age())*1000; | ||
}, | ||
} | ||
stale() { | ||
return this.maxAge() <= this.age(); | ||
}, | ||
} | ||
static fromObject(obj) { | ||
return new this(undefined, undefined, {_fromObject:obj}); | ||
} | ||
_fromObject(obj) { | ||
if (this._responseTime) throw Error("Reinitialized"); | ||
if (!obj || obj.v !== 1) throw Error("Invalid serialization"); | ||
this._responseTime = obj.t; | ||
this._isShared = obj.sh; | ||
this._cacheHeuristic = obj.ch; | ||
this._status = obj.st; | ||
this._resHeaders = obj.resh; | ||
this._rescc = obj.rescc; | ||
this._method = obj.m; | ||
this._url = obj.u; | ||
this._host = obj.h; | ||
this._noAuthorization = obj.a; | ||
this._reqHeaders = obj.reqh; | ||
this._reqcc = obj.reqcc; | ||
} | ||
toObject() { | ||
return { | ||
v:1, | ||
t: this._responseTime, | ||
sh: this._isShared, | ||
ch: this._cacheHeuristic, | ||
st: this._status, | ||
resh: this._resHeaders, | ||
rescc: this._rescc, | ||
m: this._method, | ||
u: this._url, | ||
h: this._host, | ||
a: this._noAuthorization, | ||
reqh: this._reqHeaders, | ||
reqcc: this._reqcc, | ||
}; | ||
} | ||
}; | ||
module.exports = CachePolicy; |
{ | ||
"name": "http-cache-semantics", | ||
"version": "3.1.0", | ||
"version": "3.2.0", | ||
"description": "Parses Cache-Control headers and friends", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
# Can I cache this? | ||
`CachePolicy` implements HTTP cache semantics. It can tell when responses can be reused from cache, taking into account [RFC 7234](http://httpwg.org/specs/rfc7234.html) rules for user agents and shared caches. It's aware of many tricky details such as the `Vary` header, proxy revalidation, authenticated responses. | ||
`CachePolicy` can tell when responses can be reused from cache, taking into account [HTTP RFC 7234](http://httpwg.org/specs/rfc7234.html) rules for user agents and shared caches. It's aware of many tricky details such as the `Vary` header, proxy revalidation, and authenticated responses. | ||
@@ -79,2 +79,6 @@ ## Usage | ||
### `toObject()`/`fromObject(json)` | ||
Chances are you'll want to store the `CachePolicy` object along with the cached response. `obj = policy.toObject()` gives a plain JSON-serializable object. `policy = CachePolicy.fromObject(obj)` creates an instance from it. | ||
# Yo, FRESH | ||
@@ -81,0 +85,0 @@ |
@@ -38,2 +38,7 @@ 'use strict'; | ||
assert(cache.storable()); | ||
const cache2 = CachePolicy.fromObject(JSON.parse(JSON.stringify(cache.toObject()))); | ||
assert(cache2 instanceof CachePolicy); | ||
assert(!cache2.stale()); | ||
assert(cache2.storable()); | ||
}); | ||
@@ -40,0 +45,0 @@ |
@@ -24,2 +24,7 @@ 'use strict'; | ||
assert.equal(cache.maxAge(), 456); | ||
const cache2 = CachePolicy.fromObject(JSON.parse(JSON.stringify(cache.toObject()))); | ||
assert(cache2 instanceof CachePolicy); | ||
assert(!cache2.stale()); | ||
assert.equal(cache2.maxAge(), 456); | ||
}); | ||
@@ -281,3 +286,8 @@ | ||
assert.equal(res.headers.age, '10'); | ||
const cache2 = TimeTravellingPolicy.fromObject(JSON.parse(JSON.stringify(cache.toObject()))); | ||
assert(cache2 instanceof TimeTravellingPolicy); | ||
const h2 = cache2.responseHeaders(); | ||
assert.deepEqual(h, h2); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
74895
664
99