You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2012/12/08 20:59:26 UTC

svn commit: r1418743 [1/6] - in /httpcomponents/httpclient/trunk: httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/ httpclient-cache/src/test/java/org/apache/ht...

Author: olegk
Date: Sat Dec  8 19:59:24 2012
New Revision: 1418743

URL: http://svn.apache.org/viewvc?rev=1418743&view=rev
Log:
Ported CachingHttpClient to ClientExecChain API

Added:
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java
      - copied, changed from r1418654, httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Proxies.java   (with props)
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProxyHandler.java   (with props)
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyBackend.java   (with props)
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingExec.java   (contents, props changed)
      - copied, changed from r1418654, httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/execchain/Proxies.java   (contents, props changed)
      - copied, changed from r1418654, httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/execchain/ExecProxies.java
Removed:
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DoNotTestProtocolRequirements.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyHttpClient.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/execchain/ExecProxies.java
Modified:
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidationRequest.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidator.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidationRequest.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidator.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheJiraNumber1147.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolAllowedBehavior.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRFC5861Compliance.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/ehcache/TestEhcacheProtocolRequirements.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/execchain/MainClientExec.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/execchain/RedirectExec.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/execchain/RetryExec.java

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidationRequest.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidationRequest.java?rev=1418743&r1=1418742&r2=1418743&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidationRequest.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidationRequest.java Sat Dec  8 19:59:24 2012
@@ -29,11 +29,12 @@ package org.apache.http.impl.client.cach
 import java.io.IOException;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.http.HttpHost;
-import org.apache.http.ProtocolException;
+import org.apache.http.HttpException;
 import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.methods.HttpExecutionAware;
 import org.apache.http.client.methods.HttpRequestWrapper;
