HTTPCLIENT-1035: begin adding functionality for flushing updated
entries mentioned by Content-Location in responses. Not yet hooked in to be used. git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1051865 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
7bd182d481
commit
68ed95699e
|
@ -29,16 +29,20 @@ package org.apache.http.impl.client.cache;
|
|||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.annotation.ThreadSafe;
|
||||
import org.apache.http.client.cache.HeaderConstants;
|
||||
import org.apache.http.client.cache.HttpCacheEntry;
|
||||
import org.apache.http.client.cache.HttpCacheStorage;
|
||||
import org.apache.http.impl.cookie.DateParseException;
|
||||
import org.apache.http.impl.cookie.DateUtils;
|
||||
|
||||
/**
|
||||
* Given a particular HttpRequest, flush any cache entries that this request
|
||||
|
@ -150,4 +154,45 @@ class CacheInvalidator {
|
|||
return !(HeaderConstants.GET_METHOD.equals(method) || HeaderConstants.HEAD_METHOD
|
||||
.equals(method));
|
||||
}
|
||||
|
||||
/** Flushes entries that were invalidated by the given response
|
||||
* received for the given host/request pair.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void flushInvalidatedCacheEntries(HttpHost host,
|
||||
HttpRequest request, HttpResponse response) throws IOException {
|
||||
Header contentLocation = response.getFirstHeader("Content-Location");
|
||||
if (contentLocation == null) return;
|
||||
HttpCacheEntry entry = storage.getEntry(contentLocation.getValue());
|
||||
if (entry == null) return;
|
||||
|
||||
if (!responseDateNewerThanEntryDate(response, entry)) return;
|
||||
if (!responseAndEntryEtagsDiffer(response, entry)) return;
|
||||
|
||||
storage.removeEntry(contentLocation.getValue());
|
||||
}
|
||||
|
||||
private boolean responseAndEntryEtagsDiffer(HttpResponse response,
|
||||
HttpCacheEntry entry) {
|
||||
Header entryEtag = entry.getFirstHeader("ETag");
|
||||
Header responseEtag = response.getFirstHeader("ETag");
|
||||
if (entryEtag == null || responseEtag == null) return false;
|
||||
return (!entryEtag.getValue().equals(responseEtag.getValue()));
|
||||
}
|
||||
|
||||
private boolean responseDateNewerThanEntryDate(HttpResponse response,
|
||||
HttpCacheEntry entry) {
|
||||
Header entryDateHeader = entry.getFirstHeader("Date");
|
||||
Header responseDateHeader = response.getFirstHeader("Date");
|
||||
if (entryDateHeader == null || responseDateHeader == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
|
||||
Date responseDate = DateUtils.parseDate(responseDateHeader.getValue());
|
||||
return responseDate.after(entryDate);
|
||||
} catch (DateParseException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,17 +27,24 @@
|
|||
package org.apache.http.impl.client.cache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.ProtocolVersion;
|
||||
import org.apache.http.client.cache.HttpCacheEntry;
|
||||
import org.apache.http.client.cache.HttpCacheStorage;
|
||||
import static org.apache.http.impl.cookie.DateUtils.formatDate;
|
||||
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
|
||||
import org.apache.http.message.BasicHttpRequest;
|
||||
import org.easymock.classextension.EasyMock;
|
||||
import static org.easymock.classextension.EasyMock.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -48,34 +55,42 @@ public class TestCacheInvalidator {
|
|||
private CacheInvalidator impl;
|
||||
private HttpCacheStorage mockStorage;
|
||||
private HttpHost host;
|
||||
private CacheKeyGenerator extractor;
|
||||
private CacheKeyGenerator cacheKeyGenerator;
|
||||
private HttpCacheEntry mockEntry;
|
||||
private HttpRequest request;
|
||||
private HttpResponse response;
|
||||
|
||||
private Date now;
|
||||
private Date tenSecondsAgo;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
now = new Date();
|
||||
tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
|
||||
|
||||
host = new HttpHost("foo.example.com");
|
||||
mockStorage = EasyMock.createMock(HttpCacheStorage.class);
|
||||
extractor = new CacheKeyGenerator();
|
||||
mockEntry = EasyMock.createMock(HttpCacheEntry.class);
|
||||
mockStorage = createMock(HttpCacheStorage.class);
|
||||
cacheKeyGenerator = new CacheKeyGenerator();
|
||||
mockEntry = createMock(HttpCacheEntry.class);
|
||||
response = HttpTestUtils.make200Response();
|
||||
|
||||
impl = new CacheInvalidator(extractor, mockStorage);
|
||||
impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
|
||||
}
|
||||
|
||||
private void replayMocks() {
|
||||
EasyMock.replay(mockStorage);
|
||||
EasyMock.replay(mockEntry);
|
||||
replay(mockStorage);
|
||||
replay(mockEntry);
|
||||
}
|
||||
|
||||
private void verifyMocks() {
|
||||
EasyMock.verify(mockStorage);
|
||||
EasyMock.verify(mockEntry);
|
||||
verify(mockStorage);
|
||||
verify(mockEntry);
|
||||
}
|
||||
|
||||
// Tests
|
||||
@Test
|
||||
public void testInvalidatesRequestsThatArentGETorHEAD() throws Exception {
|
||||
HttpRequest request = new BasicHttpRequest("POST","/path", HTTP_1_1);
|
||||
|
||||
request = new BasicHttpRequest("POST","/path", HTTP_1_1);
|
||||
final String theUri = "http://foo.example.com:80/path";
|
||||
Map<String,String> variantMap = new HashMap<String,String>();
|
||||
cacheEntryHasVariantMap(variantMap);
|
||||
|
@ -126,7 +141,7 @@ public class TestCacheInvalidator {
|
|||
|
||||
cacheReturnsEntryForUri(theUri);
|
||||
entryIsRemoved(theUri);
|
||||
entryIsRemoved(extractor.canonicalizeUri(contentLocation));
|
||||
entryIsRemoved(cacheKeyGenerator.canonicalizeUri(contentLocation));
|
||||
|
||||
replayMocks();
|
||||
|
||||
|
@ -182,54 +197,41 @@ public class TestCacheInvalidator {
|
|||
|
||||
@Test
|
||||
public void testDoesNotInvalidateGETRequest() throws Exception {
|
||||
HttpRequest request = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
|
||||
request = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
replayMocks();
|
||||
|
||||
impl.flushInvalidatedCacheEntries(host, request);
|
||||
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotInvalidateHEADRequest() throws Exception {
|
||||
HttpRequest request = new BasicHttpRequest("HEAD","/",HTTP_1_1);
|
||||
|
||||
request = new BasicHttpRequest("HEAD","/",HTTP_1_1);
|
||||
replayMocks();
|
||||
|
||||
impl.flushInvalidatedCacheEntries(host, request);
|
||||
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotInvalidateRequestsWithClientCacheControlHeaders() throws Exception {
|
||||
HttpRequest request = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
request = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
request.setHeader("Cache-Control","no-cache");
|
||||
|
||||
replayMocks();
|
||||
|
||||
impl.flushInvalidatedCacheEntries(host, request);
|
||||
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotInvalidateRequestsWithClientPragmaHeaders() throws Exception {
|
||||
HttpRequest request = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
request = new BasicHttpRequest("GET","/",HTTP_1_1);
|
||||
request.setHeader("Pragma","no-cache");
|
||||
|
||||
replayMocks();
|
||||
|
||||
impl.flushInvalidatedCacheEntries(host, request);
|
||||
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVariantURIsAreFlushedAlso() throws Exception {
|
||||
HttpRequest request = new BasicHttpRequest("POST","/",HTTP_1_1);
|
||||
|
||||
request = new BasicHttpRequest("POST","/",HTTP_1_1);
|
||||
final String theUri = "http://foo.example.com:80/";
|
||||
final String variantUri = "theVariantURI";
|
||||
|
||||
|
@ -249,7 +251,7 @@ public class TestCacheInvalidator {
|
|||
|
||||
@Test(expected=IOException.class)
|
||||
public void testCacheFlushException() throws Exception {
|
||||
HttpRequest request = new BasicHttpRequest("POST","/",HTTP_1_1);
|
||||
request = new BasicHttpRequest("POST","/",HTTP_1_1);
|
||||
String theURI = "http://foo.example.com:80/";
|
||||
|
||||
cacheReturnsExceptionForUri(theURI);
|
||||
|
@ -257,18 +259,182 @@ public class TestCacheInvalidator {
|
|||
replayMocks();
|
||||
impl.flushInvalidatedCacheEntries(host, request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotFlushForResponsesWithoutContentLocation()
|
||||
throws Exception {
|
||||
request = HttpTestUtils.makeDefaultRequest();
|
||||
replayMocks();
|
||||
impl.flushInvalidatedCacheEntries(host, request, response);
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flushesEntryIfFresherAndSpecifiedByContentLocation()
|
||||
throws Exception {
|
||||
response.setHeader("ETag","\"new-etag\"");
|
||||
response.setHeader("Date", formatDate(now));
|
||||
String theURI = "http://foo.example.com:80/bar";
|
||||
response.setHeader("Content-Location", theURI);
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
|
||||
new BasicHeader("Date", formatDate(tenSecondsAgo)),
|
||||
new BasicHeader("ETag", "\"old-etag\"")
|
||||
});
|
||||
|
||||
expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
|
||||
mockStorage.removeEntry(theURI);
|
||||
|
||||
replayMocks();
|
||||
impl.flushInvalidatedCacheEntries(host, request, response);
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotFlushEntrySpecifiedByContentLocationIfEtagsMatch()
|
||||
throws Exception {
|
||||
response.setHeader("ETag","\"same-etag\"");
|
||||
response.setHeader("Date", formatDate(now));
|
||||
String theURI = "http://foo.example.com:80/bar";
|
||||
response.setHeader("Content-Location", theURI);
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
|
||||
new BasicHeader("Date", formatDate(tenSecondsAgo)),
|
||||
new BasicHeader("ETag", "\"same-etag\"")
|
||||
});
|
||||
|
||||
expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
|
||||
|
||||
replayMocks();
|
||||
impl.flushInvalidatedCacheEntries(host, request, response);
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotFlushEntrySpecifiedByContentLocationIfNotNewer()
|
||||
throws Exception {
|
||||
response.setHeader("ETag","\"new-etag\"");
|
||||
response.setHeader("Date", formatDate(now));
|
||||
String theURI = "http://foo.example.com:80/bar";
|
||||
response.setHeader("Content-Location", theURI);
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
|
||||
new BasicHeader("Date", formatDate(now)),
|
||||
new BasicHeader("ETag", "\"old-etag\"")
|
||||
});
|
||||
|
||||
expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
|
||||
|
||||
replayMocks();
|
||||
impl.flushInvalidatedCacheEntries(host, request, response);
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotFlushEntryIfNotInCache()
|
||||
throws Exception {
|
||||
response.setHeader("ETag","\"new-etag\"");
|
||||
response.setHeader("Date", formatDate(now));
|
||||
String theURI = "http://foo.example.com:80/bar";
|
||||
response.setHeader("Content-Location", theURI);
|
||||
|
||||
expect(mockStorage.getEntry(theURI)).andReturn(null).anyTimes();
|
||||
|
||||
replayMocks();
|
||||
impl.flushInvalidatedCacheEntries(host, request, response);
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotFlushEntrySpecifiedByContentLocationIfResponseHasNoEtag()
|
||||
throws Exception {
|
||||
response.removeHeaders("ETag");
|
||||
response.setHeader("Date", formatDate(now));
|
||||
String theURI = "http://foo.example.com:80/bar";
|
||||
response.setHeader("Content-Location", theURI);
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
|
||||
new BasicHeader("Date", formatDate(tenSecondsAgo)),
|
||||
new BasicHeader("ETag", "\"old-etag\"")
|
||||
});
|
||||
|
||||
expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
|
||||
|
||||
replayMocks();
|
||||
impl.flushInvalidatedCacheEntries(host, request, response);
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotFlushEntrySpecifiedByContentLocationIfEntryHasNoEtag()
|
||||
throws Exception {
|
||||
response.setHeader("ETag", "\"some-etag\"");
|
||||
response.setHeader("Date", formatDate(now));
|
||||
String theURI = "http://foo.example.com:80/bar";
|
||||
response.setHeader("Content-Location", theURI);
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
|
||||
new BasicHeader("Date", formatDate(tenSecondsAgo)),
|
||||
});
|
||||
|
||||
expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
|
||||
|
||||
replayMocks();
|
||||
impl.flushInvalidatedCacheEntries(host, request, response);
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotFlushEntrySpecifiedByContentLocationIfResponseHasNoDate()
|
||||
throws Exception {
|
||||
response.setHeader("ETag", "\"new-etag\"");
|
||||
response.removeHeaders("Date");
|
||||
String theURI = "http://foo.example.com:80/bar";
|
||||
response.setHeader("Content-Location", theURI);
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
|
||||
new BasicHeader("ETag", "\"old-etag\""),
|
||||
new BasicHeader("Date", formatDate(tenSecondsAgo)),
|
||||
});
|
||||
|
||||
expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
|
||||
|
||||
replayMocks();
|
||||
impl.flushInvalidatedCacheEntries(host, request, response);
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotFlushEntrySpecifiedByContentLocationIfEntryHasNoDate()
|
||||
throws Exception {
|
||||
response.setHeader("ETag","\"new-etag\"");
|
||||
response.setHeader("Date", formatDate(now));
|
||||
String theURI = "http://foo.example.com:80/bar";
|
||||
response.setHeader("Content-Location", theURI);
|
||||
|
||||
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
|
||||
new BasicHeader("ETag", "\"old-etag\"")
|
||||
});
|
||||
|
||||
expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
|
||||
|
||||
replayMocks();
|
||||
impl.flushInvalidatedCacheEntries(host, request, response);
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
|
||||
// Expectations
|
||||
private void cacheEntryHasVariantMap(Map<String,String> variantMap) {
|
||||
org.easymock.EasyMock.expect(mockEntry.getVariantMap()).andReturn(variantMap);
|
||||
expect(mockEntry.getVariantMap()).andReturn(variantMap);
|
||||
}
|
||||
|
||||
private void cacheReturnsEntryForUri(String theUri) throws IOException {
|
||||
org.easymock.EasyMock.expect(mockStorage.getEntry(theUri)).andReturn(mockEntry);
|
||||
expect(mockStorage.getEntry(theUri)).andReturn(mockEntry);
|
||||
}
|
||||
|
||||
private void cacheReturnsExceptionForUri(String theUri) throws IOException {
|
||||
org.easymock.EasyMock.expect(mockStorage.getEntry(theUri)).andThrow(
|
||||
expect(mockStorage.getEntry(theUri)).andThrow(
|
||||
new IOException("TOTAL FAIL"));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue