Completed rewrite of re-validation code in the classic caching exec interceptor; added re-validation to the async caching exec interceptor
This commit is contained in:
parent
f16ac3ec3b
commit
16147b1852
|
@ -32,6 +32,7 @@ import java.nio.ByteBuffer;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
@ -47,8 +48,10 @@ import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage;
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||||
import org.apache.hc.client5.http.cache.ResourceFactory;
|
import org.apache.hc.client5.http.cache.ResourceFactory;
|
||||||
import org.apache.hc.client5.http.cache.ResourceIOException;
|
import org.apache.hc.client5.http.cache.ResourceIOException;
|
||||||
|
import org.apache.hc.client5.http.impl.ExecSupport;
|
||||||
import org.apache.hc.client5.http.impl.RequestCopier;
|
import org.apache.hc.client5.http.impl.RequestCopier;
|
||||||
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
||||||
|
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
|
||||||
import org.apache.hc.client5.http.utils.DateUtils;
|
import org.apache.hc.client5.http.utils.DateUtils;
|
||||||
import org.apache.hc.core5.annotation.Contract;
|
import org.apache.hc.core5.annotation.Contract;
|
||||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||||
|
@ -87,21 +90,16 @@ import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||||
public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler {
|
public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler {
|
||||||
|
|
||||||
private final HttpAsyncCache responseCache;
|
private final HttpAsyncCache responseCache;
|
||||||
|
private final DefaultAsyncCacheRevalidator cacheRevalidator;
|
||||||
private final ConditionalRequestBuilder<HttpRequest> conditionalRequestBuilder;
|
private final ConditionalRequestBuilder<HttpRequest> conditionalRequestBuilder;
|
||||||
|
|
||||||
public AsyncCachingExec(final HttpAsyncCache cache, final CacheConfig config) {
|
AsyncCachingExec(final HttpAsyncCache cache, final DefaultAsyncCacheRevalidator cacheRevalidator, final CacheConfig config) {
|
||||||
super(config);
|
super(config);
|
||||||
this.responseCache = Args.notNull(cache, "Response cache");
|
this.responseCache = Args.notNull(cache, "Response cache");
|
||||||
|
this.cacheRevalidator = cacheRevalidator;
|
||||||
this.conditionalRequestBuilder = new ConditionalRequestBuilder<>(RequestCopier.INSTANCE);
|
this.conditionalRequestBuilder = new ConditionalRequestBuilder<>(RequestCopier.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AsyncCachingExec(
|
|
||||||
final ResourceFactory resourceFactory,
|
|
||||||
final HttpAsyncCacheStorage storage,
|
|
||||||
final CacheConfig config) {
|
|
||||||
this(new BasicHttpAsyncCache(resourceFactory, storage), config);
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncCachingExec(
|
AsyncCachingExec(
|
||||||
final HttpAsyncCache responseCache,
|
final HttpAsyncCache responseCache,
|
||||||
final CacheValidityPolicy validityPolicy,
|
final CacheValidityPolicy validityPolicy,
|
||||||
|
@ -109,16 +107,37 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
|
||||||
final CachedHttpResponseGenerator responseGenerator,
|
final CachedHttpResponseGenerator responseGenerator,
|
||||||
final CacheableRequestPolicy cacheableRequestPolicy,
|
final CacheableRequestPolicy cacheableRequestPolicy,
|
||||||
final CachedResponseSuitabilityChecker suitabilityChecker,
|
final CachedResponseSuitabilityChecker suitabilityChecker,
|
||||||
final ConditionalRequestBuilder<HttpRequest> conditionalRequestBuilder,
|
|
||||||
final ResponseProtocolCompliance responseCompliance,
|
final ResponseProtocolCompliance responseCompliance,
|
||||||
final RequestProtocolCompliance requestCompliance,
|
final RequestProtocolCompliance requestCompliance,
|
||||||
|
final DefaultAsyncCacheRevalidator cacheRevalidator,
|
||||||
|
final ConditionalRequestBuilder<HttpRequest> conditionalRequestBuilder,
|
||||||
final CacheConfig config) {
|
final CacheConfig config) {
|
||||||
super(validityPolicy, responseCachingPolicy, responseGenerator, cacheableRequestPolicy,
|
super(validityPolicy, responseCachingPolicy, responseGenerator, cacheableRequestPolicy,
|
||||||
suitabilityChecker, responseCompliance, requestCompliance, config);
|
suitabilityChecker, responseCompliance, requestCompliance, config);
|
||||||
this.responseCache = responseCache;
|
this.responseCache = responseCache;
|
||||||
|
this.cacheRevalidator = cacheRevalidator;
|
||||||
this.conditionalRequestBuilder = conditionalRequestBuilder;
|
this.conditionalRequestBuilder = conditionalRequestBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AsyncCachingExec(
|
||||||
|
final HttpAsyncCache cache,
|
||||||
|
final ScheduledExecutorService executorService,
|
||||||
|
final SchedulingStrategy schedulingStrategy,
|
||||||
|
final CacheConfig config) {
|
||||||
|
this(cache,
|
||||||
|
executorService != null ? new DefaultAsyncCacheRevalidator(executorService, schedulingStrategy) : null,
|
||||||
|
config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncCachingExec(
|
||||||
|
final ResourceFactory resourceFactory,
|
||||||
|
final HttpAsyncCacheStorage storage,
|
||||||
|
final ScheduledExecutorService executorService,
|
||||||
|
final SchedulingStrategy schedulingStrategy,
|
||||||
|
final CacheConfig config) {
|
||||||
|
this(new BasicHttpAsyncCache(resourceFactory, storage), executorService, schedulingStrategy, config);
|
||||||
|
}
|
||||||
|
|
||||||
private void triggerResponse(
|
private void triggerResponse(
|
||||||
final SimpleHttpResponse cacheResponse,
|
final SimpleHttpResponse cacheResponse,
|
||||||
final AsyncExecChain.Scope scope,
|
final AsyncExecChain.Scope scope,
|
||||||
|
@ -609,7 +628,38 @@ public class AsyncCachingExec extends CachingExecBase implements AsyncExecChainH
|
||||||
triggerResponse(cacheResponse, scope, asyncExecCallback);
|
triggerResponse(cacheResponse, scope, asyncExecCallback);
|
||||||
} else if (!(entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request))) {
|
} else if (!(entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request))) {
|
||||||
log.debug("Revalidating cache entry");
|
log.debug("Revalidating cache entry");
|
||||||
revalidateCacheEntry(target, request, entityProducer, scope, chain, asyncExecCallback, entry);
|
if (cacheRevalidator != null
|
||||||
|
&& !staleResponseNotAllowed(request, entry, now)
|
||||||
|
&& validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
|
||||||
|
log.debug("Serving stale with asynchronous revalidation");
|
||||||
|
try {
|
||||||
|
final SimpleHttpResponse cacheResponse = generateCachedResponse(request, context, entry, now);
|
||||||
|
final String exchangeId = ExecSupport.getNextExchangeId();
|
||||||
|
final AsyncExecChain.Scope fork = new AsyncExecChain.Scope(
|
||||||
|
exchangeId,
|
||||||
|
scope.route,
|
||||||
|
scope.originalRequest,
|
||||||
|
new ComplexFuture<>(null),
|
||||||
|
HttpClientContext.create(),
|
||||||
|
scope.execRuntime.fork());
|
||||||
|
cacheRevalidator.revalidateCacheEntry(
|
||||||
|
responseCache.generateKey(target, request, entry),
|
||||||
|
asyncExecCallback,
|
||||||
|
new DefaultAsyncCacheRevalidator.RevalidationCall() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final AsyncExecCallback asyncExecCallback) {
|
||||||
|
revalidateCacheEntry(target, request, entityProducer, fork, chain, asyncExecCallback, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
triggerResponse(cacheResponse, scope, asyncExecCallback);
|
||||||
|
} catch (final ResourceIOException ex) {
|
||||||
|
asyncExecCallback.failed(ex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
revalidateCacheEntry(target, request, entityProducer, scope, chain, asyncExecCallback, entry);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.debug("Cache entry not usable; calling backend");
|
log.debug("Cache entry not usable; calling backend");
|
||||||
callBackend(target, request, entityProducer, scope, chain, asyncExecCallback);
|
callBackend(target, request, entityProducer, scope, chain, asyncExecCallback);
|
||||||
|
|
|
@ -84,6 +84,15 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
||||||
this( resourceFactory, storage, CacheKeyGenerator.INSTANCE);
|
this( resourceFactory, storage, CacheKeyGenerator.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateKey(final HttpHost host, final HttpRequest request, final HttpCacheEntry cacheEntry) {
|
||||||
|
if (cacheEntry == null) {
|
||||||
|
return cacheKeyGenerator.generateKey(host, request);
|
||||||
|
} else {
|
||||||
|
return cacheKeyGenerator.generateKey(host, request, cacheEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Cancellable flushCacheEntriesFor(
|
public Cancellable flushCacheEntriesFor(
|
||||||
final HttpHost host, final HttpRequest request, final FutureCallback<Boolean> callback) {
|
final HttpHost host, final HttpRequest request, final FutureCallback<Boolean> callback) {
|
||||||
|
|
|
@ -88,6 +88,15 @@ class BasicHttpCache implements HttpCache {
|
||||||
this(CacheConfig.DEFAULT);
|
this(CacheConfig.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateKey(final HttpHost host, final HttpRequest request, final HttpCacheEntry cacheEntry) {
|
||||||
|
if (cacheEntry == null) {
|
||||||
|
return cacheKeyGenerator.generateKey(host, request);
|
||||||
|
} else {
|
||||||
|
return cacheKeyGenerator.generateKey(host, request, cacheEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flushCacheEntriesFor(final HttpHost host, final HttpRequest request) {
|
public void flushCacheEntriesFor(final HttpHost host, final HttpRequest request) {
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
|
|
|
@ -90,15 +90,10 @@ import org.apache.hc.core5.util.Args;
|
||||||
* <p><b>Background validation</b>. The cache module supports the
|
* <p><b>Background validation</b>. The cache module supports the
|
||||||
* {@code stale-while-revalidate} directive of
|
* {@code stale-while-revalidate} directive of
|
||||||
* <a href="http://tools.ietf.org/html/rfc5861">RFC5861</a>, which allows
|
* <a href="http://tools.ietf.org/html/rfc5861">RFC5861</a>, which allows
|
||||||
* certain cache entry revalidations to happen in the background. You may
|
* certain cache entry revalidations to happen in the background. Asynchronous
|
||||||
* want to tweak the settings for the {@link
|
* validation is enabled by default but it could be disabled by setting the number
|
||||||
* CacheConfig#getAsynchronousWorkersCore() minimum} and {@link
|
* of re-validation workers to {@code 0} with {@link CacheConfig#getAsynchronousWorkers()}
|
||||||
* CacheConfig#getAsynchronousWorkersMax() maximum} number of background
|
* parameter</p>
|
||||||
* worker threads, as well as the {@link
|
|
||||||
* CacheConfig#getAsynchronousWorkerIdleLifetimeSecs() maximum time they
|
|
||||||
* can be idle before being reclaimed}. You can also control the {@link
|
|
||||||
* CacheConfig#getRevalidationQueueSize() size of the queue} used for
|
|
||||||
* revalidations when there aren't enough workers to keep up with demand.</p>
|
|
||||||
*/
|
*/
|
||||||
public class CacheConfig implements Cloneable {
|
public class CacheConfig implements Cloneable {
|
||||||
|
|
||||||
|
@ -142,21 +137,7 @@ public class CacheConfig implements Cloneable {
|
||||||
/** Default number of worker threads to allow for background revalidations
|
/** Default number of worker threads to allow for background revalidations
|
||||||
* resulting from the stale-while-revalidate directive.
|
* resulting from the stale-while-revalidate directive.
|
||||||
*/
|
*/
|
||||||
public static final int DEFAULT_ASYNCHRONOUS_WORKERS_MAX = 1;
|
public static final int DEFAULT_ASYNCHRONOUS_WORKERS = 1;
|
||||||
|
|
||||||
/** Default minimum number of worker threads to allow for background
|
|
||||||
* revalidations resulting from the stale-while-revalidate directive.
|
|
||||||
*/
|
|
||||||
public static final int DEFAULT_ASYNCHRONOUS_WORKERS_CORE = 1;
|
|
||||||
|
|
||||||
/** Default maximum idle lifetime for a background revalidation thread
|
|
||||||
* before it gets reclaimed.
|
|
||||||
*/
|
|
||||||
public static final int DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS = 60;
|
|
||||||
|
|
||||||
/** Default maximum queue length for background revalidation requests.
|
|
||||||
*/
|
|
||||||
public static final int DEFAULT_REVALIDATION_QUEUE_SIZE = 100;
|
|
||||||
|
|
||||||
public static final CacheConfig DEFAULT = new Builder().build();
|
public static final CacheConfig DEFAULT = new Builder().build();
|
||||||
|
|
||||||
|
@ -170,10 +151,7 @@ public class CacheConfig implements Cloneable {
|
||||||
private final long heuristicDefaultLifetime;
|
private final long heuristicDefaultLifetime;
|
||||||
private final boolean sharedCache;
|
private final boolean sharedCache;
|
||||||
private final boolean freshnessCheckEnabled;
|
private final boolean freshnessCheckEnabled;
|
||||||
private final int asynchronousWorkersMax;
|
private final int asynchronousWorkers;
|
||||||
private final int asynchronousWorkersCore;
|
|
||||||
private final int asynchronousWorkerIdleLifetimeSecs;
|
|
||||||
private final int revalidationQueueSize;
|
|
||||||
private final boolean neverCacheHTTP10ResponsesWithQuery;
|
private final boolean neverCacheHTTP10ResponsesWithQuery;
|
||||||
|
|
||||||
CacheConfig(
|
CacheConfig(
|
||||||
|
@ -187,10 +165,7 @@ public class CacheConfig implements Cloneable {
|
||||||
final long heuristicDefaultLifetime,
|
final long heuristicDefaultLifetime,
|
||||||
final boolean sharedCache,
|
final boolean sharedCache,
|
||||||
final boolean freshnessCheckEnabled,
|
final boolean freshnessCheckEnabled,
|
||||||
final int asynchronousWorkersMax,
|
final int asynchronousWorkers,
|
||||||
final int asynchronousWorkersCore,
|
|
||||||
final int asynchronousWorkerIdleLifetimeSecs,
|
|
||||||
final int revalidationQueueSize,
|
|
||||||
final boolean neverCacheHTTP10ResponsesWithQuery) {
|
final boolean neverCacheHTTP10ResponsesWithQuery) {
|
||||||
super();
|
super();
|
||||||
this.maxObjectSize = maxObjectSize;
|
this.maxObjectSize = maxObjectSize;
|
||||||
|
@ -203,10 +178,7 @@ public class CacheConfig implements Cloneable {
|
||||||
this.heuristicDefaultLifetime = heuristicDefaultLifetime;
|
this.heuristicDefaultLifetime = heuristicDefaultLifetime;
|
||||||
this.sharedCache = sharedCache;
|
this.sharedCache = sharedCache;
|
||||||
this.freshnessCheckEnabled = freshnessCheckEnabled;
|
this.freshnessCheckEnabled = freshnessCheckEnabled;
|
||||||
this.asynchronousWorkersMax = asynchronousWorkersMax;
|
this.asynchronousWorkers = asynchronousWorkers;
|
||||||
this.asynchronousWorkersCore = asynchronousWorkersCore;
|
|
||||||
this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs;
|
|
||||||
this.revalidationQueueSize = revalidationQueueSize;
|
|
||||||
this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery;
|
this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,33 +278,8 @@ public class CacheConfig implements Cloneable {
|
||||||
* revalidations due to the {@code stale-while-revalidate} directive. A
|
* revalidations due to the {@code stale-while-revalidate} directive. A
|
||||||
* value of 0 means background revalidations are disabled.
|
* value of 0 means background revalidations are disabled.
|
||||||
*/
|
*/
|
||||||
public int getAsynchronousWorkersMax() {
|
public int getAsynchronousWorkers() {
|
||||||
return asynchronousWorkersMax;
|
return asynchronousWorkers;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the minimum number of threads to keep alive for background
|
|
||||||
* revalidations due to the {@code stale-while-revalidate} directive.
|
|
||||||
*/
|
|
||||||
public int getAsynchronousWorkersCore() {
|
|
||||||
return asynchronousWorkersCore;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current maximum idle lifetime in seconds for a
|
|
||||||
* background revalidation worker thread. If a worker thread is idle
|
|
||||||
* for this long, and there are more than the core number of worker
|
|
||||||
* threads alive, the worker will be reclaimed.
|
|
||||||
*/
|
|
||||||
public int getAsynchronousWorkerIdleLifetimeSecs() {
|
|
||||||
return asynchronousWorkerIdleLifetimeSecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current maximum queue size for background revalidations.
|
|
||||||
*/
|
|
||||||
public int getRevalidationQueueSize() {
|
|
||||||
return revalidationQueueSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -354,10 +301,7 @@ public class CacheConfig implements Cloneable {
|
||||||
.setHeuristicCoefficient(config.getHeuristicCoefficient())
|
.setHeuristicCoefficient(config.getHeuristicCoefficient())
|
||||||
.setHeuristicDefaultLifetime(config.getHeuristicDefaultLifetime())
|
.setHeuristicDefaultLifetime(config.getHeuristicDefaultLifetime())
|
||||||
.setSharedCache(config.isSharedCache())
|
.setSharedCache(config.isSharedCache())
|
||||||
.setAsynchronousWorkersMax(config.getAsynchronousWorkersMax())
|
.setAsynchronousWorkers(config.getAsynchronousWorkers())
|
||||||
.setAsynchronousWorkersCore(config.getAsynchronousWorkersCore())
|
|
||||||
.setAsynchronousWorkerIdleLifetimeSecs(config.getAsynchronousWorkerIdleLifetimeSecs())
|
|
||||||
.setRevalidationQueueSize(config.getRevalidationQueueSize())
|
|
||||||
.setNeverCacheHTTP10ResponsesWithQueryString(config.isNeverCacheHTTP10ResponsesWithQuery());
|
.setNeverCacheHTTP10ResponsesWithQueryString(config.isNeverCacheHTTP10ResponsesWithQuery());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,10 +318,7 @@ public class CacheConfig implements Cloneable {
|
||||||
private long heuristicDefaultLifetime;
|
private long heuristicDefaultLifetime;
|
||||||
private boolean sharedCache;
|
private boolean sharedCache;
|
||||||
private boolean freshnessCheckEnabled;
|
private boolean freshnessCheckEnabled;
|
||||||
private int asynchronousWorkersMax;
|
private int asynchronousWorkers;
|
||||||
private int asynchronousWorkersCore;
|
|
||||||
private int asynchronousWorkerIdleLifetimeSecs;
|
|
||||||
private int revalidationQueueSize;
|
|
||||||
private boolean neverCacheHTTP10ResponsesWithQuery;
|
private boolean neverCacheHTTP10ResponsesWithQuery;
|
||||||
|
|
||||||
Builder() {
|
Builder() {
|
||||||
|
@ -391,10 +332,7 @@ public class CacheConfig implements Cloneable {
|
||||||
this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
|
this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
|
||||||
this.sharedCache = true;
|
this.sharedCache = true;
|
||||||
this.freshnessCheckEnabled = true;
|
this.freshnessCheckEnabled = true;
|
||||||
this.asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX;
|
this.asynchronousWorkers = DEFAULT_ASYNCHRONOUS_WORKERS;
|
||||||
this.asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE;
|
|
||||||
this.asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS;
|
|
||||||
this.revalidationQueueSize = DEFAULT_REVALIDATION_QUEUE_SIZE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -495,42 +433,11 @@ public class CacheConfig implements Cloneable {
|
||||||
/**
|
/**
|
||||||
* Sets the maximum number of threads to allow for background
|
* Sets the maximum number of threads to allow for background
|
||||||
* revalidations due to the {@code stale-while-revalidate} directive.
|
* revalidations due to the {@code stale-while-revalidate} directive.
|
||||||
* @param asynchronousWorkersMax number of threads; a value of 0 disables background
|
* @param asynchronousWorkers number of threads; a value of 0 disables background
|
||||||
* revalidations.
|
* revalidations.
|
||||||
*/
|
*/
|
||||||
public Builder setAsynchronousWorkersMax(final int asynchronousWorkersMax) {
|
public Builder setAsynchronousWorkers(final int asynchronousWorkers) {
|
||||||
this.asynchronousWorkersMax = asynchronousWorkersMax;
|
this.asynchronousWorkers = asynchronousWorkers;
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the minimum number of threads to keep alive for background
|
|
||||||
* revalidations due to the {@code stale-while-revalidate} directive.
|
|
||||||
* @param asynchronousWorkersCore should be greater than zero and less than or equal
|
|
||||||
* to {@code getAsynchronousWorkersMax()}
|
|
||||||
*/
|
|
||||||
public Builder setAsynchronousWorkersCore(final int asynchronousWorkersCore) {
|
|
||||||
this.asynchronousWorkersCore = asynchronousWorkersCore;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the current maximum idle lifetime in seconds for a
|
|
||||||
* background revalidation worker thread. If a worker thread is idle
|
|
||||||
* for this long, and there are more than the core number of worker
|
|
||||||
* threads alive, the worker will be reclaimed.
|
|
||||||
* @param asynchronousWorkerIdleLifetimeSecs idle lifetime in seconds
|
|
||||||
*/
|
|
||||||
public Builder setAsynchronousWorkerIdleLifetimeSecs(final int asynchronousWorkerIdleLifetimeSecs) {
|
|
||||||
this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the current maximum queue size for background revalidations.
|
|
||||||
*/
|
|
||||||
public Builder setRevalidationQueueSize(final int revalidationQueueSize) {
|
|
||||||
this.revalidationQueueSize = revalidationQueueSize;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,6 +454,11 @@ public class CacheConfig implements Cloneable {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setFreshnessCheckEnabled(final boolean freshnessCheckEnabled) {
|
||||||
|
this.freshnessCheckEnabled = freshnessCheckEnabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CacheConfig build() {
|
public CacheConfig build() {
|
||||||
return new CacheConfig(
|
return new CacheConfig(
|
||||||
maxObjectSize,
|
maxObjectSize,
|
||||||
|
@ -559,10 +471,7 @@ public class CacheConfig implements Cloneable {
|
||||||
heuristicDefaultLifetime,
|
heuristicDefaultLifetime,
|
||||||
sharedCache,
|
sharedCache,
|
||||||
freshnessCheckEnabled,
|
freshnessCheckEnabled,
|
||||||
asynchronousWorkersMax,
|
asynchronousWorkers,
|
||||||
asynchronousWorkersCore,
|
|
||||||
asynchronousWorkerIdleLifetimeSecs,
|
|
||||||
revalidationQueueSize,
|
|
||||||
neverCacheHTTP10ResponsesWithQuery);
|
neverCacheHTTP10ResponsesWithQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -581,10 +490,7 @@ public class CacheConfig implements Cloneable {
|
||||||
.append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime)
|
.append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime)
|
||||||
.append(", sharedCache=").append(this.sharedCache)
|
.append(", sharedCache=").append(this.sharedCache)
|
||||||
.append(", freshnessCheckEnabled=").append(this.freshnessCheckEnabled)
|
.append(", freshnessCheckEnabled=").append(this.freshnessCheckEnabled)
|
||||||
.append(", asynchronousWorkersMax=").append(this.asynchronousWorkersMax)
|
.append(", asynchronousWorkers=").append(this.asynchronousWorkers)
|
||||||
.append(", asynchronousWorkersCore=").append(this.asynchronousWorkersCore)
|
|
||||||
.append(", asynchronousWorkerIdleLifetimeSecs=").append(this.asynchronousWorkerIdleLifetimeSecs)
|
|
||||||
.append(", revalidationQueueSize=").append(this.revalidationQueueSize)
|
|
||||||
.append(", neverCacheHTTP10ResponsesWithQuery=").append(this.neverCacheHTTP10ResponsesWithQuery)
|
.append(", neverCacheHTTP10ResponsesWithQuery=").append(this.neverCacheHTTP10ResponsesWithQuery)
|
||||||
.append("]");
|
.append("]");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
|
|
|
@ -33,6 +33,7 @@ import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
|
||||||
|
@ -62,7 +63,7 @@ class CacheRevalidatorBase implements Closeable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ScheduledExecutor wrap(final ScheduledThreadPoolExecutor threadPoolExecutor) {
|
public static ScheduledExecutor wrap(final ScheduledExecutorService executorService) {
|
||||||
|
|
||||||
return new ScheduledExecutor() {
|
return new ScheduledExecutor() {
|
||||||
|
|
||||||
|
@ -70,18 +71,18 @@ class CacheRevalidatorBase implements Closeable {
|
||||||
public ScheduledFuture<?> schedule(final Runnable command, final TimeValue timeValue) throws RejectedExecutionException {
|
public ScheduledFuture<?> schedule(final Runnable command, final TimeValue timeValue) throws RejectedExecutionException {
|
||||||
Args.notNull(command, "Runnable");
|
Args.notNull(command, "Runnable");
|
||||||
Args.notNull(timeValue, "Time value");
|
Args.notNull(timeValue, "Time value");
|
||||||
return threadPoolExecutor.schedule(command, timeValue.getDuration(), timeValue.getTimeUnit());
|
return executorService.schedule(command, timeValue.getDuration(), timeValue.getTimeUnit());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
threadPoolExecutor.shutdown();
|
executorService.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void awaitTermination(final Timeout timeout) throws InterruptedException {
|
public void awaitTermination(final Timeout timeout) throws InterruptedException {
|
||||||
Args.notNull(timeout, "Timeout");
|
Args.notNull(timeout, "Timeout");
|
||||||
threadPoolExecutor.awaitTermination(timeout.getDuration(), timeout.getTimeUnit());
|
executorService.awaitTermination(timeout.getDuration(), timeout.getTimeUnit());
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,6 +31,7 @@ import java.io.InputStream;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.HttpRoute;
|
import org.apache.hc.client5.http.HttpRoute;
|
||||||
import org.apache.hc.client5.http.async.methods.SimpleBody;
|
import org.apache.hc.client5.http.async.methods.SimpleBody;
|
||||||
|
@ -43,8 +44,10 @@ import org.apache.hc.client5.http.cache.ResourceFactory;
|
||||||
import org.apache.hc.client5.http.cache.ResourceIOException;
|
import org.apache.hc.client5.http.cache.ResourceIOException;
|
||||||
import org.apache.hc.client5.http.classic.ExecChain;
|
import org.apache.hc.client5.http.classic.ExecChain;
|
||||||
import org.apache.hc.client5.http.classic.ExecChainHandler;
|
import org.apache.hc.client5.http.classic.ExecChainHandler;
|
||||||
|
import org.apache.hc.client5.http.impl.ExecSupport;
|
||||||
import org.apache.hc.client5.http.impl.classic.ClassicRequestCopier;
|
import org.apache.hc.client5.http.impl.classic.ClassicRequestCopier;
|
||||||
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
||||||
|
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
|
||||||
import org.apache.hc.client5.http.utils.DateUtils;
|
import org.apache.hc.client5.http.utils.DateUtils;
|
||||||
import org.apache.hc.core5.annotation.Contract;
|
import org.apache.hc.core5.annotation.Contract;
|
||||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||||
|
@ -101,27 +104,18 @@ import org.apache.logging.log4j.Logger;
|
||||||
public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
|
|
||||||
private final HttpCache responseCache;
|
private final HttpCache responseCache;
|
||||||
|
private final DefaultCacheRevalidator cacheRevalidator;
|
||||||
private final ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder;
|
private final ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder;
|
||||||
|
|
||||||
private final Logger log = LogManager.getLogger(getClass());
|
private final Logger log = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
public CachingExec(final HttpCache cache, final CacheConfig config) {
|
CachingExec(final HttpCache cache, final DefaultCacheRevalidator cacheRevalidator, final CacheConfig config) {
|
||||||
super(config);
|
super(config);
|
||||||
this.responseCache = Args.notNull(cache, "Response cache");
|
this.responseCache = Args.notNull(cache, "Response cache");
|
||||||
|
this.cacheRevalidator = cacheRevalidator;
|
||||||
this.conditionalRequestBuilder = new ConditionalRequestBuilder<>(ClassicRequestCopier.INSTANCE);
|
this.conditionalRequestBuilder = new ConditionalRequestBuilder<>(ClassicRequestCopier.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachingExec(
|
|
||||||
final ResourceFactory resourceFactory,
|
|
||||||
final HttpCacheStorage storage,
|
|
||||||
final CacheConfig config) {
|
|
||||||
this(new BasicHttpCache(resourceFactory, storage), config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CachingExec() {
|
|
||||||
this(new BasicHttpCache(), CacheConfig.DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
CachingExec(
|
CachingExec(
|
||||||
final HttpCache responseCache,
|
final HttpCache responseCache,
|
||||||
final CacheValidityPolicy validityPolicy,
|
final CacheValidityPolicy validityPolicy,
|
||||||
|
@ -129,16 +123,37 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
final CachedHttpResponseGenerator responseGenerator,
|
final CachedHttpResponseGenerator responseGenerator,
|
||||||
final CacheableRequestPolicy cacheableRequestPolicy,
|
final CacheableRequestPolicy cacheableRequestPolicy,
|
||||||
final CachedResponseSuitabilityChecker suitabilityChecker,
|
final CachedResponseSuitabilityChecker suitabilityChecker,
|
||||||
final ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder,
|
|
||||||
final ResponseProtocolCompliance responseCompliance,
|
final ResponseProtocolCompliance responseCompliance,
|
||||||
final RequestProtocolCompliance requestCompliance,
|
final RequestProtocolCompliance requestCompliance,
|
||||||
|
final DefaultCacheRevalidator cacheRevalidator,
|
||||||
|
final ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder,
|
||||||
final CacheConfig config) {
|
final CacheConfig config) {
|
||||||
super(validityPolicy, responseCachingPolicy, responseGenerator, cacheableRequestPolicy,
|
super(validityPolicy, responseCachingPolicy, responseGenerator, cacheableRequestPolicy,
|
||||||
suitabilityChecker, responseCompliance, requestCompliance, config);
|
suitabilityChecker, responseCompliance, requestCompliance, config);
|
||||||
this.responseCache = responseCache;
|
this.responseCache = responseCache;
|
||||||
|
this.cacheRevalidator = cacheRevalidator;
|
||||||
this.conditionalRequestBuilder = conditionalRequestBuilder;
|
this.conditionalRequestBuilder = conditionalRequestBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CachingExec(
|
||||||
|
final HttpCache cache,
|
||||||
|
final ScheduledExecutorService executorService,
|
||||||
|
final SchedulingStrategy schedulingStrategy,
|
||||||
|
final CacheConfig config) {
|
||||||
|
this(cache,
|
||||||
|
executorService != null ? new DefaultCacheRevalidator(executorService, schedulingStrategy) : null,
|
||||||
|
config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CachingExec(
|
||||||
|
final ResourceFactory resourceFactory,
|
||||||
|
final HttpCacheStorage storage,
|
||||||
|
final ScheduledExecutorService executorService,
|
||||||
|
final SchedulingStrategy schedulingStrategy,
|
||||||
|
final CacheConfig config) {
|
||||||
|
this(new BasicHttpCache(resourceFactory, storage), executorService, schedulingStrategy, config);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClassicHttpResponse execute(
|
public ClassicHttpResponse execute(
|
||||||
final ClassicHttpRequest request,
|
final ClassicHttpRequest request,
|
||||||
|
@ -167,7 +182,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
|
|
||||||
final SimpleHttpResponse fatalErrorResponse = getFatallyNoncompliantResponse(request, context);
|
final SimpleHttpResponse fatalErrorResponse = getFatallyNoncompliantResponse(request, context);
|
||||||
if (fatalErrorResponse != null) {
|
if (fatalErrorResponse != null) {
|
||||||
return convert(fatalErrorResponse);
|
return convert(fatalErrorResponse, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestCompliance.makeRequestCompliant(request);
|
requestCompliance.makeRequestCompliant(request);
|
||||||
|
@ -188,7 +203,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClassicHttpResponse convert(final SimpleHttpResponse cacheResponse) {
|
private static ClassicHttpResponse convert(final SimpleHttpResponse cacheResponse, final ExecChain.Scope scope) {
|
||||||
if (cacheResponse == null) {
|
if (cacheResponse == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -205,6 +220,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
response.setEntity(new ByteArrayEntity(body.getBodyBytes(), body.getContentType()));
|
response.setEntity(new ByteArrayEntity(body.getBodyBytes(), body.getContentType()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
scope.clientContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,15 +256,11 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) {
|
if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) {
|
||||||
log.debug("Cache hit");
|
log.debug("Cache hit");
|
||||||
try {
|
try {
|
||||||
final ClassicHttpResponse response = convert(generateCachedResponse(request, context, entry, now));
|
return convert(generateCachedResponse(request, context, entry, now), scope);
|
||||||
context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
|
|
||||||
return response;
|
|
||||||
} catch (final ResourceIOException ex) {
|
} catch (final ResourceIOException ex) {
|
||||||
recordCacheFailure(target, request);
|
recordCacheFailure(target, request);
|
||||||
if (!mayCallBackend(request)) {
|
if (!mayCallBackend(request)) {
|
||||||
final ClassicHttpResponse response = convert(generateGatewayTimeout(context));
|
return convert(generateGatewayTimeout(context), scope);
|
||||||
context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
|
|
||||||
return response;
|
|
||||||
} else {
|
} else {
|
||||||
setResponseStatus(scope.clientContext, CacheResponseStatus.FAILURE);
|
setResponseStatus(scope.clientContext, CacheResponseStatus.FAILURE);
|
||||||
return chain.proceed(request, scope);
|
return chain.proceed(request, scope);
|
||||||
|
@ -256,15 +268,38 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
}
|
}
|
||||||
} else if (!mayCallBackend(request)) {
|
} else if (!mayCallBackend(request)) {
|
||||||
log.debug("Cache entry not suitable but only-if-cached requested");
|
log.debug("Cache entry not suitable but only-if-cached requested");
|
||||||
final ClassicHttpResponse response = convert(generateGatewayTimeout(context));
|
return convert(generateGatewayTimeout(context), scope);
|
||||||
context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
|
|
||||||
return response;
|
|
||||||
} else if (!(entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request))) {
|
} else if (!(entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request))) {
|
||||||
log.debug("Revalidating cache entry");
|
log.debug("Revalidating cache entry");
|
||||||
try {
|
try {
|
||||||
return revalidateCacheEntry(target, request, scope, chain, entry);
|
if (cacheRevalidator != null
|
||||||
|
&& !staleResponseNotAllowed(request, entry, now)
|
||||||
|
&& validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
|
||||||
|
log.debug("Serving stale with asynchronous revalidation");
|
||||||
|
final String exchangeId = ExecSupport.getNextExchangeId();
|
||||||
|
final ExecChain.Scope fork = new ExecChain.Scope(
|
||||||
|
exchangeId,
|
||||||
|
scope.route,
|
||||||
|
scope.originalRequest,
|
||||||
|
scope.execRuntime.fork(null),
|
||||||
|
HttpClientContext.create());
|
||||||
|
final SimpleHttpResponse response = generateCachedResponse(request, context, entry, now);
|
||||||
|
cacheRevalidator.revalidateCacheEntry(
|
||||||
|
responseCache.generateKey(target, request, entry),
|
||||||
|
new DefaultCacheRevalidator.RevalidationCall() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassicHttpResponse execute() throws HttpException, IOException {
|
||||||
|
return revalidateCacheEntry(target, request, fork, chain, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
return convert(response, scope);
|
||||||
|
} else {
|
||||||
|
return revalidateCacheEntry(target, request, scope, chain, entry);
|
||||||
|
}
|
||||||
} catch (final IOException ioex) {
|
} catch (final IOException ioex) {
|
||||||
return convert(handleRevalidationFailure(request, context, entry, now));
|
return convert(handleRevalidationFailure(request, context, entry, now), scope);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.debug("Cache entry not usable; calling backend");
|
log.debug("Cache entry not usable; calling backend");
|
||||||
|
@ -307,9 +342,9 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
target, request, cacheEntry, backendResponse, requestDate, responseDate);
|
target, request, cacheEntry, backendResponse, requestDate, responseDate);
|
||||||
if (suitabilityChecker.isConditional(request)
|
if (suitabilityChecker.isConditional(request)
|
||||||
&& suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
|
&& suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
|
||||||
return convert(responseGenerator.generateNotModifiedResponse(updatedEntry));
|
return convert(responseGenerator.generateNotModifiedResponse(updatedEntry), scope);
|
||||||
}
|
}
|
||||||
return convert(responseGenerator.generateResponse(request, updatedEntry));
|
return convert(responseGenerator.generateResponse(request, updatedEntry), scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (staleIfErrorAppliesTo(statusCode)
|
if (staleIfErrorAppliesTo(statusCode)
|
||||||
|
@ -318,7 +353,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
try {
|
try {
|
||||||
final SimpleHttpResponse cachedResponse = responseGenerator.generateResponse(request, cacheEntry);
|
final SimpleHttpResponse cachedResponse = responseGenerator.generateResponse(request, cacheEntry);
|
||||||
cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
|
cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
|
||||||
return convert(cachedResponse);
|
return convert(cachedResponse, scope);
|
||||||
} finally {
|
} finally {
|
||||||
backendResponse.close();
|
backendResponse.close();
|
||||||
}
|
}
|
||||||
|
@ -344,7 +379,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
final boolean cacheable = responseCachingPolicy.isResponseCacheable(request, backendResponse);
|
final boolean cacheable = responseCachingPolicy.isResponseCacheable(request, backendResponse);
|
||||||
if (cacheable) {
|
if (cacheable) {
|
||||||
storeRequestIfModifiedSinceFor304Response(request, backendResponse);
|
storeRequestIfModifiedSinceFor304Response(request, backendResponse);
|
||||||
return cacheAndReturnResponse(target, request, backendResponse, requestDate, responseDate);
|
return cacheAndReturnResponse(target, request, backendResponse, scope, requestDate, responseDate);
|
||||||
} else {
|
} else {
|
||||||
log.debug("Backend response is not cacheable");
|
log.debug("Backend response is not cacheable");
|
||||||
responseCache.flushCacheEntriesFor(target, request);
|
responseCache.flushCacheEntriesFor(target, request);
|
||||||
|
@ -356,6 +391,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
final HttpHost target,
|
final HttpHost target,
|
||||||
final HttpRequest request,
|
final HttpRequest request,
|
||||||
final ClassicHttpResponse backendResponse,
|
final ClassicHttpResponse backendResponse,
|
||||||
|
final ExecChain.Scope scope,
|
||||||
final Date requestSent,
|
final Date requestSent,
|
||||||
final Date responseReceived) throws IOException {
|
final Date responseReceived) throws IOException {
|
||||||
log.debug("Caching backend response");
|
log.debug("Caching backend response");
|
||||||
|
@ -395,7 +431,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
cacheEntry = responseCache.createCacheEntry(target, request, backendResponse, buf, requestSent, responseReceived);
|
cacheEntry = responseCache.createCacheEntry(target, request, backendResponse, buf, requestSent, responseReceived);
|
||||||
log.debug("Backend response successfully cached (freshness check skipped)");
|
log.debug("Backend response successfully cached (freshness check skipped)");
|
||||||
}
|
}
|
||||||
return convert(responseGenerator.generateResponse(request, cacheEntry));
|
return convert(responseGenerator.generateResponse(request, cacheEntry), scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassicHttpResponse handleCacheMiss(
|
private ClassicHttpResponse handleCacheMiss(
|
||||||
|
@ -423,8 +459,7 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
final ExecChain.Scope scope,
|
final ExecChain.Scope scope,
|
||||||
final ExecChain chain,
|
final ExecChain chain,
|
||||||
final Map<String, Variant> variants) throws IOException, HttpException {
|
final Map<String, Variant> variants) throws IOException, HttpException {
|
||||||
final ClassicHttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequestFromVariants(
|
final ClassicHttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequestFromVariants(request, variants);
|
||||||
request, variants);
|
|
||||||
|
|
||||||
final Date requestDate = getCurrentDate();
|
final Date requestDate = getCurrentDate();
|
||||||
final ClassicHttpResponse backendResponse = chain.proceed(conditionalRequest, scope);
|
final ClassicHttpResponse backendResponse = chain.proceed(conditionalRequest, scope);
|
||||||
|
@ -468,11 +503,11 @@ public class CachingExec extends CachingExecBase implements ExecChainHandler {
|
||||||
target, conditionalRequest, backendResponse, matchingVariant, requestDate, responseDate);
|
target, conditionalRequest, backendResponse, matchingVariant, requestDate, responseDate);
|
||||||
backendResponse.close();
|
backendResponse.close();
|
||||||
if (shouldSendNotModifiedResponse(request, responseEntry)) {
|
if (shouldSendNotModifiedResponse(request, responseEntry)) {
|
||||||
return convert(responseGenerator.generateNotModifiedResponse(responseEntry));
|
return convert(responseGenerator.generateNotModifiedResponse(responseEntry), scope);
|
||||||
}
|
}
|
||||||
final SimpleHttpResponse resp = responseGenerator.generateResponse(request, responseEntry);
|
final SimpleHttpResponse response = responseGenerator.generateResponse(request, responseEntry);
|
||||||
responseCache.reuseVariantEntryFor(target, request, matchingVariant);
|
responseCache.reuseVariantEntryFor(target, request, matchingVariant);
|
||||||
return convert(resp);
|
return convert(response, scope);
|
||||||
} catch (final IOException | RuntimeException ex) {
|
} catch (final IOException | RuntimeException ex) {
|
||||||
backendResponse.close();
|
backendResponse.close();
|
||||||
throw ex;
|
throw ex;
|
||||||
|
|
|
@ -29,6 +29,8 @@ package org.apache.hc.client5.http.impl.cache;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.async.AsyncExecChainHandler;
|
import org.apache.hc.client5.http.async.AsyncExecChainHandler;
|
||||||
import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator;
|
import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator;
|
||||||
|
@ -38,6 +40,8 @@ import org.apache.hc.client5.http.cache.HttpCacheStorage;
|
||||||
import org.apache.hc.client5.http.cache.ResourceFactory;
|
import org.apache.hc.client5.http.cache.ResourceFactory;
|
||||||
import org.apache.hc.client5.http.impl.ChainElements;
|
import org.apache.hc.client5.http.impl.ChainElements;
|
||||||
import org.apache.hc.client5.http.impl.async.Http2AsyncClientBuilder;
|
import org.apache.hc.client5.http.impl.async.Http2AsyncClientBuilder;
|
||||||
|
import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy;
|
||||||
|
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
|
||||||
import org.apache.hc.core5.http.config.NamedElementChain;
|
import org.apache.hc.core5.http.config.NamedElementChain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,6 +55,7 @@ public class CachingHttp2AsyncClientBuilder extends Http2AsyncClientBuilder {
|
||||||
private ResourceFactory resourceFactory;
|
private ResourceFactory resourceFactory;
|
||||||
private HttpAsyncCacheStorage storage;
|
private HttpAsyncCacheStorage storage;
|
||||||
private File cacheDir;
|
private File cacheDir;
|
||||||
|
private SchedulingStrategy schedulingStrategy;
|
||||||
private CacheConfig cacheConfig;
|
private CacheConfig cacheConfig;
|
||||||
private HttpAsyncCacheInvalidator httpCacheInvalidator;
|
private HttpAsyncCacheInvalidator httpCacheInvalidator;
|
||||||
private boolean deleteCache;
|
private boolean deleteCache;
|
||||||
|
@ -84,6 +89,11 @@ public class CachingHttp2AsyncClientBuilder extends Http2AsyncClientBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final CachingHttp2AsyncClientBuilder setSchedulingStrategy(final SchedulingStrategy schedulingStrategy) {
|
||||||
|
this.schedulingStrategy = schedulingStrategy;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public final CachingHttp2AsyncClientBuilder setCacheConfig(final CacheConfig cacheConfig) {
|
public final CachingHttp2AsyncClientBuilder setCacheConfig(final CacheConfig cacheConfig) {
|
||||||
this.cacheConfig = cacheConfig;
|
this.cacheConfig = cacheConfig;
|
||||||
return this;
|
return this;
|
||||||
|
@ -138,7 +148,26 @@ public class CachingHttp2AsyncClientBuilder extends Http2AsyncClientBuilder {
|
||||||
CacheKeyGenerator.INSTANCE,
|
CacheKeyGenerator.INSTANCE,
|
||||||
this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultAsyncCacheInvalidator());
|
this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultAsyncCacheInvalidator());
|
||||||
|
|
||||||
final AsyncCachingExec cachingExec = new AsyncCachingExec(httpCache, config);
|
DefaultAsyncCacheRevalidator cacheRevalidator = null;
|
||||||
|
if (config.getAsynchronousWorkers() > 0) {
|
||||||
|
final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(config.getAsynchronousWorkers());
|
||||||
|
addCloseable(new Closeable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
executorService.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
cacheRevalidator = new DefaultAsyncCacheRevalidator(
|
||||||
|
executorService,
|
||||||
|
this.schedulingStrategy != null ? this.schedulingStrategy : ImmediateSchedulingStrategy.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
final AsyncCachingExec cachingExec = new AsyncCachingExec(
|
||||||
|
httpCache,
|
||||||
|
cacheRevalidator,
|
||||||
|
config);
|
||||||
execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name());
|
execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ package org.apache.hc.client5.http.impl.cache;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.async.AsyncExecChainHandler;
|
import org.apache.hc.client5.http.async.AsyncExecChainHandler;
|
||||||
import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator;
|
import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator;
|
||||||
|
@ -38,6 +40,8 @@ import org.apache.hc.client5.http.cache.HttpCacheStorage;
|
||||||
import org.apache.hc.client5.http.cache.ResourceFactory;
|
import org.apache.hc.client5.http.cache.ResourceFactory;
|
||||||
import org.apache.hc.client5.http.impl.ChainElements;
|
import org.apache.hc.client5.http.impl.ChainElements;
|
||||||
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
|
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
|
||||||
|
import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy;
|
||||||
|
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
|
||||||
import org.apache.hc.core5.http.config.NamedElementChain;
|
import org.apache.hc.core5.http.config.NamedElementChain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,6 +55,7 @@ public class CachingHttpAsyncClientBuilder extends HttpAsyncClientBuilder {
|
||||||
private ResourceFactory resourceFactory;
|
private ResourceFactory resourceFactory;
|
||||||
private HttpAsyncCacheStorage storage;
|
private HttpAsyncCacheStorage storage;
|
||||||
private File cacheDir;
|
private File cacheDir;
|
||||||
|
private SchedulingStrategy schedulingStrategy;
|
||||||
private CacheConfig cacheConfig;
|
private CacheConfig cacheConfig;
|
||||||
private HttpAsyncCacheInvalidator httpCacheInvalidator;
|
private HttpAsyncCacheInvalidator httpCacheInvalidator;
|
||||||
private boolean deleteCache;
|
private boolean deleteCache;
|
||||||
|
@ -84,6 +89,11 @@ public class CachingHttpAsyncClientBuilder extends HttpAsyncClientBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final CachingHttpAsyncClientBuilder setSchedulingStrategy(final SchedulingStrategy schedulingStrategy) {
|
||||||
|
this.schedulingStrategy = schedulingStrategy;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public final CachingHttpAsyncClientBuilder setCacheConfig(final CacheConfig cacheConfig) {
|
public final CachingHttpAsyncClientBuilder setCacheConfig(final CacheConfig cacheConfig) {
|
||||||
this.cacheConfig = cacheConfig;
|
this.cacheConfig = cacheConfig;
|
||||||
return this;
|
return this;
|
||||||
|
@ -138,7 +148,26 @@ public class CachingHttpAsyncClientBuilder extends HttpAsyncClientBuilder {
|
||||||
CacheKeyGenerator.INSTANCE,
|
CacheKeyGenerator.INSTANCE,
|
||||||
this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultAsyncCacheInvalidator());
|
this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultAsyncCacheInvalidator());
|
||||||
|
|
||||||
final AsyncCachingExec cachingExec = new AsyncCachingExec(httpCache, config);
|
DefaultAsyncCacheRevalidator cacheRevalidator = null;
|
||||||
|
if (config.getAsynchronousWorkers() > 0) {
|
||||||
|
final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(config.getAsynchronousWorkers());
|
||||||
|
addCloseable(new Closeable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
executorService.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
cacheRevalidator = new DefaultAsyncCacheRevalidator(
|
||||||
|
executorService,
|
||||||
|
this.schedulingStrategy != null ? this.schedulingStrategy : ImmediateSchedulingStrategy.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
final AsyncCachingExec cachingExec = new AsyncCachingExec(
|
||||||
|
httpCache,
|
||||||
|
cacheRevalidator,
|
||||||
|
config);
|
||||||
execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name());
|
execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ package org.apache.hc.client5.http.impl.cache;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheInvalidator;
|
import org.apache.hc.client5.http.cache.HttpCacheInvalidator;
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheStorage;
|
import org.apache.hc.client5.http.cache.HttpCacheStorage;
|
||||||
|
@ -36,6 +38,8 @@ import org.apache.hc.client5.http.cache.ResourceFactory;
|
||||||
import org.apache.hc.client5.http.classic.ExecChainHandler;
|
import org.apache.hc.client5.http.classic.ExecChainHandler;
|
||||||
import org.apache.hc.client5.http.impl.ChainElements;
|
import org.apache.hc.client5.http.impl.ChainElements;
|
||||||
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
|
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
|
||||||
|
import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy;
|
||||||
|
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
|
||||||
import org.apache.hc.core5.http.config.NamedElementChain;
|
import org.apache.hc.core5.http.config.NamedElementChain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,6 +53,7 @@ public class CachingHttpClientBuilder extends HttpClientBuilder {
|
||||||
private ResourceFactory resourceFactory;
|
private ResourceFactory resourceFactory;
|
||||||
private HttpCacheStorage storage;
|
private HttpCacheStorage storage;
|
||||||
private File cacheDir;
|
private File cacheDir;
|
||||||
|
private SchedulingStrategy schedulingStrategy;
|
||||||
private CacheConfig cacheConfig;
|
private CacheConfig cacheConfig;
|
||||||
private HttpCacheInvalidator httpCacheInvalidator;
|
private HttpCacheInvalidator httpCacheInvalidator;
|
||||||
private boolean deleteCache;
|
private boolean deleteCache;
|
||||||
|
@ -68,31 +73,32 @@ public class CachingHttpClientBuilder extends HttpClientBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final CachingHttpClientBuilder setHttpCacheStorage(
|
public final CachingHttpClientBuilder setHttpCacheStorage(final HttpCacheStorage storage) {
|
||||||
final HttpCacheStorage storage) {
|
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final CachingHttpClientBuilder setCacheDir(
|
public final CachingHttpClientBuilder setCacheDir(final File cacheDir) {
|
||||||
final File cacheDir) {
|
|
||||||
this.cacheDir = cacheDir;
|
this.cacheDir = cacheDir;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final CachingHttpClientBuilder setCacheConfig(
|
public final CachingHttpClientBuilder setSchedulingStrategy(final SchedulingStrategy schedulingStrategy) {
|
||||||
final CacheConfig cacheConfig) {
|
this.schedulingStrategy = schedulingStrategy;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final CachingHttpClientBuilder setCacheConfig(final CacheConfig cacheConfig) {
|
||||||
this.cacheConfig = cacheConfig;
|
this.cacheConfig = cacheConfig;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final CachingHttpClientBuilder setHttpCacheInvalidator(
|
public final CachingHttpClientBuilder setHttpCacheInvalidator(final HttpCacheInvalidator cacheInvalidator) {
|
||||||
final HttpCacheInvalidator cacheInvalidator) {
|
|
||||||
this.httpCacheInvalidator = cacheInvalidator;
|
this.httpCacheInvalidator = cacheInvalidator;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachingHttpClientBuilder setDeleteCache(final boolean deleteCache) {
|
public final CachingHttpClientBuilder setDeleteCache(final boolean deleteCache) {
|
||||||
this.deleteCache = deleteCache;
|
this.deleteCache = deleteCache;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -136,7 +142,25 @@ public class CachingHttpClientBuilder extends HttpClientBuilder {
|
||||||
CacheKeyGenerator.INSTANCE,
|
CacheKeyGenerator.INSTANCE,
|
||||||
this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultCacheInvalidator());
|
this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultCacheInvalidator());
|
||||||
|
|
||||||
final CachingExec cachingExec = new CachingExec(httpCache, config);
|
DefaultCacheRevalidator cacheRevalidator = null;
|
||||||
|
if (config.getAsynchronousWorkers() > 0) {
|
||||||
|
final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(config.getAsynchronousWorkers());
|
||||||
|
addCloseable(new Closeable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
executorService.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
cacheRevalidator = new DefaultCacheRevalidator(
|
||||||
|
executorService,
|
||||||
|
this.schedulingStrategy != null ? this.schedulingStrategy : ImmediateSchedulingStrategy.INSTANCE);
|
||||||
|
}
|
||||||
|
final CachingExec cachingExec = new CachingExec(
|
||||||
|
httpCache,
|
||||||
|
cacheRevalidator,
|
||||||
|
config);
|
||||||
execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name());
|
execChainDefinition.addBefore(ChainElements.PROTOCOL.name(), cachingExec, ChainElements.CACHING.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,20 +26,20 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hc.client5.http.impl.cache;
|
package org.apache.hc.client5.http.impl.cache;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import org.apache.hc.client5.http.async.AsyncExecCallback;
|
import org.apache.hc.client5.http.async.AsyncExecCallback;
|
||||||
import org.apache.hc.client5.http.async.AsyncExecChain;
|
import org.apache.hc.client5.http.impl.Operations;
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
|
||||||
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
|
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
|
||||||
import org.apache.hc.core5.http.HttpHost;
|
import org.apache.hc.core5.http.EntityDetails;
|
||||||
import org.apache.hc.core5.http.HttpRequest;
|
import org.apache.hc.core5.http.HttpException;
|
||||||
import org.apache.hc.core5.http.nio.AsyncEntityProducer;
|
import org.apache.hc.core5.http.HttpResponse;
|
||||||
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
|
import org.apache.hc.core5.http.nio.AsyncDataConsumer;
|
||||||
import org.apache.hc.core5.util.TimeValue;
|
import org.apache.hc.core5.util.TimeValue;
|
||||||
import org.apache.hc.core5.util.Timeout;
|
import org.apache.hc.core5.util.Timeout;
|
||||||
|
|
||||||
|
@ -49,33 +49,10 @@ import org.apache.hc.core5.util.Timeout;
|
||||||
*/
|
*/
|
||||||
class DefaultAsyncCacheRevalidator extends CacheRevalidatorBase {
|
class DefaultAsyncCacheRevalidator extends CacheRevalidatorBase {
|
||||||
|
|
||||||
private static final Future<Void> NOOP_FUTURE = new Future<Void>() {
|
interface RevalidationCall {
|
||||||
|
|
||||||
@Override
|
void execute(AsyncExecCallback asyncExecCallback);;
|
||||||
public Void get() throws InterruptedException, ExecutionException {
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public boolean cancel(final boolean mayInterruptIfRunning) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCancelled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDone() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
static class InternalScheduledExecutor implements ScheduledExecutor {
|
static class InternalScheduledExecutor implements ScheduledExecutor {
|
||||||
|
|
||||||
|
@ -89,7 +66,7 @@ class DefaultAsyncCacheRevalidator extends CacheRevalidatorBase {
|
||||||
public Future<?> schedule(final Runnable command, final TimeValue timeValue) throws RejectedExecutionException {
|
public Future<?> schedule(final Runnable command, final TimeValue timeValue) throws RejectedExecutionException {
|
||||||
if (timeValue.toMillis() <= 0) {
|
if (timeValue.toMillis() <= 0) {
|
||||||
command.run();
|
command.run();
|
||||||
return NOOP_FUTURE;
|
return new Operations.CompletedFuture<Void>(null);
|
||||||
} else {
|
} else {
|
||||||
return executor.schedule(command, timeValue);
|
return executor.schedule(command, timeValue);
|
||||||
}
|
}
|
||||||
|
@ -107,7 +84,6 @@ class DefaultAsyncCacheRevalidator extends CacheRevalidatorBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final AsyncCachingExec cachingExec;
|
|
||||||
private final CacheKeyGenerator cacheKeyGenerator;
|
private final CacheKeyGenerator cacheKeyGenerator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,42 +92,72 @@ class DefaultAsyncCacheRevalidator extends CacheRevalidatorBase {
|
||||||
*/
|
*/
|
||||||
public DefaultAsyncCacheRevalidator(
|
public DefaultAsyncCacheRevalidator(
|
||||||
final ScheduledExecutor scheduledExecutor,
|
final ScheduledExecutor scheduledExecutor,
|
||||||
final SchedulingStrategy schedulingStrategy,
|
final SchedulingStrategy schedulingStrategy) {
|
||||||
final AsyncCachingExec cachingExec) {
|
|
||||||
super(new InternalScheduledExecutor(scheduledExecutor), schedulingStrategy);
|
super(new InternalScheduledExecutor(scheduledExecutor), schedulingStrategy);
|
||||||
this.cachingExec = cachingExec;
|
|
||||||
this.cacheKeyGenerator = CacheKeyGenerator.INSTANCE;
|
this.cacheKeyGenerator = CacheKeyGenerator.INSTANCE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create CacheValidator which will make ache revalidation requests
|
* Create CacheValidator which will make ache revalidation requests
|
||||||
* using the supplied {@link SchedulingStrategy} and {@link ScheduledThreadPoolExecutor}.
|
* using the supplied {@link SchedulingStrategy} and {@link ScheduledExecutorService}.
|
||||||
*/
|
*/
|
||||||
public DefaultAsyncCacheRevalidator(
|
public DefaultAsyncCacheRevalidator(
|
||||||
final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor,
|
final ScheduledExecutorService executorService,
|
||||||
final SchedulingStrategy schedulingStrategy,
|
final SchedulingStrategy schedulingStrategy) {
|
||||||
final AsyncCachingExec cachingExec) {
|
this(wrap(executorService), schedulingStrategy);
|
||||||
this(wrap(scheduledThreadPoolExecutor), schedulingStrategy, cachingExec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedules an asynchronous re-validation
|
* Schedules an asynchronous re-validation
|
||||||
*/
|
*/
|
||||||
public void revalidateCacheEntry(
|
public void revalidateCacheEntry(
|
||||||
final HttpHost target,
|
final String cacheKey ,
|
||||||
final HttpRequest request,
|
|
||||||
final AsyncEntityProducer entityProducer,
|
|
||||||
final AsyncExecChain.Scope scope,
|
|
||||||
final AsyncExecChain chain,
|
|
||||||
final AsyncExecCallback asyncExecCallback,
|
final AsyncExecCallback asyncExecCallback,
|
||||||
final HttpCacheEntry entry) {
|
final RevalidationCall call) {
|
||||||
final String cacheKey = cacheKeyGenerator.generateKey(target, request, entry);
|
|
||||||
scheduleRevalidation(cacheKey, new Runnable() {
|
scheduleRevalidation(cacheKey, new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
cachingExec.revalidateCacheEntry(target, request, entityProducer, scope, chain, asyncExecCallback, entry);
|
call.execute(new AsyncExecCallback() {
|
||||||
|
|
||||||
|
private final AtomicReference<HttpResponse> responseRef = new AtomicReference<>(null);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AsyncDataConsumer handleResponse(
|
||||||
|
final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
|
||||||
|
responseRef.set(response);
|
||||||
|
return asyncExecCallback.handleResponse(response, entityDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void completed() {
|
||||||
|
final HttpResponse httpResponse = responseRef.getAndSet(null);
|
||||||
|
if (httpResponse != null && httpResponse.getCode() < HttpStatus.SC_SERVER_ERROR && !isStale(httpResponse)) {
|
||||||
|
jobSuccessful(cacheKey);
|
||||||
|
} else {
|
||||||
|
jobFailed(cacheKey);
|
||||||
|
}
|
||||||
|
asyncExecCallback.completed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(final Exception cause) {
|
||||||
|
if (cause instanceof IOException) {
|
||||||
|
log.debug("Asynchronous revalidation failed due to I/O error", cause);
|
||||||
|
} else if (cause instanceof HttpException) {
|
||||||
|
log.error("HTTP protocol exception during asynchronous revalidation", cause);
|
||||||
|
} else {
|
||||||
|
log.error("Unexpected runtime exception thrown during asynchronous revalidation", cause);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
jobFailed(cacheKey);
|
||||||
|
} finally {
|
||||||
|
asyncExecCallback.failed(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,15 +27,11 @@
|
||||||
package org.apache.hc.client5.http.impl.cache;
|
package org.apache.hc.client5.http.impl.cache;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
|
||||||
import org.apache.hc.client5.http.classic.ExecChain;
|
|
||||||
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
|
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
|
||||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||||
import org.apache.hc.core5.http.HttpException;
|
import org.apache.hc.core5.http.HttpException;
|
||||||
import org.apache.hc.core5.http.HttpHost;
|
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,8 +40,10 @@ import org.apache.hc.core5.http.HttpStatus;
|
||||||
*/
|
*/
|
||||||
class DefaultCacheRevalidator extends CacheRevalidatorBase {
|
class DefaultCacheRevalidator extends CacheRevalidatorBase {
|
||||||
|
|
||||||
private final CachingExec cachingExec;
|
interface RevalidationCall {
|
||||||
private final CacheKeyGenerator cacheKeyGenerator;
|
|
||||||
|
ClassicHttpResponse execute() throws IOException, HttpException;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create DefaultCacheRevalidator which will make ache revalidation requests
|
* Create DefaultCacheRevalidator which will make ache revalidation requests
|
||||||
|
@ -53,56 +51,45 @@ class DefaultCacheRevalidator extends CacheRevalidatorBase {
|
||||||
*/
|
*/
|
||||||
public DefaultCacheRevalidator(
|
public DefaultCacheRevalidator(
|
||||||
final CacheRevalidatorBase.ScheduledExecutor scheduledExecutor,
|
final CacheRevalidatorBase.ScheduledExecutor scheduledExecutor,
|
||||||
final SchedulingStrategy schedulingStrategy,
|
final SchedulingStrategy schedulingStrategy) {
|
||||||
final CachingExec cachingExec) {
|
|
||||||
super(scheduledExecutor, schedulingStrategy);
|
super(scheduledExecutor, schedulingStrategy);
|
||||||
this.cachingExec = cachingExec;
|
|
||||||
this.cacheKeyGenerator = CacheKeyGenerator.INSTANCE;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create CacheValidator which will make ache revalidation requests
|
* Create CacheValidator which will make ache revalidation requests
|
||||||
* using the supplied {@link SchedulingStrategy} and {@link ScheduledThreadPoolExecutor}.
|
* using the supplied {@link SchedulingStrategy} and {@link ScheduledExecutorService}.
|
||||||
*/
|
*/
|
||||||
public DefaultCacheRevalidator(
|
public DefaultCacheRevalidator(
|
||||||
final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor,
|
final ScheduledExecutorService scheduledThreadPoolExecutor,
|
||||||
final SchedulingStrategy schedulingStrategy,
|
final SchedulingStrategy schedulingStrategy) {
|
||||||
final CachingExec cachingExec) {
|
this(wrap(scheduledThreadPoolExecutor), schedulingStrategy);
|
||||||
this(wrap(scheduledThreadPoolExecutor), schedulingStrategy, cachingExec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedules an asynchronous re-validation
|
* Schedules an asynchronous re-validation
|
||||||
*/
|
*/
|
||||||
public void revalidateCacheEntry(
|
public void revalidateCacheEntry(
|
||||||
final HttpHost target,
|
final String cacheKey,
|
||||||
final ClassicHttpRequest request,
|
final RevalidationCall call) {
|
||||||
final ExecChain.Scope scope,
|
|
||||||
final ExecChain chain,
|
|
||||||
final HttpCacheEntry entry) {
|
|
||||||
final String cacheKey = cacheKeyGenerator.generateKey(target, request, entry);
|
|
||||||
scheduleRevalidation(cacheKey, new Runnable() {
|
scheduleRevalidation(cacheKey, new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try (ClassicHttpResponse httpResponse = call.execute()) {
|
||||||
try (ClassicHttpResponse httpResponse = cachingExec.revalidateCacheEntry(target, request, scope, chain, entry)) {
|
if (httpResponse.getCode() < HttpStatus.SC_SERVER_ERROR && !isStale(httpResponse)) {
|
||||||
if (httpResponse.getCode() < HttpStatus.SC_SERVER_ERROR && !isStale(httpResponse)) {
|
jobSuccessful(cacheKey);
|
||||||
jobSuccessful(cacheKey);
|
} else {
|
||||||
} else {
|
jobFailed(cacheKey);
|
||||||
jobFailed(cacheKey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (final IOException ioe) {
|
} catch (final IOException ex) {
|
||||||
jobFailed(cacheKey);
|
jobFailed(cacheKey);
|
||||||
log.debug("Asynchronous revalidation failed due to I/O error", ioe);
|
log.debug("Asynchronous revalidation failed due to I/O error", ex);
|
||||||
} catch (final HttpException pe) {
|
} catch (final HttpException ex) {
|
||||||
jobFailed(cacheKey);
|
jobFailed(cacheKey);
|
||||||
log.error("HTTP protocol exception during asynchronous revalidation", pe);
|
log.error("HTTP protocol exception during asynchronous revalidation", ex);
|
||||||
} catch (final RuntimeException re) {
|
} catch (final RuntimeException ex) {
|
||||||
jobFailed(cacheKey);
|
jobFailed(cacheKey);
|
||||||
log.error("Unexpected runtime exception thrown during asynchronous revalidation" + re);
|
log.error("Unexpected runtime exception thrown during asynchronous revalidation", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@ import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||||
@Internal
|
@Internal
|
||||||
interface HttpAsyncCache {
|
interface HttpAsyncCache {
|
||||||
|
|
||||||
|
String generateKey (HttpHost host, HttpRequest request, HttpCacheEntry cacheEntry);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all matching {@link HttpCacheEntry}s.
|
* Clear all matching {@link HttpCacheEntry}s.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -40,6 +40,8 @@ import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||||
*/
|
*/
|
||||||
interface HttpCache {
|
interface HttpCache {
|
||||||
|
|
||||||
|
String generateKey (HttpHost host, HttpRequest request, HttpCacheEntry cacheEntry);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all matching {@link HttpCacheEntry}s.
|
* Clear all matching {@link HttpCacheEntry}s.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -39,7 +39,7 @@ import org.apache.hc.core5.util.TimeValue;
|
||||||
@Contract(threading = ThreadingBehavior.STATELESS)
|
@Contract(threading = ThreadingBehavior.STATELESS)
|
||||||
public class ImmediateSchedulingStrategy implements SchedulingStrategy {
|
public class ImmediateSchedulingStrategy implements SchedulingStrategy {
|
||||||
|
|
||||||
private final static ImmediateSchedulingStrategy INSTANCE = new ImmediateSchedulingStrategy();
|
public final static ImmediateSchedulingStrategy INSTANCE = new ImmediateSchedulingStrategy();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TimeValue schedule(final int attemptNumber) {
|
public TimeValue schedule(final int attemptNumber) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ public abstract class AbstractProtocolTest {
|
||||||
protected HttpEntity body;
|
protected HttpEntity body;
|
||||||
protected HttpClientContext context;
|
protected HttpClientContext context;
|
||||||
protected ExecChain mockExecChain;
|
protected ExecChain mockExecChain;
|
||||||
protected ExecRuntime mockEndpoint;
|
protected ExecRuntime mockExecRuntime;
|
||||||
protected HttpCache mockCache;
|
protected HttpCache mockCache;
|
||||||
protected ClassicHttpRequest request;
|
protected ClassicHttpRequest request;
|
||||||
protected ClassicHttpResponse originResponse;
|
protected ClassicHttpResponse originResponse;
|
||||||
|
@ -101,18 +101,18 @@ public abstract class AbstractProtocolTest {
|
||||||
|
|
||||||
cache = new BasicHttpCache(config);
|
cache = new BasicHttpCache(config);
|
||||||
mockExecChain = EasyMock.createNiceMock(ExecChain.class);
|
mockExecChain = EasyMock.createNiceMock(ExecChain.class);
|
||||||
mockEndpoint = EasyMock.createNiceMock(ExecRuntime.class);
|
mockExecRuntime = EasyMock.createNiceMock(ExecRuntime.class);
|
||||||
mockCache = EasyMock.createNiceMock(HttpCache.class);
|
mockCache = EasyMock.createNiceMock(HttpCache.class);
|
||||||
impl = createCachingExecChain(cache, config);
|
impl = createCachingExecChain(cache, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
|
public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException {
|
||||||
return impl.execute(ClassicRequestCopier.INSTANCE.copy(request), new ExecChain.Scope(
|
return impl.execute(ClassicRequestCopier.INSTANCE.copy(request), new ExecChain.Scope(
|
||||||
"test", route, request, mockEndpoint, context), mockExecChain);
|
"test", route, request, mockExecRuntime, context), mockExecChain);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ExecChainHandler createCachingExecChain(final HttpCache cache, final CacheConfig config) {
|
protected ExecChainHandler createCachingExecChain(final HttpCache cache, final CacheConfig config) {
|
||||||
return new CachingExec(cache, config);
|
return new CachingExec(cache, null, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean supportsRangeAndContentRangeHeaders(final ExecChainHandler impl) {
|
protected boolean supportsRangeAndContentRangeHeaders(final ExecChainHandler impl) {
|
||||||
|
@ -148,7 +148,7 @@ public abstract class AbstractProtocolTest {
|
||||||
mockExecChain = EasyMock.createNiceMock(ExecChain.class);
|
mockExecChain = EasyMock.createNiceMock(ExecChain.class);
|
||||||
mockCache = EasyMock.createNiceMock(HttpCache.class);
|
mockCache = EasyMock.createNiceMock(HttpCache.class);
|
||||||
|
|
||||||
impl = new CachingExec(mockCache, config);
|
impl = new CachingExec(mockCache, null, config);
|
||||||
|
|
||||||
EasyMock.expect(mockCache.getCacheEntry(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)))
|
EasyMock.expect(mockCache.getCacheEntry(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)))
|
||||||
.andReturn(null).anyTimes();
|
.andReturn(null).anyTimes();
|
||||||
|
@ -174,7 +174,7 @@ public abstract class AbstractProtocolTest {
|
||||||
.setMaxObjectSize(MAX_BYTES)
|
.setMaxObjectSize(MAX_BYTES)
|
||||||
.setSharedCache(false)
|
.setSharedCache(false)
|
||||||
.build();
|
.build();
|
||||||
impl = new CachingExec(cache, config);
|
impl = new CachingExec(cache, null, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractProtocolTest() {
|
public AbstractProtocolTest() {
|
||||||
|
|
|
@ -65,7 +65,6 @@ import org.easymock.EasyMock;
|
||||||
import org.easymock.IExpectationSetters;
|
import org.easymock.IExpectationSetters;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@SuppressWarnings("boxing") // test code
|
@SuppressWarnings("boxing") // test code
|
||||||
|
@ -107,9 +106,10 @@ public class TestCachingExec extends TestCachingExecChain {
|
||||||
final CachedHttpResponseGenerator mockResponseGenerator,
|
final CachedHttpResponseGenerator mockResponseGenerator,
|
||||||
final CacheableRequestPolicy mockRequestPolicy,
|
final CacheableRequestPolicy mockRequestPolicy,
|
||||||
final CachedResponseSuitabilityChecker mockSuitabilityChecker,
|
final CachedResponseSuitabilityChecker mockSuitabilityChecker,
|
||||||
final ConditionalRequestBuilder<ClassicHttpRequest> mockConditionalRequestBuilder,
|
|
||||||
final ResponseProtocolCompliance mockResponseProtocolCompliance,
|
final ResponseProtocolCompliance mockResponseProtocolCompliance,
|
||||||
final RequestProtocolCompliance mockRequestProtocolCompliance,
|
final RequestProtocolCompliance mockRequestProtocolCompliance,
|
||||||
|
final DefaultCacheRevalidator mockCacheRevalidator,
|
||||||
|
final ConditionalRequestBuilder<ClassicHttpRequest> mockConditionalRequestBuilder,
|
||||||
final CacheConfig config) {
|
final CacheConfig config) {
|
||||||
return impl = new CachingExec(
|
return impl = new CachingExec(
|
||||||
mockCache,
|
mockCache,
|
||||||
|
@ -118,15 +118,16 @@ public class TestCachingExec extends TestCachingExecChain {
|
||||||
mockResponseGenerator,
|
mockResponseGenerator,
|
||||||
mockRequestPolicy,
|
mockRequestPolicy,
|
||||||
mockSuitabilityChecker,
|
mockSuitabilityChecker,
|
||||||
mockConditionalRequestBuilder,
|
|
||||||
mockResponseProtocolCompliance,
|
mockResponseProtocolCompliance,
|
||||||
mockRequestProtocolCompliance,
|
mockRequestProtocolCompliance,
|
||||||
|
mockCacheRevalidator,
|
||||||
|
mockConditionalRequestBuilder,
|
||||||
config);
|
config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CachingExec createCachingExecChain(final HttpCache cache, final CacheConfig config) {
|
public CachingExec createCachingExecChain(final HttpCache cache, final CacheConfig config) {
|
||||||
return impl = new CachingExec(cache, config);
|
return impl = new CachingExec(cache, null, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -210,7 +211,7 @@ public class TestCachingExec extends TestCachingExecChain {
|
||||||
Assert.assertEquals(1, impl.getCacheUpdates());
|
Assert.assertEquals(1, impl.getCacheUpdates());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test @Ignore
|
@Test
|
||||||
public void testUnsuitableValidatableCacheEntryCausesRevalidation() throws Exception {
|
public void testUnsuitableValidatableCacheEntryCausesRevalidation() throws Exception {
|
||||||
mockImplMethods(REVALIDATE_CACHE_ENTRY);
|
mockImplMethods(REVALIDATE_CACHE_ENTRY);
|
||||||
requestPolicyAllowsCaching(true);
|
requestPolicyAllowsCaching(true);
|
||||||
|
@ -447,9 +448,10 @@ public class TestCachingExec extends TestCachingExecChain {
|
||||||
mockResponseGenerator,
|
mockResponseGenerator,
|
||||||
mockRequestPolicy,
|
mockRequestPolicy,
|
||||||
mockSuitabilityChecker,
|
mockSuitabilityChecker,
|
||||||
mockConditionalRequestBuilder,
|
|
||||||
mockResponseProtocolCompliance,
|
mockResponseProtocolCompliance,
|
||||||
mockRequestProtocolCompliance,
|
mockRequestProtocolCompliance,
|
||||||
|
mockCacheRevalidator,
|
||||||
|
mockConditionalRequestBuilder,
|
||||||
config).addMockedMethods(methods).createNiceMock();
|
config).addMockedMethods(methods).createNiceMock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,10 +104,11 @@ public abstract class TestCachingExecChain {
|
||||||
protected CachedHttpResponseGenerator mockResponseGenerator;
|
protected CachedHttpResponseGenerator mockResponseGenerator;
|
||||||
private HttpClientResponseHandler<Object> mockHandler;
|
private HttpClientResponseHandler<Object> mockHandler;
|
||||||
private ClassicHttpRequest mockUriRequest;
|
private ClassicHttpRequest mockUriRequest;
|
||||||
protected ConditionalRequestBuilder<ClassicHttpRequest> mockConditionalRequestBuilder;
|
|
||||||
private HttpRequest mockConditionalRequest;
|
private HttpRequest mockConditionalRequest;
|
||||||
protected ResponseProtocolCompliance mockResponseProtocolCompliance;
|
protected ResponseProtocolCompliance mockResponseProtocolCompliance;
|
||||||
protected RequestProtocolCompliance mockRequestProtocolCompliance;
|
protected RequestProtocolCompliance mockRequestProtocolCompliance;
|
||||||
|
protected DefaultCacheRevalidator mockCacheRevalidator;
|
||||||
|
protected ConditionalRequestBuilder<ClassicHttpRequest> mockConditionalRequestBuilder;
|
||||||
protected CacheConfig config;
|
protected CacheConfig config;
|
||||||
|
|
||||||
protected HttpRoute route;
|
protected HttpRoute route;
|
||||||
|
@ -130,10 +131,11 @@ public abstract class TestCachingExecChain {
|
||||||
mockUriRequest = createNiceMock(ClassicHttpRequest.class);
|
mockUriRequest = createNiceMock(ClassicHttpRequest.class);
|
||||||
mockCacheEntry = createNiceMock(HttpCacheEntry.class);
|
mockCacheEntry = createNiceMock(HttpCacheEntry.class);
|
||||||
mockResponseGenerator = createNiceMock(CachedHttpResponseGenerator.class);
|
mockResponseGenerator = createNiceMock(CachedHttpResponseGenerator.class);
|
||||||
mockConditionalRequestBuilder = createNiceMock(ConditionalRequestBuilder.class);
|
|
||||||
mockConditionalRequest = createNiceMock(HttpRequest.class);
|
mockConditionalRequest = createNiceMock(HttpRequest.class);
|
||||||
mockResponseProtocolCompliance = createNiceMock(ResponseProtocolCompliance.class);
|
mockResponseProtocolCompliance = createNiceMock(ResponseProtocolCompliance.class);
|
||||||
mockRequestProtocolCompliance = createNiceMock(RequestProtocolCompliance.class);
|
mockRequestProtocolCompliance = createNiceMock(RequestProtocolCompliance.class);
|
||||||
|
mockCacheRevalidator = createNiceMock(DefaultCacheRevalidator.class);
|
||||||
|
mockConditionalRequestBuilder = createNiceMock(ConditionalRequestBuilder.class);
|
||||||
mockStorage = createNiceMock(HttpCacheStorage.class);
|
mockStorage = createNiceMock(HttpCacheStorage.class);
|
||||||
config = CacheConfig.DEFAULT;
|
config = CacheConfig.DEFAULT;
|
||||||
|
|
||||||
|
@ -143,9 +145,9 @@ public abstract class TestCachingExecChain {
|
||||||
context = HttpCacheContext.create();
|
context = HttpCacheContext.create();
|
||||||
entry = HttpTestUtils.makeCacheEntry();
|
entry = HttpTestUtils.makeCacheEntry();
|
||||||
impl = createCachingExecChain(mockCache, mockValidityPolicy,
|
impl = createCachingExecChain(mockCache, mockValidityPolicy,
|
||||||
mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker,
|
mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker,
|
||||||
mockConditionalRequestBuilder, mockResponseProtocolCompliance,
|
mockResponseProtocolCompliance,mockRequestProtocolCompliance,
|
||||||
mockRequestProtocolCompliance, config);
|
mockCacheRevalidator, mockConditionalRequestBuilder, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract CachingExec createCachingExecChain(
|
public abstract CachingExec createCachingExecChain(
|
||||||
|
@ -153,8 +155,9 @@ public abstract class TestCachingExecChain {
|
||||||
ResponseCachingPolicy responseCachingPolicy, CachedHttpResponseGenerator responseGenerator,
|
ResponseCachingPolicy responseCachingPolicy, CachedHttpResponseGenerator responseGenerator,
|
||||||
CacheableRequestPolicy cacheableRequestPolicy,
|
CacheableRequestPolicy cacheableRequestPolicy,
|
||||||
CachedResponseSuitabilityChecker suitabilityChecker,
|
CachedResponseSuitabilityChecker suitabilityChecker,
|
||||||
ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder,
|
|
||||||
ResponseProtocolCompliance responseCompliance, RequestProtocolCompliance requestCompliance,
|
ResponseProtocolCompliance responseCompliance, RequestProtocolCompliance requestCompliance,
|
||||||
|
DefaultCacheRevalidator cacheRevalidator,
|
||||||
|
ConditionalRequestBuilder<ClassicHttpRequest> conditionalRequestBuilder,
|
||||||
CacheConfig config);
|
CacheConfig config);
|
||||||
|
|
||||||
public abstract CachingExec createCachingExecChain(HttpCache cache, CacheConfig config);
|
public abstract CachingExec createCachingExecChain(HttpCache cache, CacheConfig config);
|
||||||
|
@ -1242,11 +1245,11 @@ public abstract class TestCachingExecChain {
|
||||||
mockCache = EasyMock.createStrictMock(HttpCache.class);
|
mockCache = EasyMock.createStrictMock(HttpCache.class);
|
||||||
impl = createCachingExecChain(mockCache, mockValidityPolicy,
|
impl = createCachingExecChain(mockCache, mockValidityPolicy,
|
||||||
mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker,
|
mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker,
|
||||||
mockConditionalRequestBuilder, mockResponseProtocolCompliance,
|
mockResponseProtocolCompliance, mockRequestProtocolCompliance,
|
||||||
mockRequestProtocolCompliance, config);
|
mockCacheRevalidator, mockConditionalRequestBuilder, config);
|
||||||
|
|
||||||
final HttpHost host = new HttpHost("foo.example.com");
|
final HttpHost host = new HttpHost("foo.example.com");
|
||||||
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
|
final ClassicHttpRequest request = new HttpGet("http://foo.example.com/bar");
|
||||||
|
|
||||||
final Date now = new Date();
|
final Date now = new Date();
|
||||||
final Date requestSent = new Date(now.getTime() - 3 * 1000L);
|
final Date requestSent = new Date(now.getTime() - 3 * 1000L);
|
||||||
|
@ -1260,8 +1263,8 @@ public abstract class TestCachingExecChain {
|
||||||
originResponse.setHeader("ETag", "\"etag\"");
|
originResponse.setHeader("ETag", "\"etag\"");
|
||||||
|
|
||||||
replayMocks();
|
replayMocks();
|
||||||
|
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockEndpoint, context);
|
||||||
impl.cacheAndReturnResponse(host, request, originResponse, requestSent, responseReceived);
|
impl.cacheAndReturnResponse(host, request, originResponse, scope, requestSent, responseReceived);
|
||||||
|
|
||||||
verifyMocks();
|
verifyMocks();
|
||||||
}
|
}
|
||||||
|
@ -1269,7 +1272,7 @@ public abstract class TestCachingExecChain {
|
||||||
@Test
|
@Test
|
||||||
public void testSmallEnoughResponsesAreCached() throws Exception {
|
public void testSmallEnoughResponsesAreCached() throws Exception {
|
||||||
final HttpHost host = new HttpHost("foo.example.com");
|
final HttpHost host = new HttpHost("foo.example.com");
|
||||||
final HttpRequest request = new HttpGet("http://foo.example.com/bar");
|
final ClassicHttpRequest request = new HttpGet("http://foo.example.com/bar");
|
||||||
|
|
||||||
final Date now = new Date();
|
final Date now = new Date();
|
||||||
final Date requestSent = new Date(now.getTime() - 3 * 1000L);
|
final Date requestSent = new Date(now.getTime() - 3 * 1000L);
|
||||||
|
@ -1297,7 +1300,8 @@ public abstract class TestCachingExecChain {
|
||||||
same(httpCacheEntry))).andReturn(response).once();
|
same(httpCacheEntry))).andReturn(response).once();
|
||||||
replayMocks();
|
replayMocks();
|
||||||
|
|
||||||
impl.cacheAndReturnResponse(host, request, originResponse, requestSent, responseReceived);
|
final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockEndpoint, context);
|
||||||
|
impl.cacheAndReturnResponse(host, request, originResponse, scope, requestSent, responseReceived);
|
||||||
|
|
||||||
verifyMocks();
|
verifyMocks();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
* ====================================================================
|
|
||||||
*
|
|
||||||
* This software consists of voluntary contributions made by many
|
|
||||||
* individuals on behalf of the Apache Software Foundation. For more
|
|
||||||
* information on the Apache Software Foundation, please see
|
|
||||||
* <http://www.apache.org/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package org.apache.hc.client5.http.impl.cache;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class TestCachingHttpClientBuilder {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAsynchronousWorkersMax0() throws Exception {
|
|
||||||
final CacheConfig cacheConfig = CacheConfig.custom()
|
|
||||||
.setAsynchronousWorkersMax(0)
|
|
||||||
.build();
|
|
||||||
// Asynchronous validation should be disabled but we should not get an
|
|
||||||
// Exception
|
|
||||||
CachingHttpClientBuilder.create().setCacheConfig(cacheConfig).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNullCacheConfig() throws Exception {
|
|
||||||
CachingHttpClientBuilder.create().setCacheConfig(null).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -142,7 +142,7 @@ public class TestHttpCacheJiraNumber1147 {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ExecChainHandler createCachingExecChain(final BasicHttpCache cache, final CacheConfig config) {
|
protected ExecChainHandler createCachingExecChain(final BasicHttpCache cache, final CacheConfig config) {
|
||||||
return new CachingExec(cache, config);
|
return new CachingExec(cache, null, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ public class TestProtocolDeviations {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ExecChainHandler createCachingExecChain(final HttpCache cache, final CacheConfig config) {
|
protected ExecChainHandler createCachingExecChain(final HttpCache cache, final CacheConfig config) {
|
||||||
return new CachingExec(cache, config);
|
return new CachingExec(cache, null, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassicHttpResponse make200Response() {
|
private ClassicHttpResponse make200Response() {
|
||||||
|
|
|
@ -2166,7 +2166,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
||||||
|
|
||||||
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
|
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
|
||||||
|
|
||||||
impl = new CachingExec(mockCache, config);
|
impl = new CachingExec(mockCache, null, config);
|
||||||
|
|
||||||
request = new BasicClassicHttpRequest("GET", "/thing");
|
request = new BasicClassicHttpRequest("GET", "/thing");
|
||||||
|
|
||||||
|
@ -2217,7 +2217,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
||||||
|
|
||||||
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
|
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
|
||||||
|
|
||||||
impl = new CachingExec(mockCache, config);
|
impl = new CachingExec(mockCache, null, config);
|
||||||
request = new BasicClassicHttpRequest("GET", "/thing");
|
request = new BasicClassicHttpRequest("GET", "/thing");
|
||||||
|
|
||||||
EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
|
EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
|
||||||
|
@ -2263,7 +2263,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
||||||
|
|
||||||
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
|
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
|
||||||
|
|
||||||
impl = new CachingExec(mockCache, config);
|
impl = new CachingExec(mockCache, null, config);
|
||||||
request = new BasicClassicHttpRequest("GET", "/thing");
|
request = new BasicClassicHttpRequest("GET", "/thing");
|
||||||
|
|
||||||
EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
|
EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
|
||||||
|
@ -2471,7 +2471,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
||||||
|
|
||||||
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
|
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes);
|
||||||
|
|
||||||
impl = new CachingExec(mockCache, config);
|
impl = new CachingExec(mockCache, null, config);
|
||||||
request = new BasicClassicHttpRequest("GET", "/thing");
|
request = new BasicClassicHttpRequest("GET", "/thing");
|
||||||
|
|
||||||
EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
|
EasyMock.expect(mockCache.getCacheEntry(EasyMock.eq(host), eqRequest(request))).andReturn(entry);
|
||||||
|
@ -2521,7 +2521,7 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
|
||||||
|
|
||||||
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(requestTime, responseTime, hdrs, bytes);
|
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(requestTime, responseTime, hdrs, bytes);
|
||||||
|
|
||||||
impl = new CachingExec(mockCache, config);
|
impl = new CachingExec(mockCache, null, config);
|
||||||
|
|
||||||
request = new BasicClassicHttpRequest("GET", "/thing");
|
request = new BasicClassicHttpRequest("GET", "/thing");
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,10 @@ import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
|
||||||
|
import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy;
|
||||||
import org.apache.hc.client5.http.utils.DateUtils;
|
import org.apache.hc.client5.http.utils.DateUtils;
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||||
import org.apache.hc.core5.http.ClassicHttpResponse;
|
import org.apache.hc.core5.http.ClassicHttpResponse;
|
||||||
|
@ -41,7 +44,9 @@ import org.apache.hc.core5.http.HttpEntity;
|
||||||
import org.apache.hc.core5.http.HttpStatus;
|
import org.apache.hc.core5.http.HttpStatus;
|
||||||
import org.apache.hc.core5.http.io.entity.InputStreamEntity;
|
import org.apache.hc.core5.http.io.entity.InputStreamEntity;
|
||||||
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
|
||||||
import org.junit.Ignore;
|
import org.easymock.EasyMock;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,6 +56,31 @@ import org.junit.Test;
|
||||||
*/
|
*/
|
||||||
public class TestRFC5861Compliance extends AbstractProtocolTest {
|
public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
|
|
||||||
|
private ScheduledExecutorService executorService;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
executorService = new ScheduledThreadPoolExecutor(1);
|
||||||
|
EasyMock.expect(mockExecRuntime.fork(null)).andReturn(mockExecRuntime).anyTimes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanup() {
|
||||||
|
executorService.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void replayMocks() {
|
||||||
|
super.replayMocks();
|
||||||
|
EasyMock.replay(mockExecRuntime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void verifyMocks() {
|
||||||
|
super.verifyMocks();
|
||||||
|
EasyMock.verify(mockExecRuntime);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "The stale-if-error Cache-Control extension indicates that when an
|
* "The stale-if-error Cache-Control extension indicates that when an
|
||||||
* error is encountered, a cached stale response MAY be used to satisfy
|
* error is encountered, a cached stale response MAY be used to satisfy
|
||||||
|
@ -169,7 +199,7 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
throws Exception{
|
throws Exception{
|
||||||
final CacheConfig configUnshared = CacheConfig.custom()
|
final CacheConfig configUnshared = CacheConfig.custom()
|
||||||
.setSharedCache(false).build();
|
.setSharedCache(false).build();
|
||||||
impl = new CachingExec(new BasicHttpCache(configUnshared), configUnshared);
|
impl = new CachingExec(new BasicHttpCache(configUnshared), null, configUnshared);
|
||||||
|
|
||||||
final Date tenSecondsAgo = new Date(new Date().getTime() - 10 * 1000L);
|
final Date tenSecondsAgo = new Date(new Date().getTime() - 10 * 1000L);
|
||||||
final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest();
|
final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest();
|
||||||
|
@ -323,16 +353,16 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
*
|
*
|
||||||
* http://tools.ietf.org/html/rfc5861
|
* http://tools.ietf.org/html/rfc5861
|
||||||
*/
|
*/
|
||||||
@Test @Ignore
|
@Test
|
||||||
public void testStaleWhileRevalidateReturnsStaleEntryWithWarning()
|
public void testStaleWhileRevalidateReturnsStaleEntryWithWarning()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
config = CacheConfig.custom()
|
config = CacheConfig.custom()
|
||||||
.setMaxCacheEntries(MAX_ENTRIES)
|
.setMaxCacheEntries(MAX_ENTRIES)
|
||||||
.setMaxObjectSize(MAX_BYTES)
|
.setMaxObjectSize(MAX_BYTES)
|
||||||
.setAsynchronousWorkersMax(1)
|
.setAsynchronousWorkers(1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
impl = new CachingExec(cache, config);
|
impl = new CachingExec(cache, executorService, ImmediateSchedulingStrategy.INSTANCE, config);
|
||||||
|
|
||||||
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
||||||
|
@ -365,17 +395,12 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHTTPCLIENT1470() {
|
|
||||||
impl = new CachingExec(cache, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test @Ignore
|
|
||||||
public void testStaleWhileRevalidateReturnsStaleNonRevalidatableEntryWithWarning()
|
public void testStaleWhileRevalidateReturnsStaleNonRevalidatableEntryWithWarning()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
config = CacheConfig.custom().setMaxCacheEntries(MAX_ENTRIES).setMaxObjectSize(MAX_BYTES)
|
config = CacheConfig.custom().setMaxCacheEntries(MAX_ENTRIES).setMaxObjectSize(MAX_BYTES)
|
||||||
.setAsynchronousWorkersMax(1).build();
|
.setAsynchronousWorkers(1).build();
|
||||||
|
|
||||||
impl = new CachingExec(cache, config);
|
impl = new CachingExec(cache, executorService, ImmediateSchedulingStrategy.INSTANCE, config);
|
||||||
|
|
||||||
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
||||||
|
@ -406,17 +431,17 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
assertTrue(warning110Found);
|
assertTrue(warning110Found);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test @Ignore
|
@Test
|
||||||
public void testCanAlsoServeStale304sWhileRevalidating()
|
public void testCanAlsoServeStale304sWhileRevalidating()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
config = CacheConfig.custom()
|
config = CacheConfig.custom()
|
||||||
.setMaxCacheEntries(MAX_ENTRIES)
|
.setMaxCacheEntries(MAX_ENTRIES)
|
||||||
.setMaxObjectSize(MAX_BYTES)
|
.setMaxObjectSize(MAX_BYTES)
|
||||||
.setAsynchronousWorkersMax(1)
|
.setAsynchronousWorkers(1)
|
||||||
.setSharedCache(false)
|
.setSharedCache(false)
|
||||||
.build();
|
.build();
|
||||||
impl = new CachingExec(cache, config);
|
impl = new CachingExec(cache, executorService, ImmediateSchedulingStrategy.INSTANCE, config);
|
||||||
|
|
||||||
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
||||||
|
@ -460,9 +485,9 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
config = CacheConfig.custom()
|
config = CacheConfig.custom()
|
||||||
.setMaxCacheEntries(MAX_ENTRIES)
|
.setMaxCacheEntries(MAX_ENTRIES)
|
||||||
.setMaxObjectSize(MAX_BYTES)
|
.setMaxObjectSize(MAX_BYTES)
|
||||||
.setAsynchronousWorkersMax(1)
|
.setAsynchronousWorkers(1)
|
||||||
.build();
|
.build();
|
||||||
impl = new CachingExec(cache, config);
|
impl = new CachingExec(cache, null, config);
|
||||||
|
|
||||||
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
||||||
|
@ -508,10 +533,10 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
config = CacheConfig.custom()
|
config = CacheConfig.custom()
|
||||||
.setMaxCacheEntries(MAX_ENTRIES)
|
.setMaxCacheEntries(MAX_ENTRIES)
|
||||||
.setMaxObjectSize(MAX_BYTES)
|
.setMaxObjectSize(MAX_BYTES)
|
||||||
.setAsynchronousWorkersMax(1)
|
.setAsynchronousWorkers(1)
|
||||||
.setSharedCache(true)
|
.setSharedCache(true)
|
||||||
.build();
|
.build();
|
||||||
impl = new CachingExec(cache, config);
|
impl = new CachingExec(cache, null, config);
|
||||||
|
|
||||||
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
||||||
|
@ -557,10 +582,10 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
config = CacheConfig.custom()
|
config = CacheConfig.custom()
|
||||||
.setMaxCacheEntries(MAX_ENTRIES)
|
.setMaxCacheEntries(MAX_ENTRIES)
|
||||||
.setMaxObjectSize(MAX_BYTES)
|
.setMaxObjectSize(MAX_BYTES)
|
||||||
.setAsynchronousWorkersMax(1)
|
.setAsynchronousWorkers(1)
|
||||||
.setSharedCache(true)
|
.setSharedCache(true)
|
||||||
.build();
|
.build();
|
||||||
impl = new CachingExec(cache, config);
|
impl = new CachingExec(cache, null, config);
|
||||||
|
|
||||||
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/");
|
||||||
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
final ClassicHttpResponse resp1 = HttpTestUtils.make200Response();
|
||||||
|
|
|
@ -36,4 +36,8 @@ public final class ExecSupport {
|
||||||
return COUNT.incrementAndGet();
|
return COUNT.incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getNextExchangeId() {
|
||||||
|
return String.format("ex-%08X", COUNT.incrementAndGet());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,10 @@
|
||||||
|
|
||||||
package org.apache.hc.client5.http.impl;
|
package org.apache.hc.client5.http.impl;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import org.apache.hc.core5.concurrent.Cancellable;
|
import org.apache.hc.core5.concurrent.Cancellable;
|
||||||
|
|
||||||
|
@ -42,6 +45,40 @@ public final class Operations {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static class CompletedFuture<T> implements Future<T> {
|
||||||
|
|
||||||
|
private final T result;
|
||||||
|
|
||||||
|
public CompletedFuture(final T result) {
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() throws InterruptedException, ExecutionException {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean cancel(final boolean mayInterruptIfRunning) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
public static Cancellable nonCancellable() {
|
public static Cancellable nonCancellable() {
|
||||||
return NOOP_CANCELLABLE;
|
return NOOP_CANCELLABLE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,7 +158,7 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa
|
||||||
clientContext.setRequestConfig(requestConfig);
|
clientContext.setRequestConfig(requestConfig);
|
||||||
}
|
}
|
||||||
final HttpRoute route = determineRoute(request, clientContext);
|
final HttpRoute route = determineRoute(request, clientContext);
|
||||||
final String exchangeId = String.format("ex-%08X", ExecSupport.getNextExecNumber());
|
final String exchangeId = ExecSupport.getNextExchangeId();
|
||||||
final AsyncExecRuntime execRuntime = crerateAsyncExecRuntime();
|
final AsyncExecRuntime execRuntime = crerateAsyncExecRuntime();
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
log.debug(exchangeId + ": preparing request execution");
|
log.debug(exchangeId + ": preparing request execution");
|
||||||
|
|
|
@ -210,7 +210,7 @@ public final class MinimalHttp2AsyncClient extends AbstractMinimalHttpAsyncClien
|
||||||
|
|
||||||
};
|
};
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
final String exchangeId = String.format("ex-%08X", ExecSupport.getNextExecNumber());
|
final String exchangeId = ExecSupport.getNextExchangeId();
|
||||||
log.debug(ConnPoolSupport.getId(session) + ": executing message exchange " + exchangeId);
|
log.debug(ConnPoolSupport.getId(session) + ": executing message exchange " + exchangeId);
|
||||||
session.addLast(new ExecutionCommand(
|
session.addLast(new ExecutionCommand(
|
||||||
new LoggingAsyncClientExchangeHandler(log, exchangeId, internalExchangeHandler),
|
new LoggingAsyncClientExchangeHandler(log, exchangeId, internalExchangeHandler),
|
||||||
|
|
|
@ -396,7 +396,7 @@ public final class MinimalHttpAsyncClient extends AbstractMinimalHttpAsyncClient
|
||||||
Asserts.check(!released.get(), "Endpoint has already been released");
|
Asserts.check(!released.get(), "Endpoint has already been released");
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
final String exchangeId = String.format("ex-%08X", ExecSupport.getNextExecNumber());
|
final String exchangeId = ExecSupport.getNextExchangeId();
|
||||||
log.debug(ConnPoolSupport.getId(connectionEndpoint) + ": executing message exchange " + exchangeId);
|
log.debug(ConnPoolSupport.getId(connectionEndpoint) + ": executing message exchange " + exchangeId);
|
||||||
connectionEndpoint.execute(
|
connectionEndpoint.execute(
|
||||||
new LoggingAsyncClientExchangeHandler(log, exchangeId, exchangeHandler),
|
new LoggingAsyncClientExchangeHandler(log, exchangeId, exchangeHandler),
|
||||||
|
|
|
@ -158,7 +158,7 @@ class InternalHttpClient extends CloseableHttpClient implements Configurable {
|
||||||
}
|
}
|
||||||
setupContext(localcontext);
|
setupContext(localcontext);
|
||||||
final HttpRoute route = determineRoute(target, request, localcontext);
|
final HttpRoute route = determineRoute(target, request, localcontext);
|
||||||
final String exchangeId = String.format("ex-%08X", ExecSupport.getNextExecNumber());
|
final String exchangeId = ExecSupport.getNextExchangeId();
|
||||||
final ExecRuntime execRuntime = new InternalExecRuntime(log, connManager, requestExecutor,
|
final ExecRuntime execRuntime = new InternalExecRuntime(log, connManager, requestExecutor,
|
||||||
request instanceof CancellableAware ? (CancellableAware) request : null);
|
request instanceof CancellableAware ? (CancellableAware) request : null);
|
||||||
final ExecChain.Scope scope = new ExecChain.Scope(exchangeId, route, request, execRuntime, localcontext);
|
final ExecChain.Scope scope = new ExecChain.Scope(exchangeId, route, request, execRuntime, localcontext);
|
||||||
|
|
Loading…
Reference in New Issue