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