Request / response Cache-Control APIs
This commit is contained in:
parent
f0d76de66d
commit
3f81f21cab
|
@ -56,10 +56,20 @@ public class HeaderConstants {
|
|||
public static final String VARY = "Vary";
|
||||
public static final String ALLOW = "Allow";
|
||||
public static final String VIA = "Via";
|
||||
/**
|
||||
* @deprecated Use {@link #CACHE_CONTROL_PUBLIC}
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String PUBLIC = "public";
|
||||
/**
|
||||
* @deprecated Use {@link #CACHE_CONTROL_PRIVATE}
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String PRIVATE = "private";
|
||||
|
||||
public static final String CACHE_CONTROL = "Cache-Control";
|
||||
public static final String CACHE_CONTROL_PUBLIC = "public";
|
||||
public static final String CACHE_CONTROL_PRIVATE = "private";
|
||||
public static final String CACHE_CONTROL_NO_STORE = "no-store";
|
||||
public static final String CACHE_CONTROL_NO_CACHE = "no-cache";
|
||||
public static final String CACHE_CONTROL_MAX_AGE = "max-age";
|
||||
|
@ -68,7 +78,18 @@ public class HeaderConstants {
|
|||
public static final String CACHE_CONTROL_MIN_FRESH = "min-fresh";
|
||||
public static final String CACHE_CONTROL_MUST_REVALIDATE = "must-revalidate";
|
||||
public static final String CACHE_CONTROL_PROXY_REVALIDATE = "proxy-revalidate";
|
||||
public static final String CACHE_CONTROL_STALE_IF_ERROR = "stale-if-error";
|
||||
public static final String CACHE_CONTROL_STALE_WHILE_REVALIDATE = "stale-while-revalidate";
|
||||
public static final String CACHE_CONTROL_ONLY_IF_CACHED = "only-if-cached";
|
||||
/**
|
||||
* @deprecated Use {@link #CACHE_CONTROL_STALE_IF_ERROR}
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String STALE_IF_ERROR = "stale-if-error";
|
||||
/**
|
||||
* @deprecated Use {@link #CACHE_CONTROL_STALE_WHILE_REVALIDATE}
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String STALE_WHILE_REVALIDATE = "stale-while-revalidate";
|
||||
|
||||
public static final String WARNING = "Warning";
|
||||
|
|
|
@ -31,229 +31,42 @@ import org.apache.hc.core5.annotation.Contract;
|
|||
import org.apache.hc.core5.annotation.Internal;
|
||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents the values of the Cache-Control header in an HTTP response, which indicate whether and for how long
|
||||
* Represents the values of the Cache-Control header in an HTTP message, which indicate whether and for how long
|
||||
* the response can be cached by the client and intermediary proxies.
|
||||
* <p>
|
||||
* The class provides methods to retrieve the maximum age of the response and the maximum age that applies to shared
|
||||
* caches. The values are expressed in seconds, with -1 indicating that the value was not specified in the header.
|
||||
* <p>
|
||||
* Instances of this class are immutable, meaning that their values cannot be changed once they are set. To create an
|
||||
* instance, use one of the constructors that take the desired values as arguments. Alternatively, use the default
|
||||
* constructor to create an instance with both values set to -1, indicating that the header was not present in the
|
||||
* response.
|
||||
* <p>
|
||||
* Example usage:
|
||||
* <pre>
|
||||
* HttpResponse response = httpClient.execute(httpGet);
|
||||
* CacheControlHeader cacheControlHeader = CacheControlHeaderParser.INSTANCE.parse(response.getHeaders("Cache-Control"));
|
||||
* long maxAge = cacheControlHeader.getMaxAge();
|
||||
* long sharedMaxAge = cacheControlHeader.getSharedMaxAge();
|
||||
* </pre>
|
||||
*
|
||||
* @since 5.3
|
||||
*/
|
||||
@Internal
|
||||
@Contract(threading = ThreadingBehavior.IMMUTABLE)
|
||||
final class CacheControl {
|
||||
|
||||
/**
|
||||
* The max-age directive value.
|
||||
*/
|
||||
private final long maxAge;
|
||||
/**
|
||||
* The shared-max-age directive value.
|
||||
*/
|
||||
private final long sharedMaxAge;
|
||||
/**
|
||||
* The isNoCache flag indicates whether the Cache-Control header includes the no-cache directive.
|
||||
*/
|
||||
private final boolean noCache;
|
||||
/**
|
||||
* The isNoStore flag indicates whether the Cache-Control header includes the no-store directive.
|
||||
*/
|
||||
private final boolean noStore;
|
||||
/**
|
||||
* The isPrivate flag indicates whether the Cache-Control header includes the private directive.
|
||||
*/
|
||||
private final boolean cachePrivate;
|
||||
/**
|
||||
* Indicates whether the Cache-Control header includes the "must-revalidate" directive.
|
||||
*/
|
||||
private final boolean mustRevalidate;
|
||||
/**
|
||||
* Indicates whether the Cache-Control header includes the "proxy-revalidate" directive.
|
||||
*/
|
||||
private final boolean proxyRevalidate;
|
||||
/**
|
||||
* Indicates whether the Cache-Control header includes the "public" directive.
|
||||
*/
|
||||
private final boolean cachePublic;
|
||||
/**
|
||||
* The number of seconds that a stale response is considered fresh for the purpose
|
||||
* of serving a response while a revalidation request is made to the origin server.
|
||||
*/
|
||||
private final long staleWhileRevalidate;
|
||||
|
||||
/**
|
||||
* A set of field names specified in the "no-cache" directive of the Cache-Control header.
|
||||
*/
|
||||
private final Set<String> noCacheFields;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code CacheControl} with default values.
|
||||
* The default values are: max-age=-1, shared-max-age=-1, must-revalidate=false, no-cache=false,
|
||||
* no-store=false, private=false, proxy-revalidate=false, public=false, stale_while_revalidate=-1,
|
||||
* and noCacheFields as an empty set.
|
||||
*/
|
||||
public CacheControl() {
|
||||
this(-1, -1, false, false, false, false, false, false,-1, Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code CacheControl} with the specified max-age, shared-max-age, no-cache, no-store,
|
||||
* private, must-revalidate, proxy-revalidate, and public values.
|
||||
*
|
||||
* @param maxAge The max-age value from the Cache-Control header.
|
||||
* @param sharedMaxAge The shared-max-age value from the Cache-Control header.
|
||||
* @param mustRevalidate The must-revalidate value from the Cache-Control header.
|
||||
* @param noCache The no-cache value from the Cache-Control header.
|
||||
* @param noStore The no-store value from the Cache-Control header.
|
||||
* @param cachePrivate The private value from the Cache-Control header.
|
||||
* @param proxyRevalidate The proxy-revalidate value from the Cache-Control header.
|
||||
* @param cachePublic The public value from the Cache-Control header.
|
||||
* @param noCacheFields The set of field names specified in the "no-cache" directive of the Cache-Control header.
|
||||
*/
|
||||
public CacheControl(final long maxAge, final long sharedMaxAge, final boolean mustRevalidate, final boolean noCache, final boolean noStore,
|
||||
final boolean cachePrivate, final boolean proxyRevalidate, final boolean cachePublic,
|
||||
final long staleWhileRevalidate,
|
||||
final Set<String> noCacheFields) {
|
||||
this.maxAge = maxAge;
|
||||
this.sharedMaxAge = sharedMaxAge;
|
||||
this.noCache = noCache;
|
||||
this.noStore = noStore;
|
||||
this.cachePrivate = cachePrivate;
|
||||
this.mustRevalidate = mustRevalidate;
|
||||
this.proxyRevalidate = proxyRevalidate;
|
||||
this.cachePublic = cachePublic;
|
||||
this.staleWhileRevalidate = staleWhileRevalidate;
|
||||
this.noCacheFields = Collections.unmodifiableSet(noCacheFields);
|
||||
}
|
||||
|
||||
interface CacheControl {
|
||||
|
||||
/**
|
||||
* Returns the max-age value from the Cache-Control header.
|
||||
*
|
||||
* @return The max-age value.
|
||||
*/
|
||||
public long getMaxAge() {
|
||||
return maxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shared-max-age value from the Cache-Control header.
|
||||
*
|
||||
* @return The shared-max-age value.
|
||||
*/
|
||||
public long getSharedMaxAge() {
|
||||
return sharedMaxAge;
|
||||
}
|
||||
long getMaxAge();
|
||||
|
||||
/**
|
||||
* Returns the no-cache flag from the Cache-Control header.
|
||||
*
|
||||
* @return The no-cache flag.
|
||||
*/
|
||||
public boolean isNoCache() {
|
||||
return noCache;
|
||||
}
|
||||
boolean isNoCache();
|
||||
|
||||
/**
|
||||
* Returns the no-store flag from the Cache-Control header.
|
||||
*
|
||||
* @return The no-store flag.
|
||||
*/
|
||||
public boolean isNoStore() {
|
||||
return noStore;
|
||||
}
|
||||
boolean isNoStore();
|
||||
|
||||
/**
|
||||
* Returns the private flag from the Cache-Control header.
|
||||
* Returns the stale-if-error value from the Cache-Control header.
|
||||
*
|
||||
* @return The private flag.
|
||||
* @return The stale-if-error value.
|
||||
*/
|
||||
public boolean isCachePrivate() {
|
||||
return cachePrivate;
|
||||
}
|
||||
long getStaleIfError();
|
||||
|
||||
/**
|
||||
* Returns whether the must-revalidate directive is present in the Cache-Control header.
|
||||
*
|
||||
* @return {@code true} if the must-revalidate directive is present, otherwise {@code false}
|
||||
*/
|
||||
public boolean isMustRevalidate() {
|
||||
return mustRevalidate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the proxy-revalidate value is set in the Cache-Control header.
|
||||
*
|
||||
* @return {@code true} if proxy-revalidate is set, {@code false} otherwise.
|
||||
*/
|
||||
public boolean isProxyRevalidate() {
|
||||
return proxyRevalidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the public value is set in the Cache-Control header.
|
||||
*
|
||||
* @return {@code true} if public is set, {@code false} otherwise.
|
||||
*/
|
||||
public boolean isPublic() {
|
||||
return cachePublic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stale-while-revalidate value from the Cache-Control header.
|
||||
*
|
||||
* @return The stale-while-revalidate value.
|
||||
*/
|
||||
public long getStaleWhileRevalidate() {
|
||||
return staleWhileRevalidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable set of field names specified in the "no-cache" directive of the Cache-Control header.
|
||||
*
|
||||
* @return The set of field names specified in the "no-cache" directive.
|
||||
*/
|
||||
public Set<String> getNoCacheFields() {
|
||||
return noCacheFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the {@code CacheControl} object, including the max-age, shared-max-age, no-cache,
|
||||
* no-store, private, must-revalidate, proxy-revalidate, public values, and noCacheFields.
|
||||
*
|
||||
* @return A string representation of the object.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CacheControl{" +
|
||||
"maxAge=" + maxAge +
|
||||
", sharedMaxAge=" + sharedMaxAge +
|
||||
", noCache=" + noCache +
|
||||
", noCacheFields=" + noCacheFields +
|
||||
", noStore=" + noStore +
|
||||
", cachePrivate=" + cachePrivate +
|
||||
", mustRevalidate=" + mustRevalidate +
|
||||
", proxyRevalidate=" + proxyRevalidate +
|
||||
", cachePublic=" + cachePublic +
|
||||
", stale_while_revalidate=" + staleWhileRevalidate +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ import java.util.BitSet;
|
|||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.apache.hc.client5.http.cache.HeaderConstants;
|
||||
import org.apache.hc.core5.annotation.Contract;
|
||||
|
@ -37,6 +38,9 @@ import org.apache.hc.core5.annotation.Internal;
|
|||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||
import org.apache.hc.core5.http.FormattedHeader;
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.http.HttpHeaders;
|
||||
import org.apache.hc.core5.http.HttpRequest;
|
||||
import org.apache.hc.core5.http.HttpResponse;
|
||||
import org.apache.hc.core5.http.message.ParserCursor;
|
||||
import org.apache.hc.core5.util.Args;
|
||||
import org.apache.hc.core5.util.CharArrayBuffer;
|
||||
|
@ -51,14 +55,14 @@ import org.slf4j.LoggerFactory;
|
|||
* This class is thread-safe and has a singleton instance ({@link #INSTANCE}).
|
||||
* </p>
|
||||
* <p>
|
||||
* The {@link #parse(Iterator)} method takes an HTTP header and returns a {@link CacheControl} object containing
|
||||
* The {@link #parseResponse(Iterator)} method takes an HTTP header and returns a {@link ResponseCacheControl} object containing
|
||||
* the relevant caching directives. The header can be either a {@link FormattedHeader} object, which contains a
|
||||
* pre-parsed {@link CharArrayBuffer}, or a plain {@link Header} object, in which case the value will be parsed and
|
||||
* stored in a new {@link CharArrayBuffer}.
|
||||
* </p>
|
||||
* <p>
|
||||
* This parser only supports two directives: "max-age" and "s-maxage". If either of these directives are present in the
|
||||
* header, their values will be parsed and stored in the {@link CacheControl} object. If both directives are
|
||||
* header, their values will be parsed and stored in the {@link ResponseCacheControl} object. If both directives are
|
||||
* present, the value of "s-maxage" takes precedence.
|
||||
* </p>
|
||||
*/
|
||||
|
@ -102,39 +106,7 @@ class CacheControlHeaderParser {
|
|||
this.tokenParser = Tokenizer.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified header and returns a new {@link CacheControl} instance containing the relevant caching
|
||||
* <p>
|
||||
* directives.
|
||||
*
|
||||
* <p>The returned {@link CacheControl} instance will contain the values for "max-age" and "s-maxage" caching
|
||||
* directives parsed from the input header. If the input header does not contain any caching directives or if the
|
||||
* <p>
|
||||
* directives are malformed, the returned {@link CacheControl} instance will have default values for "max-age" and
|
||||
* <p>
|
||||
* "s-maxage" (-1).</p>
|
||||
*
|
||||
* @param headerIterator the header to parse, cannot be {@code null}
|
||||
* @return a new {@link CacheControl} instance containing the relevant caching directives parsed from the header
|
||||
* @throws IllegalArgumentException if the input header is {@code null}
|
||||
*/
|
||||
public final CacheControl parse(final Iterator<Header> headerIterator) {
|
||||
Args.notNull(headerIterator, "headerIterator");
|
||||
|
||||
// Initialize variables to hold the Cache-Control directives
|
||||
long maxAge = -1;
|
||||
long sharedMaxAge = -1;
|
||||
boolean noCache = false;
|
||||
boolean noStore = false;
|
||||
boolean cachePrivate = false;
|
||||
boolean mustRevalidate = false;
|
||||
boolean proxyRevalidate = false;
|
||||
boolean cachePublic = false;
|
||||
long staleWhileRevalidate = -1;
|
||||
// Declare a new Set variable at the beginning of the method to store the field-names
|
||||
final Set<String> noCacheFields = new HashSet<>();
|
||||
|
||||
// Iterate over each header
|
||||
public void parse(final Iterator<Header> headerIterator, final BiConsumer<String, String> consumer) {
|
||||
while (headerIterator.hasNext()) {
|
||||
final Header header = headerIterator.next();
|
||||
final CharArrayBuffer buffer;
|
||||
|
@ -145,7 +117,7 @@ class CacheControlHeaderParser {
|
|||
} else {
|
||||
final String s = header.getValue();
|
||||
if (s == null) {
|
||||
return new CacheControl();
|
||||
return;
|
||||
}
|
||||
buffer = new CharArrayBuffer(s.length());
|
||||
buffer.append(s);
|
||||
|
@ -166,17 +138,40 @@ class CacheControlHeaderParser {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Update the Cache-Control directives based on the current header
|
||||
consumer.accept(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified response header and returns a new {@link ResponseCacheControl} instance containing
|
||||
* the relevant caching directives.
|
||||
*
|
||||
* <p>The returned {@link ResponseCacheControl} instance will contain the values for "max-age" and "s-maxage"
|
||||
* caching directives parsed from the input header. If the input header does not contain any caching directives
|
||||
* or if the directives are malformed, the returned {@link ResponseCacheControl} instance will have default values
|
||||
* for "max-age" and "s-maxage" (-1).</p>
|
||||
*
|
||||
* @param headerIterator the header to parse, cannot be {@code null}
|
||||
* @return a new {@link ResponseCacheControl} instance containing the relevant caching directives parsed
|
||||
* from the response header
|
||||
* @throws IllegalArgumentException if the input header is {@code null}
|
||||
*/
|
||||
public final ResponseCacheControl parseResponse(final Iterator<Header> headerIterator) {
|
||||
Args.notNull(headerIterator, "headerIterator");
|
||||
final ResponseCacheControl.Builder builder = new ResponseCacheControl.Builder();
|
||||
parse(headerIterator, (name, value) -> {
|
||||
if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_S_MAX_AGE)) {
|
||||
sharedMaxAge = parseSeconds(name, value);
|
||||
builder.setSharedMaxAge(parseSeconds(name, value));
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_MAX_AGE)) {
|
||||
maxAge = parseSeconds(name, value);
|
||||
builder.setMaxAge(parseSeconds(name, value));
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE)) {
|
||||
mustRevalidate = true;
|
||||
builder.setMustRevalidate(true);
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_NO_CACHE)) {
|
||||
noCache = true;
|
||||
builder.setNoCache(true);
|
||||
if (value != null) {
|
||||
final Tokenizer.Cursor valCursor = new ParserCursor(0, value.length());
|
||||
final Set<String> noCacheFields = new HashSet<>();
|
||||
while (!valCursor.atEnd()) {
|
||||
final String token = tokenParser.parseToken(value, valCursor, VALUE_DELIMS);
|
||||
if (!TextUtils.isBlank(token)) {
|
||||
|
@ -186,33 +181,78 @@ class CacheControlHeaderParser {
|
|||
valCursor.updatePos(valCursor.getPos() + 1);
|
||||
}
|
||||
}
|
||||
builder.setNoCacheFields(noCacheFields);
|
||||
}
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_NO_STORE)) {
|
||||
noStore = true;
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.PRIVATE)) {
|
||||
cachePrivate = true;
|
||||
builder.setNoStore(true);
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_PRIVATE)) {
|
||||
builder.setCachePrivate(true);
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE)) {
|
||||
proxyRevalidate = true;
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.PUBLIC)) {
|
||||
cachePublic = true;
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.STALE_WHILE_REVALIDATE)) {
|
||||
staleWhileRevalidate = parseSeconds(name, value);
|
||||
builder.setProxyRevalidate(true);
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_PUBLIC)) {
|
||||
builder.setCachePublic(true);
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_STALE_WHILE_REVALIDATE)) {
|
||||
builder.setStaleWhileRevalidate(parseSeconds(name, value));
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_STALE_IF_ERROR)) {
|
||||
builder.setStaleIfError(parseSeconds(name, value));
|
||||
}
|
||||
});
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public final ResponseCacheControl parse(final HttpResponse response) {
|
||||
return parseResponse(response.headerIterator(HttpHeaders.CACHE_CONTROL));
|
||||
}
|
||||
// Return a single CacheControl object with the combined directives
|
||||
return new CacheControl(maxAge, sharedMaxAge, mustRevalidate, noCache, noStore, cachePrivate, proxyRevalidate, cachePublic, staleWhileRevalidate, noCacheFields);
|
||||
|
||||
/**
|
||||
* Parses the specified request header and returns a new {@link RequestCacheControl} instance containing
|
||||
* the relevant caching directives.
|
||||
*
|
||||
* @param headerIterator the header to parse, cannot be {@code null}
|
||||
* @return a new {@link RequestCacheControl} instance containing the relevant caching directives parsed
|
||||
* from the request header
|
||||
* @throws IllegalArgumentException if the input header is {@code null}
|
||||
*/
|
||||
public final RequestCacheControl parseRequest(final Iterator<Header> headerIterator) {
|
||||
Args.notNull(headerIterator, "headerIterator");
|
||||
final RequestCacheControl.Builder builder = new RequestCacheControl.Builder();
|
||||
parse(headerIterator, (name, value) -> {
|
||||
if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_MAX_AGE)) {
|
||||
builder.setMaxAge(parseSeconds(name, value));
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_MAX_STALE)) {
|
||||
builder.setMaxStale(parseSeconds(name, value));
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_MIN_FRESH)) {
|
||||
builder.setMinFresh(parseSeconds(name, value));
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_NO_STORE)) {
|
||||
builder.setNoStore(true);
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_NO_CACHE)) {
|
||||
builder.setNoCache(true);
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_ONLY_IF_CACHED)) {
|
||||
builder.setOnlyIfCached(true);
|
||||
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_STALE_IF_ERROR)) {
|
||||
builder.setStaleIfError(parseSeconds(name, value));
|
||||
}
|
||||
});
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public final RequestCacheControl parse(final HttpRequest request) {
|
||||
return parseRequest(request.headerIterator(HttpHeaders.CACHE_CONTROL));
|
||||
}
|
||||
|
||||
private static long parseSeconds(final String name, final String value) {
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
return value != null ? Long.parseLong(value) : -1;
|
||||
final long n = Long.parseLong(value);
|
||||
return n < 0 ? -1 : n;
|
||||
} catch (final NumberFormatException e) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Directive {} was malformed: {}", name, value);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -407,7 +407,7 @@ public class CachingExecBase {
|
|||
if (it == null || !it.hasNext()) {
|
||||
return false;
|
||||
} else {
|
||||
final CacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parse(it);
|
||||
final ResponseCacheControl cacheControl = CacheControlHeaderParser.INSTANCE.parseResponse(it);
|
||||
return cacheControl.isNoCache();
|
||||
}
|
||||
}
|
||||
|
|
228
httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/RequestCacheControl.java
vendored
Normal file
228
httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/RequestCacheControl.java
vendored
Normal file
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* 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.hc.client5.http.impl.cache;
|
||||
|
||||
import org.apache.hc.core5.annotation.Contract;
|
||||
import org.apache.hc.core5.annotation.Internal;
|
||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||
|
||||
/**
|
||||
* Represents the values of the Cache-Control header in an HTTP request, which indicate whether and for how long
|
||||
* the client is willing to cache the response.
|
||||
*
|
||||
* @since 5.3
|
||||
*/
|
||||
@Internal
|
||||
@Contract(threading = ThreadingBehavior.IMMUTABLE)
|
||||
final class RequestCacheControl implements CacheControl {
|
||||
|
||||
private final long maxAge;
|
||||
private final long maxStale;
|
||||
private final long minFresh;
|
||||
private final boolean noCache;
|
||||
private final boolean noStore;
|
||||
private final boolean onlyIfCached;
|
||||
private final long staleIfError;
|
||||
|
||||
RequestCacheControl(final long maxAge, final long maxStale, final long minFresh, final boolean noCache,
|
||||
final boolean noStore, final boolean onlyIfCached, final long staleIfError) {
|
||||
this.maxAge = maxAge;
|
||||
this.maxStale = maxStale;
|
||||
this.minFresh = minFresh;
|
||||
this.noCache = noCache;
|
||||
this.noStore = noStore;
|
||||
this.onlyIfCached = onlyIfCached;
|
||||
this.staleIfError = staleIfError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max-age value from the Cache-Control header.
|
||||
*
|
||||
* @return The max-age value.
|
||||
*/
|
||||
@Override
|
||||
public long getMaxAge() {
|
||||
return maxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max-stale value from the Cache-Control header.
|
||||
*
|
||||
* @return The max-stale value.
|
||||
*/
|
||||
public long getMaxStale() {
|
||||
return maxStale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the min-fresh value from the Cache-Control header.
|
||||
*
|
||||
* @return The min-fresh value.
|
||||
*/
|
||||
public long getMinFresh() {
|
||||
return minFresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the no-cache flag from the Cache-Control header.
|
||||
*
|
||||
* @return The no-cache flag.
|
||||
*/
|
||||
@Override
|
||||
public boolean isNoCache() {
|
||||
return noCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the no-store flag from the Cache-Control header.
|
||||
*
|
||||
* @return The no-store flag.
|
||||
*/
|
||||
@Override
|
||||
public boolean isNoStore() {
|
||||
return noStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the only-if-cached flag from the Cache-Control header.
|
||||
*
|
||||
* @return The only-if-cached flag.
|
||||
*/
|
||||
public boolean isOnlyIfCached() {
|
||||
return onlyIfCached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stale-if-error value from the Cache-Control header.
|
||||
*
|
||||
* @return The stale-if-error value.
|
||||
*/
|
||||
@Override
|
||||
public long getStaleIfError() {
|
||||
return staleIfError;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RequestCacheControl{" +
|
||||
"maxAge=" + maxAge +
|
||||
", maxStale=" + maxStale +
|
||||
", minFresh=" + minFresh +
|
||||
", noCache=" + noCache +
|
||||
", noStore=" + noStore +
|
||||
", onlyIfCached=" + onlyIfCached +
|
||||
", staleIfError=" + staleIfError +
|
||||
'}';
|
||||
}
|
||||
|
||||
static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
|
||||
private long maxAge = -1;
|
||||
private long maxStale = -1;
|
||||
private long minFresh = -1;
|
||||
private boolean noCache;
|
||||
private boolean noStore;
|
||||
private boolean onlyIfCached;
|
||||
private long staleIfError = -1;
|
||||
|
||||
Builder() {
|
||||
}
|
||||
|
||||
public long getMaxAge() {
|
||||
return maxAge;
|
||||
}
|
||||
|
||||
public Builder setMaxAge(final long maxAge) {
|
||||
this.maxAge = maxAge;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getMaxStale() {
|
||||
return maxStale;
|
||||
}
|
||||
|
||||
public Builder setMaxStale(final long maxStale) {
|
||||
this.maxStale = maxStale;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getMinFresh() {
|
||||
return minFresh;
|
||||
}
|
||||
|
||||
public Builder setMinFresh(final long minFresh) {
|
||||
this.minFresh = minFresh;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isNoCache() {
|
||||
return noCache;
|
||||
}
|
||||
|
||||
public Builder setNoCache(final boolean noCache) {
|
||||
this.noCache = noCache;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isNoStore() {
|
||||
return noStore;
|
||||
}
|
||||
|
||||
public Builder setNoStore(final boolean noStore) {
|
||||
this.noStore = noStore;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isOnlyIfCached() {
|
||||
return onlyIfCached;
|
||||
}
|
||||
|
||||
public Builder setOnlyIfCached(final boolean onlyIfCached) {
|
||||
this.onlyIfCached = onlyIfCached;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getStaleIfError() {
|
||||
return staleIfError;
|
||||
}
|
||||
|
||||
public Builder setStaleIfError(final long staleIfError) {
|
||||
this.staleIfError = staleIfError;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RequestCacheControl build() {
|
||||
return new RequestCacheControl(maxAge, maxStale, minFresh, noCache, noStore, onlyIfCached, staleIfError);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
382
httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCacheControl.java
vendored
Normal file
382
httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCacheControl.java
vendored
Normal file
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* 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.hc.client5.http.impl.cache;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hc.core5.annotation.Contract;
|
||||
import org.apache.hc.core5.annotation.Internal;
|
||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||
|
||||
/**
|
||||
* Represents the values of the Cache-Control header in an HTTP response, which indicate whether and for how long
|
||||
* the response can be cached by the client and intermediary proxies.
|
||||
* <p>
|
||||
* The class provides methods to retrieve the maximum age of the response and the maximum age that applies to shared
|
||||
* caches. The values are expressed in seconds, with -1 indicating that the value was not specified in the header.
|
||||
* <p>
|
||||
* Instances of this class are immutable, meaning that their values cannot be changed once they are set. To create an
|
||||
* instance, use one of the constructors that take the desired values as arguments. Alternatively, use the default
|
||||
* constructor to create an instance with both values set to -1, indicating that the header was not present in the
|
||||
* response.
|
||||
*
|
||||
* @since 5.3
|
||||
*/
|
||||
@Internal
|
||||
@Contract(threading = ThreadingBehavior.IMMUTABLE)
|
||||
final class ResponseCacheControl implements CacheControl {
|
||||
|
||||
/**
|
||||
* The max-age directive value.
|
||||
*/
|
||||
private final long maxAge;
|
||||
/**
|
||||
* The shared-max-age directive value.
|
||||
*/
|
||||
private final long sharedMaxAge;
|
||||
/**
|
||||
* The isNoCache flag indicates whether the Cache-Control header includes the no-cache directive.
|
||||
*/
|
||||
private final boolean noCache;
|
||||
/**
|
||||
* The isNoStore flag indicates whether the Cache-Control header includes the no-store directive.
|
||||
*/
|
||||
private final boolean noStore;
|
||||
/**
|
||||
* The isPrivate flag indicates whether the Cache-Control header includes the private directive.
|
||||
*/
|
||||
private final boolean cachePrivate;
|
||||
/**
|
||||
* Indicates whether the Cache-Control header includes the "must-revalidate" directive.
|
||||
*/
|
||||
private final boolean mustRevalidate;
|
||||
/**
|
||||
* Indicates whether the Cache-Control header includes the "proxy-revalidate" directive.
|
||||
*/
|
||||
private final boolean proxyRevalidate;
|
||||
/**
|
||||
* Indicates whether the Cache-Control header includes the "public" directive.
|
||||
*/
|
||||
private final boolean cachePublic;
|
||||
/**
|
||||
* The number of seconds that a stale response is considered fresh for the purpose
|
||||
* of serving a response while a revalidation request is made to the origin server.
|
||||
*/
|
||||
private final long staleWhileRevalidate;
|
||||
/**
|
||||
* The number of seconds that a cached stale response MAY be used to satisfy the request,
|
||||
* regardless of other freshness information..
|
||||
*/
|
||||
private final long staleIfError;
|
||||
/**
|
||||
* A set of field names specified in the "no-cache" directive of the Cache-Control header.
|
||||
*/
|
||||
private final Set<String> noCacheFields;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code CacheControl} with the specified values.
|
||||
*
|
||||
* @param maxAge The max-age value from the Cache-Control header.
|
||||
* @param sharedMaxAge The shared-max-age value from the Cache-Control header.
|
||||
* @param mustRevalidate The must-revalidate value from the Cache-Control header.
|
||||
* @param noCache The no-cache value from the Cache-Control header.
|
||||
* @param noStore The no-store value from the Cache-Control header.
|
||||
* @param cachePrivate The private value from the Cache-Control header.
|
||||
* @param proxyRevalidate The proxy-revalidate value from the Cache-Control header.
|
||||
* @param cachePublic The public value from the Cache-Control header.
|
||||
* @param staleWhileRevalidate The stale-while-revalidate value from the Cache-Control header.
|
||||
* @param staleIfError The stale-if-error value from the Cache-Control header.
|
||||
* @param noCacheFields The set of field names specified in the "no-cache" directive of the Cache-Control header.
|
||||
*/
|
||||
ResponseCacheControl(final long maxAge, final long sharedMaxAge, final boolean mustRevalidate, final boolean noCache,
|
||||
final boolean noStore, final boolean cachePrivate, final boolean proxyRevalidate,
|
||||
final boolean cachePublic, final long staleWhileRevalidate, final long staleIfError,
|
||||
final Set<String> noCacheFields) {
|
||||
this.maxAge = maxAge;
|
||||
this.sharedMaxAge = sharedMaxAge;
|
||||
this.noCache = noCache;
|
||||
this.noStore = noStore;
|
||||
this.cachePrivate = cachePrivate;
|
||||
this.mustRevalidate = mustRevalidate;
|
||||
this.proxyRevalidate = proxyRevalidate;
|
||||
this.cachePublic = cachePublic;
|
||||
this.staleWhileRevalidate = staleWhileRevalidate;
|
||||
this.staleIfError = staleIfError;
|
||||
this.noCacheFields = noCacheFields != null ? Collections.unmodifiableSet(noCacheFields) : Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max-age value from the Cache-Control header.
|
||||
*
|
||||
* @return The max-age value.
|
||||
*/
|
||||
@Override
|
||||
public long getMaxAge() {
|
||||
return maxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shared-max-age value from the Cache-Control header.
|
||||
*
|
||||
* @return The shared-max-age value.
|
||||
*/
|
||||
public long getSharedMaxAge() {
|
||||
return sharedMaxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the no-cache flag from the Cache-Control header.
|
||||
*
|
||||
* @return The no-cache flag.
|
||||
*/
|
||||
@Override
|
||||
public boolean isNoCache() {
|
||||
return noCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the no-store flag from the Cache-Control header.
|
||||
*
|
||||
* @return The no-store flag.
|
||||
*/
|
||||
@Override
|
||||
public boolean isNoStore() {
|
||||
return noStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the private flag from the Cache-Control header.
|
||||
*
|
||||
* @return The private flag.
|
||||
*/
|
||||
public boolean isCachePrivate() {
|
||||
return cachePrivate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the must-revalidate directive is present in the Cache-Control header.
|
||||
*
|
||||
* @return {@code true} if the must-revalidate directive is present, otherwise {@code false}
|
||||
*/
|
||||
public boolean isMustRevalidate() {
|
||||
return mustRevalidate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the proxy-revalidate value is set in the Cache-Control header.
|
||||
*
|
||||
* @return {@code true} if proxy-revalidate is set, {@code false} otherwise.
|
||||
*/
|
||||
public boolean isProxyRevalidate() {
|
||||
return proxyRevalidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the public value is set in the Cache-Control header.
|
||||
*
|
||||
* @return {@code true} if public is set, {@code false} otherwise.
|
||||
*/
|
||||
public boolean isPublic() {
|
||||
return cachePublic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stale-while-revalidate value from the Cache-Control header.
|
||||
*
|
||||
* @return The stale-while-revalidate value.
|
||||
*/
|
||||
public long getStaleWhileRevalidate() {
|
||||
return staleWhileRevalidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stale-if-error value from the Cache-Control header.
|
||||
*
|
||||
* @return The stale-if-error value.
|
||||
*/
|
||||
@Override
|
||||
public long getStaleIfError() {
|
||||
return staleIfError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable set of field names specified in the "no-cache" directive of the Cache-Control header.
|
||||
*
|
||||
* @return The set of field names specified in the "no-cache" directive.
|
||||
*/
|
||||
public Set<String> getNoCacheFields() {
|
||||
return noCacheFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CacheControl{" +
|
||||
"maxAge=" + maxAge +
|
||||
", sharedMaxAge=" + sharedMaxAge +
|
||||
", noCache=" + noCache +
|
||||
", noStore=" + noStore +
|
||||
", cachePrivate=" + cachePrivate +
|
||||
", mustRevalidate=" + mustRevalidate +
|
||||
", proxyRevalidate=" + proxyRevalidate +
|
||||
", cachePublic=" + cachePublic +
|
||||
", staleWhileRevalidate=" + staleWhileRevalidate +
|
||||
", staleIfError=" + staleIfError +
|
||||
", noCacheFields=" + noCacheFields +
|
||||
'}';
|
||||
}
|
||||
|
||||
static Builder create() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
|
||||
private long maxAge = -1;
|
||||
private long sharedMaxAge = -1;
|
||||
private boolean noCache;
|
||||
private boolean noStore;
|
||||
private boolean cachePrivate;
|
||||
private boolean mustRevalidate;
|
||||
private boolean proxyRevalidate;
|
||||
private boolean cachePublic;
|
||||
private long staleWhileRevalidate = -1;
|
||||
private long staleIfError = -1;
|
||||
private Set<String> noCacheFields;
|
||||
|
||||
Builder() {
|
||||
}
|
||||
|
||||
public long getMaxAge() {
|
||||
return maxAge;
|
||||
}
|
||||
|
||||
public Builder setMaxAge(final long maxAge) {
|
||||
this.maxAge = maxAge;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getSharedMaxAge() {
|
||||
return sharedMaxAge;
|
||||
}
|
||||
|
||||
public Builder setSharedMaxAge(final long sharedMaxAge) {
|
||||
this.sharedMaxAge = sharedMaxAge;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isNoCache() {
|
||||
return noCache;
|
||||
}
|
||||
|
||||
public Builder setNoCache(final boolean noCache) {
|
||||
this.noCache = noCache;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isNoStore() {
|
||||
return noStore;
|
||||
}
|
||||
|
||||
public Builder setNoStore(final boolean noStore) {
|
||||
this.noStore = noStore;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isCachePrivate() {
|
||||
return cachePrivate;
|
||||
}
|
||||
|
||||
public Builder setCachePrivate(final boolean cachePrivate) {
|
||||
this.cachePrivate = cachePrivate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isMustRevalidate() {
|
||||
return mustRevalidate;
|
||||
}
|
||||
|
||||
public Builder setMustRevalidate(final boolean mustRevalidate) {
|
||||
this.mustRevalidate = mustRevalidate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isProxyRevalidate() {
|
||||
return proxyRevalidate;
|
||||
}
|
||||
|
||||
public Builder setProxyRevalidate(final boolean proxyRevalidate) {
|
||||
this.proxyRevalidate = proxyRevalidate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isCachePublic() {
|
||||
return cachePublic;
|
||||
}
|
||||
|
||||
public Builder setCachePublic(final boolean cachePublic) {
|
||||
this.cachePublic = cachePublic;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getStaleWhileRevalidate() {
|
||||
return staleWhileRevalidate;
|
||||
}
|
||||
|
||||
public Builder setStaleWhileRevalidate(final long staleWhileRevalidate) {
|
||||
this.staleWhileRevalidate = staleWhileRevalidate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getStaleIfError() {
|
||||
return staleIfError;
|
||||
}
|
||||
|
||||
public Builder setStaleIfError(final long staleIfError) {
|
||||
this.staleIfError = staleIfError;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Set<String> getNoCacheFields() {
|
||||
return noCacheFields;
|
||||
}
|
||||
|
||||
public Builder setNoCacheFields(final Set<String> noCacheFields) {
|
||||
this.noCacheFields = noCacheFields;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseCacheControl build() {
|
||||
return new ResponseCacheControl(maxAge, sharedMaxAge, mustRevalidate, noCache, noStore, cachePrivate, proxyRevalidate,
|
||||
cachePublic, staleWhileRevalidate, staleIfError, noCacheFields);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -166,7 +166,7 @@ class ResponseCachingPolicy {
|
|||
* @param response The origin response
|
||||
* @return {@code true} if response is cacheable
|
||||
*/
|
||||
public boolean isResponseCacheable(final String httpMethod, final HttpResponse response, final CacheControl cacheControl) {
|
||||
public boolean isResponseCacheable(final String httpMethod, final HttpResponse response, final ResponseCacheControl cacheControl) {
|
||||
boolean cacheable = false;
|
||||
|
||||
if (!HeaderConstants.GET_METHOD.equals(httpMethod) && !HeaderConstants.HEAD_METHOD.equals(httpMethod)
|
||||
|
@ -285,7 +285,7 @@ class ResponseCachingPolicy {
|
|||
* <p>
|
||||
* When cacheControl is null, returns false, implying the response is cacheable.
|
||||
*/
|
||||
protected boolean isExplicitlyNonCacheable(final CacheControl cacheControl) {
|
||||
protected boolean isExplicitlyNonCacheable(final ResponseCacheControl cacheControl) {
|
||||
if (cacheControl == null) {
|
||||
return false;
|
||||
} else {
|
||||
|
@ -313,7 +313,7 @@ class ResponseCachingPolicy {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected boolean isExplicitlyCacheable(final HttpResponse response, final CacheControl cacheControl ) {
|
||||
protected boolean isExplicitlyCacheable(final HttpResponse response, final ResponseCacheControl cacheControl ) {
|
||||
if (response.getFirstHeader(HeaderConstants.EXPIRES) != null) {
|
||||
return true;
|
||||
}
|
||||
|
@ -363,7 +363,7 @@ class ResponseCachingPolicy {
|
|||
* @param response the {@link HttpResponse} from the origin
|
||||
* @return {@code true} if response is cacheable
|
||||
*/
|
||||
public boolean isResponseCacheable(final HttpRequest request, final HttpResponse response, final CacheControl cacheControl) {
|
||||
public boolean isResponseCacheable(final HttpRequest request, final HttpResponse response, final ResponseCacheControl cacheControl) {
|
||||
final ProtocolVersion version = request.getVersion() != null ? request.getVersion() : HttpVersion.DEFAULT;
|
||||
if (version.compareToVersion(HttpVersion.HTTP_1_1) > 0) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
|
@ -403,7 +403,7 @@ class ResponseCachingPolicy {
|
|||
return isResponseCacheable(method, response, cacheControl);
|
||||
}
|
||||
|
||||
private boolean expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl(final HttpResponse response, final CacheControl cacheControl) {
|
||||
private boolean expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl(final HttpResponse response, final ResponseCacheControl cacheControl) {
|
||||
if (cacheControl != null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -464,7 +464,7 @@ class ResponseCachingPolicy {
|
|||
* @param response the HTTP response for which to calculate the freshness lifetime
|
||||
* @return the freshness lifetime of the response, in seconds
|
||||
*/
|
||||
private Duration calculateFreshnessLifetime(final HttpResponse response, final CacheControl cacheControl) {
|
||||
private Duration calculateFreshnessLifetime(final HttpResponse response, final ResponseCacheControl cacheControl) {
|
||||
|
||||
if (cacheControl == null) {
|
||||
// If no cache-control header is present, assume no caching directives and return a default value
|
||||
|
@ -516,7 +516,7 @@ class ResponseCachingPolicy {
|
|||
final int statusCode = entry.getStatus();
|
||||
if (statusCode >= HttpStatus.SC_INTERNAL_SERVER_ERROR && statusCode <= HttpStatus.SC_GATEWAY_TIMEOUT) {
|
||||
// Check if the cached response has a stale-while-revalidate directive
|
||||
final CacheControl cacheControl = parseCacheControlHeader(entry);
|
||||
final ResponseCacheControl cacheControl = parseCacheControlHeader(entry);
|
||||
if (cacheControl == null) {
|
||||
return false;
|
||||
} else {
|
||||
|
@ -534,12 +534,12 @@ class ResponseCachingPolicy {
|
|||
* @param messageHeaders the HTTP message to parse the header from
|
||||
* @return a CacheControl instance with the parsed directives or default values if the header is not present
|
||||
*/
|
||||
private CacheControl parseCacheControlHeader(final MessageHeaders messageHeaders) {
|
||||
private ResponseCacheControl parseCacheControlHeader(final MessageHeaders messageHeaders) {
|
||||
final Iterator<Header> it = messageHeaders.headerIterator(HttpHeaders.CACHE_CONTROL);
|
||||
if (it == null || !it.hasNext()) {
|
||||
return null;
|
||||
} else {
|
||||
return CacheControlHeaderParser.INSTANCE.parse(it);
|
||||
return CacheControlHeaderParser.INSTANCE.parseResponse(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -558,7 +558,7 @@ class ResponseCachingPolicy {
|
|||
* @return true if revalidation is required based on the {@code no-cache} directive, {@code false} otherwise.
|
||||
*/
|
||||
boolean responseContainsNoCacheDirective(final HttpCacheEntry entry) {
|
||||
final CacheControl responseCacheControl = parseCacheControlHeader(entry);
|
||||
final ResponseCacheControl responseCacheControl = parseCacheControlHeader(entry);
|
||||
|
||||
if (responseCacheControl != null) {
|
||||
final Set<String> noCacheFields = responseCacheControl.getNoCacheFields();
|
||||
|
|
|
@ -31,13 +31,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.http.message.BasicHeader;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
public class CacheControlParserTest {
|
||||
|
||||
private final CacheControlHeaderParser parser = CacheControlHeaderParser.INSTANCE;
|
||||
|
@ -45,69 +45,69 @@ public class CacheControlParserTest {
|
|||
@Test
|
||||
public void testParseMaxAgeZero() {
|
||||
final Header header = new BasicHeader("Cache-Control", "max-age=0 , this = stuff;");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
assertEquals(0L, cacheControl.getMaxAge());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSMaxAge() {
|
||||
final Header header = new BasicHeader("Cache-Control", "s-maxage=3600");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
assertEquals(3600L, cacheControl.getSharedMaxAge());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseInvalidCacheValue() {
|
||||
final Header header = new BasicHeader("Cache-Control", "max-age=invalid");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
assertEquals(-1L, cacheControl.getMaxAge());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
assertEquals(0L, cacheControl.getMaxAge());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseInvalidHeader() {
|
||||
final Header header = new BasicHeader("Cache-Control", "max-age");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
assertEquals(-1L, cacheControl.getMaxAge());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseNullHeader() {
|
||||
final Header header = null;
|
||||
assertThrows(NullPointerException.class, () -> parser.parse(Collections.singletonList(header).iterator()));
|
||||
assertThrows(NullPointerException.class, () -> parser.parseResponse(Collections.singletonList(header).iterator()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseEmptyHeader() {
|
||||
final Header header = new BasicHeader("Cache-Control", "");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
assertEquals(-1L, cacheControl.getMaxAge());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseCookieEmptyValue() {
|
||||
final Header header = new BasicHeader("Cache-Control", "max-age=;");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final Header header = new BasicHeader("Cache-Control", "max-age=,");
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
assertEquals(-1L, cacheControl.getMaxAge());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseNoCache() {
|
||||
final Header header = new BasicHeader(" Cache-Control", "no-cache");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
assertEquals(-1L, cacheControl.getMaxAge());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseNoDirective() {
|
||||
final Header header = new BasicHeader(" Cache-Control", "");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
assertEquals(-1L, cacheControl.getMaxAge());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGarbage() {
|
||||
final Header header = new BasicHeader("Cache-Control", ",,= blah,");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
assertEquals(-1L, cacheControl.getMaxAge());
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ public class CacheControlParserTest {
|
|||
@Test
|
||||
public void testParseMultipleDirectives() {
|
||||
final Header header = new BasicHeader("Cache-Control", "max-age=604800, stale-while-revalidate=86400, s-maxage=3600, must-revalidate, private");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
|
||||
assertAll("Must all pass",
|
||||
() -> assertEquals(604800L, cacheControl.getMaxAge()),
|
||||
|
@ -128,7 +128,7 @@ public class CacheControlParserTest {
|
|||
@Test
|
||||
public void testParseMultipleDirectives2() {
|
||||
final Header header = new BasicHeader("Cache-Control", "max-age=604800, stale-while-revalidate=86400, must-revalidate, private, s-maxage=3600");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
|
||||
assertAll("Must all pass",
|
||||
() -> assertEquals(604800L, cacheControl.getMaxAge()),
|
||||
|
@ -141,7 +141,7 @@ public class CacheControlParserTest {
|
|||
@Test
|
||||
public void testParsePublic() {
|
||||
final Header header = new BasicHeader("Cache-Control", "public");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
|
||||
assertTrue(cacheControl.isPublic());
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ public class CacheControlParserTest {
|
|||
@Test
|
||||
public void testParsePrivate() {
|
||||
final Header header = new BasicHeader("Cache-Control", "private");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
|
||||
assertTrue(cacheControl.isCachePrivate());
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ public class CacheControlParserTest {
|
|||
@Test
|
||||
public void testParseNoStore() {
|
||||
final Header header = new BasicHeader("Cache-Control", "no-store");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
|
||||
assertTrue(cacheControl.isNoStore());
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ public class CacheControlParserTest {
|
|||
@Test
|
||||
public void testParseStaleWhileRevalidate() {
|
||||
final Header header = new BasicHeader("Cache-Control", "max-age=3600, stale-while-revalidate=120");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
|
||||
assertEquals(120, cacheControl.getStaleWhileRevalidate());
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ public class CacheControlParserTest {
|
|||
@Test
|
||||
public void testParseNoCacheFields() {
|
||||
final Header header = new BasicHeader("Cache-Control", "no-cache=\"Set-Cookie, Content-Language\", stale-while-revalidate=120");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
|
||||
assertTrue(cacheControl.isNoCache());
|
||||
assertEquals(2, cacheControl.getNoCacheFields().size());
|
||||
|
@ -185,7 +185,7 @@ public class CacheControlParserTest {
|
|||
@Test
|
||||
public void testParseNoCacheFieldsNoQuote() {
|
||||
final Header header = new BasicHeader("Cache-Control", "no-cache=Set-Cookie, Content-Language, stale-while-revalidate=120");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
|
||||
assertTrue(cacheControl.isNoCache());
|
||||
assertEquals(1, cacheControl.getNoCacheFields().size());
|
||||
|
@ -196,7 +196,7 @@ public class CacheControlParserTest {
|
|||
@Test
|
||||
public void testParseNoCacheFieldsMessy() {
|
||||
final Header header = new BasicHeader("Cache-Control", "no-cache=\" , , ,,, Set-Cookie , , Content-Language , \", stale-while-revalidate=120");
|
||||
final CacheControl cacheControl = parser.parse(Collections.singletonList(header).iterator());
|
||||
final ResponseCacheControl cacheControl = parser.parseResponse(Collections.singletonList(header).iterator());
|
||||
|
||||
assertTrue(cacheControl.isNoCache());
|
||||
assertEquals(2, cacheControl.getNoCacheFields().size());
|
||||
|
@ -217,8 +217,8 @@ public class CacheControlParserTest {
|
|||
final Header header6 = new BasicHeader("Cache-Control", "must-revalidate");
|
||||
|
||||
// Parse headers
|
||||
final CacheControl cacheControl1 = parser.parse(Arrays.asList(header1, header2).iterator());
|
||||
final CacheControl cacheControl2 = parser.parse(Arrays.asList(header3, header4, header5, header6).iterator());
|
||||
final ResponseCacheControl cacheControl1 = parser.parseResponse(Arrays.asList(header1, header2).iterator());
|
||||
final ResponseCacheControl cacheControl2 = parser.parseResponse(Arrays.asList(header3, header4, header5, header6).iterator());
|
||||
|
||||
// Validate Cache-Control directives
|
||||
assertEquals(cacheControl1.getMaxAge(), cacheControl2.getMaxAge());
|
||||
|
@ -226,4 +226,21 @@ public class CacheControlParserTest {
|
|||
assertEquals(cacheControl1.isCachePrivate(), cacheControl2.isCachePrivate());
|
||||
assertEquals(cacheControl1.isMustRevalidate(), cacheControl2.isMustRevalidate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseRequestMultipleDirectives() {
|
||||
final Header header = new BasicHeader("Cache-Control", "blah, max-age=1111, max-stale=2222, " +
|
||||
"min-fresh=3333, no-cache, no-store, no-cache, no-stuff, only-if-cached, only-if-cached-or-maybe-not");
|
||||
final RequestCacheControl cacheControl = parser.parseRequest(Collections.singletonList(header).iterator());
|
||||
|
||||
assertAll("Must all pass",
|
||||
() -> assertEquals(1111L, cacheControl.getMaxAge()),
|
||||
() -> assertEquals(2222L, cacheControl.getMaxStale()),
|
||||
() -> assertEquals(3333L, cacheControl.getMinFresh()),
|
||||
() -> assertTrue(cacheControl.isNoCache()),
|
||||
() -> assertTrue(cacheControl.isNoCache()),
|
||||
() -> assertTrue(cacheControl.isOnlyIfCached())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -227,7 +227,7 @@ public class TestResponseCachingPolicy {
|
|||
final int status = getRandomStatus();
|
||||
response.setCode(status);
|
||||
response.setHeader("Cache-Control", "max-age=boom");
|
||||
Assertions.assertTrue(policy.isResponseCacheable("GET", response));
|
||||
Assertions.assertFalse(policy.isResponseCacheable("GET", response));
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue