blob: dc944e4f60377c941c599398f52ece2b0f3e9e65 [file] [log] [blame]
package com.squareup.okhttp;
import com.squareup.okhttp.internal.http.HeaderParser;
/**
* A Cache-Control header with cache directives from a server or client. These
* directives set policy on what responses can be stored, and which requests can
* be satisfied by those stored responses.
*
* <p>See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">RFC
* 2616, 14.9</a>.
*/
public final class CacheControl {
private final boolean noCache;
private final boolean noStore;
private final int maxAgeSeconds;
private final int sMaxAgeSeconds;
private final boolean isPublic;
private final boolean mustRevalidate;
private final int maxStaleSeconds;
private final int minFreshSeconds;
private final boolean onlyIfCached;
private CacheControl(boolean noCache, boolean noStore, int maxAgeSeconds, int sMaxAgeSeconds,
boolean isPublic, boolean mustRevalidate, int maxStaleSeconds, int minFreshSeconds,
boolean onlyIfCached) {
this.noCache = noCache;
this.noStore = noStore;
this.maxAgeSeconds = maxAgeSeconds;
this.sMaxAgeSeconds = sMaxAgeSeconds;
this.isPublic = isPublic;
this.mustRevalidate = mustRevalidate;
this.maxStaleSeconds = maxStaleSeconds;
this.minFreshSeconds = minFreshSeconds;
this.onlyIfCached = onlyIfCached;
}
/**
* In a response, this field's name "no-cache" is misleading. It doesn't
* prevent us from caching the response; it only means we have to validate the
* response with the origin server before returning it. We can do this with a
* conditional GET.
*
* <p>In a request, it means do not use a cache to satisfy the request.
*/
public boolean noCache() {
return noCache;
}
/** If true, this response should not be cached. */
public boolean noStore() {
return noStore;
}
/**
* The duration past the response's served date that it can be served without
* validation.
*/
public int maxAgeSeconds() {
return maxAgeSeconds;
}
/**
* The "s-maxage" directive is the max age for shared caches. Not to be
* confused with "max-age" for non-shared caches, As in Firefox and Chrome,
* this directive is not honored by this cache.
*/
public int sMaxAgeSeconds() {
return sMaxAgeSeconds;
}
public boolean isPublic() {
return isPublic;
}
public boolean mustRevalidate() {
return mustRevalidate;
}
public int maxStaleSeconds() {
return maxStaleSeconds;
}
public int minFreshSeconds() {
return minFreshSeconds;
}
/**
* This field's name "only-if-cached" is misleading. It actually means "do
* not use the network". It is set by a client who only wants to make a
* request if it can be fully satisfied by the cache. Cached responses that
* would require validation (ie. conditional gets) are not permitted if this
* header is set.
*/
public boolean onlyIfCached() {
return onlyIfCached;
}
/**
* Returns the cache directives of {@code headers}. This honors both
* Cache-Control and Pragma headers if they are present.
*/
public static CacheControl parse(Headers headers) {
boolean noCache = false;
boolean noStore = false;
int maxAgeSeconds = -1;
int sMaxAgeSeconds = -1;
boolean isPublic = false;
boolean mustRevalidate = false;
int maxStaleSeconds = -1;
int minFreshSeconds = -1;
boolean onlyIfCached = false;
for (int i = 0; i < headers.size(); i++) {
if (!headers.name(i).equalsIgnoreCase("Cache-Control")
&& !headers.name(i).equalsIgnoreCase("Pragma")) {
continue;
}
String string = headers.value(i);
int pos = 0;
while (pos < string.length()) {
int tokenStart = pos;
pos = HeaderParser.skipUntil(string, pos, "=,;");
String directive = string.substring(tokenStart, pos).trim();
String parameter;
if (pos == string.length() || string.charAt(pos) == ',' || string.charAt(pos) == ';') {
pos++; // consume ',' or ';' (if necessary)
parameter = null;
} else {
pos++; // consume '='
pos = HeaderParser.skipWhitespace(string, pos);
// quoted string
if (pos < string.length() && string.charAt(pos) == '\"') {
pos++; // consume '"' open quote
int parameterStart = pos;
pos = HeaderParser.skipUntil(string, pos, "\"");
parameter = string.substring(parameterStart, pos);
pos++; // consume '"' close quote (if necessary)
// unquoted string
} else {
int parameterStart = pos;
pos = HeaderParser.skipUntil(string, pos, ",;");
parameter = string.substring(parameterStart, pos).trim();
}
}
if ("no-cache".equalsIgnoreCase(directive)) {
noCache = true;
} else if ("no-store".equalsIgnoreCase(directive)) {
noStore = true;
} else if ("max-age".equalsIgnoreCase(directive)) {
maxAgeSeconds = HeaderParser.parseSeconds(parameter);
} else if ("s-maxage".equalsIgnoreCase(directive)) {
sMaxAgeSeconds = HeaderParser.parseSeconds(parameter);
} else if ("public".equalsIgnoreCase(directive)) {
isPublic = true;
} else if ("must-revalidate".equalsIgnoreCase(directive)) {
mustRevalidate = true;
} else if ("max-stale".equalsIgnoreCase(directive)) {
maxStaleSeconds = HeaderParser.parseSeconds(parameter);
} else if ("min-fresh".equalsIgnoreCase(directive)) {
minFreshSeconds = HeaderParser.parseSeconds(parameter);
} else if ("only-if-cached".equalsIgnoreCase(directive)) {
onlyIfCached = true;
}
}
}
return new CacheControl(noCache, noStore, maxAgeSeconds, sMaxAgeSeconds, isPublic,
mustRevalidate, maxStaleSeconds, minFreshSeconds, onlyIfCached);
}
}