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:
parent
e82184af36
commit
6d41d97eb9
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue