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.cache;
28
29 import java.io.Serializable;
30 import java.time.Instant;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.Date;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.Iterator;
37 import java.util.Map;
38 import java.util.Set;
39 import java.util.concurrent.atomic.AtomicReference;
40 import java.util.stream.Collectors;
41
42 import org.apache.hc.client5.http.utils.DateUtils;
43 import org.apache.hc.core5.annotation.Contract;
44 import org.apache.hc.core5.annotation.Internal;
45 import org.apache.hc.core5.annotation.ThreadingBehavior;
46 import org.apache.hc.core5.http.Header;
47 import org.apache.hc.core5.http.HttpHeaders;
48 import org.apache.hc.core5.http.HttpStatus;
49 import org.apache.hc.core5.http.MessageHeaders;
50 import org.apache.hc.core5.http.Method;
51 import org.apache.hc.core5.http.ProtocolException;
52 import org.apache.hc.core5.http.message.HeaderGroup;
53 import org.apache.hc.core5.util.Args;
54
55
56
57
58
59
60
61
62
63
64 @Contract(threading = ThreadingBehavior.IMMUTABLE)
65 public class HttpCacheEntry implements MessageHeaders, Serializable {
66
67 private static final long serialVersionUID = -6300496422359477413L;
68
69 private final Instant requestDate;
70 private final Instant responseDate;
71 private final String method;
72 private final String requestURI;
73 private final HeaderGroup requestHeaders;
74 private final int status;
75 private final HeaderGroup responseHeaders;
76 private final Resource resource;
77 private final Set<String> variants;
78 private final AtomicReference<Instant> dateRef;
79 private final AtomicReference<Instant> expiresRef;
80 private final AtomicReference<Instant> lastModifiedRef;
81
82
83
84
85
86 @Internal
87 public HttpCacheEntry(
88 final Instant requestDate,
89 final Instant responseDate,
90 final String method,
91 final String requestURI,
92 final HeaderGroup requestHeaders,
93 final int status,
94 final HeaderGroup responseHeaders,
95 final Resource resource,
96 final Collection<String> variants) {
97 super();
98 this.requestDate = requestDate;
99 this.responseDate = responseDate;
100 this.method = method;
101 this.requestURI = requestURI;
102 this.requestHeaders = requestHeaders;
103 this.status = status;
104 this.responseHeaders = responseHeaders;
105 this.resource = resource;
106 this.variants = variants != null ? Collections.unmodifiableSet(new HashSet<>(variants)) : null;
107 this.dateRef = new AtomicReference<>();
108 this.expiresRef = new AtomicReference<>();
109 this.lastModifiedRef = new AtomicReference<>();
110 }
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 @Deprecated
132 public HttpCacheEntry(
133 final Date requestDate,
134 final Date responseDate,
135 final int status,
136 final Header[] responseHeaders,
137 final Resource resource,
138 final Map<String, String> variantMap) {
139 this(DateUtils.toInstant(requestDate), DateUtils.toInstant(responseDate), status, responseHeaders, resource, variantMap);
140 }
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 @Deprecated
157 public HttpCacheEntry(
158 final Instant requestDate,
159 final Instant responseDate,
160 final int status,
161 final Header[] responseHeaders,
162 final Resource resource,
163 final Map<String, String> variantMap) {
164 super();
165 Args.notNull(requestDate, "Request date");
166 Args.notNull(responseDate, "Response date");
167 Args.check(status >= HttpStatus.SC_SUCCESS, "Status code");
168 Args.notNull(responseHeaders, "Response headers");
169 this.requestDate = requestDate;
170 this.responseDate = responseDate;
171 this.method = Method.GET.name();
172 this.requestURI = "/";
173 this.requestHeaders = new HeaderGroup();
174 this.status = status;
175 this.responseHeaders = new HeaderGroup();
176 this.responseHeaders.setHeaders(responseHeaders);
177 this.resource = resource;
178 this.variants = variantMap != null ? Collections.unmodifiableSet(new HashSet<>(variantMap.keySet())) : null;
179 this.dateRef = new AtomicReference<>();
180 this.expiresRef = new AtomicReference<>();
181 this.lastModifiedRef = new AtomicReference<>();
182 }
183
184
185
186
187
188
189
190
191
192
193
194 @Deprecated
195 public HttpCacheEntry(final Date requestDate, final Date responseDate, final int status,
196 final Header[] responseHeaders, final Resource resource) {
197 this(requestDate, responseDate, status, responseHeaders, resource, new HashMap<>());
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217 @Deprecated
218 public HttpCacheEntry(final Instant requestDate, final Instant responseDate, final int status,
219 final Header[] responseHeaders, final Resource resource) {
220 this(requestDate, responseDate, status, responseHeaders, resource, new HashMap<>());
221 }
222
223
224
225
226 public int getStatus() {
227 return status;
228 }
229
230
231
232
233
234
235
236 @Deprecated
237 public Date getRequestDate() {
238 return DateUtils.toDate(requestDate);
239 }
240
241
242
243
244
245
246
247 public Instant getRequestInstant() {
248 return requestDate;
249 }
250
251
252
253
254
255
256 @Deprecated
257 public Date getResponseDate() {
258 return DateUtils.toDate(responseDate);
259 }
260
261
262
263
264
265
266
267 public Instant getResponseInstant() {
268 return responseDate;
269 }
270
271
272
273
274 @Override
275 public Header[] getHeaders() {
276 return responseHeaders.getHeaders();
277 }
278
279
280
281
282
283 @Override
284 public Header getFirstHeader(final String name) {
285 return responseHeaders.getFirstHeader(name);
286 }
287
288
289
290
291 @Override
292 public Header getLastHeader(final String name) {
293 return responseHeaders.getLastHeader(name);
294 }
295
296
297
298
299
300 @Override
301 public Header[] getHeaders(final String name) {
302 return responseHeaders.getHeaders(name);
303 }
304
305
306
307
308 @Override
309 public boolean containsHeader(final String name) {
310 return responseHeaders.containsHeader(name);
311 }
312
313
314
315
316 @Override
317 public int countHeaders(final String name) {
318 return responseHeaders.countHeaders(name);
319 }
320
321
322
323
324 @Override
325 public Header getHeader(final String name) throws ProtocolException {
326 return responseHeaders.getHeader(name);
327 }
328
329
330
331
332 @Override
333 public Iterator<Header> headerIterator() {
334 return responseHeaders.headerIterator();
335 }
336
337
338
339
340 @Override
341 public Iterator<Header> headerIterator(final String name) {
342 return responseHeaders.headerIterator(name);
343 }
344
345
346
347
348 public MessageHeaders responseHeaders() {
349 return responseHeaders;
350 }
351
352
353
354
355
356
357
358 public Date getDate() {
359 return DateUtils.toDate(getInstant());
360 }
361
362 private static final Instant NON_VALUE = Instant.ofEpochSecond(Instant.MIN.getEpochSecond(), 0);
363
364 private Instant getInstant(final AtomicReference<Instant> ref, final String headerName) {
365 Instant instant = ref.get();
366 if (instant == null) {
367 instant = DateUtils.parseStandardDate(this, headerName);
368 if (instant == null) {
369 instant = NON_VALUE;
370 }
371 if (!ref.compareAndSet(null, instant)) {
372 instant = ref.get();
373 }
374 }
375 return instant != null && instant != NON_VALUE ? instant : null;
376 }
377
378
379
380
381 public Instant getInstant() {
382 return getInstant(dateRef, HttpHeaders.DATE);
383 }
384
385
386
387
388 public Instant getExpires() {
389 return getInstant(expiresRef, HttpHeaders.EXPIRES);
390 }
391
392
393
394
395 public Instant getLastModified() {
396 return getInstant(lastModifiedRef, HttpHeaders.LAST_MODIFIED);
397 }
398
399
400
401
402 public Resource getResource() {
403 return this.resource;
404 }
405
406
407
408
409
410
411 public boolean hasVariants() {
412 return variants != null;
413 }
414
415
416
417
418
419
420 public Set<String> getVariants() {
421 return variants != null ? variants : Collections.emptySet();
422 }
423
424
425
426
427 @Deprecated
428 public Map<String, String> getVariantMap() {
429 return variants != null ? variants.stream()
430 .collect(Collectors.toMap(e -> e, e -> e + requestURI)) : Collections.emptyMap();
431 }
432
433
434
435
436
437
438
439 public String getRequestMethod() {
440 return method;
441 }
442
443
444
445
446 public String getRequestURI() {
447 return requestURI;
448 }
449
450
451
452
453 public MessageHeaders requestHeaders() {
454 return requestHeaders;
455 }
456
457
458
459
460 public Iterator<Header> requestHeaderIterator() {
461 return requestHeaders.headerIterator();
462 }
463
464
465
466
467 public Iterator<Header> requestHeaderIterator(final String headerName) {
468 return requestHeaders.headerIterator(headerName);
469 }
470
471
472
473
474
475
476
477
478 public static boolean isNewer(final HttpCacheEntry entry, final MessageHeaders message) {
479 if (entry == null || message == null) {
480 return false;
481 }
482 final Instant cacheDate = entry.getInstant();
483 if (cacheDate == null) {
484 return false;
485 }
486 final Instant messageDate = DateUtils.parseStandardDate(message, HttpHeaders.DATE);
487 if (messageDate == null) {
488 return false;
489 }
490 return cacheDate.compareTo(messageDate) > 0;
491 }
492
493
494
495
496
497 @Override
498 public String toString() {
499 return "HttpCacheEntry{" +
500 "requestDate=" + requestDate +
501 ", responseDate=" + responseDate +
502 ", method='" + method + '\'' +
503 ", requestURI='" + requestURI + '\'' +
504 ", requestHeaders=" + requestHeaders +
505 ", status=" + status +
506 ", responseHeaders=" + responseHeaders +
507 ", resource=" + resource +
508 ", variants=" + variants +
509 '}';
510 }
511 }