-import org.apache.http.protocol.HttpContext;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.routing.HttpRoute;
 
 /**
  * Class used to represent an asynchronous revalidation event, such as with
@@ -41,10 +42,11 @@ import org.apache.http.protocol.HttpCont
  */
 class AsynchronousValidationRequest implements Runnable {
     private final AsynchronousValidator parent;
-    private final CachingHttpClient cachingClient;
-    private final HttpHost target;
+    private final CachingExec cachingExec;
+    private final HttpRoute route;
     private final HttpRequestWrapper request;
-    private final HttpContext context;
+    private final HttpClientContext context;
+    private final HttpExecutionAware execAware;
     private final HttpCacheEntry cacheEntry;
     private final String identifier;
 
@@ -61,27 +63,32 @@ class AsynchronousValidationRequest impl
      * @param bookKeeping
      * @param identifier
      */
-    AsynchronousValidationRequest(AsynchronousValidator parent,
-            CachingHttpClient cachingClient, HttpHost target,
-            HttpRequestWrapper request, HttpContext context,
-            HttpCacheEntry cacheEntry,
-            String identifier) {
+    AsynchronousValidationRequest(
+            final AsynchronousValidator parent,
+            final CachingExec cachingExec,
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware,
+            final HttpCacheEntry cacheEntry,
+            final String identifier) {
         this.parent = parent;
-        this.cachingClient = cachingClient;
-        this.target = target;
+        this.cachingExec = cachingExec;
+        this.route = route;
         this.request = request;
         this.context = context;
+        this.execAware = execAware;
         this.cacheEntry = cacheEntry;
         this.identifier = identifier;
     }
 
     public void run() {
         try {
-            cachingClient.revalidateCacheEntry(target, request, context, cacheEntry);
+            cachingExec.revalidateCacheEntry(route, request, context, execAware, cacheEntry);
         } catch (IOException ioe) {
-            log.debug("Asynchronous revalidation failed due to exception: " + ioe);
-        } catch (ProtocolException pe) {
-            log.error("ProtocolException thrown during asynchronous revalidation: " + pe);
+            log.debug("Asynchronous revalidation failed due to I/O error", ioe);
+        } catch (HttpException pe) {
+            log.error("HTTP protocol exception during asynchronous revalidation", pe);
         } finally {
             parent.markComplete(identifier);
         }

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidator.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidator.java?rev=1418743&r1=1418742&r2=1418743&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidator.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidator.java Sat Dec  8 19:59:24 2012
@@ -37,17 +37,18 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.http.HttpHost;
 import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.methods.HttpExecutionAware;
 import org.apache.http.client.methods.HttpRequestWrapper;
-import org.apache.http.protocol.HttpContext;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.routing.HttpRoute;
 
 /**
  * Class used for asynchronous revalidations to be used when the "stale-
  * while-revalidate" directive is present
  */
 class AsynchronousValidator {
-    private final CachingHttpClient cachingClient;
+    private final CachingExec cachingExec;
     private final ExecutorService executor;
     private final Set<String> queued;
     private final CacheKeyGenerator cacheKeyGenerator;
@@ -59,16 +60,16 @@ class AsynchronousValidator {
      * using the supplied {@link CachingHttpClient}, and
      * a {@link ThreadPoolExecutor} generated according to the thread
      * pool settings provided in the given {@link CacheConfig}.
-     * @param cachingClient used to execute asynchronous requests
+     * @param cachingExect used to execute asynchronous requests
      * @param config specifies thread pool settings. See
      * {@link CacheConfig#getAsynchronousWorkersMax()},
      * {@link CacheConfig#getAsynchronousWorkersCore()},
      * {@link CacheConfig#getAsynchronousWorkerIdleLifetimeSecs()},
      * and {@link CacheConfig#getRevalidationQueueSize()}.
      */
-    public AsynchronousValidator(CachingHttpClient cachingClient,
+    public AsynchronousValidator(CachingExec cachingExect,
             CacheConfig config) {
-        this(cachingClient,
+        this(cachingExect,
                 new ThreadPoolExecutor(config.getAsynchronousWorkersCore(),
                         config.getAsynchronousWorkersMax(),
                         config.getAsynchronousWorkerIdleLifetimeSecs(),
@@ -81,12 +82,11 @@ class AsynchronousValidator {
      * Create AsynchronousValidator which will make revalidation requests
      * using the supplied {@link CachingHttpClient} and
      * {@link ExecutorService}.
-     * @param cachingClient used to execute asynchronous requests
+     * @param cachingExect used to execute asynchronous requests
      * @param executor used to manage a thread pool of revalidation workers
      */
-    AsynchronousValidator(CachingHttpClient cachingClient,
-            ExecutorService executor) {
-        this.cachingClient = cachingClient;
+    AsynchronousValidator(CachingExec cachingExec, ExecutorService executor) {
+        this.cachingExec = cachingExec;
         this.executor = executor;
         this.queued = new HashSet<String>();
         this.cacheKeyGenerator = new CacheKeyGenerator();
@@ -94,21 +94,20 @@ class AsynchronousValidator {
 
     /**
      * Schedules an asynchronous revalidation
-     *
-     * @param target
-     * @param request
-     * @param context
-     * @param entry
      */
-    public synchronized void revalidateCacheEntry(HttpHost target,
-            HttpRequestWrapper request, HttpContext context, HttpCacheEntry entry) {
+    public synchronized void revalidateCacheEntry(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware,
+            final HttpCacheEntry entry) {
         // getVariantURI will fall back on getURI if no variants exist
-        String uri = cacheKeyGenerator.getVariantURI(target, request, entry);
+        String uri = cacheKeyGenerator.getVariantURI(route.getTargetHost(), request, entry);
 
         if (!queued.contains(uri)) {
             AsynchronousValidationRequest revalidationRequest =
-                new AsynchronousValidationRequest(this, cachingClient, target,
-                        request, context, entry, uri);
+                new AsynchronousValidationRequest(
+                        this, cachingExec, route, request, context, execAware, entry, uri);
 
             try {
                 executor.execute(revalidationRequest);

Copied: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java (from r1418654, httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java?p2=httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java&p1=httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java&r1=1418654&r2=1418743&rev=1418743&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java Sat Dec  8 19:59:24 2012
@@ -27,8 +27,6 @@
 package org.apache.http.impl.client.cache;
 
 import java.io.IOException;
-import java.lang.reflect.UndeclaredThrowableException;
-import java.net.URI;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -39,33 +37,31 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.http.Header;
 import org.apache.http.HeaderElement;
-import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpMessage;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpStatus;
 import org.apache.http.HttpVersion;
-import org.apache.http.ProtocolException;
 import org.apache.http.ProtocolVersion;
 import org.apache.http.RequestLine;
 import org.apache.http.annotation.ThreadSafe;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.ResponseHandler;
 import org.apache.http.client.cache.CacheResponseStatus;
 import org.apache.http.client.cache.HeaderConstants;
 import org.apache.http.client.cache.HttpCacheEntry;
 import org.apache.http.client.cache.HttpCacheStorage;
 import org.apache.http.client.cache.ResourceFactory;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpExecutionAware;
 import org.apache.http.client.methods.HttpRequestWrapper;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.client.execchain.ClientExecChain;
 import org.apache.http.impl.cookie.DateParseException;
 import org.apache.http.impl.cookie.DateUtils;
 import org.apache.http.message.BasicHttpResponse;
-import org.apache.http.params.HttpParams;
 import org.apache.http.protocol.ExecutionContext;
 import org.apache.http.protocol.HTTP;
 import org.apache.http.protocol.HttpContext;
@@ -73,32 +69,11 @@ import org.apache.http.util.EntityUtils;
 import org.apache.http.util.VersionInfo;
 
 /**
- * <p>The {@link CachingHttpClient} is meant to be a drop-in replacement for
- * a {@link DefaultHttpClient} that transparently adds client-side caching.
+ * <p>CachingExec is intended to transparently add client-side caching
+ * to the HttpClient {@link ClientExecChain execution chain}.
  * The current implementation is conditionally compliant with HTTP/1.1
  * (meaning all the MUST and MUST NOTs are obeyed), although quite a lot,
- * though not all, of the SHOULDs and SHOULD NOTs are obeyed too. Generally
- * speaking, you construct a {@code CachingHttpClient} by providing a
- * "backend" {@link HttpClient} used for making actual network requests and
- * provide an {@link HttpCacheStorage} instance to use for holding onto
- * cached responses. Additional configuration options can be provided by
- * passing in a {@link CacheConfig}. Note that all of the usual client
- * related configuration you want to do vis-a-vis timeouts and connection
- * pools should be done on this backend client before constructing a {@code
- * CachingHttpClient} from it.</p>
- *
- * <p>Generally speaking, the {@code CachingHttpClient} is implemented as a
- * <a href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</a>
- * of the backend client; for any incoming request it attempts to satisfy
- * it from the cache, but if it can't, or if it needs to revalidate a stale
- * cache entry, it will use the backend client to make an actual request.
- * However, a proper HTTP/1.1 cache won't change the semantics of a request
- * and response; in particular, if you issue an unconditional request you
- * will get a full response (although it may be served to you from the cache,
- * or the cache may make a conditional request on your behalf to the origin).
- * This notion of "semantic transparency" means you should be able to drop
- * a {@link CachingHttpClient} into an existing application without breaking
- * anything.</p>
+ * though not all, of the SHOULDs and SHOULD NOTs are obeyed too.</p>
  *
  * <p>Folks that would like to experiment with alternative storage backends
  * should look at the {@link HttpCacheStorage} interface and the related
@@ -108,10 +83,11 @@ import org.apache.http.util.VersionInfo;
  * org.apache.http.impl.client.cache.memcached.MemcachedHttpCacheStorage
  * memcached} storage backends.</p>
  * </p>
- * @since 4.1
+ *
+ * @since 4.3
  */
 @ThreadSafe // So long as the responseCache implementation is threadsafe
-public class CachingHttpClient implements HttpClient {
+public class CachingExec implements ClientExecChain {
 
     /**
      * This is the name under which the {@link
@@ -129,160 +105,77 @@ public class CachingHttpClient implement
 
     private final Map<ProtocolVersion, String> viaHeaders = new HashMap<ProtocolVersion, String>(4);
 
-    private final HttpClient backend;
+    private final CacheConfig cacheConfig;
+    private final ClientExecChain backend;
     private final HttpCache responseCache;
     private final CacheValidityPolicy validityPolicy;
-    private final ResponseCachingPolicy responseCachingPolicy;
     private final CachedHttpResponseGenerator responseGenerator;
     private final CacheableRequestPolicy cacheableRequestPolicy;
     private final CachedResponseSuitabilityChecker suitabilityChecker;
-
     private final ConditionalRequestBuilder conditionalRequestBuilder;
-
-    private final long maxObjectSizeBytes;
-    private final boolean sharedCache;
-
     private final ResponseProtocolCompliance responseCompliance;
     private final RequestProtocolCompliance requestCompliance;
+    private final ResponseCachingPolicy responseCachingPolicy;
 
     private final AsynchronousValidator asynchRevalidator;
 
     private final Log log = LogFactory.getLog(getClass());
 
-    CachingHttpClient(
-            HttpClient client,
+    public CachingExec(
+            ClientExecChain backend,
             HttpCache cache,
             CacheConfig config) {
         super();
-        if (client == null) {
-            throw new IllegalArgumentException("HttpClient may not be null");
+        if (backend == null) {
+            throw new IllegalArgumentException("HTTP backend may not be null");
         }
         if (cache == null) {
             throw new IllegalArgumentException("HttpCache may not be null");
         }
-        if (config == null) {
-            throw new IllegalArgumentException("CacheConfig may not be null");
-        }
-        this.maxObjectSizeBytes = config.getMaxObjectSize();
-        this.sharedCache = config.isSharedCache();
-        this.backend = client;
+        this.cacheConfig = config != null ? config : CacheConfig.DEFAULT;
+        this.backend = backend;
         this.responseCache = cache;
         this.validityPolicy = new CacheValidityPolicy();
-        this.responseCachingPolicy = new ResponseCachingPolicy(maxObjectSizeBytes, sharedCache);
         this.responseGenerator = new CachedHttpResponseGenerator(this.validityPolicy);
         this.cacheableRequestPolicy = new CacheableRequestPolicy();
         this.suitabilityChecker = new CachedResponseSuitabilityChecker(this.validityPolicy, config);
         this.conditionalRequestBuilder = new ConditionalRequestBuilder();
-
         this.responseCompliance = new ResponseProtocolCompliance();
         this.requestCompliance = new RequestProtocolCompliance();
-
+        this.responseCachingPolicy = new ResponseCachingPolicy(
+                this.cacheConfig.getMaxObjectSize(), this.cacheConfig.isSharedCache());
         this.asynchRevalidator = makeAsynchronousValidator(config);
     }
 
-    /**
-     * Constructs a {@code CachingHttpClient} with default caching settings that
-     * stores cache entries in memory and uses a vanilla {@link DefaultHttpClient}
-     * for backend requests.
-     */
-    public CachingHttpClient() {
-        this(new DefaultHttpClient(),
-                new BasicHttpCache(),
-                new CacheConfig());
-    }
-
-    /**
-     * Constructs a {@code CachingHttpClient} with the given caching options that
-     * stores cache entries in memory and uses a vanilla {@link DefaultHttpClient}
-     * for backend requests.
-     * @param config cache module options
-     */
-    public CachingHttpClient(CacheConfig config) {
-        this(new DefaultHttpClient(),
-                new BasicHttpCache(config),
-                config);
-    }
-
-    /**
-     * Constructs a {@code CachingHttpClient} with default caching settings that
-     * stores cache entries in memory and uses the given {@link HttpClient}
-     * for backend requests.
-     * @param client used to make origin requests
-     */
-    public CachingHttpClient(HttpClient client) {
-        this(client,
-                new BasicHttpCache(),
-                new CacheConfig());
-    }
-
-    /**
-     * Constructs a {@code CachingHttpClient} with the given caching options that
-     * stores cache entries in memory and uses the given {@link HttpClient}
-     * for backend requests.
-     * @param config cache module options
-     * @param client used to make origin requests
-     */
-    public CachingHttpClient(HttpClient client, CacheConfig config) {
-        this(client,
-                new BasicHttpCache(config),
-                config);
-    }
-
-    /**
-     * Constructs a {@code CachingHttpClient} with the given caching options
-     * that stores cache entries in the provided storage backend and uses
-     * the given {@link HttpClient} for backend requests. However, cached
-     * response bodies are managed using the given {@link ResourceFactory}.
-     * @param client used to make origin requests
-     * @param resourceFactory how to manage cached response bodies
-     * @param storage where to store cache entries
-     * @param config cache module options
-     */
-    public CachingHttpClient(
-            HttpClient client,
+    public CachingExec(
+            ClientExecChain backend,
             ResourceFactory resourceFactory,
             HttpCacheStorage storage,
             CacheConfig config) {
-        this(client,
-                new BasicHttpCache(resourceFactory, storage, config),
-                config);
+        this(backend, new BasicHttpCache(resourceFactory, storage, config), config);
     }
 
-    /**
-     * Constructs a {@code CachingHttpClient} with the given caching options
-     * that stores cache entries in the provided storage backend and uses
-     * the given {@link HttpClient} for backend requests.
-     * @param client used to make origin requests
-     * @param storage where to store cache entries
-     * @param config cache module options
-     */
-    public CachingHttpClient(
-            HttpClient client,
-            HttpCacheStorage storage,
-            CacheConfig config) {
-        this(client,
-                new BasicHttpCache(new HeapResourceFactory(), storage, config),
-                config);
+    public CachingExec(ClientExecChain backend) {
+        this(backend, new BasicHttpCache(), CacheConfig.DEFAULT);
     }
 
-    CachingHttpClient(
-            HttpClient backend,
+    CachingExec(
+            ClientExecChain backend,
+            HttpCache responseCache,
             CacheValidityPolicy validityPolicy,
             ResponseCachingPolicy responseCachingPolicy,
-            HttpCache responseCache,
             CachedHttpResponseGenerator responseGenerator,
             CacheableRequestPolicy cacheableRequestPolicy,
             CachedResponseSuitabilityChecker suitabilityChecker,
             ConditionalRequestBuilder conditionalRequestBuilder,
             ResponseProtocolCompliance responseCompliance,
-            RequestProtocolCompliance requestCompliance) {
-        CacheConfig config = new CacheConfig();
-        this.maxObjectSizeBytes = config.getMaxObjectSize();
-        this.sharedCache = config.isSharedCache();
+            RequestProtocolCompliance requestCompliance,
+            CacheConfig config) {
+        this.cacheConfig = config != null ? config : CacheConfig.DEFAULT;
         this.backend = backend;
+        this.responseCache = responseCache;
         this.validityPolicy = validityPolicy;
         this.responseCachingPolicy = responseCachingPolicy;
-        this.responseCache = responseCache;
         this.responseGenerator = responseGenerator;
         this.cacheableRequestPolicy = cacheableRequestPolicy;
         this.suitabilityChecker = suitabilityChecker;
@@ -327,195 +220,142 @@ public class CachingHttpClient implement
         return cacheUpdates.get();
     }
 
-    public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException {
-        HttpContext defaultContext = null;
-        return execute(target, request, defaultContext);
-    }
-
-    public <T> T execute(HttpHost target, HttpRequest request,
-                         ResponseHandler<? extends T> responseHandler) throws IOException {
-        return execute(target, request, responseHandler, null);
-    }
-
-    public <T> T execute(HttpHost target, HttpRequest request,
-                         ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException {
-        HttpResponse resp = execute(target, request, context);
-        return handleAndConsume(responseHandler,resp);
-    }
-
-    public HttpResponse execute(HttpUriRequest request) throws IOException {
-        HttpContext context = null;
-        return execute(request, context);
-    }
-
-    public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException {
-        URI uri = request.getURI();
-        HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
-        return execute(httpHost, request, context);
-    }
-
-    public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler)
-            throws IOException {
-        return execute(request, responseHandler, null);
-    }
-
-    public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler,
-                         HttpContext context) throws IOException {
-        HttpResponse resp = execute(request, context);
-        return handleAndConsume(responseHandler, resp);
-    }
-
-    private <T> T handleAndConsume(
-            final ResponseHandler<? extends T> responseHandler,
-            HttpResponse response) throws Error, IOException {
-        T result;
-        try {
-            result = responseHandler.handleResponse(response);
-        } catch (Exception t) {
-            HttpEntity entity = response.getEntity();
-            try {
-                EntityUtils.consume(entity);
-            } catch (Exception t2) {
-                // Log this exception. The original exception is more
-                // important and will be thrown to the caller.
-                this.log.warn("Error consuming content after an exception.", t2);
-            }
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            }
-            if (t instanceof IOException) {
-                throw (IOException) t;
-            }
-            throw new UndeclaredThrowableException(t);
-        }
-
-        // Handling the response was successful. Ensure that the content has
-        // been fully consumed.
-        HttpEntity entity = response.getEntity();
-        EntityUtils.consume(entity);
-        return result;
-    }
+    public CloseableHttpResponse execute(
+            final HttpRoute route,
+            final HttpRequestWrapper request) throws IOException, HttpException {
+        return execute(route, request, HttpClientContext.create(), null);
+    }
+
+    public CloseableHttpResponse execute(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context) throws IOException, HttpException {
+        return execute(route, request, context, null);
+    }
+
+    public CloseableHttpResponse execute(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware) throws IOException, HttpException {
 
-    public ClientConnectionManager getConnectionManager() {
-        return backend.getConnectionManager();
-    }
-
-    public HttpParams getParams() {
-        return backend.getParams();
-    }
-
-    public HttpResponse execute(HttpHost target, HttpRequest originalRequest, HttpContext context)
-            throws IOException {
-
-        HttpRequestWrapper request;
-        if (originalRequest instanceof HttpRequestWrapper) {
-            request = ((HttpRequestWrapper) originalRequest);
-        } else {
-            request = HttpRequestWrapper.wrap(originalRequest);
-        }
-        String via = generateViaHeader(originalRequest);
+        HttpHost target = route.getTargetHost();
+        String via = generateViaHeader(request.getOriginal());
 
         // default response context
         setResponseStatus(context, CacheResponseStatus.CACHE_MISS);
 
         if (clientRequestsOurOptions(request)) {
             setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
-            return new OptionsHttp11Response();
+            return Proxies.enhanceResponse(new OptionsHttp11Response());
         }
 
-        HttpResponse fatalErrorResponse = getFatallyNoncompliantResponse(
-                request, context);
-        if (fatalErrorResponse != null) return fatalErrorResponse;
+        HttpResponse fatalErrorResponse = getFatallyNoncompliantResponse(request, context);
+        if (fatalErrorResponse != null) {
+            return Proxies.enhanceResponse(fatalErrorResponse);
+        }
 
         requestCompliance.makeRequestCompliant(request);
         request.addHeader("Via",via);
 
-        flushEntriesInvalidatedByRequest(target, request);
+        flushEntriesInvalidatedByRequest(route.getTargetHost(), request);
 
         if (!cacheableRequestPolicy.isServableFromCache(request)) {
             log.debug("Request is not servable from cache");
-            return callBackend(target, request, context);
+            return callBackend(route, request, context, execAware);
         }
 
         HttpCacheEntry entry = satisfyFromCache(target, request);
         if (entry == null) {
             log.debug("Cache miss");
-            return handleCacheMiss(target, request, context);
+            return handleCacheMiss(route, request, context, execAware);
         }
 
-        return handleCacheHit(target, request, context, entry);
+        return handleCacheHit(route, request, context, execAware, entry);
     }
 
-    private HttpResponse handleCacheHit(HttpHost target, HttpRequestWrapper request,
-            HttpContext context, HttpCacheEntry entry)
-            throws ClientProtocolException, IOException {
+    private CloseableHttpResponse handleCacheHit(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware,
+            final HttpCacheEntry entry) throws IOException, HttpException {
+        HttpHost target = route.getTargetHost();
         recordCacheHit(target, request);
-        HttpResponse out = null;
+        CloseableHttpResponse out = null;
         Date now = getCurrentDate();
         if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) {
             log.debug("Cache hit");
-            out = generateCachedResponse(request, context, entry, now);
+            out = Proxies.enhanceResponse(generateCachedResponse(request, context, entry, now));
         } else if (!mayCallBackend(request)) {
             log.debug("Cache entry not suitable but only-if-cached requested");
-            out = generateGatewayTimeout(context);
+            out = Proxies.enhanceResponse(generateGatewayTimeout(context));
         } else if (validityPolicy.isRevalidatable(entry)) {
             log.debug("Revalidating cache entry");
-            return revalidateCacheEntry(target, request, context, entry, now);
+            return revalidateCacheEntry(route, request, context, execAware, entry, now);
         } else {
             log.debug("Cache entry not usable; calling backend");
-        	return callBackend(target, request, context);
+            return callBackend(route, request, context, execAware);
         }
         if (context != null) {
-        	context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
-        	context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
-        	context.setAttribute(ExecutionContext.HTTP_RESPONSE, out);
-        	context.setAttribute(ExecutionContext.HTTP_REQ_SENT, Boolean.TRUE);
+            context.setAttribute(ClientContext.ROUTE, route);
+            context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
+            context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
+            context.setAttribute(ExecutionContext.HTTP_RESPONSE, out);
+            context.setAttribute(ExecutionContext.HTTP_REQ_SENT, Boolean.TRUE);
         }
         return out;
     }
 
-    private HttpResponse revalidateCacheEntry(HttpHost target,
-            HttpRequestWrapper request, HttpContext context, HttpCacheEntry entry,
-            Date now) throws ClientProtocolException {
+    private CloseableHttpResponse revalidateCacheEntry(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware,
+            final HttpCacheEntry entry,
+            final Date now) throws IOException, HttpException {
 
         try {
             if (asynchRevalidator != null
                 && !staleResponseNotAllowed(request, entry, now)
                 && validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
                 log.trace("Serving stale with asynchronous revalidation");
-                final HttpResponse resp = generateCachedResponse(request, context, entry, now);
-
-                asynchRevalidator.revalidateCacheEntry(target, request, context, entry);
-
-                return resp;
+                HttpResponse resp = generateCachedResponse(request, context, entry, now);
+                asynchRevalidator.revalidateCacheEntry(route, request, context, execAware, entry);
+                return Proxies.enhanceResponse(resp);
             }
-            return revalidateCacheEntry(target, request, context, entry);
+            return revalidateCacheEntry(route, request, context, execAware, entry);
         } catch (IOException ioex) {
-            return handleRevalidationFailure(request, context, entry, now);
-        } catch (ProtocolException e) {
-            throw new ClientProtocolException(e);
+            return Proxies.enhanceResponse(
+                    handleRevalidationFailure(request, context, entry, now));
         }
     }
 
-    private HttpResponse handleCacheMiss(HttpHost target, HttpRequestWrapper request,
-            HttpContext context) throws IOException {
+    private CloseableHttpResponse handleCacheMiss(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware) throws IOException, HttpException {
+        HttpHost target = route.getTargetHost();
         recordCacheMiss(target, request);
 
         if (!mayCallBackend(request)) {
-            return new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_GATEWAY_TIMEOUT,
-                    "Gateway Timeout");
+            return Proxies.enhanceResponse(
+                    new BasicHttpResponse(
+                            HttpVersion.HTTP_1_1, HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout"));
         }
 
-        Map<String, Variant> variants =
-            getExistingCacheVariants(target, request);
+        Map<String, Variant> variants = getExistingCacheVariants(target, request);
         if (variants != null && variants.size() > 0) {
-            return negotiateResponseFromVariants(target, request, context, variants);
+            return Proxies.enhanceResponse(
+                    negotiateResponseFromVariants(route, request, context, execAware, variants));
         }
 
-        return callBackend(target, request, context);
+        return callBackend(route, request, context, execAware);
     }
 
-    private HttpCacheEntry satisfyFromCache(HttpHost target, HttpRequestWrapper request) {
+    private HttpCacheEntry satisfyFromCache(
+            final HttpHost target, final HttpRequestWrapper request) {
         HttpCacheEntry entry = null;
         try {
             entry = responseCache.getCacheEntry(target, request);
@@ -525,8 +365,9 @@ public class CachingHttpClient implement
         return entry;
     }
 
-    private HttpResponse getFatallyNoncompliantResponse(HttpRequestWrapper request,
-            HttpContext context) {
+    private HttpResponse getFatallyNoncompliantResponse(
+            final HttpRequestWrapper request,
+            final HttpContext context) {
         HttpResponse fatalErrorResponse = null;
         List<RequestProtocolError> fatalError = requestCompliance.requestIsFatallyNonCompliant(request);
 
@@ -537,8 +378,9 @@ public class CachingHttpClient implement
         return fatalErrorResponse;
     }
 
-    private Map<String, Variant> getExistingCacheVariants(HttpHost target,
-            HttpRequestWrapper request) {
+    private Map<String, Variant> getExistingCacheVariants(
+            final HttpHost target,
+            final HttpRequestWrapper request) {
         Map<String,Variant> variants = null;
         try {
             variants = responseCache.getVariantCacheEntriesWithEtags(target, request);
@@ -548,7 +390,7 @@ public class CachingHttpClient implement
         return variants;
     }
 
-    private void recordCacheMiss(HttpHost target, HttpRequestWrapper request) {
+    private void recordCacheMiss(final HttpHost target, final HttpRequestWrapper request) {
         cacheMisses.getAndIncrement();
         if (log.isTraceEnabled()) {
             RequestLine rl = request.getRequestLine();
@@ -556,7 +398,7 @@ public class CachingHttpClient implement
         }
     }
 
-    private void recordCacheHit(HttpHost target, HttpRequestWrapper request) {
+    private void recordCacheHit(final HttpHost target, final HttpRequestWrapper request) {
         cacheHits.getAndIncrement();
         if (log.isTraceEnabled()) {
             RequestLine rl = request.getRequestLine();
@@ -569,8 +411,9 @@ public class CachingHttpClient implement
         setResponseStatus(context, CacheResponseStatus.VALIDATED);
     }
 
-    private void flushEntriesInvalidatedByRequest(HttpHost target,
-            HttpRequestWrapper request) {
+    private void flushEntriesInvalidatedByRequest(
+            final HttpHost target,
+            final HttpRequestWrapper request) {
         try {
             responseCache.flushInvalidatedCacheEntriesFor(target, request);
         } catch (IOException ioe) {
@@ -594,8 +437,11 @@ public class CachingHttpClient implement
         return cachedResponse;
     }
 
-    private HttpResponse handleRevalidationFailure(HttpRequestWrapper request,
-            HttpContext context, HttpCacheEntry entry, Date now) {
+    private HttpResponse handleRevalidationFailure(
+            final HttpRequestWrapper request,
+            final HttpContext context,
+            final HttpCacheEntry entry,
+            final Date now) {
         if (staleResponseNotAllowed(request, entry, now)) {
             return generateGatewayTimeout(context);
         } else {
@@ -603,28 +449,30 @@ public class CachingHttpClient implement
         }
     }
 
-    private HttpResponse generateGatewayTimeout(HttpContext context) {
+    private HttpResponse generateGatewayTimeout(final HttpContext context) {
         setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
         return new BasicHttpResponse(HttpVersion.HTTP_1_1,
                 HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout");
     }
 
-    private HttpResponse unvalidatedCacheHit(HttpContext context,
-            HttpCacheEntry entry) {
+    private HttpResponse unvalidatedCacheHit(
+            final HttpContext context, final HttpCacheEntry entry) {
         final HttpResponse cachedResponse = responseGenerator.generateResponse(entry);
         setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
         cachedResponse.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\"");
         return cachedResponse;
     }
 
-    private boolean staleResponseNotAllowed(HttpRequestWrapper request,
-            HttpCacheEntry entry, Date now) {
+    private boolean staleResponseNotAllowed(
+            final HttpRequestWrapper request,
+            final HttpCacheEntry entry,
+            final Date now) {
         return validityPolicy.mustRevalidate(entry)
-            || (isSharedCache() && validityPolicy.proxyRevalidate(entry))
+            || (cacheConfig.isSharedCache() && validityPolicy.proxyRevalidate(entry))
             || explicitFreshnessRequest(request, entry, now);
     }
 
-    private boolean mayCallBackend(HttpRequestWrapper request) {
+    private boolean mayCallBackend(final HttpRequestWrapper request) {
         for (Header h: request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
             for (HeaderElement elt : h.getElements()) {
                 if ("only-if-cached".equals(elt.getName())) {
@@ -636,7 +484,10 @@ public class CachingHttpClient implement
         return true;
     }
 
-    private boolean explicitFreshnessRequest(HttpRequestWrapper request, HttpCacheEntry entry, Date now) {
+    private boolean explicitFreshnessRequest(
+            final HttpRequestWrapper request,
+            final HttpCacheEntry entry,
+            final Date now) {
         for(Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
             for(HeaderElement elt : h.getElements()) {
                 if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) {
@@ -657,7 +508,7 @@ public class CachingHttpClient implement
         return false;
     }
 
-    private String generateViaHeader(HttpMessage msg) {
+    private String generateViaHeader(final HttpMessage msg) {
 
         final ProtocolVersion pv = msg.getProtocolVersion();
         String existingEntry = viaHeaders.get(pv);
@@ -695,22 +546,11 @@ public class CachingHttpClient implement
         return SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS;
     }
 
-    /**
-     * Reports whether this {@code CachingHttpClient} is configured as
-     * a shared (public) or non-shared (private) cache. See {@link
-     * CacheConfig#setSharedCache(boolean)}.
-     * @return {@code true} if we are behaving as a shared (public)
-     *   cache
-     */
-    public boolean isSharedCache() {
-        return sharedCache;
-    }
-
     Date getCurrentDate() {
         return new Date();
     }
 
-    boolean clientRequestsOurOptions(HttpRequest request) {
+    boolean clientRequestsOurOptions(final HttpRequest request) {
         RequestLine line = request.getRequestLine();
 
         if (!HeaderConstants.OPTIONS_METHOD.equals(line.getMethod()))
@@ -725,17 +565,19 @@ public class CachingHttpClient implement
         return true;
     }
 
-    HttpResponse callBackend(HttpHost target, HttpRequestWrapper request, HttpContext context)
-            throws IOException {
+    CloseableHttpResponse callBackend(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware) throws IOException, HttpException  {
 
         Date requestDate = getCurrentDate();
 
         log.trace("Calling the backend");
-        HttpResponse backendResponse = backend.execute(target, request, context);
+        CloseableHttpResponse backendResponse = backend.execute(route, request, context, execAware);
         backendResponse.addHeader("Via", generateViaHeader(backendResponse));
-        return handleBackendResponse(target, request, requestDate, getCurrentDate(),
-                backendResponse);
-
+        return handleBackendResponse(route, request, context, execAware,
+                requestDate, getCurrentDate(), backendResponse);
     }
 
     private boolean revalidationResponseIsTooOld(HttpResponse backendResponse,
@@ -757,51 +599,56 @@ public class CachingHttpClient implement
         return false;
     }
 
-    HttpResponse negotiateResponseFromVariants(HttpHost target,
-            HttpRequestWrapper request, HttpContext context,
-            Map<String, Variant> variants) throws IOException {
+    HttpResponse negotiateResponseFromVariants(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware,
+            final Map<String, Variant> variants) throws IOException, HttpException {
         HttpRequestWrapper conditionalRequest = conditionalRequestBuilder
             .buildConditionalRequestFromVariants(request, variants);
 
         Date requestDate = getCurrentDate();
-        HttpResponse backendResponse = backend.execute(target, conditionalRequest, context);
+        CloseableHttpResponse backendResponse = backend.execute(
+                route, conditionalRequest, context, execAware);
         Date responseDate = getCurrentDate();
 
         backendResponse.addHeader("Via", generateViaHeader(backendResponse));
 
         if (backendResponse.getStatusLine().getStatusCode() != HttpStatus.SC_NOT_MODIFIED) {
-            return handleBackendResponse(target, request, requestDate, responseDate, backendResponse);
+            return handleBackendResponse(
+                    route, request, context, execAware,
+                    requestDate, responseDate, backendResponse);
         }
 
         Header resultEtagHeader = backendResponse.getFirstHeader(HeaderConstants.ETAG);
         if (resultEtagHeader == null) {
             log.warn("304 response did not contain ETag");
-            return callBackend(target, request, context);
+            return callBackend(route, request, context, execAware);
         }
 
         String resultEtag = resultEtagHeader.getValue();
         Variant matchingVariant = variants.get(resultEtag);
         if (matchingVariant == null) {
             log.debug("304 response did not contain ETag matching one sent in If-None-Match");
-            return callBackend(target, request, context);
+            return callBackend(route, request, context, execAware);
         }
 
         HttpCacheEntry matchedEntry = matchingVariant.getEntry();
 
         if (revalidationResponseIsTooOld(backendResponse, matchedEntry)) {
             EntityUtils.consume(backendResponse.getEntity());
-            return retryRequestUnconditionally(target, request, context,
-                    matchedEntry);
+            return retryRequestUnconditionally(route, request, context, execAware, matchedEntry);
         }
 
         recordCacheUpdate(context);
 
-        HttpCacheEntry responseEntry = getUpdatedVariantEntry(target,
-                conditionalRequest, requestDate, responseDate, backendResponse,
-                matchingVariant, matchedEntry);
+        HttpCacheEntry responseEntry = getUpdatedVariantEntry(
+                route.getTargetHost(), conditionalRequest, requestDate, responseDate,
+                backendResponse, matchingVariant, matchedEntry);
 
         HttpResponse resp = responseGenerator.generateResponse(responseEntry);
-        tryToUpdateVariantMap(target, request, matchingVariant);
+        tryToUpdateVariantMap(route.getTargetHost(), request, matchingVariant);
 
         if (shouldSendNotModifiedResponse(request, responseEntry)) {
             return responseGenerator.generateNotModifiedResponse(responseEntry);
@@ -810,18 +657,25 @@ public class CachingHttpClient implement
         return resp;
     }
 
-    private HttpResponse retryRequestUnconditionally(HttpHost target,
-            HttpRequestWrapper request, HttpContext context,
-            HttpCacheEntry matchedEntry) throws IOException {
+    private HttpResponse retryRequestUnconditionally(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware,
+            final HttpCacheEntry matchedEntry) throws IOException, HttpException {
         HttpRequestWrapper unconditional = conditionalRequestBuilder
             .buildUnconditionalRequest(request, matchedEntry);
-        return callBackend(target, unconditional, context);
+        return callBackend(route, unconditional, context, execAware);
     }
 
-    private HttpCacheEntry getUpdatedVariantEntry(HttpHost target,
-            HttpRequestWrapper conditionalRequest, Date requestDate,
-            Date responseDate, HttpResponse backendResponse,
-            Variant matchingVariant, HttpCacheEntry matchedEntry) {
+    private HttpCacheEntry getUpdatedVariantEntry(
+            final HttpHost target,
+            final HttpRequestWrapper conditionalRequest,
+            final Date requestDate,
+            final Date responseDate,
+            final HttpResponse backendResponse,
+            final Variant matchingVariant,
+            final HttpCacheEntry matchedEntry) {
         HttpCacheEntry responseEntry = matchedEntry;
         try {
             responseEntry = responseCache.updateVariantCacheEntry(target, conditionalRequest,
@@ -832,8 +686,10 @@ public class CachingHttpClient implement
         return responseEntry;
     }
 
-    private void tryToUpdateVariantMap(HttpHost target, HttpRequestWrapper request,
-            Variant matchingVariant) {
+    private void tryToUpdateVariantMap(
+            final HttpHost target,
+            final HttpRequestWrapper request,
+            final Variant matchingVariant) {
         try {
             responseCache.reuseVariantEntryFor(target, request, matchingVariant);
         } catch (IOException ioe) {
@@ -841,30 +697,33 @@ public class CachingHttpClient implement
         }
     }
 
-    private boolean shouldSendNotModifiedResponse(HttpRequestWrapper request,
-            HttpCacheEntry responseEntry) {
+    private boolean shouldSendNotModifiedResponse(
+            final HttpRequestWrapper request,
+            final HttpCacheEntry responseEntry) {
         return (suitabilityChecker.isConditional(request)
                 && suitabilityChecker.allConditionalsMatch(request, responseEntry, new Date()));
     }
 
-    HttpResponse revalidateCacheEntry(
-            HttpHost target,
-            HttpRequestWrapper request,
-            HttpContext context,
-            HttpCacheEntry cacheEntry) throws IOException, ProtocolException {
+    CloseableHttpResponse revalidateCacheEntry(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware,
+            final HttpCacheEntry cacheEntry) throws IOException, HttpException {
 
         HttpRequestWrapper conditionalRequest = conditionalRequestBuilder.buildConditionalRequest(request, cacheEntry);
 
         Date requestDate = getCurrentDate();
-        HttpResponse backendResponse = backend.execute(target, conditionalRequest, context);
+        CloseableHttpResponse backendResponse = backend.execute(
+                route, conditionalRequest, context, execAware);
         Date responseDate = getCurrentDate();
 
         if (revalidationResponseIsTooOld(backendResponse, cacheEntry)) {
-            EntityUtils.consume(backendResponse.getEntity());
-            HttpRequest unconditional = conditionalRequestBuilder
+            backendResponse.close();
+            HttpRequestWrapper unconditional = conditionalRequestBuilder
                 .buildUnconditionalRequest(request, cacheEntry);
             requestDate = getCurrentDate();
-            backendResponse = backend.execute(target, unconditional, context);
+            backendResponse = backend.execute(route, unconditional, context, execAware);
             responseDate = getCurrentDate();
         }
 
@@ -876,27 +735,31 @@ public class CachingHttpClient implement
         }
 
         if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
-            HttpCacheEntry updatedEntry = responseCache.updateCacheEntry(target, request, cacheEntry,
+            HttpCacheEntry updatedEntry = responseCache.updateCacheEntry(
+                    route.getTargetHost(), request, cacheEntry,
                     backendResponse, requestDate, responseDate);
             if (suitabilityChecker.isConditional(request)
                     && suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
-                return responseGenerator.generateNotModifiedResponse(updatedEntry);
+                return Proxies.enhanceResponse(
+                        responseGenerator.generateNotModifiedResponse(updatedEntry));
             }
-            return responseGenerator.generateResponse(updatedEntry);
+            return Proxies.enhanceResponse(responseGenerator.generateResponse(updatedEntry));
         }
 
         if (staleIfErrorAppliesTo(statusCode)
             && !staleResponseNotAllowed(request, cacheEntry, getCurrentDate())
             && validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
-            final HttpResponse cachedResponse = responseGenerator.generateResponse(cacheEntry);
-            cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
-            HttpEntity errorBody = backendResponse.getEntity();
-            if (errorBody != null) EntityUtils.consume(errorBody);
-            return cachedResponse;
+            try {
+                HttpResponse cachedResponse = responseGenerator.generateResponse(cacheEntry);
+                cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
+                return Proxies.enhanceResponse(cachedResponse);
+            } finally {
+                backendResponse.close();
+            }
         }
-
-        return handleBackendResponse(target, conditionalRequest, requestDate, responseDate,
-                                     backendResponse);
+        return handleBackendResponse(
+                route, conditionalRequest, context, execAware,
+                requestDate, responseDate, backendResponse);
     }
 
     private boolean staleIfErrorAppliesTo(int statusCode) {
@@ -906,25 +769,29 @@ public class CachingHttpClient implement
                 || statusCode == HttpStatus.SC_GATEWAY_TIMEOUT;
     }
 
-    HttpResponse handleBackendResponse(
-            HttpHost target,
-            HttpRequestWrapper request,
-            Date requestDate,
-            Date responseDate,
-            HttpResponse backendResponse) throws IOException {
+    CloseableHttpResponse handleBackendResponse(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware,
+            final Date requestDate,
+            final Date responseDate,
+            final CloseableHttpResponse backendResponse) throws IOException {
 
         log.trace("Handling Backend response");
         responseCompliance.ensureProtocolCompliance(request, backendResponse);
 
+        HttpHost target = route.getTargetHost();
         boolean cacheable = responseCachingPolicy.isResponseCacheable(request, backendResponse);
         responseCache.flushInvalidatedCacheEntriesFor(target, request, backendResponse);
-        if (cacheable &&
-            !alreadyHaveNewerCacheEntry(target, request, backendResponse)) {
+        if (cacheable && !alreadyHaveNewerCacheEntry(target, request, backendResponse)) {
             try {
-                return responseCache.cacheAndReturnResponse(target, request, backendResponse, requestDate,
-                        responseDate);
+                return Proxies.enhanceResponse(responseCache.cacheAndReturnResponse(
+                        target, request, backendResponse, requestDate, responseDate));
             } catch (IOException ioe) {
                 log.warn("Unable to store entries in cache", ioe);
+            } finally {
+                backendResponse.close();
             }
         }
         if (!cacheable) {

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java?rev=1418743&r1=1418742&r2=1418743&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java Sat Dec  8 19:59:24 2012
@@ -29,10 +29,18 @@ package org.apache.http.impl.client.cach
 import java.io.IOException;
 import java.lang.reflect.UndeclaredThrowableException;
 import java.net.URI;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.commons.logging.Log;
@@ -109,7 +117,10 @@ import org.apache.http.util.VersionInfo;
  * memcached} storage backends.</p>
  * </p>
  * @since 4.1
+ *
+ * @deprecated (4.3)
  */
+@Deprecated
 @ThreadSafe // So long as the responseCache implementation is threadsafe
 public class CachingHttpClient implements HttpClient {
 
@@ -960,4 +971,151 @@ public class CachingHttpClient implement
         return false;
     }
 
+    static class AsynchronousValidator {
+        private final CachingHttpClient cachingClient;
+        private final ExecutorService executor;
+        private final Set<String> queued;
+        private final CacheKeyGenerator cacheKeyGenerator;
+
+        private final Log log = LogFactory.getLog(getClass());
+
+        /**
+         * Create AsynchronousValidator which will make revalidation requests
+         * using the supplied {@link CachingHttpClient}, and
+         * a {@link ThreadPoolExecutor} generated according to the thread
+         * pool settings provided in the given {@link CacheConfig}.
+         * @param cachingClient used to execute asynchronous requests
+         * @param config specifies thread pool settings. See
+         * {@link CacheConfig#getAsynchronousWorkersMax()},
+         * {@link CacheConfig#getAsynchronousWorkersCore()},
+         * {@link CacheConfig#getAsynchronousWorkerIdleLifetimeSecs()},
+         * and {@link CacheConfig#getRevalidationQueueSize()}.
+         */
+        public AsynchronousValidator(CachingHttpClient cachingClient,
+                CacheConfig config) {
+            this(cachingClient,
+                    new ThreadPoolExecutor(config.getAsynchronousWorkersCore(),
+                            config.getAsynchronousWorkersMax(),
+                            config.getAsynchronousWorkerIdleLifetimeSecs(),
+                            TimeUnit.SECONDS,
+                            new ArrayBlockingQueue<Runnable>(config.getRevalidationQueueSize()))
+                    );
+        }
+
+        /**
+         * Create AsynchronousValidator which will make revalidation requests
+         * using the supplied {@link CachingHttpClient} and
+         * {@link ExecutorService}.
+         * @param cachingClient used to execute asynchronous requests
+         * @param executor used to manage a thread pool of revalidation workers
+         */
+        AsynchronousValidator(CachingHttpClient cachingClient,
+                ExecutorService executor) {
+            this.cachingClient = cachingClient;
+            this.executor = executor;
+            this.queued = new HashSet<String>();
+            this.cacheKeyGenerator = new CacheKeyGenerator();
+        }
+
+        /**
+         * Schedules an asynchronous revalidation
+         *
+         * @param target
+         * @param request
+         * @param context
+         * @param entry
+         */
+        public synchronized void revalidateCacheEntry(HttpHost target,
+                HttpRequestWrapper request, HttpContext context, HttpCacheEntry entry) {
+            // getVariantURI will fall back on getURI if no variants exist
+            String uri = cacheKeyGenerator.getVariantURI(target, request, entry);
+
+            if (!queued.contains(uri)) {
+                AsynchronousValidationRequest revalidationRequest =
+                    new AsynchronousValidationRequest(this, cachingClient, target,
+                            request, context, entry, uri);
+
+                try {
+                    executor.execute(revalidationRequest);
+                    queued.add(uri);
+                } catch (RejectedExecutionException ree) {
+                    log.debug("Revalidation for [" + uri + "] not scheduled: " + ree);
+                }
+            }
+        }
+
+        /**
+         * Removes an identifier from the internal list of revalidation jobs in
+         * progress.  This is meant to be called by
+         * {@link AsynchronousValidationRequest#run()} once the revalidation is
+         * complete, using the identifier passed in during constructions.
+         * @param identifier
+         */
+        synchronized void markComplete(String identifier) {
+            queued.remove(identifier);
+        }
+
+        Set<String> getScheduledIdentifiers() {
+            return Collections.unmodifiableSet(queued);
+        }
+
+        ExecutorService getExecutor() {
+            return executor;
+        }
+    }
+
+    static class AsynchronousValidationRequest implements Runnable {
+        private final AsynchronousValidator parent;
+        private final CachingHttpClient cachingClient;
+        private final HttpHost target;
+        private final HttpRequestWrapper request;
+        private final HttpContext context;
+        private final HttpCacheEntry cacheEntry;
+        private final String identifier;
+
+        private final Log log = LogFactory.getLog(getClass());
+
+        /**
+         * Used internally by {@link AsynchronousValidator} to schedule a
+         * revalidation.
+         * @param cachingClient
+         * @param target
+         * @param request
+         * @param context
+         * @param cacheEntry
+         * @param bookKeeping
+         * @param identifier
+         */
+        AsynchronousValidationRequest(AsynchronousValidator parent,
+                CachingHttpClient cachingClient, HttpHost target,
+                HttpRequestWrapper request, HttpContext context,
+                HttpCacheEntry cacheEntry,
+                String identifier) {
+            this.parent = parent;
+            this.cachingClient = cachingClient;
+            this.target = target;
+            this.request = request;
+            this.context = context;
+            this.cacheEntry = cacheEntry;
+            this.identifier = identifier;
+        }
+
+        public void run() {
+            try {
+                cachingClient.revalidateCacheEntry(target, request, context, cacheEntry);
+            } catch (IOException ioe) {
+                log.debug("Asynchronous revalidation failed due to exception: " + ioe);
+            } catch (ProtocolException pe) {
+                log.error("ProtocolException thrown during asynchronous revalidation: " + pe);
+            } finally {
+                parent.markComplete(identifier);
+            }
+        }
+
+        String getIdentifier() {
+            return identifier;
+        }
+
+    }
+
 }

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Proxies.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Proxies.java?rev=1418743&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Proxies.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Proxies.java Sat Dec  8 19:59:24 2012
@@ -0,0 +1,30 @@
+package org.apache.http.impl.client.cache;
+
+import java.lang.reflect.Proxy;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.annotation.NotThreadSafe;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.util.Args;
+
+/**
+ * Proxies for HTTP message objects.
+ *
+ * @since 4.3
+ */
+@NotThreadSafe
+class Proxies {
+
+    public static CloseableHttpResponse enhanceResponse(final HttpResponse original) {
+        Args.notNull(original, "HTTP response");
+        if (original instanceof CloseableHttpResponse) {
+            return (CloseableHttpResponse) original;
+        } else {
+            return (CloseableHttpResponse) Proxy.newProxyInstance(
+                    ResponseProxyHandler.class.getClassLoader(),
+                    new Class<?>[] { CloseableHttpResponse.class },
+                    new ResponseProxyHandler(original));
+        }
+    }
+
+}
\ No newline at end of file

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Proxies.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Proxies.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Proxies.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProxyHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProxyHandler.java?rev=1418743&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProxyHandler.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProxyHandler.java Sat Dec  8 19:59:24 2012
@@ -0,0 +1,89 @@
+/*
+ * ====================================================================
+ * 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.http.impl.client.cache;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.annotation.NotThreadSafe;
+import org.apache.http.util.EntityUtils;
+
+/**
+ * A proxy class that can enhance an arbitrary {@link HttpResponse} with
+ * {@link Closeable#close()} method.
+ *
+ * @since 4.3
+ */
+@NotThreadSafe
+class ResponseProxyHandler implements InvocationHandler {
+
+    private static final Method CLOSE_METHOD;
+
+    static {
+        try {
+            CLOSE_METHOD = Closeable.class.getMethod("close");
+        } catch (NoSuchMethodException ex) {
+            throw new Error(ex);
+        }
+    }
+
+    private final HttpResponse original;
+
+    ResponseProxyHandler(final HttpResponse original) {
+        super();
+        this.original = original;
+    }
+
+    public void close() throws IOException {
+        EntityUtils.consume(original.getEntity());
+    }
+
+    public Object invoke(
+            final Object proxy, final Method method, final Object[] args) throws Throwable {
+        if (method.equals(CLOSE_METHOD)) {
+            close();
+            return null;
+        } else {
+            try {
+                return method.invoke(this.original, args);
+            } catch (InvocationTargetException ex) {
+                Throwable cause = ex.getCause();
+                if (cause != null) {
+                    throw cause;
+                } else {
+                    throw ex;
+                }
+            }
+        }
+    }
+
+}

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProxyHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProxyHandler.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProxyHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java?rev=1418743&r1=1418742&r2=1418743&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java Sat Dec  8 19:59:24 2012
@@ -43,7 +43,6 @@ import org.apache.http.client.cache.Http
 import org.apache.http.client.cache.HttpCacheStorage;
 import org.apache.http.client.cache.HttpCacheUpdateCallback;
 import org.apache.http.impl.client.cache.CacheConfig;
-import org.apache.http.impl.client.cache.CachingHttpClient;
 
 /**
  * <p>This class is a storage backend that uses an external <i>memcached</i>
@@ -62,13 +61,13 @@ import org.apache.http.impl.client.cache
  * fails (see the <a href="http://dustin.github.com/java-memcached-client/apidocs/net/spy/memcached/KetamaConnectionFactory.html">
  * KetamaConnectionFactory</a>).
  * </p>
- * 
+ *
  * <p>Because memcached places limits on the size of its keys, we need to
  * introduce a key hashing scheme to map the annotated URLs the higher-level
- * {@link CachingHttpClient} wants to use as keys onto ones that are suitable
+ * caching HTTP client wants to use as keys onto ones that are suitable
  * for use with memcached. Please see {@link KeyHashingScheme} if you would
  * like to use something other than the provided {@link SHA256KeyHashingScheme}.</p>
- * 
+ *
  * <p>Because this hashing scheme can potentially result in key collisions (though
  * highly unlikely), we need to store the higher-level logical storage key along
  * with the {@link HttpCacheEntry} so that we can re-check it on retrieval. There
@@ -87,7 +86,7 @@ import org.apache.http.impl.client.cache
 public class MemcachedHttpCacheStorage implements HttpCacheStorage {
 
     private static final Log log = LogFactory.getLog(MemcachedHttpCacheStorage.class);
-    
+
     private final MemcachedClientIF client;
     private final KeyHashingScheme keyHashingScheme;
     private final MemcachedCacheEntryFactory memcachedCacheEntryFactory;
@@ -158,7 +157,7 @@ public class MemcachedHttpCacheStorage i
         this.memcachedCacheEntryFactory = memcachedCacheEntryFactory;
         this.keyHashingScheme = keyHashingScheme;
     }
-    
+
     public void putEntry(String url, HttpCacheEntry entry) throws IOException  {
         byte[] bytes = serializeEntry(url, entry);
         String key = getCacheKey(url);
@@ -209,7 +208,7 @@ public class MemcachedHttpCacheStorage i
         }
         return mce;
     }
-    
+
     public HttpCacheEntry getEntry(String url) throws IOException {
         String key = getCacheKey(url);
         if (key == null) return null;

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java?rev=1418743&r1=1418742&r2=1418743&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java Sat Dec  8 19:59:24 2012
@@ -27,15 +27,19 @@
 package org.apache.http.impl.client.cache;
 
 import java.util.HashMap;
+
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpVersion;
-import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpExecutionAware;
 import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.client.execchain.ClientExecChain;
 import org.apache.http.message.BasicHttpRequest;
-import org.apache.http.protocol.HttpContext;
 import org.easymock.IExpectationSetters;
 import org.easymock.classextension.EasyMock;
 import org.junit.Before;
@@ -46,16 +50,17 @@ public abstract class AbstractProtocolTe
     protected static final int MAX_ENTRIES = 100;
     protected int entityLength = 128;
     protected HttpHost host;
+    protected HttpRoute route;
     protected HttpEntity body;
-    protected HttpClient mockBackend;
+    protected ClientExecChain mockBackend;
     protected HttpCache mockCache;
     protected HttpRequestWrapper request;
-    protected HttpResponse originResponse;
+    protected CloseableHttpResponse originResponse;
     protected CacheConfig config;
-    protected CachingHttpClient impl;
+    protected CachingExec impl;
     protected HttpCache cache;
 
-    public static HttpRequest eqRequest(HttpRequest in) {
+    public static HttpRequestWrapper eqRequest(HttpRequestWrapper in) {
         EasyMock.reportMatcher(new RequestEquivalent(in));
         return null;
     }
@@ -64,20 +69,23 @@ public abstract class AbstractProtocolTe
     public void setUp() {
         host = new HttpHost("foo.example.com");
 
+        route = new HttpRoute(host);
+
         body = HttpTestUtils.makeBody(entityLength);
 
         request = HttpRequestWrapper.wrap(new BasicHttpRequest("GET", "/foo", HttpVersion.HTTP_1_1));
 
-        originResponse = HttpTestUtils.make200Response();
+        originResponse = Proxies.enhanceResponse(HttpTestUtils.make200Response());
 
         config = CacheConfig.custom()
             .setMaxCacheEntries(MAX_ENTRIES)
             .setMaxObjectSize(MAX_BYTES)
             .build();
+
         cache = new BasicHttpCache(config);
-        mockBackend = EasyMock.createNiceMock(HttpClient.class);
+        mockBackend = EasyMock.createNiceMock(ClientExecChain.class);
         mockCache = EasyMock.createNiceMock(HttpCache.class);
-        impl = new CachingHttpClient(mockBackend, cache, config);
+        impl = new CachingExec(mockBackend, cache, config);
     }
 
     protected void replayMocks() {
@@ -90,17 +98,30 @@ public abstract class AbstractProtocolTe
         EasyMock.verify(mockCache);
     }
 
-    protected IExpectationSetters<HttpResponse> backendExpectsAnyRequest() throws Exception {
-        HttpResponse resp = mockBackend.execute(EasyMock.isA(HttpHost.class), EasyMock
-                .isA(HttpRequest.class), (HttpContext) EasyMock.isNull());
+    protected IExpectationSetters<CloseableHttpResponse> backendExpectsAnyRequest() throws Exception {
+        CloseableHttpResponse resp = mockBackend.execute(
+                EasyMock.isA(HttpRoute.class),
+                EasyMock.isA(HttpRequestWrapper.class),
+                EasyMock.isA(HttpClientContext.class),
+                EasyMock.<HttpExecutionAware>isNull());
         return EasyMock.expect(resp);
     }
 
+    protected IExpectationSetters<CloseableHttpResponse> backendExpectsAnyRequestAndReturn(
+            HttpResponse reponse) throws Exception {
+        CloseableHttpResponse resp = mockBackend.execute(
+                EasyMock.isA(HttpRoute.class),
+                EasyMock.isA(HttpRequestWrapper.class),
+                EasyMock.isA(HttpClientContext.class),
+                EasyMock.<HttpExecutionAware>isNull());
+        return EasyMock.expect(resp).andReturn(Proxies.enhanceResponse(reponse));
+    }
+
     protected void emptyMockCacheExpectsNoPuts() throws Exception {
-        mockBackend = EasyMock.createNiceMock(HttpClient.class);
+        mockBackend = EasyMock.createNiceMock(ClientExecChain.class);
         mockCache = EasyMock.createNiceMock(HttpCache.class);
 
-        impl = new CachingHttpClient(mockBackend, mockCache, config);
+        impl = new CachingExec(mockBackend, mockCache, config);
 
         EasyMock.expect(mockCache.getCacheEntry(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)))
             .andReturn(null).anyTimes();
@@ -126,7 +147,7 @@ public abstract class AbstractProtocolTe
                 .setMaxObjectSize(MAX_BYTES)
                 .setSharedCache(false)
                 .build();
-        impl = new CachingHttpClient(mockBackend, cache, config);
+        impl = new CachingExec(mockBackend, cache, config);
     }
 
     public AbstractProtocolTest() {

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyBackend.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyBackend.java?rev=1418743&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyBackend.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyBackend.java Sat Dec  8 19:59:24 2012
@@ -0,0 +1,70 @@
+/*
+ * ====================================================================
+ *
+ *  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.http.impl.client.cache;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpExecutionAware;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.client.execchain.ClientExecChain;
+import org.apache.http.message.BasicHttpResponse;
+
+public class DummyBackend implements ClientExecChain {
+
+    private HttpRequest request;
+    private HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP",1,1), HttpStatus.SC_OK, "OK");
+    private int executions = 0;
+
+    public void setResponse(HttpResponse resp) {
+        response = resp;
+    }
+
+    public HttpRequest getCapturedRequest() {
+        return request;
+    }
+
+    public CloseableHttpResponse execute(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext clientContext,
+            final HttpExecutionAware execAware) throws IOException, HttpException {
+        this.request = request;
+        executions++;
+        return Proxies.enhanceResponse(response);
+    }
+
+    public int getExecutions() {
+        return executions;
+    }
+}

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyBackend.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyBackend.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyBackend.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain