Browse Source

Vary

master
Kornel Lesiński 9 years ago
committed by Kornel Lesiński
parent
commit
5f6f0837ec
  1. 8
      README.md
  2. 19
      index.js
  3. 75
      test/varytest.js

8
README.md

@ -45,6 +45,12 @@ Returns `true` if the response is stale (i.e. not fresh).
It generally means the response can't be used any more without revalidation with the server. However, there are exceptions, e.g. client can explicitly allow stale responses. A fresh response still may not be used if other conditions—such as `Vary`—are not satisfied.
### `cacheKey()`
Returns a string that is a combination of method, URL, and headers selected with `Vary`.
Note that `Vary: *` never matches any request, so matching of cache keys alone is not sufficient to satisfy a request.
## Implemented
* `Expires` with check for bad clocks
@ -52,8 +58,8 @@ It generally means the response can't be used any more without revalidation with
* `Pragma` response header
* `Age` response header
* Default cacheability of statuses and methods
* Basic support for `Vary`
## Unimplemented
* `Vary` support is as lame and incomplete as in web browsers
* No support for revalidation and stale responses

19
index.js

@ -93,6 +93,22 @@ CachePolicy.prototype = {
return this._rescc['must-revalidate'] || this._rescc['public'] || this._rescc['s-maxage'];
},
_varyKeyForRequest(req) {
if (!this._res.headers.vary) return '';
let key = '';
const fields = this._res.headers.vary.toLowerCase().split(/\s*,\s*/);
fields.sort();
for(const name of fields) {
key += `${name}:${req.headers[name] || '÷'}\n`;
}
return key;
},
cacheKey() {
return `${this._req.method || 'GET'} ${this._req.url || ''} ${this._varyKeyForRequest(this._req)}`;
},
/**
* Value of the Date response header or current time if Date was demed invalid
* @return timestamp
@ -134,8 +150,7 @@ CachePolicy.prototype = {
return 0;
}
// TODO: vary is not supported yet
if (this._res.headers['vary']) {
if (this._res.headers.vary === '*') {
return 0;
}

75
test/varytest.js

@ -0,0 +1,75 @@
'use strict';
const assert = require('assert');
const CachePolicy = require('..');
describe('Vary', function() {
it('Basic', function() {
const cache1 = new CachePolicy({method:'GET',headers:{'weather': 'nice'}}, {headers:{'vary':'weather'}});
const cache2 = new CachePolicy({method:'GET',headers:{'weather': 'bad'}}, {headers:{'vary':'WEATHER'}});
assert.equal(cache1.cacheKey(), cache1.cacheKey());
assert.equal(cache2.cacheKey(), cache2.cacheKey());
assert.notEqual(cache1.cacheKey(), cache2.cacheKey());
});
it("* doesn't match other", function() {
const cache1 = new CachePolicy({method:'GET',headers:{'weather': 'ok'}}, {headers:{'vary':'*'}});
const cache2 = new CachePolicy({method:'GET',headers:{'weather': 'ok'}}, {headers:{'vary':'weather'}});
assert.equal(cache2.cacheKey(), cache2.cacheKey());
assert.notEqual(cache1.cacheKey(), cache2.cacheKey());
});
it("* is stale", function() {
const cache1 = new CachePolicy({method:'GET',headers:{'weather': 'ok'}}, {headers:{'cache-control':'public,max-age=99', 'vary':'*'}});
const cache2 = new CachePolicy({method:'GET',headers:{'weather': 'ok'}}, {headers:{'cache-control':'public,max-age=99', 'vary':'weather'}});
assert.notEqual(cache1.cacheKey(), cache2.cacheKey());
assert(cache1.stale());
assert(!cache2.stale());
});
it('Values are case-sensitive', function() {
const cache1 = new CachePolicy({method:'GET',headers:{'weather': 'BAD'}}, {headers:{'vary':'weather'}});
const cache2 = new CachePolicy({method:'GET',headers:{'weather': 'bad'}}, {headers:{'vary':'weather'}});
assert.notEqual(cache1.cacheKey(), cache2.cacheKey());
});
it('Irrelevant headers ignored', function() {
const cache1 = new CachePolicy({method:'GET',headers:{'weather': 'nice'}}, {headers:{'vary':'moon-phase'}});
const cache2 = new CachePolicy({method:'GET',headers:{'weather': 'bad'}}, {headers:{'vary':'moon-phase'}});
assert.equal(cache1.cacheKey(), cache1.cacheKey());
assert.equal(cache1.cacheKey(), cache2.cacheKey());
});
it('Absence is meaningful', function() {
const cache1 = new CachePolicy({method:'GET',headers:{'weather': 'nice'}}, {headers:{'vary':'moon-phase'}});
const cache2 = new CachePolicy({method:'GET',headers:{'weather': 'bad'}}, {headers:{'vary':'sunshine'}});
assert.equal(cache2.cacheKey(), cache2.cacheKey());
assert.notEqual(cache1.cacheKey(), cache2.cacheKey());
});
it('All values must match', function() {
const cache1 = new CachePolicy({method:'GET',headers:{'sun': 'shining', 'weather': 'nice'}}, {headers:{'vary':'weather, sun'}});
const cache2 = new CachePolicy({method:'GET',headers:{'sun': 'shining', 'weather': 'bad'}}, {headers:{'vary':'weather, sun'}});
assert.notEqual(cache1.cacheKey(), cache2.cacheKey());
});
it('Order is irrelevant', function() {
const cache1 = new CachePolicy({method:'GET',headers:{'weather': 'nice'}}, {headers:{'vary':'moon-phase, SUNSHINE'}});
const cache2 = new CachePolicy({method:'GET',headers:{'weather': 'bad'}}, {headers:{'vary':'sunshine, moon-phase'}});
assert.equal(cache1.cacheKey(), cache2.cacheKey());
const cache3 = new CachePolicy({method:'GET',headers:{'weather': 'nice'}}, {headers:{'vary':'moon-phase, weather'}});
const cache4 = new CachePolicy({method:'GET',headers:{'weather': 'nice'}}, {headers:{'vary':'weather, moon-phase'}});
assert.equal(cache3.cacheKey(), cache4.cacheKey());
assert.notEqual(cache1.cacheKey(), cache3.cacheKey());
assert.notEqual(cache2.cacheKey(), cache4.cacheKey());
});
});
Loading…
Cancel
Save