1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.client5.http.impl.cache;
28
29
30 import static org.junit.jupiter.api.Assertions.assertEquals;
31 import static org.junit.jupiter.api.Assertions.assertTrue;
32
33 import java.time.Instant;
34
35 import org.apache.hc.client5.http.cache.HttpCacheEntry;
36 import org.apache.hc.client5.http.cache.ResponseCacheControl;
37 import org.apache.hc.client5.http.utils.DateUtils;
38 import org.apache.hc.core5.http.Header;
39 import org.apache.hc.core5.http.message.BasicHeader;
40 import org.apache.hc.core5.util.TimeValue;
41 import org.junit.jupiter.api.BeforeEach;
42 import org.junit.jupiter.api.Test;
43
44 class TestCacheValidityPolicy {
45
46 private CacheValidityPolicy impl;
47 private Instant now;
48 private Instant oneSecondAgo;
49 private Instant sixSecondsAgo;
50 private Instant tenSecondsAgo;
51 private Instant elevenSecondsAgo;
52
53 @BeforeEach
54 void setUp() {
55 impl = new CacheValidityPolicy();
56 now = Instant.now();
57 oneSecondAgo = now.minusSeconds(1);
58 sixSecondsAgo = now.minusSeconds(6);
59 tenSecondsAgo = now.minusSeconds(10);
60 elevenSecondsAgo = now.minusSeconds(11);
61 }
62
63 @Test
64 void testApparentAgeIsMaxIntIfDateHeaderNotPresent() {
65 final Header[] headers = {
66 new BasicHeader("Server", "MockServer/1.0")
67 };
68 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
69 assertEquals(CacheSupport.MAX_AGE, impl.getApparentAge(entry));
70 }
71
72 @Test
73 void testApparentAgeIsResponseReceivedTimeLessDateHeader() {
74 final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)) };
75
76 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, sixSecondsAgo, headers);
77 assertEquals(TimeValue.ofSeconds(4), impl.getApparentAge(entry));
78 }
79
80 @Test
81 void testNegativeApparentAgeIsBroughtUpToZero() {
82 final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(sixSecondsAgo)) };
83 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, tenSecondsAgo, headers);
84 assertEquals(TimeValue.ofSeconds(0), impl.getApparentAge(entry));
85 }
86
87 @Test
88 void testCorrectedReceivedAgeIsAgeHeaderIfLarger() {
89 final Header[] headers = new Header[] { new BasicHeader("Age", "10"), };
90 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
91 impl = new CacheValidityPolicy() {
92 @Override
93 protected TimeValue getApparentAge(final HttpCacheEntry ent) {
94 return TimeValue.ofSeconds(6);
95 }
96 };
97 assertEquals(TimeValue.ofSeconds(10), impl.getCorrectedAgeValue(entry));
98 }
99
100 @Test
101 void testGetCorrectedAgeValue() {
102 final Header[] headers = new Header[] { new BasicHeader("Age", "6"), };
103 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
104 assertEquals(TimeValue.ofSeconds(6), impl.getCorrectedAgeValue(entry));
105 }
106
107 @Test
108 void testResponseDelayIsDifferenceBetweenResponseAndRequestTimes() {
109 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, sixSecondsAgo);
110 assertEquals(TimeValue.ofSeconds(4), impl.getResponseDelay(entry));
111 }
112
113 @Test
114 void testCorrectedInitialAgeIsCorrectedReceivedAgePlusResponseDelay() {
115 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
116 impl = new CacheValidityPolicy() {
117 @Override
118 protected TimeValue getCorrectedAgeValue(final HttpCacheEntry ent) {
119 return TimeValue.ofSeconds(7);
120 }
121
122 @Override
123 protected TimeValue getResponseDelay(final HttpCacheEntry ent) {
124 return TimeValue.ofSeconds(13);
125 }
126 };
127 assertEquals(TimeValue.ofSeconds(7), impl.getCorrectedInitialAge(entry));
128 }
129
130 @Test
131 void testResidentTimeSecondsIsTimeSinceResponseTime() {
132 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, sixSecondsAgo);
133 assertEquals(TimeValue.ofSeconds(6), impl.getResidentTime(entry, now));
134 }
135
136 @Test
137 void testCurrentAgeIsCorrectedInitialAgePlusResidentTime() {
138 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
139 impl = new CacheValidityPolicy() {
140 @Override
141 protected TimeValue getCorrectedInitialAge(final HttpCacheEntry ent) {
142 return TimeValue.ofSeconds(11);
143 }
144 @Override
145 protected TimeValue getResidentTime(final HttpCacheEntry ent, final Instant d) {
146 return TimeValue.ofSeconds(17);
147 }
148 };
149 assertEquals(TimeValue.ofSeconds(28), impl.getCurrentAge(entry, Instant.now()));
150 }
151
152 @Test
153 void testFreshnessLifetimeIsSMaxAgeIfPresent() {
154 final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
155 .setSharedMaxAge(10)
156 .setMaxAge(5)
157 .build();
158 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
159 assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(cacheControl, entry));
160 }
161
162 @Test
163 void testSMaxAgeIsIgnoredWhenNotShared() {
164 final CacheConfig cacheConfig = CacheConfig.custom()
165 .setSharedCache(false)
166 .build();
167 impl = new CacheValidityPolicy(cacheConfig);
168 final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
169 .setSharedMaxAge(10)
170 .setMaxAge(5)
171 .build();
172 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
173 assertEquals(TimeValue.ofSeconds(5), impl.getFreshnessLifetime(cacheControl, entry));
174 }
175
176 @Test
177 void testFreshnessLifetimeIsMaxAgeIfPresent() {
178 final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
179 .setMaxAge(10)
180 .build();
181 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
182 assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(cacheControl, entry));
183 }
184
185 @Test
186 void testFreshnessLifetimeUsesSharedMaxAgeInSharedCache() {
187
188 final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
189 .setMaxAge(10)
190 .setSharedMaxAge(20)
191 .build();
192 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
193 assertEquals(TimeValue.ofSeconds(20), impl.getFreshnessLifetime(cacheControl, entry));
194 }
195
196 @Test
197 void testFreshnessLifetimeUsesMaxAgeWhenSharedMaxAgeNotPresent() {
198
199 final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
200 .setMaxAge(10)
201 .build();
202 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
203 assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(cacheControl, entry));
204 }
205
206 @Test
207 void testFreshnessLifetimeIsMaxAgeEvenIfExpiresIsPresent() {
208 final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
209 .setMaxAge(10)
210 .build();
211 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
212 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
213 new BasicHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)));
214 assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(cacheControl, entry));
215 }
216
217 @Test
218 void testFreshnessLifetimeIsSMaxAgeEvenIfExpiresIsPresent() {
219 final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
220 .setSharedMaxAge(10)
221 .build();
222 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
223 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
224 new BasicHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)));
225 assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(cacheControl, entry));
226 }
227
228 @Test
229 void testFreshnessLifetimeIsFromExpiresHeaderIfNoMaxAge() {
230 final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
231 .build();
232 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
233 new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
234 new BasicHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)));
235 assertEquals(TimeValue.ofSeconds(4), impl.getFreshnessLifetime(cacheControl, entry));
236 }
237
238 @Test
239 void testHeuristicFreshnessLifetime() {
240 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
241 new BasicHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)),
242 new BasicHeader("Last-Modified", DateUtils.formatStandardDate(elevenSecondsAgo)));
243 assertEquals(TimeValue.ofSeconds(1), impl.getHeuristicFreshnessLifetime(entry));
244 }
245
246 @Test
247 void testHeuristicFreshnessLifetimeDefaultsProperly() {
248 final TimeValue defaultFreshness = TimeValue.ofSeconds(0);
249 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
250 assertEquals(defaultFreshness, impl.getHeuristicFreshnessLifetime(entry));
251 }
252
253 @Test
254 void testHeuristicFreshnessLifetimeIsNonNegative() {
255 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
256 new BasicHeader("Date", DateUtils.formatStandardDate(elevenSecondsAgo)),
257 new BasicHeader("Last-Modified", DateUtils.formatStandardDate(oneSecondAgo)));
258 assertTrue(TimeValue.isNonNegative(impl.getHeuristicFreshnessLifetime(entry)));
259 }
260
261 @Test
262 void testHeuristicFreshnessLifetimeCustomProperly() {
263 final CacheConfig cacheConfig = CacheConfig.custom().setHeuristicDefaultLifetime(TimeValue.ofSeconds(10))
264 .setHeuristicCoefficient(0.5f).build();
265 impl = new CacheValidityPolicy(cacheConfig);
266 final TimeValue defaultFreshness = TimeValue.ofSeconds(10);
267 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
268 assertEquals(defaultFreshness, impl.getHeuristicFreshnessLifetime(entry));
269 }
270
271 @Test
272 void testNegativeAgeHeaderValueReturnsZero() {
273 final Header[] headers = new Header[] { new BasicHeader("Age", "-100") };
274 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
275
276 assertEquals(0, impl.getAgeValue(entry));
277 }
278
279 @Test
280 void testMalformedAgeHeaderValueReturnsMaxAge() {
281 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
282 new BasicHeader("Age", "asdf"));
283
284 assertEquals(0, impl.getAgeValue(entry));
285 }
286
287 @Test
288 void testMalformedAgeHeaderMultipleWellFormedAges() {
289 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
290 new BasicHeader("Age", "123,456,789"));
291
292 assertEquals(123, impl.getAgeValue(entry));
293 }
294
295 @Test
296 void testMalformedAgeHeaderMultiplesMalformedAges() {
297 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
298 new BasicHeader("Age", "123 456 789"));
299
300 assertEquals(0, impl.getAgeValue(entry));
301 }
302
303 @Test
304 void testMalformedAgeHeaderNegativeAge() {
305 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
306 new BasicHeader("Age", "-123"));
307
308 assertEquals(0, impl.getAgeValue(entry));
309 }
310
311 @Test
312 void testMalformedAgeHeaderOverflow() {
313 final String reallyOldAge = "1" + Long.MAX_VALUE;
314 final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
315 new BasicHeader("Age", reallyOldAge));
316
317 assertEquals(0, impl.getAgeValue(entry));
318 }
319
320 }