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
30 import static org.junit.jupiter.api.Assertions.assertEquals;
31 import static org.junit.jupiter.api.Assertions.assertFalse;
32 import static org.junit.jupiter.api.Assertions.assertNotNull;
33 import static org.junit.jupiter.api.Assertions.assertNull;
34 import static org.junit.jupiter.api.Assertions.assertSame;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.verifyNoMoreInteractions;
37
38 import java.net.URI;
39 import java.time.Instant;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.stream.Collectors;
45
46 import org.apache.hc.client5.http.HeadersMatcher;
47 import org.apache.hc.client5.http.cache.HttpCacheEntry;
48 import org.apache.hc.client5.http.classic.methods.HttpGet;
49 import org.apache.hc.client5.http.utils.DateUtils;
50 import org.apache.hc.core5.http.HttpHeaders;
51 import org.apache.hc.core5.http.HttpHost;
52 import org.apache.hc.core5.http.HttpRequest;
53 import org.apache.hc.core5.http.HttpResponse;
54 import org.apache.hc.core5.http.HttpStatus;
55 import org.apache.hc.core5.http.message.BasicHeader;
56 import org.apache.hc.core5.http.message.BasicHttpRequest;
57 import org.apache.hc.core5.http.message.BasicHttpResponse;
58 import org.apache.hc.core5.net.URIBuilder;
59 import org.apache.hc.core5.util.ByteArrayBuffer;
60 import org.hamcrest.MatcherAssert;
61 import org.junit.jupiter.api.Assertions;
62 import org.junit.jupiter.api.BeforeEach;
63 import org.junit.jupiter.api.Test;
64 import org.mockito.Mockito;
65
66 class TestBasicHttpCache {
67
68 private HttpHost host;
69 private Instant now;
70 private Instant tenSecondsAgo;
71 private SimpleHttpCacheStorage backing;
72 private BasicHttpCache impl;
73
74 @BeforeEach
75 void setUp() {
76 host = new HttpHost("foo.example.com");
77 now = Instant.now();
78 tenSecondsAgo = now.minusSeconds(10);
79 backing = Mockito.spy(new SimpleHttpCacheStorage());
80 impl = new BasicHttpCache(new HeapResourceFactory(), backing);
81 }
82
83 @Test
84 void testGetCacheEntryReturnsNullOnCacheMiss() {
85 final HttpHost host = new HttpHost("foo.example.com");
86 final HttpRequest request = new HttpGet("http://foo.example.com/bar");
87 final CacheMatch result = impl.match(host, request);
88 assertNull(result);
89 }
90
91 @Test
92 void testGetCacheEntryFetchesFromCacheOnCacheHitIfNoVariants() {
93 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
94 assertFalse(entry.hasVariants());
95 final HttpHost host = new HttpHost("foo.example.com");
96 final HttpRequest request = new HttpGet("http://foo.example.com/bar");
97
98 final String key = CacheKeyGenerator.INSTANCE.generateKey(host, request);
99
100 backing.map.put(key,entry);
101
102 final CacheMatch result = impl.match(host, request);
103 assertNotNull(result);
104 assertNotNull(result.hit);
105 assertSame(entry, result.hit.entry);
106 }
107
108 @Test
109 void testGetCacheEntryReturnsNullIfNoVariantInCache() {
110 final HttpRequest origRequest = new HttpGet("http://foo.example.com/bar");
111 origRequest.setHeader("Accept-Encoding","gzip");
112
113 final ByteArrayBuffer buf = HttpTestUtils.makeRandomBuffer(128);
114 final HttpResponse origResponse = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
115 origResponse.setHeader("Date", DateUtils.formatStandardDate(now));
116 origResponse.setHeader("Cache-Control", "max-age=3600, public");
117 origResponse.setHeader("ETag", "\"etag\"");
118 origResponse.setHeader("Vary", "Accept-Encoding");
119 origResponse.setHeader("Content-Encoding","gzip");
120
121 impl.store(host, origRequest, origResponse, buf, now, now);
122
123 final HttpRequest request = new HttpGet("http://foo.example.com/bar");
124 final CacheMatch result = impl.match(host, request);
125 assertNotNull(result);
126 assertNull(result.hit);
127 }
128
129 @Test
130 void testGetCacheEntryReturnsVariantIfPresentInCache() {
131 final HttpRequest origRequest = new HttpGet("http://foo.example.com/bar");
132 origRequest.setHeader("Accept-Encoding","gzip");
133
134 final ByteArrayBuffer buf = HttpTestUtils.makeRandomBuffer(128);
135 final HttpResponse origResponse = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
136 origResponse.setHeader("Date", DateUtils.formatStandardDate(now));
137 origResponse.setHeader("Cache-Control", "max-age=3600, public");
138 origResponse.setHeader("ETag", "\"etag\"");
139 origResponse.setHeader("Vary", "Accept-Encoding");
140 origResponse.setHeader("Content-Encoding","gzip");
141
142 impl.store(host, origRequest, origResponse, buf, now, now);
143
144 final HttpRequest request = new HttpGet("http://foo.example.com/bar");
145 request.setHeader("Accept-Encoding","gzip");
146 final CacheMatch result = impl.match(host, request);
147 assertNotNull(result);
148 assertNotNull(result.hit);
149 }
150
151 @Test
152 void testGetCacheEntryReturnsVariantWithMostRecentDateHeader() {
153 final HttpRequest origRequest = new HttpGet("http://foo.example.com/bar");
154 origRequest.setHeader("Accept-Encoding", "gzip");
155
156 final ByteArrayBuffer buf = HttpTestUtils.makeRandomBuffer(128);
157
158
159 final HttpResponse origResponse1 = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
160 origResponse1.setHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(now.minusSeconds(3600)));
161 origResponse1.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=3600, public");
162 origResponse1.setHeader(HttpHeaders.ETAG, "\"etag1\"");
163 origResponse1.setHeader(HttpHeaders.VARY, "Accept-Encoding");
164
165 final HttpResponse origResponse2 = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
166 origResponse2.setHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(now));
167 origResponse2.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=3600, public");
168 origResponse2.setHeader(HttpHeaders.ETAG, "\"etag2\"");
169 origResponse2.setHeader(HttpHeaders.VARY, "Accept-Encoding");
170
171
172 impl.store(host, origRequest, origResponse1, buf, now, now);
173 impl.store(host, origRequest, origResponse2, buf, now, now);
174
175 final HttpRequest request = new HttpGet("http://foo.example.com/bar");
176 request.setHeader("Accept-Encoding", "gzip");
177 final CacheMatch result = impl.match(host, request);
178 assertNotNull(result);
179 assertNotNull(result.hit);
180 final HttpCacheEntry entry = result.hit.entry;
181 assertNotNull(entry);
182
183
184
185 final String expectedEtag = origResponse2.getFirstHeader(HttpHeaders.ETAG).getValue();
186 final String actualEtag = entry.getFirstHeader(HttpHeaders.ETAG).getValue();
187
188 assertEquals(expectedEtag, actualEtag);
189 }
190
191 @Test
192 void testGetVariantsRootNoVariants() {
193 final HttpCacheEntry root = HttpTestUtils.makeCacheEntry();
194 final List<CacheHit> variants = impl.getVariants(new CacheHit("root-key", root));
195
196 assertNotNull(variants);
197 assertEquals(0, variants.size());
198 }
199
200 @Test
201 void testGetVariantsRootNonExistentVariants() {
202 final Set<String> varinats = new HashSet<>();
203 varinats.add("variant1");
204 varinats.add("variant2");
205 final HttpCacheEntry root = HttpTestUtils.makeCacheEntry(varinats);
206 final List<CacheHit> variants = impl.getVariants(new CacheHit("root-key", root));
207
208 assertNotNull(variants);
209 assertEquals(0, variants.size());
210 }
211
212 @Test
213 void testGetVariantCacheEntriesReturnsAllVariants() throws Exception {
214 final HttpHost host = new HttpHost("foo.example.com");
215 final URI uri = new URI("http://foo.example.com/bar");
216 final HttpRequest req1 = new HttpGet(uri);
217 req1.setHeader("Accept-Encoding", "gzip");
218
219 final String rootKey = CacheKeyGenerator.INSTANCE.generateKey(uri);
220
221 final HttpResponse resp1 = HttpTestUtils.make200Response();
222 resp1.setHeader("Date", DateUtils.formatStandardDate(now));
223 resp1.setHeader("Cache-Control", "max-age=3600, public");
224 resp1.setHeader("ETag", "\"etag1\"");
225 resp1.setHeader("Vary", "Accept-Encoding");
226 resp1.setHeader("Content-Encoding","gzip");
227
228 final HttpRequest req2 = new HttpGet(uri);
229 req2.setHeader("Accept-Encoding", "identity");
230
231 final HttpResponse resp2 = HttpTestUtils.make200Response();
232 resp2.setHeader("Date", DateUtils.formatStandardDate(now));
233 resp2.setHeader("Cache-Control", "max-age=3600, public");
234 resp2.setHeader("ETag", "\"etag2\"");
235 resp2.setHeader("Vary", "Accept-Encoding");
236 resp2.setHeader("Content-Encoding","gzip");
237
238 final CacheHit hit1 = impl.store(host, req1, resp1, null, now, now);
239 final CacheHit hit2 = impl.store(host, req2, resp2, null, now, now);
240
241 final Set<String> variants = new HashSet<>();
242 variants.add("{accept-encoding=gzip}");
243 variants.add("{accept-encoding=identity}");
244
245 final Map<String, HttpCacheEntry> variantMap = impl.getVariants(new CacheHit(hit1.rootKey,
246 HttpTestUtils.makeCacheEntry(variants))).stream()
247 .collect(Collectors.toMap(CacheHit::getEntryKey, e -> e.entry));
248
249 assertNotNull(variantMap);
250 assertEquals(2, variantMap.size());
251 MatcherAssert.assertThat(variantMap.get("{accept-encoding=gzip}" + rootKey),
252 HttpCacheEntryMatcher.equivalent(hit1.entry));
253 MatcherAssert.assertThat(variantMap.get("{accept-encoding=identity}" + rootKey),
254 HttpCacheEntryMatcher.equivalent(hit2.entry));
255 }
256
257 @Test
258 void testUpdateCacheEntry() throws Exception {
259 final HttpHost host = new HttpHost("foo.example.com");
260 final URI uri = new URI("http://foo.example.com/bar");
261 final HttpRequest req1 = new HttpGet(uri);
262
263 final HttpResponse resp1 = HttpTestUtils.make200Response();
264 resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo));
265 resp1.setHeader("Cache-Control", "max-age=3600, public");
266 resp1.setHeader("ETag", "\"etag1\"");
267 resp1.setHeader("Content-Encoding","gzip");
268
269 final HttpRequest revalidate = new HttpGet(uri);
270 revalidate.setHeader("If-None-Match","\"etag1\"");
271
272 final HttpResponse resp2 = HttpTestUtils.make304Response();
273 resp2.setHeader("Date", DateUtils.formatStandardDate(now));
274 resp2.setHeader("Cache-Control", "max-age=3600, public");
275
276 final CacheHit hit1 = impl.store(host, req1, resp1, null, now, now);
277 Assertions.assertNotNull(hit1);
278 Assertions.assertEquals(1, backing.map.size());
279 Assertions.assertSame(hit1.entry, backing.map.get(hit1.getEntryKey()));
280
281 final CacheHit updated = impl.update(hit1, host, req1, resp2, now, now);
282 Assertions.assertNotNull(updated);
283 Assertions.assertEquals(1, backing.map.size());
284 Assertions.assertSame(updated.entry, backing.map.get(hit1.getEntryKey()));
285
286 MatcherAssert.assertThat(
287 updated.entry.getHeaders(),
288 HeadersMatcher.same(
289 new BasicHeader("Server", "MockOrigin/1.0"),
290 new BasicHeader("ETag", "\"etag1\""),
291 new BasicHeader("Content-Encoding","gzip"),
292 new BasicHeader("Date", DateUtils.formatStandardDate(now)),
293 new BasicHeader("Cache-Control", "max-age=3600, public")
294 ));
295 }
296
297 @Test
298 void testUpdateVariantCacheEntry() throws Exception {
299 final HttpHost host = new HttpHost("foo.example.com");
300 final URI uri = new URI("http://foo.example.com/bar");
301 final HttpRequest req1 = new HttpGet(uri);
302 req1.setHeader("User-Agent", "agent1");
303
304 final HttpResponse resp1 = HttpTestUtils.make200Response();
305 resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo));
306 resp1.setHeader("Cache-Control", "max-age=3600, public");
307 resp1.setHeader("ETag", "\"etag1\"");
308 resp1.setHeader("Content-Encoding","gzip");
309 resp1.setHeader("Vary", "User-Agent");
310
311 final HttpRequest revalidate = new HttpGet(uri);
312 revalidate.setHeader("If-None-Match","\"etag1\"");
313
314 final HttpResponse resp2 = HttpTestUtils.make304Response();
315 resp2.setHeader("Date", DateUtils.formatStandardDate(now));
316 resp2.setHeader("Cache-Control", "max-age=3600, public");
317
318 final CacheHit hit1 = impl.store(host, req1, resp1, null, now, now);
319 Assertions.assertNotNull(hit1);
320 Assertions.assertEquals(2, backing.map.size());
321 Assertions.assertSame(hit1.entry, backing.map.get(hit1.getEntryKey()));
322
323 final CacheHit updated = impl.update(hit1, host, req1, resp2, now, now);
324 Assertions.assertNotNull(updated);
325 Assertions.assertEquals(2, backing.map.size());
326 Assertions.assertSame(updated.entry, backing.map.get(hit1.getEntryKey()));
327
328 MatcherAssert.assertThat(
329 updated.entry.getHeaders(),
330 HeadersMatcher.same(
331 new BasicHeader("Server", "MockOrigin/1.0"),
332 new BasicHeader("ETag", "\"etag1\""),
333 new BasicHeader("Content-Encoding","gzip"),
334 new BasicHeader("Vary","User-Agent"),
335 new BasicHeader("Date", DateUtils.formatStandardDate(now)),
336 new BasicHeader("Cache-Control", "max-age=3600, public")
337 ));
338 }
339
340 @Test
341 void testUpdateCacheEntryTurnsVariant() throws Exception {
342 final HttpHost host = new HttpHost("foo.example.com");
343 final URI uri = new URI("http://foo.example.com/bar");
344 final HttpRequest req1 = new HttpGet(uri);
345 req1.setHeader("User-Agent", "agent1");
346
347 final HttpResponse resp1 = HttpTestUtils.make200Response();
348 resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo));
349 resp1.setHeader("Cache-Control", "max-age=3600, public");
350 resp1.setHeader("ETag", "\"etag1\"");
351 resp1.setHeader("Content-Encoding","gzip");
352
353 final HttpRequest revalidate = new HttpGet(uri);
354 revalidate.setHeader("If-None-Match","\"etag1\"");
355
356 final HttpResponse resp2 = HttpTestUtils.make304Response();
357 resp2.setHeader("Date", DateUtils.formatStandardDate(now));
358 resp2.setHeader("Cache-Control", "max-age=3600, public");
359 resp2.setHeader("Vary", "User-Agent");
360
361 final CacheHit hit1 = impl.store(host, req1, resp1, null, now, now);
362 Assertions.assertNotNull(hit1);
363 Assertions.assertEquals(1, backing.map.size());
364 Assertions.assertSame(hit1.entry, backing.map.get(hit1.getEntryKey()));
365
366 final CacheHit updated = impl.update(hit1, host, req1, resp2, now, now);
367 Assertions.assertNotNull(updated);
368 Assertions.assertEquals(2, backing.map.size());
369
370 MatcherAssert.assertThat(
371 updated.entry.getHeaders(),
372 HeadersMatcher.same(
373 new BasicHeader("Server", "MockOrigin/1.0"),
374 new BasicHeader("ETag", "\"etag1\""),
375 new BasicHeader("Content-Encoding","gzip"),
376 new BasicHeader("Date", DateUtils.formatStandardDate(now)),
377 new BasicHeader("Cache-Control", "max-age=3600, public"),
378 new BasicHeader("Vary","User-Agent")));
379 }
380
381 @Test
382 void testStoreFromNegotiatedVariant() throws Exception {
383 final HttpHost host = new HttpHost("foo.example.com");
384 final URI uri = new URI("http://foo.example.com/bar");
385 final HttpRequest req1 = new HttpGet(uri);
386 req1.setHeader("User-Agent", "agent1");
387
388 final HttpResponse resp1 = HttpTestUtils.make200Response();
389 resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo));
390 resp1.setHeader("Cache-Control", "max-age=3600, public");
391 resp1.setHeader("ETag", "\"etag1\"");
392 resp1.setHeader("Content-Encoding","gzip");
393 resp1.setHeader("Vary", "User-Agent");
394
395 final CacheHit hit1 = impl.store(host, req1, resp1, null, now, now);
396 Assertions.assertNotNull(hit1);
397 Assertions.assertEquals(2, backing.map.size());
398 Assertions.assertSame(hit1.entry, backing.map.get(hit1.getEntryKey()));
399
400 final HttpRequest req2 = new HttpGet(uri);
401 req2.setHeader("User-Agent", "agent2");
402
403 final HttpResponse resp2 = HttpTestUtils.make304Response();
404 resp2.setHeader("Date", DateUtils.formatStandardDate(now));
405 resp2.setHeader("Cache-Control", "max-age=3600, public");
406
407 final CacheHit hit2 = impl.storeFromNegotiated(hit1, host, req2, resp2, now, now);
408 Assertions.assertNotNull(hit2);
409 Assertions.assertEquals(3, backing.map.size());
410
411 MatcherAssert.assertThat(
412 hit2.entry.getHeaders(),
413 HeadersMatcher.same(
414 new BasicHeader("Server", "MockOrigin/1.0"),
415 new BasicHeader("ETag", "\"etag1\""),
416 new BasicHeader("Content-Encoding","gzip"),
417 new BasicHeader("Vary","User-Agent"),
418 new BasicHeader("Date", DateUtils.formatStandardDate(now)),
419 new BasicHeader("Cache-Control", "max-age=3600, public")));
420 }
421
422 @Test
423 void testInvalidatesUnsafeRequests() throws Exception {
424 final HttpRequest request = new BasicHttpRequest("POST","/path");
425 final String key = CacheKeyGenerator.INSTANCE.generateKey(host, request);
426
427 final HttpResponse response = HttpTestUtils.make200Response();
428
429 backing.putEntry(key, HttpTestUtils.makeCacheEntry());
430
431 impl.evictInvalidatedEntries(host, request, response);
432
433 verify(backing).getEntry(key);
434 verify(backing).removeEntry(key);
435
436 Assertions.assertNull(backing.getEntry(key));
437 }
438
439 @Test
440 void testDoesNotInvalidateSafeRequests() {
441 final HttpRequest request1 = new BasicHttpRequest("GET","/");
442 final HttpResponse response1 = HttpTestUtils.make200Response();
443
444 impl.evictInvalidatedEntries(host, request1, response1);
445
446 verifyNoMoreInteractions(backing);
447
448 final HttpRequest request2 = new BasicHttpRequest("HEAD","/");
449 final HttpResponse response2 = HttpTestUtils.make200Response();
450 impl.evictInvalidatedEntries(host, request2, response2);
451
452 verifyNoMoreInteractions(backing);
453 }
454
455 @Test
456 void testInvalidatesUnsafeRequestsWithVariants() throws Exception {
457 final HttpRequest request = new BasicHttpRequest("POST","/path");
458 final String rootKey = CacheKeyGenerator.INSTANCE.generateKey(host, request);
459 final Set<String> variants = new HashSet<>();
460 variants.add("{var1}");
461 variants.add("{var2}");
462 final String variantKey1 = "{var1}" + rootKey;
463 final String variantKey2 = "{var2}" + rootKey;
464
465 final HttpResponse response = HttpTestUtils.make200Response();
466
467 backing.putEntry(rootKey, HttpTestUtils.makeCacheEntry(variants));
468 backing.putEntry(variantKey1, HttpTestUtils.makeCacheEntry());
469 backing.putEntry(variantKey2, HttpTestUtils.makeCacheEntry());
470
471 impl.evictInvalidatedEntries(host, request, response);
472
473 verify(backing).getEntry(rootKey);
474 verify(backing).removeEntry(rootKey);
475 verify(backing).removeEntry(variantKey1);
476 verify(backing).removeEntry(variantKey2);
477
478 Assertions.assertNull(backing.getEntry(rootKey));
479 Assertions.assertNull(backing.getEntry(variantKey1));
480 Assertions.assertNull(backing.getEntry(variantKey2));
481 }
482
483 @Test
484 void testInvalidateUriSpecifiedByContentLocationAndFresher() throws Exception {
485 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
486 final String rootKey = CacheKeyGenerator.INSTANCE.generateKey(host, request);
487 final URI contentUri = new URIBuilder()
488 .setHttpHost(host)
489 .setPath("/bar")
490 .build();
491 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
492
493 final HttpResponse response = HttpTestUtils.make200Response();
494 response.setHeader("ETag","\"new-etag\"");
495 response.setHeader("Date", DateUtils.formatStandardDate(now));
496 response.setHeader("Content-Location", contentUri.toASCIIString());
497
498 backing.putEntry(rootKey, HttpTestUtils.makeCacheEntry());
499 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
500 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
501 new BasicHeader("ETag", "\"old-etag\"")
502 ));
503
504 impl.evictInvalidatedEntries(host, request, response);
505
506 verify(backing).getEntry(rootKey);
507 verify(backing).removeEntry(rootKey);
508 verify(backing).getEntry(contentKey);
509 verify(backing).removeEntry(contentKey);
510 }
511
512 @Test
513 void testInvalidateUriSpecifiedByLocationAndFresher() throws Exception {
514 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
515 final String rootKey = CacheKeyGenerator.INSTANCE.generateKey(host, request);
516 final URI contentUri = new URIBuilder()
517 .setHttpHost(host)
518 .setPath("/bar")
519 .build();
520 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
521
522 final HttpResponse response = HttpTestUtils.make200Response();
523 response.setHeader("ETag","\"new-etag\"");
524 response.setHeader("Date", DateUtils.formatStandardDate(now));
525 response.setHeader("Location", contentUri.toASCIIString());
526
527 backing.putEntry(rootKey, HttpTestUtils.makeCacheEntry());
528 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
529 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
530 new BasicHeader("ETag", "\"old-etag\"")
531 ));
532
533 impl.evictInvalidatedEntries(host, request, response);
534
535 verify(backing).getEntry(rootKey);
536 verify(backing).removeEntry(rootKey);
537 verify(backing).getEntry(contentKey);
538 verify(backing).removeEntry(contentKey);
539 }
540
541 @Test
542 void testDoesNotInvalidateForUnsuccessfulResponse() throws Exception {
543 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
544 final URI contentUri = new URIBuilder()
545 .setHttpHost(host)
546 .setPath("/bar")
547 .build();
548 final HttpResponse response = HttpTestUtils.make500Response();
549 response.setHeader("ETag","\"new-etag\"");
550 response.setHeader("Date", DateUtils.formatStandardDate(now));
551 response.setHeader("Content-Location", contentUri.toASCIIString());
552
553 impl.evictInvalidatedEntries(host, request, response);
554
555 verifyNoMoreInteractions(backing);
556 }
557
558 @Test
559 void testInvalidateUriSpecifiedByContentLocationNonCanonical() throws Exception {
560 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
561 final String rootKey = CacheKeyGenerator.INSTANCE.generateKey(host, request);
562 final URI contentUri = new URIBuilder()
563 .setHttpHost(host)
564 .setPath("/bar")
565 .build();
566 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
567
568 final HttpResponse response = HttpTestUtils.make200Response();
569 response.setHeader("ETag","\"new-etag\"");
570 response.setHeader("Date", DateUtils.formatStandardDate(now));
571
572 response.setHeader("Content-Location", contentUri.toASCIIString());
573
574 backing.putEntry(rootKey, HttpTestUtils.makeCacheEntry());
575
576 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
577 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
578 new BasicHeader("ETag", "\"old-etag\"")));
579
580 impl.evictInvalidatedEntries(host, request, response);
581
582 verify(backing).getEntry(rootKey);
583 verify(backing).removeEntry(rootKey);
584 verify(backing).getEntry(contentKey);
585 verify(backing).removeEntry(contentKey);
586 Assertions.assertNull(backing.getEntry(rootKey));
587 Assertions.assertNull(backing.getEntry(contentKey));
588 }
589
590 @Test
591 void testInvalidateUriSpecifiedByContentLocationRelative() throws Exception {
592 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
593 final String rootKey = CacheKeyGenerator.INSTANCE.generateKey(host, request);
594 final URI contentUri = new URIBuilder()
595 .setHttpHost(host)
596 .setPath("/bar")
597 .build();
598 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
599
600 final HttpResponse response = HttpTestUtils.make200Response();
601 response.setHeader("ETag","\"new-etag\"");
602 response.setHeader("Date", DateUtils.formatStandardDate(now));
603
604 response.setHeader("Content-Location", "/bar");
605
606 backing.putEntry(rootKey, HttpTestUtils.makeCacheEntry());
607
608 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
609 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
610 new BasicHeader("ETag", "\"old-etag\"")));
611
612 impl.evictInvalidatedEntries(host, request, response);
613
614 verify(backing).getEntry(rootKey);
615 verify(backing).removeEntry(rootKey);
616 verify(backing).getEntry(contentKey);
617 verify(backing).removeEntry(contentKey);
618 Assertions.assertNull(backing.getEntry(rootKey));
619 Assertions.assertNull(backing.getEntry(contentKey));
620 }
621
622 @Test
623 void testDoesNotInvalidateUriSpecifiedByContentLocationOtherOrigin() throws Exception {
624 final HttpRequest request = new BasicHttpRequest("PUT", "/");
625 final URI contentUri = new URIBuilder()
626 .setHost("bar.example.com")
627 .setPath("/")
628 .build();
629 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
630
631 final HttpResponse response = HttpTestUtils.make200Response();
632 response.setHeader("ETag","\"new-etag\"");
633 response.setHeader("Date", DateUtils.formatStandardDate(now));
634 response.setHeader("Content-Location", contentUri.toASCIIString());
635
636 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry());
637
638 impl.evictInvalidatedEntries(host, request, response);
639
640 verify(backing, Mockito.never()).getEntry(contentKey);
641 verify(backing, Mockito.never()).removeEntry(contentKey);
642 }
643
644 @Test
645 void testDoesNotInvalidateUriSpecifiedByContentLocationIfEtagsMatch() throws Exception {
646 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
647 final URI contentUri = new URIBuilder()
648 .setHttpHost(host)
649 .setPath("/bar")
650 .build();
651 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
652
653 final HttpResponse response = HttpTestUtils.make200Response();
654 response.setHeader("ETag","\"same-etag\"");
655 response.setHeader("Date", DateUtils.formatStandardDate(now));
656 response.setHeader("Content-Location", contentUri.toASCIIString());
657
658 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
659 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
660 new BasicHeader("ETag", "\"same-etag\"")));
661
662 impl.evictInvalidatedEntries(host, request, response);
663
664 verify(backing).getEntry(contentKey);
665 verify(backing, Mockito.never()).removeEntry(contentKey);
666 }
667
668 @Test
669 void testDoesNotInvalidateUriSpecifiedByContentLocationIfOlder() throws Exception {
670 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
671 final URI contentUri = new URIBuilder()
672 .setHttpHost(host)
673 .setPath("/bar")
674 .build();
675 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
676
677 final HttpResponse response = HttpTestUtils.make200Response();
678 response.setHeader("ETag","\"new-etag\"");
679 response.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo));
680 response.setHeader("Content-Location", contentUri.toASCIIString());
681
682 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
683 new BasicHeader("Date", DateUtils.formatStandardDate(now)),
684 new BasicHeader("ETag", "\"old-etag\"")));
685
686 impl.evictInvalidatedEntries(host, request, response);
687
688 verify(backing).getEntry(contentKey);
689 verify(backing, Mockito.never()).removeEntry(contentKey);
690 }
691
692 @Test
693 void testDoesNotInvalidateUriSpecifiedByContentLocationIfResponseHasNoEtag() throws Exception {
694 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
695 final URI contentUri = new URIBuilder()
696 .setHttpHost(host)
697 .setPath("/bar")
698 .build();
699 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
700
701 final HttpResponse response = HttpTestUtils.make200Response();
702 response.removeHeaders("ETag");
703 response.setHeader("Date", DateUtils.formatStandardDate(now));
704 response.setHeader("Content-Location", contentUri.toASCIIString());
705
706 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
707 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
708 new BasicHeader("ETag", "\"old-etag\"")));
709
710 impl.evictInvalidatedEntries(host, request, response);
711
712 verify(backing).getEntry(contentKey);
713 verify(backing, Mockito.never()).removeEntry(contentKey);
714 }
715
716 @Test
717 void testDoesNotInvalidateUriSpecifiedByContentLocationIfEntryHasNoEtag() throws Exception {
718 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
719 final URI contentUri = new URIBuilder()
720 .setHttpHost(host)
721 .setPath("/bar")
722 .build();
723 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
724
725 final HttpResponse response = HttpTestUtils.make200Response();
726 response.setHeader("ETag", "\"some-etag\"");
727 response.setHeader("Date", DateUtils.formatStandardDate(now));
728 response.setHeader("Content-Location", contentUri.toASCIIString());
729
730 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
731 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo))));
732
733 impl.evictInvalidatedEntries(host, request, response);
734
735 verify(backing).getEntry(contentKey);
736 verify(backing, Mockito.never()).removeEntry(contentKey);
737 }
738
739 @Test
740 void testInvalidatesUriSpecifiedByContentLocationIfResponseHasNoDate() throws Exception {
741 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
742 final URI contentUri = new URIBuilder()
743 .setHttpHost(host)
744 .setPath("/bar")
745 .build();
746 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
747
748 final HttpResponse response = HttpTestUtils.make200Response();
749 response.setHeader("ETag", "\"new-etag\"");
750 response.removeHeaders("Date");
751 response.setHeader("Content-Location", contentUri.toASCIIString());
752
753 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
754 new BasicHeader("ETag", "\"old-etag\""),
755 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo))));
756
757 impl.evictInvalidatedEntries(host, request, response);
758
759 verify(backing).getEntry(contentKey);
760 verify(backing).removeEntry(contentKey);
761 }
762
763 @Test
764 void testInvalidatesUriSpecifiedByContentLocationIfEntryHasNoDate() throws Exception {
765 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
766 final URI contentUri = new URIBuilder()
767 .setHttpHost(host)
768 .setPath("/bar")
769 .build();
770 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
771
772 final HttpResponse response = HttpTestUtils.make200Response();
773 response.setHeader("ETag","\"new-etag\"");
774 response.setHeader("Date", DateUtils.formatStandardDate(now));
775 response.setHeader("Content-Location", contentUri.toASCIIString());
776
777 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
778 new BasicHeader("ETag", "\"old-etag\"")));
779
780 impl.evictInvalidatedEntries(host, request, response);
781
782 verify(backing).getEntry(contentKey);
783 verify(backing).removeEntry(contentKey);
784 }
785
786 @Test
787 void testInvalidatesUriSpecifiedByContentLocationIfResponseHasMalformedDate() throws Exception {
788 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
789 final URI contentUri = new URIBuilder()
790 .setHttpHost(host)
791 .setPath("/bar")
792 .build();
793 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
794
795 final HttpResponse response = HttpTestUtils.make200Response();
796 response.setHeader("ETag","\"new-etag\"");
797 response.setHeader("Date", "huh?");
798 response.setHeader("Content-Location", contentUri.toASCIIString());
799
800 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
801 new BasicHeader("ETag", "\"old-etag\""),
802 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo))));
803
804 impl.evictInvalidatedEntries(host, request, response);
805
806 verify(backing).getEntry(contentKey);
807 verify(backing).removeEntry(contentKey);
808 }
809
810 @Test
811 void testInvalidatesUriSpecifiedByContentLocationIfEntryHasMalformedDate() throws Exception {
812 final HttpRequest request = new BasicHttpRequest("PUT", "/foo");
813 final URI contentUri = new URIBuilder()
814 .setHttpHost(host)
815 .setPath("/bar")
816 .build();
817 final String contentKey = CacheKeyGenerator.INSTANCE.generateKey(contentUri);
818
819 final HttpResponse response = HttpTestUtils.make200Response();
820 response.setHeader("ETag","\"new-etag\"");
821 response.setHeader("Date", DateUtils.formatStandardDate(now));
822 response.setHeader("Content-Location", contentUri.toASCIIString());
823
824 backing.putEntry(contentKey, HttpTestUtils.makeCacheEntry(
825 new BasicHeader("ETag", "\"old-etag\""),
826 new BasicHeader("Date", "huh?")));
827
828 impl.evictInvalidatedEntries(host, request, response);
829
830 verify(backing).getEntry(contentKey);
831 verify(backing).removeEntry(contentKey);
832 }
833
834 }