View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
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 = {}; // no Content-Length header
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 }