1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.http.impl.client.cache;
28
29 import static org.junit.Assert.assertEquals;
30 import static org.junit.Assert.assertFalse;
31 import static org.junit.Assert.assertNull;
32 import static org.junit.Assert.assertSame;
33 import static org.junit.Assert.assertTrue;
34
35 import java.util.Date;
36
37 import org.apache.http.Header;
38 import org.apache.http.HttpRequest;
39 import org.apache.http.HttpVersion;
40 import org.apache.http.client.cache.HttpCacheEntry;
41 import org.apache.http.client.utils.DateUtils;
42 import org.apache.http.message.BasicHeader;
43 import org.apache.http.message.BasicHttpRequest;
44 import org.apache.http.protocol.HTTP;
45 import org.junit.Before;
46 import org.junit.Test;
47
48 public class TestCacheValidityPolicy {
49
50 private CacheValidityPolicy impl;
51 private Date now;
52 private Date oneSecondAgo;
53 private Date sixSecondsAgo;
54 private Date tenSecondsAgo;
55 private Date elevenSecondsAgo;
56
57 @Before
58 public void setUp() {
59 impl = new CacheValidityPolicy();
60 now = new Date();
61 oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
62 sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
63 tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
64 elevenSecondsAgo = new Date(now.getTime() - 11 * 1000L);
65 }
66
67 @Test
68 public void testApparentAgeIsMaxIntIfDateHeaderNotPresent() {
69 final Header[] headers = {
70 new BasicHeader("Server", "MockServer/1.0")
71 };
72 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
73 assertEquals(2147483648L, impl.getApparentAgeSecs(entry));
74 }
75
76 @Test
77 public void testApparentAgeIsResponseReceivedTimeLessDateHeader() {
78 final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils
79 .formatDate(tenSecondsAgo)) };
80
81 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, sixSecondsAgo, headers);
82 assertEquals(4, impl.getApparentAgeSecs(entry));
83 }
84
85 @Test
86 public void testNegativeApparentAgeIsBroughtUpToZero() {
87 final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils
88 .formatDate(sixSecondsAgo)) };
89 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now,tenSecondsAgo,headers);
90 assertEquals(0, impl.getApparentAgeSecs(entry));
91 }
92
93 @Test
94 public void testCorrectedReceivedAgeIsAgeHeaderIfLarger() {
95 final Header[] headers = new Header[] { new BasicHeader("Age", "10"), };
96 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
97 impl = new CacheValidityPolicy() {
98 @Override
99 protected long getApparentAgeSecs(final HttpCacheEntry ent) {
100 return 6;
101 }
102 };
103 assertEquals(10, impl.getCorrectedReceivedAgeSecs(entry));
104 }
105
106 @Test
107 public void testCorrectedReceivedAgeIsApparentAgeIfLarger() {
108 final Header[] headers = new Header[] { new BasicHeader("Age", "6"), };
109 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
110 impl = new CacheValidityPolicy() {
111 @Override
112 protected long getApparentAgeSecs(final HttpCacheEntry ent) {
113 return 10;
114 }
115 };
116 assertEquals(10, impl.getCorrectedReceivedAgeSecs(entry));
117 }
118
119 @Test
120 public void testResponseDelayIsDifferenceBetweenResponseAndRequestTimes() {
121 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, sixSecondsAgo);
122 assertEquals(4, impl.getResponseDelaySecs(entry));
123 }
124
125 @Test
126 public void testCorrectedInitialAgeIsCorrectedReceivedAgePlusResponseDelay() {
127 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
128 impl = new CacheValidityPolicy() {
129 @Override
130 protected long getCorrectedReceivedAgeSecs(final HttpCacheEntry ent) {
131 return 7;
132 }
133
134 @Override
135 protected long getResponseDelaySecs(final HttpCacheEntry ent) {
136 return 13;
137 }
138 };
139 assertEquals(20, impl.getCorrectedInitialAgeSecs(entry));
140 }
141
142 @Test
143 public void testResidentTimeSecondsIsTimeSinceResponseTime() {
144 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, sixSecondsAgo);
145 assertEquals(6, impl.getResidentTimeSecs(entry, now));
146 }
147
148 @Test
149 public void testCurrentAgeIsCorrectedInitialAgePlusResidentTime() {
150 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
151 impl = new CacheValidityPolicy() {
152 @Override
153 protected long getCorrectedInitialAgeSecs(final HttpCacheEntry ent) {
154 return 11;
155 }
156 @Override
157 protected long getResidentTimeSecs(final HttpCacheEntry ent, final Date d) {
158 return 17;
159 }
160 };
161 assertEquals(28, impl.getCurrentAgeSecs(entry, new Date()));
162 }
163
164 @Test
165 public void testFreshnessLifetimeIsSMaxAgeIfPresent() {
166 final Header[] headers = new Header[] { new BasicHeader("Cache-Control", "s-maxage=10") };
167 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
168 assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
169 }
170
171 @Test
172 public void testFreshnessLifetimeIsMaxAgeIfPresent() {
173 final Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10") };
174 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
175 assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
176 }
177
178 @Test
179 public void testFreshnessLifetimeIsMostRestrictiveOfMaxAgeAndSMaxAge() {
180 Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10"),
181 new BasicHeader("Cache-Control", "s-maxage=20") };
182 HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
183 assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
184
185 headers = new Header[] { new BasicHeader("Cache-Control", "max-age=20"),
186 new BasicHeader("Cache-Control", "s-maxage=10") };
187 entry = HttpTestUtils.makeCacheEntry(headers);
188 assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
189 }
190
191 @Test
192 public void testFreshnessLifetimeIsMaxAgeEvenIfExpiresIsPresent() {
193 final Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10"),
194 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
195 new BasicHeader("Expires", DateUtils.formatDate(sixSecondsAgo)) };
196
197 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
198 assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
199 }
200
201 @Test
202 public void testFreshnessLifetimeIsSMaxAgeEvenIfExpiresIsPresent() {
203 final Header[] headers = new Header[] { new BasicHeader("Cache-Control", "s-maxage=10"),
204 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
205 new BasicHeader("Expires", DateUtils.formatDate(sixSecondsAgo)) };
206
207 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
208 assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
209 }
210
211 @Test
212 public void testFreshnessLifetimeIsFromExpiresHeaderIfNoMaxAge() {
213 final Header[] headers = new Header[] {
214 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
215 new BasicHeader("Expires", DateUtils.formatDate(sixSecondsAgo)) };
216
217 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
218 assertEquals(4, impl.getFreshnessLifetimeSecs(entry));
219 }
220
221 @Test
222 public void testHeuristicFreshnessLifetime() {
223 final Header[] headers = new Header[] {
224 new BasicHeader("Date", DateUtils.formatDate(oneSecondAgo)),
225 new BasicHeader("Last-Modified", DateUtils.formatDate(elevenSecondsAgo))
226 };
227 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
228 assertEquals(1, impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, 0));
229 }
230
231 @Test
232 public void testHeuristicFreshnessLifetimeDefaultsProperly() {
233 final long defaultFreshness = 10;
234 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
235 assertEquals(defaultFreshness, impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, defaultFreshness));
236 }
237
238 @Test
239 public void testHeuristicFreshnessLifetimeIsNonNegative() {
240 final Header[] headers = new Header[] {
241 new BasicHeader("Date", DateUtils.formatDate(elevenSecondsAgo)),
242 new BasicHeader("Last-Modified", DateUtils.formatDate(oneSecondAgo))
243 };
244
245 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
246 assertTrue(impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, 10) >= 0);
247 }
248
249 @Test
250 public void testResponseIsFreshIfFreshnessLifetimeExceedsCurrentAge() {
251 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
252 impl = new CacheValidityPolicy() {
253 @Override
254 public long getCurrentAgeSecs(final HttpCacheEntry e, final Date d) {
255 assertSame(entry, e);
256 assertEquals(now, d);
257 return 6;
258 }
259 @Override
260 public long getFreshnessLifetimeSecs(final HttpCacheEntry e) {
261 assertSame(entry, e);
262 return 10;
263 }
264 };
265 assertTrue(impl.isResponseFresh(entry, now));
266 }
267
268 @Test
269 public void testResponseIsNotFreshIfFreshnessLifetimeEqualsCurrentAge() {
270 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
271 impl = new CacheValidityPolicy() {
272 @Override
273 public long getCurrentAgeSecs(final HttpCacheEntry e, final Date d) {
274 assertEquals(now, d);
275 assertSame(entry, e);
276 return 6;
277 }
278 @Override
279 public long getFreshnessLifetimeSecs(final HttpCacheEntry e) {
280 assertSame(entry, e);
281 return 6;
282 }
283 };
284 assertFalse(impl.isResponseFresh(entry, now));
285 }
286
287 @Test
288 public void testResponseIsNotFreshIfCurrentAgeExceedsFreshnessLifetime() {
289 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
290 impl = new CacheValidityPolicy() {
291 @Override
292 public long getCurrentAgeSecs(final HttpCacheEntry e, final Date d) {
293 assertEquals(now, d);
294 assertSame(entry, e);
295 return 10;
296 }
297 @Override
298 public long getFreshnessLifetimeSecs(final HttpCacheEntry e) {
299 assertSame(entry, e);
300 return 6;
301 }
302 };
303 assertFalse(impl.isResponseFresh(entry, now));
304 }
305
306 @Test
307 public void testCacheEntryIsRevalidatableIfHeadersIncludeETag() {
308 final Header[] headers = {
309 new BasicHeader("Expires", DateUtils.formatDate(new Date())),
310 new BasicHeader("ETag", "somevalue")};
311 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
312 assertTrue(impl.isRevalidatable(entry));
313 }
314
315 @Test
316 public void testCacheEntryIsRevalidatableIfHeadersIncludeLastModifiedDate() {
317 final Header[] headers = {
318 new BasicHeader("Expires", DateUtils.formatDate(new Date())),
319 new BasicHeader("Last-Modified", DateUtils.formatDate(new Date())) };
320 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
321 assertTrue(impl.isRevalidatable(entry));
322 }
323
324 @Test
325 public void testCacheEntryIsNotRevalidatableIfNoAppropriateHeaders() {
326 final Header[] headers = {
327 new BasicHeader("Expires", DateUtils.formatDate(new Date())),
328 new BasicHeader("Cache-Control", "public") };
329 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
330 assertFalse(impl.isRevalidatable(entry));
331 }
332
333 @Test
334 public void testMissingContentLengthDoesntInvalidateEntry() {
335 final int contentLength = 128;
336 final Header[] headers = {};
337 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers, HttpTestUtils.getRandomBytes(contentLength));
338 assertTrue(impl.contentLengthHeaderMatchesActualLength(entry));
339 }
340
341 @Test
342 public void testCorrectContentLengthDoesntInvalidateEntry() {
343 final int contentLength = 128;
344 final Header[] headers = { new BasicHeader(HTTP.CONTENT_LEN, Integer.toString(contentLength)) };
345 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers, HttpTestUtils.getRandomBytes(contentLength));
346 assertTrue(impl.contentLengthHeaderMatchesActualLength(entry));
347 }
348
349 @Test
350 public void testWrongContentLengthInvalidatesEntry() {
351 final int contentLength = 128;
352 final Header[] headers = {new BasicHeader(HTTP.CONTENT_LEN, Integer.toString(contentLength+1))};
353 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers, HttpTestUtils.getRandomBytes(contentLength));
354 assertFalse(impl.contentLengthHeaderMatchesActualLength(entry));
355 }
356
357 @Test
358 public void testNullResourceInvalidatesEntry() {
359 final int contentLength = 128;
360 final Header[] headers = {new BasicHeader(HTTP.CONTENT_LEN, Integer.toString(contentLength))};
361 final HttpCacheEntry entry = HttpTestUtils.makeHeadCacheEntry(headers);
362 assertFalse(impl.contentLengthHeaderMatchesActualLength(entry));
363 }
364
365 @Test
366 public void testMalformedContentLengthReturnsNegativeOne() {
367 final Header[] headers = new Header[] { new BasicHeader("Content-Length", "asdf") };
368 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
369 assertEquals(-1, impl.getContentLengthValue(entry));
370 }
371
372 @Test
373 public void testNegativeAgeHeaderValueReturnsMaxAge() {
374 final Header[] headers = new Header[] { new BasicHeader("Age", "-100") };
375 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
376 assertEquals(CacheValidityPolicy.MAX_AGE, impl.getAgeValue(entry));
377 }
378
379 @Test
380 public void testMalformedAgeHeaderValueReturnsMaxAge() {
381 final Header[] headers = new Header[] { new BasicHeader("Age", "asdf") };
382 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
383 assertEquals(CacheValidityPolicy.MAX_AGE, impl.getAgeValue(entry));
384 }
385
386 @Test
387 public void testMalformedCacheControlMaxAgeHeaderReturnsZero() {
388 final Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=asdf") };
389 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
390 assertEquals(0, impl.getMaxAge(entry));
391 }
392
393 @Test
394 public void testMalformedExpirationDateReturnsNull() {
395 final Header[] headers = new Header[] { new BasicHeader("Expires", "asdf") };
396 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
397 assertNull(impl.getExpirationDate(entry));
398 }
399
400 @Test
401 public void testMustRevalidateIsFalseIfDirectiveNotPresent() {
402 final Header[] headers = new Header[] { new BasicHeader("Cache-Control","public") };
403 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
404 assertFalse(impl.mustRevalidate(entry));
405 }
406
407 @Test
408 public void testMustRevalidateIsTrueWhenDirectiveIsPresent() {
409 final Header[] headers = new Header[] { new BasicHeader("Cache-Control","public, must-revalidate") };
410 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
411 assertTrue(impl.mustRevalidate(entry));
412 }
413
414 @Test
415 public void testProxyRevalidateIsFalseIfDirectiveNotPresent() {
416 final Header[] headers = new Header[] { new BasicHeader("Cache-Control","public") };
417 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
418 assertFalse(impl.proxyRevalidate(entry));
419 }
420
421 @Test
422 public void testProxyRevalidateIsTrueWhenDirectiveIsPresent() {
423 final Header[] headers = new Header[] { new BasicHeader("Cache-Control","public, proxy-revalidate") };
424 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
425 assertTrue(impl.proxyRevalidate(entry));
426 }
427
428 @Test
429 public void testMayReturnStaleIfErrorInResponseIsTrueWithinStaleness(){
430 final Header[] headers = new Header[] {
431 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
432 new BasicHeader("Cache-Control", "max-age=5, stale-if-error=15")
433 };
434 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
435 final HttpRequest req = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
436 assertTrue(impl.mayReturnStaleIfError(req, entry, now));
437 }
438
439 @Test
440 public void testMayReturnStaleIfErrorInRequestIsTrueWithinStaleness(){
441 final Header[] headers = new Header[] {
442 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
443 new BasicHeader("Cache-Control", "max-age=5")
444 };
445 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
446 final HttpRequest req = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
447 req.setHeader("Cache-Control","stale-if-error=15");
448 assertTrue(impl.mayReturnStaleIfError(req, entry, now));
449 }
450
451 @Test
452 public void testMayNotReturnStaleIfErrorInResponseAndAfterResponseWindow(){
453 final Header[] headers = new Header[] {
454 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
455 new BasicHeader("Cache-Control", "max-age=5, stale-if-error=1")
456 };
457 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
458 final HttpRequest req = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
459 assertFalse(impl.mayReturnStaleIfError(req, entry, now));
460 }
461
462 @Test
463 public void testMayNotReturnStaleIfErrorInResponseAndAfterRequestWindow(){
464 final Header[] headers = new Header[] {
465 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
466 new BasicHeader("Cache-Control", "max-age=5")
467 };
468 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
469 final HttpRequest req = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
470 req.setHeader("Cache-Control","stale-if-error=1");
471 assertFalse(impl.mayReturnStaleIfError(req, entry, now));
472 }
473
474 @Test
475 public void testMayReturnStaleWhileRevalidatingIsFalseWhenDirectiveIsAbsent() {
476 final Header[] headers = new Header[] { new BasicHeader("Cache-control", "public") };
477 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
478
479 assertFalse(impl.mayReturnStaleWhileRevalidating(entry, now));
480 }
481
482 @Test
483 public void testMayReturnStaleWhileRevalidatingIsTrueWhenWithinStaleness() {
484 final Header[] headers = new Header[] {
485 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
486 new BasicHeader("Cache-Control", "max-age=5, stale-while-revalidate=15")
487 };
488 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
489
490 assertTrue(impl.mayReturnStaleWhileRevalidating(entry, now));
491 }
492
493 @Test
494 public void testMayReturnStaleWhileRevalidatingIsFalseWhenPastStaleness() {
495 final Date twentyFiveSecondsAgo = new Date(now.getTime() - 25 * 1000L);
496 final Header[] headers = new Header[] {
497 new BasicHeader("Date", DateUtils.formatDate(twentyFiveSecondsAgo)),
498 new BasicHeader("Cache-Control", "max-age=5, stale-while-revalidate=15")
499 };
500 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
501
502 assertFalse(impl.mayReturnStaleWhileRevalidating(entry, now));
503 }
504
505 @Test
506 public void testMayReturnStaleWhileRevalidatingIsFalseWhenDirectiveEmpty() {
507 final Header[] headers = new Header[] {
508 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
509 new BasicHeader("Cache-Control", "max-age=5, stale-while-revalidate=")
510 };
511 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
512
513 assertFalse(impl.mayReturnStaleWhileRevalidating(entry, now));
514 }
515 }