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