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 2013/10/22 21:59:15 UTC

svn commit: r1534757 - in /httpcomponents/httpclient/trunk/httpclient-cache/src: main/java/org/apache/http/impl/client/cache/ test/java/org/apache/http/impl/client/cache/

Author: olegk
Date: Tue Oct 22 19:59:14 2013
New Revision: 1534757

URL: http://svn.apache.org/r1534757
Log:
HTTPCLIENT-1425: Fixed socket closed exception thrown by caching HttpClient when the origin server sends a long chunked response (follow-up)
Contributed by James Leigh <james at 3roundstones dot com>

Modified:
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/SizeLimitedResponseReader.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/TestSizeLimitedResponseReader.java

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java?rev=1534757&r1=1534756&r2=1534757&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java Tue Oct 22 19:59:14 2013
@@ -206,8 +206,8 @@ class BasicHttpCache implements HttpCach
         return (resource.length() < contentLength);
     }
 
-    HttpResponse generateIncompleteResponseError(final HttpResponse response,
-            final Resource resource) {
+    CloseableHttpResponse generateIncompleteResponseError(
+            final HttpResponse response, final Resource resource) {
         final int contentLength = Integer.parseInt(response.getFirstHeader(HTTP.CONTENT_LEN).getValue());
         final HttpResponse error =
             new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_BAD_GATEWAY, "Bad Gateway");
@@ -218,7 +218,7 @@ class BasicHttpCache implements HttpCach
         final byte[] msgBytes = msg.getBytes();
         error.setHeader("Content-Length", Integer.toString(msgBytes.length));
         error.setEntity(new ByteArrayEntity(msgBytes));
-        return error;
+        return Proxies.enhanceResponse(error);
     }
 
     HttpCacheEntry doGetUpdatedParentEntry(
@@ -281,7 +281,7 @@ class BasicHttpCache implements HttpCach
                 responseReceived);
     }
 
-    public HttpResponse cacheAndReturnResponse(
+    public CloseableHttpResponse cacheAndReturnResponse(
             final HttpHost host,
             final HttpRequest request,
             final CloseableHttpResponse originResponse,
@@ -318,7 +318,8 @@ class BasicHttpCache implements HttpCach
         }
     }
 
