HttCache and HttpAsyncCache implementation to treat ResourceIOExceptions as non-fatal and log ResourceIOExceptions as warnings

This commit is contained in:
Oleg Kalnichevski 2017-12-30 00:08:47 +01:00
parent 13acc440ed
commit 77703a7eef
9 changed files with 527 additions and 253 deletions

View File

@ -826,8 +826,7 @@ public void completed() {
callBackend(target, request, entityProducer, scope, chain, asyncExecCallback);
return;
}
final HttpCacheEntry matchedEntry = matchingVariant.getEntry();
if (revalidationResponseIsTooOld(backendResponse, matchedEntry)) {
if (revalidationResponseIsTooOld(backendResponse, matchingVariant.getEntry())) {
final HttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest(request);
scope.clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, unconditional);
callBackend(target, unconditional, entityProducer, scope, chain, asyncExecCallback);
@ -837,11 +836,10 @@ public void completed() {
future.setDependency(responseCache.updateVariantCacheEntry(
target,
conditionalRequest,
matchedEntry,
backendResponse,
matchingVariant,
requestDate,
responseDateRef.get(),
matchingVariant.getCacheKey(),
new FutureCallback<HttpCacheEntry>() {
@Override

View File

@ -29,6 +29,7 @@
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.hc.client5.http.StandardMethods;
import org.apache.hc.client5.http.cache.HeaderConstants;
@ -36,6 +37,7 @@
import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage;
import org.apache.hc.client5.http.cache.HttpCacheCASOperation;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.HttpCacheUpdateException;
import org.apache.hc.client5.http.cache.ResourceFactory;
import org.apache.hc.client5.http.cache.ResourceIOException;
import org.apache.hc.client5.http.impl.Operations;
@ -45,10 +47,16 @@
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.message.RequestLine;
import org.apache.hc.core5.http.message.StatusLine;
import org.apache.hc.core5.util.ByteArrayBuffer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
class BasicHttpAsyncCache implements HttpAsyncCache {
private final Logger log = LogManager.getLogger(getClass());
private final CacheUpdateHandler cacheUpdateHandler;
private final CacheKeyGenerator cacheKeyGenerator;
private final HttpAsyncCacheInvalidator cacheInvalidator;
@ -79,9 +87,36 @@ public BasicHttpAsyncCache(final ResourceFactory resourceFactory, final HttpAsyn
@Override
public Cancellable flushCacheEntriesFor(
final HttpHost host, final HttpRequest request, final FutureCallback<Boolean> callback) {
if (log.isDebugEnabled()) {
log.debug("Flush cache entries: " + host + "; " + new RequestLine(request));
}
if (!StandardMethods.isSafe(request.getMethod())) {
final String uri = cacheKeyGenerator.generateKey(host, request);
return storage.removeEntry(uri, callback);
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
return storage.removeEntry(cacheKey, new FutureCallback<Boolean>() {
@Override
public void completed(final Boolean result) {
callback.completed(result);
}
@Override
public void failed(final Exception ex) {
if (ex instanceof ResourceIOException) {
if (log.isWarnEnabled()) {
log.warn("I/O error removing cache entry with key " + cacheKey);
}
callback.completed(Boolean.TRUE);
} else {
callback.failed(ex);
}
}
@Override
public void cancelled() {
callback.cancelled();
}
});
} else {
callback.completed(Boolean.TRUE);
return Operations.nonCancellable();
@ -91,6 +126,9 @@ public Cancellable flushCacheEntriesFor(
@Override
public Cancellable flushInvalidatedCacheEntriesFor(
final HttpHost host, final HttpRequest request, final HttpResponse response, final FutureCallback<Boolean> callback) {
if (log.isDebugEnabled()) {
log.debug("Flush cache entries: " + host + "; " + new RequestLine(request) + " " + new StatusLine(response));
}
if (!StandardMethods.isSafe(request.getMethod())) {
return cacheInvalidator.flushInvalidatedCacheEntries(host, request, response, cacheKeyGenerator, storage, callback);
} else {
@ -102,52 +140,117 @@ public Cancellable flushInvalidatedCacheEntriesFor(
@Override
public Cancellable flushInvalidatedCacheEntriesFor(
final HttpHost host, final HttpRequest request, final FutureCallback<Boolean> callback) {
if (log.isDebugEnabled()) {
log.debug("Flush invalidated cache entries: " + host + "; " + new RequestLine(request));
}
return cacheInvalidator.flushInvalidatedCacheEntries(host, request, cacheKeyGenerator, storage, callback);
}
Cancellable storeInCache(
final HttpHost target, final HttpRequest request, final HttpCacheEntry entry, final FutureCallback<Boolean> callback) {
final String cacheKey,
final HttpHost host,
final HttpRequest request,
final HttpCacheEntry entry,
final FutureCallback<Boolean> callback) {
if (entry.hasVariants()) {
return storeVariantEntry(target, request, entry, callback);
return storeVariantEntry(cacheKey, host, request, entry, callback);
} else {
return storeNonVariantEntry(target, request, entry, callback);
return storeEntry(cacheKey, entry, callback);
}
}
Cancellable storeNonVariantEntry(
final HttpHost target,
final HttpRequest req,
Cancellable storeEntry(
final String cacheKey,
final HttpCacheEntry entry,
final FutureCallback<Boolean> callback) {
final String uri = cacheKeyGenerator.generateKey(target, req);
return storage.putEntry(uri, entry, callback);
}
Cancellable storeVariantEntry(
final HttpHost target,
final HttpRequest req,
final HttpCacheEntry entry,
final FutureCallback<Boolean> callback) {
final String parentCacheKey = cacheKeyGenerator.generateKey(target, req);
final String variantKey = cacheKeyGenerator.generateVariantKey(req, entry);
final String variantURI = cacheKeyGenerator.generateVariantURI(target, req, entry);
return storage.putEntry(variantURI, entry, new FutureCallback<Boolean>() {
return storage.putEntry(cacheKey, entry, new FutureCallback<Boolean>() {
@Override
public void completed(final Boolean result) {
storage.updateEntry(parentCacheKey, new HttpCacheCASOperation() {
@Override
public HttpCacheEntry execute(final HttpCacheEntry existing) throws ResourceIOException {
return cacheUpdateHandler.updateParentCacheEntry(req.getRequestUri(), existing, entry, variantKey, variantURI);
}
}, callback);
callback.completed(result);
}
@Override
public void failed(final Exception ex) {
callback.failed(ex);
if (ex instanceof ResourceIOException) {
if (log.isWarnEnabled()) {
log.warn("I/O error storing cache entry with key " + cacheKey);
}
callback.completed(Boolean.TRUE);
} else {
callback.failed(ex);
}
}
@Override
public void cancelled() {
callback.cancelled();
}
});
}
Cancellable storeVariantEntry(
final String cacheKey,
final HttpHost host,
final HttpRequest req,
final HttpCacheEntry entry,
final FutureCallback<Boolean> callback) {
final String variantKey = cacheKeyGenerator.generateVariantKey(req, entry);
final String variantCacheKey = cacheKeyGenerator.generateVariantURI(host, req, entry);
return storage.putEntry(variantCacheKey, entry, new FutureCallback<Boolean>() {
@Override
public void completed(final Boolean result) {
storage.updateEntry(cacheKey,
new HttpCacheCASOperation() {
@Override
public HttpCacheEntry execute(final HttpCacheEntry existing) throws ResourceIOException {
return cacheUpdateHandler.updateParentCacheEntry(req.getRequestUri(), existing, entry, variantKey, variantCacheKey);
}
},
new FutureCallback<Boolean>() {
@Override
public void completed(final Boolean result) {
callback.completed(result);
}
@Override
public void failed(final Exception ex) {
if (ex instanceof HttpCacheUpdateException) {
if (log.isWarnEnabled()) {
log.warn("Cannot update cache entry with key " + cacheKey);
}
} else if (ex instanceof ResourceIOException) {
if (log.isWarnEnabled()) {
log.warn("I/O error updating cache entry with key " + cacheKey);
}
} else {
callback.failed(ex);
}
}
@Override
public void cancelled() {
callback.cancelled();
}
});
}
@Override
public void failed(final Exception ex) {
if (ex instanceof ResourceIOException) {
if (log.isWarnEnabled()) {
log.warn("I/O error updating cache entry with key " + variantCacheKey);
}
callback.completed(Boolean.TRUE);
} else {
callback.failed(ex);
}
}
@Override
@ -160,30 +263,66 @@ public void cancelled() {
@Override
public Cancellable reuseVariantEntryFor(
final HttpHost target, final HttpRequest req, final Variant variant, final FutureCallback<Boolean> callback) {
final String parentCacheKey = cacheKeyGenerator.generateKey(target, req);
final HttpHost host, final HttpRequest request, final Variant variant, final FutureCallback<Boolean> callback) {
if (log.isDebugEnabled()) {
log.debug("Re-use variant entry: " + host + "; " + new RequestLine(request) + " / " + variant);
}
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
final HttpCacheEntry entry = variant.getEntry();
final String variantKey = cacheKeyGenerator.generateVariantKey(req, entry);
final String variantKey = cacheKeyGenerator.generateVariantKey(request, entry);
final String variantCacheKey = variant.getCacheKey();
return storage.updateEntry(parentCacheKey, new HttpCacheCASOperation() {
return storage.updateEntry(cacheKey,
new HttpCacheCASOperation() {
@Override
public HttpCacheEntry execute(final HttpCacheEntry existing) throws ResourceIOException {
return cacheUpdateHandler.updateParentCacheEntry(req.getRequestUri(), existing, entry, variantKey, variantCacheKey);
}
@Override
public HttpCacheEntry execute(final HttpCacheEntry existing) throws ResourceIOException {
return cacheUpdateHandler.updateParentCacheEntry(request.getRequestUri(), existing, entry, variantKey, variantCacheKey);
}
}, callback);
},
new FutureCallback<Boolean>() {
@Override
public void completed(final Boolean result) {
callback.completed(result);
}
@Override
public void failed(final Exception ex) {
if (ex instanceof HttpCacheUpdateException) {
if (log.isWarnEnabled()) {
log.warn("Cannot update cache entry with key " + cacheKey);
}
} else if (ex instanceof ResourceIOException) {
if (log.isWarnEnabled()) {
log.warn("I/O error updating cache entry with key " + cacheKey);
}
} else {
callback.failed(ex);
}
}
@Override
public void cancelled() {
callback.cancelled();
}
});
}
@Override
public Cancellable updateCacheEntry(
final HttpHost target,
final HttpHost host,
final HttpRequest request,
final HttpCacheEntry stale,
final HttpResponse originResponse,
final Date requestSent,
final Date responseReceived,
final FutureCallback<HttpCacheEntry> callback) {
if (log.isDebugEnabled()) {
log.debug("Update cache entry: " + host + "; " + new RequestLine(request));
}
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
try {
final HttpCacheEntry updatedEntry = cacheUpdateHandler.updateCacheEntry(
request.getRequestUri(),
@ -191,7 +330,7 @@ public Cancellable updateCacheEntry(
requestSent,
responseReceived,
originResponse);
return storeInCache(target, request, updatedEntry, new FutureCallback<Boolean>() {
return storeInCache(cacheKey, host, request, updatedEntry, new FutureCallback<Boolean>() {
@Override
public void completed(final Boolean result) {
@ -210,29 +349,36 @@ public void cancelled() {
});
} catch (final ResourceIOException ex) {
callback.failed(ex);
if (log.isWarnEnabled()) {
log.warn("I/O error updating cache entry with key " + cacheKey);
}
callback.completed(stale);
return Operations.nonCancellable();
}
}
@Override
public Cancellable updateVariantCacheEntry(
final HttpHost target,
final HttpHost host,
final HttpRequest request,
final HttpCacheEntry stale,
final HttpResponse originResponse,
final Variant variant,
final Date requestSent,
final Date responseReceived,
final String cacheKey,
final FutureCallback<HttpCacheEntry> callback) {
if (log.isDebugEnabled()) {
log.debug("Update variant cache entry: " + host + "; " + new RequestLine(request) + " / " + variant);
}
final HttpCacheEntry entry = variant.getEntry();
final String cacheKey = variant.getCacheKey();
try {
final HttpCacheEntry updatedEntry = cacheUpdateHandler.updateCacheEntry(
request.getRequestUri(),
stale,
entry,
requestSent,
responseReceived,
originResponse);
return storage.putEntry(cacheKey, updatedEntry, new FutureCallback<Boolean>() {
return storeEntry(cacheKey, updatedEntry, new FutureCallback<Boolean>() {
@Override
public void completed(final Boolean result) {
@ -251,7 +397,10 @@ public void cancelled() {
});
} catch (final ResourceIOException ex) {
callback.failed(ex);
if (log.isWarnEnabled()) {
log.warn("I/O error updating cache entry with key " + cacheKey);
}
callback.completed(entry);
return Operations.nonCancellable();
}
}
@ -265,9 +414,13 @@ public Cancellable createCacheEntry(
final Date requestSent,
final Date responseReceived,
final FutureCallback<HttpCacheEntry> callback) {
if (log.isDebugEnabled()) {
log.debug("Create cache entry: " + host + "; " + new RequestLine(request));
}
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
try {
final HttpCacheEntry entry = cacheUpdateHandler.createtCacheEntry(request, originResponse, content, requestSent, responseReceived);
return storeInCache(host, request, entry, new FutureCallback<Boolean>() {
return storeInCache(cacheKey, host, request, entry, new FutureCallback<Boolean>() {
@Override
public void completed(final Boolean result) {
@ -286,13 +439,24 @@ public void cancelled() {
});
} catch (final ResourceIOException ex) {
callback.failed(ex);
if (log.isWarnEnabled()) {
log.warn("I/O error creating cache entry with key " + cacheKey);
}
callback.completed(new HttpCacheEntry(
requestSent,
responseReceived,
originResponse.getCode(),
originResponse.getAllHeaders(),
content != null ? HeapResourceFactory.INSTANCE.generate(null, content.array(), 0, content.length()) : null));
return Operations.nonCancellable();
}
}
@Override
public Cancellable getCacheEntry(final HttpHost host, final HttpRequest request, final FutureCallback<HttpCacheEntry> callback) {
if (log.isDebugEnabled()) {
log.debug("Get cache entry: " + host + "; " + new RequestLine(request));
}
final ComplexCancellable complexCancellable = new ComplexCancellable();
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
complexCancellable.setDependency(storage.getEntry(cacheKey, new FutureCallback<HttpCacheEntry>() {
@ -301,9 +465,36 @@ public Cancellable getCacheEntry(final HttpHost host, final HttpRequest request,
public void completed(final HttpCacheEntry root) {
if (root != null) {
if (root.hasVariants()) {
final String variantCacheKey = root.getVariantMap().get(cacheKeyGenerator.generateVariantKey(request, root));
final String variantKey = cacheKeyGenerator.generateVariantKey(request, root);
final String variantCacheKey = root.getVariantMap().get(variantKey);
if (variantCacheKey != null) {
complexCancellable.setDependency(storage.getEntry(variantCacheKey, callback));
complexCancellable.setDependency(storage.getEntry(
variantCacheKey,
new FutureCallback<HttpCacheEntry>() {
@Override
public void completed(final HttpCacheEntry result) {
callback.completed(result);
}
@Override
public void failed(final Exception ex) {
if (ex instanceof ResourceIOException) {
if (log.isWarnEnabled()) {
log.warn("I/O error retrieving cache entry with key " + variantCacheKey);
}
callback.completed(null);
} else {
callback.failed(ex);
}
}
@Override
public void cancelled() {
callback.cancelled();
}
}));
return;
}
}
@ -313,7 +504,14 @@ public void completed(final HttpCacheEntry root) {
@Override
public void failed(final Exception ex) {
callback.failed(ex);
if (ex instanceof ResourceIOException) {
if (log.isWarnEnabled()) {
log.warn("I/O error retrieving cache entry with key " + cacheKey);
}
callback.completed(null);
} else {
callback.failed(ex);
}
}
@Override
@ -328,16 +526,20 @@ public void cancelled() {
@Override
public Cancellable getVariantCacheEntriesWithEtags(
final HttpHost host, final HttpRequest request, final FutureCallback<Map<String, Variant>> callback) {
if (log.isDebugEnabled()) {
log.debug("Get variant cache entries: " + host + "; " + new RequestLine(request));
}
final ComplexCancellable complexCancellable = new ComplexCancellable();
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
final Map<String, Variant> variants = new HashMap<>();
complexCancellable.setDependency(storage.getEntry(cacheKey, new FutureCallback<HttpCacheEntry>() {
@Override
public void completed(final HttpCacheEntry rootEntry) {
final Map<String, Variant> variants = new HashMap<>();
if (rootEntry != null && rootEntry.hasVariants()) {
final Set<String> variantCacheKeys = rootEntry.getVariantMap().keySet();
complexCancellable.setDependency(storage.getEntries(
rootEntry.getVariantMap().keySet(),
variantCacheKeys,
new FutureCallback<Map<String, HttpCacheEntry>>() {
@Override
@ -355,7 +557,14 @@ public void completed(final Map<String, HttpCacheEntry> resultMap) {
@Override
public void failed(final Exception ex) {
callback.failed(ex);
if (ex instanceof ResourceIOException) {
if (log.isWarnEnabled()) {
log.warn("I/O error retrieving cache entry with keys " + variantCacheKeys);
}
callback.completed(variants);
} else {
callback.failed(ex);
}
}
@Override
@ -371,7 +580,14 @@ public void cancelled() {
@Override
public void failed(final Exception ex) {
callback.failed(ex);
if (ex instanceof ResourceIOException) {
if (log.isWarnEnabled()) {
log.warn("I/O error retrieving cache entry with key " + cacheKey);
}
callback.completed(variants);
} else {
callback.failed(ex);
}
}
@Override

View File

@ -43,19 +43,21 @@
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.message.RequestLine;
import org.apache.hc.core5.http.message.StatusLine;
import org.apache.hc.core5.util.ByteArrayBuffer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
class BasicHttpCache implements HttpCache {
private final Logger log = LogManager.getLogger(getClass());
private final CacheUpdateHandler cacheUpdateHandler;
private final CacheKeyGenerator cacheKeyGenerator;
private final HttpCacheInvalidator cacheInvalidator;
private final HttpCacheStorage storage;
private final Logger log = LogManager.getLogger(getClass());
public BasicHttpCache(
final ResourceFactory resourceFactory,
final HttpCacheStorage storage,
@ -87,68 +89,72 @@ public BasicHttpCache() {
}
@Override
public void flushCacheEntriesFor(final HttpHost host, final HttpRequest request) throws ResourceIOException {
public void flushCacheEntriesFor(final HttpHost host, final HttpRequest request) {
if (log.isDebugEnabled()) {
log.debug("Flush cache entries: " + host + "; " + new RequestLine(request));
}
if (!StandardMethods.isSafe(request.getMethod())) {
final String uri = cacheKeyGenerator.generateKey(host, request);
storage.removeEntry(uri);
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
try {
storage.removeEntry(cacheKey);
} catch (final ResourceIOException ex) {
if (log.isWarnEnabled()) {
log.warn("I/O error removing cache entry with key " + cacheKey);
}
}
}
}
@Override
public void flushInvalidatedCacheEntriesFor(final HttpHost host, final HttpRequest request, final HttpResponse response) {
if (log.isDebugEnabled()) {
log.debug("Flush cache entries: " + host + "; " + new RequestLine(request) + " " + new StatusLine(response));
}
if (!StandardMethods.isSafe(request.getMethod())) {
cacheInvalidator.flushInvalidatedCacheEntries(host, request, response, cacheKeyGenerator, storage);
}
}
@Override
public void flushInvalidatedCacheEntriesFor(final HttpHost host, final HttpRequest request) {
if (log.isDebugEnabled()) {
log.debug("Flush invalidated cache entries: " + host + "; " + new RequestLine(request));
}
cacheInvalidator.flushInvalidatedCacheEntries(host, request, cacheKeyGenerator, storage);
}
void storeInCache(
final HttpHost target, final HttpRequest request, final HttpCacheEntry entry) throws ResourceIOException {
final String cacheKey,
final HttpHost host,
final HttpRequest request,
final HttpCacheEntry entry) {
if (entry.hasVariants()) {
storeVariantEntry(target, request, entry);
storeVariantEntry(cacheKey, host, request, entry);
} else {
storeNonVariantEntry(target, request, entry);
storeEntry(cacheKey, entry);
}
}
void storeNonVariantEntry(
final HttpHost target, final HttpRequest req, final HttpCacheEntry entry) throws ResourceIOException {
final String uri = cacheKeyGenerator.generateKey(target, req);
storage.putEntry(uri, entry);
void storeEntry(final String cacheKey, final HttpCacheEntry entry) {
try {
storage.putEntry(cacheKey, entry);
} catch (final ResourceIOException ex) {
if (log.isWarnEnabled()) {
log.warn("I/O error storing cache entry with key " + cacheKey);
}
}
}
void storeVariantEntry(
final HttpHost target,
final String cacheKey,
final HttpHost host,
final HttpRequest req,
final HttpCacheEntry entry) throws ResourceIOException {
final String parentCacheKey = cacheKeyGenerator.generateKey(target, req);
final HttpCacheEntry entry) {
final String variantKey = cacheKeyGenerator.generateVariantKey(req, entry);
final String variantURI = cacheKeyGenerator.generateVariantURI(target, req, entry);
storage.putEntry(variantURI, entry);
final String variantCacheKey = cacheKeyGenerator.generateVariantURI(host, req, entry);
storeEntry(variantCacheKey, entry);
try {
storage.updateEntry(parentCacheKey, new HttpCacheCASOperation() {
@Override
public HttpCacheEntry execute(final HttpCacheEntry existing) throws ResourceIOException {
return cacheUpdateHandler.updateParentCacheEntry(req.getRequestUri(), existing, entry, variantKey, variantURI);
}
});
} catch (final HttpCacheUpdateException e) {
log.warn("Could not processChallenge key [" + parentCacheKey + "]", e);
}
}
@Override
public void reuseVariantEntryFor(
final HttpHost target, final HttpRequest req, final Variant variant) throws ResourceIOException {
final String parentCacheKey = cacheKeyGenerator.generateKey(target, req);
final HttpCacheEntry entry = variant.getEntry();
final String variantKey = cacheKeyGenerator.generateVariantKey(req, entry);
final String variantCacheKey = variant.getCacheKey();
try {
storage.updateEntry(parentCacheKey, new HttpCacheCASOperation() {
storage.updateEntry(cacheKey, new HttpCacheCASOperation() {
@Override
public HttpCacheEntry execute(final HttpCacheEntry existing) throws ResourceIOException {
@ -156,41 +162,105 @@ public HttpCacheEntry execute(final HttpCacheEntry existing) throws ResourceIOEx
}
});
} catch (final HttpCacheUpdateException e) {
log.warn("Could not processChallenge key [" + parentCacheKey + "]", e);
} catch (final HttpCacheUpdateException ex) {
if (log.isWarnEnabled()) {
log.warn("Cannot update cache entry with key " + cacheKey);
}
} catch (final ResourceIOException ex) {
if (log.isWarnEnabled()) {
log.warn("I/O error updating cache entry with key " + cacheKey);
}
}
}
@Override
public void reuseVariantEntryFor(
final HttpHost host, final HttpRequest request, final Variant variant) {
if (log.isDebugEnabled()) {
log.debug("Re-use variant entry: " + host + "; " + new RequestLine(request) + " / " + variant);
}
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
final HttpCacheEntry entry = variant.getEntry();
final String variantKey = cacheKeyGenerator.generateVariantKey(request, entry);
final String variantCacheKey = variant.getCacheKey();
try {
storage.updateEntry(cacheKey, new HttpCacheCASOperation() {
@Override
public HttpCacheEntry execute(final HttpCacheEntry existing) throws ResourceIOException {
return cacheUpdateHandler.updateParentCacheEntry(request.getRequestUri(), existing, entry, variantKey, variantCacheKey);
}
});
} catch (final HttpCacheUpdateException ex) {
if (log.isWarnEnabled()) {
log.warn("Cannot update cache entry with key " + cacheKey);
}
} catch (final ResourceIOException ex) {
if (log.isWarnEnabled()) {
log.warn("I/O error updating cache entry with key " + cacheKey);
}
}
}
@Override
public HttpCacheEntry updateCacheEntry(
final HttpHost target,
final HttpHost host,
final HttpRequest request,
final HttpCacheEntry stale,
final HttpResponse originResponse,
final Date requestSent,
final Date responseReceived) throws ResourceIOException {
final HttpCacheEntry updatedEntry = cacheUpdateHandler.updateCacheEntry(
request.getRequestUri(),
stale,
requestSent,
responseReceived,
originResponse);
storeInCache(target, request, updatedEntry);
return updatedEntry;
final Date responseReceived) {
if (log.isDebugEnabled()) {
log.debug("Update cache entry: " + host + "; " + new RequestLine(request));
}
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
try {
final HttpCacheEntry updatedEntry = cacheUpdateHandler.updateCacheEntry(
request.getRequestUri(),
stale,
requestSent,
responseReceived,
originResponse);
storeInCache(cacheKey, host, request, updatedEntry);
return updatedEntry;
} catch (final ResourceIOException ex) {
if (log.isWarnEnabled()) {
log.warn("I/O error updating cache entry with key " + cacheKey);
}
return stale;
}
}
@Override
public HttpCacheEntry updateVariantCacheEntry(final HttpHost target, final HttpRequest request,
final HttpCacheEntry stale, final HttpResponse originResponse,
final Date requestSent, final Date responseReceived, final String cacheKey) throws ResourceIOException {
final HttpCacheEntry updatedEntry = cacheUpdateHandler.updateCacheEntry(
request.getRequestUri(),
stale,
requestSent,
responseReceived,
originResponse);
storage.putEntry(cacheKey, updatedEntry);
return updatedEntry;
public HttpCacheEntry updateVariantCacheEntry(
final HttpHost host,
final HttpRequest request,
final HttpResponse originResponse,
final Variant variant,
final Date requestSent,
final Date responseReceived) {
if (log.isDebugEnabled()) {
log.debug("Update variant cache entry: " + host + "; " + new RequestLine(request) + " / " + variant);
}
final HttpCacheEntry entry = variant.getEntry();
final String cacheKey = variant.getCacheKey();
try {
final HttpCacheEntry updatedEntry = cacheUpdateHandler.updateCacheEntry(
request.getRequestUri(),
entry,
requestSent,
responseReceived,
originResponse);
storeEntry(cacheKey, updatedEntry);
return updatedEntry;
} catch (final ResourceIOException ex) {
if (log.isWarnEnabled()) {
log.warn("I/O error updating cache entry with key " + cacheKey);
}
return entry;
}
}
@Override
@ -200,60 +270,100 @@ public HttpCacheEntry createCacheEntry(
final HttpResponse originResponse,
final ByteArrayBuffer content,
final Date requestSent,
final Date responseReceived) throws ResourceIOException {
final HttpCacheEntry entry = cacheUpdateHandler.createtCacheEntry(request, originResponse, content, requestSent, responseReceived);
storeInCache(host, request, entry);
return entry;
final Date responseReceived) {
if (log.isDebugEnabled()) {
log.debug("Create cache entry: " + host + "; " + new RequestLine(request));
}
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
try {
final HttpCacheEntry entry = cacheUpdateHandler.createtCacheEntry(request, originResponse, content, requestSent, responseReceived);
storeInCache(cacheKey, host, request, entry);
return entry;
} catch (final ResourceIOException ex) {
if (log.isWarnEnabled()) {
log.warn("I/O error creating cache entry with key " + cacheKey);
}
return new HttpCacheEntry(
requestSent,
responseReceived,
originResponse.getCode(),
originResponse.getAllHeaders(),
content != null ? HeapResourceFactory.INSTANCE.generate(null, content.array(), 0, content.length()) : null);
}
}
@Override
public HttpCacheEntry getCacheEntry(final HttpHost host, final HttpRequest request) throws ResourceIOException {
final HttpCacheEntry root = storage.getEntry(cacheKeyGenerator.generateKey(host, request));
public HttpCacheEntry getCacheEntry(final HttpHost host, final HttpRequest request) {
if (log.isDebugEnabled()) {
log.debug("Get cache entry: " + host + "; " + new RequestLine(request));
}
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
final HttpCacheEntry root;
try {
root = storage.getEntry(cacheKey);
} catch (final ResourceIOException ex) {
if (log.isWarnEnabled()) {
log.warn("I/O error retrieving cache entry with key " + cacheKey);
}
return null;
}
if (root == null) {
return null;
}
if (!root.hasVariants()) {
return root;
}
final String variantCacheKey = root.getVariantMap().get(cacheKeyGenerator.generateVariantKey(request, root));
final String variantKey = cacheKeyGenerator.generateVariantKey(request, root);
final String variantCacheKey = root.getVariantMap().get(variantKey);
if (variantCacheKey == null) {
return null;
}
return storage.getEntry(variantCacheKey);
try {
return storage.getEntry(variantCacheKey);
} catch (final ResourceIOException ex) {
if (log.isWarnEnabled()) {
log.warn("I/O error retrieving cache entry with key " + variantCacheKey);
}
return null;
}
}
@Override
public void flushInvalidatedCacheEntriesFor(final HttpHost host,
final HttpRequest request) throws ResourceIOException {
cacheInvalidator.flushInvalidatedCacheEntries(host, request, cacheKeyGenerator, storage);
}
@Override
public Map<String, Variant> getVariantCacheEntriesWithEtags(final HttpHost host, final HttpRequest request)
throws ResourceIOException {
public Map<String, Variant> getVariantCacheEntriesWithEtags(final HttpHost host, final HttpRequest request) {
if (log.isDebugEnabled()) {
log.debug("Get variant cache entries: " + host + "; " + new RequestLine(request));
}
final Map<String,Variant> variants = new HashMap<>();
final HttpCacheEntry root = storage.getEntry(cacheKeyGenerator.generateKey(host, request));
if (root == null || !root.hasVariants()) {
final String cacheKey = cacheKeyGenerator.generateKey(host, request);
final HttpCacheEntry root;
try {
root = storage.getEntry(cacheKey);
} catch (final ResourceIOException ex) {
if (log.isWarnEnabled()) {
log.warn("I/O error retrieving cache entry with key " + cacheKey);
}
return variants;
}
for(final Map.Entry<String, String> variant : root.getVariantMap().entrySet()) {
final String variantCacheKey = variant.getValue();
addVariantWithEtag(variantCacheKey, variants);
if (root != null && root.hasVariants()) {
for(final Map.Entry<String, String> variant : root.getVariantMap().entrySet()) {
final String variantCacheKey = variant.getValue();
try {
final HttpCacheEntry entry = storage.getEntry(variantCacheKey);
if (entry != null) {
final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG);
if (etagHeader != null) {
variants.put(etagHeader.getValue(), new Variant(variantCacheKey, entry));
}
}
} catch (final ResourceIOException ex) {
if (log.isWarnEnabled()) {
log.warn("I/O error retrieving cache entry with key " + variantCacheKey);
}
return variants;
}
}
}
return variants;
}
private void addVariantWithEtag(
final String variantCacheKey, final Map<String, Variant> variants) throws ResourceIOException {
final HttpCacheEntry entry = storage.getEntry(variantCacheKey);
if (entry == null) {
return;
}
final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG);
if (etagHeader == null) {
return;
}
variants.put(etagHeader.getValue(), new Variant(variantCacheKey, entry));
}
}

View File

@ -175,11 +175,11 @@ public ClassicHttpResponse execute(
if (!cacheableRequestPolicy.isServableFromCache(request)) {
log.debug("Request is not servable from cache");
flushEntriesInvalidatedByRequest(target, request);
responseCache.flushInvalidatedCacheEntriesFor(target, request);
return callBackend(target, request, scope, chain);
}
final HttpCacheEntry entry = satisfyFromCache(target, request);
final HttpCacheEntry entry = responseCache.getCacheEntry(target, request);
if (entry == null) {
log.debug("Cache miss");
return handleCacheMiss(target, request, scope, chain);
@ -305,8 +305,7 @@ ClassicHttpResponse revalidateCacheEntry(
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
final HttpCacheEntry updatedEntry = responseCache.updateCacheEntry(
target, request, cacheEntry,
backendResponse, requestDate, responseDate);
target, request, cacheEntry, backendResponse, requestDate, responseDate);
if (suitabilityChecker.isConditional(request)
&& suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
return convert(responseGenerator.generateNotModifiedResponse(updatedEntry));
@ -332,45 +331,6 @@ ClassicHttpResponse revalidateCacheEntry(
}
}
HttpCacheEntry satisfyFromCache(final HttpHost target, final HttpRequest request) {
HttpCacheEntry entry = null;
try {
entry = responseCache.getCacheEntry(target, request);
} catch (final IOException ioe) {
log.warn("Unable to retrieve entries from cache", ioe);
}
return entry;
}
Map<String, Variant> getExistingCacheVariants(final HttpHost target, final HttpRequest request) {
Map<String,Variant> variants = null;
try {
variants = responseCache.getVariantCacheEntriesWithEtags(target, request);
} catch (final IOException ioe) {
log.warn("Unable to retrieve variant entries from cache", ioe);
}
return variants;
}
void flushEntriesInvalidatedByRequest(final HttpHost target, final HttpRequest request) {
try {
responseCache.flushInvalidatedCacheEntriesFor(target, request);
} catch (final IOException ioe) {
log.warn("Unable to flush invalidated entries from cache", ioe);
}
}
void tryToUpdateVariantMap(
final HttpHost target,
final HttpRequest request,
final Variant matchingVariant) {
try {
responseCache.reuseVariantEntryFor(target, request, matchingVariant);
} catch (final IOException ioe) {
log.warn("Could not update cache entry to reuse variant", ioe);
}
}
ClassicHttpResponse handleBackendResponse(
final HttpHost target,
final ClassicHttpRequest request,
@ -388,11 +348,7 @@ ClassicHttpResponse handleBackendResponse(
return cacheAndReturnResponse(target, request, backendResponse, requestDate, responseDate);
} else {
log.debug("Backend response is not cacheable");
try {
responseCache.flushCacheEntriesFor(target, request);
} catch (final IOException ioe) {
log.warn("Unable to flush invalid cache entries", ioe);
}
responseCache.flushCacheEntriesFor(target, request);
}
return backendResponse;
}
@ -447,7 +403,7 @@ private ClassicHttpResponse handleCacheMiss(
return new BasicClassicHttpResponse(HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout");
}
final Map<String, Variant> variants = getExistingCacheVariants(target, request);
final Map<String, Variant> variants = responseCache.getVariantCacheEntriesWithEtags(target, request);
if (variants != null && !variants.isEmpty()) {
return negotiateResponseFromVariants(target, request, scope, chain, variants);
}
@ -492,9 +448,7 @@ ClassicHttpResponse negotiateResponseFromVariants(
return callBackend(target, request, scope, chain);
}
final HttpCacheEntry matchedEntry = matchingVariant.getEntry();
if (revalidationResponseIsTooOld(backendResponse, matchedEntry)) {
if (revalidationResponseIsTooOld(backendResponse, matchingVariant.getEntry())) {
EntityUtils.consume(backendResponse.getEntity());
backendResponse.close();
final ClassicHttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest(request);
@ -503,21 +457,14 @@ ClassicHttpResponse negotiateResponseFromVariants(
recordCacheUpdate(scope.clientContext);
HttpCacheEntry responseEntry = matchedEntry;
try {
responseEntry = responseCache.updateVariantCacheEntry(target, conditionalRequest,
matchedEntry, backendResponse, requestDate, responseDate, matchingVariant.getCacheKey());
} catch (final IOException ioe) {
log.warn("Could not processChallenge cache entry", ioe);
} finally {
backendResponse.close();
}
final HttpCacheEntry responseEntry = responseCache.updateVariantCacheEntry(
target, conditionalRequest, backendResponse, matchingVariant, requestDate, responseDate);
backendResponse.close();
if (shouldSendNotModifiedResponse(request, responseEntry)) {
return convert(responseGenerator.generateNotModifiedResponse(responseEntry));
}
final SimpleHttpResponse resp = responseGenerator.generateResponse(request, responseEntry);
tryToUpdateVariantMap(target, request, matchingVariant);
responseCache.reuseVariantEntryFor(target, request, matchingVariant);
return convert(resp);
} catch (final IOException | RuntimeException ex) {
backendResponse.close();

View File

@ -46,7 +46,7 @@ public class HeapResourceFactory implements ResourceFactory {
@Override
public Resource generate(
final String requestId,
final byte[] content, final int off, final int len) throws ResourceIOException {
final byte[] content, final int off, final int len) {
final byte[] copy = new byte[len];
System.arraycopy(content, off, copy, 0, len);
return new HeapResource(copy);

View File

@ -90,7 +90,7 @@ Cancellable createCacheEntry(
* Update a {@link HttpCacheEntry} using a 304 {@link HttpResponse}.
*/
Cancellable updateCacheEntry(
HttpHost target,
HttpHost host,
HttpRequest request,
HttpCacheEntry stale,
HttpResponse originResponse,
@ -103,13 +103,12 @@ Cancellable updateCacheEntry(
* using a 304 {@link HttpResponse}.
*/
Cancellable updateVariantCacheEntry(
HttpHost target,
HttpHost host,
HttpRequest request,
HttpCacheEntry stale,
HttpResponse originResponse,
Variant variant,
Date requestSent,
Date responseReceived,
String cacheKey,
FutureCallback<HttpCacheEntry> callback);
/**
@ -117,7 +116,7 @@ Cancellable updateVariantCacheEntry(
* requests whose varying headers match those of the given client request.
*/
Cancellable reuseVariantEntryFor(
HttpHost target,
HttpHost host,
HttpRequest req,
Variant variant,
FutureCallback<Boolean> callback);

View File

@ -30,7 +30,6 @@
import java.util.Map;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.ResourceIOException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
@ -44,12 +43,12 @@ interface HttpCache {
/**
* Clear all matching {@link HttpCacheEntry}s.
*/
void flushCacheEntriesFor(HttpHost host, HttpRequest request) throws ResourceIOException;
void flushCacheEntriesFor(HttpHost host, HttpRequest request);
/**
* Clear invalidated matching {@link HttpCacheEntry}s
*/
void flushInvalidatedCacheEntriesFor(HttpHost host, HttpRequest request) throws ResourceIOException;
void flushInvalidatedCacheEntriesFor(HttpHost host, HttpRequest request);
/** Clear any entries that may be invalidated by the given response to
* a particular request.
@ -59,13 +58,13 @@ interface HttpCache {
/**
* Retrieve matching {@link HttpCacheEntry} from the cache if it exists.
*/
HttpCacheEntry getCacheEntry(HttpHost host, HttpRequest request) throws ResourceIOException;
HttpCacheEntry getCacheEntry(HttpHost host, HttpRequest request);
/**
* Retrieve all variants from the cache, if there are no variants then an empty
* {@link Map} is returned
*/
Map<String,Variant> getVariantCacheEntriesWithEtags(HttpHost host, HttpRequest request) throws ResourceIOException;
Map<String,Variant> getVariantCacheEntriesWithEtags(HttpHost host, HttpRequest request);
/**
* Store a {@link HttpResponse} in the cache if possible, and return
@ -76,38 +75,37 @@ HttpCacheEntry createCacheEntry(
HttpResponse originResponse,
ByteArrayBuffer content,
Date requestSent,
Date responseReceived) throws ResourceIOException;
Date responseReceived);
/**
* Update a {@link HttpCacheEntry} using a 304 {@link HttpResponse}.
*/
HttpCacheEntry updateCacheEntry(
HttpHost target,
HttpHost host,
HttpRequest request,
HttpCacheEntry stale,
HttpResponse originResponse,
Date requestSent,
Date responseReceived) throws ResourceIOException;
Date responseReceived);
/**
* Update a specific {@link HttpCacheEntry} representing a cached variant
* using a 304 {@link HttpResponse}.
*/
HttpCacheEntry updateVariantCacheEntry(
HttpHost target,
HttpHost host,
HttpRequest request,
HttpCacheEntry stale,
HttpResponse originResponse,
Variant variant,
Date requestSent,
Date responseReceived,
String cacheKey) throws ResourceIOException;
Date responseReceived);
/**
* Specifies cache should reuse the given cached variant to satisfy
* requests whose varying headers match those of the given client request.
*/
void reuseVariantEntryFor(
HttpHost target,
HttpRequest req,
Variant variant) throws ResourceIOException;
HttpHost host,
HttpRequest request,
Variant variant);
}

View File

@ -46,4 +46,10 @@ public String getCacheKey() {
public HttpCacheEntry getEntry() {
return entry;
}
@Override
public String toString() {
return cacheKey;
}
}

View File

@ -71,7 +71,7 @@ public void setUp() throws Exception {
public void testDoNotFlushCacheEntriesOnGet() throws Exception {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req = new HttpGet("/bar");
final String key = (new CacheKeyGenerator()).generateKey(host, req);
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
backing.map.put(key, entry);
@ -85,7 +85,7 @@ public void testDoNotFlushCacheEntriesOnGet() throws Exception {
public void testDoNotFlushCacheEntriesOnHead() throws Exception {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req = new HttpHead("/bar");
final String key = (new CacheKeyGenerator()).generateKey(host, req);
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
backing.map.put(key, entry);
@ -99,7 +99,7 @@ public void testDoNotFlushCacheEntriesOnHead() throws Exception {
public void testDoNotFlushCacheEntriesOnOptions() throws Exception {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req = new HttpOptions("/bar");
final String key = (new CacheKeyGenerator()).generateKey(host, req);
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
backing.map.put(key, entry);
@ -113,7 +113,7 @@ public void testDoNotFlushCacheEntriesOnOptions() throws Exception {
public void testDoNotFlushCacheEntriesOnTrace() throws Exception {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req = new HttpTrace("/bar");
final String key = (new CacheKeyGenerator()).generateKey(host, req);
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
backing.map.put(key, entry);
@ -131,7 +131,7 @@ public void testFlushContentLocationEntryIfUnSafeRequest()
final HttpResponse resp = HttpTestUtils.make200Response();
resp.setHeader("Content-Location", "/bar");
resp.setHeader(HeaderConstants.ETAG, "\"etag\"");
final String key = (new CacheKeyGenerator()).generateKey(host, new HttpGet("/bar"));
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, new HttpGet("/bar"));
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
new BasicHeader("Date", DateUtils.formatDate(new Date())),
@ -152,7 +152,7 @@ public void testDoNotFlushContentLocationEntryIfSafeRequest()
final HttpRequest req = new HttpGet("/foo");
final HttpResponse resp = HttpTestUtils.make200Response();
resp.setHeader("Content-Location", "/bar");
final String key = (new CacheKeyGenerator()).generateKey(host, new HttpGet("/bar"));
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, new HttpGet("/bar"));
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
new BasicHeader("Date", DateUtils.formatDate(new Date())),
@ -170,7 +170,7 @@ public void testDoNotFlushContentLocationEntryIfSafeRequest()
public void testCanFlushCacheEntriesAtUri() throws Exception {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req = new HttpDelete("/bar");
final String key = (new CacheKeyGenerator()).generateKey(host, req);
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
backing.map.put(key, entry);
@ -186,9 +186,9 @@ public void testStoreInCachePutsNonVariantEntryInPlace() throws Exception {
assertFalse(entry.hasVariants());
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req = new HttpGet("http://foo.example.com/bar");
final String key = (new CacheKeyGenerator()).generateKey(host, req);
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
impl.storeInCache(host, req, entry);
impl.storeInCache(key, host, req, entry);
assertSame(entry, backing.map.get(key));
}
@ -207,7 +207,7 @@ public void testGetCacheEntryFetchesFromCacheOnCacheHitIfNoVariants() throws Exc
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
final String key = (new CacheKeyGenerator()).generateKey(host, request);
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, request);
backing.map.put(key,entry);