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.http.impl.client.cache;
28
29 import java.util.Date;
30
31 import org.apache.http.Header;
32 import org.apache.http.HeaderElement;
33 import org.apache.http.HttpRequest;
34 import org.apache.http.annotation.Contract;
35 import org.apache.http.annotation.ThreadingBehavior;
36 import org.apache.http.client.cache.HeaderConstants;
37 import org.apache.http.client.cache.HttpCacheEntry;
38 import org.apache.http.client.utils.DateUtils;
39 import org.apache.http.protocol.HTTP;
40
41
42
43
44 @Contract(threading = ThreadingBehavior.IMMUTABLE)
45 class CacheValidityPolicy {
46
47 public static final long MAX_AGE = 2147483648L;
48
49 CacheValidityPolicy() {
50 super();
51 }
52
53 public long getCurrentAgeSecs(final HttpCacheEntry entry, final Date now) {
54 return getCorrectedInitialAgeSecs(entry) + getResidentTimeSecs(entry, now);
55 }
56
57 public long getFreshnessLifetimeSecs(final HttpCacheEntry entry) {
58 final long maxage = getMaxAge(entry);
59 if (maxage > -1) {
60 return maxage;
61 }
62
63 final Date dateValue = entry.getDate();
64 if (dateValue == null) {
65 return 0L;
66 }
67
68 final Date expiry = getExpirationDate(entry);
69 if (expiry == null) {
70 return 0;
71 }
72 final long diff = expiry.getTime() - dateValue.getTime();
73 return (diff / 1000);
74 }
75
76 public boolean isResponseFresh(final HttpCacheEntry entry, final Date now) {
77 return (getCurrentAgeSecs(entry, now) < getFreshnessLifetimeSecs(entry));
78 }
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 public boolean isResponseHeuristicallyFresh(final HttpCacheEntry entry,
94 final Date now, final float coefficient, final long defaultLifetime) {
95 return (getCurrentAgeSecs(entry, now) < getHeuristicFreshnessLifetimeSecs(entry, coefficient, defaultLifetime));
96 }
97
98 public long getHeuristicFreshnessLifetimeSecs(final HttpCacheEntry entry,
99 final float coefficient, final long defaultLifetime) {
100 final Date dateValue = entry.getDate();
101 final Date lastModifiedValue = getLastModifiedValue(entry);
102
103 if (dateValue != null && lastModifiedValue != null) {
104 final long diff = dateValue.getTime() - lastModifiedValue.getTime();
105 if (diff < 0) {
106 return 0;
107 }
108 return (long)(coefficient * (diff / 1000));
109 }
110
111 return defaultLifetime;
112 }
113
114 public boolean isRevalidatable(final HttpCacheEntry entry) {
115 return entry.getFirstHeader(HeaderConstants.ETAG) != null
116 || entry.getFirstHeader(HeaderConstants.LAST_MODIFIED) != null;
117 }
118
119 public boolean mustRevalidate(final HttpCacheEntry entry) {
120 return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE);
121 }
122
123 public boolean proxyRevalidate(final HttpCacheEntry entry) {
124 return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE);
125 }
126
127 public boolean mayReturnStaleWhileRevalidating(final HttpCacheEntry entry, final Date now) {
128 for (final Header h : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) {
129 for(final HeaderElement elt : h.getElements()) {
130 if (HeaderConstants.STALE_WHILE_REVALIDATE.equalsIgnoreCase(elt.getName())) {
131 try {
132 final int allowedStalenessLifetime = Integer.parseInt(elt.getValue());
133 if (getStalenessSecs(entry, now) <= allowedStalenessLifetime) {
134 return true;
135 }
136 } catch (final NumberFormatException nfe) {
137
138 }
139 }
140 }
141 }
142
143 return false;
144 }
145
146 public boolean mayReturnStaleIfError(final HttpRequest request,
147 final HttpCacheEntry entry, final Date now) {
148 final long stalenessSecs = getStalenessSecs(entry, now);
149 return mayReturnStaleIfError(request.getHeaders(HeaderConstants.CACHE_CONTROL),
150 stalenessSecs)
151 || mayReturnStaleIfError(entry.getHeaders(HeaderConstants.CACHE_CONTROL),
152 stalenessSecs);
153 }
154
155 private boolean mayReturnStaleIfError(final Header[] headers, final long stalenessSecs) {
156 boolean result = false;
157 for(final Header h : headers) {
158 for(final HeaderElement elt : h.getElements()) {
159 if (HeaderConstants.STALE_IF_ERROR.equals(elt.getName())) {
160 try {
161 final int staleIfErrorSecs = Integer.parseInt(elt.getValue());
162 if (stalenessSecs <= staleIfErrorSecs) {
163 result = true;
164 break;
165 }
166 } catch (final NumberFormatException nfe) {
167
168 }
169 }
170 }
171 }
172 return result;
173 }
174
175
176
177
178
179
180 @Deprecated
181 protected Date getDateValue(final HttpCacheEntry entry) {
182 return entry.getDate();
183 }
184
185 protected Date getLastModifiedValue(final HttpCacheEntry entry) {
186 final Header dateHdr = entry.getFirstHeader(HeaderConstants.LAST_MODIFIED);
187 if (dateHdr == null) {
188 return null;
189 }
190 return DateUtils.parseDate(dateHdr.getValue());
191 }
192
193 protected long getContentLengthValue(final HttpCacheEntry entry) {
194 final Header cl = entry.getFirstHeader(HTTP.CONTENT_LEN);
195 if (cl == null) {
196 return -1;
197 }
198
199 try {
200 return Long.parseLong(cl.getValue());
201 } catch (final NumberFormatException ex) {
202 return -1;
203 }
204 }
205
206 protected boolean hasContentLengthHeader(final HttpCacheEntry entry) {
207 return null != entry.getFirstHeader(HTTP.CONTENT_LEN);
208 }
209
210
211
212
213
214
215
216
217 protected boolean contentLengthHeaderMatchesActualLength(final HttpCacheEntry entry) {
218 return !hasContentLengthHeader(entry) ||
219 (entry.getResource() != null && getContentLengthValue(entry) == entry.getResource().length());
220 }
221
222 protected long getApparentAgeSecs(final HttpCacheEntry entry) {
223 final Date dateValue = entry.getDate();
224 if (dateValue == null) {
225 return MAX_AGE;
226 }
227 final long diff = entry.getResponseDate().getTime() - dateValue.getTime();
228 if (diff < 0L) {
229 return 0;
230 }
231 return (diff / 1000);
232 }
233
234 protected long getAgeValue(final HttpCacheEntry entry) {
235 long ageValue = 0;
236 for (final Header hdr : entry.getHeaders(HeaderConstants.AGE)) {
237 long hdrAge;
238 try {
239 hdrAge = Long.parseLong(hdr.getValue());
240 if (hdrAge < 0) {
241 hdrAge = MAX_AGE;
242 }
243 } catch (final NumberFormatException nfe) {
244 hdrAge = MAX_AGE;
245 }
246 ageValue = (hdrAge > ageValue) ? hdrAge : ageValue;
247 }
248 return ageValue;
249 }
250
251 protected long getCorrectedReceivedAgeSecs(final HttpCacheEntry entry) {
252 final long apparentAge = getApparentAgeSecs(entry);
253 final long ageValue = getAgeValue(entry);
254 return (apparentAge > ageValue) ? apparentAge : ageValue;
255 }
256
257 protected long getResponseDelaySecs(final HttpCacheEntry entry) {
258 final long diff = entry.getResponseDate().getTime() - entry.getRequestDate().getTime();
259 return (diff / 1000L);
260 }
261
262 protected long getCorrectedInitialAgeSecs(final HttpCacheEntry entry) {
263 return getCorrectedReceivedAgeSecs(entry) + getResponseDelaySecs(entry);
264 }
265
266 protected long getResidentTimeSecs(final HttpCacheEntry entry, final Date now) {
267 final long diff = now.getTime() - entry.getResponseDate().getTime();
268 return (diff / 1000L);
269 }
270
271 protected long getMaxAge(final HttpCacheEntry entry) {
272 long maxage = -1;
273 for (final Header hdr : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) {
274 for (final HeaderElement elt : hdr.getElements()) {
275 if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())
276 || "s-maxage".equals(elt.getName())) {
277 try {
278 final long currMaxAge = Long.parseLong(elt.getValue());
279 if (maxage == -1 || currMaxAge < maxage) {
280 maxage = currMaxAge;
281 }
282 } catch (final NumberFormatException nfe) {
283
284 maxage = 0;
285 }
286 }
287 }
288 }
289 return maxage;
290 }
291
292 protected Date getExpirationDate(final HttpCacheEntry entry) {
293 final Header expiresHeader = entry.getFirstHeader(HeaderConstants.EXPIRES);
294 if (expiresHeader == null) {
295 return null;
296 }
297 return DateUtils.parseDate(expiresHeader.getValue());
298 }
299
300 public boolean hasCacheControlDirective(final HttpCacheEntry entry,
301 final String directive) {
302 for (final Header h : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) {
303 for(final HeaderElement elt : h.getElements()) {
304 if (directive.equalsIgnoreCase(elt.getName())) {
305 return true;
306 }
307 }
308 }
309 return false;
310 }
311
312 public long getStalenessSecs(final HttpCacheEntry entry, final Date now) {
313 final long age = getCurrentAgeSecs(entry, now);
314 final long freshness = getFreshnessLifetimeSecs(entry);
315 if (age <= freshness) {
316 return 0L;
317 }
318 return (age - freshness);
319 }
320
321
322 }