* Caching protocol classes to use CacheControlHeaderParser to parse request and response cache control directives

* Several protocol recommendations related to Cache-Control header composition no longer mentioned by RFC 7234 have been removed
* Cleanup of header constants
This commit is contained in:
Oleg Kalnichevski 2023-05-16 13:27:49 +02:00
parent 3f81f21cab
commit cd2930af1f
29 changed files with 303 additions and 619 deletions

View File

@ -27,7 +27,7 @@
package org.apache.hc.client5.http.cache;
/**
* Records static constants for various HTTP header names.
* Records static constants for caching directives.
*
* @since 4.1
*/
@ -41,20 +41,75 @@ public class HeaderConstants {
public static final String TRACE_METHOD = "TRACE";
public static final String POST_METHOD = "POST";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String LAST_MODIFIED = "Last-Modified";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String IF_MATCH = "If-Match";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String IF_RANGE = "If-Range";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String IF_NONE_MATCH = "If-None-Match";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String PRAGMA = "Pragma";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String MAX_FORWARDS = "Max-Forwards";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String ETAG = "ETag";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String EXPIRES = "Expires";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String AGE = "Age";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String VARY = "Vary";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String ALLOW = "Allow";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String VIA = "Via";
/**
* @deprecated Use {@link #CACHE_CONTROL_PUBLIC}
@ -92,11 +147,35 @@ public class HeaderConstants {
@Deprecated
public static final String STALE_WHILE_REVALIDATE = "stale-while-revalidate";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String WARNING = "Warning";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String RANGE = "Range";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String CONTENT_RANGE = "Content-Range";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
/**
* @deprecated Use {@link org.apache.hc.core5.http.HttpHeaders}
*/
@Deprecated
public static final String AUTHORIZATION = "Authorization";
}

View File

