HTTPCLIENT-1035: finished cache-flushing logic for updated

entries mentioned by Content-Location in responses. Still
not hooked in to main request handling flow yet.



git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1052234 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jonathan Moore 2010-12-23 12:02:37 +00:00
parent 68ed95699e
commit 7b99aca6ab
2 changed files with 180 additions and 35 deletions

View File

@ -54,7 +54,7 @@ import org.apache.http.impl.cookie.DateUtils;
class CacheInvalidator {
private final HttpCacheStorage storage;
private final CacheKeyGenerator uriExtractor;
private final CacheKeyGenerator cacheKeyGenerator;
private final Log log = LogFactory.getLog(getClass());
@ -68,7 +68,7 @@ class CacheInvalidator {
public CacheInvalidator(
final CacheKeyGenerator uriExtractor,
final HttpCacheStorage storage) {
this.uriExtractor = uriExtractor;
this.cacheKeyGenerator = uriExtractor;
this.storage = storage;
}
@ -79,26 +79,24 @@ class CacheInvalidator {
* @param host The backend host we are talking to
* @param req The HttpRequest to that host
*/
public void flushInvalidatedCacheEntries(HttpHost host, HttpRequest req) throws IOException {
public void flushInvalidatedCacheEntries(HttpHost host, HttpRequest req) {
if (requestShouldNotBeCached(req)) {
log.debug("Request should not be cached");
String theUri = uriExtractor.getURI(host, req);
String theUri = cacheKeyGenerator.getURI(host, req);
HttpCacheEntry parent = storage.getEntry(theUri);
HttpCacheEntry parent = getEntry(theUri);
log.debug("parent entry: " + parent);
if (parent != null) {
for (String variantURI : parent.getVariantMap().values()) {
storage.removeEntry(variantURI);
flushEntry(variantURI);
}
storage.removeEntry(theUri);
flushEntry(theUri);
}
URL reqURL;
try {
reqURL = new URL(theUri);
} catch (MalformedURLException mue) {
URL reqURL = getAbsoluteURL(theUri);
if (reqURL == null) {
log.error("Couldn't transform request into valid URL");
return;
}
@ -116,35 +114,65 @@ class CacheInvalidator {
}
}
protected void flushUriIfSameHost(URL requestURL, URL targetURL) throws IOException {
URL canonicalTarget = new URL(uriExtractor.canonicalizeUri(targetURL.toString()));
if (canonicalTarget.getAuthority().equalsIgnoreCase(requestURL.getAuthority())) {
storage.removeEntry(canonicalTarget.toString());
private void flushEntry(String uri) {
try {
storage.removeEntry(uri);
} catch (IOException ioe) {
log.warn("unable to flush cache entry", ioe);
}
}
protected void flushRelativeUriFromSameHost(URL reqURL, String relUri) throws IOException {
URL relURL;
private HttpCacheEntry getEntry(String theUri) {
try {
relURL = new URL(reqURL,relUri);
} catch (MalformedURLException e) {
log.debug("Invalid relative URI",e);
return;
return storage.getEntry(theUri);
} catch (IOException ioe) {
log.warn("could not retrieve entry from storage", ioe);
}
return null;
}
protected void flushUriIfSameHost(URL requestURL, URL targetURL) {
URL canonicalTarget = getAbsoluteURL(cacheKeyGenerator.canonicalizeUri(targetURL.toString()));
if (canonicalTarget == null) return;
if (canonicalTarget.getAuthority().equalsIgnoreCase(requestURL.getAuthority())) {
flushEntry(canonicalTarget.toString());
}
}
protected void flushRelativeUriFromSameHost(URL reqURL, String relUri) {
URL relURL = getRelativeURL(reqURL, relUri);
if (relURL == null) return;
flushUriIfSameHost(reqURL, relURL);
}
protected boolean flushAbsoluteUriFromSameHost(URL reqURL, String uri) throws IOException {
URL absURL;
try {
absURL = new URL(uri);
} catch (MalformedURLException mue) {
return false;
}
protected boolean flushAbsoluteUriFromSameHost(URL reqURL, String uri) {
URL absURL = getAbsoluteURL(uri);
if (absURL == null) return false;
flushUriIfSameHost(reqURL,absURL);
return true;
}
private URL getAbsoluteURL(String uri) {
URL absURL = null;
try {
absURL = new URL(uri);
} catch (MalformedURLException mue) {
// nop
}
return absURL;
}
private URL getRelativeURL(URL reqURL, String relUri) {
URL relURL = null;
try {
relURL = new URL(reqURL,relUri);
} catch (MalformedURLException e) {
// nop
}
return relURL;
}
protected boolean requestShouldNotBeCached(HttpRequest req) {
String method = req.getRequestLine().getMethod();
return notGetOrHeadRequest(method);
@ -160,16 +188,28 @@ class CacheInvalidator {
* @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());
HttpRequest request, HttpResponse response) {
URL reqURL = getAbsoluteURL(cacheKeyGenerator.getURI(host, request));
if (reqURL == null) return;
URL canonURL = getContentLocationURL(reqURL, response);
if (canonURL == null) return;
String cacheKey = cacheKeyGenerator.canonicalizeUri(canonURL.toString());
HttpCacheEntry entry = getEntry(cacheKey);
if (entry == null) return;
if (!responseDateNewerThanEntryDate(response, entry)) return;
if (!responseAndEntryEtagsDiffer(response, entry)) return;
storage.removeEntry(contentLocation.getValue());
flushUriIfSameHost(reqURL, canonURL);
}
private URL getContentLocationURL(URL reqURL, HttpResponse response) {
Header clHeader = response.getFirstHeader("Content-Location");
if (clHeader == null) return null;
String contentLocation = clHeader.getValue();
URL canonURL = getAbsoluteURL(contentLocation);
if (canonURL != null) return canonURL;
return getRelativeURL(reqURL, contentLocation);
}
private boolean responseAndEntryEtagsDiffer(HttpResponse response,

View File

@ -72,6 +72,7 @@ public class TestCacheInvalidator {
mockStorage = createMock(HttpCacheStorage.class);
cacheKeyGenerator = new CacheKeyGenerator();
mockEntry = createMock(HttpCacheEntry.class);
request = HttpTestUtils.makeDefaultRequest();
response = HttpTestUtils.make200Response();
impl = new CacheInvalidator(cacheKeyGenerator, mockStorage);
@ -249,7 +250,7 @@ public class TestCacheInvalidator {
verifyMocks();
}
@Test(expected=IOException.class)
@Test
public void testCacheFlushException() throws Exception {
request = new BasicHttpRequest("POST","/",HTTP_1_1);
String theURI = "http://foo.example.com:80/";
@ -258,12 +259,12 @@ public class TestCacheInvalidator {
replayMocks();
impl.flushInvalidatedCacheEntries(host, request);
verifyMocks();
}
@Test
public void doesNotFlushForResponsesWithoutContentLocation()
throws Exception {
request = HttpTestUtils.makeDefaultRequest();
replayMocks();
impl.flushInvalidatedCacheEntries(host, request, response);
verifyMocks();
@ -290,6 +291,70 @@ public class TestCacheInvalidator {
verifyMocks();
}
@Test
public void flushesEntryIfFresherAndSpecifiedByNonCanonicalContentLocation()
throws Exception {
response.setHeader("ETag","\"new-etag\"");
response.setHeader("Date", formatDate(now));
String cacheKey = "http://foo.example.com:80/bar";
response.setHeader("Content-Location", "http://foo.example.com/bar");
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
new BasicHeader("Date", formatDate(tenSecondsAgo)),
new BasicHeader("ETag", "\"old-etag\"")
});
expect(mockStorage.getEntry(cacheKey)).andReturn(entry).anyTimes();
mockStorage.removeEntry(cacheKey);
replayMocks();
impl.flushInvalidatedCacheEntries(host, request, response);
verifyMocks();
}
@Test
public void flushesEntryIfFresherAndSpecifiedByRelativeContentLocation()
throws Exception {
response.setHeader("ETag","\"new-etag\"");
response.setHeader("Date", formatDate(now));
String cacheKey = "http://foo.example.com:80/bar";
response.setHeader("Content-Location", "/bar");
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
new BasicHeader("Date", formatDate(tenSecondsAgo)),
new BasicHeader("ETag", "\"old-etag\"")
});
expect(mockStorage.getEntry(cacheKey)).andReturn(entry).anyTimes();
mockStorage.removeEntry(cacheKey);
replayMocks();
impl.flushInvalidatedCacheEntries(host, request, response);
verifyMocks();
}
@Test
public void doesNotFlushEntryIfContentLocationFromDifferentHost()
throws Exception {
response.setHeader("ETag","\"new-etag\"");
response.setHeader("Date", formatDate(now));
String cacheKey = "http://baz.example.com:80/bar";
response.setHeader("Content-Location", cacheKey);
HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
new BasicHeader("Date", formatDate(tenSecondsAgo)),
new BasicHeader("ETag", "\"old-etag\"")
});
expect(mockStorage.getEntry(cacheKey)).andReturn(entry).anyTimes();
replayMocks();
impl.flushInvalidatedCacheEntries(host, request, response);
verifyMocks();
}
@Test
public void doesNotFlushEntrySpecifiedByContentLocationIfEtagsMatch()
throws Exception {
@ -423,6 +488,46 @@ public class TestCacheInvalidator {
verifyMocks();
}
@Test
public void doesNotFlushEntrySpecifiedByContentLocationIfResponseHasMalformedDate()
throws Exception {
response.setHeader("ETag","\"new-etag\"");
response.setHeader("Date", "blarg");
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 doesNotFlushEntrySpecifiedByContentLocationIfEntryHasMalformedDate()
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\""),
new BasicHeader("Date", "foo")
});
expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
replayMocks();
impl.flushInvalidatedCacheEntries(host, request, response);
verifyMocks();
}
// Expectations
private void cacheEntryHasVariantMap(Map<String,String> variantMap) {