-    SizeLimitedResponseReader getResponseReader(final HttpRequest request, final HttpResponse backEndResponse) {
+    SizeLimitedResponseReader getResponseReader(final HttpRequest request,
+            final CloseableHttpResponse backEndResponse) {
         return new SizeLimitedResponseReader(
                 resourceFactory, maxObjectSizeBytes, request, backEndResponse);
     }

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java?rev=1534757&r1=1534756&r2=1534757&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java Tue Oct 22 19:59:14 2013
@@ -36,6 +36,7 @@ import org.apache.http.HttpVersion;
 import org.apache.http.annotation.Immutable;
 import org.apache.http.client.cache.HeaderConstants;
 import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.utils.DateUtils;
 import org.apache.http.message.BasicHeader;
 import org.apache.http.message.BasicHttpResponse;
@@ -67,7 +68,7 @@ class CachedHttpResponseGenerator {
      *            {@link CacheEntity} to transform into an {@link HttpResponse}
      * @return {@link HttpResponse} that was constructed
      */
-    HttpResponse generateResponse(final HttpCacheEntry entry) {
+    CloseableHttpResponse generateResponse(final HttpCacheEntry entry) {
 
         final Date now = new Date();
         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, entry
@@ -90,17 +91,14 @@ class CachedHttpResponseGenerator {
             }
         }
 
-        return response;
+        return Proxies.enhanceResponse(response);
     }
 
     /**
      * Generate a 304 - Not Modified response from a {@link CacheEntity}.  This should be
      * used to respond to conditional requests, when the entry exists or has been re-validated.
-     *
-     * @param entry
-     * @return
      */
-    HttpResponse generateNotModifiedResponse(final HttpCacheEntry entry) {
+    CloseableHttpResponse generateNotModifiedResponse(final HttpCacheEntry entry) {
 
         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
                 HttpStatus.SC_NOT_MODIFIED, "Not Modified");
@@ -145,7 +143,7 @@ class CachedHttpResponseGenerator {
             response.addHeader(varyHeader);
         }
 
-        return response;
+        return Proxies.enhanceResponse(response);
     }
 
     private void addMissingContentLengthHeader(final HttpResponse response, final HttpEntity entity) {

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java?rev=1534757&r1=1534756&r2=1534757&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingExec.java Tue Oct 22 19:59:14 2013
@@ -278,10 +278,10 @@ public class CachingExec implements Clie
         final Date now = getCurrentDate();
         if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) {
             log.debug("Cache hit");
-            out = Proxies.enhanceResponse(generateCachedResponse(request, context, entry, now));
+            out = generateCachedResponse(request, context, entry, now);
         } else if (!mayCallBackend(request)) {
             log.debug("Cache entry not suitable but only-if-cached requested");
-            out = Proxies.enhanceResponse(generateGatewayTimeout(context));
+            out = generateGatewayTimeout(context);
         } else if (!(entry.getStatusCode() == HttpStatus.SC_NOT_MODIFIED
                 && !suitabilityChecker.isConditional(request))) {
             log.debug("Revalidating cache entry");
@@ -311,14 +311,13 @@ public class CachingExec implements Clie
                 && !staleResponseNotAllowed(request, entry, now)
                 && validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
                 log.trace("Serving stale with asynchronous revalidation");
-                final HttpResponse resp = generateCachedResponse(request, context, entry, now);
+                final CloseableHttpResponse resp = generateCachedResponse(request, context, entry, now);
                 asynchRevalidator.revalidateCacheEntry(this, route, request, context, execAware, entry);
-                return Proxies.enhanceResponse(resp);
+                return resp;
             }
             return revalidateCacheEntry(route, request, context, execAware, entry);
         } catch (final IOException ioex) {
-            return Proxies.enhanceResponse(
-                    handleRevalidationFailure(request, context, entry, now));
+            return handleRevalidationFailure(request, context, entry, now);
         }
     }
 
@@ -338,8 +337,8 @@ public class CachingExec implements Clie
 
         final Map<String, Variant> variants = getExistingCacheVariants(target, request);
         if (variants != null && variants.size() > 0) {
-            return Proxies.enhanceResponse(
-                    negotiateResponseFromVariants(route, request, context, execAware, variants));
+            return negotiateResponseFromVariants(route, request, context,
+                    execAware, variants);
         }
 
         return callBackend(route, request, context, execAware);
@@ -412,9 +411,9 @@ public class CachingExec implements Clie
         }
     }
 
