diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java index 528678e04..d43a0c4d9 100644 --- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java +++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java @@ -194,6 +194,19 @@ class BasicHttpCache implements HttpCache { return updatedEntry; } + public HttpCacheEntry updateVariantCacheEntry(HttpHost target, HttpRequest request, + HttpCacheEntry stale, HttpResponse originResponse, + Date requestSent, Date responseReceived, String cacheKey) throws IOException { + HttpCacheEntry updatedEntry = cacheEntryUpdater.updateCacheEntry( + request.getRequestLine().getUri(), + stale, + requestSent, + responseReceived, + originResponse); + storage.putEntry(cacheKey, updatedEntry); + return updatedEntry; + } + public HttpResponse cacheAndReturnResponse(HttpHost host, HttpRequest request, HttpResponse originResponse, Date requestSent, Date responseReceived) throws IOException { diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java index 5247c6a27..567a67ee3 100644 --- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java +++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java @@ -635,7 +635,8 @@ public class CachingHttpClient implements HttpClient { // SHOULD update cache entry according to rfc HttpCacheEntry responseEntry = matchedEntry; try { - responseEntry = responseCache.updateCacheEntry(target, conditionalRequest, matchedEntry, backendResponse, requestDate, responseDate); + responseEntry = responseCache.updateVariantCacheEntry(target, conditionalRequest, + matchedEntry, backendResponse, requestDate, responseDate, matchingVariant.getCacheKey()); } catch (IOException ioe) { log.warn("Could not update cache entry", ioe); } diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java index 60cc454ca..5671045b6 100644 --- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java +++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java @@ -109,4 +109,22 @@ interface HttpCache { Date requestSent, Date responseReceived) throws IOException; + /** + * Update a specific {@link HttpCacheEntry} representing a cached variant + * using a 304 {@link HttpResponse}. + * @param target host for client request + * @param request actual request from upstream client + * @param stale current variant cache entry + * @param originResponse 304 response received from origin + * @param requestSent when the validating request was sent + * @param responseReceived when the validating response was received + * @param cacheKey where in the cache this entry is currently stored + * @return + * @throws IOException + */ + HttpCacheEntry updateVariantCacheEntry(HttpHost target, HttpRequest request, + HttpCacheEntry stale, HttpResponse originResponse, Date requestSent, + Date responseReceived, String cacheKey) + throws IOException; + } diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java index 4601ab9b1..05b515c55 100644 --- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java +++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java @@ -1615,7 +1615,7 @@ public class TestCachingHttpClient { backendCall(variantConditionalRequest, originResponse); - EasyMock.expect(mockCache.updateCacheEntry(EasyMock.same(host), EasyMock.same(variantConditionalRequest), EasyMock.same(variant2), EasyMock.same(originResponse), EasyMock.isA(Date.class), EasyMock.isA(Date.class))).andReturn(updatedMatchedEntry); + EasyMock.expect(mockCache.updateVariantCacheEntry(EasyMock.same(host), EasyMock.same(variantConditionalRequest), EasyMock.same(variant2), EasyMock.same(originResponse), EasyMock.isA(Date.class), EasyMock.isA(Date.class), EasyMock.eq("D"))).andReturn(updatedMatchedEntry); EasyMock.expect(mockResponseGenerator.generateResponse(updatedMatchedEntry)).andReturn(matchedResponse); diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java index 402b92e6a..04279f0a3 100644 --- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java +++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java @@ -865,4 +865,66 @@ public class TestProtocolRecommendations extends AbstractProtocolTest { } assertTrue(foundEtag1 && foundEtag2); } + + /* "If the entity-tag of the new response matches that of an existing + * entry, the new response SHOULD be used to update the header fields + * of the existing entry, and the result MUST be returned to the + * client." + * + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 + */ + @Test + public void testResponseToExistingVariantsUpdatesEntry() + throws Exception { + + Date now = new Date(); + Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); + + HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1); + req1.setHeader("User-Agent", "agent1"); + + HttpResponse resp1 = HttpTestUtils.make200Response(); + resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo)); + resp1.setHeader("Vary", "User-Agent"); + resp1.setHeader("Cache-Control", "max-age=3600"); + resp1.setHeader("ETag", "\"etag1\""); + + + backendExpectsAnyRequest().andReturn(resp1); + + HttpRequest req2 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1); + req2.setHeader("User-Agent", "agent2"); + + HttpResponse resp2 = HttpTestUtils.make200Response(); + resp2.setHeader("Date", DateUtils.formatDate(tenSecondsAgo)); + resp2.setHeader("Vary", "User-Agent"); + resp2.setHeader("Cache-Control", "max-age=3600"); + resp2.setHeader("ETag", "\"etag2\""); + + backendExpectsAnyRequest().andReturn(resp2); + + HttpRequest req3 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1); + req3.setHeader("User-Agent", "agent3"); + + HttpResponse resp3 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_NOT_MODIFIED, "Not Modified"); + resp3.setHeader("Date", DateUtils.formatDate(now)); + resp3.setHeader("ETag", "\"etag1\""); + + backendExpectsAnyRequest().andReturn(resp3); + + HttpRequest req4 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1); + req4.setHeader("User-Agent", "agent1"); + + replayMocks(); + impl.execute(host, req1); + impl.execute(host, req2); + HttpResponse result1 = impl.execute(host, req3); + HttpResponse result2 = impl.execute(host, req4); + verifyMocks(); + + assertEquals(HttpStatus.SC_OK, result1.getStatusLine().getStatusCode()); + assertEquals("\"etag1\"", result1.getFirstHeader("ETag").getValue()); + assertEquals(DateUtils.formatDate(now), result1.getFirstHeader("Date").getValue()); + assertEquals(DateUtils.formatDate(now), result2.getFirstHeader("Date").getValue()); + } }