@ -352,7 +352,7 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
* @return {@code true} if this cached response was a variant
*/
public boolean hasVariants() {
return getFirstHeader(HeaderConstants.VARY) != null;
return getFirstHeader(HttpHeaders.VARY) != null;
}
/**

View File

@ -43,7 +43,6 @@ import org.apache.hc.client5.http.async.AsyncExecChainHandler;
import org.apache.hc.client5.http.async.methods.SimpleBody;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.cache.CacheResponseStatus;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.ResourceFactory;
@ -238,7 +237,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
}
requestCompliance.makeRequestCompliant(request);
request.addHeader("Via",via);
request.addHeader(HttpHeaders.VIA,via);
if (!cacheableRequestPolicy.isServableFromCache(request)) {
LOG.debug("Request is not servable from cache");
@ -318,7 +317,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
final HttpResponse backendResponse,
final EntityDetails entityDetails) throws HttpException, IOException {
final Instant responseDate = getCurrentDate();
backendResponse.addHeader("Via", generateViaHeader(backendResponse));
backendResponse.addHeader(HttpHeaders.VIA, generateViaHeader(backendResponse));
final AsyncExecCallback callback = new BackendResponseHandler(target, request, requestDate, responseDate, scope, asyncExecCallback);
callbackRef.set(callback);
@ -774,7 +773,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
void triggerResponseStaleCacheEntry() {
try {
final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, cacheEntry);
cacheResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
cacheResponse.addHeader(HttpHeaders.WARNING, "110 localhost \"Response is stale\"");
triggerResponse(cacheResponse, scope, asyncExecCallback);
} catch (final ResourceIOException ex) {
asyncExecCallback.failed(ex);
@ -782,7 +781,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
}
AsyncExecCallback evaluateResponse(final HttpResponse backendResponse, final Instant responseDate) {
backendResponse.addHeader(HeaderConstants.VIA, generateViaHeader(backendResponse));
backendResponse.addHeader(HttpHeaders.VIA, generateViaHeader(backendResponse));
final int statusCode = backendResponse.getCode();
if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) {
@ -1022,7 +1021,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
final HttpResponse backendResponse,
final EntityDetails entityDetails) throws HttpException, IOException {
final Instant responseDate = getCurrentDate();
backendResponse.addHeader("Via", generateViaHeader(backendResponse));
backendResponse.addHeader(HttpHeaders.VIA, generateViaHeader(backendResponse));
final AsyncExecCallback callback;
// Handle 304 Not Modified responses
@ -1090,7 +1089,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
if (backendResponse.getCode() != HttpStatus.SC_NOT_MODIFIED) {
callback = new BackendResponseHandler(target, request, requestDate, responseDate, scope, asyncExecCallback);
} else {
final Header resultEtagHeader = backendResponse.getFirstHeader(HeaderConstants.ETAG);
final Header resultEtagHeader = backendResponse.getFirstHeader(HttpHeaders.ETAG);
if (resultEtagHeader == null) {
LOG.warn("304 response did not contain ETag");
callback = new AsyncExecCallbackWrapper(asyncExecCallback, () -> callBackend(target, request, entityProducer, scope, chain, asyncExecCallback));

View File

@ -31,7 +31,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator;
import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
@ -43,6 +42,7 @@ import org.apache.hc.core5.concurrent.Cancellable;
import org.apache.hc.core5.concurrent.ComplexCancellable;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
@ -540,7 +540,7 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
for (final Map.Entry<String, HttpCacheEntry> resultMapEntry : resultMap.entrySet()) {
final String cacheKey = resultMapEntry.getKey();
final HttpCacheEntry cacheEntry = resultMapEntry.getValue();
final Header etagHeader = cacheEntry.getFirstHeader(HeaderConstants.ETAG);
final Header etagHeader = cacheEntry.getFirstHeader(HttpHeaders.ETAG);
if (etagHeader != null) {
variants.put(etagHeader.getValue(), new Variant(cacheKey, cacheEntry));
}

View File

@ -28,10 +28,8 @@ package org.apache.hc.client5.http.impl.cache;
import java.time.Instant;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.HttpCacheInvalidator;
import org.apache.hc.client5.http.cache.HttpCacheStorage;
@ -39,13 +37,11 @@ import org.apache.hc.client5.http.cache.HttpCacheUpdateException;
import org.apache.hc.client5.http.cache.ResourceFactory;
import org.apache.hc.client5.http.cache.ResourceIOException;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.Method;
import org.apache.hc.core5.http.message.MessageSupport;
import org.apache.hc.core5.http.message.RequestLine;
import org.apache.hc.core5.http.message.StatusLine;
import org.apache.hc.core5.util.ByteArrayBuffer;
@ -326,8 +322,7 @@ class BasicHttpCache implements HttpCache {
continue;
}
final Iterator<HeaderElement> it = MessageSupport.iterate(variant, HttpHeaders.DATE);
if (!it.hasNext()) {
if (!variant.containsHeader(HttpHeaders.DATE)) {
continue;
}
@ -360,7 +355,7 @@ class BasicHttpCache implements HttpCache {
try {
final HttpCacheEntry entry = storage.getEntry(variantCacheKey);
if (entry != null) {
final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG);
final Header etagHeader = entry.getFirstHeader(HttpHeaders.ETAG);
if (etagHeader != null) {
variants.put(etagHeader.getValue(), new Variant(variantCacheKey, entry));
}

View File

@ -33,6 +33,7 @@ import java.util.Set;
import java.util.function.BiConsumer;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.Internal;
import org.apache.hc.core5.annotation.ThreadingBehavior;
@ -204,6 +205,10 @@ class CacheControlHeaderParser {
return parseResponse(response.headerIterator(HttpHeaders.CACHE_CONTROL));
}
public final ResponseCacheControl parse(final HttpCacheEntry cacheEntry) {
return parseResponse(cacheEntry.headerIterator(HttpHeaders.CACHE_CONTROL));
}
/**
* Parses the specified request header and returns a new {@link RequestCacheControl} instance containing
* the relevant caching directives.

View File

@ -89,8 +89,8 @@ class CacheInvalidatorBase {
static boolean responseAndEntryEtagsDiffer(final HttpResponse response,
final HttpCacheEntry entry) {
final Header entryEtag = entry.getFirstHeader(HeaderConstants.ETAG);
final Header responseEtag = response.getFirstHeader(HeaderConstants.ETAG);
final Header entryEtag = entry.getFirstHeader(HttpHeaders.ETAG);
final Header responseEtag = response.getFirstHeader(HttpHeaders.ETAG);
if (entryEtag == null || responseEtag == null) {
return false;
}

View File

@ -36,13 +36,13 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.function.Resolver;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.message.MessageSupport;
@ -137,7 +137,7 @@ public class CacheKeyGenerator implements Resolver<URI, String> {
*/
public String generateVariantKey(final HttpRequest req, final HttpCacheEntry entry) {
final List<String> variantHeaderNames = new ArrayList<>();
final Iterator<HeaderElement> it = MessageSupport.iterate(entry, HeaderConstants.VARY);
final Iterator<HeaderElement> it = MessageSupport.iterate(entry, HttpHeaders.VARY);
while (it.hasNext()) {
final HeaderElement elt = it.next();
variantHeaderNames.add(elt.getName());

View File

@ -37,10 +37,10 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.schedule.ConcurrentCountMap;
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.TimeValue;
@ -173,7 +173,7 @@ class CacheRevalidatorBase implements Closeable {
* @return whether the response is stale or not
*/
boolean isStale(final HttpResponse httpResponse) {
for (final Iterator<Header> it = httpResponse.headerIterator(HeaderConstants.WARNING); it.hasNext(); ) {
for (final Iterator<Header> it = httpResponse.headerIterator(HttpHeaders.WARNING); it.hasNext(); ) {
/*
* warn-codes
* 110 = Response is stale

View File

@ -31,7 +31,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.Resource;
import org.apache.hc.client5.http.cache.ResourceFactory;
@ -149,7 +148,7 @@ class CacheUpdateHandler {
// remove cache entry 1xx warnings
for (final Iterator<Header> it = headerGroup.headerIterator(); it.hasNext(); ) {
final Header cacheHeader = it.next();
if (HeaderConstants.WARNING.equalsIgnoreCase(cacheHeader.getName())) {
if (HttpHeaders.WARNING.equalsIgnoreCase(cacheHeader.getName())) {
final String warningValue = cacheHeader.getValue();
if (warningValue != null && warningValue.startsWith("1")) {
it.remove();

View File

@ -28,18 +28,13 @@ package org.apache.hc.client5.http.impl.cache;
import java.time.Duration;
import java.time.Instant;
import java.util.Iterator;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.Resource;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.MessageHeaders;
import org.apache.hc.core5.http.message.MessageSupport;
import org.apache.hc.core5.util.TimeValue;
class CacheValidityPolicy {
@ -66,7 +61,7 @@ class CacheValidityPolicy {
return TimeValue.ZERO_MILLISECONDS;
}
final Instant expiry = DateUtils.parseStandardDate(entry, HeaderConstants.EXPIRES);
final Instant expiry = DateUtils.parseStandardDate(entry, HttpHeaders.EXPIRES);
if (expiry == null) {
return TimeValue.ZERO_MILLISECONDS;
}
@ -100,7 +95,7 @@ class CacheValidityPolicy {
public TimeValue getHeuristicFreshnessLifetime(final HttpCacheEntry entry,
final float coefficient, final TimeValue defaultLifetime) {
final Instant dateValue = entry.getInstant();
final Instant lastModifiedValue = DateUtils.parseStandardDate(entry, HeaderConstants.LAST_MODIFIED);
final Instant lastModifiedValue = DateUtils.parseStandardDate(entry, HttpHeaders.LAST_MODIFIED);
if (dateValue != null && lastModifiedValue != null) {
final Duration diff = Duration.between(lastModifiedValue, dateValue);
@ -115,63 +110,40 @@ class CacheValidityPolicy {
}
public boolean isRevalidatable(final HttpCacheEntry entry) {
return entry.getFirstHeader(HeaderConstants.ETAG) != null
|| entry.getFirstHeader(HeaderConstants.LAST_MODIFIED) != null;
return entry.getFirstHeader(HttpHeaders.ETAG) != null
|| entry.getFirstHeader(HttpHeaders.LAST_MODIFIED) != null;
}
public boolean mustRevalidate(final HttpCacheEntry entry) {
return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE);
final ResponseCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(entry);
return cacheControl.isMustRevalidate();
}
public boolean proxyRevalidate(final HttpCacheEntry entry) {
return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE);
final ResponseCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(entry);
return cacheControl.isProxyRevalidate();
}
public boolean mayReturnStaleWhileRevalidating(final HttpCacheEntry entry, final Instant now) {
final Iterator<HeaderElement> it = MessageSupport.iterate(entry, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if (HeaderConstants.STALE_WHILE_REVALIDATE.equalsIgnoreCase(elt.getName())) {
try {
// in seconds
final int allowedStalenessLifetime = Integer.parseInt(elt.getValue());
if (getStaleness(entry, now).compareTo(TimeValue.ofSeconds(allowedStalenessLifetime)) <= 0) {
return true;
}
} catch (final NumberFormatException nfe) {
// skip malformed directive
}
final ResponseCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(entry);
if (cacheControl.getStaleWhileRevalidate() >= 0) {
final TimeValue staleness = getStaleness(entry, now);
if (staleness.compareTo(TimeValue.ofSeconds(cacheControl.getStaleWhileRevalidate())) <= 0) {
return true;
}
}
return false;
}
public boolean mayReturnStaleIfError(final HttpRequest request, final HttpCacheEntry entry, final Instant now) {
final RequestCacheControl requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(request);
final ResponseCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(entry);
final TimeValue staleness = getStaleness(entry, now);
return mayReturnStaleIfError(request, HeaderConstants.CACHE_CONTROL, staleness)
|| mayReturnStaleIfError(entry, HeaderConstants.CACHE_CONTROL, staleness);
return mayReturnStaleIfError(requestCacheControl, staleness) || mayReturnStaleIfError(cacheControl, staleness);
}
private boolean mayReturnStaleIfError(final MessageHeaders headers, final String name, final TimeValue staleness) {
boolean result = false;
final Iterator<HeaderElement> it = MessageSupport.iterate(headers, name);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if (HeaderConstants.STALE_IF_ERROR.equals(elt.getName())) {
try {
// in seconds
final int staleIfError = Integer.parseInt(elt.getValue());
if (staleness.compareTo(TimeValue.ofSeconds(staleIfError)) <= 0) {
result = true;
break;
}
} catch (final NumberFormatException nfe) {
// skip malformed directive
}
}
}
return result;
private boolean mayReturnStaleIfError(final CacheControl cacheControl, final TimeValue staleness) {
return cacheControl.getStaleIfError() >= 0 && staleness.compareTo(TimeValue.ofSeconds(cacheControl.getStaleIfError())) <= 0;
}
/**
@ -214,7 +186,7 @@ class CacheValidityPolicy {
protected long getAgeValue(final HttpCacheEntry entry) {
// This is a header value, we leave as-is
long ageValue = 0;
for (final Header hdr : entry.getHeaders(HeaderConstants.AGE)) {
for (final Header hdr : entry.getHeaders(HttpHeaders.AGE)) {
long hdrAge;
try {
hdrAge = Long.parseLong(hdr.getValue());
@ -251,35 +223,16 @@ class CacheValidityPolicy {
protected long getMaxAge(final HttpCacheEntry entry) {
// This is a header value, we leave as-is
long maxAge = -1;
final Iterator<HeaderElement> it = MessageSupport.iterate(entry, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName()) || "s-maxage".equals(elt.getName())) {
try {
final long currMaxAge = Long.parseLong(elt.getValue());
if (maxAge == -1 || currMaxAge < maxAge) {
maxAge = currMaxAge;
}
} catch (final NumberFormatException nfe) {
// be conservative if can't parse
maxAge = 0;
}
}
final ResponseCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(entry);
final long maxAge = cacheControl.getMaxAge();
final long sharedMaxAge = cacheControl.getSharedMaxAge();
if (sharedMaxAge == -1) {
return maxAge;
} else if (maxAge == -1) {
return sharedMaxAge;
} else {
return Math.min(maxAge, sharedMaxAge);
}
return maxAge;
}
public boolean hasCacheControlDirective(final HttpCacheEntry entry, final String directive) {
final Iterator<HeaderElement> it = MessageSupport.iterate(entry, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if (directive.equalsIgnoreCase(elt.getName())) {
return true;
}
}
return false;
}
public TimeValue getStaleness(final HttpCacheEntry entry, final Instant now) {

View File

@ -26,14 +26,11 @@
*/
package org.apache.hc.client5.http.impl.cache;
import java.util.Iterator;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http.ProtocolVersion;
import org.apache.hc.core5.http.message.MessageSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -67,24 +64,20 @@ class CacheableRequestPolicy {
return false;
}
if (request.countHeaders(HeaderConstants.PRAGMA) > 0) {
if (request.countHeaders(HttpHeaders.PRAGMA) > 0) {
LOG.debug("request with Pragma header is not serveable from cache");
return false;
}
final Iterator<HeaderElement> it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement cacheControlElement = it.next();
if (HeaderConstants.CACHE_CONTROL_NO_STORE.equalsIgnoreCase(cacheControlElement.getName())) {
LOG.debug("Request with no-store is not serveable from cache");
return false;
}
if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equalsIgnoreCase(cacheControlElement.getName())) {
LOG.debug("Request with no-cache is not serveable from cache");
return false;
}
final RequestCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(request);
if (cacheControl.isNoStore()) {
LOG.debug("Request with no-store is not serveable from cache");
return false;
}
if (cacheControl.isNoCache()) {
LOG.debug("Request with no-cache is not serveable from cache");
return false;
}
LOG.debug("Request is serveable from cache");
return true;
}

View File

@ -82,9 +82,9 @@ class CachedHttpResponseGenerator {
final TimeValue age = this.validityStrategy.getCurrentAge(entry, now);
if (TimeValue.isPositive(age)) {
if (age.compareTo(CacheValidityPolicy.MAX_AGE) >= 0) {
response.setHeader(HeaderConstants.AGE, "" + CacheValidityPolicy.MAX_AGE.toSeconds());
response.setHeader(HttpHeaders.AGE, Long.toString(CacheValidityPolicy.MAX_AGE.toSeconds()));
} else {
response.setHeader(HeaderConstants.AGE, "" + age.toSeconds());
response.setHeader(HttpHeaders.AGE, Long.toString(age.toSeconds()));
}
}
@ -111,7 +111,7 @@ class CachedHttpResponseGenerator {
// - ETag and/or Content-Location, if the header would have been sent
// in a 200 response to the same request
final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG);
final Header etagHeader = entry.getFirstHeader(HttpHeaders.ETAG);
if (etagHeader != null) {
response.addHeader(etagHeader);
}
@ -124,17 +124,17 @@ class CachedHttpResponseGenerator {
// - Expires, Cache-Control, and/or Vary, if the field-value might
// differ from that sent in any previous response for the same
// variant
final Header expiresHeader = entry.getFirstHeader(HeaderConstants.EXPIRES);
final Header expiresHeader = entry.getFirstHeader(HttpHeaders.EXPIRES);
if (expiresHeader != null) {
response.addHeader(expiresHeader);
}
final Header cacheControlHeader = entry.getFirstHeader(HeaderConstants.CACHE_CONTROL);
final Header cacheControlHeader = entry.getFirstHeader(HttpHeaders.CACHE_CONTROL);
if (cacheControlHeader != null) {
response.addHeader(cacheControlHeader);
}
final Header varyHeader = entry.getFirstHeader(HeaderConstants.VARY);
final Header varyHeader = entry.getFirstHeader(HttpHeaders.VARY);
if (varyHeader != null) {
response.addHeader(varyHeader);
}
@ -192,10 +192,6 @@ class CachedHttpResponseGenerator {
return SimpleHttpResponse.create(HttpStatus.SC_BAD_REQUEST,
"Weak eTag not compatible with PUT or DELETE requests");
case NO_CACHE_DIRECTIVE_WITH_FIELD_NAME:
return SimpleHttpResponse.create(HttpStatus.SC_BAD_REQUEST,
"No-Cache directive MUST NOT include a field name");
default:
throw new IllegalStateException(
"The request was compliant, therefore no error can be generated for it.");

View File

@ -34,6 +34,7 @@ import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.message.MessageSupport;
@ -80,11 +81,11 @@ class CachedResponseSuitabilityChecker {
if (originInsistsOnFreshness(entry)) {
return false;
}
final long maxStale = getMaxStale(request);
if (maxStale == -1) {
final RequestCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(request);
if (cacheControl.getMaxStale() == -1) {
return false;
}
return (maxStale > validityStrategy.getStaleness(entry, now).toSeconds());
return (cacheControl.getMaxStale() > validityStrategy.getStaleness(entry, now).toSeconds());
}
private boolean originInsistsOnFreshness(final HttpCacheEntry entry) {
@ -94,36 +95,8 @@ class CachedResponseSuitabilityChecker {
if (!sharedCache) {
return false;
}
return validityStrategy.proxyRevalidate(entry) ||
validityStrategy.hasCacheControlDirective(entry, "s-maxage");
}
private long getMaxStale(final HttpRequest request) {
// This is a header value, we leave as-is
long maxStale = -1;
final Iterator<HeaderElement> it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) {
if ((elt.getValue() == null || elt.getValue().trim().isEmpty()) && maxStale == -1) {
maxStale = Long.MAX_VALUE;
} else {
try {
long val = Long.parseLong(elt.getValue());
if (val < 0) {
val = 0;
}
if (maxStale == -1 || val < maxStale) {
maxStale = val;
}
} catch (final NumberFormatException nfe) {
// err on the side of preserving semantic transparency
maxStale = 0;
}
}
}
}
return maxStale;
final ResponseCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(entry);
return cacheControl.isProxyRevalidate() || cacheControl.getSharedMaxAge() >= 0;
}
/**
@ -176,68 +149,41 @@ class CachedResponseSuitabilityChecker {
"request method, entity or a 204 response");
return false;
}
final Iterator<HeaderElement> it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elt.getName())) {
LOG.debug("Response contained NO CACHE directive, cache was not suitable");
final RequestCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(request);
if (cacheControl.isNoCache()) {
LOG.debug("Response contained NO CACHE directive, cache was not suitable");
return false;
}
if (cacheControl.isNoStore()) {
LOG.debug("Response contained NO STORE directive, cache was not suitable");
return false;
}
if (cacheControl.getMaxAge() >= 0) {
if (validityStrategy.getCurrentAge(entry, now).toSeconds() > cacheControl.getMaxAge()) {
LOG.debug("Response from cache was not suitable due to max age");
return false;
}
}
if (HeaderConstants.CACHE_CONTROL_NO_STORE.equals(elt.getName())) {
LOG.debug("Response contained NO STORE directive, cache was not suitable");
if (cacheControl.getMaxStale() >= 0) {
if (validityStrategy.getFreshnessLifetime(entry).toSeconds() > cacheControl.getMaxStale()) {
LOG.debug("Response from cache was not suitable due to max stale freshness");
return false;
}
}
if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())) {
try {
// in seconds
final int maxAge = Integer.parseInt(elt.getValue());
if (validityStrategy.getCurrentAge(entry, now).toSeconds() > maxAge) {
LOG.debug("Response from cache was not suitable due to max age");
return false;
}
} catch (final NumberFormatException ex) {
// err conservatively
LOG.debug("Response from cache was malformed: {}", ex.getMessage());
return false;
}
if (cacheControl.getMinFresh() >= 0) {
if (cacheControl.getMinFresh() == 0) {
return false;
}
if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) {
try {
// in seconds
final int maxStale = Integer.parseInt(elt.getValue());
if (validityStrategy.getFreshnessLifetime(entry).toSeconds() > maxStale) {
LOG.debug("Response from cache was not suitable due to max stale freshness");
return false;
}
} catch (final NumberFormatException ex) {
// err conservatively
LOG.debug("Response from cache was malformed: {}", ex.getMessage());
return false;
}
}
if (HeaderConstants.CACHE_CONTROL_MIN_FRESH.equals(elt.getName())) {
try {
// in seconds
final long minFresh = Long.parseLong(elt.getValue());
if (minFresh < 0L) {
return false;
}
final TimeValue age = validityStrategy.getCurrentAge(entry, now);
final TimeValue freshness = validityStrategy.getFreshnessLifetime(entry);
if (freshness.toSeconds() - age.toSeconds() < minFresh) {
LOG.debug("Response from cache was not suitable due to min fresh " +
"freshness requirement");
return false;
}
} catch (final NumberFormatException ex) {
// err conservatively
LOG.debug("Response from cache was malformed: {}", ex.getMessage());
return false;
}
final TimeValue age = validityStrategy.getCurrentAge(entry, now);
final TimeValue freshness = validityStrategy.getFreshnessLifetime(entry);
if (freshness.toSeconds() - age.toSeconds() < cacheControl.getMinFresh()) {
LOG.debug("Response from cache was not suitable due to min fresh " +
"freshness requirement");
return false;
}
}
@ -310,17 +256,17 @@ class CachedResponseSuitabilityChecker {
}
private boolean hasUnsupportedConditionalHeaders(final HttpRequest request) {
return (request.getFirstHeader(HeaderConstants.IF_RANGE) != null
|| request.getFirstHeader(HeaderConstants.IF_MATCH) != null
|| hasValidDateField(request, HeaderConstants.IF_UNMODIFIED_SINCE));
return (request.getFirstHeader(HttpHeaders.IF_RANGE) != null
|| request.getFirstHeader(HttpHeaders.IF_MATCH) != null
|| hasValidDateField(request, HttpHeaders.IF_UNMODIFIED_SINCE));
}
private boolean hasSupportedEtagValidator(final HttpRequest request) {
return request.containsHeader(HeaderConstants.IF_NONE_MATCH);
return request.containsHeader(HttpHeaders.IF_NONE_MATCH);
}
private boolean hasSupportedLastModifiedValidator(final HttpRequest request) {
return hasValidDateField(request, HeaderConstants.IF_MODIFIED_SINCE);
return hasValidDateField(request, HttpHeaders.IF_MODIFIED_SINCE);
}
/**
@ -330,9 +276,9 @@ class CachedResponseSuitabilityChecker {
* @return boolean does the etag validator match
*/
private boolean etagValidatorMatches(final HttpRequest request, final HttpCacheEntry entry) {
final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG);
final Header etagHeader = entry.getFirstHeader(HttpHeaders.ETAG);
final String etag = (etagHeader != null) ? etagHeader.getValue() : null;
final Iterator<HeaderElement> it = MessageSupport.iterate(request, HeaderConstants.IF_NONE_MATCH);
final Iterator<HeaderElement> it = MessageSupport.iterate(request, HttpHeaders.IF_NONE_MATCH);
while (it.hasNext()) {
final HeaderElement elt = it.next();
final String reqEtag = elt.toString();
@ -344,20 +290,19 @@ class CachedResponseSuitabilityChecker {
}
/**
* Check entry against If-Modified-Since, if If-Modified-Since is in the future it is invalid as per
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
* Check entry against If-Modified-Since, if If-Modified-Since is in the future it is invalid
* @param request The current httpRequest being made
* @param entry the cache entry
* @param now right NOW in time
* @return boolean Does the last modified header match
*/
private boolean lastModifiedValidatorMatches(final HttpRequest request, final HttpCacheEntry entry, final Instant now) {
final Instant lastModified = DateUtils.parseStandardDate(entry, HeaderConstants.LAST_MODIFIED);
final Instant lastModified = DateUtils.parseStandardDate(entry, HttpHeaders.LAST_MODIFIED);
if (lastModified == null) {
return false;
}
for (final Header h : request.getHeaders(HeaderConstants.IF_MODIFIED_SINCE)) {
for (final Header h : request.getHeaders(HttpHeaders.IF_MODIFIED_SINCE)) {
final Instant ifModifiedSince = DateUtils.parseStandardDate(h.getValue());
if (ifModifiedSince != null) {
if (ifModifiedSince.isAfter(now) || lastModified.isAfter(ifModifiedSince)) {

View File

@ -37,7 +37,6 @@ import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.async.methods.SimpleBody;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.cache.CacheResponseStatus;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.HttpCacheStorage;
import org.apache.hc.client5.http.cache.ResourceFactory;
@ -216,7 +215,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
}
requestCompliance.makeRequestCompliant(request);
request.addHeader("Via",via);
request.addHeader(HttpHeaders.VIA, via);
if (!cacheableRequestPolicy.isServableFromCache(request)) {
LOG.debug("Request is not servable from cache");
@ -268,7 +267,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
LOG.debug("Calling the backend");
final ClassicHttpResponse backendResponse = chain.proceed(request, scope);
try {
backendResponse.addHeader("Via", generateViaHeader(backendResponse));
backendResponse.addHeader(HttpHeaders.VIA, generateViaHeader(backendResponse));
return handleBackendResponse(target, request, scope, requestDate, getCurrentDate(), backendResponse);
} catch (final IOException | RuntimeException ex) {
backendResponse.close();
@ -377,7 +376,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
responseDate = getCurrentDate();
}
backendResponse.addHeader(HeaderConstants.VIA, generateViaHeader(backendResponse));
backendResponse.addHeader(HttpHeaders.VIA, generateViaHeader(backendResponse));
final int statusCode = backendResponse.getCode();
if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) {
@ -399,7 +398,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
&& validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
try {
final SimpleHttpResponse cachedResponse = responseGenerator.generateResponse(request, cacheEntry);
cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
cachedResponse.addHeader(HttpHeaders.WARNING, "110 localhost \"Response is stale\"");
return convert(cachedResponse, scope);
} finally {
backendResponse.close();
@ -528,13 +527,13 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
try {
final Instant responseDate = getCurrentDate();
backendResponse.addHeader("Via", generateViaHeader(backendResponse));
backendResponse.addHeader(HttpHeaders.VIA, generateViaHeader(backendResponse));
if (backendResponse.getCode() != HttpStatus.SC_NOT_MODIFIED) {
return handleBackendResponse(target, request, scope, requestDate, responseDate, backendResponse);
}
final Header resultEtagHeader = backendResponse.getFirstHeader(HeaderConstants.ETAG);
final Header resultEtagHeader = backendResponse.getFirstHeader(HttpHeaders.ETAG);
if (resultEtagHeader == null) {
LOG.warn("304 response did not contain ETag");
EntityUtils.consume(backendResponse.getEntity());

View File

@ -42,7 +42,6 @@ import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.ResourceIOException;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpMessage;
@ -52,7 +51,6 @@ import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http.ProtocolVersion;
import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.http.message.MessageSupport;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.VersionInfo;
@ -191,26 +189,26 @@ public class CachingExecBase {
final HttpCacheEntry entry,
final Instant now) throws ResourceIOException {
final SimpleHttpResponse cachedResponse;
if (request.containsHeader(HeaderConstants.IF_NONE_MATCH)
|| request.containsHeader(HeaderConstants.IF_MODIFIED_SINCE)) {
if (request.containsHeader(HttpHeaders.IF_NONE_MATCH)
|| request.containsHeader(HttpHeaders.IF_MODIFIED_SINCE)) {
cachedResponse = responseGenerator.generateNotModifiedResponse(entry);
} else {
cachedResponse = responseGenerator.generateResponse(request, entry);
}
setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
if (TimeValue.isPositive(validityPolicy.getStaleness(entry, now))) {
cachedResponse.addHeader(HeaderConstants.WARNING,"110 localhost \"Response is stale\"");
cachedResponse.addHeader(HttpHeaders.WARNING,"110 localhost \"Response is stale\"");
}
// Adding Warning: 113 - "Heuristic Expiration"
if (!entry.containsHeader(HeaderConstants.WARNING)) {
if (!entry.containsHeader(HttpHeaders.WARNING)) {
final Header header = entry.getFirstHeader(HttpHeaders.DATE);
if (header != null) {
final Instant responseDate = DateUtils.parseStandardDate(header.getValue());
final TimeValue freshnessLifetime = validityPolicy.getFreshnessLifetime(entry);
final TimeValue currentAge = validityPolicy.getCurrentAge(entry, responseDate);
if (freshnessLifetime.compareTo(ONE_DAY) > 0 && currentAge.compareTo(ONE_DAY) > 0) {
cachedResponse.addHeader(HeaderConstants.WARNING,"113 localhost \"Heuristic expiration\"");
cachedResponse.addHeader(HttpHeaders.WARNING,"113 localhost \"Heuristic expiration\"");
if (LOG.isDebugEnabled()) {
LOG.debug("Added Warning 113 - Heuristic expiration to the response header.");
}
@ -245,7 +243,7 @@ public class CachingExecBase {
final HttpCacheEntry entry) throws IOException {
final SimpleHttpResponse cachedResponse = responseGenerator.generateResponse(request, entry);
setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
cachedResponse.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\"");
cachedResponse.addHeader(HttpHeaders.WARNING, "111 localhost \"Revalidation failed\"");
return cachedResponse;
}
@ -256,37 +254,24 @@ public class CachingExecBase {
}
boolean mayCallBackend(final HttpRequest request) {
final Iterator<HeaderElement> it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if ("only-if-cached".equals(elt.getName())) {
LOG.debug("Request marked only-if-cached");
return false;
}
final RequestCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(request);
if (cacheControl.isOnlyIfCached()) {
LOG.debug("Request marked only-if-cached");
return false;
}
return true;
}
boolean explicitFreshnessRequest(final HttpRequest request, final HttpCacheEntry entry, final Instant now) {
final Iterator<HeaderElement> it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) {
try {
// in seconds
final int maxStale = Integer.parseInt(elt.getValue());
final TimeValue age = validityPolicy.getCurrentAge(entry, now);
final TimeValue lifetime = validityPolicy.getFreshnessLifetime(entry);
if (age.toSeconds() - lifetime.toSeconds() > maxStale) {
return true;
}
} catch (final NumberFormatException nfe) {
return true;
}
} else if (HeaderConstants.CACHE_CONTROL_MIN_FRESH.equals(elt.getName())
|| HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())) {
final RequestCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(request);
if (cacheControl.getMaxStale() >= 0) {
final TimeValue age = validityPolicy.getCurrentAge(entry, now);
final TimeValue lifetime = validityPolicy.getFreshnessLifetime(entry);
if (age.toSeconds() - lifetime.toSeconds() > cacheControl.getMaxStale()) {
return true;
}
} else if (cacheControl.getMinFresh() >= 0 || cacheControl.getMaxAge() >= 0) {
return true;
}
return false;
}
@ -349,7 +334,7 @@ public class CachingExecBase {
return false;
}
final Header h = request.getFirstHeader(HeaderConstants.MAX_FORWARDS);
final Header h = request.getFirstHeader(HttpHeaders.MAX_FORWARDS);
return "0".equals(h != null ? h.getValue() : null);
}

View File

@ -26,16 +26,14 @@
*/
package org.apache.hc.client5.http.impl.cache;
import java.util.Iterator;
import java.util.Map;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.core5.function.Factory;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.message.MessageSupport;
class ConditionalRequestBuilder<T extends HttpRequest> {
@ -58,26 +56,17 @@ class ConditionalRequestBuilder<T extends HttpRequest> {
public T buildConditionalRequest(final T request, final HttpCacheEntry cacheEntry) {
final T newRequest = messageCopier.create(request);
final Header eTag = cacheEntry.getFirstHeader(HeaderConstants.ETAG);
final Header eTag = cacheEntry.getFirstHeader(HttpHeaders.ETAG);
if (eTag != null) {
newRequest.setHeader(HeaderConstants.IF_NONE_MATCH, eTag.getValue());
newRequest.setHeader(HttpHeaders.IF_NONE_MATCH, eTag.getValue());
}
final Header lastModified = cacheEntry.getFirstHeader(HeaderConstants.LAST_MODIFIED);
final Header lastModified = cacheEntry.getFirstHeader(HttpHeaders.LAST_MODIFIED);
if (lastModified != null) {
newRequest.setHeader(HeaderConstants.IF_MODIFIED_SINCE, lastModified.getValue());
newRequest.setHeader(HttpHeaders.IF_MODIFIED_SINCE, lastModified.getValue());
}
boolean mustRevalidate = false;
final Iterator<HeaderElement> it = MessageSupport.iterate(cacheEntry, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if (HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE.equalsIgnoreCase(elt.getName())
|| HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE.equalsIgnoreCase(elt.getName())) {
mustRevalidate = true;
break;
}
}
if (mustRevalidate) {
newRequest.addHeader(HeaderConstants.CACHE_CONTROL, HeaderConstants.CACHE_CONTROL_MAX_AGE + "=0");
final ResponseCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(cacheEntry);
if (cacheControl.isMustRevalidate() || cacheControl.isProxyRevalidate()) {
newRequest.addHeader(HttpHeaders.CACHE_CONTROL, HeaderConstants.CACHE_CONTROL_MAX_AGE + "=0");
}
return newRequest;
@ -108,7 +97,7 @@ class ConditionalRequestBuilder<T extends HttpRequest> {
etags.append(etag);
}
newRequest.setHeader(HeaderConstants.IF_NONE_MATCH, etags.toString());
newRequest.setHeader(HttpHeaders.IF_NONE_MATCH, etags.toString());
return newRequest;
}
@ -124,13 +113,13 @@ class ConditionalRequestBuilder<T extends HttpRequest> {
*/
public T buildUnconditionalRequest(final T request) {
final T newRequest = messageCopier.create(request);
newRequest.addHeader(HeaderConstants.CACHE_CONTROL,HeaderConstants.CACHE_CONTROL_NO_CACHE);
newRequest.addHeader(HeaderConstants.PRAGMA,HeaderConstants.CACHE_CONTROL_NO_CACHE);
newRequest.removeHeaders(HeaderConstants.IF_RANGE);
newRequest.removeHeaders(HeaderConstants.IF_MATCH);
newRequest.removeHeaders(HeaderConstants.IF_NONE_MATCH);
newRequest.removeHeaders(HeaderConstants.IF_UNMODIFIED_SINCE);
newRequest.removeHeaders(HeaderConstants.IF_MODIFIED_SINCE);
newRequest.addHeader(HttpHeaders.CACHE_CONTROL,HeaderConstants.CACHE_CONTROL_NO_CACHE);
newRequest.addHeader(HttpHeaders.PRAGMA,HeaderConstants.CACHE_CONTROL_NO_CACHE);
newRequest.removeHeaders(HttpHeaders.IF_RANGE);
newRequest.removeHeaders(HttpHeaders.IF_MATCH);
newRequest.removeHeaders(HttpHeaders.IF_NONE_MATCH);
newRequest.removeHeaders(HttpHeaders.IF_UNMODIFIED_SINCE);
newRequest.removeHeaders(HttpHeaders.IF_MODIFIED_SINCE);
return newRequest;
}

View File

@ -27,17 +27,14 @@
package org.apache.hc.client5.http.impl.cache;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http.ProtocolVersion;
import org.apache.hc.core5.http.message.MessageSupport;
class RequestProtocolCompliance {
private final boolean weakETagOnPutDeleteAllowed;
@ -52,9 +49,6 @@ class RequestProtocolCompliance {
this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed;
}
private static final List<String> disallowedWithNoCache =
Arrays.asList(HeaderConstants.CACHE_CONTROL_MIN_FRESH, HeaderConstants.CACHE_CONTROL_MAX_STALE, HeaderConstants.CACHE_CONTROL_MAX_AGE);
/**
* Test to see if the {@link HttpRequest} is HTTP1.1 compliant or not
* and if not, we can not continue.
@ -77,11 +71,6 @@ class RequestProtocolCompliance {
}
}
anError = requestContainsNoCacheDirectiveWithFieldName(request);
if (anError != null) {
theErrors.add(anError);
}
return theErrors;
}
@ -93,61 +82,26 @@ class RequestProtocolCompliance {
*/
public void makeRequestCompliant(final HttpRequest request) {
decrementOPTIONSMaxForwardsIfGreaterThen0(request);
stripOtherFreshnessDirectivesWithNoCache(request);
if (requestVersionIsTooLow(request) || requestMinorVersionIsTooHighMajorVersionsMatch(request)) {
request.setVersion(HttpVersion.HTTP_1_1);
}
}
private void stripOtherFreshnessDirectivesWithNoCache(final HttpRequest request) {
final List<HeaderElement> outElts = new ArrayList<>();
boolean shouldStrip = false;
final Iterator<HeaderElement> it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if (!disallowedWithNoCache.contains(elt.getName())) {
outElts.add(elt);
}
if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elt.getName())) {
shouldStrip = true;
}
}
if (!shouldStrip) {
return;
}
request.removeHeaders(HeaderConstants.CACHE_CONTROL);
request.setHeader(HeaderConstants.CACHE_CONTROL, buildHeaderFromElements(outElts));
}
private String buildHeaderFromElements(final List<HeaderElement> outElts) {
final StringBuilder newHdr = new StringBuilder();
boolean first = true;
for(final HeaderElement elt : outElts) {
if (!first) {
newHdr.append(",");
} else {
first = false;
}
newHdr.append(elt);
}
return newHdr.toString();
}
private void decrementOPTIONSMaxForwardsIfGreaterThen0(final HttpRequest request) {
if (!HeaderConstants.OPTIONS_METHOD.equals(request.getMethod())) {
return;
}
final Header maxForwards = request.getFirstHeader(HeaderConstants.MAX_FORWARDS);
final Header maxForwards = request.getFirstHeader(HttpHeaders.MAX_FORWARDS);
if (maxForwards == null) {
return;
}
request.removeHeaders(HeaderConstants.MAX_FORWARDS);
request.removeHeaders(HttpHeaders.MAX_FORWARDS);
final int currentMaxForwards = Integer.parseInt(maxForwards.getValue());
request.setHeader(HeaderConstants.MAX_FORWARDS, Integer.toString(currentMaxForwards - 1));
request.setHeader(HttpHeaders.MAX_FORWARDS, Integer.toString(currentMaxForwards - 1));
}
protected boolean requestMinorVersionIsTooHighMajorVersionsMatch(final HttpRequest request) {
@ -174,12 +128,12 @@ class RequestProtocolCompliance {
return null;
}
final Header range = request.getFirstHeader(HeaderConstants.RANGE);
final Header range = request.getFirstHeader(HttpHeaders.RANGE);
if (range == null) {
return null;
}
final Header ifRange = request.getFirstHeader(HeaderConstants.IF_RANGE);
final Header ifRange = request.getFirstHeader(HttpHeaders.IF_RANGE);
if (ifRange == null) {
return null;
}
@ -200,14 +154,14 @@ class RequestProtocolCompliance {
return null;
}
final Header ifMatch = request.getFirstHeader(HeaderConstants.IF_MATCH);
final Header ifMatch = request.getFirstHeader(HttpHeaders.IF_MATCH);
if (ifMatch != null) {
final String val = ifMatch.getValue();
if (val.startsWith("W/")) {
return RequestProtocolError.WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR;
}
} else {
final Header ifNoneMatch = request.getFirstHeader(HeaderConstants.IF_NONE_MATCH);
final Header ifNoneMatch = request.getFirstHeader(HttpHeaders.IF_NONE_MATCH);
if (ifNoneMatch == null) {
return null;
}
@ -221,14 +175,4 @@ class RequestProtocolCompliance {
return null;
}
private RequestProtocolError requestContainsNoCacheDirectiveWithFieldName(final HttpRequest request) {
final Iterator<HeaderElement> it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equalsIgnoreCase(elt.getName()) && elt.getValue() != null) {
return RequestProtocolError.NO_CACHE_DIRECTIVE_WITH_FIELD_NAME;
}
}
return null;
}
}

View File

@ -31,7 +31,6 @@ enum RequestProtocolError {
UNKNOWN,
BODY_BUT_NO_LENGTH_ERROR,
WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR,
WEAK_ETAG_AND_RANGE_ERROR,
NO_CACHE_DIRECTIVE_WITH_FIELD_NAME
WEAK_ETAG_AND_RANGE_ERROR
}

View File

@ -41,7 +41,6 @@ import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpMessage;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus;
@ -206,12 +205,12 @@ class ResponseCachingPolicy {
}
}
if (response.countHeaders(HeaderConstants.AGE) > 1) {
if (response.countHeaders(HttpHeaders.AGE) > 1) {
LOG.debug("Multiple Age headers");
return false;
}
if (response.countHeaders(HeaderConstants.EXPIRES) > 1) {
if (response.countHeaders(HttpHeaders.EXPIRES) > 1) {
LOG.debug("Multiple Expires headers");
return false;
}
@ -227,7 +226,7 @@ class ResponseCachingPolicy {
return false;
}
final Iterator<HeaderElement> it = MessageSupport.iterate(response, HeaderConstants.VARY);
final Iterator<HeaderElement> it = MessageSupport.iterate(response, HttpHeaders.VARY);
while (it.hasNext()) {
final HeaderElement elem = it.next();
if ("*".equals(elem.getName())) {
@ -296,25 +295,8 @@ class ResponseCachingPolicy {
}
}
/**
* @deprecated As of version 5.0, use {@link ResponseCachingPolicy#parseCacheControlHeader(MessageHeaders)} instead.
*/
@Deprecated
protected boolean hasCacheControlParameterFrom(final HttpMessage msg, final String[] params) {
final Iterator<HeaderElement> it = MessageSupport.iterate(msg, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elem = it.next();
for (final String param : params) {
if (param.equalsIgnoreCase(elem.getName())) {
return true;
}
}
}
return false;
}
protected boolean isExplicitlyCacheable(final HttpResponse response, final ResponseCacheControl cacheControl ) {
if (response.getFirstHeader(HeaderConstants.EXPIRES) != null) {
if (response.getFirstHeader(HttpHeaders.EXPIRES) != null) {
return true;
}
if (cacheControl == null) {
@ -392,7 +374,7 @@ class ResponseCachingPolicy {
}
if (sharedCache) {
if (request.countHeaders(HeaderConstants.AUTHORIZATION) > 0
if (request.countHeaders(HttpHeaders.AUTHORIZATION) > 0
&& cacheControl != null && !(cacheControl.getSharedMaxAge() > -1 || cacheControl.isMustRevalidate() || cacheControl.isPublic())) {
LOG.debug("Request contains private credentials");
return false;
@ -407,7 +389,7 @@ class ResponseCachingPolicy {
if (cacheControl != null) {
return false;
}
final Header expiresHdr = response.getFirstHeader(HeaderConstants.EXPIRES);
final Header expiresHdr = response.getFirstHeader(HttpHeaders.EXPIRES);
final Header dateHdr = response.getFirstHeader(HttpHeaders.DATE);
if (expiresHdr == null || dateHdr == null) {
return false;
@ -421,7 +403,7 @@ class ResponseCachingPolicy {
}
private boolean from1_0Origin(final HttpResponse response) {
final Iterator<HeaderElement> it = MessageSupport.iterate(response, HeaderConstants.VIA);
final Iterator<HeaderElement> it = MessageSupport.iterate(response, HttpHeaders.VIA);
if (it.hasNext()) {
final HeaderElement elt = it.next();
final String proto = elt.toString().split("\\s")[0];

View File

@ -90,7 +90,7 @@ class ResponseProtocolCompliance {
return;
}
final Header[] warningHeaders = response.getHeaders(HeaderConstants.WARNING);
final Header[] warningHeaders = response.getHeaders(HttpHeaders.WARNING);
if (warningHeaders == null || warningHeaders.length == 0) {
return;
@ -102,14 +102,14 @@ class ResponseProtocolCompliance {
for(final WarningValue wv : WarningValue.getWarningValues(h)) {
final Instant warnInstant = wv.getWarnDate();
if (warnInstant == null || warnInstant.equals(responseDate)) {
newWarningHeaders.add(new BasicHeader(HeaderConstants.WARNING,wv.toString()));
newWarningHeaders.add(new BasicHeader(HttpHeaders.WARNING,wv.toString()));
} else {
modified = true;
}
}
}
if (modified) {
response.removeHeaders(HeaderConstants.WARNING);
response.removeHeaders(HttpHeaders.WARNING);
for(final Header h : newWarningHeaders) {
response.addHeader(h);
}
@ -160,7 +160,7 @@ class ResponseProtocolCompliance {
private void ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(final HttpRequest request,
final HttpResponse response) throws IOException {
if (request.getFirstHeader(HeaderConstants.RANGE) != null
if (request.getFirstHeader(HttpHeaders.RANGE) != null
|| response.getCode() != HttpStatus.SC_PARTIAL_CONTENT) {
return;
}
@ -183,9 +183,9 @@ class ResponseProtocolCompliance {
}
private void ensure304DoesNotContainExtraEntityHeaders(final HttpResponse response) {
final String[] disallowedEntityHeaders = { HeaderConstants.ALLOW, HttpHeaders.CONTENT_ENCODING,
final String[] disallowedEntityHeaders = { HttpHeaders.ALLOW, HttpHeaders.CONTENT_ENCODING,
"Content-Language", HttpHeaders.CONTENT_LENGTH, "Content-MD5",
"Content-Range", HttpHeaders.CONTENT_TYPE, HeaderConstants.LAST_MODIFIED
"Content-Range", HttpHeaders.CONTENT_TYPE, HttpHeaders.LAST_MODIFIED
};
if (response.getCode() == HttpStatus.SC_NOT_MODIFIED) {
for(final String hdr : disallowedEntityHeaders) {

View File

@ -36,7 +36,6 @@ import static org.junit.jupiter.api.Assertions.assertSame;
import java.time.Instant;
import java.util.Map;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.classic.methods.HttpDelete;
import org.apache.hc.client5.http.classic.methods.HttpGet;
@ -131,7 +130,7 @@ public class TestBasicHttpCache {
final HttpRequest req = new HttpPost("/foo");
final HttpResponse resp = HttpTestUtils.make200Response();
resp.setHeader("Content-Location", "/bar");
resp.setHeader(HeaderConstants.ETAG, "\"etag\"");
resp.setHeader(HttpHeaders.ETAG, "\"etag\"");
final String key = CacheKeyGenerator.INSTANCE.generateKey(host, new HttpGet("/bar"));
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
@ -295,7 +294,7 @@ public class TestBasicHttpCache {
// Retrieve the ETag header value from the original response and assert that
// the returned cache entry has the same ETag value
final String expectedEtag = origResponse2.getFirstHeader(HttpHeaders.ETAG).getValue();
final String actualEtag = result.getFirstHeader(HeaderConstants.ETAG).getValue();
final String actualEtag = result.getFirstHeader(HttpHeaders.ETAG).getValue();
assertEquals(expectedEtag, actualEtag);
}

View File

@ -33,8 +33,8 @@ import static org.mockito.Mockito.when;
import java.util.concurrent.RejectedExecutionException;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.message.BasicHttpResponse;
@ -127,15 +127,15 @@ public class TestCacheRevalidatorBase {
@Test
public void testStaleResponse() {
final HttpResponse response1 = new BasicHttpResponse(HttpStatus.SC_OK);
response1.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
response1.addHeader(HttpHeaders.WARNING, "110 localhost \"Response is stale\"");
assertThat(impl.isStale(response1), CoreMatchers.equalTo(true));
final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_OK);
response2.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\"");
response2.addHeader(HttpHeaders.WARNING, "111 localhost \"Revalidation failed\"");
assertThat(impl.isStale(response2), CoreMatchers.equalTo(true));
final HttpResponse response3 = new BasicHttpResponse(HttpStatus.SC_OK);
response3.addHeader(HeaderConstants.WARNING, "xxx localhost \"Huh?\"");
response3.addHeader(HttpHeaders.WARNING, "xxx localhost \"Huh?\"");
assertThat(impl.isStale(response3), CoreMatchers.equalTo(false));
final HttpResponse response4 = new BasicHttpResponse(HttpStatus.SC_OK);

View File

@ -188,31 +188,6 @@ public class TestCachedResponseSuitabilityChecker {
Assertions.assertFalse(impl.canCachedResponseBeUsed(request, entry, now));
}
@Test
public void testMalformedCacheControlMaxAgeRequestHeaderCausesUnsuitableEntry() {
request.addHeader(new BasicHeader("Cache-Control", "max-age=foo"));
final Header[] headers = {
new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
new BasicHeader("Cache-Control", "max-age=3600"),
new BasicHeader("Content-Length","128")
};
entry = getEntry(headers);
Assertions.assertFalse(impl.canCachedResponseBeUsed(request, entry, now));
}
@Test
public void testMalformedCacheControlMinFreshRequestHeaderCausesUnsuitableEntry() {
request.addHeader(new BasicHeader("Cache-Control", "min-fresh=foo"));
final Header[] headers = {
new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
new BasicHeader("Cache-Control", "max-age=3600"),
new BasicHeader("Content-Length","128")
};
entry = getEntry(headers);
Assertions.assertFalse(impl.canCachedResponseBeUsed(request, entry, now));
}
@Test
public void testSuitableIfCacheEntryIsHeuristicallyFreshEnough() {
final Instant oneSecondAgo = now.minusSeconds(1);

View File

@ -31,11 +31,11 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BasicHttpRequest;
@ -151,16 +151,8 @@ public class TestConditionalRequestBuilder {
final HttpRequest result = impl.buildConditionalRequest(basicRequest, cacheEntry);
boolean foundMaxAge0 = false;
final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if ("max-age".equalsIgnoreCase(elt.getName()) && "0".equals(elt.getValue())) {
foundMaxAge0 = true;
}
}
Assertions.assertTrue(foundMaxAge0);
final RequestCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(result);
Assertions.assertEquals(0, cacheControl.getMaxAge());
}
@Test
@ -179,15 +171,8 @@ public class TestConditionalRequestBuilder {
final HttpRequest result = impl.buildConditionalRequest(basicRequest, cacheEntry);
boolean foundMaxAge0 = false;
final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if ("max-age".equalsIgnoreCase(elt.getName()) && "0".equals(elt.getValue())) {
foundMaxAge0 = true;
}
}
Assertions.assertTrue(foundMaxAge0);
final RequestCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(result);
Assertions.assertEquals(0, cacheControl.getMaxAge());
}
@Test
@ -210,15 +195,8 @@ public class TestConditionalRequestBuilder {
public void testBuildUnconditionalRequestAddsCacheControlNoCache()
throws Exception {
final HttpRequest result = impl.buildUnconditionalRequest(request);
boolean ccNoCacheFound = false;
final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if ("no-cache".equals(elt.getName())) {
ccNoCacheFound = true;
}
}
Assertions.assertTrue(ccNoCacheFound);
final RequestCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(result);
Assertions.assertTrue(cacheControl.isNoCache());
}
@Test
@ -226,7 +204,7 @@ public class TestConditionalRequestBuilder {
throws Exception {
final HttpRequest result = impl.buildUnconditionalRequest(request);
boolean ccNoCacheFound = false;
final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.PRAGMA);
final Iterator<HeaderElement> it = MessageSupport.iterate(result, HttpHeaders.PRAGMA);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if ("no-cache".equals(elt.getName())) {
@ -292,14 +270,14 @@ public class TestConditionalRequestBuilder {
final String etag3 = "\"789\"";
final Map<String,Variant> variantEntries = new HashMap<>();
variantEntries.put(etag1, new Variant("A", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag1) })));
variantEntries.put(etag2, new Variant("B", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag2) })));
variantEntries.put(etag3, new Variant("C", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag3) })));
variantEntries.put(etag1, new Variant("A", HttpTestUtils.makeCacheEntry(new BasicHeader("ETag", etag1))));
variantEntries.put(etag2, new Variant("B", HttpTestUtils.makeCacheEntry(new BasicHeader("ETag", etag2))));
variantEntries.put(etag3, new Variant("C", HttpTestUtils.makeCacheEntry(new BasicHeader("ETag", etag3))));
final HttpRequest conditional = impl.buildConditionalRequestFromVariants(request, variantEntries);
// seems like a lot of work, but necessary, check for existence and exclusiveness
String ifNoneMatch = conditional.getFirstHeader(HeaderConstants.IF_NONE_MATCH).getValue();
String ifNoneMatch = conditional.getFirstHeader(HttpHeaders.IF_NONE_MATCH).getValue();
Assertions.assertTrue(ifNoneMatch.contains(etag1));
Assertions.assertTrue(ifNoneMatch.contains(etag2));
Assertions.assertTrue(ifNoneMatch.contains(etag3));

View File

@ -37,7 +37,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@ -1301,7 +1300,7 @@ public class TestProtocolRecommendations {
resp2.setEntity(HttpTestUtils.makeBody(200));
resp2.setHeader("Content-Length","200");
resp2.setHeader("Date", DateUtils.formatStandardDate(now));
resp2.setHeader("Via","1.0 someproxy");
resp2.setHeader(HttpHeaders.VIA,"1.0 someproxy");
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2);
@ -1487,42 +1486,6 @@ public class TestProtocolRecommendations {
assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
}
/*
* "If a request includes the no-cache directive, it SHOULD NOT
* include min-fresh, max-stale, or max-age."
*
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4
*/
@Test
public void otherFreshnessRequestDirectivesNotAllowedWithNoCache() throws Exception {
final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest();
req1.setHeader("Cache-Control", "min-fresh=10, no-cache");
req1.addHeader("Cache-Control", "max-stale=0, max-age=0");
execute(req1);
final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
final ClassicHttpRequest captured = reqCapture.getValue();
boolean foundNoCache = false;
boolean foundDisallowedDirective = false;
final List<String> disallowed =
Arrays.asList("min-fresh", "max-stale", "max-age");
final Iterator<HeaderElement> it = MessageSupport.iterate(captured, HttpHeaders.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if (disallowed.contains(elt.getName())) {
foundDisallowedDirective = true;
}
if ("no-cache".equals(elt.getName())) {
foundNoCache = true;
}
}
assertTrue(foundNoCache);
assertFalse(foundDisallowedDirective);
}
/*
* "To do this, the client may include the only-if-cached directive in
* a request. If it receives this directive, a cache SHOULD either

View File

@ -404,9 +404,9 @@ public class TestProtocolRequirements {
@Test
public void testOrderOfMultipleViaHeadersIsPreservedOnRequests() throws Exception {
request.addHeader("Via", "1.0 fred, 1.1 nowhere.com (Apache/1.1)");
request.addHeader("Via", "1.0 ricky, 1.1 mertz, 1.0 lucy");
testOrderOfMultipleHeadersIsPreservedOnRequests("Via", request);
request.addHeader(HttpHeaders.VIA, "1.0 fred, 1.1 nowhere.com (Apache/1.1)");
request.addHeader(HttpHeaders.VIA, "1.0 ricky, 1.1 mertz, 1.0 lucy");
testOrderOfMultipleHeadersIsPreservedOnRequests(HttpHeaders.VIA, request);
}
@Test
@ -465,9 +465,9 @@ public class TestProtocolRequirements {
@Test
public void testOrderOfMultipleViaHeadersIsPreservedOnResponses() throws Exception {
originResponse.addHeader("Via", "1.0 fred, 1.1 nowhere.com (Apache/1.1)");
originResponse.addHeader("Via", "1.0 ricky, 1.1 mertz, 1.0 lucy");
testOrderOfMultipleHeadersIsPreservedOnResponses("Via");
originResponse.addHeader(HttpHeaders.VIA, "1.0 fred, 1.1 nowhere.com (Apache/1.1)");
originResponse.addHeader(HttpHeaders.VIA, "1.0 ricky, 1.1 mertz, 1.0 lucy");
testOrderOfMultipleHeadersIsPreservedOnResponses(HttpHeaders.VIA);
}
@Test
@ -1980,7 +1980,7 @@ public class TestProtocolRequirements {
resp1.setHeader("ETag", "\"etag\"");
resp1.setHeader("Cache-Control", "max-age=5");
resp1.setHeader("Warning", "110 squid \"stale stuff\"");
resp1.setHeader("Via", "1.1 fred");
resp1.setHeader(HttpHeaders.VIA, "1.1 fred");
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
@ -1992,7 +1992,7 @@ public class TestProtocolRequirements {
resp2.setHeader("Date", DateUtils.formatStandardDate(now));
resp2.setHeader("Server", "MockServer/1.0");
resp2.setHeader("ETag", "\"etag\"");
resp2.setHeader("Via", "1.1 fred");
resp2.setHeader(HttpHeaders.VIA, "1.1 fred");
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1);
Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(validate), Mockito.any())).thenReturn(resp2);
@ -2040,7 +2040,7 @@ public class TestProtocolRequirements {
resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo));
resp1.setHeader("ETag", "\"etag\"");
resp1.setHeader("Cache-Control", "max-age=5");
resp1.setHeader("Via", "1.1 xproxy");
resp1.setHeader(HttpHeaders.VIA, "1.1 xproxy");
resp1.setHeader("Warning", "214 xproxy \"transformed stuff\"");
final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/");
@ -2053,7 +2053,7 @@ public class TestProtocolRequirements {
resp2.setHeader("Date", DateUtils.formatStandardDate(now));
resp2.setHeader("Server", "MockServer/1.0");
resp2.setHeader("ETag", "\"etag\"");
resp1.setHeader("Via", "1.1 xproxy");
resp1.setHeader(HttpHeaders.VIA, "1.1 xproxy");
final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/");
@ -4223,37 +4223,6 @@ public class TestProtocolRequirements {
}
}
/* "Field names MUST NOT be included with the no-cache directive in a
* request."
*
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4
*/
@Test
public void testDoesNotTransmitNoCacheDirectivesWithFieldsDownstream() throws Exception {
request.setHeader("Cache-Control","no-cache=\"X-Field\"");
try {
execute(request);
} catch (final ClientProtocolException acceptable) {
}
final ArgumentCaptor<ClassicHttpRequest> reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class);
Mockito.verify(mockExecChain, Mockito.atMostOnce()).proceed(reqCapture.capture(), Mockito.any());
final List<ClassicHttpRequest> allRequests = reqCapture.getAllValues();
if (!allRequests.isEmpty()) {
final ClassicHttpRequest captured = reqCapture.getValue();
final Iterator<HeaderElement> it = MessageSupport.iterate(captured, HttpHeaders.CACHE_CONTROL);
while (it.hasNext()) {
final HeaderElement elt = it.next();
if ("no-cache".equals(elt.getName())) {
Assertions.assertNull(elt.getValue());
}
}
}
}
/* "The request includes a "no-cache" cache-control directive or, for
* compatibility with HTTP/1.0 clients, "Pragma: no-cache".... The
* server MUST NOT use a cached copy when responding to such a request."
@ -4883,7 +4852,7 @@ public class TestProtocolRequirements {
*/
@Test
public void testProperlyFormattedViaHeaderIsAddedToRequests() throws Exception {
request.removeHeaders("Via");
request.removeHeaders(HttpHeaders.VIA);
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
execute(request);
@ -4892,21 +4861,21 @@ public class TestProtocolRequirements {
Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
final ClassicHttpRequest captured = reqCapture.getValue();
final String via = captured.getFirstHeader("Via").getValue();
final String via = captured.getFirstHeader(HttpHeaders.VIA).getValue();
assertValidViaHeader(via);
}
@Test
public void testProperlyFormattedViaHeaderIsAddedToResponses() throws Exception {
originResponse.removeHeaders("Via");
originResponse.removeHeaders(HttpHeaders.VIA);
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
final ClassicHttpResponse result = execute(request);
assertValidViaHeader(result.getFirstHeader("Via").getValue());
assertValidViaHeader(result.getFirstHeader(HttpHeaders.VIA).getValue());
}
private void assertValidViaHeader(final String via) {
// Via = "Via" ":" 1#( received-protocol received-by [ comment ] )
// Via = HttpHeaders.VIA ":" 1#( received-protocol received-by [ comment ] )
// received-protocol = [ protocol-name "/" ] protocol-version
// protocol-name = token
// protocol-version = token
@ -4978,7 +4947,7 @@ public class TestProtocolRequirements {
final ClassicHttpRequest originalRequest = new BasicClassicHttpRequest("GET", "/");
originalRequest.setVersion(HttpVersion.HTTP_1_0);
request = originalRequest;
request.removeHeaders("Via");
request.removeHeaders(HttpHeaders.VIA);
Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse);
@ -4988,7 +4957,7 @@ public class TestProtocolRequirements {
Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any());
final ClassicHttpRequest captured = reqCapture.getValue();
final String via = captured.getFirstHeader("Via").getValue();
final String via = captured.getFirstHeader(HttpHeaders.VIA).getValue();
final String protocol = via.split("\\s+")[0];
final String[] protoParts = protocol.split("/");
if (protoParts.length > 1) {
@ -5007,7 +4976,7 @@ public class TestProtocolRequirements {
final ClassicHttpResponse result = execute(request);
final String via = result.getFirstHeader("Via").getValue();
final String via = result.getFirstHeader(HttpHeaders.VIA).getValue();
final String protocol = via.split("\\s+")[0];
final String[] protoParts = protocol.split("/");
Assertions.assertTrue(protoParts.length >= 1);

View File

@ -72,13 +72,6 @@ public class TestRequestProtocolCompliance {
assertEquals(Collections.emptyList(), impl.requestIsFatallyNonCompliant(req));
}
@Test
public void testRequestContainsNoCacheDirectiveWithFieldName() throws Exception {
final HttpRequest req = new BasicHttpRequest("GET", "/");
req.setHeader("Cache-Control", "no-cache=false");
assertEquals(1, impl.requestIsFatallyNonCompliant(req).size());
}
@Test
public void doesNotModifyACompliantRequest() throws Exception {
final HttpRequest req = new BasicHttpRequest("GET", "/");
@ -105,59 +98,4 @@ public class TestRequestProtocolCompliance {
assertEquals(HttpVersion.HTTP_1_1, wrapper.getVersion());
}
@Test
public void stripsMinFreshFromRequestIfNoCachePresent()
throws Exception {
final HttpRequest req = new BasicHttpRequest("GET", "/");
req.setHeader("Cache-Control", "no-cache, min-fresh=10");
final HttpRequest wrapper = BasicRequestBuilder.copy(req).build();
impl.makeRequestCompliant(wrapper);
assertEquals("no-cache",
wrapper.getFirstHeader("Cache-Control").getValue());
}
@Test
public void stripsMaxFreshFromRequestIfNoCachePresent()
throws Exception {
final HttpRequest req = new BasicHttpRequest("GET", "/");
req.setHeader("Cache-Control", "no-cache, max-stale=10");
final HttpRequest wrapper = BasicRequestBuilder.copy(req).build();
impl.makeRequestCompliant(wrapper);
assertEquals("no-cache",
wrapper.getFirstHeader("Cache-Control").getValue());
}
@Test
public void stripsMaxAgeFromRequestIfNoCachePresent()
throws Exception {
final HttpRequest req = new BasicHttpRequest("GET", "/");
req.setHeader("Cache-Control", "no-cache, max-age=10");
final HttpRequest wrapper = BasicRequestBuilder.copy(req).build();
impl.makeRequestCompliant(wrapper);
assertEquals("no-cache",
wrapper.getFirstHeader("Cache-Control").getValue());
}
@Test
public void doesNotStripMinFreshFromRequestWithoutNoCache()
throws Exception {
final HttpRequest req = new BasicHttpRequest("GET", "/");
req.setHeader("Cache-Control", "min-fresh=10");
final HttpRequest wrapper = BasicRequestBuilder.copy(req).build();
impl.makeRequestCompliant(wrapper);
assertEquals("min-fresh=10",
wrapper.getFirstHeader("Cache-Control").getValue());
}
@Test
public void correctlyStripsMinFreshFromMiddleIfNoCache()
throws Exception {
final HttpRequest req = new BasicHttpRequest("GET", "/");
req.setHeader("Cache-Control", "no-cache,min-fresh=10,no-store");
final HttpRequest wrapper = BasicRequestBuilder.copy(req).build();
impl.makeRequestCompliant(wrapper);
assertEquals("no-cache,no-store",
wrapper.getFirstHeader("Cache-Control").getValue());
}
}

View File

@ -738,14 +738,14 @@ public class TestResponseCachingPolicy {
@Test
public void getsWithQueryParametersFrom1_0OriginsViaProxiesAreNotCacheable() {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response.setHeader("Via", "1.0 someproxy");
response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
Assertions.assertFalse(policy.isResponseCacheable(request, response));
}
@Test
public void headsWithQueryParametersFrom1_0OriginsViaProxiesAreNotCacheable() {
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response.setHeader("Via", "1.0 someproxy");
response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
Assertions.assertFalse(policy.isResponseCacheable(request, response));
}
@ -754,7 +754,7 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatStandardDate(now));
response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
Assertions.assertTrue(policy.isResponseCacheable(request, response));
}
@ -764,7 +764,7 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatStandardDate(now));
response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
Assertions.assertTrue(policy.isResponseCacheable(request, response));
}
@ -774,7 +774,7 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatStandardDate(now));
response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
Assertions.assertFalse(policy.isResponseCacheable(request, response));
}
@ -784,7 +784,7 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatStandardDate(now));
response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
response.setHeader(HttpHeaders.VIA, "1.0 someproxy");
Assertions.assertFalse(policy.isResponseCacheable(request, response));
}
@ -793,7 +793,7 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatStandardDate(now));
response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy");
Assertions.assertTrue(policy.isResponseCacheable(request, response));
}
@ -803,7 +803,7 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatStandardDate(now));
response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy");
Assertions.assertTrue(policy.isResponseCacheable(request, response));
}
@ -813,7 +813,7 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatStandardDate(now));
response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy");
Assertions.assertFalse(policy.isResponseCacheable(request, response));
}
@ -823,7 +823,7 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatStandardDate(now));
response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy");
Assertions.assertFalse(policy.isResponseCacheable(request, response));
}
@ -834,7 +834,7 @@ public class TestResponseCachingPolicy {
response.setVersion(HttpVersion.HTTP_1_0);
response.setHeader("Date", DateUtils.formatStandardDate(now));
response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
response.setHeader("Via", "1.1 someproxy");
response.setHeader(HttpHeaders.VIA, "1.1 someproxy");
Assertions.assertTrue(policy.isResponseCacheable(request, response));
}
@ -846,7 +846,7 @@ public class TestResponseCachingPolicy {
response.setVersion(HttpVersion.HTTP_1_0);
response.setHeader("Date", DateUtils.formatStandardDate(now));
response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow));
response.setHeader("Via", "1.1 someproxy");
response.setHeader(HttpHeaders.VIA, "1.1 someproxy");
Assertions.assertTrue(policy.isResponseCacheable(request, response));
}