You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2018/01/01 16:01:43 UTC
[1/3] httpcomponents-client git commit: HttCache and HttpAsyncCache
implementation to treat ResourceIOExceptions as non-fatal and log
ResourceIOExceptions as warnings
Repository: httpcomponents-client
Updated Branches:
refs/heads/master 13acc440e -> 194e4f528
HttCache and HttpAsyncCache implementation to treat ResourceIOExceptions as non-fatal and log ResourceIOExceptions as warnings
Project: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/commit/77703a7e
Tree: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/tree/77703a7e
Diff: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/diff/77703a7e
Branch: refs/heads/master
Commit: 77703a7eef7e1131cbb1d495366adfa2cc5c0233
Parents: 13acc44
Author: Oleg Kalnichevski <ol...@apache.org>
Authored: Sat Dec 30 00:08:47 2017 +0100
Committer: Oleg Kalnichevski <ol...@apache.org>
Committed: Sat Dec 30 12:31:46 2017 +0100
----------------------------------------------------------------------
.../http/impl/cache/AsyncCachingExec.java | 6 +-
.../http/impl/cache/BasicHttpAsyncCache.java | 312 ++++++++++++++++---
.../client5/http/impl/cache/BasicHttpCache.java | 278 ++++++++++++-----
.../hc/client5/http/impl/cache/CachingExec.java | 73 +----
.../http/impl/cache/HeapResourceFactory.java | 2 +-
.../client5/http/impl/cache/HttpAsyncCache.java | 9 +-
.../hc/client5/http/impl/cache/HttpCache.java | 28 +-
.../hc/client5/http/impl/cache/Variant.java | 6 +
.../http/impl/cache/TestBasicHttpCache.java | 20 +-
9 files changed, 504 insertions(+), 230 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/77703a7e/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
index c429868..1332f74 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
@@ -826,8 +826,7 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
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 class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
future.setDependency(responseCache.updateVariantCacheEntry(
target,
conditionalRequest,
- matchedEntry,
backendResponse,
+ matchingVariant,
requestDate,
responseDateRef.get(),
- matchingVariant.getCacheKey(),
new FutureCallback<HttpCacheEntry>() {
@Override
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/77703a7e/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicHttpAsyncCache.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicHttpAsyncCache.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicHttpAsyncCache.java
index 853f72f..55337dc 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicHttpAsyncCache.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicHttpAsyncCache.java
@@ -29,6 +29,7 @@ package org.apache.hc.client5.http.impl.cache;
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.HttpAsyncCacheInvalidator;
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.Header;
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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
@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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
@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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
@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);
+ return storage.putEntry(cacheKey, entry, 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 storing cache entry with key " + cacheKey);
+ }
+ callback.completed(Boolean.TRUE);
+ } else {
+ callback.failed(ex);
+ }
+ }
+
+ @Override
+ public void cancelled() {
+ callback.cancelled();
+ }
+
+ });
}
Cancellable storeVariantEntry(
- final HttpHost target,
+ final String cacheKey,
+ final HttpHost host,
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>() {
+ 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(parentCacheKey, new HttpCacheCASOperation() {
+ 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 HttpCacheEntry execute(final HttpCacheEntry existing) throws ResourceIOException {
- return cacheUpdateHandler.updateParentCacheEntry(req.getRequestUri(), existing, entry, variantKey, variantURI);
- }
+ @Override
+ public void cancelled() {
+ callback.cancelled();
+ }
- }, callback);
+ });
}
@Override
public void failed(final Exception ex) {
- callback.failed(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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
@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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
});
} 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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
});
} 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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
});
} 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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
@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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
@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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
@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 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
@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
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/77703a7e/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicHttpCache.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicHttpCache.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicHttpCache.java
index fc4acef..6c8463a 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicHttpCache.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicHttpCache.java
@@ -43,19 +43,21 @@ import org.apache.hc.core5.http.Header;
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,110 +89,178 @@ class BasicHttpCache implements HttpCache {
}
@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() {
+ storage.updateEntry(cacheKey, new HttpCacheCASOperation() {
@Override
public HttpCacheEntry execute(final HttpCacheEntry existing) throws ResourceIOException {
- return cacheUpdateHandler.updateParentCacheEntry(req.getRequestUri(), existing, entry, variantKey, variantURI);
+ return cacheUpdateHandler.updateParentCacheEntry(req.getRequestUri(), existing, entry, variantKey, variantCacheKey);
}
});
- } 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 target, final HttpRequest req, final Variant variant) throws ResourceIOException {
- final String parentCacheKey = cacheKeyGenerator.generateKey(target, req);
+ 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(req, entry);
+ final String variantKey = cacheKeyGenerator.generateVariantKey(request, 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 {
- return cacheUpdateHandler.updateParentCacheEntry(req.getRequestUri(), existing, entry, variantKey, variantCacheKey);
+ return cacheUpdateHandler.updateParentCacheEntry(request.getRequestUri(), existing, entry, variantKey, variantCacheKey);
}
});
- } 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 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 @@ class BasicHttpCache implements HttpCache {
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);
- }
-
- @Override
- public void flushInvalidatedCacheEntriesFor(final HttpHost host,
- final HttpRequest request) throws ResourceIOException {
- cacheInvalidator.flushInvalidatedCacheEntries(host, request, cacheKeyGenerator, storage);
+ 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 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));
- }
-
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/77703a7e/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
index 92ed4e8..57de39c 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
@@ -175,11 +175,11 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
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 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
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 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
}
}
- 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 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
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 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
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 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
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 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
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();
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/77703a7e/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HeapResourceFactory.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HeapResourceFactory.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HeapResourceFactory.java
index d407a56..bb656e0 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HeapResourceFactory.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HeapResourceFactory.java
@@ -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);
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/77703a7e/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HttpAsyncCache.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HttpAsyncCache.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HttpAsyncCache.java
index 7a8b08b..a83cb63 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HttpAsyncCache.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HttpAsyncCache.java
@@ -90,7 +90,7 @@ interface HttpAsyncCache {
* 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 @@ interface HttpAsyncCache {
* 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 @@ interface HttpAsyncCache {
* requests whose varying headers match those of the given client request.
*/
Cancellable reuseVariantEntryFor(
- HttpHost target,
+ HttpHost host,
HttpRequest req,
Variant variant,
FutureCallback<Boolean> callback);
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/77703a7e/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HttpCache.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HttpCache.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HttpCache.java
index 7bcdd82..756f6f3 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HttpCache.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HttpCache.java
@@ -30,7 +30,6 @@ import java.util.Date;
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 @@ interface HttpCache {
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);
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/77703a7e/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/Variant.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/Variant.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/Variant.java
index 5f94c60..c5fd4c1 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/Variant.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/Variant.java
@@ -46,4 +46,10 @@ class Variant {
public HttpCacheEntry getEntry() {
return entry;
}
+
+ @Override
+ public String toString() {
+ return cacheKey;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/77703a7e/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestBasicHttpCache.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestBasicHttpCache.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestBasicHttpCache.java
index 45b7cdd..1b21f59 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestBasicHttpCache.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestBasicHttpCache.java
@@ -71,7 +71,7 @@ public class TestBasicHttpCache {
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 class TestBasicHttpCache {
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 class TestBasicHttpCache {
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 class TestBasicHttpCache {
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 class TestBasicHttpCache {
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 class TestBasicHttpCache {
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 class TestBasicHttpCache {
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 class TestBasicHttpCache {
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 class TestBasicHttpCache {
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);
[3/3] httpcomponents-client git commit: Corrected handling of request
with non-repeatable by caching request execution interceptors
Posted by ol...@apache.org.
Corrected handling of request with non-repeatable by caching request execution interceptors
Project: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/commit/194e4f52
Tree: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/tree/194e4f52
Diff: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/diff/194e4f52
Branch: refs/heads/master
Commit: 194e4f528973a7139c178f198fde644d4ea03cb3
Parents: c607197
Author: Oleg Kalnichevski <ol...@apache.org>
Authored: Mon Jan 1 16:52:50 2018 +0100
Committer: Oleg Kalnichevski <ol...@apache.org>
Committed: Mon Jan 1 16:52:50 2018 +0100
----------------------------------------------------------------------
.../org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java | 5 +++--
.../java/org/apache/hc/client5/http/impl/cache/CachingExec.java | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/194e4f52/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
index 179fbcd..c812b12 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
@@ -711,7 +711,8 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
final Date responseDate1 = getCurrentDate();
final AsyncExecCallback callback1;
- if (revalidationResponseIsTooOld(backendResponse1, cacheEntry)) {
+ if (revalidationResponseIsTooOld(backendResponse1, cacheEntry)
+ && (entityProducer == null || entityProducer.isRepeatable())) {
final HttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest(
scope.originalRequest);
@@ -805,7 +806,7 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
@Override
public void completed(final Map<String, Variant> variants) {
- if (variants != null && !variants.isEmpty()) {
+ if (variants != null && !variants.isEmpty() && (entityProducer == null || entityProducer.isRepeatable())) {
negotiateResponseFromVariants(target, request, entityProducer, scope, chain, asyncExecCallback, variants);
} else {
callBackend(target, request, entityProducer, scope, chain, asyncExecCallback);
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/194e4f52/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
index 724fa8a..7fb7e42 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
@@ -447,7 +447,8 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
return callBackend(target, request, scope, chain);
}
- if (revalidationResponseIsTooOld(backendResponse, matchingVariant.getEntry())) {
+ if (revalidationResponseIsTooOld(backendResponse, matchingVariant.getEntry())
+ && (request.getEntity() == null || request.getEntity().isRepeatable())) {
EntityUtils.consume(backendResponse.getEntity());
backendResponse.close();
final ClassicHttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest(request);
[2/3] httpcomponents-client git commit: Code cleanup in
AsyncCachingExec: async code made more consistent with that of its classic
counterpart and hopefully a bit more readable
Posted by ol...@apache.org.
Code cleanup in AsyncCachingExec: async code made more consistent with that of its classic counterpart and hopefully a bit more readable
Project: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/commit/c607197f
Tree: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/tree/c607197f
Diff: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/diff/c607197f
Branch: refs/heads/master
Commit: c607197fd00a26bc1f946ebdeb865c8777c23540
Parents: 77703a7
Author: Oleg Kalnichevski <ol...@apache.org>
Authored: Sun Dec 31 14:08:46 2017 +0100
Committer: Oleg Kalnichevski <ol...@apache.org>
Committed: Mon Jan 1 16:45:46 2018 +0100
----------------------------------------------------------------------
.../http/impl/cache/AsyncCachingExec.java | 953 ++++++++++---------
.../hc/client5/http/impl/cache/CachingExec.java | 3 +-
2 files changed, 522 insertions(+), 434 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/c607197f/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
index 1332f74..179fbcd 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java
@@ -143,6 +143,34 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
}
}
+ static class AsyncExecCallbackWrapper implements AsyncExecCallback {
+
+ private final AsyncExecCallback asyncExecCallback;
+ private final Runnable command;
+
+ AsyncExecCallbackWrapper(final AsyncExecCallback asyncExecCallback, final Runnable command) {
+ this.asyncExecCallback = asyncExecCallback;
+ this.command = command;
+ }
+
+ @Override
+ public AsyncDataConsumer handleResponse(
+ final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
+ return null;
+ }
+
+ @Override
+ public void completed() {
+ command.run();
+ }
+
+ @Override
+ public void failed(final Exception cause) {
+ asyncExecCallback.failed(cause);
+ }
+
+ }
+
@Override
public void execute(
final HttpRequest request,
@@ -230,10 +258,17 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
}
}
- interface InternalCallback extends AsyncExecCallback {
-
- boolean cacheResponse(HttpResponse backendResponse) throws HttpException, IOException;
-
+ void chainProceed(
+ final HttpRequest request,
+ final AsyncEntityProducer entityProducer,
+ final AsyncExecChain.Scope scope,
+ final AsyncExecChain chain,
+ final AsyncExecCallback asyncExecCallback) {
+ try {
+ chain.proceed(request, entityProducer, scope, asyncExecCallback);
+ } catch (final HttpException | IOException ex) {
+ asyncExecCallback.failed(ex);
+ }
}
void callBackend(
@@ -243,273 +278,290 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
final AsyncExecChain.Scope scope,
final AsyncExecChain chain,
final AsyncExecCallback asyncExecCallback) {
- callBackendInternal(target, request, entityProducer, scope, chain, new InternalCallback() {
-
- @Override
- public boolean cacheResponse(final HttpResponse backendResponse) {
- return true;
- }
+ log.debug("Calling the backend");
+ final Date requestDate = getCurrentDate();
+ final AtomicReference<AsyncExecCallback> callbackRef = new AtomicReference<>();
+ chainProceed(request, entityProducer, scope, chain, new AsyncExecCallback() {
@Override
public AsyncDataConsumer handleResponse(
- final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
- return asyncExecCallback.handleResponse(response, entityDetails);
+ final HttpResponse backendResponse, final EntityDetails entityDetails) throws HttpException, IOException {
+ final Date responseDate = getCurrentDate();
+ backendResponse.addHeader("Via", generateViaHeader(backendResponse));
+
+ final AsyncExecCallback callback = new BackendResponseHandler(target, request, requestDate, responseDate, scope, asyncExecCallback);
+ callbackRef.set(callback);
+ return callback.handleResponse(backendResponse, entityDetails);
}
@Override
public void completed() {
- asyncExecCallback.completed();
+ final AsyncExecCallback callback = callbackRef.getAndSet(null);
+ if (callback != null) {
+ callback.completed();
+ } else {
+ asyncExecCallback.completed();
+ }
}
@Override
public void failed(final Exception cause) {
- asyncExecCallback.failed(cause);
+ final AsyncExecCallback callback = callbackRef.getAndSet(null);
+ if (callback != null) {
+ callback.failed(cause);
+ } else {
+ asyncExecCallback.failed(cause);
+ }
}
});
}
- static class ResponseState {
-
- final HttpResponse backendResponse;
- final Date responseDate;
- final ByteArrayBuffer buffer;
-
- ResponseState(final HttpResponse backendResponse, final Date responseDate, final ByteArrayBuffer buffer) {
+ class CachingAsyncDataConsumer implements AsyncDataConsumer {
+
+ private final AsyncExecCallback fallback;
+ private final HttpResponse backendResponse;
+ private final EntityDetails entityDetails;
+ private final Date responseDate;
+ private final AtomicBoolean writtenThrough;
+ private final AtomicReference<ByteArrayBuffer> bufferRef;
+ private final AtomicReference<AsyncDataConsumer> dataConsumerRef;
+
+ CachingAsyncDataConsumer(
+ final AsyncExecCallback fallback,
+ final HttpResponse backendResponse,
+ final EntityDetails entityDetails,
+ final Date responseDate) {
+ this.fallback = fallback;
this.backendResponse = backendResponse;
this.responseDate = responseDate;
- this.buffer = buffer;
+ this.entityDetails = entityDetails;
+ this.writtenThrough = new AtomicBoolean(false);
+ this.bufferRef = new AtomicReference<>(entityDetails != null ? new ByteArrayBuffer(1024) : null);
+ this.dataConsumerRef = new AtomicReference<>();
}
- }
-
- void callBackendInternal(
- final HttpHost target,
- final HttpRequest request,
- final AsyncEntityProducer entityProducer,
- final AsyncExecChain.Scope scope,
- final AsyncExecChain chain,
- final InternalCallback asyncExecCallback) {
- log.debug("Calling the backend");
- final ComplexFuture<?> future = scope.future;
- final Date requestDate = getCurrentDate();
- try {
- chain.proceed(request, entityProducer, scope, new AsyncExecCallback() {
+ @Override
+ public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
+ final AsyncDataConsumer dataConsumer = dataConsumerRef.get();
+ if (dataConsumer != null) {
+ dataConsumer.updateCapacity(capacityChannel);
+ } else {
+ capacityChannel.update(Integer.MAX_VALUE);
+ }
+ }
- private final AtomicReference<ResponseState> responseStateRef = new AtomicReference<>();
- private final AtomicReference<AsyncDataConsumer> dataConsumerRef = new AtomicReference<>();
+ @Override
+ public final int consume(final ByteBuffer src) throws IOException {
+ final ByteArrayBuffer buffer = bufferRef.get();
+ if (buffer != null) {
+ if (src.hasArray()) {
+ buffer.append(src.array(), src.arrayOffset() + src.position(), src.remaining());
+ } else {
+ while (src.hasRemaining()) {
+ buffer.append(src.get());
+ }
+ }
+ if (buffer.length() > cacheConfig.getMaxObjectSize()) {
+ log.debug("Backend response content length exceeds maximum");
+ // Over the max limit. Stop buffering and forward the response
+ // along with all the data buffered so far to the caller.
+ bufferRef.set(null);
+ try {
+ final AsyncDataConsumer dataConsumer = fallback.handleResponse(backendResponse, entityDetails);
+ if (dataConsumer != null) {
+ dataConsumerRef.set(dataConsumer);
+ writtenThrough.set(true);
+ return dataConsumer.consume(ByteBuffer.wrap(buffer.array(), 0, buffer.length()));
+ }
+ } catch (final HttpException ex) {
+ fallback.failed(ex);
+ }
+ }
+ return Integer.MAX_VALUE;
+ } else {
+ final AsyncDataConsumer dataConsumer = dataConsumerRef.get();
+ if (dataConsumer != null) {
+ return dataConsumer.consume(src);
+ } else {
+ return Integer.MAX_VALUE;
+ }
+ }
+ }
- @Override
- public AsyncDataConsumer handleResponse(
- final HttpResponse backendResponse,
- final EntityDetails entityDetails) throws HttpException, IOException {
- final Date responseDate = getCurrentDate();
- backendResponse.addHeader("Via", generateViaHeader(backendResponse));
+ @Override
+ public final void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
+ final AsyncDataConsumer dataConsumer = dataConsumerRef.getAndSet(null);
+ if (dataConsumer != null) {
+ dataConsumer.streamEnd(trailers);
+ }
+ }
- responseCompliance.ensureProtocolCompliance(scope.originalRequest, request, backendResponse);
- responseCache.flushInvalidatedCacheEntriesFor(target, request, backendResponse, new FutureCallback<Boolean>() {
+ @Override
+ public void releaseResources() {
+ final AsyncDataConsumer dataConsumer = dataConsumerRef.getAndSet(null);
+ if (dataConsumer != null) {
+ dataConsumer.releaseResources();
+ }
+ }
- @Override
- public void completed(final Boolean result) {
- }
+ };
+
+ class BackendResponseHandler implements AsyncExecCallback {
+
+ private final HttpHost target;
+ private final HttpRequest request;
+ private final Date requestDate;
+ private final Date responseDate;
+ private final AsyncExecChain.Scope scope;
+ private final AsyncExecCallback asyncExecCallback;
+ private final AtomicReference<CachingAsyncDataConsumer> cachingConsumerRef;
+
+ BackendResponseHandler(
+ final HttpHost target,
+ final HttpRequest request,
+ final Date requestDate,
+ final Date responseDate,
+ final AsyncExecChain.Scope scope,
+ final AsyncExecCallback asyncExecCallback) {
+ this.target = target;
+ this.request = request;
+ this.requestDate = requestDate;
+ this.responseDate = responseDate;
+ this.scope = scope;
+ this.asyncExecCallback = asyncExecCallback;
+ this.cachingConsumerRef = new AtomicReference<>();
+ }
- @Override
- public void failed(final Exception ex) {
- log.warn("Unable to flush invalidated entries from cache", ex);
- }
+ @Override
+ public AsyncDataConsumer handleResponse(
+ final HttpResponse backendResponse,
+ final EntityDetails entityDetails) throws HttpException, IOException {
+ responseCompliance.ensureProtocolCompliance(scope.originalRequest, request, backendResponse);
+ responseCache.flushInvalidatedCacheEntriesFor(target, request, backendResponse, new FutureCallback<Boolean>() {
- @Override
- public void cancelled() {
- }
+ @Override
+ public void completed(final Boolean result) {
+ }
- });
- final boolean cacheable = asyncExecCallback.cacheResponse(backendResponse)
- && responseCachingPolicy.isResponseCacheable(request, backendResponse);
- if (cacheable) {
- responseStateRef.set(new ResponseState(
- backendResponse,
- responseDate,
- entityDetails != null ? new ByteArrayBuffer(1024) : null));
- storeRequestIfModifiedSinceFor304Response(request, backendResponse);
- } else {
- log.debug("Backend response is not cacheable");
- responseCache.flushCacheEntriesFor(target, request, new FutureCallback<Boolean>() {
+ @Override
+ public void failed(final Exception ex) {
+ log.warn("Unable to flush invalidated entries from cache", ex);
+ }
- @Override
- public void completed(final Boolean result) {
- }
+ @Override
+ public void cancelled() {
+ }
- @Override
- public void failed(final Exception ex) {
- log.warn("Unable to flush invalidated entries from cache", ex);
- }
+ });
+ final boolean cacheable = responseCachingPolicy.isResponseCacheable(request, backendResponse);
+ if (cacheable) {
+ cachingConsumerRef.set(new CachingAsyncDataConsumer(asyncExecCallback, backendResponse, entityDetails, responseDate));
+ storeRequestIfModifiedSinceFor304Response(request, backendResponse);
+ } else {
+ log.debug("Backend response is not cacheable");
+ responseCache.flushCacheEntriesFor(target, request, new FutureCallback<Boolean>() {
+
+ @Override
+ public void completed(final Boolean result) {
+ }
- @Override
- public void cancelled() {
- }
+ @Override
+ public void failed(final Exception ex) {
+ log.warn("Unable to flush invalidated entries from cache", ex);
+ }
- });
+ @Override
+ public void cancelled() {
}
- if (responseStateRef.get() != null) {
- log.debug("Caching backend response");
- if (entityDetails == null) {
- scope.execRuntime.releaseConnection();
- return null;
- } else {
- return new AsyncDataConsumer() {
- @Override
- public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
- final AsyncDataConsumer dataConsumer = dataConsumerRef.get();
- if (dataConsumer != null) {
- dataConsumer.updateCapacity(capacityChannel);
- } else {
- capacityChannel.update(Integer.MAX_VALUE);
- }
- }
+ });
+ }
+ final CachingAsyncDataConsumer cachingDataConsumer = cachingConsumerRef.get();
+ if (cachingDataConsumer != null) {
+ log.debug("Caching backend response");
+ return cachingDataConsumer;
+ } else {
+ return asyncExecCallback.handleResponse(backendResponse, entityDetails);
+ }
+ }
- @Override
- public final int consume(final ByteBuffer src) throws IOException {
- final ResponseState responseState = responseStateRef.get();
- if (responseState != null) {
- final ByteArrayBuffer buffer = responseState.buffer;
- if (src.hasArray()) {
- buffer.append(src.array(), src.arrayOffset() + src.position(), src.remaining());
- } else {
- while (src.hasRemaining()) {
- buffer.append(src.get());
- }
- }
- if (buffer.length() > cacheConfig.getMaxObjectSize()) {
- log.debug("Backend response content length exceeds maximum");
- // Over the max limit. Stop buffering and forward the response
- // along with all the data buffered so far to the caller.
- responseStateRef.set(null);
+ @Override
+ public void completed() {
+ final ComplexFuture<?> future = scope.future;
+ final CachingAsyncDataConsumer cachingDataConsumer = cachingConsumerRef.getAndSet(null);
+ if (cachingDataConsumer != null && !cachingDataConsumer.writtenThrough.get()) {
+ future.setDependency(responseCache.getCacheEntry(target, request, new FutureCallback<HttpCacheEntry>() {
+
+ @Override
+ public void completed(final HttpCacheEntry existingEntry) {
+ final HttpResponse backendResponse = cachingDataConsumer.backendResponse;
+ if (DateUtils.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
+ try {
+ final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, existingEntry);
+ triggerResponse(cacheResponse, scope, asyncExecCallback);
+ } catch (final ResourceIOException ex) {
+ asyncExecCallback.failed(ex);
+ }
+ } else {
+ final Date responseDate = cachingDataConsumer.responseDate;
+ final ByteArrayBuffer buffer = cachingDataConsumer.bufferRef.getAndSet(null);
+ future.setDependency(responseCache.createCacheEntry(
+ target,
+ request,
+ backendResponse,
+ buffer,
+ requestDate,
+ responseDate,
+ new FutureCallback<HttpCacheEntry>() {
+
+ @Override
+ public void completed(final HttpCacheEntry newEntry) {
+ log.debug("Backend response successfully cached");
try {
- final AsyncDataConsumer dataConsumer = asyncExecCallback.handleResponse(
- backendResponse, entityDetails);
- if (dataConsumer != null) {
- dataConsumerRef.set(dataConsumer);
- return dataConsumer.consume(ByteBuffer.wrap(buffer.array(), 0, buffer.length()));
- }
- } catch (final HttpException ex) {
+ final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, newEntry);
+ triggerResponse(cacheResponse, scope, asyncExecCallback);
+ } catch (final ResourceIOException ex) {
asyncExecCallback.failed(ex);
}
}
- return Integer.MAX_VALUE;
- } else {
- final AsyncDataConsumer dataConsumer = dataConsumerRef.get();
- if (dataConsumer != null) {
- return dataConsumer.consume(src);
- } else {
- return Integer.MAX_VALUE;
+
+ @Override
+ public void failed(final Exception ex) {
+ asyncExecCallback.failed(ex);
}
- }
- }
- @Override
- public final void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
- scope.execRuntime.releaseConnection();
- final AsyncDataConsumer dataConsumer = dataConsumerRef.getAndSet(null);
- if (dataConsumer != null) {
- dataConsumer.streamEnd(trailers);
- }
- }
+ @Override
+ public void cancelled() {
+ asyncExecCallback.failed(new InterruptedIOException());
+ }
- @Override
- public void releaseResources() {
- final AsyncDataConsumer dataConsumer = dataConsumerRef.getAndSet(null);
- if (dataConsumer != null) {
- dataConsumer.releaseResources();
- }
- }
+ }));
- };
}
- } else {
- return asyncExecCallback.handleResponse(backendResponse, entityDetails);
}
- }
-
- @Override
- public void completed() {
- final ResponseState responseState = responseStateRef.getAndSet(null);
- if (responseState != null) {
- future.setDependency(responseCache.getCacheEntry(target, request, new FutureCallback<HttpCacheEntry>() {
-
- @Override
- public void completed(final HttpCacheEntry existingEntry) {
- final HttpResponse backendResponse = responseState.backendResponse;
- if (DateUtils.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
- try {
- final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, existingEntry);
- triggerResponse(cacheResponse, scope, asyncExecCallback);
- } catch (final ResourceIOException ex) {
- asyncExecCallback.failed(ex);
- }
- } else {
- final Date responseDate = responseState.responseDate;
- final ByteArrayBuffer buffer = responseState.buffer;
- future.setDependency(responseCache.createCacheEntry(
- target,
- request,
- backendResponse,
- buffer,
- requestDate,
- responseDate,
- new FutureCallback<HttpCacheEntry>() {
-
- @Override
- public void completed(final HttpCacheEntry newEntry) {
- log.debug("Backend response successfully cached");
- try {
- final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, newEntry);
- triggerResponse(cacheResponse, scope, asyncExecCallback);
- } catch (final ResourceIOException ex) {
- asyncExecCallback.failed(ex);
- }
- }
-
- @Override
- public void failed(final Exception ex) {
- asyncExecCallback.failed(ex);
- }
-
- @Override
- public void cancelled() {
- asyncExecCallback.failed(new InterruptedIOException());
- }
-
- }));
-
- }
- }
-
- @Override
- public void failed(final Exception cause) {
- asyncExecCallback.failed(cause);
- }
- @Override
- public void cancelled() {
- asyncExecCallback.failed(new InterruptedIOException());
- }
+ @Override
+ public void failed(final Exception cause) {
+ asyncExecCallback.failed(cause);
+ }
- }));
- } else {
- asyncExecCallback.completed();
+ @Override
+ public void cancelled() {
+ asyncExecCallback.failed(new InterruptedIOException());
}
- }
- @Override
- public void failed(final Exception cause) {
- asyncExecCallback.failed(cause);
- }
+ }));
+ } else {
+ asyncExecCallback.completed();
+ }
+ }
- });
- } catch (final HttpException | IOException ex) {
- asyncExecCallback.failed(ex);
+ @Override
+ public void failed(final Exception cause) {
+ asyncExecCallback.failed(cause);
}
+
}
private void handleCacheHit(
@@ -563,170 +615,172 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
final AsyncExecChain chain,
final AsyncExecCallback asyncExecCallback,
final HttpCacheEntry cacheEntry) {
-
- final ComplexFuture<?> future = scope.future;
final Date requestDate = getCurrentDate();
- final InternalCallback internalCallback = new InternalCallback() {
+ final HttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequest(scope.originalRequest, cacheEntry);
+ chainProceed(conditionalRequest, entityProducer, scope, chain, new AsyncExecCallback() {
+
+ final AtomicReference<AsyncExecCallback> callbackRef = new AtomicReference<>();
+
+ void triggerUpdatedCacheEntryResponse(final HttpResponse backendResponse, final Date responseDate) {
+ final ComplexFuture<?> future = scope.future;
+ recordCacheUpdate(scope.clientContext);
+ future.setDependency(responseCache.updateCacheEntry(
+ target,
+ request,
+ cacheEntry,
+ backendResponse,
+ requestDate,
+ responseDate,
+ new FutureCallback<HttpCacheEntry>() {
+
+ @Override
+ public void completed(final HttpCacheEntry updatedEntry) {
+ if (suitabilityChecker.isConditional(request)
+ && suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
+ final SimpleHttpResponse cacheResponse = responseGenerator.generateNotModifiedResponse(updatedEntry);
+ triggerResponse(cacheResponse, scope, asyncExecCallback);
+ } else {
+ try {
+ final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, updatedEntry);
+ triggerResponse(cacheResponse, scope, asyncExecCallback);
+ } catch (final ResourceIOException ex) {
+ asyncExecCallback.failed(ex);
+ }
+ }
+ }
- private final AtomicReference<Date> responseDateRef = new AtomicReference<>(null);
- private final AtomicReference<HttpResponse> backendResponseRef = new AtomicReference<>(null);
+ @Override
+ public void failed(final Exception ex) {
+ asyncExecCallback.failed(ex);
+ }
+
+ @Override
+ public void cancelled() {
+ asyncExecCallback.failed(new InterruptedIOException());
+ }
+
+ }));
+ }
+
+ void triggerResponseStaleCacheEntry() {
+ try {
+ final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, cacheEntry);
+ cacheResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
+ triggerResponse(cacheResponse, scope, asyncExecCallback);
+ } catch (final ResourceIOException ex) {
+ asyncExecCallback.failed(ex);
+ }
+ }
+
+ AsyncExecCallback evaluateResponse(final HttpResponse backendResponse, final Date responseDate) {
+ backendResponse.addHeader(HeaderConstants.VIA, generateViaHeader(backendResponse));
- @Override
- public boolean cacheResponse(final HttpResponse backendResponse) throws IOException {
- final Date responseDate = getCurrentDate();
- responseDateRef.set(requestDate);
final int statusCode = backendResponse.getCode();
if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) {
recordCacheUpdate(scope.clientContext);
}
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
- backendResponseRef.set(backendResponse);
- return false;
+ return new AsyncExecCallbackWrapper(asyncExecCallback, new Runnable() {
+
+ @Override
+ public void run() {
+ triggerUpdatedCacheEntryResponse(backendResponse, responseDate);
+ }
+
+ });
}
if (staleIfErrorAppliesTo(statusCode)
&& !staleResponseNotAllowed(request, cacheEntry, getCurrentDate())
&& validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
- backendResponseRef.set(backendResponse);
- return false;
+ return new AsyncExecCallbackWrapper(asyncExecCallback, new Runnable() {
+
+ @Override
+ public void run() {
+ triggerResponseStaleCacheEntry();
+ }
+
+ });
}
- return true;
+ return new BackendResponseHandler(target, conditionalRequest, requestDate, responseDate, scope, asyncExecCallback);
}
@Override
public AsyncDataConsumer handleResponse(
- final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
- if (backendResponseRef.get() == null) {
- return asyncExecCallback.handleResponse(response, entityDetails);
- } else {
- return null;
- }
- }
+ final HttpResponse backendResponse1, final EntityDetails entityDetails) throws HttpException, IOException {
- @Override
- public void completed() {
- final HttpResponse backendResponse = backendResponseRef.getAndSet(null);
- if (backendResponse != null) {
- final int statusCode = backendResponse.getCode();
- try {
- if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
- future.setDependency(responseCache.updateCacheEntry(
- target,
- request,
- cacheEntry,
- backendResponse,
- requestDate,
- responseDateRef.get(),
- new FutureCallback<HttpCacheEntry>() {
+ final Date responseDate1 = getCurrentDate();
- @Override
- public void completed(final HttpCacheEntry updatedEntry) {
- if (suitabilityChecker.isConditional(request)
- && suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
- final SimpleHttpResponse cacheResponse = responseGenerator.generateNotModifiedResponse(updatedEntry);
- triggerResponse(cacheResponse, scope, asyncExecCallback);
- } else {
- try {
- final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, updatedEntry);
- triggerResponse(cacheResponse, scope, asyncExecCallback);
- } catch (final ResourceIOException ex) {
- asyncExecCallback.failed(ex);
- }
- }
- }
+ final AsyncExecCallback callback1;
+ if (revalidationResponseIsTooOld(backendResponse1, cacheEntry)) {
- @Override
- public void failed(final Exception ex) {
- asyncExecCallback.failed(ex);
- }
+ final HttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest(
+ scope.originalRequest);
- @Override
- public void cancelled() {
- asyncExecCallback.failed(new InterruptedIOException());
- }
+ callback1 = new AsyncExecCallbackWrapper(asyncExecCallback, new Runnable() {
- }));
- } else if (staleIfErrorAppliesTo(statusCode)) {
- final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, cacheEntry);
- cacheResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
- triggerResponse(cacheResponse, scope, asyncExecCallback);
- }
- } catch (final IOException ex) {
- asyncExecCallback.failed(ex);
- }
- } else {
- asyncExecCallback.completed();
- }
- }
+ @Override
+ public void run() {
+ chainProceed(unconditional, entityProducer, scope, chain, new AsyncExecCallback() {
- @Override
- public void failed(final Exception cause) {
- asyncExecCallback.failed(cause);
- }
+ @Override
+ public AsyncDataConsumer handleResponse(
+ final HttpResponse backendResponse2, final EntityDetails entityDetails) throws HttpException, IOException {
+ final Date responseDate2 = getCurrentDate();
+ final AsyncExecCallback callback2 = evaluateResponse(backendResponse2, responseDate2);
+ callbackRef.set(callback2);
+ return callback2.handleResponse(backendResponse2, entityDetails);
+ }
- };
+ @Override
+ public void completed() {
+ final AsyncExecCallback callback2 = callbackRef.getAndSet(null);
+ if (callback2 != null) {
+ callback2.completed();
+ } else {
+ asyncExecCallback.completed();
+ }
+ }
- final HttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequest(scope.originalRequest, cacheEntry);
- callBackendInternal(target, conditionalRequest, entityProducer, scope, chain, new InternalCallback() {
+ @Override
+ public void failed(final Exception cause) {
+ final AsyncExecCallback callback2 = callbackRef.getAndSet(null);
+ if (callback2 != null) {
+ callback2.failed(cause);
+ } else {
+ asyncExecCallback.failed(cause);
+ }
+ }
- private final AtomicBoolean revalidate = new AtomicBoolean(false);
+ });
- @Override
- public boolean cacheResponse(final HttpResponse backendResponse) throws HttpException, IOException {
- if (revalidationResponseIsTooOld(backendResponse, cacheEntry)) {
- revalidate.set(true);
- return false;
- } else {
- return internalCallback.cacheResponse(backendResponse);
- }
- }
+ }
- @Override
- public AsyncDataConsumer handleResponse(
- final HttpResponse response,
- final EntityDetails entityDetails) throws HttpException, IOException {
- if (revalidate.get()) {
- return null;
+ });
} else {
- return internalCallback.handleResponse(response, entityDetails);
+ callback1 = evaluateResponse(backendResponse1, responseDate1);
}
+ callbackRef.set(callback1);
+ return callback1.handleResponse(backendResponse1, entityDetails);
}
@Override
public void completed() {
- if (revalidate.getAndSet(false)) {
- final HttpRequest unconditionalRequest = conditionalRequestBuilder.buildUnconditionalRequest(scope.originalRequest);
- callBackendInternal(target, unconditionalRequest, entityProducer, scope, chain, new InternalCallback() {
-
- @Override
- public boolean cacheResponse(final HttpResponse backendResponse) throws HttpException, IOException {
- return internalCallback.cacheResponse(backendResponse);
- }
-
- @Override
- public AsyncDataConsumer handleResponse(
- final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
- return internalCallback.handleResponse(response, entityDetails);
- }
-
- @Override
- public void completed() {
- internalCallback.completed();
- }
-
- @Override
- public void failed(final Exception cause) {
- internalCallback.failed(cause);
- }
-
- });
+ final AsyncExecCallback callback1 = callbackRef.getAndSet(null);
+ if (callback1 != null) {
+ callback1.completed();
} else {
- internalCallback.completed();
+ asyncExecCallback.completed();
}
}
@Override
public void failed(final Exception cause) {
- internalCallback.failed(cause);
+ final AsyncExecCallback callback1 = callbackRef.getAndSet(null);
+ if (callback1 != null) {
+ callback1.failed(cause);
+ } else {
+ asyncExecCallback.failed(cause);
+ }
}
});
@@ -787,109 +841,139 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
final HttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequestFromVariants(request, variants);
final Date requestDate = getCurrentDate();
- callBackendInternal(target, conditionalRequest, entityProducer, scope, chain, new InternalCallback() {
+ chainProceed(conditionalRequest, entityProducer, scope, chain, new AsyncExecCallback() {
- private final AtomicReference<Date> responseDateRef = new AtomicReference<>(null);
- private final AtomicReference<HttpResponse> backendResponseRef = new AtomicReference<>(null);
+ final AtomicReference<AsyncExecCallback> callbackRef = new AtomicReference<>();
- @Override
- public boolean cacheResponse(final HttpResponse backendResponse) throws IOException {
- responseDateRef.set(getCurrentDate());
- if (backendResponse.getCode() == HttpStatus.SC_NOT_MODIFIED) {
- backendResponseRef.set(backendResponse);
- return false;
- } else {
- return true;
- }
+ void updateVariantCacheEntry(final HttpResponse backendResponse, final Date responseDate, final Variant matchingVariant) {
+ recordCacheUpdate(scope.clientContext);
+ future.setDependency(responseCache.updateVariantCacheEntry(
+ target,
+ conditionalRequest,
+ backendResponse,
+ matchingVariant,
+ requestDate,
+ responseDate,
+ new FutureCallback<HttpCacheEntry>() {
+
+ @Override
+ public void completed(final HttpCacheEntry responseEntry) {
+ if (shouldSendNotModifiedResponse(request, responseEntry)) {
+ final SimpleHttpResponse cacheResponse = responseGenerator.generateNotModifiedResponse(responseEntry);
+ triggerResponse(cacheResponse, scope, asyncExecCallback);
+ } else {
+ try {
+ final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, responseEntry);
+ future.setDependency(responseCache.reuseVariantEntryFor(
+ target,
+ request,
+ matchingVariant,
+ new FutureCallback<Boolean>() {
+
+ @Override
+ public void completed(final Boolean result) {
+ triggerResponse(cacheResponse, scope, asyncExecCallback);
+ }
+
+ @Override
+ public void failed(final Exception ex) {
+ asyncExecCallback.failed(ex);
+ }
+
+ @Override
+ public void cancelled() {
+ asyncExecCallback.failed(new InterruptedIOException());
+ }
+
+ }));
+ } catch (final ResourceIOException ex) {
+ asyncExecCallback.failed(ex);
+ }
+ }
+ }
+
+ @Override
+ public void failed(final Exception ex) {
+ asyncExecCallback.failed(ex);
+ }
+
+ @Override
+ public void cancelled() {
+ asyncExecCallback.failed(new InterruptedIOException());
+ }
+
+ }));
}
@Override
public AsyncDataConsumer handleResponse(
- final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
- return asyncExecCallback.handleResponse(response, entityDetails);
- }
+ final HttpResponse backendResponse, final EntityDetails entityDetails) throws HttpException, IOException {
+ final Date responseDate = getCurrentDate();
+ backendResponse.addHeader("Via", generateViaHeader(backendResponse));
- @Override
- public void completed() {
- final HttpResponse backendResponse = backendResponseRef.getAndSet(null);
- if (backendResponse != null) {
+ final AsyncExecCallback callback;
+
+ if (backendResponse.getCode() != HttpStatus.SC_NOT_MODIFIED) {
+ callback = new BackendResponseHandler(target, request, requestDate, responseDate, scope, asyncExecCallback);
+ } else {
final Header resultEtagHeader = backendResponse.getFirstHeader(HeaderConstants.ETAG);
if (resultEtagHeader == null) {
log.warn("304 response did not contain ETag");
- callBackend(target, request, entityProducer, scope, chain, asyncExecCallback);
- return;
- }
- final String resultEtag = resultEtagHeader.getValue();
- final Variant matchingVariant = variants.get(resultEtag);
- if (matchingVariant == null) {
- log.debug("304 response did not contain ETag matching one sent in If-None-Match");
- callBackend(target, request, entityProducer, scope, chain, asyncExecCallback);
- return;
- }
- 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);
- return;
- }
- recordCacheUpdate(scope.clientContext);
- future.setDependency(responseCache.updateVariantCacheEntry(
- target,
- conditionalRequest,
- backendResponse,
- matchingVariant,
- requestDate,
- responseDateRef.get(),
- new FutureCallback<HttpCacheEntry>() {
+ callback = new AsyncExecCallbackWrapper(asyncExecCallback, new Runnable() {
- @Override
- public void completed(final HttpCacheEntry responseEntry) {
- if (shouldSendNotModifiedResponse(request, responseEntry)) {
- final SimpleHttpResponse cacheResponse = responseGenerator.generateNotModifiedResponse(responseEntry);
- triggerResponse(cacheResponse, scope, asyncExecCallback);
- } else {
- try {
- final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, responseEntry);
- future.setDependency(responseCache.reuseVariantEntryFor(
- target,
- request,
- matchingVariant,
- new FutureCallback<Boolean>() {
-
- @Override
- public void completed(final Boolean result) {
- triggerResponse(cacheResponse, scope, asyncExecCallback);
- }
-
- @Override
- public void failed(final Exception ex) {
- asyncExecCallback.failed(ex);
- }
-
- @Override
- public void cancelled() {
- asyncExecCallback.failed(new InterruptedIOException());
- }
-
- }));
- } catch (final ResourceIOException ex) {
- asyncExecCallback.failed(ex);
- }
- }
- }
+ @Override
+ public void run() {
+ callBackend(target, request, entityProducer, scope, chain, asyncExecCallback);
+ }
- @Override
- public void failed(final Exception ex) {
- asyncExecCallback.failed(ex);
- }
+ });
+ } else {
+ final String resultEtag = resultEtagHeader.getValue();
+ final Variant matchingVariant = variants.get(resultEtag);
+ if (matchingVariant == null) {
+ log.debug("304 response did not contain ETag matching one sent in If-None-Match");
+ callback = new AsyncExecCallbackWrapper(asyncExecCallback, new Runnable() {
@Override
- public void cancelled() {
- asyncExecCallback.failed(new InterruptedIOException());
+ public void run() {
+ callBackend(target, request, entityProducer, scope, chain, asyncExecCallback);
}
- }));
+ });
+ } else {
+ if (revalidationResponseIsTooOld(backendResponse, matchingVariant.getEntry())) {
+ final HttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest(request);
+ scope.clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, unconditional);
+ callback = new AsyncExecCallbackWrapper(asyncExecCallback, new Runnable() {
+
+ @Override
+ public void run() {
+ callBackend(target, request, entityProducer, scope, chain, asyncExecCallback);
+ }
+
+ });
+ } else {
+ callback = new AsyncExecCallbackWrapper(asyncExecCallback, new Runnable() {
+
+ @Override
+ public void run() {
+ updateVariantCacheEntry(backendResponse, responseDate, matchingVariant);
+ }
+ });
+ }
+ }
+ }
+ }
+ callbackRef.set(callback);
+ return callback.handleResponse(backendResponse, entityDetails);
+ }
+
+ @Override
+ public void completed() {
+ final AsyncExecCallback callback = callbackRef.getAndSet(null);
+ if (callback != null) {
+ callback.completed();
} else {
asyncExecCallback.completed();
}
@@ -897,7 +981,12 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
@Override
public void failed(final Exception cause) {
- asyncExecCallback.failed(cause);
+ final AsyncExecCallback callback = callbackRef.getAndSet(null);
+ if (callback != null) {
+ callback.failed(cause);
+ } else {
+ asyncExecCallback.failed(cause);
+ }
}
});
http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/c607197f/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
----------------------------------------------------------------------
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
index 57de39c..724fa8a 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java
@@ -278,11 +278,10 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
final ExecChain.Scope scope,
final ExecChain chain,
final HttpCacheEntry cacheEntry) throws IOException, HttpException {
-
+ Date requestDate = getCurrentDate();
final ClassicHttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequest(
scope.originalRequest, cacheEntry);
- Date requestDate = getCurrentDate();
ClassicHttpResponse backendResponse = chain.proceed(conditionalRequest, scope);
try {
Date responseDate = getCurrentDate();