HTTPCLIENT-975: committed patch to support stale-if-error from
RFC5861, with thanks to Mohammed Azeem Uddin (mohammedazeem_uddin at comcast dot com). git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1049179 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8aef0609de
commit
a91847b57c
|
@ -30,6 +30,7 @@ import java.util.Date;
|
|||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HeaderElement;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.annotation.Immutable;
|
||||
import org.apache.http.client.cache.HeaderConstants;
|
||||
import org.apache.http.client.cache.HttpCacheEntry;
|
||||
|
@ -119,6 +120,35 @@ class CacheValidityPolicy {
|
|||
return hasCacheControlDirective(entry, "proxy-revalidate");
|
||||
}
|
||||
|
||||
public boolean mayReturnStaleIfError(HttpRequest request,
|
||||
HttpCacheEntry entry, Date now) {
|
||||
long stalenessSecs = getStalenessSecs(entry, now);
|
||||
return mayReturnStaleIfError(request.getHeaders("Cache-Control"),
|
||||
stalenessSecs)
|
||||
|| mayReturnStaleIfError(entry.getHeaders("Cache-Control"),
|
||||
stalenessSecs);
|
||||
}
|
||||
|
||||
private boolean mayReturnStaleIfError(Header[] headers, long stalenessSecs) {
|
||||
boolean result = false;
|
||||
for(Header h : headers) {
|
||||
for(HeaderElement elt : h.getElements()) {
|
||||
if ("stale-if-error".equals(elt.getName())) {
|
||||
try {
|
||||
int staleIfErrorSecs = Integer.parseInt(elt.getValue());
|
||||
if (stalenessSecs <= staleIfErrorSecs) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
// skip malformed directive
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Date getDateValue(final HttpCacheEntry entry) {
|
||||
Header dateHdr = entry.getFirstHeader(HTTP.DATE_HEADER);
|
||||
if (dateHdr == null)
|
||||
|
|
|
@ -692,10 +692,24 @@ public class CachingHttpClient implements HttpClient {
|
|||
return responseGenerator.generateResponse(updatedEntry);
|
||||
}
|
||||
|
||||
if (staleIfErrorAppliesTo(statusCode)
|
||||
&& validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
|
||||
final HttpResponse cachedResponse = responseGenerator.generateResponse(cacheEntry);
|
||||
cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
return handleBackendResponse(target, conditionalRequest, requestDate, responseDate,
|
||||
backendResponse);
|
||||
}
|
||||
|
||||
private boolean staleIfErrorAppliesTo(int statusCode) {
|
||||
return statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR
|
||||
|| statusCode == HttpStatus.SC_BAD_GATEWAY
|
||||
|| statusCode == HttpStatus.SC_SERVICE_UNAVAILABLE
|
||||
|| statusCode == HttpStatus.SC_GATEWAY_TIMEOUT;
|
||||
}
|
||||
|
||||
HttpResponse handleBackendResponse(
|
||||
HttpHost target,
|
||||
HttpRequest request,
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.util.Date;
|
|||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HeaderElement;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpMessage;
|
||||
import org.apache.http.HttpRequest;
|
||||
|
@ -43,8 +44,10 @@ import org.apache.http.client.cache.HttpCacheEntry;
|
|||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.impl.cookie.DateUtils;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicHttpRequest;
|
||||
import org.apache.http.message.BasicHttpResponse;
|
||||
import org.apache.http.message.BasicStatusLine;
|
||||
import org.junit.Assert;
|
||||
|
||||
public class HttpTestUtils {
|
||||
|
||||
|
@ -297,4 +300,35 @@ public class HttpTestUtils {
|
|||
out.setEntity(makeBody(128));
|
||||
return out;
|
||||
}
|
||||
|
||||
public static final HttpResponse make200Response(Date date, String cacheControl) {
|
||||
HttpResponse response = HttpTestUtils.make200Response();
|
||||
response.setHeader("Date", DateUtils.formatDate(date));
|
||||
response.setHeader("Cache-Control",cacheControl);
|
||||
response.setHeader("Etag","\"etag\"");
|
||||
return response;
|
||||
}
|
||||
|
||||
public static final void assert110WarningFound(HttpResponse response) {
|
||||
boolean found110Warning = false;
|
||||
for(Header h : response.getHeaders("Warning")) {
|
||||
for(HeaderElement elt : h.getElements()) {
|
||||
String[] parts = elt.getName().split("\\s");
|
||||
if ("110".equals(parts[0])) {
|
||||
found110Warning = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.assertTrue(found110Warning);
|
||||
}
|
||||
|
||||
public static HttpRequest makeDefaultRequest() {
|
||||
return new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
|
||||
}
|
||||
|
||||
public static HttpResponse make500Response() {
|
||||
return new BasicHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpStatus.SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
|
||||
}
|
||||
}
|
|
@ -26,107 +26,101 @@
|
|||
*/
|
||||
package org.apache.http.impl.client.cache;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpVersion;
|
||||
import org.apache.http.client.cache.HttpCacheEntry;
|
||||
import org.apache.http.impl.cookie.DateUtils;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.junit.Assert;
|
||||
import org.apache.http.message.BasicHttpRequest;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestCacheValidityPolicy {
|
||||
|
||||
private CacheValidityPolicy impl;
|
||||
private Date now;
|
||||
private Date oneSecondAgo;
|
||||
private Date sixSecondsAgo;
|
||||
private Date tenSecondsAgo;
|
||||
private Date elevenSecondsAgo;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
impl = new CacheValidityPolicy();
|
||||
now = new Date();
|
||||
oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
|
||||
sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
|
||||
tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
elevenSecondsAgo = new Date(now.getTime() - 11 * 1000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApparentAgeIsMaxIntIfDateHeaderNotPresent() {
|
||||
Header[] headers = {
|
||||
new BasicHeader("Server", "MockServer/1.0")
|
||||
};
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Assert.assertEquals(2147483648L, impl.getApparentAgeSecs(entry));
|
||||
assertEquals(2147483648L, impl.getApparentAgeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApparentAgeIsResponseReceivedTimeLessDateHeader() {
|
||||
Date now = new Date();
|
||||
Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
|
||||
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
|
||||
Header[] headers = new Header[] { new BasicHeader("Date", DateUtils
|
||||
.formatDate(tenSecondsAgo)) };
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, sixSecondsAgo, headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
|
||||
Assert.assertEquals(4, impl.getApparentAgeSecs(entry));
|
||||
assertEquals(4, impl.getApparentAgeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeApparentAgeIsBroughtUpToZero() {
|
||||
Date now = new Date();
|
||||
Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
|
||||
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
|
||||
Header[] headers = new Header[] { new BasicHeader("Date", DateUtils
|
||||
.formatDate(sixSecondsAgo)) };
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now,tenSecondsAgo,headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Assert.assertEquals(0, impl.getApparentAgeSecs(entry));
|
||||
assertEquals(0, impl.getApparentAgeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCorrectedReceivedAgeIsAgeHeaderIfLarger() {
|
||||
Header[] headers = new Header[] { new BasicHeader("Age", "10"), };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy() {
|
||||
|
||||
impl = new CacheValidityPolicy() {
|
||||
@Override
|
||||
protected long getApparentAgeSecs(HttpCacheEntry entry) {
|
||||
return 6;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Assert.assertEquals(10, impl.getCorrectedReceivedAgeSecs(entry));
|
||||
assertEquals(10, impl.getCorrectedReceivedAgeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCorrectedReceivedAgeIsApparentAgeIfLarger() {
|
||||
Header[] headers = new Header[] { new BasicHeader("Age", "6"), };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy() {
|
||||
|
||||
impl = new CacheValidityPolicy() {
|
||||
@Override
|
||||
protected long getApparentAgeSecs(HttpCacheEntry entry) {
|
||||
return 10;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Assert.assertEquals(10, impl.getCorrectedReceivedAgeSecs(entry));
|
||||
assertEquals(10, impl.getCorrectedReceivedAgeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseDelayIsDifferenceBetweenResponseAndRequestTimes() {
|
||||
Date now = new Date();
|
||||
Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
|
||||
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, sixSecondsAgo);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
|
||||
Assert.assertEquals(4, impl.getResponseDelaySecs(entry));
|
||||
assertEquals(4, impl.getResponseDelaySecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCorrectedInitialAgeIsCorrectedReceivedAgePlusResponseDelay() {
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy() {
|
||||
|
||||
impl = new CacheValidityPolicy() {
|
||||
@Override
|
||||
protected long getCorrectedReceivedAgeSecs(HttpCacheEntry entry) {
|
||||
return 7;
|
||||
|
@ -136,62 +130,50 @@ public class TestCacheValidityPolicy {
|
|||
protected long getResponseDelaySecs(HttpCacheEntry entry) {
|
||||
return 13;
|
||||
}
|
||||
|
||||
};
|
||||
Assert.assertEquals(20, impl.getCorrectedInitialAgeSecs(entry));
|
||||
assertEquals(20, impl.getCorrectedInitialAgeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResidentTimeSecondsIsTimeSinceResponseTime() {
|
||||
final Date now = new Date();
|
||||
final Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, sixSecondsAgo);
|
||||
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy() {
|
||||
|
||||
impl = new CacheValidityPolicy() {
|
||||
@Override
|
||||
protected Date getCurrentDate() {
|
||||
return now;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Assert.assertEquals(6, impl.getResidentTimeSecs(entry, now));
|
||||
assertEquals(6, impl.getResidentTimeSecs(entry, now));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrentAgeIsCorrectedInitialAgePlusResidentTime() {
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy() {
|
||||
|
||||
impl = new CacheValidityPolicy() {
|
||||
@Override
|
||||
protected long getCorrectedInitialAgeSecs(HttpCacheEntry entry) {
|
||||
return 11;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getResidentTimeSecs(HttpCacheEntry entry, Date d) {
|
||||
return 17;
|
||||
}
|
||||
};
|
||||
Assert.assertEquals(28, impl.getCurrentAgeSecs(entry, new Date()));
|
||||
assertEquals(28, impl.getCurrentAgeSecs(entry, new Date()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFreshnessLifetimeIsSMaxAgeIfPresent() {
|
||||
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "s-maxage=10") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Assert.assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFreshnessLifetimeIsMaxAgeIfPresent() {
|
||||
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Assert.assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -199,312 +181,269 @@ public class TestCacheValidityPolicy {
|
|||
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10"),
|
||||
new BasicHeader("Cache-Control", "s-maxage=20") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Assert.assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
|
||||
headers = new Header[] { new BasicHeader("Cache-Control", "max-age=20"),
|
||||
new BasicHeader("Cache-Control", "s-maxage=10") };
|
||||
entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
Assert.assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFreshnessLifetimeIsMaxAgeEvenIfExpiresIsPresent() {
|
||||
Date now = new Date();
|
||||
Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
|
||||
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10"),
|
||||
new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
|
||||
new BasicHeader("Expires", DateUtils.formatDate(sixSecondsAgo)) };
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Assert.assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFreshnessLifetimeIsSMaxAgeEvenIfExpiresIsPresent() {
|
||||
Date now = new Date();
|
||||
Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
|
||||
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "s-maxage=10"),
|
||||
new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
|
||||
new BasicHeader("Expires", DateUtils.formatDate(sixSecondsAgo)) };
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Assert.assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFreshnessLifetimeIsFromExpiresHeaderIfNoMaxAge() {
|
||||
Date now = new Date();
|
||||
Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
|
||||
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
Header[] headers = new Header[] {
|
||||
new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
|
||||
new BasicHeader("Expires", DateUtils.formatDate(sixSecondsAgo)) };
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Assert.assertEquals(4, impl.getFreshnessLifetimeSecs(entry));
|
||||
assertEquals(4, impl.getFreshnessLifetimeSecs(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeuristicFreshnessLifetime() {
|
||||
Date now = new Date();
|
||||
Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
|
||||
Date elevenSecondsAgo = new Date(now.getTime() - 11 * 1000L);
|
||||
|
||||
Header[] headers = new Header[] {
|
||||
new BasicHeader("Date", DateUtils.formatDate(oneSecondAgo)),
|
||||
new BasicHeader("Last-Modified", DateUtils.formatDate(elevenSecondsAgo))
|
||||
};
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Assert.assertEquals(1, impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, 0));
|
||||
assertEquals(1, impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeuristicFreshnessLifetimeDefaultsProperly() {
|
||||
long defaultFreshness = 10;
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
|
||||
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Assert.assertEquals(defaultFreshness, impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, defaultFreshness));
|
||||
assertEquals(defaultFreshness, impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, defaultFreshness));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeuristicFreshnessLifetimeIsNonNegative() {
|
||||
Date now = new Date();
|
||||
Date oneSecondAgo = new Date(now.getTime() - 1 * 1000L);
|
||||
Date elevenSecondsAgo = new Date(now.getTime() - 1 * 1000L);
|
||||
|
||||
Header[] headers = new Header[] {
|
||||
new BasicHeader("Date", DateUtils.formatDate(elevenSecondsAgo)),
|
||||
new BasicHeader("Last-Modified", DateUtils.formatDate(oneSecondAgo))
|
||||
};
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Assert.assertTrue(impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, 10) >= 0);
|
||||
assertTrue(impl.getHeuristicFreshnessLifetimeSecs(entry, 0.1f, 10) >= 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseIsFreshIfFreshnessLifetimeExceedsCurrentAge() {
|
||||
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
|
||||
final Date now = new Date();
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy() {
|
||||
|
||||
impl = new CacheValidityPolicy() {
|
||||
@Override
|
||||
public long getCurrentAgeSecs(HttpCacheEntry e, Date d) {
|
||||
Assert.assertSame(entry, e);
|
||||
Assert.assertEquals(now, d);
|
||||
assertSame(entry, e);
|
||||
assertEquals(now, d);
|
||||
return 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFreshnessLifetimeSecs(HttpCacheEntry e) {
|
||||
Assert.assertSame(entry, e);
|
||||
assertSame(entry, e);
|
||||
return 10;
|
||||
}
|
||||
};
|
||||
|
||||
Assert.assertTrue(impl.isResponseFresh(entry, now));
|
||||
assertTrue(impl.isResponseFresh(entry, now));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseIsNotFreshIfFreshnessLifetimeEqualsCurrentAge() {
|
||||
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
|
||||
final Date now = new Date();
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy() {
|
||||
|
||||
impl = new CacheValidityPolicy() {
|
||||
@Override
|
||||
public long getCurrentAgeSecs(HttpCacheEntry e, Date d) {
|
||||
Assert.assertEquals(now, d);
|
||||
Assert.assertSame(entry, e);
|
||||
assertEquals(now, d);
|
||||
assertSame(entry, e);
|
||||
return 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFreshnessLifetimeSecs(HttpCacheEntry e) {
|
||||
Assert.assertSame(entry, e);
|
||||
assertSame(entry, e);
|
||||
return 6;
|
||||
}
|
||||
};
|
||||
|
||||
Assert.assertFalse(impl.isResponseFresh(entry, now));
|
||||
assertFalse(impl.isResponseFresh(entry, now));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseIsNotFreshIfCurrentAgeExceedsFreshnessLifetime() {
|
||||
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
|
||||
final Date now = new Date();
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy() {
|
||||
|
||||
impl = new CacheValidityPolicy() {
|
||||
@Override
|
||||
public long getCurrentAgeSecs(HttpCacheEntry e, Date d) {
|
||||
Assert.assertEquals(now, d);
|
||||
Assert.assertSame(entry, e);
|
||||
assertEquals(now, d);
|
||||
assertSame(entry, e);
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFreshnessLifetimeSecs(HttpCacheEntry e) {
|
||||
Assert.assertSame(entry, e);
|
||||
assertSame(entry, e);
|
||||
return 6;
|
||||
}
|
||||
};
|
||||
|
||||
Assert.assertFalse(impl.isResponseFresh(entry, now));
|
||||
assertFalse(impl.isResponseFresh(entry, now));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheEntryIsRevalidatableIfHeadersIncludeETag() {
|
||||
|
||||
Header[] headers = {
|
||||
new BasicHeader("Expires", DateUtils.formatDate(new Date())),
|
||||
new BasicHeader("ETag", "somevalue")};
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
|
||||
Assert.assertTrue(impl.isRevalidatable(entry));
|
||||
assertTrue(impl.isRevalidatable(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheEntryIsRevalidatableIfHeadersIncludeLastModifiedDate() {
|
||||
|
||||
Header[] headers = {
|
||||
new BasicHeader("Expires", DateUtils.formatDate(new Date())),
|
||||
new BasicHeader("Last-Modified", DateUtils.formatDate(new Date())) };
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
|
||||
Assert.assertTrue(impl.isRevalidatable(entry));
|
||||
assertTrue(impl.isRevalidatable(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheEntryIsNotRevalidatableIfNoAppropriateHeaders() {
|
||||
|
||||
Header[] headers = {
|
||||
new BasicHeader("Expires", DateUtils.formatDate(new Date())),
|
||||
new BasicHeader("Cache-Control", "public") };
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
|
||||
Assert.assertFalse(impl.isRevalidatable(entry));
|
||||
assertFalse(impl.isRevalidatable(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMalformedDateHeaderIsIgnored() {
|
||||
|
||||
Header[] headers = new Header[] { new BasicHeader("Date", "asdf") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Date d = impl.getDateValue(entry);
|
||||
|
||||
Assert.assertNull(d);
|
||||
assertNull(impl.getDateValue(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMalformedContentLengthReturnsNegativeOne() {
|
||||
|
||||
Header[] headers = new Header[] { new BasicHeader("Content-Length", "asdf") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
long length = impl.getContentLengthValue(entry);
|
||||
|
||||
Assert.assertEquals(-1, length);
|
||||
assertEquals(-1, impl.getContentLengthValue(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeAgeHeaderValueReturnsMaxAge() {
|
||||
|
||||
Header[] headers = new Header[] { new BasicHeader("Age", "-100") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
long length = impl.getAgeValue(entry);
|
||||
|
||||
Assert.assertEquals(CacheValidityPolicy.MAX_AGE, length);
|
||||
assertEquals(CacheValidityPolicy.MAX_AGE, impl.getAgeValue(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMalformedAgeHeaderValueReturnsMaxAge() {
|
||||
|
||||
Header[] headers = new Header[] { new BasicHeader("Age", "asdf") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
long length = impl.getAgeValue(entry);
|
||||
|
||||
Assert.assertEquals(CacheValidityPolicy.MAX_AGE, length);
|
||||
assertEquals(CacheValidityPolicy.MAX_AGE, impl.getAgeValue(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMalformedCacheControlMaxAgeHeaderReturnsZero() {
|
||||
|
||||
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=asdf") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
long maxage = impl.getMaxAge(entry);
|
||||
|
||||
Assert.assertEquals(0, maxage);
|
||||
assertEquals(0, impl.getMaxAge(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMalformedExpirationDateReturnsNull() {
|
||||
Header[] headers = new Header[] { new BasicHeader("Expires", "asdf") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
Date expirationDate = impl.getExpirationDate(entry);
|
||||
|
||||
Assert.assertNull(expirationDate);
|
||||
assertNull(impl.getExpirationDate(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMustRevalidateIsFalseIfDirectiveNotPresent() {
|
||||
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
|
||||
Assert.assertFalse(impl.mustRevalidate(entry));
|
||||
assertFalse(impl.mustRevalidate(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMustRevalidateIsTrueWhenDirectiveIsPresent() {
|
||||
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public, must-revalidate") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
|
||||
Assert.assertTrue(impl.mustRevalidate(entry));
|
||||
assertTrue(impl.mustRevalidate(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProxyRevalidateIsFalseIfDirectiveNotPresent() {
|
||||
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
|
||||
Assert.assertFalse(impl.proxyRevalidate(entry));
|
||||
assertFalse(impl.proxyRevalidate(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProxyRevalidateIsTrueWhenDirectiveIsPresent() {
|
||||
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public, proxy-revalidate") };
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
|
||||
CacheValidityPolicy impl = new CacheValidityPolicy();
|
||||
|
||||
Assert.assertTrue(impl.proxyRevalidate(entry));
|
||||
assertTrue(impl.proxyRevalidate(entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMayReturnStaleIfErrorInResponseIsTrueWithinStaleness(){
|
||||
Header[] headers = new Header[] {
|
||||
new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
|
||||
new BasicHeader("Cache-Control", "max-age=5, stale-if-error=15")
|
||||
};
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
|
||||
HttpRequest req = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
|
||||
assertTrue(impl.mayReturnStaleIfError(req, entry, now));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMayReturnStaleIfErrorInRequestIsTrueWithinStaleness(){
|
||||
Header[] headers = new Header[] {
|
||||
new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
|
||||
new BasicHeader("Cache-Control", "max-age=5")
|
||||
};
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
|
||||
HttpRequest req = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
|
||||
req.setHeader("Cache-Control","stale-if-error=15");
|
||||
assertTrue(impl.mayReturnStaleIfError(req, entry, now));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMayNotReturnStaleIfErrorInResponseAndAfterResponseWindow(){
|
||||
Header[] headers = new Header[] {
|
||||
new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
|
||||
new BasicHeader("Cache-Control", "max-age=5, stale-if-error=1")
|
||||
};
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
|
||||
HttpRequest req = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
|
||||
assertFalse(impl.mayReturnStaleIfError(req, entry, now));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMayNotReturnStaleIfErrorInResponseAndAfterRequestWindow(){
|
||||
Header[] headers = new Header[] {
|
||||
new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
|
||||
new BasicHeader("Cache-Control", "max-age=5")
|
||||
};
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
|
||||
HttpRequest req = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
|
||||
req.setHeader("Cache-Control","stale-if-error=1");
|
||||
assertFalse(impl.mayReturnStaleIfError(req, entry, now));
|
||||
}
|
||||
}
|
||||
|
|
157
httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRFC5861Compliance.java
vendored
Normal file
157
httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRFC5861Compliance.java
vendored
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
package org.apache.http.impl.client.cache;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* A suite of acceptance tests for compliance with RFC5861, which
|
||||
* describes the stale-if-error and stale-while-revalidate
|
||||
* Cache-Control extensions.
|
||||
*/
|
||||
public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||
|
||||
/*
|
||||
* "The stale-if-error Cache-Control extension indicates that when an
|
||||
* error is encountered, a cached stale response MAY be used to satisfy
|
||||
* the request, regardless of other freshness information.When used as a
|
||||
* request Cache-Control extension, its scope of application is the request
|
||||
* it appears in; when used as a response Cache-Control extension, its
|
||||
* scope is any request applicable to the cached response in which it
|
||||
* occurs.Its value indicates the upper limit to staleness; when the cached
|
||||
* response is more stale than the indicated amount, the cached response
|
||||
* SHOULD NOT be used to satisfy the request, absent other information.
|
||||
* In this context, an error is any situation that would result in a
|
||||
* 500, 502, 503, or 504 HTTP response status code being returned."
|
||||
*
|
||||
* http://tools.ietf.org/html/rfc5861
|
||||
*/
|
||||
@Test
|
||||
public void testStaleIfErrorInResponseIsTrueReturnsStaleEntryWithWarning()
|
||||
throws Exception{
|
||||
Date tenSecondsAgo = new Date(new Date().getTime() - 10 * 1000L);
|
||||
HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
|
||||
HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||
"public, max-age=5, stale-if-error=60");
|
||||
|
||||
backendExpectsAnyRequest().andReturn(resp1);
|
||||
|
||||
HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||
HttpResponse resp2 = HttpTestUtils.make500Response();
|
||||
|
||||
backendExpectsAnyRequest().andReturn(resp2);
|
||||
|
||||
replayMocks();
|
||||
impl.execute(host,req1);
|
||||
HttpResponse result = impl.execute(host,req2);
|
||||
verifyMocks();
|
||||
|
||||
HttpTestUtils.assert110WarningFound(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaleIfErrorInRequestIsTrueReturnsStaleEntryWithWarning()
|
||||
throws Exception{
|
||||
Date tenSecondsAgo = new Date(new Date().getTime() - 10 * 1000L);
|
||||
HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
|
||||
HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||
"public, max-age=5");
|
||||
|
||||
backendExpectsAnyRequest().andReturn(resp1);
|
||||
|
||||
HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||
req2.setHeader("Cache-Control","public, max-age=5, stale-if-error=60");
|
||||
HttpResponse resp2 = HttpTestUtils.make500Response();
|
||||
|
||||
backendExpectsAnyRequest().andReturn(resp2);
|
||||
|
||||
replayMocks();
|
||||
impl.execute(host,req1);
|
||||
HttpResponse result = impl.execute(host,req2);
|
||||
verifyMocks();
|
||||
|
||||
HttpTestUtils.assert110WarningFound(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaleIfErrorInResponseIsFalseReturnsError()
|
||||
throws Exception{
|
||||
Date now = new Date();
|
||||
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
|
||||
HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||
"public, max-age=5, stale-if-error=2");
|
||||
|
||||
backendExpectsAnyRequest().andReturn(resp1);
|
||||
|
||||
HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||
HttpResponse resp2 = HttpTestUtils.make500Response();
|
||||
|
||||
backendExpectsAnyRequest().andReturn(resp2);
|
||||
|
||||
replayMocks();
|
||||
impl.execute(host,req1);
|
||||
HttpResponse result = impl.execute(host,req2);
|
||||
verifyMocks();
|
||||
|
||||
assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR,
|
||||
result.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaleIfErrorInRequestIsFalseReturnsError()
|
||||
throws Exception{
|
||||
Date now = new Date();
|
||||
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
|
||||
HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
|
||||
"public, max-age=5");
|
||||
|
||||
backendExpectsAnyRequest().andReturn(resp1);
|
||||
|
||||
HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
|
||||
req2.setHeader("Cache-Control","stale-if-error=2");
|
||||
HttpResponse resp2 = HttpTestUtils.make500Response();
|
||||
|
||||
backendExpectsAnyRequest().andReturn(resp2);
|
||||
|
||||
replayMocks();
|
||||
impl.execute(host,req1);
|
||||
HttpResponse result = impl.execute(host,req2);
|
||||
verifyMocks();
|
||||
|
||||
assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR,
|
||||
result.getStatusLine().getStatusCode());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue