1 | 'use strict';
|
2 |
|
3 | const assert = require('assert');
|
4 | const CachePolicy = require('..');
|
5 |
|
6 | const req = {method:'GET', headers:{}};
|
7 |
|
8 | describe('Response headers', function() {
|
9 | it('simple miss', function() {
|
10 | const cache = new CachePolicy(req, {headers:{}});
|
11 | assert(cache.stale());
|
12 | });
|
13 |
|
14 | it('simple hit', function() {
|
15 | const cache = new CachePolicy(req, {headers:{'cache-control': 'public, max-age=999999'}});
|
16 | assert(!cache.stale());
|
17 | assert.equal(cache.maxAge(), 999999);
|
18 | });
|
19 |
|
20 | it('weird syntax', function() {
|
21 | const cache = new CachePolicy(req, {headers:{'cache-control': ',,,,max-age = 456 ,'}});
|
22 | assert(!cache.stale());
|
23 | assert.equal(cache.maxAge(), 456);
|
24 |
|
25 | const cache2 = CachePolicy.fromObject(JSON.parse(JSON.stringify(cache.toObject())));
|
26 | assert(cache2 instanceof CachePolicy);
|
27 | assert(!cache2.stale());
|
28 | assert.equal(cache2.maxAge(), 456);
|
29 | });
|
30 |
|
31 | it('quoted syntax', function() {
|
32 | const cache = new CachePolicy(req, {headers:{'cache-control': ' max-age = "678" '}});
|
33 | assert(!cache.stale());
|
34 | assert.equal(cache.maxAge(), 678);
|
35 | });
|
36 |
|
37 | it('pre-check tolerated', function() {
|
38 | const cc = 'pre-check=0, post-check=0, no-store, no-cache, max-age=100';
|
39 | const cache = new CachePolicy(req, {headers:{'cache-control': cc}});
|
40 | assert(cache.stale());
|
41 | assert(!cache.storable());
|
42 | assert.equal(cache.maxAge(), 0);
|
43 | assert.equal(cache.responseHeaders()['cache-control'], cc);
|
44 | });
|
45 |
|
46 | it('pre-check poison', function() {
|
47 | const origCC = 'pre-check=0, post-check=0, no-cache, no-store, max-age=100, custom, foo=bar';
|
48 | const res = {headers:{'cache-control': origCC, pragma: 'no-cache'}};
|
49 | const cache = new CachePolicy(req, res, {ignoreCargoCult:true});
|
50 | assert(!cache.stale());
|
51 | assert(cache.storable());
|
52 | assert.equal(cache.maxAge(), 100);
|
53 |
|
54 | const cc = cache.responseHeaders()['cache-control'];
|
55 | assert(!/pre-check/.test(cc), cc);
|
56 | assert(!/post-check/.test(cc), cc);
|
57 | assert(!/no-store/.test(cc), cc);
|
58 |
|
59 | assert(/max-age=100/.test(cc));
|
60 | assert(/custom(,|$)/.test(cc));
|
61 | assert(/foo=bar/.test(cc));
|
62 |
|
63 | assert.equal(res.headers['cache-control'], origCC);
|
64 | assert(res.headers['pragma']);
|
65 | assert(!cache.responseHeaders()['pragma']);
|
66 | });
|
67 |
|
68 | it('pre-check poison undefined header', function() {
|
69 | const origCC = 'pre-check=0, post-check=0, no-cache, no-store';
|
70 | const res = {headers:{'cache-control': origCC, expires: 'yesterday!'}};
|
71 | const cache = new CachePolicy(req, res, {ignoreCargoCult:true});
|
72 | assert(cache.stale());
|
73 | assert(cache.storable());
|
74 | assert.equal(cache.maxAge(), 0);
|
75 |
|
76 | const cc = cache.responseHeaders()['cache-control'];
|
77 | assert(!cc);
|
78 |
|
79 | assert(res.headers['expires']);
|
80 | assert(!cache.responseHeaders()['expires']);
|
81 | });
|
82 |
|
83 | it('cache with expires', function() {
|
84 | const cache = new CachePolicy(req, {headers:{
|
85 | 'date': new Date().toGMTString(),
|
86 | 'expires': new Date(Date.now() + 2000).toGMTString(),
|
87 | }});
|
88 | assert(!cache.stale());
|
89 | assert.equal(2, cache.maxAge());
|
90 | });
|
91 |
|
92 | it('cache expires no date', function() {
|
93 | const cache = new CachePolicy(req, {headers:{
|
94 | 'cache-control': 'public',
|
95 | 'expires': new Date(Date.now()+3600*1000).toGMTString(),
|
96 | }});
|
97 | assert(!cache.stale());
|
98 | assert(cache.maxAge() > 3595);
|
99 | assert(cache.maxAge() < 3605);
|
100 | });
|
101 |
|
102 | it('Ages', function() {
|
103 | let now = 1000;
|
104 | class TimeTravellingPolicy extends CachePolicy {
|
105 | now() {
|
106 | return now;
|
107 | }
|
108 | }
|
109 | const cache = new TimeTravellingPolicy(req, {headers:{
|
110 | 'cache-control':'max-age=100',
|
111 | 'age': '50',
|
112 | }});
|
113 | assert(cache.storable());
|
114 |
|
115 | assert.equal(50*1000, cache.timeToLive());
|
116 | assert(!cache.stale());
|
117 | now += 48*1000;
|
118 | assert.equal(2*1000, cache.timeToLive());
|
119 | assert(!cache.stale());
|
120 | now += 5*1000;
|
121 | assert(cache.stale());
|
122 | assert.equal(0, cache.timeToLive());
|
123 | });
|
124 |
|
125 | it('Age can make stale', function() {
|
126 | const cache = new CachePolicy(req, {headers:{
|
127 | 'cache-control':'max-age=100',
|
128 | 'age': '101',
|
129 | }});
|
130 | assert(cache.stale());
|
131 | assert(cache.storable());
|
132 | });
|
133 |
|
134 | it('Age not always stale', function() {
|
135 | const cache = new CachePolicy(req, {headers:{
|
136 | 'cache-control':'max-age=20',
|
137 | 'age': '15',
|
138 | }});
|
139 | assert(!cache.stale());
|
140 | assert(cache.storable());
|
141 | });
|
142 |
|
143 | it('Bogus age ignored', function() {
|
144 | const cache = new CachePolicy(req, {headers:{
|
145 | 'cache-control':'max-age=20',
|
146 | 'age': 'golden',
|
147 | }});
|
148 | assert(!cache.stale());
|
149 | assert(cache.storable());
|
150 | });
|
151 |
|
152 | it('cache old files', function() {
|
153 | const cache = new CachePolicy(req, {headers:{
|
154 | 'date': new Date().toGMTString(),
|
155 | 'last-modified': 'Mon, 07 Mar 2016 11:52:56 GMT',
|
156 | }});
|
157 | assert(!cache.stale());
|
158 | assert(cache.maxAge() > 100);
|
159 | });
|
160 |
|
161 | it('immutable simple hit', function() {
|
162 | const cache = new CachePolicy(req, {headers:{'cache-control': 'immutable, max-age=999999'}});
|
163 | assert(!cache.stale());
|
164 | assert.equal(cache.maxAge(), 999999);
|
165 | });
|
166 |
|
167 | it('immutable can expire', function() {
|
168 | const cache = new CachePolicy(req, {headers:{'cache-control': 'immutable, max-age=0'}});
|
169 | assert(cache.stale());
|
170 | assert.equal(cache.maxAge(), 0);
|
171 | });
|
172 |
|
173 | it('cache immutable files', function() {
|
174 | const cache = new CachePolicy(req, {headers:{
|
175 | 'date': new Date().toGMTString(),
|
176 | 'cache-control':'immutable',
|
177 | 'last-modified': new Date().toGMTString(),
|
178 | }});
|
179 | assert(!cache.stale());
|
180 | assert(cache.maxAge() > 100);
|
181 | });
|
182 |
|
183 | it('immutable can be off', function() {
|
184 | const cache = new CachePolicy(req, {headers:{
|
185 | 'date': new Date().toGMTString(),
|
186 | 'cache-control':'immutable',
|
187 | 'last-modified': new Date().toGMTString(),
|
188 | }}, {immutableMinTimeToLive: 0});
|
189 | assert(cache.stale());
|
190 | assert.equal(cache.maxAge(), 0);
|
191 | });
|
192 |
|
193 | it('pragma: no-cache', function() {
|
194 | const cache = new CachePolicy(req, {headers:{
|
195 | 'pragma': 'no-cache',
|
196 | 'last-modified': 'Mon, 07 Mar 2016 11:52:56 GMT',
|
197 | }});
|
198 | assert(cache.stale());
|
199 | });
|
200 |
|
201 | it('no-store', function() {
|
202 | const cache = new CachePolicy(req, {headers:{
|
203 | 'cache-control': 'no-store, public, max-age=1',
|
204 | }});
|
205 | assert(cache.stale());
|
206 | assert.equal(0, cache.maxAge());
|
207 | });
|
208 |
|
209 | it('observe private cache', function() {
|
210 | const privateHeader = {
|
211 | 'cache-control': 'private, max-age=1234',
|
212 | };
|
213 | const proxyCache = new CachePolicy(req, {headers:privateHeader});
|
214 | assert(proxyCache.stale());
|
215 | assert.equal(0, proxyCache.maxAge());
|
216 |
|
217 | const uaCache = new CachePolicy(req, {headers:privateHeader}, {shared:false});
|
218 | assert(!uaCache.stale());
|
219 | assert.equal(1234, uaCache.maxAge());
|
220 | });
|
221 |
|
222 | it('don\'t share cookies', function() {
|
223 | const cookieHeader = {
|
224 | 'set-cookie': 'foo=bar',
|
225 | 'cache-control': 'max-age=99',
|
226 | };
|
227 | const proxyCache = new CachePolicy(req, {headers:cookieHeader}, {shared:true});
|
228 | assert(proxyCache.stale());
|
229 | assert.equal(0, proxyCache.maxAge());
|
230 |
|
231 | const uaCache = new CachePolicy(req, {headers:cookieHeader}, {shared:false});
|
232 | assert(!uaCache.stale());
|
233 | assert.equal(99, uaCache.maxAge());
|
234 | });
|
235 |
|
236 | it('do share cookies if immutable', function() {
|
237 | const cookieHeader = {
|
238 | 'set-cookie': 'foo=bar',
|
239 | 'cache-control': 'immutable, max-age=99',
|
240 | };
|
241 | const proxyCache = new CachePolicy(req, {headers:cookieHeader}, {shared:true});
|
242 | assert(!proxyCache.stale());
|
243 | assert.equal(99, proxyCache.maxAge());
|
244 | });
|
245 |
|
246 | it('cache explicitly public cookie', function() {
|
247 | const cookieHeader = {
|
248 | 'set-cookie': 'foo=bar',
|
249 | 'cache-control': 'max-age=5, public',
|
250 | };
|
251 | const proxyCache = new CachePolicy(req, {headers:cookieHeader}, {shared:true});
|
252 | assert(!proxyCache.stale());
|
253 | assert.equal(5, proxyCache.maxAge());
|
254 | });
|
255 |
|
256 | it('miss max-age=0', function() {
|
257 | const cache = new CachePolicy(req, {headers:{
|
258 | 'cache-control': 'public, max-age=0',
|
259 | }});
|
260 | assert(cache.stale());
|
261 | assert.equal(0, cache.maxAge());
|
262 | });
|
263 |
|
264 | it('uncacheable 503', function() {
|
265 | const cache = new CachePolicy(req, {
|
266 | status: 503,
|
267 | headers:{
|
268 | 'cache-control': 'public, max-age=1000',
|
269 | }});
|
270 | assert(cache.stale());
|
271 | assert.equal(0, cache.maxAge());
|
272 | });
|
273 |
|
274 | it('cacheable 301', function() {
|
275 | const cache = new CachePolicy(req, {
|
276 | status: 301,
|
277 | headers:{
|
278 | 'last-modified': 'Mon, 07 Mar 2016 11:52:56 GMT',
|
279 | }});
|
280 | assert(!cache.stale());
|
281 | });
|
282 |
|
283 | it('uncacheable 303', function() {
|
284 | const cache = new CachePolicy(req, {
|
285 | status: 303,
|
286 | headers:{
|
287 | 'last-modified': 'Mon, 07 Mar 2016 11:52:56 GMT',
|
288 | }});
|
289 | assert(cache.stale());
|
290 | assert.equal(0, cache.maxAge());
|
291 | });
|
292 |
|
293 | it('cacheable 303', function() {
|
294 | const cache = new CachePolicy(req, {
|
295 | status: 303,
|
296 | headers:{
|
297 | 'cache-control': 'max-age=1000',
|
298 | }});
|
299 | assert(!cache.stale());
|
300 | });
|
301 |
|
302 | it('uncacheable 412', function() {
|
303 | const cache = new CachePolicy(req, {
|
304 | status: 412,
|
305 | headers:{
|
306 | 'cache-control': 'public, max-age=1000',
|
307 | }});
|
308 | assert(cache.stale());
|
309 | assert.equal(0, cache.maxAge());
|
310 | });
|
311 |
|
312 | it('expired expires cached with max-age', function() {
|
313 | const cache = new CachePolicy(req, {headers:{
|
314 | 'cache-control': 'public, max-age=9999',
|
315 | 'expires': 'Sat, 07 May 2016 15:35:18 GMT',
|
316 | }});
|
317 | assert(!cache.stale());
|
318 | assert.equal(9999, cache.maxAge());
|
319 | });
|
320 |
|
321 | it('expired expires cached with s-maxage', function() {
|
322 | const sMaxAgeHeaders = {
|
323 | 'cache-control': 'public, s-maxage=9999',
|
324 | 'expires': 'Sat, 07 May 2016 15:35:18 GMT',
|
325 | };
|
326 | const proxyCache = new CachePolicy(req, {headers:sMaxAgeHeaders});
|
327 | assert(!proxyCache.stale());
|
328 | assert.equal(9999, proxyCache.maxAge());
|
329 |
|
330 | const uaCache = new CachePolicy(req, {headers:sMaxAgeHeaders}, {shared:false});
|
331 | assert(uaCache.stale());
|
332 | assert.equal(0, uaCache.maxAge());
|
333 | });
|
334 |
|
335 | it('max-age wins over future expires', function() {
|
336 | const cache = new CachePolicy(req, {headers:{
|
337 | 'cache-control': 'public, max-age=333',
|
338 | 'expires': new Date(Date.now()+3600*1000).toGMTString(),
|
339 | }});
|
340 | assert(!cache.stale());
|
341 | assert.equal(333, cache.maxAge());
|
342 | });
|
343 |
|
344 | it('remove hop headers', function() {
|
345 | let now = 10000;
|
346 | class TimeTravellingPolicy extends CachePolicy {
|
347 | now() {
|
348 | return now;
|
349 | }
|
350 | }
|
351 |
|
352 | const res = {headers:{
|
353 | 'te': 'deflate',
|
354 | 'date': 'now',
|
355 | 'custom': 'header',
|
356 | 'oompa': 'lumpa',
|
357 | 'connection': 'close, oompa, header',
|
358 | 'age': '10',
|
359 | 'cache-control': 'public, max-age=333',
|
360 | }};
|
361 | const cache = new TimeTravellingPolicy(req, res);
|
362 |
|
363 | now += 1005;
|
364 | const h = cache.responseHeaders();
|
365 | assert(!h.connection);
|
366 | assert(!h.te);
|
367 | assert(!h.oompa);
|
368 | assert.equal(h['cache-control'], 'public, max-age=333');
|
369 | assert.equal(h.date, 'now', "date must stay the same for expires, age, etc");
|
370 | assert.equal(h.custom, 'header');
|
371 | assert.equal(h.age, '11');
|
372 | assert.equal(res.headers.age, '10');
|
373 |
|
374 | const cache2 = TimeTravellingPolicy.fromObject(JSON.parse(JSON.stringify(cache.toObject())));
|
375 | assert(cache2 instanceof TimeTravellingPolicy);
|
376 | const h2 = cache2.responseHeaders();
|
377 | assert.deepEqual(h, h2);
|
378 | });
|
379 | });
|