Index: src/main/java/org/apache/http/impl/client/cache/CacheEntity.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/CacheEntity.java (revision 1420574) +++ src/main/java/org/apache/http/impl/client/cache/CacheEntity.java (working copy) @@ -67,8 +67,7 @@ } public long getContentLength() { - Resource resource = this.cacheEntry.getResource(); - return (resource != null) ? resource.length() : 0L; + return this.cacheEntry.getResource().length(); } public InputStream getContent() throws IOException { Index: src/main/java/org/apache/http/impl/client/cache/CacheEntryUpdater.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/CacheEntryUpdater.java (revision 1420574) +++ src/main/java/org/apache/http/impl/client/cache/CacheEntryUpdater.java (working copy) @@ -87,7 +87,10 @@ if (response.getStatusLine().getStatusCode() != HttpStatus.SC_NOT_MODIFIED) throw new IllegalArgumentException("Response must have 304 status code"); Header[] mergedHeaders = mergeHeaders(entry, response); - Resource resource = resourceFactory.copy(requestId, entry.getResource()); + Resource oldResource = entry.getResource(); + Resource resource = null; + if(oldResource != null) + resource = resourceFactory.copy(requestId, entry.getResource()); return new HttpCacheEntry( requestDate, responseDate, Index: src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java (revision 1420574) +++ src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java (working copy) @@ -72,13 +72,16 @@ Date now = new Date(); HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, entry .getStatusCode(), entry.getReasonPhrase()); - - HttpEntity entity = new CacheEntity(entry); + response.setHeaders(entry.getAllHeaders()); - addMissingContentLengthHeader(response, entity); - response.setEntity(entity); + if (entry.getResource() != null) { + HttpEntity entity = new CacheEntity(entry); + addMissingContentLengthHeader(response, entity); + response.setEntity(entity); + } + long age = this.validityStrategy.getCurrentAgeSecs(entry, now); if (age > 0) { if (age >= Integer.MAX_VALUE) { Index: src/test/java/org/apache/http/impl/client/cache/TestCachingExec.java =================================================================== --- src/test/java/org/apache/http/impl/client/cache/TestCachingExec.java (revision 1420574) +++ src/test/java/org/apache/http/impl/client/cache/TestCachingExec.java (working copy) @@ -42,6 +42,7 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Date; @@ -74,6 +75,7 @@ import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHttpRequest; import org.apache.http.message.BasicHttpResponse; +import org.apache.http.util.EntityUtils; import org.easymock.Capture; import org.easymock.IExpectationSetters; import org.easymock.classextension.EasyMock; @@ -1526,7 +1528,75 @@ impl.execute(route, request); assertEquals(1, backend.getExecutions()); } + + @Test + public void testNoEntityForIfNoneMatchRequestNotYetInCache() + throws Exception { + Date now = new Date(); + Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); + + impl = new CachingExec(mockBackend, new BasicHttpCache(), + CacheConfig.DEFAULT); + HttpRequestWrapper req1 = HttpRequestWrapper.wrap(new HttpGet( + "http://foo.example.com/")); + req1.addHeader("If-None-Match", "\"etag\""); + + HttpResponse resp1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, + HttpStatus.SC_NOT_MODIFIED, "Not modified"); + resp1.setHeader("Content-Length", "128"); + resp1.setHeader("ETag", "\"etag\""); + resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo)); + resp1.setHeader("Cache-Control", "public, max-age=5"); + + backendExpectsAnyRequestAndReturn(resp1); + replayMocks(); + HttpResponse result = impl.execute(route, req1); + verifyMocks(); + + assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getStatusLine() + .getStatusCode()); + assertNull("The 304 response messages MUST NOT contain a message-body", + result.getEntity()); + } + + @Test + public void testNotModifiedResponseUpdatesCacheEntryWhenNoEntity() + throws Exception { + + Date now = new Date(); + + impl = new CachingExec(mockBackend, new BasicHttpCache(),CacheConfig.DEFAULT); + + HttpRequestWrapper req1 = HttpRequestWrapper.wrap(new HttpGet("http://foo.example.com/")); + req1.addHeader("If-None-Match", "etag"); + + HttpRequestWrapper req2 = HttpRequestWrapper.wrap(new HttpGet("http://foo.example.com/")); + req2.addHeader("If-None-Match", "etag"); + + HttpResponse resp1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_NOT_MODIFIED, "Not modified"); + resp1.setHeader("Date", DateUtils.formatDate(now)); + resp1.setHeader("Cache-Control","max-age=0"); + resp1.setHeader("Etag", "etag"); + + HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_NOT_MODIFIED, "Not modified"); + resp2.setHeader("Date", DateUtils.formatDate(now)); + resp2.setHeader("Cache-Control","max-age=0"); + resp1.setHeader("Etag", "etag"); + + backendExpectsAnyRequestAndReturn(resp1); + backendExpectsAnyRequestAndReturn(resp2); + replayMocks(); + HttpResponse result1 = impl.execute(route, req1); + HttpResponse result2 = impl.execute(route, req2); + verifyMocks(); + + assertEquals(HttpStatus.SC_NOT_MODIFIED, result1.getStatusLine().getStatusCode()); + assertEquals("etag", result1.getFirstHeader("Etag").getValue()); + assertEquals(HttpStatus.SC_NOT_MODIFIED, result2.getStatusLine().getStatusCode()); + assertEquals("etag", result2.getFirstHeader("Etag").getValue()); + } + private IExpectationSetters backendExpectsAnyRequestAndReturn( HttpResponse response) throws Exception { CloseableHttpResponse resp = mockBackend.execute(