HTTP Cache API refactoring: entry validation logic factored out from CacheEntry to a separate policy class; HttpCacheEntry should now contain (almost) no protocol specific code

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@966642 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2010-07-22 13:16:29 +00:00
parent 8cf6a94f60
commit 7205e7d340
35 changed files with 1651 additions and 1482 deletions

View File

@ -24,7 +24,7 @@
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client.cache;
package org.apache.http.client.cache;
import java.io.Serializable;
@ -35,5 +35,5 @@ import org.apache.http.message.HeaderGroup;
class CachedHeaderGroup extends HeaderGroup implements Serializable {
private static final long serialVersionUID = -4572663568087431896L;
}

View File

@ -24,7 +24,7 @@
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client.cache;
package org.apache.http.client.cache;
import org.apache.http.annotation.Immutable;
@ -32,7 +32,7 @@ import org.apache.http.annotation.Immutable;
* @since 4.1
*/
@Immutable
class HeaderConstants {
public class HeaderConstants {
public static final String GET_METHOD = "GET";
public static final String HEAD_METHOD = "HEAD";

View File

@ -29,15 +29,15 @@ package org.apache.http.client.cache;
/**
* @since 4.1
*/
public interface HttpCache<K, E> {
public interface HttpCache {
void putEntry(K key, E entry) throws HttpCacheOperationException;
void putEntry(String key, HttpCacheEntry entry) throws HttpCacheOperationException;
E getEntry(K key) throws HttpCacheOperationException;
HttpCacheEntry getEntry(String key) throws HttpCacheOperationException;
void removeEntry(K key) throws HttpCacheOperationException;
void removeEntry(String key) throws HttpCacheOperationException;
void updateEntry(
K key, HttpCacheUpdateCallback<E> callback) throws HttpCacheOperationException;
String key, HttpCacheUpdateCallback callback) throws HttpCacheOperationException;
}

View File

@ -0,0 +1,218 @@
/*
* ====================================================================
* 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.client.cache;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.annotation.Immutable;
import org.apache.http.message.BasicHeader;
/**
* Structure used to store an {@link HttpResponse} in a cache
*
* @since 4.1
*/
@Immutable
public class HttpCacheEntry implements Serializable {
private static final long serialVersionUID = -6300496422359477413L;
private final Date requestDate;
private final Date responseDate;
private final StatusLine statusLine;
private final CachedHeaderGroup responseHeaders;
private final HttpEntity body;
private final Set<String> variantURIs;
/**
* Create a new {@link HttpCacheEntry}
*
* @param requestDate
* Date/time when the request was made (Used for age
* calculations)
* @param responseDate
* Date/time that the response came back (Used for age
* calculations)
* @param version
* HTTP Response Version
* @param responseHeaders
* Header[] from original HTTP Response
* @param body
* HttpEntity representing the body of the response
* @param status
* Numeric HTTP Status Code
* @param reason
* String message from HTTP Status Line
*/
public HttpCacheEntry(
final Date requestDate,
final Date responseDate,
final StatusLine statusLine,
final Header[] responseHeaders,
final HttpEntity body,
final Set<String> variants) {
super();
if (requestDate == null) {
throw new IllegalArgumentException("Request date may not be null");
}
if (responseDate == null) {
throw new IllegalArgumentException("Response date may not be null");
}
if (statusLine == null) {
throw new IllegalArgumentException("Status line may not be null");
}
if (responseHeaders == null) {
throw new IllegalArgumentException("Response headers may not be null");
}
if (body == null) {
throw new IllegalArgumentException("Response body may not be null");
}
this.requestDate = requestDate;
this.responseDate = responseDate;
this.statusLine = statusLine;
this.responseHeaders = new CachedHeaderGroup();
this.responseHeaders.setHeaders(responseHeaders);
this.body = body;
this.variantURIs = variants != null ? new HashSet<String>(variants) : new HashSet<String>();
}
public StatusLine getStatusLine() {
return this.statusLine;
}
public ProtocolVersion getProtocolVersion() {
return this.statusLine.getProtocolVersion();
}
public String getReasonPhrase() {
return this.statusLine.getReasonPhrase();
}
public int getStatusCode() {
return this.statusLine.getStatusCode();
}
public Date getRequestDate() {
return requestDate;
}
public Date getResponseDate() {
return responseDate;
}
public HttpEntity getBody() {
return body;
}
public Header[] getAllHeaders() {
return responseHeaders.getAllHeaders();
}
public Header getFirstHeader(String name) {
return responseHeaders.getFirstHeader(name);
}
public Header[] getHeaders(String name) {
return responseHeaders.getHeaders(name);
}
public boolean hasVariants() {
return getFirstHeader(HeaderConstants.VARY) != null;
}
public Set<String> getVariantURIs() {
return Collections.unmodifiableSet(this.variantURIs);
}
private void writeObject(ObjectOutputStream out) throws IOException {
// write CacheEntry
out.defaultWriteObject();
// write (non-serializable) responseHeaders
if (null == responseHeaders || responseHeaders.getAllHeaders().length < 1)
return;
int headerCount = responseHeaders.getAllHeaders().length;
Header[] headers = responseHeaders.getAllHeaders();
String[][] sheaders = new String[headerCount][2];
for (int i = 0; i < headerCount; i++) {
sheaders[i][0] = headers[i].getName();
sheaders[i][1] = headers[i].getValue();
}
out.writeObject(sheaders);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
// read CacheEntry
in.defaultReadObject();
// read (non-serializable) responseHeaders
String[][] sheaders = (String[][]) in.readObject();
if (null == sheaders || sheaders.length < 1)
return;
BasicHeader[] headers = new BasicHeader[sheaders.length];
for (int i = 0; i < sheaders.length; i++) {
String[] sheader = sheaders[i];
headers[i] = new BasicHeader(sheader[0], sheader[1]);
}
this.responseHeaders.setHeaders(headers);
}
@Override
public String toString() {
return "[request date=" + this.requestDate + "; response date=" + this.responseDate
+ "; statusLine=" + this.statusLine + "]";
}
public static HttpCacheEntry copyWithVariant(final HttpCacheEntry entry, final String variantURI){
Set<String> variants = new HashSet<String>(entry.getVariantURIs());
variants.add(variantURI);
return new HttpCacheEntry(
entry.getRequestDate(),
entry.getResponseDate(),
entry.getStatusLine(),
entry.getAllHeaders(),
entry.getBody(), variants);
}
}

View File

@ -26,7 +26,7 @@
*/
package org.apache.http.client.cache;
public interface HttpCacheUpdateCallback<E> {
public interface HttpCacheUpdateCallback {
/**
* Returns the new cache entry that should replace an existing one.
@ -41,6 +41,6 @@ public interface HttpCacheUpdateCallback<E> {
*
* @since 4.1
*/
E update(E existing) throws HttpCacheOperationException;
HttpCacheEntry update(HttpCacheEntry existing) throws HttpCacheOperationException;
}

View File

@ -31,6 +31,7 @@ import java.util.Map;
import org.apache.http.annotation.ThreadSafe;
import org.apache.http.client.cache.HttpCache;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.HttpCacheOperationException;
import org.apache.http.client.cache.HttpCacheUpdateCallback;
@ -40,15 +41,15 @@ import org.apache.http.client.cache.HttpCacheUpdateCallback;
* @since 4.1
*/
@ThreadSafe
public class BasicHttpCache implements HttpCache<String, CacheEntry> {
public class BasicHttpCache implements HttpCache {
private final LinkedHashMap<String, CacheEntry> baseMap = new LinkedHashMap<String, CacheEntry>(
private final LinkedHashMap<String, HttpCacheEntry> baseMap = new LinkedHashMap<String, HttpCacheEntry>(
20, 0.75f, true) {
private static final long serialVersionUID = -7750025207539768511L;
@Override
protected boolean removeEldestEntry(Map.Entry<String, CacheEntry> eldest) {
protected boolean removeEldestEntry(Map.Entry<String, HttpCacheEntry> eldest) {
return size() > maxEntries;
}
@ -61,14 +62,14 @@ public class BasicHttpCache implements HttpCache<String, CacheEntry> {
}
/**
* Places a CacheEntry in the cache
* Places a HttpCacheEntry in the cache
*
* @param url
* Url to use as the cache key
* @param entry
* CacheEntry to place in the cache
* HttpCacheEntry to place in the cache
*/
public synchronized void putEntry(String url, CacheEntry entry) {
public synchronized void putEntry(String url, HttpCacheEntry entry) {
baseMap.put(url, entry);
}
@ -77,14 +78,14 @@ public class BasicHttpCache implements HttpCache<String, CacheEntry> {
*
* @param url
* Url that is the cache key
* @return CacheEntry if one exists, or null for cache miss
* @return HttpCacheEntry if one exists, or null for cache miss
*/
public synchronized CacheEntry getEntry(String url) {
public synchronized HttpCacheEntry getEntry(String url) {
return baseMap.get(url);
}
/**
* Removes a CacheEntry from the cache
* Removes a HttpCacheEntry from the cache
*
* @param url
* Url that is the cache key
@ -95,8 +96,8 @@ public class BasicHttpCache implements HttpCache<String, CacheEntry> {
public synchronized void updateEntry(
String url,
HttpCacheUpdateCallback<CacheEntry> callback) throws HttpCacheOperationException {
CacheEntry existingEntry = baseMap.get(url);
HttpCacheUpdateCallback callback) throws HttpCacheOperationException {
HttpCacheEntry existingEntry = baseMap.get(url);
baseMap.put(url, callback.update(existingEntry));
}

View File

@ -1,412 +0,0 @@
/*
* ====================================================================
* 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 java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.annotation.Immutable;
import org.apache.http.impl.cookie.DateParseException;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
/**
* Structure used to store an {@link HttpResponse} in a cache
*
* @since 4.1
*/
@Immutable
public class CacheEntry implements Serializable {
private static final long serialVersionUID = -6300496422359477413L;
public static final long MAX_AGE = 2147483648L;
private final Date requestDate;
private final Date responseDate;
private final ProtocolVersion version;
private final int status;
private final String reason;
private final CachedHeaderGroup responseHeaders;
private final HttpEntity body;
private final Set<String> variantURIs;
/**
* Create a new {@link CacheEntry}
*
* @param requestDate
* Date/time when the request was made (Used for age
* calculations)
* @param responseDate
* Date/time that the response came back (Used for age
* calculations)
* @param version
* HTTP Response Version
* @param responseHeaders
* Header[] from original HTTP Response
* @param body
* HttpEntity representing the body of the response
* @param status
* Numeric HTTP Status Code
* @param reason
* String message from HTTP Status Line
*/
public CacheEntry(Date requestDate, Date responseDate, ProtocolVersion version,
Header[] responseHeaders, HttpEntity body, int status, String reason) {
super();
this.requestDate = requestDate;
this.responseDate = responseDate;
this.version = version;
this.responseHeaders = new CachedHeaderGroup();
this.responseHeaders.setHeaders(responseHeaders);
this.status = status;
this.reason = reason;
this.body = body;
this.variantURIs = new HashSet<String>();
}
/**
* Constructor used to create a copy of an existing entry, while adding another variant URI to it.
*
* @param entry CacheEntry to be duplicated
* @param variantURI URI to add
*/
private CacheEntry(CacheEntry entry, String variantURI){
this(entry.getRequestDate(),
entry.getResponseDate(),
entry.getProtocolVersion(),
entry.getAllHeaders(),
entry.body,
entry.getStatusCode(),
entry.getReasonPhrase());
this.variantURIs.addAll(entry.getVariantURIs());
this.variantURIs.add(variantURI);
}
public CacheEntry copyWithVariant(String variantURI){
return new CacheEntry(this,variantURI);
}
public ProtocolVersion getProtocolVersion() {
return version;
}
public String getReasonPhrase() {
return reason;
}
public int getStatusCode() {
return status;
}
public Date getRequestDate() {
return requestDate;
}
public Date getResponseDate() {
return responseDate;
}
public HttpEntity getBody() {
return body;
}
public Header[] getAllHeaders() {
return responseHeaders.getAllHeaders();
}
public Header getFirstHeader(String name) {
return responseHeaders.getFirstHeader(name);
}
public Header[] getHeaders(String name) {
return responseHeaders.getHeaders(name);
}
public long getCurrentAgeSecs() {
return getCorrectedInitialAgeSecs() + getResidentTimeSecs();
}
public long getFreshnessLifetimeSecs() {
long maxage = getMaxAge();
if (maxage > -1)
return maxage;
Date dateValue = getDateValue();
if (dateValue == null)
return 0L;
Date expiry = getExpirationDate();
if (expiry == null)
return 0;
long diff = expiry.getTime() - dateValue.getTime();
return (diff / 1000);
}
public boolean isResponseFresh() {
return (getCurrentAgeSecs() < getFreshnessLifetimeSecs());
}
/**
*
* @return boolean indicating whether ETag or Last-Modified responseHeaders
* are present
*/
public boolean isRevalidatable() {
return getFirstHeader(HeaderConstants.ETAG) != null
|| getFirstHeader(HeaderConstants.LAST_MODIFIED) != null;
}
public boolean modifiedSince(HttpRequest request) {
Header unmodHeader = request.getFirstHeader(HeaderConstants.IF_UNMODIFIED_SINCE);
if (unmodHeader == null) {
return false;
}
try {
Date unmodifiedSinceDate = DateUtils.parseDate(unmodHeader.getValue());
Date lastModifiedDate = DateUtils.parseDate(getFirstHeader(
HeaderConstants.LAST_MODIFIED).getValue());
if (unmodifiedSinceDate.before(lastModifiedDate)) {
return true;
}
} catch (DateParseException e) {
return false;
}
return false;
}
/**
*
* @return boolean indicating whether any Vary responseHeaders are present
*/
public boolean hasVariants() {
return (getFirstHeader(HeaderConstants.VARY) != null);
}
public Set<String> getVariantURIs() {
return Collections.unmodifiableSet(this.variantURIs);
}
public boolean mustRevalidate() {
return hasCacheControlDirective("must-revalidate");
}
public boolean proxyRevalidate() {
return hasCacheControlDirective("proxy-revalidate");
}
Date getDateValue() {
Header dateHdr = getFirstHeader(HTTP.DATE_HEADER);
if (dateHdr == null)
return null;
try {
return DateUtils.parseDate(dateHdr.getValue());
} catch (DateParseException dpe) {
// ignore malformed date
}
return null;
}
long getContentLengthValue() {
Header cl = getFirstHeader(HTTP.CONTENT_LEN);
if (cl == null)
return -1;
try {
return Long.parseLong(cl.getValue());
} catch (NumberFormatException ex) {
return -1;
}
}
/**
* This matters for deciding whether the cache entry is valid to serve as a
* response. If these values do not match, we might have a partial response
*
* @return boolean indicating whether actual length matches Content-Length
*/
boolean contentLengthHeaderMatchesActualLength() {
return getContentLengthValue() == body.getContentLength();
}
long getApparentAgeSecs() {
Date dateValue = getDateValue();
if (dateValue == null)
return MAX_AGE;
long diff = responseDate.getTime() - dateValue.getTime();
if (diff < 0L)
return 0;
return (diff / 1000);
}
long getAgeValue() {
long ageValue = 0;
for (Header hdr : getHeaders(HeaderConstants.AGE)) {
long hdrAge;
try {
hdrAge = Long.parseLong(hdr.getValue());
if (hdrAge < 0) {
hdrAge = MAX_AGE;
}
} catch (NumberFormatException nfe) {
hdrAge = MAX_AGE;
}
ageValue = (hdrAge > ageValue) ? hdrAge : ageValue;
}
return ageValue;
}
long getCorrectedReceivedAgeSecs() {
long apparentAge = getApparentAgeSecs();
long ageValue = getAgeValue();
return (apparentAge > ageValue) ? apparentAge : ageValue;
}
long getResponseDelaySecs() {
long diff = responseDate.getTime() - requestDate.getTime();
return (diff / 1000L);
}
long getCorrectedInitialAgeSecs() {
return getCorrectedReceivedAgeSecs() + getResponseDelaySecs();
}
Date getCurrentDate() {
return new Date();
}
long getResidentTimeSecs() {
long diff = getCurrentDate().getTime() - responseDate.getTime();
return (diff / 1000L);
}
long getMaxAge() {
long maxage = -1;
for (Header hdr : getHeaders(HeaderConstants.CACHE_CONTROL)) {
for (HeaderElement elt : hdr.getElements()) {
if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())
|| "s-maxage".equals(elt.getName())) {
try {
long currMaxAge = Long.parseLong(elt.getValue());
if (maxage == -1 || currMaxAge < maxage) {
maxage = currMaxAge;
}
} catch (NumberFormatException nfe) {
// be conservative if can't parse
maxage = 0;
}
}
}
}
return maxage;
}
Date getExpirationDate() {
Header expiresHeader = getFirstHeader(HeaderConstants.EXPIRES);
if (expiresHeader == null)
return null;
try {
return DateUtils.parseDate(expiresHeader.getValue());
} catch (DateParseException dpe) {
// malformed expires header
}
return null;
}
boolean hasCacheControlDirective(String directive) {
for(Header h : responseHeaders.getHeaders("Cache-Control")) {
for(HeaderElement elt : h.getElements()) {
if (directive.equalsIgnoreCase(elt.getName())) {
return true;
}
}
}
return false;
}
private void writeObject(ObjectOutputStream out) throws IOException {
// write CacheEntry
out.defaultWriteObject();
// write (non-serializable) responseHeaders
if (null == responseHeaders || responseHeaders.getAllHeaders().length < 1)
return;
int headerCount = responseHeaders.getAllHeaders().length;
Header[] headers = responseHeaders.getAllHeaders();
String[][] sheaders = new String[headerCount][2];
for (int i = 0; i < headerCount; i++) {
sheaders[i][0] = headers[i].getName();
sheaders[i][1] = headers[i].getValue();
}
out.writeObject(sheaders);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
// read CacheEntry
in.defaultReadObject();
// read (non-serializable) responseHeaders
String[][] sheaders = (String[][]) in.readObject();
if (null == sheaders || sheaders.length < 1)
return;
BasicHeader[] headers = new BasicHeader[sheaders.length];
for (int i = 0; i < sheaders.length; i++) {
String[] sheader = sheaders[i];
headers[i] = new BasicHeader(sheader[0], sheader[1]);
}
this.responseHeaders.setHeaders(headers);
}
@Override
public String toString() {
return "[request date=" + requestDate + "; response date=" + responseDate
+ "; status=" + status + "]";
}
}

View File

@ -30,6 +30,7 @@ import java.util.Date;
import org.apache.http.HttpResponse;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HttpCacheEntry;
/**
* Generates a {@link CacheEntry} from a {@link HttpResponse}
@ -39,18 +40,18 @@ import org.apache.http.annotation.Immutable;
@Immutable
class CacheEntryGenerator {
public CacheEntry generateEntry(
Date requestDate,
Date responseDate,
public HttpCacheEntry generateEntry(
Date requestDate,
Date responseDate,
HttpResponse response,
byte[] body) {
CacheEntity entity = new CacheEntity(body, response);
return new CacheEntry(requestDate,
return new HttpCacheEntry(requestDate,
responseDate,
response.getProtocolVersion(),
response.getStatusLine(),
response.getAllHeaders(),
entity,
response.getStatusLine().getStatusCode(),
response.getStatusLine().getReasonPhrase());
null);
}
}

View File

@ -36,12 +36,14 @@ import java.util.ListIterator;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.impl.cookie.DateParseException;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.protocol.HTTP;
/**
* Update a {@link CacheEntry} with new or updated information based on the latest
* Update a {@link HttpCacheEntry} with new or updated information based on the latest
* 200 or 304 status responses from the Server. Use the {@link HttpResponse} to perform
* the update.
*
@ -57,27 +59,25 @@ class CacheEntryUpdater {
* @param requestDate When the request was performed
* @param responseDate When the response was gotten
* @param response The HttpResponse from the backend server call
* @return CacheEntry an updated version of the cache entry
* @return HttpCacheEntry an updated version of the cache entry
* @throws java.io.IOException if something bad happens while trying to read the body from the original entry
*/
public CacheEntry updateCacheEntry(
CacheEntry entry,
public HttpCacheEntry updateCacheEntry(
HttpCacheEntry entry,
Date requestDate,
Date responseDate,
HttpResponse response) throws IOException {
Header[] mergedHeaders = mergeHeaders(entry, response);
CacheEntry updated = new CacheEntry(requestDate, responseDate,
entry.getProtocolVersion(),
HttpCacheEntry updated = new HttpCacheEntry(requestDate, responseDate,
entry.getStatusLine(),
mergedHeaders,
entry.getBody(),
entry.getStatusCode(),
entry.getReasonPhrase());
null);
return updated;
}
protected Header[] mergeHeaders(CacheEntry entry, HttpResponse response) {
protected Header[] mergeHeaders(HttpCacheEntry entry, HttpResponse response) {
List<Header> cacheEntryHeaderList = new ArrayList<Header>(Arrays.asList(entry
.getAllHeaders()));
@ -112,7 +112,7 @@ class CacheEntryUpdater {
}
}
private void removeCacheEntry1xxWarnings(List<Header> cacheEntryHeaderList, CacheEntry entry) {
private void removeCacheEntry1xxWarnings(List<Header> cacheEntryHeaderList, HttpCacheEntry entry) {
ListIterator<Header> cacheEntryHeaderListIter = cacheEntryHeaderList.listIterator();
while (cacheEntryHeaderListIter.hasNext()) {
@ -128,7 +128,7 @@ class CacheEntryUpdater {
}
}
private boolean entryDateHeaderNewerThenResponse(CacheEntry entry, HttpResponse response) {
private boolean entryDateHeaderNewerThenResponse(HttpCacheEntry entry, HttpResponse response) {
try {
Date entryDate = DateUtils.parseDate(entry.getFirstHeader(HTTP.DATE_HEADER)
.getValue());
@ -145,7 +145,7 @@ class CacheEntryUpdater {
return true;
}
private boolean entryAndResponseHaveDateHeader(CacheEntry entry, HttpResponse response) {
private boolean entryAndResponseHaveDateHeader(HttpCacheEntry entry, HttpResponse response) {
if (entry.getFirstHeader(HTTP.DATE_HEADER) != null
&& response.getFirstHeader(HTTP.DATE_HEADER) != null) {
return true;

View File

@ -36,7 +36,9 @@ import org.apache.http.HeaderElement;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.annotation.ThreadSafe;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.client.cache.HttpCache;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.HttpCacheOperationException;
/**
@ -48,7 +50,7 @@ import org.apache.http.client.cache.HttpCacheOperationException;
@ThreadSafe // so long as the cache implementation is thread-safe
class CacheInvalidator {
private final HttpCache<String, CacheEntry> cache;
private final HttpCache cache;
private final URIExtractor uriExtractor;
private final Log log = LogFactory.getLog(getClass());
@ -60,7 +62,9 @@ class CacheInvalidator {
* @param uriExtractor Provides identifiers for the keys to store cache entries
* @param cache the cache to store items away in
*/
public CacheInvalidator(URIExtractor uriExtractor, HttpCache<String, CacheEntry> cache) {
public CacheInvalidator(
final URIExtractor uriExtractor,
final HttpCache cache) {
this.uriExtractor = uriExtractor;
this.cache = cache;
}
@ -79,7 +83,7 @@ class CacheInvalidator {
try {
String theUri = uriExtractor.getURI(host, req);
CacheEntry parent = cache.getEntry(theUri);
HttpCacheEntry parent = cache.getEntry(theUri);
log.debug("parent entry: " + parent);

View File

@ -0,0 +1,241 @@
/*
* ====================================================================
* 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 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;
import org.apache.http.impl.cookie.DateParseException;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.protocol.HTTP;
/**
* @since 4.1
*/
@Immutable
class CacheValidityPolicy {
public static final long MAX_AGE = 2147483648L;
CacheValidityPolicy() {
super();
}
public long getCurrentAgeSecs(final HttpCacheEntry entry) {
return getCorrectedInitialAgeSecs(entry) + getResidentTimeSecs(entry);
}
public long getFreshnessLifetimeSecs(final HttpCacheEntry entry) {
long maxage = getMaxAge(entry);
if (maxage > -1)
return maxage;
Date dateValue = getDateValue(entry);
if (dateValue == null)
return 0L;
Date expiry = getExpirationDate(entry);
if (expiry == null)
return 0;
long diff = expiry.getTime() - dateValue.getTime();
return (diff / 1000);
}
public boolean isResponseFresh(final HttpCacheEntry entry) {
return (getCurrentAgeSecs(entry) < getFreshnessLifetimeSecs(entry));
}
public boolean isRevalidatable(final HttpCacheEntry entry) {
return entry.getFirstHeader(HeaderConstants.ETAG) != null
|| entry.getFirstHeader(HeaderConstants.LAST_MODIFIED) != null;
}
public boolean modifiedSince(final HttpCacheEntry entry, final HttpRequest request) {
Header unmodHeader = request.getFirstHeader(HeaderConstants.IF_UNMODIFIED_SINCE);
if (unmodHeader == null) {
return false;
}
try {
Date unmodifiedSinceDate = DateUtils.parseDate(unmodHeader.getValue());
Date lastModifiedDate = DateUtils.parseDate(entry.getFirstHeader(
HeaderConstants.LAST_MODIFIED).getValue());
if (unmodifiedSinceDate.before(lastModifiedDate)) {
return true;
}
} catch (DateParseException e) {
return false;
}
return false;
}
public boolean mustRevalidate(final HttpCacheEntry entry) {
return hasCacheControlDirective(entry, "must-revalidate");
}
public boolean proxyRevalidate(final HttpCacheEntry entry) {
return hasCacheControlDirective(entry, "proxy-revalidate");
}
protected Date getDateValue(final HttpCacheEntry entry) {
Header dateHdr = entry.getFirstHeader(HTTP.DATE_HEADER);
if (dateHdr == null)
return null;
try {
return DateUtils.parseDate(dateHdr.getValue());
} catch (DateParseException dpe) {
// ignore malformed date
}
return null;
}
protected long getContentLengthValue(final HttpCacheEntry entry) {
Header cl = entry.getFirstHeader(HTTP.CONTENT_LEN);
if (cl == null)
return -1;
try {
return Long.parseLong(cl.getValue());
} catch (NumberFormatException ex) {
return -1;
}
}
/**
* This matters for deciding whether the cache entry is valid to serve as a
* response. If these values do not match, we might have a partial response
*
* @return boolean indicating whether actual length matches Content-Length
*/
protected boolean contentLengthHeaderMatchesActualLength(final HttpCacheEntry entry) {
return getContentLengthValue(entry) == entry.getBody().getContentLength();
}
protected long getApparentAgeSecs(final HttpCacheEntry entry) {
Date dateValue = getDateValue(entry);
if (dateValue == null)
return MAX_AGE;
long diff = entry.getResponseDate().getTime() - dateValue.getTime();
if (diff < 0L)
return 0;
return (diff / 1000);
}
protected long getAgeValue(final HttpCacheEntry entry) {
long ageValue = 0;
for (Header hdr : entry.getHeaders(HeaderConstants.AGE)) {
long hdrAge;
try {
hdrAge = Long.parseLong(hdr.getValue());
if (hdrAge < 0) {
hdrAge = MAX_AGE;
}
} catch (NumberFormatException nfe) {
hdrAge = MAX_AGE;
}
ageValue = (hdrAge > ageValue) ? hdrAge : ageValue;
}
return ageValue;
}
protected long getCorrectedReceivedAgeSecs(final HttpCacheEntry entry) {
long apparentAge = getApparentAgeSecs(entry);
long ageValue = getAgeValue(entry);
return (apparentAge > ageValue) ? apparentAge : ageValue;
}
protected long getResponseDelaySecs(final HttpCacheEntry entry) {
long diff = entry.getResponseDate().getTime() - entry.getRequestDate().getTime();
return (diff / 1000L);
}
protected long getCorrectedInitialAgeSecs(final HttpCacheEntry entry) {
return getCorrectedReceivedAgeSecs(entry) + getResponseDelaySecs(entry);
}
protected Date getCurrentDate() {
return new Date();
}
protected long getResidentTimeSecs(final HttpCacheEntry entry) {
long diff = getCurrentDate().getTime() - entry.getResponseDate().getTime();
return (diff / 1000L);
}
protected long getMaxAge(final HttpCacheEntry entry) {
long maxage = -1;
for (Header hdr : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) {
for (HeaderElement elt : hdr.getElements()) {
if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())
|| "s-maxage".equals(elt.getName())) {
try {
long currMaxAge = Long.parseLong(elt.getValue());
if (maxage == -1 || currMaxAge < maxage) {
maxage = currMaxAge;
}
} catch (NumberFormatException nfe) {
// be conservative if can't parse
maxage = 0;
}
}
}
}
return maxage;
}
protected Date getExpirationDate(final HttpCacheEntry entry) {
Header expiresHeader = entry.getFirstHeader(HeaderConstants.EXPIRES);
if (expiresHeader == null)
return null;
try {
return DateUtils.parseDate(expiresHeader.getValue());
} catch (DateParseException dpe) {
// malformed expires header
}
return null;
}
protected boolean hasCacheControlDirective(final HttpCacheEntry entry, final String directive) {
for (Header h : entry.getHeaders("Cache-Control")) {
for(HeaderElement elt : h.getElements()) {
if (directive.equalsIgnoreCase(elt.getName())) {
return true;
}
}
}
return false;
}
}

View File

@ -34,6 +34,7 @@ import org.apache.http.HttpRequest;
import org.apache.http.HttpVersion;
import org.apache.http.ProtocolVersion;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HeaderConstants;
/**
* Determines if an HttpRequest is allowed to be served from the cache.

View File

@ -32,6 +32,8 @@ import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.protocol.HTTP;
@ -44,6 +46,17 @@ import org.apache.http.protocol.HTTP;
@Immutable
class CachedHttpResponseGenerator {
private final CacheValidityPolicy validityStrategy;
CachedHttpResponseGenerator(final CacheValidityPolicy validityStrategy) {
super();
this.validityStrategy = validityStrategy;
}
CachedHttpResponseGenerator() {
this(new CacheValidityPolicy());
}
/**
* If I was able to use a {@link CacheEntry} to response to the {@link org.apache.http.HttpRequest} then
* generate an {@link HttpResponse} based on the cache entry.
@ -51,7 +64,7 @@ class CachedHttpResponseGenerator {
* {@link CacheEntry} to transform into an {@link HttpResponse}
* @return {@link HttpResponse} that was constructed
*/
HttpResponse generateResponse(CacheEntry entry) {
HttpResponse generateResponse(HttpCacheEntry entry) {
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, entry
.getStatusCode(), entry.getReasonPhrase());
@ -63,7 +76,7 @@ class CachedHttpResponseGenerator {
addMissingContentLengthHeader(response, entity);
}
long age = entry.getCurrentAgeSecs();
long age = this.validityStrategy.getCurrentAgeSecs(entry);
if (age > 0) {
if (age >= Integer.MAX_VALUE) {
response.setHeader(HeaderConstants.AGE, "2147483648");

View File

@ -33,9 +33,11 @@ import org.apache.http.HeaderElement;
import org.apache.http.HttpHost;
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;
/**
* Determines whether a given {@link CacheEntry} is suitable to be
* Determines whether a given {@link HttpCacheEntry} is suitable to be
* used as a response for a given {@link HttpRequest}.
*
* @since 4.1
@ -45,8 +47,19 @@ class CachedResponseSuitabilityChecker {
private final Log log = LogFactory.getLog(getClass());
private final CacheValidityPolicy validityStrategy;
CachedResponseSuitabilityChecker(final CacheValidityPolicy validityStrategy) {
super();
this.validityStrategy = validityStrategy;
}
CachedResponseSuitabilityChecker() {
this(new CacheValidityPolicy());
}
/**
* Determine if I can utilize a {@link CacheEntry} to respond to the given
* Determine if I can utilize a {@link HttpCacheEntry} to respond to the given
* {@link HttpRequest}
*
* @param host
@ -54,21 +67,21 @@ class CachedResponseSuitabilityChecker {
* @param request
* {@link HttpRequest}
* @param entry
* {@link CacheEntry}
* {@link HttpCacheEntry}
* @return boolean yes/no answer
*/
public boolean canCachedResponseBeUsed(HttpHost host, HttpRequest request, CacheEntry entry) {
if (!entry.isResponseFresh()) {
public boolean canCachedResponseBeUsed(HttpHost host, HttpRequest request, HttpCacheEntry entry) {
if (!validityStrategy.isResponseFresh(entry)) {
log.debug("Cache entry was not fresh enough");
return false;
}
if (!entry.contentLengthHeaderMatchesActualLength()) {
if (!validityStrategy.contentLengthHeaderMatchesActualLength(entry)) {
log.debug("Cache entry Content-Length and header information do not match");
return false;
}
if (entry.modifiedSince(request)) {
if (validityStrategy.modifiedSince(entry, request)) {
log.debug("Cache entry modified times didn't line up. Cache Entry should not be used");
return false;
}
@ -88,7 +101,7 @@ class CachedResponseSuitabilityChecker {
if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())) {
try {
int maxage = Integer.parseInt(elt.getValue());
if (entry.getCurrentAgeSecs() > maxage) {
if (validityStrategy.getCurrentAgeSecs(entry) > maxage) {
log.debug("Response from cache was NOT suitable due to max age");
return false;
}
@ -102,7 +115,7 @@ class CachedResponseSuitabilityChecker {
if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) {
try {
int maxstale = Integer.parseInt(elt.getValue());
if (entry.getFreshnessLifetimeSecs() > maxstale) {
if (validityStrategy.getFreshnessLifetimeSecs(entry) > maxstale) {
log.debug("Response from cache was not suitable due to Max stale freshness");
return false;
}
@ -116,7 +129,7 @@ class CachedResponseSuitabilityChecker {
if (HeaderConstants.CACHE_CONTROL_MIN_FRESH.equals(elt.getName())) {
try {
int minfresh = Integer.parseInt(elt.getValue());
if (entry.getFreshnessLifetimeSecs() < minfresh) {
if (validityStrategy.getFreshnessLifetimeSecs(entry) < minfresh) {
log.debug("Response from cache was not suitable due to min fresh " +
"freshness requirement");
return false;

View File

@ -47,7 +47,9 @@ import org.apache.http.annotation.ThreadSafe;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.client.cache.HttpCache;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.HttpCacheOperationException;
import org.apache.http.client.cache.HttpCacheUpdateCallback;
import org.apache.http.client.methods.HttpUriRequest;
@ -75,10 +77,11 @@ public class CachingHttpClient implements HttpClient {
private final AtomicLong cacheUpdates = new AtomicLong();
private final HttpClient backend;
private final HttpCache responseCache;
private final CacheValidityPolicy validityPolicy;
private final ResponseCachingPolicy responseCachingPolicy;
private final CacheEntryGenerator cacheEntryGenerator;
private final URIExtractor uriExtractor;
private final HttpCache<String, CacheEntry> responseCache;
private final CachedHttpResponseGenerator responseGenerator;
private final CacheInvalidator cacheInvalidator;
private final CacheableRequestPolicy cacheableRequestPolicy;
@ -93,17 +96,18 @@ public class CachingHttpClient implements HttpClient {
private final Log log = LogFactory.getLog(getClass());
public CachingHttpClient(HttpClient client, HttpCache<String, CacheEntry> cache, int maxObjectSizeBytes) {
public CachingHttpClient(HttpClient client, HttpCache cache, int maxObjectSizeBytes) {
super();
this.responseCache = cache;
this.backend = client;
this.responseCache = cache;
this.validityPolicy = new CacheValidityPolicy();
this.responseCachingPolicy = new ResponseCachingPolicy(maxObjectSizeBytes);
this.responseGenerator = new CachedHttpResponseGenerator(this.validityPolicy);
this.cacheEntryGenerator = new CacheEntryGenerator();
this.uriExtractor = new URIExtractor();
this.responseGenerator = new CachedHttpResponseGenerator();
this.cacheInvalidator = new CacheInvalidator(this.uriExtractor, this.responseCache);
this.cacheableRequestPolicy = new CacheableRequestPolicy();
this.suitabilityChecker = new CachedResponseSuitabilityChecker();
this.suitabilityChecker = new CachedResponseSuitabilityChecker(this.validityPolicy);
this.conditionalRequestBuilder = new ConditionalRequestBuilder();
this.cacheEntryUpdater = new CacheEntryUpdater();
this.maxObjectSizeBytes = maxObjectSizeBytes;
@ -115,13 +119,13 @@ public class CachingHttpClient implements HttpClient {
this(new DefaultHttpClient(), new BasicHttpCache(MAX_CACHE_ENTRIES), DEFAULT_MAX_OBJECT_SIZE_BYTES);
}
public CachingHttpClient(HttpCache<String, CacheEntry> cache, int maxObjectSizeBytes) {
public CachingHttpClient(HttpCache cache, int maxObjectSizeBytes) {
this(new DefaultHttpClient(), cache, maxObjectSizeBytes);
}
CachingHttpClient(HttpClient backend, ResponseCachingPolicy responseCachingPolicy,
CachingHttpClient(HttpClient backend, CacheValidityPolicy validityPolicy, ResponseCachingPolicy responseCachingPolicy,
CacheEntryGenerator cacheEntryGenerator, URIExtractor uriExtractor,
HttpCache<String, CacheEntry> responseCache, CachedHttpResponseGenerator responseGenerator,
HttpCache responseCache, CachedHttpResponseGenerator responseGenerator,
CacheInvalidator cacheInvalidator, CacheableRequestPolicy cacheableRequestPolicy,
CachedResponseSuitabilityChecker suitabilityChecker,
ConditionalRequestBuilder conditionalRequestBuilder, CacheEntryUpdater entryUpdater,
@ -129,6 +133,7 @@ public class CachingHttpClient implements HttpClient {
RequestProtocolCompliance requestCompliance) {
this.maxObjectSizeBytes = DEFAULT_MAX_OBJECT_SIZE_BYTES;
this.backend = backend;
this.validityPolicy = validityPolicy;
this.responseCachingPolicy = responseCachingPolicy;
this.cacheEntryGenerator = cacheEntryGenerator;
this.uriExtractor = uriExtractor;
@ -330,7 +335,7 @@ public class CachingHttpClient implements HttpClient {
return callBackend(target, request, context);
}
CacheEntry entry = getCacheEntry(target, request);
HttpCacheEntry entry = getCacheEntry(target, request);
if (entry == null) {
cacheMisses.getAndIncrement();
if (log.isDebugEnabled()) {
@ -352,14 +357,14 @@ public class CachingHttpClient implements HttpClient {
return responseGenerator.generateResponse(entry);
}
if (entry.isRevalidatable()) {
if (validityPolicy.isRevalidatable(entry)) {
log.debug("Revalidating the cache entry");
try {
return revalidateCacheEntry(target, request, context, entry);
} catch (IOException ioex) {
if (entry.mustRevalidate()
|| (isSharedCache() && entry.proxyRevalidate())) {
if (validityPolicy.mustRevalidate(entry)
|| (isSharedCache() && validityPolicy.proxyRevalidate(entry))) {
return new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout");
} else {
HttpResponse response = responseGenerator.generateResponse(entry);
@ -386,17 +391,18 @@ public class CachingHttpClient implements HttpClient {
return new Date();
}
CacheEntry getCacheEntry(HttpHost target, HttpRequest request) {
HttpCacheEntry getCacheEntry(HttpHost target, HttpRequest request) {
String uri = uriExtractor.getURI(target, request);
CacheEntry entry = null;
HttpCacheEntry entry = null;
try {
entry = responseCache.getEntry(uri);
} catch (HttpCacheOperationException ex) {
log.debug("Was unable to get an entry from the cache based on the uri provided", ex);
}
if (entry == null || !entry.hasVariants())
if (entry == null || !entry.hasVariants()) {
return entry;
}
String variantUri = uriExtractor.getVariantURI(target, request, entry);
try {
@ -445,7 +451,7 @@ public class CachingHttpClient implements HttpClient {
HttpHost target,
HttpRequest request,
HttpContext context,
CacheEntry cacheEntry) throws IOException, ProtocolException {
HttpCacheEntry cacheEntry) throws IOException, ProtocolException {
HttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequest(request, cacheEntry);
Date requestDate = getCurrentDate();
@ -456,7 +462,7 @@ public class CachingHttpClient implements HttpClient {
int statusCode = backendResponse.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) {
cacheUpdates.getAndIncrement();
CacheEntry updatedEntry = cacheEntryUpdater.updateCacheEntry(cacheEntry, requestDate, responseDate, backendResponse);
HttpCacheEntry updatedEntry = cacheEntryUpdater.updateCacheEntry(cacheEntry, requestDate, responseDate, backendResponse);
storeInCache(target, request, updatedEntry);
return responseGenerator.generateResponse(updatedEntry);
}
@ -465,7 +471,7 @@ public class CachingHttpClient implements HttpClient {
backendResponse);
}
void storeInCache(HttpHost target, HttpRequest request, CacheEntry entry) {
void storeInCache(HttpHost target, HttpRequest request, HttpCacheEntry entry) {
if (entry.hasVariants()) {
storeVariantEntry(target, request, entry);
} else {
@ -473,7 +479,7 @@ public class CachingHttpClient implements HttpClient {
}
}
void storeNonVariantEntry(HttpHost target, HttpRequest req, CacheEntry entry) {
void storeNonVariantEntry(HttpHost target, HttpRequest req, HttpCacheEntry entry) {
String uri = uriExtractor.getURI(target, req);
try {
responseCache.putEntry(uri, entry);
@ -485,7 +491,7 @@ public class CachingHttpClient implements HttpClient {
void storeVariantEntry(
final HttpHost target,
final HttpRequest req,
final CacheEntry entry) {
final HttpCacheEntry entry) {
final String variantURI = uriExtractor.getVariantURI(target, req, entry);
try {
responseCache.putEntry(variantURI, entry);
@ -493,9 +499,9 @@ public class CachingHttpClient implements HttpClient {
log.debug("Was unable to PUT a variant entry into the cache based on the uri provided", e);
}
HttpCacheUpdateCallback<CacheEntry> callback = new HttpCacheUpdateCallback<CacheEntry>() {
HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
public CacheEntry update(CacheEntry existing) throws HttpCacheOperationException {
public HttpCacheEntry update(HttpCacheEntry existing) throws HttpCacheOperationException {
return doGetUpdatedParentEntry(existing, entry, variantURI);
}
@ -508,13 +514,13 @@ public class CachingHttpClient implements HttpClient {
}
}
CacheEntry doGetUpdatedParentEntry(
CacheEntry existing,
CacheEntry entry, String variantURI) throws HttpCacheOperationException {
HttpCacheEntry doGetUpdatedParentEntry(
HttpCacheEntry existing,
HttpCacheEntry entry, String variantURI) throws HttpCacheOperationException {
if (existing != null) {
return existing.copyWithVariant(variantURI);
return HttpCacheEntry.copyWithVariant(existing, variantURI);
} else {
return entry.copyWithVariant(variantURI);
return HttpCacheEntry.copyWithVariant(entry, variantURI);
}
}
@ -570,7 +576,7 @@ public class CachingHttpClient implements HttpClient {
responseBytes);
int correctedStatus = corrected.getStatusLine().getStatusCode();
if (HttpStatus.SC_BAD_GATEWAY != correctedStatus) {
CacheEntry entry = cacheEntryGenerator
HttpCacheEntry entry = cacheEntryGenerator
.generateEntry(requestDate, responseDate, corrected,
responseBytes);
storeInCache(target, request, entry);

View File

@ -31,6 +31,8 @@ import org.apache.http.HeaderElement;
import org.apache.http.HttpRequest;
import org.apache.http.ProtocolException;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.impl.client.RequestWrapper;
/**
@ -40,7 +42,7 @@ import org.apache.http.impl.client.RequestWrapper;
class ConditionalRequestBuilder {
/**
* When a {@link CacheEntry} is stale but 'might' be used as a response
* When a {@link HttpCacheEntry} is stale but 'might' be used as a response
* to an {@link HttpRequest} we will attempt to revalidate the entry with
* the origin. Build the origin {@link HttpRequest} here and return it.
*
@ -49,7 +51,7 @@ class ConditionalRequestBuilder {
* @return the wrapped request
* @throws ProtocolException when I am unable to build a new origin request.
*/
public HttpRequest buildConditionalRequest(HttpRequest request, CacheEntry cacheEntry)
public HttpRequest buildConditionalRequest(HttpRequest request, HttpCacheEntry cacheEntry)
throws ProtocolException {
RequestWrapper wrapperRequest = new RequestWrapper(request);
wrapperRequest.resetHeaders();

View File

@ -39,6 +39,7 @@ import org.apache.http.HttpVersion;
import org.apache.http.ProtocolException;
import org.apache.http.ProtocolVersion;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.impl.client.RequestWrapper;
import org.apache.http.message.BasicHeader;

View File

@ -36,6 +36,7 @@ import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.impl.cookie.DateParseException;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.protocol.HTTP;

View File

@ -36,6 +36,7 @@ import org.apache.http.HttpVersion;
import org.apache.http.ProtocolVersion;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.impl.client.RequestWrapper;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.protocol.HTTP;

View File

@ -37,6 +37,8 @@ import org.apache.http.HeaderElement;
import org.apache.http.HttpHost;
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;
import org.apache.http.protocol.HTTP;
/**
@ -84,7 +86,7 @@ class URIExtractor {
* @param entry the parent entry used to track the varients
* @return String the extracted variant URI
*/
public String getVariantURI(HttpHost host, HttpRequest req, CacheEntry entry) {
public String getVariantURI(HttpHost host, HttpRequest req, HttpCacheEntry entry) {
Header[] varyHdrs = entry.getHeaders(HeaderConstants.VARY);
if (varyHdrs == null || varyHdrs.length == 0) {
return getURI(host, req);

View File

@ -0,0 +1,84 @@
/*
* ====================================================================
* 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 java.util.Date;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.entity.ByteArrayEntity;
public class CacheEntry extends HttpCacheEntry {
private static final long serialVersionUID = 7964121802841871079L;
public static final long MAX_AGE = CacheValidityPolicy.MAX_AGE;
public CacheEntry(
Date requestDate,
Date responseDate,
StatusLine statusLine,
Header[] responseHeaders,
HttpEntity body) {
super(requestDate, responseDate, statusLine, responseHeaders, body, null);
}
public CacheEntry(
Date requestDate,
Date responseDate) {
super(requestDate, responseDate, new OKStatus(), new Header[] {}, new BasicHttpEntity(), null);
}
public CacheEntry(
Date requestDate,
Date responseDate,
Header[] headers) {
super(requestDate, responseDate, new OKStatus(), headers, new BasicHttpEntity(), null);
}
public CacheEntry(Header[] headers) {
super(new Date(), new Date(), new OKStatus(), headers, new BasicHttpEntity(), null);
}
public CacheEntry(
Header[] headers,
byte[] content) {
super(new Date(), new Date(), new OKStatus(), headers, new ByteArrayEntity(content), null);
}
public CacheEntry() {
this(new Date(), new Date());
}
public CacheEntry(byte[] content) {
super(new Date(), new Date(), new OKStatus(), new Header[] {}, new ByteArrayEntity(content), null);
}
}

View File

@ -62,13 +62,12 @@ public class DoNotTestProtocolRequirements {
private HttpHost host;
private HttpEntity mockEntity;
private HttpClient mockBackend;
private HttpCache<String, CacheEntry> mockCache;
private HttpCache mockCache;
private HttpRequest request;
private HttpResponse originResponse;
private CachingHttpClient impl;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
host = new HttpHost("foo.example.com");
@ -77,7 +76,7 @@ public class DoNotTestProtocolRequirements {
originResponse = make200Response();
HttpCache<String, CacheEntry> cache = new BasicHttpCache(MAX_ENTRIES);
HttpCache cache = new BasicHttpCache(MAX_ENTRIES);
mockBackend = EasyMock.createMock(HttpClient.class);
mockEntity = EasyMock.createMock(HttpEntity.class);
mockCache = EasyMock.createMock(HttpCache.class);

View File

@ -0,0 +1,39 @@
/*
* ====================================================================
* 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 org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.message.BasicStatusLine;
public class OKStatus extends BasicStatusLine {
public OKStatus() {
super(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
}
}

View File

@ -26,38 +26,29 @@
*/
package org.apache.http.impl.client.cache;
import java.util.Date;
import java.util.Set;
import org.apache.http.Header;
import org.apache.http.ProtocolVersion;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.message.BasicHeader;
import org.junit.Assert;
import org.junit.Test;
public class TestCacheEntry {
private static final ProtocolVersion HTTP_1_1 = new ProtocolVersion("HTTP",1,1);
@Test
public void testGetHeadersReturnsCorrectHeaders() {
Header[] headers = new Header[] { new BasicHeader("foo", "fooValue"),
new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") };
CacheEntry entry = getEntry(headers);
CacheEntry entry = new CacheEntry(headers);
Assert.assertEquals(2, entry.getHeaders("bar").length);
}
private CacheEntry getEntry(Header[] headers) {
return getEntry(new Date(), new Date(), headers);
}
@Test
public void testGetFirstHeaderReturnsCorrectHeader() {
Header[] headers = new Header[] { new BasicHeader("foo", "fooValue"),
new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") };
CacheEntry entry = getEntry(headers);
CacheEntry entry = new CacheEntry(headers);
Assert.assertEquals("barValue1", entry.getFirstHeader("bar").getValue());
}
@ -67,8 +58,7 @@ public class TestCacheEntry {
Header[] headers = new Header[] { new BasicHeader("foo", "fooValue"),
new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") };
CacheEntry entry = getEntry(headers);
CacheEntry entry = new CacheEntry(headers);
Assert.assertEquals(0, entry.getHeaders("baz").length);
}
@ -77,334 +67,23 @@ public class TestCacheEntry {
public void testGetFirstHeaderReturnsNullIfNoneMatch() {
Header[] headers = new Header[] { new BasicHeader("foo", "fooValue"),
new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") };
CacheEntry entry = getEntry(headers);
CacheEntry entry = new CacheEntry(headers);
Assert.assertEquals(null, entry.getFirstHeader("quux"));
}
@Test
public void testApparentAgeIsMaxIntIfDateHeaderNotPresent() {
Header[] headers = new Header[0];
CacheEntry entry = getEntry(headers);
Assert.assertEquals(2147483648L, entry.getApparentAgeSecs());
}
@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)) };
CacheEntry entry = getEntry(now, sixSecondsAgo, headers);
Assert.assertEquals(4, entry.getApparentAgeSecs());
}
private CacheEntry getEntry(Date requestDate, Date responseDate, Header[] headers) {
return new CacheEntry(requestDate, responseDate, HTTP_1_1, headers,
new ByteArrayEntity(new byte[] {}), 200, "OK");
}
@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)) };
CacheEntry entry = getEntry(now,tenSecondsAgo,headers);
Assert.assertEquals(0, entry.getApparentAgeSecs());
}
@Test
public void testCorrectedReceivedAgeIsAgeHeaderIfLarger() {
Header[] headers = new Header[] { new BasicHeader("Age", "10"), };
CacheEntry entry = new CacheEntry(new Date(), new Date(), HTTP_1_1,
headers, new ByteArrayEntity(new byte[] {}), 200, "OK") {
private static final long serialVersionUID = 1L;
@Override
protected long getApparentAgeSecs() {
return 6;
}
};
Assert.assertEquals(10, entry.getCorrectedReceivedAgeSecs());
}
@Test
public void testCorrectedReceivedAgeIsApparentAgeIfLarger() {
Header[] headers = new Header[] { new BasicHeader("Age", "6"), };
CacheEntry entry = new CacheEntry(new Date(), new Date(), HTTP_1_1,
headers, new ByteArrayEntity(new byte[] {}), 200 ,"OK") {
private static final long serialVersionUID = 1L;
@Override
protected long getApparentAgeSecs() {
return 10;
}
};
Assert.assertEquals(10, entry.getCorrectedReceivedAgeSecs());
}
@Test
public void testResponseDelayIsDifferenceBetweenResponseAndRequestTimes() {
Date now = new Date();
Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
Header[] headers = new Header[]{};
CacheEntry entry = new CacheEntry(tenSecondsAgo, sixSecondsAgo,
new ProtocolVersion("HTTP",1,1), headers, new ByteArrayEntity(new byte[] {}),
200, "OK");
Assert.assertEquals(4, entry.getResponseDelaySecs());
}
@Test
public void testCorrectedInitialAgeIsCorrectedReceivedAgePlusResponseDelay() {
CacheEntry entry = new CacheEntry(new Date(), new Date(), HTTP_1_1, new Header[] {},
new ByteArrayEntity(new byte[] {}), 200, "OK") {
private static final long serialVersionUID = 1L;
@Override
protected long getCorrectedReceivedAgeSecs() {
return 7;
}
@Override
protected long getResponseDelaySecs() {
return 13;
}
};
Assert.assertEquals(20, entry.getCorrectedInitialAgeSecs());
}
@Test
public void testResidentTimeSecondsIsTimeSinceResponseTime() {
final Date now = new Date();
Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
CacheEntry entry = new CacheEntry(new Date(), sixSecondsAgo, HTTP_1_1, new Header[]{},
new ByteArrayEntity(new byte[] {}), 200, "OK") {
private static final long serialVersionUID = 1L;
@Override
protected Date getCurrentDate() {
return now;
}
};
Assert.assertEquals(6, entry.getResidentTimeSecs());
}
@Test
public void testCurrentAgeIsCorrectedInitialAgePlusResidentTime() {
CacheEntry entry = new CacheEntry(new Date(), new Date(), HTTP_1_1, new Header[]{},
new ByteArrayEntity(new byte[] {}), 200, "OK") {
private static final long serialVersionUID = 1L;
@Override
protected long getCorrectedInitialAgeSecs() {
return 11;
}
@Override
protected long getResidentTimeSecs() {
return 17;
}
};
Assert.assertEquals(28, entry.getCurrentAgeSecs());
}
@Test
public void testFreshnessLifetimeIsSMaxAgeIfPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "s-maxage=10") };
CacheEntry entry = getEntry(headers);
Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
}
@Test
public void testFreshnessLifetimeIsMaxAgeIfPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10") };
CacheEntry entry = getEntry(headers);
Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
}
@Test
public void testFreshnessLifetimeIsMostRestrictiveOfMaxAgeAndSMaxAge() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10"),
new BasicHeader("Cache-Control", "s-maxage=20") };
CacheEntry entry = getEntry(headers);
Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
headers = new Header[] { new BasicHeader("Cache-Control", "max-age=20"),
new BasicHeader("Cache-Control", "s-maxage=10") };
entry = getEntry(headers);
Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
}
@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)) };
CacheEntry entry = getEntry(headers);
Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
}
@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)) };
CacheEntry entry = getEntry(headers);
Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
}
@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)) };
CacheEntry entry = getEntry(headers);
Assert.assertEquals(4, entry.getFreshnessLifetimeSecs());
}
@Test
public void testResponseIsFreshIfFreshnessLifetimeExceedsCurrentAge() {
CacheEntry entry = new CacheEntry(new Date(), new Date(), HTTP_1_1, new Header[]{},
new ByteArrayEntity(new byte[] {}), 200, "OK") {
private static final long serialVersionUID = 1L;
@Override
public long getCurrentAgeSecs() {
return 6;
}
@Override
public long getFreshnessLifetimeSecs() {
return 10;
}
};
Assert.assertTrue(entry.isResponseFresh());
}
@Test
public void testResponseIsNotFreshIfFreshnessLifetimeEqualsCurrentAge() {
CacheEntry entry = new CacheEntry(new Date(), new Date(), HTTP_1_1, new Header[]{},
new ByteArrayEntity(new byte[] {}), 200, "OK") {
private static final long serialVersionUID = 1L;
@Override
public long getCurrentAgeSecs() {
return 6;
}
@Override
public long getFreshnessLifetimeSecs() {
return 6;
}
};
Assert.assertFalse(entry.isResponseFresh());
}
@Test
public void testResponseIsNotFreshIfCurrentAgeExceedsFreshnessLifetime() {
CacheEntry entry = new CacheEntry(new Date(), new Date(), HTTP_1_1, new Header[] {},
new ByteArrayEntity(new byte[] {}), 200, "OK") {
private static final long serialVersionUID = 1L;
@Override
public long getCurrentAgeSecs() {
return 10;
}
@Override
public long getFreshnessLifetimeSecs() {
return 6;
}
};
Assert.assertFalse(entry.isResponseFresh());
}
@Test
public void testCacheEntryIsRevalidatableIfHeadersIncludeETag() {
Header[] headers = {
new BasicHeader("Expires", DateUtils.formatDate(new Date())),
new BasicHeader("ETag", "somevalue")};
CacheEntry entry = getEntry(headers);
Assert.assertTrue(entry.isRevalidatable());
}
@Test
public void testCacheEntryIsRevalidatableIfHeadersIncludeLastModifiedDate() {
Header[] headers = {
new BasicHeader("Expires", DateUtils.formatDate(new Date())),
new BasicHeader("Last-Modified", DateUtils.formatDate(new Date())) };
CacheEntry entry = getEntry(headers);
Assert.assertTrue(entry.isRevalidatable());
}
@Test
public void testCacheEntryIsNotRevalidatableIfNoAppropriateHeaders() {
Header[] headers = {
new BasicHeader("Expires", DateUtils.formatDate(new Date())),
new BasicHeader("Cache-Control", "public") };
CacheEntry entry = getEntry(headers);
Assert.assertFalse(entry.isRevalidatable());
}
@Test
public void testCacheEntryWithNoVaryHeaderDoesNotHaveVariants() {
Header[] headers = new Header[0];
CacheEntry entry = getEntry(headers);
CacheEntry entry = new CacheEntry(headers);
Assert.assertFalse(entry.hasVariants());
}
@Test
public void testCacheEntryWithOneVaryHeaderHasVariants() {
Header[] headers = { new BasicHeader("Vary", "User-Agent") };
CacheEntry entry = getEntry(headers);
CacheEntry entry = new CacheEntry(headers);
Assert.assertTrue(entry.hasVariants());
}
@ -412,14 +91,14 @@ public class TestCacheEntry {
public void testCacheEntryWithMultipleVaryHeadersHasVariants() {
Header[] headers = { new BasicHeader("Vary", "User-Agent"),
new BasicHeader("Vary", "Accept-Encoding") };
CacheEntry entry = getEntry(headers);
CacheEntry entry = new CacheEntry(headers);
Assert.assertTrue(entry.hasVariants());
}
@Test
public void testCacheEntryWithVaryStarHasVariants(){
Header[] headers = { new BasicHeader("Vary", "*") };
CacheEntry entry = getEntry(headers);
CacheEntry entry = new CacheEntry(headers);
Assert.assertTrue(entry.hasVariants());
}
@ -427,10 +106,10 @@ public class TestCacheEntry {
public void testCacheEntryCanStoreMultipleVariantUris() {
Header[] headers = new Header[]{};
CacheEntry entry = getEntry(headers);
CacheEntry entry = new CacheEntry(headers);
CacheEntry addedOne = entry.copyWithVariant("foo");
CacheEntry addedTwo = addedOne.copyWithVariant("bar");
HttpCacheEntry addedOne = HttpCacheEntry.copyWithVariant(entry, "foo");
HttpCacheEntry addedTwo = HttpCacheEntry.copyWithVariant(addedOne, "bar");
Set<String> variants = addedTwo.getVariantURIs();
@ -438,99 +117,4 @@ public class TestCacheEntry {
Assert.assertTrue(variants.contains("bar"));
}
@Test
public void testMalformedDateHeaderIsIgnored() {
Header[] headers = new Header[] { new BasicHeader("Date", "asdf") };
CacheEntry entry = getEntry(headers);
Date d = entry.getDateValue();
Assert.assertNull(d);
}
@Test
public void testMalformedContentLengthReturnsNegativeOne() {
Header[] headers = new Header[] { new BasicHeader("Content-Length", "asdf") };
CacheEntry entry = getEntry(headers);
long length = entry.getContentLengthValue();
Assert.assertEquals(-1, length);
}
@Test
public void testNegativeAgeHeaderValueReturnsMaxAge() {
Header[] headers = new Header[] { new BasicHeader("Age", "-100") };
CacheEntry entry = getEntry(headers);
long length = entry.getAgeValue();
Assert.assertEquals(CacheEntry.MAX_AGE, length);
}
@Test
public void testMalformedAgeHeaderValueReturnsMaxAge() {
Header[] headers = new Header[] { new BasicHeader("Age", "asdf") };
CacheEntry entry = getEntry(headers);
long length = entry.getAgeValue();
Assert.assertEquals(CacheEntry.MAX_AGE, length);
}
@Test
public void testMalformedCacheControlMaxAgeHeaderReturnsZero() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=asdf") };
CacheEntry entry = getEntry(headers);
long maxage = entry.getMaxAge();
Assert.assertEquals(0, maxage);
}
@Test
public void testMalformedExpirationDateReturnsNull() {
Header[] headers = new Header[] { new BasicHeader("Expires", "asdf") };
CacheEntry entry = getEntry(headers);
Date expirationDate = entry.getExpirationDate();
Assert.assertNull(expirationDate);
}
@Test
public void testMustRevalidateIsFalseIfDirectiveNotPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public") };
CacheEntry entry = getEntry(headers);
Assert.assertFalse(entry.mustRevalidate());
}
@Test
public void testMustRevalidateIsTrueWhenDirectiveIsPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public, must-revalidate") };
CacheEntry entry = getEntry(headers);
Assert.assertTrue(entry.mustRevalidate());
}
@Test
public void testProxyRevalidateIsFalseIfDirectiveNotPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public") };
CacheEntry entry = getEntry(headers);
Assert.assertFalse(entry.proxyRevalidate());
}
@Test
public void testProxyRevalidateIsTrueWhenDirectiveIsPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public, proxy-revalidate") };
CacheEntry entry = getEntry(headers);
Assert.assertTrue(entry.proxyRevalidate());
}
}

View File

@ -32,6 +32,7 @@ import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.message.BasicHttpResponse;
import org.junit.Assert;
@ -51,7 +52,7 @@ public class TestCacheEntryGenerator {
response.setHeader("fooHeader", "fooHeaderValue");
CacheEntry entry = gen.generateEntry(new Date(), new Date(), response, new byte[] {});
HttpCacheEntry entry = gen.generateEntry(new Date(), new Date(), response, new byte[] {});
Assert.assertEquals("HTTP", entry.getProtocolVersion().getProtocol());
Assert.assertEquals(1, entry.getProtocolVersion().getMajor());

View File

@ -29,13 +29,13 @@ package org.apache.http.impl.client.cache;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.ProtocolVersion;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;
import org.easymock.classextension.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -48,59 +48,28 @@ import static junit.framework.Assert.assertNotSame;
public class TestCacheEntryUpdater {
private static final ProtocolVersion HTTP_1_1 = new ProtocolVersion("HTTP", 1, 1);
private HttpResponse mockResponse;
private CacheEntry mockCacheEntry;
private Date requestDate;
private Date responseDate;
private boolean implMocked = false;
private CacheEntryUpdater impl;
@Before
public void setUp() throws Exception {
mockResponse = EasyMock.createMock(HttpResponse.class);
mockCacheEntry = EasyMock.createMock(CacheEntry.class);
requestDate = new Date(System.currentTimeMillis() - 1000);
responseDate = new Date();
impl = new CacheEntryUpdater();
}
private void replayMocks() {
EasyMock.replay(mockResponse);
EasyMock.replay(mockCacheEntry);
if (implMocked) {
EasyMock.replay(impl);
}
}
private void verifyMocks() {
EasyMock.verify(mockResponse);
EasyMock.verify(mockCacheEntry);
if (implMocked) {
EasyMock.verify(impl);
}
}
@Test
public void testUpdateCacheEntryReturnsDifferentEntryInstance() throws IOException {
CacheEntry entry = getEntry(new Header[]{});
BasicHttpResponse response = new BasicHttpResponse(HTTP_1_1, 200, "OK");
CacheEntry entry = new CacheEntry();
BasicHttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
replayMocks();
CacheEntry newEntry = impl.updateCacheEntry(entry, requestDate, responseDate, response);
verifyMocks();
HttpCacheEntry newEntry = impl.updateCacheEntry(entry, requestDate, responseDate, response);
assertNotSame(newEntry, entry);
}
@Test
@ -110,13 +79,13 @@ public class TestCacheEntryUpdater {
new BasicHeader("Date", DateUtils.formatDate(responseDate)),
new BasicHeader("ETag", "\"etag\"")};
CacheEntry cacheEntry = getEntry(headers);
CacheEntry cacheEntry = new CacheEntry(headers);
HttpResponse response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion(
"http", 1, 1), HttpStatus.SC_NOT_MODIFIED, ""));
response.setHeaders(new Header[]{});
CacheEntry updatedEntry = impl.updateCacheEntry(cacheEntry, new Date(), new Date(), response);
HttpCacheEntry updatedEntry = impl.updateCacheEntry(cacheEntry, new Date(), new Date(), response);
Assert.assertEquals(2, updatedEntry.getAllHeaders().length);
@ -133,7 +102,7 @@ public class TestCacheEntryUpdater {
new BasicHeader("Cache-Control", "private"), new BasicHeader("ETag", "\"etag\""),
new BasicHeader("Last-Modified", DateUtils.formatDate(requestDate)),
new BasicHeader("Cache-Control", "max-age=0"),};
CacheEntry cacheEntry = getEntry(headers);
CacheEntry cacheEntry = new CacheEntry(headers);
HttpResponse response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion(
"http", 1, 1), HttpStatus.SC_NOT_MODIFIED, ""));
@ -141,7 +110,7 @@ public class TestCacheEntryUpdater {
new BasicHeader("Last-Modified", DateUtils.formatDate(responseDate)),
new BasicHeader("Cache-Control", "public"),});
CacheEntry updatedEntry = impl.updateCacheEntry(cacheEntry, new Date(), new Date(), response);
HttpCacheEntry updatedEntry = impl.updateCacheEntry(cacheEntry, new Date(), new Date(), response);
Assert.assertEquals(4, updatedEntry.getAllHeaders().length);
@ -160,14 +129,14 @@ public class TestCacheEntryUpdater {
new BasicHeader("Date", DateUtils.formatDate(requestDate)),
new BasicHeader("ETag", "\"etag\"")};
CacheEntry cacheEntry = getEntry(headers);
CacheEntry cacheEntry = new CacheEntry(headers);
HttpResponse response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion(
"http", 1, 1), HttpStatus.SC_NOT_MODIFIED, ""));
response.setHeaders(new Header[]{
new BasicHeader("Last-Modified", DateUtils.formatDate(responseDate)),
new BasicHeader("Cache-Control", "public"),});
CacheEntry updatedEntry = impl.updateCacheEntry(cacheEntry, new Date(), new Date(), response);
HttpCacheEntry updatedEntry = impl.updateCacheEntry(cacheEntry, new Date(), new Date(), response);
Assert.assertEquals(4, updatedEntry.getAllHeaders().length);
@ -191,23 +160,17 @@ public class TestCacheEntryUpdater {
Date twoSecondsAgo = new Date(now.getTime() - 2000L);
Date oneSecondAgo = new Date(now.getTime() - 1000L);
Header[] headers = new Header[]{};
CacheEntry entry = new CacheEntry(tenSecondsAgo, eightSecondsAgo);
CacheEntry entry = new CacheEntry(tenSecondsAgo, eightSecondsAgo, HTTP_1_1, headers,
new ByteArrayEntity(new byte[] {}), 200, "OK");
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
HttpResponse response = new BasicHttpResponse(HTTP_1_1, 200, "OK");
CacheEntry updated = impl.updateCacheEntry(entry, twoSecondsAgo, oneSecondAgo, response);
HttpCacheEntry updated = impl.updateCacheEntry(entry, twoSecondsAgo, oneSecondAgo, response);
assertEquals(twoSecondsAgo, updated.getRequestDate());
assertEquals(oneSecondAgo, updated.getResponseDate());
}
// UTILITY
private void headersContain(Header[] headers, String name, String value) {
for (Header header : headers) {
if (header.getName().equals(name)) {
@ -219,13 +182,4 @@ public class TestCacheEntryUpdater {
Assert.fail("Header [" + name + ": " + value + "] not found in headers.");
}
private CacheEntry getEntry(Header[] headers) {
return getEntry(new Date(), new Date(), headers);
}
private CacheEntry getEntry(Date requestDate, Date responseDate, Header[] headers) {
return new CacheEntry(requestDate, responseDate, HTTP_1_1, headers,
new ByteArrayEntity(new byte[] {}), 200, "OK");
}
}

View File

@ -46,14 +46,11 @@ public class TestCacheInvalidator {
private static final ProtocolVersion HTTP_1_1 = new ProtocolVersion("HTTP", 1, 1);
private CacheInvalidator impl;
private HttpCache<String, CacheEntry> mockCache;
private HttpCache mockCache;
private HttpHost host;
private URIExtractor extractor;
private CacheEntry mockEntry;
private boolean mockedImpl;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
host = new HttpHost("foo.example.com");
@ -67,17 +64,11 @@ public class TestCacheInvalidator {
private void replayMocks() {
EasyMock.replay(mockCache);
EasyMock.replay(mockEntry);
if (mockedImpl)
EasyMock.replay(impl);
}
private void verifyMocks() {
EasyMock.verify(mockCache);
EasyMock.verify(mockEntry);
if (mockedImpl)
EasyMock.verify(impl);
}
// Tests

View File

@ -0,0 +1,453 @@
/*
* ====================================================================
* 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 java.util.Date;
import org.apache.http.Header;
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.junit.Test;
public class TestCacheValidityPolicy {
@Test
public void testApparentAgeIsMaxIntIfDateHeaderNotPresent() {
CacheEntry entry = new CacheEntry();
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.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)) };
CacheEntry entry = new CacheEntry(now, sixSecondsAgo, headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.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)) };
CacheEntry entry = new CacheEntry(now,tenSecondsAgo,headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertEquals(0, impl.getApparentAgeSecs(entry));
}
@Test
public void testCorrectedReceivedAgeIsAgeHeaderIfLarger() {
Header[] headers = new Header[] { new BasicHeader("Age", "10"), };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy() {
@Override
protected long getApparentAgeSecs(HttpCacheEntry entry) {
return 6;
}
};
Assert.assertEquals(10, impl.getCorrectedReceivedAgeSecs(entry));
}
@Test
public void testCorrectedReceivedAgeIsApparentAgeIfLarger() {
Header[] headers = new Header[] { new BasicHeader("Age", "6"), };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy() {
@Override
protected long getApparentAgeSecs(HttpCacheEntry entry) {
return 10;
}
};
Assert.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);
CacheEntry entry = new CacheEntry(tenSecondsAgo, sixSecondsAgo);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertEquals(4, impl.getResponseDelaySecs(entry));
}
@Test
public void testCorrectedInitialAgeIsCorrectedReceivedAgePlusResponseDelay() {
CacheEntry entry = new CacheEntry();
CacheValidityPolicy impl = new CacheValidityPolicy() {
@Override
protected long getCorrectedReceivedAgeSecs(HttpCacheEntry entry) {
return 7;
}
@Override
protected long getResponseDelaySecs(HttpCacheEntry entry) {
return 13;
}
};
Assert.assertEquals(20, impl.getCorrectedInitialAgeSecs(entry));
}
@Test
public void testResidentTimeSecondsIsTimeSinceResponseTime() {
final Date now = new Date();
final Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
CacheEntry entry = new CacheEntry(now, sixSecondsAgo);
CacheValidityPolicy impl = new CacheValidityPolicy() {
@Override
protected Date getCurrentDate() {
return now;
}
};
Assert.assertEquals(6, impl.getResidentTimeSecs(entry));
}
@Test
public void testCurrentAgeIsCorrectedInitialAgePlusResidentTime() {
CacheEntry entry = new CacheEntry();
CacheValidityPolicy impl = new CacheValidityPolicy() {
@Override
protected long getCorrectedInitialAgeSecs(HttpCacheEntry entry) {
return 11;
}
@Override
protected long getResidentTimeSecs(HttpCacheEntry entry) {
return 17;
}
};
Assert.assertEquals(28, impl.getCurrentAgeSecs(entry));
}
@Test
public void testFreshnessLifetimeIsSMaxAgeIfPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "s-maxage=10") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
}
@Test
public void testFreshnessLifetimeIsMaxAgeIfPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
}
@Test
public void testFreshnessLifetimeIsMostRestrictiveOfMaxAgeAndSMaxAge() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10"),
new BasicHeader("Cache-Control", "s-maxage=20") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertEquals(10, impl.getFreshnessLifetimeSecs(entry));
headers = new Header[] { new BasicHeader("Cache-Control", "max-age=20"),
new BasicHeader("Cache-Control", "s-maxage=10") };
entry = new CacheEntry(headers);
Assert.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)) };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.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)) };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.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)) };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertEquals(4, impl.getFreshnessLifetimeSecs(entry));
}
@Test
public void testResponseIsFreshIfFreshnessLifetimeExceedsCurrentAge() {
CacheEntry entry = new CacheEntry();
CacheValidityPolicy impl = new CacheValidityPolicy() {
@Override
public long getCurrentAgeSecs(HttpCacheEntry entry) {
return 6;
}
@Override
public long getFreshnessLifetimeSecs(HttpCacheEntry entry) {
return 10;
}
};
Assert.assertTrue(impl.isResponseFresh(entry));
}
@Test
public void testResponseIsNotFreshIfFreshnessLifetimeEqualsCurrentAge() {
CacheEntry entry = new CacheEntry();
CacheValidityPolicy impl = new CacheValidityPolicy() {
@Override
public long getCurrentAgeSecs(HttpCacheEntry entry) {
return 6;
}
@Override
public long getFreshnessLifetimeSecs(HttpCacheEntry entry) {
return 6;
}
};
Assert.assertFalse(impl.isResponseFresh(entry));
}
@Test
public void testResponseIsNotFreshIfCurrentAgeExceedsFreshnessLifetime() {
CacheEntry entry = new CacheEntry();
CacheValidityPolicy impl = new CacheValidityPolicy() {
@Override
public long getCurrentAgeSecs(HttpCacheEntry entry) {
return 10;
}
@Override
public long getFreshnessLifetimeSecs(HttpCacheEntry entry) {
return 6;
}
};
Assert.assertFalse(impl.isResponseFresh(entry));
}
@Test
public void testCacheEntryIsRevalidatableIfHeadersIncludeETag() {
Header[] headers = {
new BasicHeader("Expires", DateUtils.formatDate(new Date())),
new BasicHeader("ETag", "somevalue")};
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.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())) };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertTrue(impl.isRevalidatable(entry));
}
@Test
public void testCacheEntryIsNotRevalidatableIfNoAppropriateHeaders() {
Header[] headers = {
new BasicHeader("Expires", DateUtils.formatDate(new Date())),
new BasicHeader("Cache-Control", "public") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertFalse(impl.isRevalidatable(entry));
}
@Test
public void testMalformedDateHeaderIsIgnored() {
Header[] headers = new Header[] { new BasicHeader("Date", "asdf") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Date d = impl.getDateValue(entry);
Assert.assertNull(d);
}
@Test
public void testMalformedContentLengthReturnsNegativeOne() {
Header[] headers = new Header[] { new BasicHeader("Content-Length", "asdf") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
long length = impl.getContentLengthValue(entry);
Assert.assertEquals(-1, length);
}
@Test
public void testNegativeAgeHeaderValueReturnsMaxAge() {
Header[] headers = new Header[] { new BasicHeader("Age", "-100") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
long length = impl.getAgeValue(entry);
Assert.assertEquals(CacheEntry.MAX_AGE, length);
}
@Test
public void testMalformedAgeHeaderValueReturnsMaxAge() {
Header[] headers = new Header[] { new BasicHeader("Age", "asdf") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
long length = impl.getAgeValue(entry);
Assert.assertEquals(CacheEntry.MAX_AGE, length);
}
@Test
public void testMalformedCacheControlMaxAgeHeaderReturnsZero() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=asdf") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
long maxage = impl.getMaxAge(entry);
Assert.assertEquals(0, maxage);
}
@Test
public void testMalformedExpirationDateReturnsNull() {
Header[] headers = new Header[] { new BasicHeader("Expires", "asdf") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Date expirationDate = impl.getExpirationDate(entry);
Assert.assertNull(expirationDate);
}
@Test
public void testMustRevalidateIsFalseIfDirectiveNotPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertFalse(impl.mustRevalidate(entry));
}
@Test
public void testMustRevalidateIsTrueWhenDirectiveIsPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public, must-revalidate") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertTrue(impl.mustRevalidate(entry));
}
@Test
public void testProxyRevalidateIsFalseIfDirectiveNotPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertFalse(impl.proxyRevalidate(entry));
}
@Test
public void testProxyRevalidateIsTrueWhenDirectiveIsPresent() {
Header[] headers = new Header[] { new BasicHeader("Cache-Control","public, proxy-revalidate") };
CacheEntry entry = new CacheEntry(headers);
CacheValidityPolicy impl = new CacheValidityPolicy();
Assert.assertTrue(impl.proxyRevalidate(entry));
}
}

View File

@ -30,26 +30,45 @@ import java.util.Date;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.message.BasicHeader;
import org.easymock.classextension.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class TestCachedHttpResponseGenerator {
private CacheEntry entry;
private CacheValidityPolicy mockValidityPolicy;
private CachedHttpResponseGenerator impl;
@Before
public void setUp() {
Date now = new Date();
Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
Date eightSecondsAgo = new Date(now.getTime() - 8 * 1000L);
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
Date tenSecondsFromNow = new Date(now.getTime() + 10 * 1000L);
Header[] hdrs = { new BasicHeader("Date", DateUtils.formatDate(eightSecondsAgo)),
new BasicHeader("Expires", DateUtils.formatDate(tenSecondsFromNow)),
new BasicHeader("Content-Length", "150") };
entry = new CacheEntry(tenSecondsAgo, sixSecondsAgo, hdrs);
mockValidityPolicy = EasyMock.createMock(CacheValidityPolicy.class);
impl = new CachedHttpResponseGenerator(mockValidityPolicy);
}
public void replayMocks() {
EasyMock.replay(mockValidityPolicy);
}
@Test
public void testResponseHasContentLength() {
Header[] hdrs = new Header[] {};
byte[] buf = new byte[] { 1, 2, 3, 4, 5 };
CacheEntry entry = new CacheEntry(
new Date(), new Date(), new ProtocolVersion("HTTP", 1, 1), hdrs,
new ByteArrayEntity(buf), 200, "OK");
CacheEntry entry = new CacheEntry(buf);
CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
HttpResponse response = gen.generateResponse(entry);
HttpResponse response = impl.generateResponse(entry);
Header length = response.getFirstHeader("Content-Length");
Assert.assertNotNull("Content-Length Header is missing", length);
@ -63,13 +82,9 @@ public class TestCachedHttpResponseGenerator {
Header[] hdrs = new Header[] { new BasicHeader("Transfer-Encoding", "chunked") };
byte[] buf = new byte[] { 1, 2, 3, 4, 5 };
CacheEntry entry = new CacheEntry(
new Date(), new Date(), new ProtocolVersion("HTTP", 1, 1), hdrs,
new ByteArrayEntity(buf), 200, "OK");
CacheEntry entry = new CacheEntry(hdrs, buf);
CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
HttpResponse response = gen.generateResponse(entry);
HttpResponse response = impl.generateResponse(entry);
Header length = response.getFirstHeader("Content-Length");
@ -78,10 +93,7 @@ public class TestCachedHttpResponseGenerator {
@Test
public void testResponseMatchesCacheEntry() {
CacheEntry entry = buildEntry();
CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
HttpResponse response = gen.generateResponse(entry);
HttpResponse response = impl.generateResponse(entry);
Assert.assertTrue(response.containsHeader("Content-Length"));
@ -92,36 +104,29 @@ public class TestCachedHttpResponseGenerator {
@Test
public void testResponseStatusCodeMatchesCacheEntry() {
CacheEntry entry = buildEntry();
CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
HttpResponse response = gen.generateResponse(entry);
HttpResponse response = impl.generateResponse(entry);
Assert.assertEquals(entry.getStatusCode(), response.getStatusLine().getStatusCode());
}
@Test
public void testAgeHeaderIsPopulatedWithCurrentAgeOfCacheEntryIfNonZero() {
final long currAge = 10L;
currentAge(10L);
replayMocks();
CacheEntry entry = buildEntryWithCurrentAge(currAge);
CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
HttpResponse response = gen.generateResponse(entry);
HttpResponse response = impl.generateResponse(entry);
Header ageHdr = response.getFirstHeader("Age");
Assert.assertNotNull(ageHdr);
Assert.assertEquals(currAge, Long.parseLong(ageHdr.getValue()));
Assert.assertEquals(10L, Long.parseLong(ageHdr.getValue()));
}
@Test
public void testAgeHeaderIsNotPopulatedIfCurrentAgeOfCacheEntryIsZero() {
final long currAge = 0L;
currentAge(0L);
replayMocks();
CacheEntry entry = buildEntryWithCurrentAge(currAge);
CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
HttpResponse response = gen.generateResponse(entry);
HttpResponse response = impl.generateResponse(entry);
Header ageHdr = response.getFirstHeader("Age");
Assert.assertNull(ageHdr);
@ -129,53 +134,19 @@ public class TestCachedHttpResponseGenerator {
@Test
public void testAgeHeaderIsPopulatedWithMaxAgeIfCurrentAgeTooBig() {
currentAge(CacheEntry.MAX_AGE + 1L);
replayMocks();
CacheEntry entry = buildEntryWithCurrentAge(CacheEntry.MAX_AGE + 1L);
CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
HttpResponse response = gen.generateResponse(entry);
HttpResponse response = impl.generateResponse(entry);
Header ageHdr = response.getFirstHeader("Age");
Assert.assertNotNull(ageHdr);
Assert.assertEquals(CacheEntry.MAX_AGE, Long.parseLong(ageHdr.getValue()));
}
private CacheEntry buildEntry() {
Date now = new Date();
Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
Date eightSecondsAgo = new Date(now.getTime() - 8 * 1000L);
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
Date tenSecondsFromNow = new Date(now.getTime() + 10 * 1000L);
Header[] hdrs = { new BasicHeader("Date", DateUtils.formatDate(eightSecondsAgo)),
new BasicHeader("Expires", DateUtils.formatDate(tenSecondsFromNow)),
new BasicHeader("Content-Length", "150") };
return new CacheEntry(tenSecondsAgo, sixSecondsAgo, new ProtocolVersion("HTTP", 1, 1),
hdrs, new ByteArrayEntity(new byte[] {}), 200, "OK");
private void currentAge(long sec) {
EasyMock.expect(
mockValidityPolicy.getCurrentAgeSecs(entry)).andReturn(sec);
}
private CacheEntry buildEntryWithCurrentAge(final long currAge){
Date now = new Date();
Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
Date eightSecondsAgo = new Date(now.getTime() - 8 * 1000L);
Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
Date tenSecondsFromNow = new Date(now.getTime() + 10 * 1000L);
Header[] hdrs = { new BasicHeader("Date", DateUtils.formatDate(eightSecondsAgo)),
new BasicHeader("Expires", DateUtils.formatDate(tenSecondsFromNow)),
new BasicHeader("Content-Length", "150") };
return new CacheEntry(tenSecondsAgo, sixSecondsAgo, new ProtocolVersion("HTTP", 1, 1),
hdrs, new ByteArrayEntity(new byte[] {}), 200, "OK"){
private static final long serialVersionUID = 1L;
@Override
public long getCurrentAgeSecs() {
return currAge;
}
};
}
}

View File

@ -26,7 +26,6 @@
*/
package org.apache.http.impl.client.cache;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.message.BasicHeader;
@ -38,28 +37,28 @@ import org.junit.Test;
public class TestCachedResponseSuitabilityChecker {
private CachedResponseSuitabilityChecker impl;
private HttpHost host;
private HttpRequest request;
private CacheEntry mockEntry;
private HttpRequest mockRequest;
private CacheEntry entry;
private CacheValidityPolicy mockValidityPolicy;
private CachedResponseSuitabilityChecker impl;
@Before
public void setUp() {
host = new HttpHost("foo.example.com");
request = new BasicHttpRequest("GET", "/foo");
mockEntry = EasyMock.createMock(CacheEntry.class);
mockRequest = EasyMock.createMock(HttpRequest.class);
mockValidityPolicy = EasyMock.createMock(CacheValidityPolicy.class);
entry = new CacheEntry();
impl = new CachedResponseSuitabilityChecker();
impl = new CachedResponseSuitabilityChecker(mockValidityPolicy);
}
public void replayMocks() {
EasyMock.replay(mockEntry, mockRequest);
EasyMock.replay(mockValidityPolicy);
}
public void verifyMocks() {
EasyMock.verify(mockEntry, mockRequest);
EasyMock.verify(mockValidityPolicy);
}
@Test
@ -68,7 +67,7 @@ public class TestCachedResponseSuitabilityChecker {
contentLengthMatchesActualLength(false);
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, request, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
@ -82,7 +81,7 @@ public class TestCachedResponseSuitabilityChecker {
modifiedSince(false, request);
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, request, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
@ -97,7 +96,7 @@ public class TestCachedResponseSuitabilityChecker {
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, request, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
@ -109,7 +108,7 @@ public class TestCachedResponseSuitabilityChecker {
responseIsFresh(false);
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, request, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
@ -125,7 +124,7 @@ public class TestCachedResponseSuitabilityChecker {
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, request, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
Assert.assertFalse(result);
}
@ -136,11 +135,11 @@ public class TestCachedResponseSuitabilityChecker {
responseIsFresh(true);
contentLengthMatchesActualLength(true);
modifiedSince(false, request);
currentAge(20L);
org.easymock.EasyMock.expect(mockEntry.getCurrentAgeSecs()).andReturn(20L);
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, request, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
Assert.assertFalse(result);
}
@ -151,11 +150,11 @@ public class TestCachedResponseSuitabilityChecker {
responseIsFresh(true);
contentLengthMatchesActualLength(true);
modifiedSince(false, request);
currentAge(5L);
org.easymock.EasyMock.expect(mockEntry.getCurrentAgeSecs()).andReturn(5L);
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, request, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
Assert.assertTrue(result);
}
@ -166,11 +165,11 @@ public class TestCachedResponseSuitabilityChecker {
responseIsFresh(true);
contentLengthMatchesActualLength(true);
modifiedSince(false, request);
freshnessLifetime(15L);
org.easymock.EasyMock.expect(mockEntry.getFreshnessLifetimeSecs()).andReturn(15L);
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, request, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
Assert.assertTrue(result);
}
@ -181,11 +180,11 @@ public class TestCachedResponseSuitabilityChecker {
responseIsFresh(true);
contentLengthMatchesActualLength(true);
modifiedSince(false, request);
freshnessLifetime(5L);
org.easymock.EasyMock.expect(mockEntry.getFreshnessLifetimeSecs()).andReturn(5L);
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, request, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
Assert.assertFalse(result);
}
@ -199,23 +198,21 @@ public class TestCachedResponseSuitabilityChecker {
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, request, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
Assert.assertFalse(result);
}
@Test
public void testMalformedCacheControlMaxAgeRequestHeaderCausesUnsuitableEntry() {
Header[] hdrs = new Header[] { new BasicHeader("Cache-Control", "max-age=foo") };
request.addHeader(new BasicHeader("Cache-Control", "max-age=foo"));
responseIsFresh(true);
contentLengthMatchesActualLength(true);
modifiedSince(false, mockRequest);
modifiedSince(false, request);
org.easymock.EasyMock.expect(mockRequest.getHeaders("Cache-Control")).andReturn(hdrs);
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, mockRequest, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
@ -224,32 +221,43 @@ public class TestCachedResponseSuitabilityChecker {
@Test
public void testMalformedCacheControlMinFreshRequestHeaderCausesUnsuitableEntry() {
request.addHeader(new BasicHeader("Cache-Control", "min-fresh=foo"));
Header[] hdrs = new Header[] { new BasicHeader("Cache-Control", "min-fresh=foo") };
responseIsFresh(true);
contentLengthMatchesActualLength(true);
modifiedSince(false, mockRequest);
modifiedSince(false, request);
org.easymock.EasyMock.expect(mockRequest.getHeaders("Cache-Control")).andReturn(hdrs);
replayMocks();
boolean result = impl.canCachedResponseBeUsed(host, mockRequest, mockEntry);
boolean result = impl.canCachedResponseBeUsed(host, request, entry);
verifyMocks();
Assert.assertFalse(result);
}
private void currentAge(long sec) {
EasyMock.expect(
mockValidityPolicy.getCurrentAgeSecs(entry)).andReturn(sec);
}
private void freshnessLifetime(long sec) {
EasyMock.expect(
mockValidityPolicy.getFreshnessLifetimeSecs(entry)).andReturn(sec);
}
private void responseIsFresh(boolean fresh) {
org.easymock.EasyMock.expect(mockEntry.isResponseFresh()).andReturn(fresh);
EasyMock.expect(
mockValidityPolicy.isResponseFresh(entry)).andReturn(fresh);
}
private void modifiedSince(boolean modified, HttpRequest request) {
org.easymock.EasyMock.expect(mockEntry.modifiedSince(request)).andReturn(modified);
EasyMock.expect(
mockValidityPolicy.modifiedSince(entry, request)).andReturn(modified);
}
private void contentLengthMatchesActualLength(boolean b) {
org.easymock.EasyMock.expect(mockEntry.contentLengthHeaderMatchesActualLength()).andReturn(
b);
EasyMock.expect(
mockValidityPolicy.contentLengthHeaderMatchesActualLength(entry)).andReturn(b);
}
}

View File

@ -50,6 +50,7 @@ import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.cache.HttpCache;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
@ -89,11 +90,13 @@ public class TestCachingHttpClient {
private static final String GET_RESPONSE_READER = "getResponseReader";
private CachingHttpClient impl;
private boolean mockedImpl;
private CacheInvalidator mockInvalidator;
private CacheValidityPolicy mockValidityPolicy;
private CacheableRequestPolicy mockRequestPolicy;
private HttpClient mockBackend;
private HttpCache<String, CacheEntry> mockCache;
private HttpCache mockCache;
private CachedResponseSuitabilityChecker mockSuitabilityChecker;
private ResponseCachingPolicy mockResponsePolicy;
private HttpRequest mockRequest;
@ -123,8 +126,6 @@ public class TestCachingHttpClient {
private Date requestDate;
private Date responseDate;
private boolean mockedImpl;
private CacheEntryUpdater mockCacheEntryUpdater;
private ResponseProtocolCompliance mockResponseProtocolCompliance;
private RequestProtocolCompliance mockRequestProtocolCompliance;
@ -136,6 +137,7 @@ public class TestCachingHttpClient {
mockInvalidator = EasyMock.createMock(CacheInvalidator.class);
mockRequestPolicy = EasyMock.createMock(CacheableRequestPolicy.class);
mockValidityPolicy = EasyMock.createMock(CacheValidityPolicy.class);
mockBackend = EasyMock.createMock(HttpClient.class);
mockCache = EasyMock.createMock(HttpCache.class);
mockSuitabilityChecker = EasyMock.createMock(CachedResponseSuitabilityChecker.class);
@ -168,7 +170,7 @@ public class TestCachingHttpClient {
requestDate = new Date(System.currentTimeMillis() - 1000);
responseDate = new Date();
host = new HttpHost("foo.example.com");
impl = new CachingHttpClient(mockBackend, mockResponsePolicy, mockEntryGenerator,
impl = new CachingHttpClient(mockBackend, mockValidityPolicy, mockResponsePolicy, mockEntryGenerator,
mockExtractor, mockCache, mockResponseGenerator, mockInvalidator,
mockRequestPolicy, mockSuitabilityChecker, mockConditionalRequestBuilder,
mockCacheEntryUpdater, mockResponseProtocolCompliance,
@ -179,6 +181,7 @@ public class TestCachingHttpClient {
EasyMock.replay(mockInvalidator);
EasyMock.replay(mockRequestPolicy);
EasyMock.replay(mockValidityPolicy);
EasyMock.replay(mockSuitabilityChecker);
EasyMock.replay(mockResponsePolicy);
EasyMock.replay(mockCacheEntry);
@ -204,7 +207,6 @@ public class TestCachingHttpClient {
EasyMock.replay(mockReconstructedResponse);
EasyMock.replay(mockResponseProtocolCompliance);
EasyMock.replay(mockRequestProtocolCompliance);
if (mockedImpl) {
EasyMock.replay(impl);
}
@ -213,6 +215,7 @@ public class TestCachingHttpClient {
private void verifyMocks() {
EasyMock.verify(mockInvalidator);
EasyMock.verify(mockRequestPolicy);
EasyMock.verify(mockValidityPolicy);
EasyMock.verify(mockSuitabilityChecker);
EasyMock.verify(mockResponsePolicy);
EasyMock.verify(mockCacheEntry);
@ -238,7 +241,6 @@ public class TestCachingHttpClient {
EasyMock.verify(mockReconstructedResponse);
EasyMock.verify(mockResponseProtocolCompliance);
EasyMock.verify(mockRequestProtocolCompliance);
if (mockedImpl) {
EasyMock.verify(impl);
}
@ -288,7 +290,7 @@ public class TestCachingHttpClient {
}
private void requestInspectsRequestLine() {
org.easymock.EasyMock.expect(mockRequest.getRequestLine()).andReturn(mockRequestLine);
EasyMock.expect(mockRequest.getRequestLine()).andReturn(mockRequestLine);
}
private void requestIsFatallyNonCompliant(RequestProtocolError error) {
@ -296,7 +298,7 @@ public class TestCachingHttpClient {
if (error != null) {
errors.add(error);
}
org.easymock.EasyMock.expect(
EasyMock.expect(
mockRequestProtocolCompliance.requestIsFatallyNonCompliant(mockRequest)).andReturn(
errors);
}
@ -320,12 +322,12 @@ public class TestCachingHttpClient {
final String variantURI = "variantURI";
final CacheEntry entry = new CacheEntry(new Date(), new Date(), HTTP_1_1,
new Header[] {}, new ByteArrayEntity(new byte[] {}), 200, "OK");
final CacheEntry entry = new CacheEntry(new Date(), new Date(), new OKStatus(),
new Header[] {}, new ByteArrayEntity(new byte[] {}));
replayMocks();
CacheEntry updatedEntry = impl.doGetUpdatedParentEntry(null, entry, variantURI);
HttpCacheEntry updatedEntry = impl.doGetUpdatedParentEntry(null, entry, variantURI);
verifyMocks();
@ -518,7 +520,7 @@ public class TestCachingHttpClient {
gotCacheMiss(theURI);
replayMocks();
CacheEntry result = impl.getCacheEntry(host, mockRequest);
HttpCacheEntry result = impl.getCacheEntry(host, mockRequest);
verifyMocks();
Assert.assertNull(result);
}
@ -532,7 +534,7 @@ public class TestCachingHttpClient {
cacheEntryHasVariants(false);
replayMocks();
CacheEntry result = impl.getCacheEntry(host, mockRequest);
HttpCacheEntry result = impl.getCacheEntry(host, mockRequest);
verifyMocks();
Assert.assertSame(mockCacheEntry, result);
}
@ -549,7 +551,7 @@ public class TestCachingHttpClient {
gotCacheMiss(variantURI);
replayMocks();
CacheEntry result = impl.getCacheEntry(host, mockRequest);
HttpCacheEntry result = impl.getCacheEntry(host, mockRequest);
verifyMocks();
Assert.assertNull(result);
}
@ -566,7 +568,7 @@ public class TestCachingHttpClient {
gotCacheHit(variantURI, mockVariantCacheEntry);
replayMocks();
CacheEntry result = impl.getCacheEntry(host, mockRequest);
HttpCacheEntry result = impl.getCacheEntry(host, mockRequest);
verifyMocks();
Assert.assertSame(mockVariantCacheEntry, result);
}
@ -624,7 +626,7 @@ public class TestCachingHttpClient {
final HttpHost theHost = host;
final HttpRequest theRequest = mockRequest;
final HttpResponse theResponse = mockBackendResponse;
impl = new CachingHttpClient(mockBackend, mockResponsePolicy, mockEntryGenerator,
impl = new CachingHttpClient(mockBackend, mockValidityPolicy, mockResponsePolicy, mockEntryGenerator,
mockExtractor, mockCache, mockResponseGenerator, mockInvalidator,
mockRequestPolicy, mockSuitabilityChecker, mockConditionalRequestBuilder,
mockCacheEntryUpdater, mockResponseProtocolCompliance,
@ -656,7 +658,7 @@ public class TestCachingHttpClient {
final HttpResponse theResponse = mockBackendResponse;
final ResponseHandler<Object> theHandler = mockHandler;
final Object value = new Object();
impl = new CachingHttpClient(mockBackend, mockResponsePolicy, mockEntryGenerator,
impl = new CachingHttpClient(mockBackend, mockValidityPolicy, mockResponsePolicy, mockEntryGenerator,
mockExtractor, mockCache, mockResponseGenerator, mockInvalidator,
mockRequestPolicy, mockSuitabilityChecker, mockConditionalRequestBuilder,
mockCacheEntryUpdater, mockResponseProtocolCompliance,
@ -677,7 +679,7 @@ public class TestCachingHttpClient {
}
};
org.easymock.EasyMock.expect(mockHandler.handleResponse(mockBackendResponse)).andReturn(
EasyMock.expect(mockHandler.handleResponse(mockBackendResponse)).andReturn(
value);
replayMocks();
@ -696,7 +698,7 @@ public class TestCachingHttpClient {
final HttpRequest theRequest = mockRequest;
final HttpResponse theResponse = mockBackendResponse;
final HttpContext theContext = mockContext;
impl = new CachingHttpClient(mockBackend, mockResponsePolicy, mockEntryGenerator,
impl = new CachingHttpClient(mockBackend, mockValidityPolicy, mockResponsePolicy, mockEntryGenerator,
mockExtractor, mockCache, mockResponseGenerator, mockInvalidator,
mockRequestPolicy, mockSuitabilityChecker, mockConditionalRequestBuilder,
mockCacheEntryUpdater, mockResponseProtocolCompliance,
@ -713,7 +715,7 @@ public class TestCachingHttpClient {
final Object theObject = new Object();
org.easymock.EasyMock.expect(mockHandler.handleResponse(mockBackendResponse)).andReturn(
EasyMock.expect(mockHandler.handleResponse(mockBackendResponse)).andReturn(
theObject);
replayMocks();
@ -728,7 +730,7 @@ public class TestCachingHttpClient {
final Counter c = new Counter();
final HttpUriRequest theRequest = mockUriRequest;
final HttpResponse theResponse = mockBackendResponse;
impl = new CachingHttpClient(mockBackend, mockResponsePolicy, mockEntryGenerator,
impl = new CachingHttpClient(mockBackend, mockValidityPolicy, mockResponsePolicy, mockEntryGenerator,
mockExtractor, mockCache, mockResponseGenerator, mockInvalidator,
mockRequestPolicy, mockSuitabilityChecker, mockConditionalRequestBuilder,
mockCacheEntryUpdater, mockResponseProtocolCompliance,
@ -758,7 +760,7 @@ public class TestCachingHttpClient {
final HttpRequest theRequest = mockUriRequest;
final HttpContext theContext = mockContext;
final HttpResponse theResponse = mockBackendResponse;
impl = new CachingHttpClient(mockBackend, mockResponsePolicy, mockEntryGenerator,
impl = new CachingHttpClient(mockBackend, mockValidityPolicy, mockResponsePolicy, mockEntryGenerator,
mockExtractor, mockCache, mockResponseGenerator, mockInvalidator,
mockRequestPolicy, mockSuitabilityChecker, mockConditionalRequestBuilder,
mockCacheEntryUpdater, mockResponseProtocolCompliance,
@ -775,7 +777,7 @@ public class TestCachingHttpClient {
}
};
org.easymock.EasyMock.expect(mockUriRequest.getURI()).andReturn(uri);
EasyMock.expect(mockUriRequest.getURI()).andReturn(uri);
replayMocks();
HttpResponse result = impl.execute(mockUriRequest, mockContext);
@ -791,7 +793,7 @@ public class TestCachingHttpClient {
final HttpUriRequest theRequest = mockUriRequest;
final HttpResponse theResponse = mockBackendResponse;
final Object theValue = new Object();
impl = new CachingHttpClient(mockBackend, mockResponsePolicy, mockEntryGenerator,
impl = new CachingHttpClient(mockBackend, mockValidityPolicy, mockResponsePolicy, mockEntryGenerator,
mockExtractor, mockCache, mockResponseGenerator, mockInvalidator,
mockRequestPolicy, mockSuitabilityChecker, mockConditionalRequestBuilder,
mockCacheEntryUpdater, mockResponseProtocolCompliance,
@ -806,7 +808,7 @@ public class TestCachingHttpClient {
}
};
org.easymock.EasyMock.expect(mockHandler.handleResponse(mockBackendResponse)).andReturn(
EasyMock.expect(mockHandler.handleResponse(mockBackendResponse)).andReturn(
theValue);
replayMocks();
@ -826,7 +828,7 @@ public class TestCachingHttpClient {
final HttpContext theContext = mockContext;
final HttpResponse theResponse = mockBackendResponse;
final Object theValue = new Object();
impl = new CachingHttpClient(mockBackend, mockResponsePolicy, mockEntryGenerator,
impl = new CachingHttpClient(mockBackend, mockValidityPolicy, mockResponsePolicy, mockEntryGenerator,
mockExtractor, mockCache, mockResponseGenerator, mockInvalidator,
mockRequestPolicy, mockSuitabilityChecker, mockConditionalRequestBuilder,
mockCacheEntryUpdater, mockResponseProtocolCompliance,
@ -841,7 +843,7 @@ public class TestCachingHttpClient {
}
};
org.easymock.EasyMock.expect(mockHandler.handleResponse(mockBackendResponse)).andReturn(
EasyMock.expect(mockHandler.handleResponse(mockBackendResponse)).andReturn(
theValue);
replayMocks();
@ -853,7 +855,7 @@ public class TestCachingHttpClient {
@Test
public void testUsesBackendsConnectionManager() {
org.easymock.EasyMock.expect(mockBackend.getConnectionManager()).andReturn(
EasyMock.expect(mockBackend.getConnectionManager()).andReturn(
mockConnectionManager);
replayMocks();
ClientConnectionManager result = impl.getConnectionManager();
@ -863,7 +865,7 @@ public class TestCachingHttpClient {
@Test
public void testUsesBackendsHttpParams() {
org.easymock.EasyMock.expect(mockBackend.getParams()).andReturn(mockParams);
EasyMock.expect(mockBackend.getParams()).andReturn(mockParams);
replayMocks();
HttpParams result = impl.getParams();
verifyMocks();
@ -881,7 +883,7 @@ public class TestCachingHttpClient {
ClientConnectionManager cm = new ThreadSafeClientConnManager(schemeRegistry);
HttpClient httpClient = new DefaultHttpClient(cm);
HttpCache<String, CacheEntry> cacheImpl = new BasicHttpCache(100);
HttpCache cacheImpl = new BasicHttpCache(100);
CachingHttpClient cachingClient = new CachingHttpClient(httpClient, cacheImpl, 8192);
@ -1072,19 +1074,19 @@ public class TestCachingHttpClient {
}
private void callBackendReturnsResponse(HttpResponse response) throws IOException {
org.easymock.EasyMock.expect(impl.callBackend(host, mockRequest, mockContext)).andReturn(
EasyMock.expect(impl.callBackend(host, mockRequest, mockContext)).andReturn(
response);
}
private void revalidateCacheEntryReturns(HttpResponse response) throws IOException,
ProtocolException {
org.easymock.EasyMock.expect(
EasyMock.expect(
impl.revalidateCacheEntry(host, mockRequest, mockContext, mockCacheEntry))
.andReturn(response);
}
private void cacheEntryValidatable(boolean b) {
org.easymock.EasyMock.expect(mockCacheEntry.isRevalidatable()).andReturn(b);
EasyMock.expect(mockValidityPolicy.isRevalidatable(mockCacheEntry)).andReturn(b);
}
private void cacheEntryUpdaterCalled() throws IOException {
@ -1094,26 +1096,26 @@ public class TestCachingHttpClient {
}
private void getCacheEntryReturns(CacheEntry entry) {
org.easymock.EasyMock.expect(impl.getCacheEntry(host, mockRequest)).andReturn(entry);
EasyMock.expect(impl.getCacheEntry(host, mockRequest)).andReturn(entry);
}
private void backendResponseCodeIs(int code) {
org.easymock.EasyMock.expect(mockBackendResponse.getStatusLine()).andReturn(mockStatusLine);
org.easymock.EasyMock.expect(mockStatusLine.getStatusCode()).andReturn(code);
EasyMock.expect(mockBackendResponse.getStatusLine()).andReturn(mockStatusLine);
EasyMock.expect(mockStatusLine.getStatusCode()).andReturn(code);
}
private void conditionalRequestBuilderCalled() throws ProtocolException {
org.easymock.EasyMock.expect(
EasyMock.expect(
mockConditionalRequestBuilder.buildConditionalRequest(mockRequest, mockCacheEntry))
.andReturn(mockConditionalRequest);
}
private void getCurrentDateReturns(Date date) {
org.easymock.EasyMock.expect(impl.getCurrentDate()).andReturn(date);
EasyMock.expect(impl.getCurrentDate()).andReturn(date);
}
private void getMockResponseReader() {
org.easymock.EasyMock.expect(impl.getResponseReader(mockBackendResponse)).andReturn(
EasyMock.expect(impl.getResponseReader(mockBackendResponse)).andReturn(
mockResponseReader);
}
@ -1122,7 +1124,7 @@ public class TestCachingHttpClient {
}
private void requestPolicyAllowsCaching(boolean allow) {
org.easymock.EasyMock.expect(mockRequestPolicy.isServableFromCache(mockRequest)).andReturn(
EasyMock.expect(mockRequestPolicy.isServableFromCache(mockRequest)).andReturn(
allow);
}
@ -1133,50 +1135,50 @@ public class TestCachingHttpClient {
private byte[] responseReaderReturnsBufferOfSize(int bufferSize) {
byte[] buffer = new byte[bufferSize];
org.easymock.EasyMock.expect(mockResponseReader.getResponseBytes()).andReturn(buffer);
EasyMock.expect(mockResponseReader.getResponseBytes()).andReturn(buffer);
return buffer;
}
private void readerReturnsReconstructedResponse() {
org.easymock.EasyMock.expect(mockResponseReader.getReconstructedResponse()).andReturn(
EasyMock.expect(mockResponseReader.getReconstructedResponse()).andReturn(
mockReconstructedResponse);
}
private void responseIsTooLarge(boolean tooLarge) throws Exception {
org.easymock.EasyMock.expect(mockResponseReader.isResponseTooLarge()).andReturn(tooLarge);
EasyMock.expect(mockResponseReader.isResponseTooLarge()).andReturn(tooLarge);
}
private void backendCallWasMadeWithRequest(HttpRequest request) throws IOException {
org.easymock.EasyMock.expect(mockBackend.execute(host, request, mockContext)).andReturn(
EasyMock.expect(mockBackend.execute(host, request, mockContext)).andReturn(
mockBackendResponse);
}
private void responsePolicyAllowsCaching(boolean allow) {
org.easymock.EasyMock.expect(
EasyMock.expect(
mockResponsePolicy.isResponseCacheable(mockRequest, mockBackendResponse))
.andReturn(allow);
}
private void gotCacheMiss(String theURI) throws Exception {
org.easymock.EasyMock.expect(mockCache.getEntry(theURI)).andReturn(null);
EasyMock.expect(mockCache.getEntry(theURI)).andReturn(null);
}
private void cacheEntrySuitable(boolean suitable) {
org.easymock.EasyMock.expect(
EasyMock.expect(
mockSuitabilityChecker.canCachedResponseBeUsed(host, mockRequest, mockCacheEntry))
.andReturn(suitable);
}
private void gotCacheHit(String theURI) throws Exception {
org.easymock.EasyMock.expect(mockCache.getEntry(theURI)).andReturn(mockCacheEntry);
EasyMock.expect(mockCache.getEntry(theURI)).andReturn(mockCacheEntry);
}
private void gotCacheHit(String theURI, CacheEntry entry) throws Exception {
org.easymock.EasyMock.expect(mockCache.getEntry(theURI)).andReturn(entry);
EasyMock.expect(mockCache.getEntry(theURI)).andReturn(entry);
}
private void cacheEntryHasVariants(boolean b) {
org.easymock.EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(b);
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(b);
}
private void cacheEntryHasVariants(boolean b, CacheEntry entry) {
@ -1195,12 +1197,12 @@ public class TestCachingHttpClient {
}
private void responseIsGeneratedFromCache(CacheEntry entry) {
org.easymock.EasyMock.expect(mockResponseGenerator.generateResponse(entry))
EasyMock.expect(mockResponseGenerator.generateResponse(entry))
.andReturn(mockCachedResponse);
}
private void extractTheURI(String theURI) {
org.easymock.EasyMock.expect(mockExtractor.getURI(host, mockRequest)).andReturn(theURI);
EasyMock.expect(mockExtractor.getURI(host, mockRequest)).andReturn(theURI);
}
private void extractVariantURI(String variantURI) {
@ -1208,7 +1210,7 @@ public class TestCachingHttpClient {
}
private void extractVariantURI(String variantURI, CacheEntry entry){
org.easymock.EasyMock
EasyMock
.expect(mockExtractor.getVariantURI(host, mockRequest, entry)).andReturn(
variantURI);
}
@ -1222,14 +1224,14 @@ public class TestCachingHttpClient {
}
private void generateCacheEntry(Date requestDate, Date responseDate, byte[] bytes) {
org.easymock.EasyMock.expect(
EasyMock.expect(
mockEntryGenerator.generateEntry(requestDate, responseDate, mockBackendResponse,
bytes)).andReturn(mockCacheEntry);
}
private void handleBackendResponseReturnsResponse(HttpRequest request, HttpResponse response)
throws IOException {
org.easymock.EasyMock.expect(
EasyMock.expect(
impl.handleBackendResponse(host, request, requestDate, responseDate,
mockBackendResponse)).andReturn(response);
}
@ -1247,24 +1249,25 @@ public class TestCachingHttpClient {
}
private void requestProtocolValidationIsCalled() throws Exception {
org.easymock.EasyMock.expect(
EasyMock.expect(
mockRequestProtocolCompliance.makeRequestCompliant(mockRequest)).andReturn(
mockRequest);
}
private void requestCannotBeMadeCompliantThrows(ProtocolException exception) throws Exception {
org.easymock.EasyMock.expect(
EasyMock.expect(
mockRequestProtocolCompliance.makeRequestCompliant(mockRequest))
.andThrow(exception);
}
private void mockImplMethods(String... methods) {
mockedImpl = true;
impl = EasyMock.createMockBuilder(CachingHttpClient.class).withConstructor(mockBackend,
impl = EasyMock.createMockBuilder(CachingHttpClient.class).withConstructor(mockBackend, mockValidityPolicy,
mockResponsePolicy, mockEntryGenerator, mockExtractor, mockCache,
mockResponseGenerator, mockInvalidator, mockRequestPolicy, mockSuitabilityChecker,
mockConditionalRequestBuilder, mockCacheEntryUpdater,
mockResponseProtocolCompliance, mockRequestProtocolCompliance).addMockedMethods(
methods).createMock();
}
}

View File

@ -31,10 +31,8 @@ import java.util.Date;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpRequest;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.ProtocolException;
import org.apache.http.ProtocolVersion;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.message.BasicHeader;
@ -66,8 +64,8 @@ public class TestConditionalRequestBuilder {
new BasicHeader("Last-Modified", lastModified) };
CacheEntry cacheEntry = new CacheEntry(new Date(), new Date(),
new ProtocolVersion("HTTP",1,1), headers,
new ByteArrayEntity(new byte[] {}), 200, "OK");
new OKStatus(), headers,
new ByteArrayEntity(new byte[] {}));
HttpRequest newRequest = impl.buildConditionalRequest(request, cacheEntry);
Assert.assertNotSame(request, newRequest);
@ -100,9 +98,7 @@ public class TestConditionalRequestBuilder {
new BasicHeader("ETag", theETag) };
CacheEntry cacheEntry = new CacheEntry(new Date(), new Date(),
new ProtocolVersion("HTTP",1,1), headers, new ByteArrayEntity(new byte[] {}),
200, "OK");
new OKStatus(), headers, new ByteArrayEntity(new byte[] {}));
HttpRequest newRequest = impl.buildConditionalRequest(request, cacheEntry);
@ -135,8 +131,7 @@ public class TestConditionalRequestBuilder {
new BasicHeader("ETag", "\"etag\""),
new BasicHeader("Cache-Control","max-age=5, must-revalidate") };
CacheEntry cacheEntry = new CacheEntry(elevenSecondsAgo, nineSecondsAgo,
HttpVersion.HTTP_1_1, cacheEntryHeaders, new ByteArrayEntity(new byte[0]),
HttpStatus.SC_OK, "OK");
new OKStatus(), cacheEntryHeaders, new ByteArrayEntity(new byte[0]));
HttpRequest result = impl.buildConditionalRequest(request, cacheEntry);
@ -165,8 +160,7 @@ public class TestConditionalRequestBuilder {
new BasicHeader("ETag", "\"etag\""),
new BasicHeader("Cache-Control","max-age=5, proxy-revalidate") };
CacheEntry cacheEntry = new CacheEntry(elevenSecondsAgo, nineSecondsAgo,
HttpVersion.HTTP_1_1, cacheEntryHeaders, new ByteArrayEntity(new byte[0]),
HttpStatus.SC_OK, "OK");
new OKStatus(), cacheEntryHeaders, new ByteArrayEntity(new byte[0]));
HttpRequest result = impl.buildConditionalRequest(request, cacheEntry);

View File

@ -77,13 +77,12 @@ public class TestProtocolDeviations {
private HttpEntity body;
private HttpEntity mockEntity;
private HttpClient mockBackend;
private HttpCache<String, CacheEntry> mockCache;
private HttpCache mockCache;
private HttpRequest request;
private HttpResponse originResponse;
private CachingHttpClient impl;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
host = new HttpHost("foo.example.com");
@ -94,7 +93,7 @@ public class TestProtocolDeviations {
originResponse = make200Response();
HttpCache<String, CacheEntry> cache = new BasicHttpCache(MAX_ENTRIES);
HttpCache cache = new BasicHttpCache(MAX_ENTRIES);
mockBackend = EasyMock.createMock(HttpClient.class);
mockEntity = EasyMock.createMock(HttpEntity.class);
mockCache = EasyMock.createMock(HttpCache.class);

View File

@ -26,6 +26,7 @@
*/
package org.apache.http.impl.client.cache;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.cache.HttpCacheOperationException;
import org.apache.http.client.cache.HttpCacheUpdateCallback;
import org.apache.http.entity.ByteArrayEntity;
@ -41,19 +42,19 @@ import java.io.IOException;
public class TestResponseCache {
private BasicHttpCache cache;
private CacheEntry mockEntry;
private HttpCacheEntry mockEntry;
@Before
public void setUp() {
cache = new BasicHttpCache(5);
mockEntry = EasyMock.createMock(CacheEntry.class);
mockEntry = EasyMock.createMock(HttpCacheEntry.class);
}
@Test
public void testEntryRemainsInCacheWhenPutThere() {
cache.putEntry("foo", mockEntry);
CacheEntry cachedEntry = cache.getEntry("foo");
HttpCacheEntry cachedEntry = cache.getEntry("foo");
Assert.assertSame(mockEntry, cachedEntry);
}
@ -64,7 +65,7 @@ public class TestResponseCache {
cache.removeEntry("foo");
CacheEntry nullEntry = cache.getEntry("foo");
HttpCacheEntry nullEntry = cache.getEntry("foo");
Assert.assertNull(nullEntry);
}
@ -73,22 +74,22 @@ public class TestResponseCache {
public void testCacheHoldsNoMoreThanSpecifiedMaxEntries() {
BasicHttpCache cache = new BasicHttpCache(1);
CacheEntry entry1 = EasyMock.createMock(CacheEntry.class);
HttpCacheEntry entry1 = EasyMock.createMock(HttpCacheEntry.class);
cache.putEntry("foo", entry1);
CacheEntry entry2 = EasyMock.createMock(CacheEntry.class);
HttpCacheEntry entry2 = EasyMock.createMock(HttpCacheEntry.class);
cache.putEntry("bar", entry2);
CacheEntry entry3 = EasyMock.createMock(CacheEntry.class);
HttpCacheEntry entry3 = EasyMock.createMock(HttpCacheEntry.class);
cache.putEntry("baz", entry3);
CacheEntry e1 = cache.getEntry("foo");
HttpCacheEntry e1 = cache.getEntry("foo");
Assert.assertNull("Got foo entry when we should not", e1);
CacheEntry e2 = cache.getEntry("bar");
HttpCacheEntry e2 = cache.getEntry("bar");
Assert.assertNull("Got bar entry when we should not", e2);
CacheEntry e3 = cache.getEntry("baz");
HttpCacheEntry e3 = cache.getEntry("baz");
Assert.assertNotNull("Did not get baz entry, but should have", e3);
}
@ -100,7 +101,7 @@ public class TestResponseCache {
// fill the cache with entries
for (int i = 0; i < max_size; i++) {
CacheEntry entry = EasyMock.createMock(CacheEntry.class);
HttpCacheEntry entry = EasyMock.createMock(HttpCacheEntry.class);
cache.putEntry("entry" + i, entry);
}
@ -109,17 +110,17 @@ public class TestResponseCache {
// add another entry, which kicks out the eldest (should be the 2nd one
// created), and becomes the new MRU entry
CacheEntry newMru = EasyMock.createMock(CacheEntry.class);
HttpCacheEntry newMru = EasyMock.createMock(HttpCacheEntry.class);
cache.putEntry("newMru", newMru);
// get the original second eldest
CacheEntry gone = cache.getEntry("entry1");
HttpCacheEntry gone = cache.getEntry("entry1");
Assert.assertNull("entry1 should be gone", gone);
CacheEntry latest = cache.getEntry("newMru");
HttpCacheEntry latest = cache.getEntry("newMru");
Assert.assertNotNull("latest entry should still be there", latest);
CacheEntry originalEldest = cache.getEntry("entry0");
HttpCacheEntry originalEldest = cache.getEntry("entry0");
Assert.assertNotNull("original eldest entry should still be there", originalEldest);
}
@ -127,10 +128,10 @@ public class TestResponseCache {
public void testZeroMaxSizeCacheDoesNotStoreAnything() {
BasicHttpCache cache = new BasicHttpCache(0);
CacheEntry entry = EasyMock.createMock(CacheEntry.class);
HttpCacheEntry entry = EasyMock.createMock(HttpCacheEntry.class);
cache.putEntry("foo", entry);
CacheEntry gone = cache.getEntry("foo");
HttpCacheEntry gone = cache.getEntry("foo");
Assert.assertNull("This cache should not have anything in it!", gone);
}
@ -141,30 +142,29 @@ public class TestResponseCache {
final byte[] expectedArray = new byte[] { 1, 2, 3, 4, 5 };
CacheEntry entry = EasyMock.createMock(CacheEntry.class);
CacheEntry entry2 = EasyMock.createMock(CacheEntry.class);
HttpCacheEntry entry = EasyMock.createMock(HttpCacheEntry.class);
HttpCacheEntry entry2 = EasyMock.createMock(HttpCacheEntry.class);
cache.putEntry("foo", entry);
cache.putEntry("bar", entry2);
cache.updateEntry("foo", new HttpCacheUpdateCallback<CacheEntry>() {
cache.updateEntry("foo", new HttpCacheUpdateCallback() {
public CacheEntry update(CacheEntry existing) {
CacheEntry updated = new CacheEntry(
public HttpCacheEntry update(HttpCacheEntry existing) {
HttpCacheEntry updated = new HttpCacheEntry(
existing.getRequestDate(),
existing.getRequestDate(),
existing.getProtocolVersion(),
existing.getStatusLine(),
existing.getAllHeaders(),
new ByteArrayEntity(expectedArray),
existing.getStatusCode(),
existing.getReasonPhrase());
null);
cache.removeEntry("bar");
return updated;
}
});
CacheEntry afterUpdate = cache.getEntry("foo");
CacheEntry bar = cache.getEntry("bar");
HttpCacheEntry afterUpdate = cache.getEntry("foo");
HttpCacheEntry bar = cache.getEntry("bar");
Assert.assertNull(bar);