HTTPCLIENT-998: cache should use both Last-Modified and ETag for validations when available

Contributed by Jonathan Moore <jonathan_moore at comcast.com> 


git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1000023 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2010-09-22 15:06:53 +00:00
parent e82184af36
commit 6d41d97eb9
3 changed files with 121 additions and 3 deletions

View File

@ -58,8 +58,9 @@ class ConditionalRequestBuilder {
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;

View File

@ -85,6 +85,28 @@ public class TestConditionalRequestBuilder {
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 {
String theMethod = "GET";
@ -110,7 +132,7 @@ public class TestConditionalRequestBuilder {
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());

View File

@ -44,6 +44,7 @@ import org.apache.http.message.BasicHttpRequest;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.protocol.HttpContext;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.junit.Test;
/*
@ -305,6 +306,100 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
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 = HttpTestUtils.make200Response();
resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
resp1.setHeader("Last-Modified", lmDate);
resp1.setHeader("Cache-Control","max-age=5");
backendExpectsAnyRequest().andReturn(resp1);
Capture<HttpRequest> cap = new Capture<HttpRequest>();
HttpRequest req2 =
new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
HttpResponse resp2 = HttpTestUtils.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 = HttpTestUtils.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<HttpRequest> cap = new Capture<HttpRequest>();
HttpRequest req2 =
new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
HttpResponse resp2 = HttpTestUtils.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());
}
/*
* "If an origin server wishes to force a semantically transparent cache
* to validate every request, it MAY assign an explicit expiration time