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.junit.jupiter.api.Assertions.assertFalse;
30  import static org.junit.jupiter.api.Assertions.assertTrue;
31  
32  import java.time.Duration;
33  import java.time.Instant;
34  import java.time.temporal.ChronoUnit;
35  import java.util.Random;
36  
37  import org.apache.hc.client5.http.auth.StandardAuthScheme;
38  import org.apache.hc.client5.http.classic.methods.HttpOptions;
39  import org.apache.hc.client5.http.utils.DateUtils;
40  import org.apache.hc.core5.http.HttpHeaders;
41  import org.apache.hc.core5.http.HttpRequest;
42  import org.apache.hc.core5.http.HttpResponse;
43  import org.apache.hc.core5.http.HttpStatus;
44  import org.apache.hc.core5.http.HttpVersion;
45  import org.apache.hc.core5.http.Method;
46  import org.apache.hc.core5.http.message.BasicHttpRequest;
47  import org.apache.hc.core5.http.message.BasicHttpResponse;
48  import org.junit.jupiter.api.Assertions;
49  import org.junit.jupiter.api.BeforeEach;
50  import org.junit.jupiter.api.Test;
51  
52  public class TestResponseCachingPolicy {
53  
54      private ResponseCachingPolicy policy;
55      private HttpResponse response;
56      private HttpRequest request;
57      private final int[] acceptableCodes = new int[] { HttpStatus.SC_OK,
58              HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, HttpStatus.SC_MULTIPLE_CHOICES,
59              HttpStatus.SC_MOVED_PERMANENTLY, HttpStatus.SC_GONE };
60      private Instant now;
61      private Instant tenSecondsFromNow;
62      private Instant sixSecondsAgo;
63      private ResponseCacheControl responseCacheControl;
64  
65      @BeforeEach
66      public void setUp() throws Exception {
67          now = Instant.now();
68          sixSecondsAgo = now.minusSeconds(6);
69          tenSecondsFromNow = now.plusSeconds(10);
70  
71          policy = new ResponseCachingPolicy(true, false, false);
72          request = new BasicHttpRequest("GET","/");
73          response = new BasicHttpResponse(HttpStatus.SC_OK, "");
74          response.setHeader("Date", DateUtils.formatStandardDate(Instant.now()));
75          response.setHeader("Content-Length", "0");
76          responseCacheControl = ResponseCacheControl.builder().build();
77      }
78  
79      @Test
80      public void testGetCacheable() {
81          policy = new ResponseCachingPolicy(true, false, false);
82          request = new BasicHttpRequest(Method.GET, "/");
83          Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
84      }
85  
86      @Test
87      public void testHeadCacheable() {
88          policy = new ResponseCachingPolicy(true, false, false);
89          request = new BasicHttpRequest(Method.HEAD, "/");
90          Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
91      }
92  
93      @Test
94      public void testArbitraryMethodNotCacheable() {
95          request = new BasicHttpRequest("PUT", "/");
96          Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
97  
98          request = new BasicHttpRequest("huh", "/");
99          Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
100     }
101 
102     @Test
103     public void testResponsesToRequestsWithAuthorizationHeadersAreNotCacheableBySharedCache() {
104         request = new BasicHttpRequest("GET","/");
105         request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q=");
106         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
107     }
108 
109     @Test
110     public void testResponsesToRequestsWithAuthorizationHeadersAreCacheableByNonSharedCache() {
111         policy = new ResponseCachingPolicy(false, false, false);
112         request = new BasicHttpRequest("GET","/");
113         request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q=");
114         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
115     }
116 
117     @Test
118     public void testAuthorizedResponsesWithSMaxAgeAreCacheable() {
119         request = new BasicHttpRequest("GET","/");
120         request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q=");
121         responseCacheControl = ResponseCacheControl.builder()
122                 .setSharedMaxAge(3600)
123                 .build();
124 
125         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
126     }
127 
128     @Test
129     public void testAuthorizedResponsesWithCacheControlPublicAreCacheable() {
130         request = new BasicHttpRequest("GET","/");
131         request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q=");
132         responseCacheControl = ResponseCacheControl.builder()
133                 .setCachePublic(true)
134                 .build();
135         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
136     }
137 
138     @Test
139     public void testAuthorizedResponsesWithCacheControlMaxAgeAreNotCacheable() {
140         request = new BasicHttpRequest("GET","/");
141         request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q=");
142         responseCacheControl = ResponseCacheControl.builder()
143                 .setMaxAge(3600)
144                 .build();
145         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
146     }
147 
148     @Test
149     public void test203ResponseCodeIsCacheable() {
150         response.setCode(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION);
151         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
152     }
153 
154     @Test
155     public void test206ResponseCodeIsNotCacheable() {
156         response.setCode(HttpStatus.SC_PARTIAL_CONTENT);
157         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
158     }
159 
160     @Test
161     public void test300ResponseCodeIsCacheable() {
162         response.setCode(HttpStatus.SC_MULTIPLE_CHOICES);
163         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
164     }
165 
166     @Test
167     public void test301ResponseCodeIsCacheable() {
168         response.setCode(HttpStatus.SC_MOVED_PERMANENTLY);
169         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
170     }
171 
172     @Test
173     public void test410ResponseCodeIsCacheable() {
174         response.setCode(HttpStatus.SC_GONE);
175         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
176     }
177 
178     @Test
179     public void testPlain302ResponseCodeIsNotCacheable() {
180         response.setCode(HttpStatus.SC_MOVED_TEMPORARILY);
181         response.removeHeaders("Expires");
182         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
183     }
184 
185     @Test
186     public void testPlain303ResponseCodeIsNotCacheableUnderDefaultBehavior() {
187         response.setCode(HttpStatus.SC_SEE_OTHER);
188         response.removeHeaders("Expires");
189         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
190     }
191 
192     @Test
193     public void testPlain303ResponseCodeIsNotCacheableEvenIf303CachingEnabled() {
194         policy = new ResponseCachingPolicy(true, false, true);
195         response.setCode(HttpStatus.SC_SEE_OTHER);
196         response.removeHeaders("Expires");
197         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
198     }
199 
200 
201     @Test
202     public void testPlain307ResponseCodeIsNotCacheable() {
203         response.setCode(HttpStatus.SC_TEMPORARY_REDIRECT);
204         response.removeHeaders("Expires");
205         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
206     }
207 
208     @Test
209     public void testNon206WithExplicitExpiresIsCacheable() {
210         final int status = getRandomStatus();
211         response.setCode(status);
212         response.setHeader("Expires", DateUtils.formatStandardDate(Instant.now().plus(1, ChronoUnit.HOURS)));
213         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
214     }
215 
216     @Test
217     public void testNon206WithMaxAgeIsCacheable() {
218         final int status = getRandomStatus();
219         response.setCode(status);
220         responseCacheControl = ResponseCacheControl.builder()
221                 .setMaxAge(0)
222                 .build();
223         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
224     }
225 
226     @Test
227     public void testMissingCacheControlHeader() {
228         final int status = getRandomStatus();
229         response.setCode(status);
230         response.removeHeaders(HttpHeaders.CACHE_CONTROL);
231         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
232     }
233 
234     @Test
235     public void testNon206WithSMaxAgeIsCacheable() {
236         final int status = getRandomStatus();
237         response.setCode(status);
238         responseCacheControl = ResponseCacheControl.builder()
239                 .setSharedMaxAge(1)
240                 .build();
241         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
242     }
243 
244     @Test
245     public void testNon206WithMustRevalidateIsCacheable() {
246         final int status = getRandomStatus();
247         response.setCode(status);
248         responseCacheControl = ResponseCacheControl.builder()
249                 .setMustRevalidate(true)
250                 .build();
251         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
252     }
253 
254     @Test
255     public void testNon206WithProxyRevalidateIsCacheable() {
256         final int status = getRandomStatus();
257         response.setCode(status);
258         responseCacheControl = ResponseCacheControl.builder()
259                 .setProxyRevalidate(true)
260                 .build();
261         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
262     }
263 
264     @Test
265     public void testNon206WithPublicCacheControlIsCacheable() {
266         final int status = getRandomStatus();
267         response.setCode(status);
268         responseCacheControl = ResponseCacheControl.builder()
269                 .setCachePublic(true)
270                 .build();
271         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
272     }
273 
274     @Test
275     public void testNon206WithPrivateCacheControlIsNotCacheableBySharedCache() {
276         final int status = getRandomStatus();
277         response.setCode(status);
278         responseCacheControl = ResponseCacheControl.builder()
279                 .setCachePrivate(true)
280                 .build();
281         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
282     }
283 
284     @Test
285     public void test200ResponseWithPrivateCacheControlIsCacheableByNonSharedCache() {
286         policy = new ResponseCachingPolicy(false, false, false);
287         response.setCode(HttpStatus.SC_OK);
288         responseCacheControl = ResponseCacheControl.builder()
289                 .setCachePrivate(true)
290                 .build();
291         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
292     }
293 
294     @Test
295     public void testControlNoCacheCacheable() {
296         responseCacheControl = ResponseCacheControl.builder()
297                 .setNoCache(true)
298                 .build();
299 
300         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
301     }
302 
303     @Test
304     public void testControlNoStoreNotCacheable() {
305         responseCacheControl = ResponseCacheControl.builder()
306                 .setNoStore(true)
307                 .build();
308 
309         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
310     }
311 
312     @Test
313     public void testControlNoStoreEmbeddedInListCacheable() {
314         responseCacheControl = ResponseCacheControl.builder()
315                 .setCachePublic(true)
316                 .setNoStore(true)
317                 .build();
318 
319         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
320     }
321 
322     @Test
323     public void testControlNoCacheEmbeddedInListCacheable() {
324         responseCacheControl = ResponseCacheControl.builder()
325                 .setCachePublic(true)
326                 .setNoCache(true)
327                 .build();
328 
329         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
330     }
331 
332     @Test
333     public void testControlNoCacheEmbeddedInListAfterFirstHeaderCacheable() {
334         responseCacheControl = ResponseCacheControl.builder()
335                 .setMaxAge(20)
336                 .setCachePublic(true)
337                 .setNoCache(true)
338                 .build();
339 
340         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
341     }
342 
343     @Test
344     public void testControlNoStoreEmbeddedInListAfterFirstHeaderCacheable() {
345         responseCacheControl = ResponseCacheControl.builder()
346                 .setMaxAge(20)
347                 .setCachePublic(true)
348                 .setNoStore(true)
349                 .build();
350 
351         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
352     }
353 
354     @Test
355     public void testControlAnyCacheControlCacheable() {
356         responseCacheControl = ResponseCacheControl.builder()
357                 .setMaxAge(10)
358                 .build();
359 
360         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
361 
362         response = new BasicHttpResponse(HttpStatus.SC_OK, "");
363         response.setHeader("Date", DateUtils.formatStandardDate(Instant.now()));
364         response.setHeader("Content-Length", "0");
365         responseCacheControl = ResponseCacheControl.builder()
366                 .build();
367 
368         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
369     }
370 
371     @Test
372     public void testControlWithout200Cacheable() {
373         HttpResponse response404 = new BasicHttpResponse(HttpStatus.SC_NOT_FOUND, "");
374 
375         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response404));
376 
377         response404 = new BasicHttpResponse(HttpStatus.SC_GATEWAY_TIMEOUT, "");
378 
379         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response404));
380     }
381 
382     @Test
383     public void testVaryStarIsNotCacheable() {
384         response.setHeader("Vary", "*");
385         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
386     }
387 
388     @Test
389     public void testVaryStarIsNotCacheableUsingSharedPublicCache() {
390         policy = new ResponseCachingPolicy(true, false, false);
391 
392         request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
393         response.setHeader("Vary", "*");
394         responseCacheControl = ResponseCacheControl.builder()
395                 .setCachePublic(true)
396                 .build();
397         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
398     }
399 
400     @Test
401     public void testRequestWithVaryHeaderCacheable() {
402         response.addHeader("Vary", "Accept-Encoding");
403         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
404     }
405 
406     @Test
407     public void testIsArbitraryMethodCacheableUsingSharedPublicCache() {
408         policy = new ResponseCachingPolicy(true, false, false);
409 
410         request = new HttpOptions("http://foo.example.com/");
411         request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
412         response.setCode(HttpStatus.SC_NO_CONTENT);
413         responseCacheControl = ResponseCacheControl.builder()
414                 .setCachePublic(true)
415                 .build();
416 
417         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
418     }
419 
420     @Test
421     public void testResponsesWithMultipleAgeHeadersAreCacheable() {
422         response.addHeader("Age", "3");
423         response.addHeader("Age", "5");
424         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
425     }
426 
427     @Test
428     public void testResponsesWithMultipleAgeHeadersAreNotCacheableUsingSharedPublicCache() {
429         policy = new ResponseCachingPolicy(true, false, false);
430 
431         request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
432         response.addHeader("Age", "3");
433         response.addHeader("Age", "5");
434         responseCacheControl = ResponseCacheControl.builder()
435                 .setCachePublic(true)
436                 .build();
437         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
438     }
439 
440     @Test
441     public void testResponsesWithMultipleDateHeadersAreNotCacheable() {
442         response.addHeader("Date", DateUtils.formatStandardDate(now));
443         response.addHeader("Date", DateUtils.formatStandardDate(sixSecondsAgo));
444         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
445     }
446 
447     @Test
448     public void testResponsesWithMultipleDateHeadersAreNotCacheableUsingSharedPublicCache() {
449         policy = new ResponseCachingPolicy(true, false, false);
450 
451         request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
452         response.addHeader("Date", DateUtils.formatStandardDate(now));
453         response.addHeader("Date", DateUtils.formatStandardDate(sixSecondsAgo));
454         responseCacheControl = ResponseCacheControl.builder()
455                 .setCachePublic(true)
456                 .build();
457         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
458     }
459 
460     @Test
461     public void testResponsesWithMalformedDateHeadersAreNotCacheable() {
462         response.addHeader("Date", "garbage");
463         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
464     }
465 
466     @Test
467     public void testResponsesWithMalformedDateHeadersAreNotCacheableUsingSharedPublicCache() {
468         policy = new ResponseCachingPolicy(true, false, false);
469 
470         request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
471         response.addHeader("Date", "garbage");
472         responseCacheControl = ResponseCacheControl.builder()
473                 .setCachePublic(true)
474                 .build();
475         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
476     }
477 
478     @Test
479     public void testResponsesWithMultipleExpiresHeadersAreNotCacheable() {
480         response.addHeader("Expires", DateUtils.formatStandardDate(now));
481         response.addHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo));
482         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
483     }
484 
485     @Test
486     public void testResponsesWithMultipleExpiresHeadersAreNotCacheableUsingSharedPublicCache() {
487         policy = new ResponseCachingPolicy(true, false, false);
488 
489         request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
490         response.addHeader("Expires", DateUtils.formatStandardDate(now));
491         response.addHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo));
492         responseCacheControl = ResponseCacheControl.builder()
493                 .setCachePublic(true)
494                 .build();
495         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
496     }
497 
498     @Test
499     public void testResponsesThatAreSmallEnoughAreCacheable() {
500         response.setHeader("Content-Length", "0");
501         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
502     }
503 
504     @Test
505     public void testResponsesToGETWithQueryParamsButNoExplicitCachingAreNotCacheable() {
506         request = new BasicHttpRequest("GET", "/foo?s=bar");
507         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
508     }
509 
510     @Test
511     public void testResponsesToHEADWithQueryParamsButNoExplicitCachingAreNotCacheable() {
512         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
513         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
514     }
515 
516     @Test
517     public void testResponsesToGETWithQueryParamsButNoExplicitCachingAreNotCacheableEvenWhen1_0QueryCachingDisabled() {
518         policy = new ResponseCachingPolicy(true, true, false);
519         request = new BasicHttpRequest("GET", "/foo?s=bar");
520         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
521     }
522 
523     @Test
524     public void testResponsesToHEADWithQueryParamsButNoExplicitCachingAreNotCacheableEvenWhen1_0QueryCachingDisabled() {
525         policy = new ResponseCachingPolicy(true, true, false);
526         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
527         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
528     }
529 
530     @Test
531     public void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheable() {
532         request = new BasicHttpRequest("GET", "/foo?s=bar");
533         response.setHeader("Date", DateUtils.formatStandardDate(now));
534         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
535         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
536     }
537 
538     @Test
539     public void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheable() {
540         policy = new ResponseCachingPolicy(true, false, false);
541         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
542         response.setHeader("Date", DateUtils.formatStandardDate(now));
543         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
544         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
545     }
546 
547     @Test
548     public void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0QueryCachingDisabled() {
549         policy = new ResponseCachingPolicy(true, true, false);
550         request = new BasicHttpRequest("GET", "/foo?s=bar");
551         response.setHeader("Date", DateUtils.formatStandardDate(now));
552         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
553         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
554     }
555 
556     @Test
557     public void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0QueryCachingDisabled() {
558         policy = new ResponseCachingPolicy(true, true, false);
559         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
560         response.setHeader("Date", DateUtils.formatStandardDate(now));
561         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
562         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
563     }
564 
565     @Test
566     public void getsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheable() {
567         request = new BasicHttpRequest("GET", "/foo?s=bar");
568         response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
569         response.setVersion(HttpVersion.HTTP_1_0);
570         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
571     }
572 
573     @Test
574     public void headsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheable() {
575         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
576         response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
577         response.setVersion(HttpVersion.HTTP_1_0);
578         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
579     }
580 
581     @Test
582     public void getsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheableEvenWithSetting() {
583         policy = new ResponseCachingPolicy(true, true, false);
584         request = new BasicHttpRequest("GET", "/foo?s=bar");
585         response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
586         response.setVersion(HttpVersion.HTTP_1_0);
587         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
588     }
589 
590     @Test
591     public void headsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheableEvenWithSetting() {
592         policy = new ResponseCachingPolicy(true, true, false);
593         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
594         response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
595         response.setVersion(HttpVersion.HTTP_1_0);
596         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
597     }
598 
599     @Test
600     public void getsWithQueryParametersDirectlyFrom1_0OriginsAreCacheableWithExpires() {
601         request = new BasicHttpRequest("GET", "/foo?s=bar");
602         response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
603         response.setVersion(HttpVersion.HTTP_1_0);
604         response.setHeader("Date", DateUtils.formatStandardDate(now));
605         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
606         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
607     }
608 
609     @Test
610     public void headsWithQueryParametersDirectlyFrom1_0OriginsAreCacheableWithExpires() {
611         policy = new ResponseCachingPolicy(true, false, false);
612         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
613         response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
614         response.setVersion(HttpVersion.HTTP_1_0);
615         response.setHeader("Date", DateUtils.formatStandardDate(now));
616         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
617         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
618     }
619 
620     @Test
621     public void getsWithQueryParametersDirectlyFrom1_0OriginsCanBeNotCacheableEvenWithExpires() {
622         policy = new ResponseCachingPolicy(true, true, false);
623         request = new BasicHttpRequest("GET", "/foo?s=bar");
624         response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
625         response.setVersion(HttpVersion.HTTP_1_0);
626         response.setHeader("Date", DateUtils.formatStandardDate(now));
627         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
628         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
629     }
630 
631     @Test
632     public void headsWithQueryParametersDirectlyFrom1_0OriginsCanBeNotCacheableEvenWithExpires() {
633         policy = new ResponseCachingPolicy(true, true, false);
634         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
635         response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
636         response.setVersion(HttpVersion.HTTP_1_0);
637         response.setHeader("Date", DateUtils.formatStandardDate(now));
638         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
639         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
640     }
641 
642     @Test
643     public void getsWithQueryParametersFrom1_0OriginsViaProxiesAreNotCacheable() {
644         request = new BasicHttpRequest("GET", "/foo?s=bar");
645         response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
646         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
647     }
648 
649     @Test
650     public void headsWithQueryParametersFrom1_0OriginsViaProxiesAreNotCacheable() {
651         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
652         response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
653         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
654     }
655 
656     @Test
657     public void getsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() {
658         request = new BasicHttpRequest("GET", "/foo?s=bar");
659         response.setHeader("Date", DateUtils.formatStandardDate(now));
660         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
661         response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
662         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
663     }
664 
665     @Test
666     public void headsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() {
667         policy = new ResponseCachingPolicy(true, false, false);
668         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
669         response.setHeader("Date", DateUtils.formatStandardDate(now));
670         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
671         response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
672         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
673     }
674 
675     @Test
676     public void getsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithExpires() {
677         policy = new ResponseCachingPolicy(true, true, true);
678         request = new BasicHttpRequest("GET", "/foo?s=bar");
679         response.setHeader("Date", DateUtils.formatStandardDate(now));
680         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
681         response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
682         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
683     }
684 
685     @Test
686     public void headsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithExpires() {
687         policy = new ResponseCachingPolicy(true, true, true);
688         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
689         response.setHeader("Date", DateUtils.formatStandardDate(now));
690         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
691         response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
692         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
693     }
694 
695     @Test
696     public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExpires() {
697         request = new BasicHttpRequest("GET", "/foo?s=bar");
698         response.setHeader("Date", DateUtils.formatStandardDate(now));
699         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
700         response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy");
701         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
702     }
703 
704     @Test
705     public void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExpires() {
706         policy = new ResponseCachingPolicy(true, false, false);
707         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
708         response.setHeader("Date", DateUtils.formatStandardDate(now));
709         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
710         response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy");
711         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
712     }
713 
714     @Test
715     public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEvenWithExpires() {
716         policy = new ResponseCachingPolicy(true, true, true);
717         request = new BasicHttpRequest("GET", "/foo?s=bar");
718         response.setHeader("Date", DateUtils.formatStandardDate(now));
719         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
720         response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy");
721         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
722     }
723 
724     @Test
725     public void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEvenWithExpires() {
726         policy = new ResponseCachingPolicy(true, true, true);
727         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
728         response.setHeader("Date", DateUtils.formatStandardDate(now));
729         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
730         response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy");
731         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
732     }
733 
734     @Test
735     public void getsWithQueryParametersFrom1_1OriginsVia1_0ProxiesAreCacheableWithExpires() {
736         request = new BasicHttpRequest("GET", "/foo?s=bar");
737         response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
738         response.setVersion(HttpVersion.HTTP_1_0);
739         response.setHeader("Date", DateUtils.formatStandardDate(now));
740         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
741         response.setHeader(HttpHeaders.VIA, "1.1 someproxy");
742         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
743     }
744 
745     @Test
746     public void headsWithQueryParametersFrom1_1OriginsVia1_0ProxiesAreCacheableWithExpires() {
747         policy = new ResponseCachingPolicy(true, false, false);
748         request = new BasicHttpRequest("HEAD", "/foo?s=bar");
749         response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
750         response.setVersion(HttpVersion.HTTP_1_0);
751         response.setHeader("Date", DateUtils.formatStandardDate(now));
752         response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
753         response.setHeader(HttpHeaders.VIA, "1.1 someproxy");
754 
755         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
756     }
757 
758     @Test
759     public void notCacheableIfExpiresEqualsDateAndNoCacheControl() {
760         response.setHeader("Date", DateUtils.formatStandardDate(now));
761         response.setHeader("Expires", DateUtils.formatStandardDate(now));
762         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
763     }
764 
765     @Test
766     public void notCacheableIfExpiresPrecedesDateAndNoCacheControl() {
767         response.setHeader("Date", DateUtils.formatStandardDate(now));
768         response.setHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo));
769         Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
770     }
771 
772     @Test
773     public void test302WithExplicitCachingHeaders() {
774         response.setCode(HttpStatus.SC_MOVED_TEMPORARILY);
775         response.setHeader("Date", DateUtils.formatStandardDate(now));
776         responseCacheControl = ResponseCacheControl.builder()
777                 .setMaxAge(300)
778                 .build();
779         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
780     }
781 
782     @Test
783     public void test303WithExplicitCachingHeadersWhenPermittedByConfig() {
784         // HTTPbis working group says ok if explicitly indicated by
785         // response headers
786         policy = new ResponseCachingPolicy(true, false, true);
787         response.setCode(HttpStatus.SC_SEE_OTHER);
788         response.setHeader("Date", DateUtils.formatStandardDate(now));
789         responseCacheControl = ResponseCacheControl.builder()
790                 .setMaxAge(300)
791                 .build();
792         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
793     }
794 
795     @Test
796     public void test307WithExplicitCachingHeaders() {
797         response.setCode(HttpStatus.SC_TEMPORARY_REDIRECT);
798         response.setHeader("Date", DateUtils.formatStandardDate(now));
799         responseCacheControl = ResponseCacheControl.builder()
800                 .setMaxAge(300)
801                 .build();
802         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
803     }
804 
805     @Test
806     public void otherStatusCodesAreCacheableWithExplicitCachingHeaders() {
807         response.setCode(HttpStatus.SC_NOT_FOUND);
808         response.setHeader("Date", DateUtils.formatStandardDate(now));
809         responseCacheControl = ResponseCacheControl.builder()
810                 .setMaxAge(300)
811                 .build();
812         assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
813     }
814 
815     @Test
816     void testIsResponseCacheableNullCacheControl() {
817 
818         // Set up test data
819         final Duration tenSecondsFromNow = Duration.ofSeconds(10);
820 
821         response = new BasicHttpResponse(HttpStatus.SC_OK, "");
822         response.setHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now()));
823         response.setHeader(HttpHeaders.EXPIRES, DateUtils.formatStandardDate(Instant.now().plus(tenSecondsFromNow)));
824 
825 
826         // Create ResponseCachingPolicy instance and test the method
827         policy = new ResponseCachingPolicy(true, false, false);
828         request = new BasicHttpRequest("GET", "/foo");
829         assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
830     }
831 
832 
833     @Test
834     void testIsResponseCacheableNotNullCacheControlSmaxAge60() {
835 
836         // Set up test data
837         final Duration tenSecondsFromNow = Duration.ofSeconds(10);
838 
839         response = new BasicHttpResponse(HttpStatus.SC_OK, "");
840         response.setHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now()));
841         response.setHeader(HttpHeaders.EXPIRES, DateUtils.formatStandardDate(Instant.now().plus(tenSecondsFromNow)));
842 
843 
844         // Create ResponseCachingPolicy instance and test the method
845         policy = new ResponseCachingPolicy(true, false, false);
846         request = new BasicHttpRequest("GET", "/foo");
847         responseCacheControl = ResponseCacheControl.builder()
848                 .setMaxAge(60)
849                 .build();
850         assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
851     }
852 
853     @Test
854     void testIsResponseCacheableNotNullCacheControlMaxAge60() {
855 
856         // Set up test data
857         final Duration tenSecondsFromNow = Duration.ofSeconds(10);
858 
859         response = new BasicHttpResponse(HttpStatus.SC_OK, "");
860         response.setHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now()));
861         response.setHeader(HttpHeaders.EXPIRES, DateUtils.formatStandardDate(Instant.now().plus(tenSecondsFromNow)));
862 
863 
864         // Create ResponseCachingPolicy instance and test the method
865         policy = new ResponseCachingPolicy(true, false, false);
866         request = new BasicHttpRequest("GET", "/foo");
867         responseCacheControl = ResponseCacheControl.builder()
868                 .setMaxAge(60)
869                 .build();
870         assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
871     }
872 
873     @Test
874     void testIsResponseCacheableNotExsiresAndDate() {
875 
876         // Set up test data
877         final Duration tenSecondsFromNow = Duration.ofSeconds(10);
878 
879         response = new BasicHttpResponse(HttpStatus.SC_OK, "");
880         response.setHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now()));
881         response.setHeader(HttpHeaders.EXPIRES, DateUtils.formatStandardDate(Instant.now().plus(tenSecondsFromNow)));
882 
883 
884         // Create ResponseCachingPolicy instance and test the method
885         policy = new ResponseCachingPolicy(true, false, false);
886         request = new BasicHttpRequest("GET", "/foo");
887         assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
888     }
889 
890     private int getRandomStatus() {
891         final int rnd = new Random().nextInt(acceptableCodes.length);
892 
893         return acceptableCodes[rnd];
894     }
895 
896     @Test
897     public void testIsResponseCacheable() {
898         request = new BasicHttpRequest("GET","/foo?s=bar");
899         // HTTPbis working group says ok if explicitly indicated by
900         // response headers
901         policy = new ResponseCachingPolicy(true, false, true);
902         response.setCode(HttpStatus.SC_OK);
903         response.setHeader("Date", DateUtils.formatStandardDate(now));
904         assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
905     }
906 
907     @Test
908     void testIsResponseCacheableNoCache() {
909         // Set up test data
910         response = new BasicHttpResponse(HttpStatus.SC_OK, "");
911         response.setHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now()));
912 
913         // Create ResponseCachingPolicy instance and test the method
914         policy = new ResponseCachingPolicy(true, false, false);
915         request = new BasicHttpRequest("GET", "/foo");
916         responseCacheControl = ResponseCacheControl.builder()
917                 .setNoCache(true)
918                 .build();
919         assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
920     }
921 
922     @Test
923     void testIsResponseCacheableNoStore() {
924         // Set up test data
925         response = new BasicHttpResponse(HttpStatus.SC_OK, "");
926         response.setHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now()));
927 
928         // Create ResponseCachingPolicy instance and test the method
929         policy = new ResponseCachingPolicy(true, false, false);
930         request = new BasicHttpRequest("GET", "/foo");
931         responseCacheControl = ResponseCacheControl.builder()
932                 .setNoStore(true)
933                 .build();
934         assertFalse(policy.isResponseCacheable(responseCacheControl, request, response));
935     }
936 
937     @Test
938     public void testImmutableAndFreshResponseIsCacheable() {
939         responseCacheControl = ResponseCacheControl.builder()
940                 .setImmutable(true)
941                 .setMaxAge(3600) // set this to a value that ensures the response is still fresh
942                 .build();
943 
944         Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
945     }
946 }