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.io.IOException;
30 import java.net.MalformedURLException;
31 import java.net.URL;
32 import java.util.Date;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.http.Header;
37 import org.apache.http.HttpHost;
38 import org.apache.http.HttpRequest;
39 import org.apache.http.HttpResponse;
40 import org.apache.http.annotation.Contract;
41 import org.apache.http.annotation.ThreadingBehavior;
42 import org.apache.http.client.cache.HeaderConstants;
43 import org.apache.http.client.cache.HttpCacheEntry;
44 import org.apache.http.client.cache.HttpCacheInvalidator;
45 import org.apache.http.client.cache.HttpCacheStorage;
46 import org.apache.http.client.utils.DateUtils;
47 import org.apache.http.protocol.HTTP;
48
49
50
51
52
53
54
55 @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
56 class CacheInvalidator implements HttpCacheInvalidator {
57
58 private final HttpCacheStorage storage;
59 private final CacheKeyGenerator cacheKeyGenerator;
60
61 private final Log log = LogFactory.getLog(getClass());
62
63
64
65
66
67
68
69
70 public CacheInvalidator(
71 final CacheKeyGenerator uriExtractor,
72 final HttpCacheStorage storage) {
73 this.cacheKeyGenerator = uriExtractor;
74 this.storage = storage;
75 }
76
77
78
79
80
81
82
83
84 @Override
85 public void flushInvalidatedCacheEntries(final HttpHost host, final HttpRequest req) {
86 final String theUri = cacheKeyGenerator.getURI(host, req);
87 final HttpCacheEntry parent = getEntry(theUri);
88
89 if (requestShouldNotBeCached(req) || shouldInvalidateHeadCacheEntry(req, parent)) {
90 log.debug("Invalidating parent cache entry: " + parent);
91 if (parent != null) {
92 for (final String variantURI : parent.getVariantMap().values()) {
93 flushEntry(variantURI);
94 }
95 flushEntry(theUri);
96 }
97 final URL reqURL = getAbsoluteURL(theUri);
98 if (reqURL == null) {
99 log.error("Couldn't transform request into valid URL");
100 return;
101 }
102 final Header clHdr = req.getFirstHeader("Content-Location");
103 if (clHdr != null) {
104 final String contentLocation = clHdr.getValue();
105 if (!flushAbsoluteUriFromSameHost(reqURL, contentLocation)) {
106 flushRelativeUriFromSameHost(reqURL, contentLocation);
107 }
108 }
109 final Header lHdr = req.getFirstHeader("Location");
110 if (lHdr != null) {
111 flushAbsoluteUriFromSameHost(reqURL, lHdr.getValue());
112 }
113 }
114 }
115
116 private boolean shouldInvalidateHeadCacheEntry(final HttpRequest req, final HttpCacheEntry parentCacheEntry) {
117 return requestIsGet(req) && isAHeadCacheEntry(parentCacheEntry);
118 }
119
120 private boolean requestIsGet(final HttpRequest req) {
121 return req.getRequestLine().getMethod().equals((HeaderConstants.GET_METHOD));
122 }
123
124 private boolean isAHeadCacheEntry(final HttpCacheEntry parentCacheEntry) {
125 return parentCacheEntry != null && parentCacheEntry.getRequestMethod().equals(HeaderConstants.HEAD_METHOD);
126 }
127
128 private void flushEntry(final String uri) {
129 try {
130 storage.removeEntry(uri);
131 } catch (final IOException ioe) {
132 log.warn("unable to flush cache entry", ioe);
133 }
134 }
135
136 private HttpCacheEntry getEntry(final String theUri) {
137 try {
138 return storage.getEntry(theUri);
139 } catch (final IOException ioe) {
140 log.warn("could not retrieve entry from storage", ioe);
141 }
142 return null;
143 }
144
145 protected void flushUriIfSameHost(final URL requestURL, final URL targetURL) {
146 final URL canonicalTarget = getAbsoluteURL(cacheKeyGenerator.canonicalizeUri(targetURL.toString()));
147 if (canonicalTarget == null) {
148 return;
149 }
150 if (canonicalTarget.getAuthority().equalsIgnoreCase(requestURL.getAuthority())) {
151 flushEntry(canonicalTarget.toString());
152 }
153 }
154
155 protected void flushRelativeUriFromSameHost(final URL reqURL, final String relUri) {
156 final URL relURL = getRelativeURL(reqURL, relUri);
157 if (relURL == null) {
158 return;
159 }
160 flushUriIfSameHost(reqURL, relURL);
161 }
162
163
164 protected boolean flushAbsoluteUriFromSameHost(final URL reqURL, final String uri) {
165 final URL absURL = getAbsoluteURL(uri);
166 if (absURL == null) {
167 return false;
168 }
169 flushUriIfSameHost(reqURL,absURL);
170 return true;
171 }
172
173 private URL getAbsoluteURL(final String uri) {
174 URL absURL = null;
175 try {
176 absURL = new URL(uri);
177 } catch (final MalformedURLException mue) {
178
179 }
180 return absURL;
181 }
182
183 private URL getRelativeURL(final URL reqURL, final String relUri) {
184 URL relURL = null;
185 try {
186 relURL = new URL(reqURL,relUri);
187 } catch (final MalformedURLException e) {
188
189 }
190 return relURL;
191 }
192
193 protected boolean requestShouldNotBeCached(final HttpRequest req) {
194 final String method = req.getRequestLine().getMethod();
195 return notGetOrHeadRequest(method);
196 }
197
198 private boolean notGetOrHeadRequest(final String method) {
199 return !(HeaderConstants.GET_METHOD.equals(method) || HeaderConstants.HEAD_METHOD
200 .equals(method));
201 }
202
203
204
205
206 @Override
207 public void flushInvalidatedCacheEntries(final HttpHost host,
208 final HttpRequest request, final HttpResponse response) {
209 final int status = response.getStatusLine().getStatusCode();
210 if (status < 200 || status > 299) {
211 return;
212 }
213 final URL reqURL = getAbsoluteURL(cacheKeyGenerator.getURI(host, request));
214 if (reqURL == null) {
215 return;
216 }
217 final URL contentLocation = getContentLocationURL(reqURL, response);
218 if (contentLocation != null) {
219 flushLocationCacheEntry(reqURL, response, contentLocation);
220 }
221 final URL location = getLocationURL(reqURL, response);
222 if (location != null) {
223 flushLocationCacheEntry(reqURL, response, location);
224 }
225 }
226
227 private void flushLocationCacheEntry(final URL reqURL,
228 final HttpResponse response, final URL location) {
229 final String cacheKey = cacheKeyGenerator.canonicalizeUri(location.toString());
230 final HttpCacheEntry entry = getEntry(cacheKey);
231 if (entry == null) {
232 return;
233 }
234
235
236
237
238 if (responseDateOlderThanEntryDate(response, entry)) {
239 return;
240 }
241 if (!responseAndEntryEtagsDiffer(response, entry)) {
242 return;
243 }
244
245 flushUriIfSameHost(reqURL, location);
246 }
247
248 private URL getContentLocationURL(final URL reqURL, final HttpResponse response) {
249 final Header clHeader = response.getFirstHeader("Content-Location");
250 if (clHeader == null) {
251 return null;
252 }
253 final String contentLocation = clHeader.getValue();
254 final URL canonURL = getAbsoluteURL(contentLocation);
255 if (canonURL != null) {
256 return canonURL;
257 }
258 return getRelativeURL(reqURL, contentLocation);
259 }
260
261 private URL getLocationURL(final URL reqURL, final HttpResponse response) {
262 final Header clHeader = response.getFirstHeader("Location");
263 if (clHeader == null) {
264 return null;
265 }
266 final String location = clHeader.getValue();
267 final URL canonURL = getAbsoluteURL(location);
268 if (canonURL != null) {
269 return canonURL;
270 }
271 return getRelativeURL(reqURL, location);
272 }
273
274 private boolean responseAndEntryEtagsDiffer(final HttpResponse response,
275 final HttpCacheEntry entry) {
276 final Header entryEtag = entry.getFirstHeader(HeaderConstants.ETAG);
277 final Header responseEtag = response.getFirstHeader(HeaderConstants.ETAG);
278 if (entryEtag == null || responseEtag == null) {
279 return false;
280 }
281 return (!entryEtag.getValue().equals(responseEtag.getValue()));
282 }
283
284 private boolean responseDateOlderThanEntryDate(final HttpResponse response,
285 final HttpCacheEntry entry) {
286 final Header entryDateHeader = entry.getFirstHeader(HTTP.DATE_HEADER);
287 final Header responseDateHeader = response.getFirstHeader(HTTP.DATE_HEADER);
288 if (entryDateHeader == null || responseDateHeader == null) {
289
290 return false;
291 }
292 final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
293 final Date responseDate = DateUtils.parseDate(responseDateHeader.getValue());
294 if (entryDate == null || responseDate == null) {
295 return false;
296 }
297 return responseDate.before(entryDate);
298 }
299 }