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 java.time.Instant;
30  
31  import org.apache.hc.client5.http.cache.HttpCacheEntry;
32  import org.apache.hc.client5.http.cache.RequestCacheControl;
33  import org.apache.hc.client5.http.cache.ResponseCacheControl;
34  import org.apache.hc.client5.http.utils.DateUtils;
35  import org.apache.hc.core5.http.Header;
36  import org.apache.hc.core5.http.HttpHost;
37  import org.apache.hc.core5.http.HttpRequest;
38  import org.apache.hc.core5.http.Method;
39  import org.apache.hc.core5.http.message.BasicHeader;
40  import org.apache.hc.core5.http.message.BasicHttpRequest;
41  import org.apache.hc.core5.http.support.BasicRequestBuilder;
42  import org.apache.hc.core5.util.TimeValue;
43  import org.junit.jupiter.api.Assertions;
44  import org.junit.jupiter.api.BeforeEach;
45  import org.junit.jupiter.api.Test;
46  
47  public class TestCachedResponseSuitabilityChecker {
48  
49      private Instant now;
50      private Instant elevenSecondsAgo;
51      private Instant tenSecondsAgo;
52      private Instant nineSecondsAgo;
53  
54      private HttpRequest request;
55      private HttpCacheEntry entry;
56      private RequestCacheControl requestCacheControl;
57      private ResponseCacheControl responseCacheControl;
58      private CachedResponseSuitabilityChecker impl;
59  
60      @BeforeEach
61      public void setUp() {
62          now = Instant.now();
63          elevenSecondsAgo = now.minusSeconds(11);
64          tenSecondsAgo = now.minusSeconds(10);
65          nineSecondsAgo = now.minusSeconds(9);
66  
67          request = new BasicHttpRequest("GET", "/foo");
68          entry = HttpTestUtils.makeCacheEntry();
69          requestCacheControl = RequestCacheControl.builder().build();
70          responseCacheControl = ResponseCacheControl.builder().build();
71  
72          impl = new CachedResponseSuitabilityChecker(CacheConfig.DEFAULT);
73      }
74  
75      private HttpCacheEntry makeEntry(final Instant requestDate,
76                                       final Instant responseDate,
77                                       final Method method,
78                                       final String requestUri,
79                                       final Header[] requestHeaders,
80                                       final int status,
81                                       final Header[] responseHeaders) {
82          return HttpTestUtils.makeCacheEntry(requestDate, responseDate, method, requestUri, requestHeaders,
83                  status, responseHeaders, HttpTestUtils.makeNullResource());
84      }
85  
86      private HttpCacheEntry makeEntry(final Header... headers) {
87          return makeEntry(elevenSecondsAgo, nineSecondsAgo, Method.GET, "/foo", null, 200, headers);
88      }
89  
90      private HttpCacheEntry makeEntry(final Instant requestDate,
91                                       final Instant responseDate,
92                                       final Header... headers) {
93          return makeEntry(requestDate, responseDate, Method.GET, "/foo", null, 200, headers);
94      }
95  
96      private HttpCacheEntry makeEntry(final Method method, final String requestUri, final Header... headers) {
97          return makeEntry(elevenSecondsAgo, nineSecondsAgo, method, requestUri, null, 200, headers);
98      }
99  
100     private HttpCacheEntry makeEntry(final Method method, final String requestUri, final Header[] requestHeaders,
101                                      final int status, final Header[] responseHeaders) {
102         return makeEntry(elevenSecondsAgo, nineSecondsAgo, method, requestUri, requestHeaders,
103                 status, responseHeaders);
104     }
105 
106     @Test
107     public void testRequestMethodMatch() {
108         request = new BasicHttpRequest("GET", "/foo");
109         entry = makeEntry(Method.GET, "/foo",
110                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
111         Assertions.assertTrue(impl.requestMethodMatch(request, entry));
112 
113         request = new BasicHttpRequest("HEAD", "/foo");
114         Assertions.assertTrue(impl.requestMethodMatch(request, entry));
115 
116         request = new BasicHttpRequest("POST", "/foo");
117         Assertions.assertFalse(impl.requestMethodMatch(request, entry));
118 
119         request = new BasicHttpRequest("HEAD", "/foo");
120         entry = makeEntry(Method.HEAD, "/foo",
121                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
122         Assertions.assertTrue(impl.requestMethodMatch(request, entry));
123 
124         request = new BasicHttpRequest("GET", "/foo");
125         entry = makeEntry(Method.HEAD, "/foo",
126                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
127         Assertions.assertFalse(impl.requestMethodMatch(request, entry));
128     }
129 
130     @Test
131     public void testRequestUriMatch() {
132         request = new BasicHttpRequest("GET", "/foo");
133         entry = makeEntry(Method.GET, "/foo",
134                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
135         Assertions.assertTrue(impl.requestUriMatch(request, entry));
136 
137         request = new BasicHttpRequest("GET", new HttpHost("some-host"), "/foo");
138         entry = makeEntry(Method.GET, "http://some-host:80/foo",
139                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
140         Assertions.assertTrue(impl.requestUriMatch(request, entry));
141 
142         request = new BasicHttpRequest("GET", new HttpHost("Some-Host"), "/foo?bar");
143         entry = makeEntry(Method.GET, "http://some-host:80/foo?bar",
144                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
145         Assertions.assertTrue(impl.requestUriMatch(request, entry));
146 
147         request = new BasicHttpRequest("GET", new HttpHost("some-other-host"), "/foo");
148         entry = makeEntry(Method.GET, "http://some-host:80/foo",
149                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
150         Assertions.assertFalse(impl.requestUriMatch(request, entry));
151 
152         request = new BasicHttpRequest("GET", new HttpHost("some-host"), "/foo?huh");
153         entry = makeEntry(Method.GET, "http://some-host:80/foo?bar",
154                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
155         Assertions.assertFalse(impl.requestUriMatch(request, entry));
156     }
157 
158     @Test
159     public void testRequestHeadersMatch() {
160         request = BasicRequestBuilder.get("/foo").build();
161         entry = makeEntry(
162                 Method.GET, "/foo",
163                 new Header[]{},
164                 200,
165                 new Header[]{
166                         new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo))
167                 });
168         Assertions.assertTrue(impl.requestHeadersMatch(request, entry));
169 
170         request = BasicRequestBuilder.get("/foo").build();
171         entry = makeEntry(
172                 Method.GET, "/foo",
173                 new Header[]{},
174                 200,
175                 new Header[]{
176                         new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
177                         new BasicHeader("Vary", "")
178                 });
179         Assertions.assertTrue(impl.requestHeadersMatch(request, entry));
180 
181         request = BasicRequestBuilder.get("/foo")
182                 .addHeader("Accept-Encoding", "blah")
183                 .build();
184         entry = makeEntry(
185                 Method.GET, "/foo",
186                 new Header[]{
187                         new BasicHeader("Accept-Encoding", "blah")
188                 },
189                 200,
190                 new Header[]{
191                         new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
192                         new BasicHeader("Vary", "Accept-Encoding")
193                 });
194         Assertions.assertTrue(impl.requestHeadersMatch(request, entry));
195 
196         request = BasicRequestBuilder.get("/foo")
197                 .addHeader("Accept-Encoding", "gzip, deflate, deflate ,  zip, ")
198                 .build();
199         entry = makeEntry(
200                 Method.GET, "/foo",
201                 new Header[]{
202                         new BasicHeader("Accept-Encoding", " gzip, zip, deflate")
203                 },
204                 200,
205                 new Header[]{
206                         new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
207                         new BasicHeader("Vary", "Accept-Encoding")
208                 });
209         Assertions.assertTrue(impl.requestHeadersMatch(request, entry));
210 
211         request = BasicRequestBuilder.get("/foo")
212                 .addHeader("Accept-Encoding", "gzip, deflate, zip")
213                 .build();
214         entry = makeEntry(
215                 Method.GET, "/foo",
216                 new Header[]{
217                         new BasicHeader("Accept-Encoding", " gzip, deflate")
218                 },
219                 200,
220                 new Header[]{
221                         new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
222                         new BasicHeader("Vary", "Accept-Encoding")
223                 });
224         Assertions.assertFalse(impl.requestHeadersMatch(request, entry));
225     }
226 
227     @Test
228     public void testResponseNoCache() {
229         entry = makeEntry(new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
230         responseCacheControl = ResponseCacheControl.builder()
231                 .setNoCache(false)
232                 .build();
233 
234         Assertions.assertFalse(impl.isResponseNoCache(responseCacheControl, entry));
235 
236         responseCacheControl = ResponseCacheControl.builder()
237                 .setNoCache(true)
238                 .build();
239 
240         Assertions.assertTrue(impl.isResponseNoCache(responseCacheControl, entry));
241 
242         responseCacheControl = ResponseCacheControl.builder()
243                 .setNoCache(true)
244                 .setNoCacheFields("stuff", "more-stuff")
245                 .build();
246 
247         Assertions.assertFalse(impl.isResponseNoCache(responseCacheControl, entry));
248 
249         entry = makeEntry(
250                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
251                 new BasicHeader("stuff", "booh"));
252 
253         Assertions.assertTrue(impl.isResponseNoCache(responseCacheControl, entry));
254     }
255 
256     @Test
257     public void testSuitableIfCacheEntryIsFresh() {
258         entry = makeEntry(new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
259         responseCacheControl = ResponseCacheControl.builder()
260                 .setMaxAge(3600)
261                 .build();
262         Assertions.assertEquals(CacheSuitability.FRESH, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
263     }
264 
265     @Test
266     public void testNotSuitableIfCacheEntryIsNotFresh() {
267         entry = makeEntry(
268                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
269         responseCacheControl = ResponseCacheControl.builder()
270                 .setMaxAge(5)
271                 .build();
272         Assertions.assertEquals(CacheSuitability.STALE, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
273     }
274 
275     @Test
276     public void testNotSuitableIfRequestHasNoCache() {
277         requestCacheControl = RequestCacheControl.builder()
278                 .setNoCache(true)
279                 .build();
280         entry = makeEntry(
281                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
282         responseCacheControl = ResponseCacheControl.builder()
283                 .setMaxAge(3600)
284                 .build();
285         Assertions.assertEquals(CacheSuitability.REVALIDATION_REQUIRED, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
286     }
287 
288     @Test
289     public void testNotSuitableIfAgeExceedsRequestMaxAge() {
290         requestCacheControl = RequestCacheControl.builder()
291                 .setMaxAge(10)
292                 .build();
293         responseCacheControl = ResponseCacheControl.builder()
294                 .setMaxAge(3600)
295                 .build();
296         entry = makeEntry(
297                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
298         Assertions.assertEquals(CacheSuitability.REVALIDATION_REQUIRED, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
299     }
300 
301     @Test
302     public void testSuitableIfFreshAndAgeIsUnderRequestMaxAge() {
303         requestCacheControl = RequestCacheControl.builder()
304                 .setMaxAge(15)
305                 .build();
306         entry = makeEntry(
307                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
308         responseCacheControl = ResponseCacheControl.builder()
309                 .setMaxAge(3600)
310                 .build();
311         Assertions.assertEquals(CacheSuitability.FRESH, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
312     }
313 
314     @Test
315     public void testSuitableIfFreshAndFreshnessLifetimeGreaterThanRequestMinFresh() {
316         requestCacheControl = RequestCacheControl.builder()
317                 .setMinFresh(10)
318                 .build();
319         entry = makeEntry(
320                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
321         responseCacheControl = ResponseCacheControl.builder()
322                 .setMaxAge(3600)
323                 .build();
324         Assertions.assertEquals(CacheSuitability.FRESH, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
325     }
326 
327     @Test
328     public void testNotSuitableIfFreshnessLifetimeLessThanRequestMinFresh() {
329         requestCacheControl = RequestCacheControl.builder()
330                 .setMinFresh(10)
331                 .build();
332         entry = makeEntry(
333                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
334         responseCacheControl = ResponseCacheControl.builder()
335                 .setMaxAge(15)
336                 .build();
337         Assertions.assertEquals(CacheSuitability.REVALIDATION_REQUIRED, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
338     }
339 
340     @Test
341     public void testSuitableEvenIfStaleButPermittedByRequestMaxStale() {
342         requestCacheControl = RequestCacheControl.builder()
343                 .setMaxStale(10)
344                 .build();
345         final Header[] headers = {
346                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo))
347         };
348         entry = makeEntry(headers);
349         responseCacheControl = ResponseCacheControl.builder()
350                 .setMaxAge(5)
351                 .build();
352         Assertions.assertEquals(CacheSuitability.FRESH_ENOUGH, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
353     }
354 
355     @Test
356     public void testNotSuitableIfStaleButTooStaleForRequestMaxStale() {
357         requestCacheControl = RequestCacheControl.builder()
358                 .setMaxStale(2)
359                 .build();
360         entry = makeEntry(
361                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
362         responseCacheControl = ResponseCacheControl.builder()
363                 .setMaxAge(5)
364                 .build();
365         Assertions.assertEquals(CacheSuitability.REVALIDATION_REQUIRED, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
366     }
367 
368     @Test
369     public void testSuitableIfCacheEntryIsHeuristicallyFreshEnough() {
370         final Instant oneSecondAgo = now.minusSeconds(1);
371         final Instant twentyOneSecondsAgo = now.minusSeconds(21);
372 
373         entry = makeEntry(oneSecondAgo, oneSecondAgo,
374                 new BasicHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)),
375                 new BasicHeader("Last-Modified", DateUtils.formatStandardDate(twentyOneSecondsAgo)));
376 
377         final CacheConfig config = CacheConfig.custom()
378             .setHeuristicCachingEnabled(true)
379             .setHeuristicCoefficient(0.1f).build();
380         impl = new CachedResponseSuitabilityChecker(config);
381 
382         Assertions.assertEquals(CacheSuitability.FRESH, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
383     }
384 
385     @Test
386     public void testSuitableIfCacheEntryIsHeuristicallyFreshEnoughByDefault() {
387         entry = makeEntry(
388                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
389 
390         final CacheConfig config = CacheConfig.custom()
391             .setHeuristicCachingEnabled(true)
392             .setHeuristicDefaultLifetime(TimeValue.ofSeconds(20L))
393             .build();
394         impl = new CachedResponseSuitabilityChecker(config);
395 
396         Assertions.assertEquals(CacheSuitability.FRESH, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
397     }
398 
399     @Test
400     public void testSuitableIfRequestMethodisHEAD() {
401         final HttpRequest headRequest = new BasicHttpRequest("HEAD", "/foo");
402         entry = makeEntry(
403                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
404         responseCacheControl = ResponseCacheControl.builder()
405                 .setMaxAge(3600)
406                 .build();
407 
408         Assertions.assertEquals(CacheSuitability.FRESH, impl.assessSuitability(requestCacheControl, responseCacheControl, headRequest, entry, now));
409     }
410 
411     @Test
412     public void testSuitableForGETIfEntryDoesNotSpecifyARequestMethodButContainsEntity() {
413         impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build());
414         entry = makeEntry(Method.GET, "/foo",
415                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
416         responseCacheControl = ResponseCacheControl.builder()
417                 .setMaxAge(3600)
418                 .build();
419 
420         Assertions.assertEquals(CacheSuitability.FRESH, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
421     }
422 
423     @Test
424     public void testSuitableForGETIfHeadResponseCachingEnabledAndEntryDoesNotSpecifyARequestMethodButContains204Response() {
425         impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build());
426         entry = makeEntry(Method.GET, "/foo",
427                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
428         responseCacheControl = ResponseCacheControl.builder()
429                 .setMaxAge(3600)
430                 .build();
431 
432         Assertions.assertEquals(CacheSuitability.FRESH, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
433     }
434 
435     @Test
436     public void testSuitableForHEADIfHeadResponseCachingEnabledAndEntryDoesNotSpecifyARequestMethod() {
437         final HttpRequest headRequest = new BasicHttpRequest("HEAD", "/foo");
438         impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build());
439         final Header[] headers = {
440 
441         };
442         entry = makeEntry(Method.GET, "/foo",
443                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
444         responseCacheControl = ResponseCacheControl.builder()
445                 .setMaxAge(3600)
446                 .build();
447 
448         Assertions.assertEquals(CacheSuitability.FRESH, impl.assessSuitability(requestCacheControl, responseCacheControl, headRequest, entry, now));
449     }
450 
451     @Test
452     public void testNotSuitableIfGetRequestWithHeadCacheEntry() {
453         // Prepare a cache entry with HEAD method
454         entry = makeEntry(Method.HEAD, "/foo",
455                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
456         responseCacheControl = ResponseCacheControl.builder()
457                 .setMaxAge(3600)
458                 .build();
459         // Validate that the cache entry is not suitable for the GET request
460         Assertions.assertEquals(CacheSuitability.MISMATCH, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
461     }
462 
463     @Test
464     public void testSuitableIfErrorRequestCacheControl() {
465         // Prepare a cache entry with HEAD method
466         entry = makeEntry(Method.GET, "/foo",
467                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
468         responseCacheControl = ResponseCacheControl.builder()
469                 .setMaxAge(5)
470                 .build();
471 
472         // the entry has been stale for 6 seconds
473 
474         requestCacheControl = RequestCacheControl.builder()
475                 .setStaleIfError(10)
476                 .build();
477         Assertions.assertTrue(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
478 
479         requestCacheControl = RequestCacheControl.builder()
480                 .setStaleIfError(5)
481                 .build();
482         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
483 
484         requestCacheControl = RequestCacheControl.builder()
485                 .setStaleIfError(10)
486                 .setMinFresh(4) // should take precedence over stale-if-error
487                 .build();
488         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
489 
490         requestCacheControl = RequestCacheControl.builder()
491                 .setStaleIfError(-1) // not set or not valid
492                 .build();
493         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
494     }
495 
496     @Test
497     public void testSuitableIfErrorResponseCacheControl() {
498         // Prepare a cache entry with HEAD method
499         entry = makeEntry(Method.GET, "/foo",
500                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
501         responseCacheControl = ResponseCacheControl.builder()
502                 .setMaxAge(5)
503                 .setStaleIfError(10)
504                 .build();
505 
506         // the entry has been stale for 6 seconds
507 
508         Assertions.assertTrue(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
509 
510         responseCacheControl = ResponseCacheControl.builder()
511                 .setMaxAge(5)
512                 .setStaleIfError(5)
513                 .build();
514         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
515 
516         responseCacheControl = ResponseCacheControl.builder()
517                 .setMaxAge(5)
518                 .setStaleIfError(-1) // not set or not valid
519                 .build();
520         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
521     }
522 
523     @Test
524     public void testSuitableIfErrorRequestCacheControlTakesPrecedenceOverResponseCacheControl() {
525         // Prepare a cache entry with HEAD method
526         entry = makeEntry(Method.GET, "/foo",
527                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
528         responseCacheControl = ResponseCacheControl.builder()
529                 .setMaxAge(5)
530                 .setStaleIfError(5)
531                 .build();
532 
533         // the entry has been stale for 6 seconds
534 
535         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
536 
537         requestCacheControl = RequestCacheControl.builder()
538                 .setStaleIfError(10)
539                 .build();
540         Assertions.assertTrue(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
541     }
542 
543     @Test
544     public void testSuitableIfErrorConfigDefault() {
545         // Prepare a cache entry with HEAD method
546         entry = makeEntry(Method.GET, "/foo",
547                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
548         responseCacheControl = ResponseCacheControl.builder()
549                 .setMaxAge(5)
550                 .build();
551         impl = new CachedResponseSuitabilityChecker(CacheConfig.custom()
552                 .setStaleIfErrorEnabled(true)
553                 .build());
554         Assertions.assertTrue(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
555 
556         requestCacheControl = RequestCacheControl.builder()
557                 .setStaleIfError(5)
558                 .build();
559 
560         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
561     }
562 
563 }