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 import java.net.URI;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.Map;
33
34 import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator;
35 import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage;
36 import org.apache.hc.client5.http.cache.HttpCacheEntry;
37 import org.apache.hc.client5.http.impl.Operations;
38 import org.apache.hc.client5.http.utils.URIUtils;
39 import org.apache.hc.core5.annotation.Contract;
40 import org.apache.hc.core5.annotation.Internal;
41 import org.apache.hc.core5.annotation.ThreadingBehavior;
42 import org.apache.hc.core5.concurrent.Cancellable;
43 import org.apache.hc.core5.concurrent.FutureCallback;
44 import org.apache.hc.core5.function.Resolver;
45 import org.apache.hc.core5.http.Header;
46 import org.apache.hc.core5.http.HttpHost;
47 import org.apache.hc.core5.http.HttpRequest;
48 import org.apache.hc.core5.http.HttpResponse;
49 import org.apache.hc.core5.http.HttpStatus;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53
54
55
56
57
58
59 @Contract(threading = ThreadingBehavior.STATELESS)
60 @Internal
61 public class DefaultAsyncCacheInvalidator extends CacheInvalidatorBase implements HttpAsyncCacheInvalidator {
62
63 public static final DefaultAsyncCacheInvalidatorltAsyncCacheInvalidator.html#DefaultAsyncCacheInvalidator">DefaultAsyncCacheInvalidator INSTANCE = new DefaultAsyncCacheInvalidator();
64
65 private static final Logger LOG = LoggerFactory.getLogger(DefaultAsyncCacheInvalidator.class);
66
67 private void removeEntry(final HttpAsyncCacheStorage storage, final String cacheKey) {
68 storage.removeEntry(cacheKey, new FutureCallback<Boolean>() {
69
70 @Override
71 public void completed(final Boolean result) {
72 if (LOG.isDebugEnabled()) {
73 if (result) {
74 LOG.debug("Cache entry with key {} successfully flushed", cacheKey);
75 } else {
76 LOG.debug("Cache entry with key {} could not be flushed", cacheKey);
77 }
78 }
79 }
80
81 @Override
82 public void failed(final Exception ex) {
83 if (LOG.isWarnEnabled()) {
84 LOG.warn("Unable to flush cache entry with key {}", cacheKey, ex);
85 }
86 }
87
88 @Override
89 public void cancelled() {
90 }
91
92 });
93 }
94
95 @Override
96 public Cancellable flushCacheEntriesInvalidatedByRequest(
97 final HttpHost host,
98 final HttpRequest request,
99 final Resolver<URI, String> cacheKeyResolver,
100 final HttpAsyncCacheStorage storage,
101 final FutureCallback<Boolean> callback) {
102 final String s = HttpCacheSupport.getRequestUri(request, host);
103 final URI uri = HttpCacheSupport.normalizeQuetly(s);
104 final String cacheKey = uri != null ? cacheKeyResolver.resolve(uri) : s;
105 return storage.getEntry(cacheKey, new FutureCallback<HttpCacheEntry>() {
106
107 @Override
108 public void completed(final HttpCacheEntry parentEntry) {
109 if (requestShouldNotBeCached(request) || shouldInvalidateHeadCacheEntry(request, parentEntry)) {
110 if (parentEntry != null) {
111 if (LOG.isDebugEnabled()) {
112 LOG.debug("Invalidating parentEntry cache entry with key {}", cacheKey);
113 }
114 for (final String variantURI : parentEntry.getVariantMap().values()) {
115 removeEntry(storage, variantURI);
116 }
117 removeEntry(storage, cacheKey);
118 }
119 if (uri != null) {
120 if (LOG.isWarnEnabled()) {
121 LOG.warn("{} is not a valid URI", s);
122 }
123 final Header clHdr = request.getFirstHeader("Content-Location");
124 if (clHdr != null) {
125 final URI contentLocation = HttpCacheSupport.normalizeQuetly(clHdr.getValue());
126 if (contentLocation != null) {
127 if (!flushAbsoluteUriFromSameHost(uri, contentLocation, cacheKeyResolver, storage)) {
128 flushRelativeUriFromSameHost(uri, contentLocation, cacheKeyResolver, storage);
129 }
130 }
131 }
132 final Header lHdr = request.getFirstHeader("Location");
133 if (lHdr != null) {
134 final URI location = HttpCacheSupport.normalizeQuetly(lHdr.getValue());
135 if (location != null) {
136 flushAbsoluteUriFromSameHost(uri, location, cacheKeyResolver, storage);
137 }
138 }
139 }
140 }
141 callback.completed(Boolean.TRUE);
142 }
143
144 @Override
145 public void failed(final Exception ex) {
146 callback.failed(ex);
147 }
148
149 @Override
150 public void cancelled() {
151 callback.cancelled();
152 }
153
154 });
155
156 }
157
158 private void flushRelativeUriFromSameHost(
159 final URI requestUri,
160 final URI uri,
161 final Resolver<URI, String> cacheKeyResolver,
162 final HttpAsyncCacheStorage storage) {
163 final URI resolvedUri = uri != null ? URIUtils.resolve(requestUri, uri) : null;
164 if (resolvedUri != null && isSameHost(requestUri, resolvedUri)) {
165 removeEntry(storage, cacheKeyResolver.resolve(resolvedUri));
166 }
167 }
168
169 private boolean flushAbsoluteUriFromSameHost(
170 final URI requestUri,
171 final URI uri,
172 final Resolver<URI, String> cacheKeyResolver,
173 final HttpAsyncCacheStorage storage) {
174 if (uri != null && isSameHost(requestUri, uri)) {
175 removeEntry(storage, cacheKeyResolver.resolve(uri));
176 return true;
177 }
178 return false;
179 }
180
181 @Override
182 public Cancellable flushCacheEntriesInvalidatedByExchange(
183 final HttpHost host,
184 final HttpRequest request,
185 final HttpResponse response,
186 final Resolver<URI, String> cacheKeyResolver,
187 final HttpAsyncCacheStorage storage,
188 final FutureCallback<Boolean> callback) {
189 final int status = response.getCode();
190 if (status >= HttpStatus.SC_SUCCESS && status < HttpStatus.SC_REDIRECTION) {
191 final String s = HttpCacheSupport.getRequestUri(request, host);
192 final URI requestUri = HttpCacheSupport.normalizeQuetly(s);
193 if (requestUri != null) {
194 final List<String> cacheKeys = new ArrayList<>(2);
195 final URI contentLocation = getContentLocationURI(requestUri, response);
196 if (contentLocation != null && isSameHost(requestUri, contentLocation)) {
197 cacheKeys.add(cacheKeyResolver.resolve(contentLocation));
198 }
199 final URI location = getLocationURI(requestUri, response);
200 if (location != null && isSameHost(requestUri, location)) {
201 cacheKeys.add(cacheKeyResolver.resolve(location));
202 }
203 if (cacheKeys.size() == 1) {
204 final String key = cacheKeys.get(0);
205 storage.getEntry(key, new FutureCallback<HttpCacheEntry>() {
206
207 @Override
208 public void completed(final HttpCacheEntry entry) {
209 if (entry != null) {
210
211
212 if (!responseDateOlderThanEntryDate(response, entry) && responseAndEntryEtagsDiffer(response, entry)) {
213 removeEntry(storage, key);
214 }
215 }
216 callback.completed(Boolean.TRUE);
217 }
218
219 @Override
220 public void failed(final Exception ex) {
221 callback.failed(ex);
222 }
223
224 @Override
225 public void cancelled() {
226 callback.cancelled();
227 }
228
229 });
230 } else if (cacheKeys.size() > 1) {
231 storage.getEntries(cacheKeys, new FutureCallback<Map<String, HttpCacheEntry>>() {
232
233 @Override
234 public void completed(final Map<String, HttpCacheEntry> resultMap) {
235 for (final Map.Entry<String, HttpCacheEntry> resultEntry: resultMap.entrySet()) {
236
237
238 final String key = resultEntry.getKey();
239 final HttpCacheEntry entry = resultEntry.getValue();
240 if (!responseDateOlderThanEntryDate(response, entry) && responseAndEntryEtagsDiffer(response, entry)) {
241 removeEntry(storage, key);
242 }
243 }
244 callback.completed(Boolean.TRUE);
245 }
246
247 @Override
248 public void failed(final Exception ex) {
249 callback.failed(ex);
250 }
251
252 @Override
253 public void cancelled() {
254 callback.cancelled();
255 }
256
257 });
258 }
259 }
260 }
261 callback.completed(Boolean.TRUE);
262 return Operations.nonCancellable();
263 }
264
265 }