### Eclipse Workspace Patch 1.0 #P httpcomponents-client Index: httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ConditionalRequestBuilder.java =================================================================== --- httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ConditionalRequestBuilder.java (revision 999434) +++ httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ConditionalRequestBuilder.java (working copy) @@ -58,8 +58,9 @@ Header eTag = cacheEntry.getFirstHeader(HeaderConstants.ETAG); if (eTag != null) { wrapperRequest.setHeader(HeaderConstants.IF_NONE_MATCH, eTag.getValue()); - } else { - Header lastModified = cacheEntry.getFirstHeader(HeaderConstants.LAST_MODIFIED); + } + Header lastModified = cacheEntry.getFirstHeader(HeaderConstants.LAST_MODIFIED); + if (lastModified != null) { wrapperRequest.setHeader(HeaderConstants.IF_MODIFIED_SINCE, lastModified.getValue()); } boolean mustRevalidate = false; Index: httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java =================================================================== --- httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java (revision 999434) +++ httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java (working copy) @@ -39,6 +39,9 @@ import org.apache.http.HttpVersion; import org.apache.http.impl.cookie.DateUtils; import org.apache.http.message.BasicHttpRequest; +import org.apache.http.protocol.HttpContext; +import org.easymock.Capture; +import org.easymock.EasyMock; import org.junit.Test; /* @@ -299,4 +302,98 @@ assertEquals(warning, result.getFirstHeader("Warning").getValue()); } + + /* + * "[HTTP/1.1 clients], If only a Last-Modified value has been provided + * by the origin server, SHOULD use that value in non-subrange cache- + * conditional requests (using If-Modified-Since)." + * + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.4 + */ + @Test + public void testUsesLastModifiedDateForCacheConditionalRequests() + throws Exception { + Date now = new Date(); + Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); + Date twentySecondsAgo = new Date(now.getTime() - 20 * 1000L); + final String lmDate = DateUtils.formatDate(twentySecondsAgo); + + HttpRequest req1 = + new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1); + HttpResponse resp1 = make200Response(); + resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo)); + resp1.setHeader("Last-Modified", lmDate); + resp1.setHeader("Cache-Control","max-age=5"); + + backendExpectsAnyRequest().andReturn(resp1); + + Capture cap = new Capture(); + HttpRequest req2 = + new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1); + HttpResponse resp2 = make200Response(); + + EasyMock.expect(mockBackend.execute(EasyMock.same(host), + EasyMock.capture(cap), (HttpContext)EasyMock.isNull())) + .andReturn(resp2); + + replayMocks(); + impl.execute(host, req1); + impl.execute(host, req2); + verifyMocks(); + + HttpRequest captured = cap.getValue(); + Header ifModifiedSince = + captured.getFirstHeader("If-Modified-Since"); + assertEquals(lmDate, ifModifiedSince.getValue()); + } + + /* + * "[HTTP/1.1 clients], if both an entity tag and a Last-Modified value + * have been provided by the origin server, SHOULD use both validators + * in cache-conditional requests. This allows both HTTP/1.0 and + * HTTP/1.1 caches to respond appropriately." + * + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.4 + */ + @Test + public void testUsesBothLastModifiedAndETagForConditionalRequestsIfAvailable() + throws Exception { + Date now = new Date(); + Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); + Date twentySecondsAgo = new Date(now.getTime() - 20 * 1000L); + final String lmDate = DateUtils.formatDate(twentySecondsAgo); + final String etag = "\"etag\""; + + HttpRequest req1 = + new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1); + HttpResponse resp1 = make200Response(); + resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo)); + resp1.setHeader("Last-Modified", lmDate); + resp1.setHeader("Cache-Control","max-age=5"); + resp1.setHeader("ETag", etag); + + backendExpectsAnyRequest().andReturn(resp1); + + Capture cap = new Capture(); + HttpRequest req2 = + new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1); + HttpResponse resp2 = make200Response(); + + EasyMock.expect(mockBackend.execute(EasyMock.same(host), + EasyMock.capture(cap), (HttpContext)EasyMock.isNull())) + .andReturn(resp2); + + replayMocks(); + impl.execute(host, req1); + impl.execute(host, req2); + verifyMocks(); + + HttpRequest captured = cap.getValue(); + Header ifModifiedSince = + captured.getFirstHeader("If-Modified-Since"); + assertEquals(lmDate, ifModifiedSince.getValue()); + Header ifNoneMatch = + captured.getFirstHeader("If-None-Match"); + assertEquals(etag, ifNoneMatch.getValue()); + } } Index: httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestConditionalRequestBuilder.java =================================================================== --- httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestConditionalRequestBuilder.java (revision 999434) +++ httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestConditionalRequestBuilder.java (working copy) @@ -80,6 +80,28 @@ Assert.assertEquals("If-Modified-Since", newRequest.getAllHeaders()[1].getName()); Assert.assertEquals(lastModified, newRequest.getAllHeaders()[1].getValue()); } + + @Test + public void testConditionalRequestForEntryWithLastModifiedAndEtagIncludesBothAsValidators() + throws Exception { + Date now = new Date(); + Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); + Date twentySecondsAgo = new Date(now.getTime() - 20 * 1000L); + final String lmDate = DateUtils.formatDate(twentySecondsAgo); + final String etag = "\"etag\""; + Header[] headers = { + new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)), + new BasicHeader("Last-Modified", lmDate), + new BasicHeader("ETag", etag) + }; + HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1); + HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); + HttpRequest result = impl.buildConditionalRequest(request, entry); + Assert.assertEquals(lmDate, + result.getFirstHeader("If-Modified-Since").getValue()); + Assert.assertEquals(etag, + result.getFirstHeader("If-None-Match").getValue()); + } @Test public void testBuildConditionalRequestWithETag() throws ProtocolException { @@ -106,7 +128,7 @@ Assert.assertEquals(request.getRequestLine().getProtocolVersion(), newRequest .getRequestLine().getProtocolVersion()); - Assert.assertEquals(2, newRequest.getAllHeaders().length); + Assert.assertEquals(3, newRequest.getAllHeaders().length); Assert.assertEquals("Accept-Encoding", newRequest.getAllHeaders()[0].getName()); Assert.assertEquals("gzip", newRequest.getAllHeaders()[0].getValue());