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  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      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     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     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     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     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     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     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     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     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     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     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     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     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     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     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     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     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     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     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     void testSuitableForHEADIfHeadResponseCachingEnabledAndEntryDoesNotSpecifyARequestMethod() {
437         final HttpRequest headRequest = new BasicHttpRequest("HEAD", "/foo");
438         impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build());
439         entry = makeEntry(Method.GET, "/foo",
440                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
441         responseCacheControl = ResponseCacheControl.builder()
442                 .setMaxAge(3600)
443                 .build();
444 
445         Assertions.assertEquals(CacheSuitability.FRESH, impl.assessSuitability(requestCacheControl, responseCacheControl, headRequest, entry, now));
446     }
447 
448     @Test
449     void testNotSuitableIfGetRequestWithHeadCacheEntry() {
450         // Prepare a cache entry with HEAD method
451         entry = makeEntry(Method.HEAD, "/foo",
452                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
453         responseCacheControl = ResponseCacheControl.builder()
454                 .setMaxAge(3600)
455                 .build();
456         // Validate that the cache entry is not suitable for the GET request
457         Assertions.assertEquals(CacheSuitability.MISMATCH, impl.assessSuitability(requestCacheControl, responseCacheControl, request, entry, now));
458     }
459 
460     @Test
461     void testSuitableIfErrorRequestCacheControl() {
462         // Prepare a cache entry with HEAD method
463         entry = makeEntry(Method.GET, "/foo",
464                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
465         responseCacheControl = ResponseCacheControl.builder()
466                 .setMaxAge(5)
467                 .build();
468 
469         // the entry has been stale for 6 seconds
470 
471         requestCacheControl = RequestCacheControl.builder()
472                 .setStaleIfError(10)
473                 .build();
474         Assertions.assertTrue(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
475 
476         requestCacheControl = RequestCacheControl.builder()
477                 .setStaleIfError(5)
478                 .build();
479         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
480 
481         requestCacheControl = RequestCacheControl.builder()
482                 .setStaleIfError(10)
483                 .setMinFresh(4) // should take precedence over stale-if-error
484                 .build();
485         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
486 
487         requestCacheControl = RequestCacheControl.builder()
488                 .setStaleIfError(-1) // not set or not valid
489                 .build();
490         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
491     }
492 
493     @Test
494     void testSuitableIfErrorResponseCacheControl() {
495         // Prepare a cache entry with HEAD method
496         entry = makeEntry(Method.GET, "/foo",
497                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
498         responseCacheControl = ResponseCacheControl.builder()
499                 .setMaxAge(5)
500                 .setStaleIfError(10)
501                 .build();
502 
503         // the entry has been stale for 6 seconds
504 
505         Assertions.assertTrue(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
506 
507         responseCacheControl = ResponseCacheControl.builder()
508                 .setMaxAge(5)
509                 .setStaleIfError(5)
510                 .build();
511         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
512 
513         responseCacheControl = ResponseCacheControl.builder()
514                 .setMaxAge(5)
515                 .setStaleIfError(-1) // not set or not valid
516                 .build();
517         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
518     }
519 
520     @Test
521     void testSuitableIfErrorRequestCacheControlTakesPrecedenceOverResponseCacheControl() {
522         // Prepare a cache entry with HEAD method
523         entry = makeEntry(Method.GET, "/foo",
524                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
525         responseCacheControl = ResponseCacheControl.builder()
526                 .setMaxAge(5)
527                 .setStaleIfError(5)
528                 .build();
529 
530         // the entry has been stale for 6 seconds
531 
532         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
533 
534         requestCacheControl = RequestCacheControl.builder()
535                 .setStaleIfError(10)
536                 .build();
537         Assertions.assertTrue(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
538     }
539 
540     @Test
541     void testSuitableIfErrorConfigDefault() {
542         // Prepare a cache entry with HEAD method
543         entry = makeEntry(Method.GET, "/foo",
544                 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)));
545         responseCacheControl = ResponseCacheControl.builder()
546                 .setMaxAge(5)
547                 .build();
548         impl = new CachedResponseSuitabilityChecker(CacheConfig.custom()
549                 .setStaleIfErrorEnabled(true)
550                 .build());
551         Assertions.assertTrue(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
552 
553         requestCacheControl = RequestCacheControl.builder()
554                 .setStaleIfError(5)
555                 .build();
556 
557         Assertions.assertFalse(impl.isSuitableIfError(requestCacheControl, responseCacheControl, entry, now));
558     }
559 
560 }