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.mockito.Mockito.mock;
30  import static org.mockito.Mockito.verify;
31  import static org.mockito.Mockito.verifyNoMoreInteractions;
32  import static org.mockito.Mockito.when;
33  
34  import java.io.IOException;
35  import java.util.Date;
36  import java.util.HashMap;
37  import java.util.Map;
38  
39  import org.apache.http.Header;
40  import org.apache.http.HttpEntityEnclosingRequest;
41  import org.apache.http.HttpHost;
42  import org.apache.http.HttpRequest;
43  import org.apache.http.HttpResponse;
44  import org.apache.http.HttpStatus;
45  import org.apache.http.HttpVersion;
46  import org.apache.http.ProtocolVersion;
47  import org.apache.http.client.cache.HttpCacheEntry;
48  import org.apache.http.client.cache.HttpCacheStorage;
49  import org.apache.http.client.utils.DateUtils;
50  import org.apache.http.message.BasicHeader;
51  import org.apache.http.message.BasicHttpEntityEnclosingRequest;
52  import org.apache.http.message.BasicHttpRequest;
53  import org.apache.http.message.BasicHttpResponse;
54  import org.junit.Before;
55  import org.junit.Test;
56  
57  public class TestCacheInvalidator {
58  
59      private static final ProtocolVersion HTTP_1_1 = new ProtocolVersion("HTTP", 1, 1);
60  
61      private CacheInvalidator impl;
62      private HttpCacheStorage mockStorage;
63      private HttpHost host;
64      private CacheKeyGenerator cacheKeyGenerator;
65      private HttpCacheEntry mockEntry;
66      private HttpRequest request;
67      private HttpResponse response;
68  
69      private Date now;
70      private Date tenSecondsAgo;
71  
72      @Before
73      public void setUp() {
74          now = new Date();
75          tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
76  
77          host = new HttpHost("foo.example.com");
78          mockStorage = mock(HttpCacheStorage.class);
79          cacheKeyGenerator = new CacheKeyGenerator();
80          mockEntry = mock(HttpCacheEntry.class);
81          request = HttpTestUtils.makeDefaultRequest();
82          response = HttpTestUtils.make200Response();
83  
84          impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
85      }
86  
87      // Tests
88      @Test
89      public void testInvalidatesRequestsThatArentGETorHEAD() throws Exception {
90          request = new BasicHttpRequest("POST","/path", HTTP_1_1);
91          final String theUri = "http://foo.example.com:80/path";
92          final Map<String,String> variantMap = new HashMap<String,String>();
93          cacheEntryHasVariantMap(variantMap);
94  
95          cacheReturnsEntryForUri(theUri);
96  
97          impl.flushInvalidatedCacheEntries(host, request);
98  
99          verify(mockEntry).getVariantMap();
100         verify(mockStorage).getEntry(theUri);
101         verify(mockStorage).removeEntry(theUri);
102     }
103 
104     @Test
105     public void testInvalidatesUrisInContentLocationHeadersOnPUTs() throws Exception {
106         final HttpEntityEnclosingRequest putRequest = new BasicHttpEntityEnclosingRequest("PUT","/",HTTP_1_1);
107         putRequest.setEntity(HttpTestUtils.makeBody(128));
108         putRequest.setHeader("Content-Length","128");
109 
110         final String contentLocation = "http://foo.example.com/content";
111         putRequest.setHeader("Content-Location", contentLocation);
112 
113         final String theUri = "http://foo.example.com:80/";
114         cacheEntryHasVariantMap(new HashMap<String,String>());
115 
116         cacheReturnsEntryForUri(theUri);
117 
118         impl.flushInvalidatedCacheEntries(host, putRequest);
119 
120         verify(mockEntry).getVariantMap();
121         verify(mockStorage).getEntry(theUri);
122         verify(mockStorage).removeEntry(theUri);
123         verify(mockStorage).removeEntry("http://foo.example.com:80/content");
124     }
125 
126     @Test
127     public void testInvalidatesUrisInLocationHeadersOnPUTs() throws Exception {
128         final HttpEntityEnclosingRequest putRequest = new BasicHttpEntityEnclosingRequest("PUT","/",HTTP_1_1);
129         putRequest.setEntity(HttpTestUtils.makeBody(128));
130         putRequest.setHeader("Content-Length","128");
131 
132         final String contentLocation = "http://foo.example.com/content";
133         putRequest.setHeader("Location",contentLocation);
134 
135         final String theUri = "http://foo.example.com:80/";
136         cacheEntryHasVariantMap(new HashMap<String,String>());
137 
138         cacheReturnsEntryForUri(theUri);
139 
140         impl.flushInvalidatedCacheEntries(host, putRequest);
141 
142         verify(mockEntry).getVariantMap();
143         verify(mockStorage).getEntry(theUri);
144         verify(mockStorage).removeEntry(theUri);
145         verify(mockStorage).removeEntry(cacheKeyGenerator.canonicalizeUri(contentLocation));
146     }
147 
148     @Test
149     public void testInvalidatesRelativeUrisInContentLocationHeadersOnPUTs() throws Exception {
150         final HttpEntityEnclosingRequest putRequest = new BasicHttpEntityEnclosingRequest("PUT","/",HTTP_1_1);
151         putRequest.setEntity(HttpTestUtils.makeBody(128));
152         putRequest.setHeader("Content-Length","128");
153 
154         final String relativePath = "/content";
155         putRequest.setHeader("Content-Location",relativePath);
156 
157         final String theUri = "http://foo.example.com:80/";
158         cacheEntryHasVariantMap(new HashMap<String,String>());
159 
160         cacheReturnsEntryForUri(theUri);
161 
162         impl.flushInvalidatedCacheEntries(host, putRequest);
163 
164         verify(mockEntry).getVariantMap();
165         verify(mockStorage).getEntry(theUri);
166         verify(mockStorage).removeEntry(theUri);
167         verify(mockStorage).removeEntry("http://foo.example.com:80/content");
168     }
169 
170     @Test
171     public void testDoesNotInvalidateUrisInContentLocationHeadersOnPUTsToDifferentHosts() throws Exception {
172         final HttpEntityEnclosingRequest putRequest = new BasicHttpEntityEnclosingRequest("PUT","/",HTTP_1_1);
173         putRequest.setEntity(HttpTestUtils.makeBody(128));
174         putRequest.setHeader("Content-Length","128");
175 
176         final String contentLocation = "http://bar.example.com/content";
177         putRequest.setHeader("Content-Location",contentLocation);
178 
179         final String theUri = "http://foo.example.com:80/";
180         cacheEntryHasVariantMap(new HashMap<String,String>());
181 
182         cacheReturnsEntryForUri(theUri);
183 
184         impl.flushInvalidatedCacheEntries(host, putRequest);
185 
186         verify(mockEntry).getVariantMap();
187         verify(mockStorage).getEntry(theUri);
188         verify(mockStorage).removeEntry(theUri);
189     }
190 
191     @Test
192     public void testDoesNotInvalidateGETRequest() throws Exception {
193         request = new BasicHttpRequest("GET","/",HTTP_1_1);
194         impl.flushInvalidatedCacheEntries(host, request);
195 
196         verify(mockStorage).getEntry("http://foo.example.com:80/");
197         verifyNoMoreInteractions(mockStorage);
198     }
199 
200     @Test
201     public void testDoesNotInvalidateHEADRequest() throws Exception {
202         request = new BasicHttpRequest("HEAD","/",HTTP_1_1);
203         impl.flushInvalidatedCacheEntries(host, request);
204 
205         verify(mockStorage).getEntry("http://foo.example.com:80/");
206         verifyNoMoreInteractions(mockStorage);
207     }
208 
209     @Test
210     public void testInvalidatesHEADCacheEntryIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception {
211         impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
212         final String theURI = "http://foo.example.com:80/";
213         request = new BasicHttpRequest("GET", theURI,HTTP_1_1);
214 
215         cacheEntryisForMethod("HEAD");
216         cacheEntryHasVariantMap(new HashMap<String, String>());
217         cacheReturnsEntryForUri(theURI);
218 
219         impl.flushInvalidatedCacheEntries(host, request);
220 
221         verify(mockEntry).getRequestMethod();
222         verify(mockEntry).getVariantMap();
223         verify(mockStorage).getEntry(theURI);
224         verify(mockStorage).removeEntry(theURI);
225     }
226 
227     @Test
228     public void testInvalidatesVariantHEADCacheEntriesIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception {
229         impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
230         final String theURI = "http://foo.example.com:80/";
231         request = new BasicHttpRequest("GET", theURI,HTTP_1_1);
232         final String theVariantKey = "{Accept-Encoding=gzip%2Cdeflate&User-Agent=Apache-HttpClient}";
233         final String theVariantURI = "{Accept-Encoding=gzip%2Cdeflate&User-Agent=Apache-HttpClient}http://foo.example.com:80/";
234         final Map<String, String> variants = HttpTestUtils.makeDefaultVariantMap(theVariantKey, theVariantURI);
235 
236         cacheEntryisForMethod("HEAD");
237         cacheEntryHasVariantMap(variants);
238         cacheReturnsEntryForUri(theURI);
239 
240         impl.flushInvalidatedCacheEntries(host, request);
241 
242         verify(mockEntry).getRequestMethod();
243         verify(mockEntry).getVariantMap();
244         verify(mockStorage).getEntry(theURI);
245         verify(mockStorage).removeEntry(theURI);
246         verify(mockStorage).removeEntry(theVariantURI);
247     }
248 
249     @Test
250     public void testDoesNotInvalidateHEADCacheEntry() throws Exception {
251         final String theURI = "http://foo.example.com:80/";
252         request = new BasicHttpRequest("HEAD", theURI,HTTP_1_1);
253 
254         cacheReturnsEntryForUri(theURI);
255 
256         impl.flushInvalidatedCacheEntries(host, request);
257 
258         verify(mockStorage).getEntry(theURI);
259         verifyNoMoreInteractions(mockStorage);
260     }
261 
262     @Test
263     public void testDoesNotInvalidateHEADCacheEntryIfSubsequentHEADRequestsAreMadeToTheSameURI() throws Exception {
264         impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
265         final String theURI = "http://foo.example.com:80/";
266         request = new BasicHttpRequest("HEAD", theURI,HTTP_1_1);
267 
268         cacheReturnsEntryForUri(theURI);
269 
270         impl.flushInvalidatedCacheEntries(host, request);
271 
272         verify(mockStorage).getEntry(theURI);
273         verifyNoMoreInteractions(mockStorage);
274     }
275 
276     @Test
277     public void testDoesNotInvalidateGETCacheEntryIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception {
278         impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
279         final String theURI = "http://foo.example.com:80/";
280         request = new BasicHttpRequest("GET", theURI,HTTP_1_1);
281 
282         cacheEntryisForMethod("GET");
283         cacheReturnsEntryForUri(theURI);
284 
285         impl.flushInvalidatedCacheEntries(host, request);
286 
287         verify(mockEntry).getRequestMethod();
288         verify(mockStorage).getEntry(theURI);
289         verifyNoMoreInteractions(mockStorage);
290     }
291 
292     @Test
293     public void testDoesNotInvalidateRequestsWithClientCacheControlHeaders() throws Exception {
294         request = new BasicHttpRequest("GET","/",HTTP_1_1);
295         request.setHeader("Cache-Control","no-cache");
296 
297         impl.flushInvalidatedCacheEntries(host, request);
298 
299         verify(mockStorage).getEntry("http://foo.example.com:80/");
300         verifyNoMoreInteractions(mockStorage);
301     }
302 
303     @Test
304     public void testDoesNotInvalidateRequestsWithClientPragmaHeaders() throws Exception {
305         request = new BasicHttpRequest("GET","/",HTTP_1_1);
306         request.setHeader("Pragma","no-cache");
307 
308         impl.flushInvalidatedCacheEntries(host, request);
309 
310         verify(mockStorage).getEntry("http://foo.example.com:80/");
311         verifyNoMoreInteractions(mockStorage);
312     }
313 
314     @Test
315     public void testVariantURIsAreFlushedAlso() throws Exception {
316         request = new BasicHttpRequest("POST","/",HTTP_1_1);
317         final String theUri = "http://foo.example.com:80/";
318         final String variantUri = "theVariantURI";
319         final Map<String,String> mapOfURIs = HttpTestUtils.makeDefaultVariantMap(variantUri, variantUri);
320 
321         cacheReturnsEntryForUri(theUri);
322         cacheEntryHasVariantMap(mapOfURIs);
323 
324         impl.flushInvalidatedCacheEntries(host, request);
325 
326         verify(mockStorage).getEntry(theUri);
327         verify(mockEntry).getVariantMap();
328         verify(mockStorage).removeEntry(variantUri);
329         verify(mockStorage).removeEntry(theUri);
330     }
331 
332     @Test
333     public void testCacheFlushException() throws Exception {
334         request = new BasicHttpRequest("POST","/",HTTP_1_1);
335         final String theURI = "http://foo.example.com:80/";
336 
337         cacheReturnsExceptionForUri(theURI);
338 
339         impl.flushInvalidatedCacheEntries(host, request);
340 
341         verify(mockStorage).getEntry(theURI);
342         verifyNoMoreInteractions(mockStorage);
343     }
344 
345     @Test
346     public void doesNotFlushForResponsesWithoutContentLocation()
347             throws Exception {
348         impl.flushInvalidatedCacheEntries(host, request, response);
349 
350         verifyNoMoreInteractions(mockStorage);
351     }
352 
353     @Test
354     public void flushesEntryIfFresherAndSpecifiedByContentLocation()
355             throws Exception {
356         response.setHeader("ETag","\"new-etag\"");
357         response.setHeader("Date", DateUtils.formatDate(now));
358         final String theURI = "http://foo.example.com:80/bar";
359         response.setHeader("Content-Location", theURI);
360 
361         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
362            new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
363            new BasicHeader("ETag", "\"old-etag\"")
364         });
365 
366         when(mockStorage.getEntry(theURI)).thenReturn(entry);
367 
368         impl.flushInvalidatedCacheEntries(host, request, response);
369 
370         verify(mockStorage).getEntry(theURI);
371         verify(mockStorage).removeEntry(theURI);
372     }
373 
374     @Test
375     public void flushesEntryIfFresherAndSpecifiedByLocation()
376             throws Exception {
377         response.setStatusCode(201);
378         response.setHeader("ETag","\"new-etag\"");
379         response.setHeader("Date", DateUtils.formatDate(now));
380         final String theURI = "http://foo.example.com:80/bar";
381         response.setHeader("Location", theURI);
382 
383         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
384            new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
385            new BasicHeader("ETag", "\"old-etag\"")
386         });
387 
388         when(mockStorage.getEntry(theURI)).thenReturn(entry);
389 
390         impl.flushInvalidatedCacheEntries(host, request, response);
391 
392         verify(mockStorage).getEntry(theURI);
393         verify(mockStorage).removeEntry(theURI);
394     }
395 
396     @Test
397     public void doesNotFlushEntryForUnsuccessfulResponse()
398             throws Exception {
399         response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_BAD_REQUEST, "Bad Request");
400         response.setHeader("ETag","\"new-etag\"");
401         response.setHeader("Date", DateUtils.formatDate(now));
402         final String theURI = "http://foo.example.com:80/bar";
403         response.setHeader("Content-Location", theURI);
404 
405         impl.flushInvalidatedCacheEntries(host, request, response);
406 
407         verifyNoMoreInteractions(mockStorage);
408     }
409 
410     @Test
411     public void flushesEntryIfFresherAndSpecifiedByNonCanonicalContentLocation()
412             throws Exception {
413         response.setHeader("ETag","\"new-etag\"");
414         response.setHeader("Date", DateUtils.formatDate(now));
415         final String cacheKey = "http://foo.example.com:80/bar";
416         response.setHeader("Content-Location", "http://foo.example.com/bar");
417 
418         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
419            new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
420            new BasicHeader("ETag", "\"old-etag\"")
421         });
422 
423         when(mockStorage.getEntry(cacheKey)).thenReturn(entry);
424 
425         impl.flushInvalidatedCacheEntries(host, request, response);
426 
427         verify(mockStorage).getEntry(cacheKey);
428         verify(mockStorage).removeEntry(cacheKey);
429     }
430 
431     @Test
432     public void flushesEntryIfFresherAndSpecifiedByRelativeContentLocation()
433             throws Exception {
434         response.setHeader("ETag","\"new-etag\"");
435         response.setHeader("Date", DateUtils.formatDate(now));
436         final String cacheKey = "http://foo.example.com:80/bar";
437         response.setHeader("Content-Location", "/bar");
438 
439         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
440            new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
441            new BasicHeader("ETag", "\"old-etag\"")
442         });
443 
444         when(mockStorage.getEntry(cacheKey)).thenReturn(entry);
445 
446         impl.flushInvalidatedCacheEntries(host, request, response);
447 
448         verify(mockStorage).getEntry(cacheKey);
449         verify(mockStorage).removeEntry(cacheKey);
450     }
451 
452     @Test
453     public void doesNotFlushEntryIfContentLocationFromDifferentHost()
454             throws Exception {
455         response.setHeader("ETag","\"new-etag\"");
456         response.setHeader("Date", DateUtils.formatDate(now));
457         final String cacheKey = "http://baz.example.com:80/bar";
458         response.setHeader("Content-Location", cacheKey);
459 
460         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
461            new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
462            new BasicHeader("ETag", "\"old-etag\"")
463         });
464 
465         when(mockStorage.getEntry(cacheKey)).thenReturn(entry);
466 
467         impl.flushInvalidatedCacheEntries(host, request, response);
468 
469         verify(mockStorage).getEntry(cacheKey);
470         verifyNoMoreInteractions(mockStorage);
471     }
472 
473 
474 
475     @Test
476     public void doesNotFlushEntrySpecifiedByContentLocationIfEtagsMatch()
477             throws Exception {
478         response.setHeader("ETag","\"same-etag\"");
479         response.setHeader("Date", DateUtils.formatDate(now));
480         final String theURI = "http://foo.example.com:80/bar";
481         response.setHeader("Content-Location", theURI);
482 
483         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
484            new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
485            new BasicHeader("ETag", "\"same-etag\"")
486         });
487 
488         when(mockStorage.getEntry(theURI)).thenReturn(entry);
489         impl.flushInvalidatedCacheEntries(host, request, response);
490 
491         verify(mockStorage).getEntry(theURI);
492         verifyNoMoreInteractions(mockStorage);
493     }
494 
495     @Test
496     public void doesNotFlushEntrySpecifiedByContentLocationIfOlder()
497             throws Exception {
498         response.setHeader("ETag","\"new-etag\"");
499         response.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
500         final String theURI = "http://foo.example.com:80/bar";
501         response.setHeader("Content-Location", theURI);
502 
503         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
504            new BasicHeader("Date", DateUtils.formatDate(now)),
505            new BasicHeader("ETag", "\"old-etag\"")
506         });
507 
508         when(mockStorage.getEntry(theURI)).thenReturn(entry);
509 
510         impl.flushInvalidatedCacheEntries(host, request, response);
511 
512         verify(mockStorage).getEntry(theURI);
513         verifyNoMoreInteractions(mockStorage);
514     }
515 
516     @Test
517     public void doesNotFlushEntryIfNotInCache()
518             throws Exception {
519         response.setHeader("ETag","\"new-etag\"");
520         response.setHeader("Date", DateUtils.formatDate(now));
521         final String theURI = "http://foo.example.com:80/bar";
522         response.setHeader("Content-Location", theURI);
523 
524         when(mockStorage.getEntry(theURI)).thenReturn(null);
525 
526         impl.flushInvalidatedCacheEntries(host, request, response);
527 
528         verify(mockStorage).getEntry(theURI);
529         verifyNoMoreInteractions(mockStorage);
530     }
531 
532     @Test
533     public void doesNotFlushEntrySpecifiedByContentLocationIfResponseHasNoEtag()
534             throws Exception {
535         response.removeHeaders("ETag");
536         response.setHeader("Date", DateUtils.formatDate(now));
537         final String theURI = "http://foo.example.com:80/bar";
538         response.setHeader("Content-Location", theURI);
539 
540         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
541            new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
542            new BasicHeader("ETag", "\"old-etag\"")
543         });
544 
545         when(mockStorage.getEntry(theURI)).thenReturn(entry);
546 
547         impl.flushInvalidatedCacheEntries(host, request, response);
548 
549         verify(mockStorage).getEntry(theURI);
550         verifyNoMoreInteractions(mockStorage);
551     }
552 
553     @Test
554     public void doesNotFlushEntrySpecifiedByContentLocationIfEntryHasNoEtag()
555             throws Exception {
556         response.setHeader("ETag", "\"some-etag\"");
557         response.setHeader("Date", DateUtils.formatDate(now));
558         final String theURI = "http://foo.example.com:80/bar";
559         response.setHeader("Content-Location", theURI);
560 
561         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
562            new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
563         });
564 
565         when(mockStorage.getEntry(theURI)).thenReturn(entry);
566 
567         impl.flushInvalidatedCacheEntries(host, request, response);
568 
569         verify(mockStorage).getEntry(theURI);
570         verifyNoMoreInteractions(mockStorage);
571     }
572 
573     @Test
574     public void flushesEntrySpecifiedByContentLocationIfResponseHasNoDate()
575             throws Exception {
576         response.setHeader("ETag", "\"new-etag\"");
577         response.removeHeaders("Date");
578         final String theURI = "http://foo.example.com:80/bar";
579         response.setHeader("Content-Location", theURI);
580 
581         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
582                 new BasicHeader("ETag", "\"old-etag\""),
583                 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
584         });
585 
586         when(mockStorage.getEntry(theURI)).thenReturn(entry);
587 
588         impl.flushInvalidatedCacheEntries(host, request, response);
589 
590         verify(mockStorage).getEntry(theURI);
591         verify(mockStorage).removeEntry(theURI);
592         verifyNoMoreInteractions(mockStorage);
593     }
594 
595     @Test
596     public void flushesEntrySpecifiedByContentLocationIfEntryHasNoDate()
597             throws Exception {
598         response.setHeader("ETag","\"new-etag\"");
599         response.setHeader("Date", DateUtils.formatDate(now));
600         final String theURI = "http://foo.example.com:80/bar";
601         response.setHeader("Content-Location", theURI);
602 
603         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
604            new BasicHeader("ETag", "\"old-etag\"")
605         });
606 
607         when(mockStorage.getEntry(theURI)).thenReturn(entry);
608 
609         impl.flushInvalidatedCacheEntries(host, request, response);
610 
611         verify(mockStorage).getEntry(theURI);
612         verify(mockStorage).removeEntry(theURI);
613         verifyNoMoreInteractions(mockStorage);
614     }
615 
616     @Test
617     public void flushesEntrySpecifiedByContentLocationIfResponseHasMalformedDate()
618             throws Exception {
619         response.setHeader("ETag","\"new-etag\"");
620         response.setHeader("Date", "blarg");
621         final String theURI = "http://foo.example.com:80/bar";
622         response.setHeader("Content-Location", theURI);
623 
624         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
625                 new BasicHeader("ETag", "\"old-etag\""),
626                 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo))
627         });
628 
629         when(mockStorage.getEntry(theURI)).thenReturn(entry);
630 
631         impl.flushInvalidatedCacheEntries(host, request, response);
632 
633         verify(mockStorage).getEntry(theURI);
634         verify(mockStorage).removeEntry(theURI);
635         verifyNoMoreInteractions(mockStorage);
636     }
637 
638     @Test
639     public void flushesEntrySpecifiedByContentLocationIfEntryHasMalformedDate()
640             throws Exception {
641         response.setHeader("ETag","\"new-etag\"");
642         response.setHeader("Date", DateUtils.formatDate(now));
643         final String theURI = "http://foo.example.com:80/bar";
644         response.setHeader("Content-Location", theURI);
645 
646         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
647                 new BasicHeader("ETag", "\"old-etag\""),
648                 new BasicHeader("Date", "foo")
649         });
650 
651         when(mockStorage.getEntry(theURI)).thenReturn(entry);
652 
653         impl.flushInvalidatedCacheEntries(host, request, response);
654 
655         verify(mockStorage).getEntry(theURI);
656         verify(mockStorage).removeEntry(theURI);
657         verifyNoMoreInteractions(mockStorage);
658     }
659 
660 
661     // Expectations
662     private void cacheEntryHasVariantMap(final Map<String,String> variantMap) {
663         when(mockEntry.getVariantMap()).thenReturn(variantMap);
664     }
665 
666     private void cacheReturnsEntryForUri(final String theUri) throws IOException {
667         when(mockStorage.getEntry(theUri)).thenReturn(mockEntry);
668     }
669 
670     private void cacheReturnsExceptionForUri(final String theUri) throws IOException {
671         when(mockStorage.getEntry(theUri)).thenThrow(
672                 new IOException("TOTAL FAIL"));
673     }
674 
675     private void cacheEntryisForMethod(final String httpMethod) {
676         when(mockEntry.getRequestMethod()).thenReturn(httpMethod);
677     }
678 }