-    private HttpResponse generateCachedResponse(final HttpRequestWrapper request,
+    private CloseableHttpResponse generateCachedResponse(final HttpRequestWrapper request,
             final HttpContext context, final HttpCacheEntry entry, final Date now) {
-        final HttpResponse cachedResponse;
+        final CloseableHttpResponse cachedResponse;
         if (request.containsHeader(HeaderConstants.IF_NONE_MATCH)
                 || request.containsHeader(HeaderConstants.IF_MODIFIED_SINCE)) {
             cachedResponse = responseGenerator.generateNotModifiedResponse(entry);
@@ -428,7 +427,7 @@ public class CachingExec implements Clie
         return cachedResponse;
     }
 
-    private HttpResponse handleRevalidationFailure(
+    private CloseableHttpResponse handleRevalidationFailure(
             final HttpRequestWrapper request,
             final HttpContext context,
             final HttpCacheEntry entry,
@@ -440,15 +439,17 @@ public class CachingExec implements Clie
         }
     }
 
-    private HttpResponse generateGatewayTimeout(final HttpContext context) {
+    private CloseableHttpResponse generateGatewayTimeout(
+            final HttpContext context) {
         setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
-        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"));
     }
 
-    private HttpResponse unvalidatedCacheHit(
+    private CloseableHttpResponse unvalidatedCacheHit(
             final HttpContext context, final HttpCacheEntry entry) {
-        final HttpResponse cachedResponse = responseGenerator.generateResponse(entry);
+        final CloseableHttpResponse cachedResponse = responseGenerator.generateResponse(entry);
         setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
         cachedResponse.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\"");
         return cachedResponse;
@@ -607,7 +608,7 @@ public class CachingExec implements Clie
         return false;
     }
 
-    HttpResponse negotiateResponseFromVariants(
+    CloseableHttpResponse negotiateResponseFromVariants(
             final HttpRoute route,
             final HttpRequestWrapper request,
             final HttpClientContext context,
@@ -662,7 +663,7 @@ public class CachingExec implements Clie
                     backendResponse, matchingVariant, matchedEntry);
             backendResponse.close();
 
-            final HttpResponse resp = responseGenerator.generateResponse(responseEntry);
+            final CloseableHttpResponse resp = responseGenerator.generateResponse(responseEntry);
             tryToUpdateVariantMap(route.getTargetHost(), request, matchingVariant);
 
             if (shouldSendNotModifiedResponse(request, responseEntry)) {
@@ -763,19 +764,19 @@ public class CachingExec implements Clie
                     backendResponse, requestDate, responseDate);
             if (suitabilityChecker.isConditional(request)
                     && suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
-                return Proxies.enhanceResponse(
-                        responseGenerator.generateNotModifiedResponse(updatedEntry));
+                return responseGenerator
+                        .generateNotModifiedResponse(updatedEntry);
             }
-            return Proxies.enhanceResponse(responseGenerator.generateResponse(updatedEntry));
+            return responseGenerator.generateResponse(updatedEntry);
         }
 
         if (staleIfErrorAppliesTo(statusCode)
             && !staleResponseNotAllowed(request, cacheEntry, getCurrentDate())
             && validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
             try {
-                final HttpResponse cachedResponse = responseGenerator.generateResponse(cacheEntry);
+                final CloseableHttpResponse cachedResponse = responseGenerator.generateResponse(cacheEntry);
                 cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
-                return Proxies.enhanceResponse(cachedResponse);
+                return cachedResponse;
             } finally {
                 backendResponse.close();
             }
@@ -809,8 +810,8 @@ public class CachingExec implements Clie
         responseCache.flushInvalidatedCacheEntriesFor(target, request, backendResponse);
         if (cacheable && !alreadyHaveNewerCacheEntry(target, request, backendResponse)) {
             storeRequestIfModifiedSinceFor304Response(request, backendResponse);
-            return Proxies.enhanceResponse(responseCache.cacheAndReturnResponse(
-                    target, request, backendResponse, requestDate, responseDate));
+            return responseCache.cacheAndReturnResponse(target, request,
+                    backendResponse, requestDate, responseDate);
         }
         if (!cacheable) {
             try {

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java?rev=1534757&r1=1534756&r2=1534757&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java Tue Oct 22 19:59:14 2013
@@ -114,9 +114,9 @@ interface HttpCache {
      * @return the {@link HttpResponse}
      * @throws IOException
      */
-    HttpResponse cacheAndReturnResponse(HttpHost host, HttpRequest request,
-            CloseableHttpResponse originResponse, Date requestSent,
-            Date responseReceived)
+    CloseableHttpResponse cacheAndReturnResponse(HttpHost host,
+            HttpRequest request, CloseableHttpResponse originResponse,
+            Date requestSent, Date responseReceived)
         throws IOException;
 
     /**

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/SizeLimitedResponseReader.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/SizeLimitedResponseReader.java?rev=1534757&r1=1534756&r2=1534757&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/SizeLimitedResponseReader.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/SizeLimitedResponseReader.java Tue Oct 22 19:59:14 2013
@@ -28,6 +28,7 @@ package org.apache.http.impl.client.cach
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Proxy;
 
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpRequest;
@@ -36,6 +37,7 @@ import org.apache.http.annotation.NotThr
 import org.apache.http.client.cache.InputLimit;
 import org.apache.http.client.cache.Resource;
 import org.apache.http.client.cache.ResourceFactory;
+import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.message.BasicHttpResponse;
 
 /**
@@ -47,7 +49,7 @@ class SizeLimitedResponseReader {
     private final ResourceFactory resourceFactory;
     private final long maxResponseSizeBytes;
     private final HttpRequest request;
-    private final HttpResponse response;
+    private final CloseableHttpResponse response;
 
     private InputStream instream;
     private InputLimit limit;
@@ -62,7 +64,7 @@ class SizeLimitedResponseReader {
             final ResourceFactory resourceFactory,
             final long maxResponseSizeBytes,
             final HttpRequest request,
-            final HttpResponse response) {
+            final CloseableHttpResponse response) {
         super();
         this.resourceFactory = resourceFactory;
         this.maxResponseSizeBytes = maxResponseSizeBytes;
@@ -119,7 +121,7 @@ class SizeLimitedResponseReader {
         return resource;
     }
 
-    HttpResponse getReconstructedResponse() throws IOException {
+    CloseableHttpResponse getReconstructedResponse() throws IOException {
         ensureConsumed();
         final HttpResponse reconstructed = new BasicHttpResponse(response.getStatusLine());
         reconstructed.setHeaders(response.getAllHeaders());
@@ -132,7 +134,16 @@ class SizeLimitedResponseReader {
             combinedEntity.setChunked(entity.isChunked());
         }
         reconstructed.setEntity(combinedEntity);
-        return reconstructed;
+        return (CloseableHttpResponse) Proxy.newProxyInstance(
+                ResponseProxyHandler.class.getClassLoader(),
+                new Class<?>[] { CloseableHttpResponse.class },
+                new ResponseProxyHandler(reconstructed) {
+
+                    public void close() throws IOException {
+                        response.close();
+                    }
+
+                });
     }
 
 }

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java?rev=1534757&r1=1534756&r2=1534757&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java Tue Oct 22 19:59:14 2013
@@ -2877,7 +2877,7 @@ public class TestProtocolRequirements ex
         validated.setHeader("Content-Length", "128");
         validated.setEntity(new ByteArrayEntity(bytes));
 
-        final HttpResponse reconstructed = HttpTestUtils.make200Response();
+        final CloseableHttpResponse reconstructed = Proxies.enhanceResponse(HttpTestUtils.make200Response());
 
         final Capture<HttpRequestWrapper> cap = new Capture<HttpRequestWrapper>();
 

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestSizeLimitedResponseReader.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestSizeLimitedResponseReader.java?rev=1534757&r1=1534756&r2=1534757&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestSizeLimitedResponseReader.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestSizeLimitedResponseReader.java Tue Oct 22 19:59:14 2013
@@ -26,11 +26,16 @@
  */
 package org.apache.http.impl.client.cache;
 
+import java.io.IOException;
+import java.lang.reflect.Proxy;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpStatus;
 import org.apache.http.HttpVersion;
+import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.entity.StringEntity;
@@ -55,8 +60,7 @@ public class TestSizeLimitedResponseRead
     @Test
     public void testLargeResponseIsTooLarge() throws Exception {
         final byte[] buf = new byte[] { 1, 2, 3, 4, 5 };
-        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
-        response.setEntity(new ByteArrayEntity(buf));
+        final CloseableHttpResponse response = make200Response(buf);
 
         impl = new SizeLimitedResponseReader(new HeapResourceFactory(), MAX_SIZE, request, response);
 
@@ -72,8 +76,7 @@ public class TestSizeLimitedResponseRead
     @Test
     public void testExactSizeResponseIsNotTooLarge() throws Exception {
         final byte[] buf = new byte[] { 1, 2, 3, 4 };
-        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
-        response.setEntity(new ByteArrayEntity(buf));
+        final CloseableHttpResponse response = make200Response(buf);
 
         impl = new SizeLimitedResponseReader(new HeapResourceFactory(), MAX_SIZE, request, response);
 
@@ -89,8 +92,7 @@ public class TestSizeLimitedResponseRead
     @Test
     public void testSmallResponseIsNotTooLarge() throws Exception {
         final byte[] buf = new byte[] { 1, 2, 3 };
-        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
-        response.setEntity(new ByteArrayEntity(buf));
+        final CloseableHttpResponse response = make200Response(buf);
 
         impl = new SizeLimitedResponseReader(new HeapResourceFactory(), MAX_SIZE, request, response);
 
@@ -105,7 +107,7 @@ public class TestSizeLimitedResponseRead
 
     @Test
     public void testResponseWithNoEntityIsNotTooLarge() throws Exception {
-        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
+        final CloseableHttpResponse response = make200Response();
 
         impl = new SizeLimitedResponseReader(new HeapResourceFactory(), MAX_SIZE, request, response);
 
@@ -117,7 +119,7 @@ public class TestSizeLimitedResponseRead
 
     @Test
     public void testTooLargeEntityHasOriginalContentTypes() throws Exception {
-        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
+        final CloseableHttpResponse response = make200Response();
         final StringEntity entity = new StringEntity("large entity content");
         response.setEntity(entity);
 
@@ -137,10 +139,45 @@ public class TestSizeLimitedResponseRead
     }
 
     @Test
+    public void testTooLargeResponseCombinedClosed() throws Exception {
+        final AtomicBoolean closed = new AtomicBoolean(false);
+        final CloseableHttpResponse response = (CloseableHttpResponse) Proxy
+                .newProxyInstance(ResponseProxyHandler.class.getClassLoader(),
+                        new Class<?>[] { CloseableHttpResponse.class },
+                        new ResponseProxyHandler(new BasicHttpResponse(
+                                HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK")) {
+                            public void close() throws IOException {
+                                closed.set(true);
+                            }
+                        });
+        final StringEntity entity = new StringEntity("large entity content");
+        response.setEntity(entity);
+
+        impl = new SizeLimitedResponseReader(new HeapResourceFactory(), MAX_SIZE, request, response);
+
+        impl.readResponse();
+        final boolean tooLarge = impl.isLimitReached();
+        final CloseableHttpResponse result = impl.getReconstructedResponse();
+        try {
+            final HttpEntity reconstructedEntity = result.getEntity();
+            Assert.assertEquals(entity.getContentEncoding(), reconstructedEntity.getContentEncoding());
+            Assert.assertEquals(entity.getContentType(), reconstructedEntity.getContentType());
+
+            Assert.assertFalse(closed.get());
+            final String content = EntityUtils.toString(reconstructedEntity);
+
+            Assert.assertTrue(tooLarge);
+            Assert.assertEquals("large entity content", content);
+        } finally {
+            result.close();
+        }
+        Assert.assertTrue(closed.get());
+    }
+
+    @Test
     public void testResponseCopiesAllOriginalHeaders() throws Exception {
         final byte[] buf = new byte[] { 1, 2, 3 };
-        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
-        response.setEntity(new ByteArrayEntity(buf));
+        final CloseableHttpResponse response = make200Response(buf);
         response.setHeader("Content-Encoding", "gzip");
 
         impl = new SizeLimitedResponseReader(new HeapResourceFactory(), MAX_SIZE, request, response);
@@ -155,4 +192,16 @@ public class TestSizeLimitedResponseRead
         Assert.assertEquals("gzip", reconstructed.getFirstHeader("Content-Encoding").getValue());
     }
 
+    private CloseableHttpResponse make200Response() {
+        return Proxies.enhanceResponse(new BasicHttpResponse(
+                HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"));
+    }
+
+    private CloseableHttpResponse make200Response(final byte[] buf) {
+        final HttpResponse response = new BasicHttpResponse(
+                HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
+        response.setEntity(new ByteArrayEntity(buf));
+        return Proxies.enhanceResponse(response);
+    }
+
 }