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