mirror of
https://github.com/square/okhttp.git
synced 2026-01-24 04:02:07 +03:00
New CacheControl class.
This moves cache control headers from the main request and response classes to their own class.
This commit is contained in:
176
okhttp/src/main/java/com/squareup/okhttp/CacheControl.java
Normal file
176
okhttp/src/main/java/com/squareup/okhttp/CacheControl.java
Normal file
@@ -0,0 +1,176 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ package com.squareup.okhttp;
|
||||
|
||||
import com.squareup.okhttp.internal.Platform;
|
||||
import com.squareup.okhttp.internal.Util;
|
||||
import com.squareup.okhttp.internal.http.HeaderParser;
|
||||
import com.squareup.okhttp.internal.http.HttpDate;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -45,6 +44,7 @@ public final class Request {
|
||||
|
||||
private volatile ParsedHeaders parsedHeaders; // Lazily initialized.
|
||||
private volatile URI uri; // Lazily initialized.
|
||||
private volatile CacheControl cacheControl; // Lazily initialized.
|
||||
|
||||
private Request(Builder builder) {
|
||||
this.url = builder.url;
|
||||
@@ -103,26 +103,6 @@ public final class Request {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public boolean getNoCache() {
|
||||
return parsedHeaders().noCache;
|
||||
}
|
||||
|
||||
public int getMaxAgeSeconds() {
|
||||
return parsedHeaders().maxAgeSeconds;
|
||||
}
|
||||
|
||||
public int getMaxStaleSeconds() {
|
||||
return parsedHeaders().maxStaleSeconds;
|
||||
}
|
||||
|
||||
public int getMinFreshSeconds() {
|
||||
return parsedHeaders().minFreshSeconds;
|
||||
}
|
||||
|
||||
public boolean getOnlyIfCached() {
|
||||
return parsedHeaders().onlyIfCached;
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return parsedHeaders().userAgent;
|
||||
}
|
||||
@@ -136,57 +116,29 @@ public final class Request {
|
||||
return result != null ? result : (parsedHeaders = new ParsedHeaders(headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache control directives for this response. This is never null,
|
||||
* even if this response contains no {@code Cache-Control} header.
|
||||
*/
|
||||
public CacheControl cacheControl() {
|
||||
CacheControl result = cacheControl;
|
||||
return result != null ? result : (cacheControl = CacheControl.parse(headers));
|
||||
}
|
||||
|
||||
public boolean isHttps() {
|
||||
return url().getProtocol().equals("https");
|
||||
}
|
||||
|
||||
/** Parsed request headers, computed on-demand and cached. */
|
||||
private static class ParsedHeaders {
|
||||
/** Don't use a cache to satisfy this request. */
|
||||
private boolean noCache;
|
||||
private int maxAgeSeconds = -1;
|
||||
private int maxStaleSeconds = -1;
|
||||
private int minFreshSeconds = -1;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private boolean onlyIfCached;
|
||||
|
||||
private String userAgent;
|
||||
private String proxyAuthorization;
|
||||
|
||||
public ParsedHeaders(Headers headers) {
|
||||
HeaderParser.CacheControlHandler handler = new HeaderParser.CacheControlHandler() {
|
||||
@Override public void handle(String directive, String parameter) {
|
||||
if ("no-cache".equalsIgnoreCase(directive)) {
|
||||
noCache = true;
|
||||
} else if ("max-age".equalsIgnoreCase(directive)) {
|
||||
maxAgeSeconds = HeaderParser.parseSeconds(parameter);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < headers.size(); i++) {
|
||||
String fieldName = headers.name(i);
|
||||
String value = headers.value(i);
|
||||
if ("Cache-Control".equalsIgnoreCase(fieldName)) {
|
||||
HeaderParser.parseCacheControl(value, handler);
|
||||
} else if ("Pragma".equalsIgnoreCase(fieldName)) {
|
||||
if ("no-cache".equalsIgnoreCase(value)) {
|
||||
noCache = true;
|
||||
}
|
||||
} else if ("User-Agent".equalsIgnoreCase(fieldName)) {
|
||||
if ("User-Agent".equalsIgnoreCase(fieldName)) {
|
||||
userAgent = value;
|
||||
} else if ("Proxy-Authorization".equalsIgnoreCase(fieldName)) {
|
||||
proxyAuthorization = value;
|
||||
|
||||
@@ -51,6 +51,7 @@ public final class Response {
|
||||
private final Response redirectedBy;
|
||||
|
||||
private volatile ParsedHeaders parsedHeaders; // Lazily initialized.
|
||||
private volatile CacheControl cacheControl; // Lazily initialized.
|
||||
|
||||
private Response(Builder builder) {
|
||||
this.request = builder.request;
|
||||
@@ -148,30 +149,6 @@ public final class Response {
|
||||
return parsedHeaders().expires;
|
||||
}
|
||||
|
||||
public boolean isNoCache() {
|
||||
return parsedHeaders().noCache;
|
||||
}
|
||||
|
||||
public boolean isNoStore() {
|
||||
return parsedHeaders().noStore;
|
||||
}
|
||||
|
||||
public int getMaxAgeSeconds() {
|
||||
return parsedHeaders().maxAgeSeconds;
|
||||
}
|
||||
|
||||
public int getSMaxAgeSeconds() {
|
||||
return parsedHeaders().sMaxAgeSeconds;
|
||||
}
|
||||
|
||||
public boolean isPublic() {
|
||||
return parsedHeaders().isPublic;
|
||||
}
|
||||
|
||||
public boolean isMustRevalidate() {
|
||||
return parsedHeaders().mustRevalidate;
|
||||
}
|
||||
|
||||
public String getEtag() {
|
||||
return parsedHeaders().etag;
|
||||
}
|
||||
@@ -319,6 +296,15 @@ public final class Response {
|
||||
return result != null ? result : (parsedHeaders = new ParsedHeaders(headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache control directives for this response. This is never null,
|
||||
* even if this response contains no {@code Cache-Control} header.
|
||||
*/
|
||||
public CacheControl cacheControl() {
|
||||
CacheControl result = cacheControl;
|
||||
return result != null ? result : (cacheControl = CacheControl.parse(headers));
|
||||
}
|
||||
|
||||
/** Parsed response headers, computed on-demand and cached. */
|
||||
private static class ParsedHeaders {
|
||||
/** The server's time when this response was served, if known. */
|
||||
@@ -345,73 +331,17 @@ public final class Response {
|
||||
*/
|
||||
long receivedResponseMillis;
|
||||
|
||||
/**
|
||||
* In the 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.
|
||||
*/
|
||||
boolean noCache;
|
||||
|
||||
/** If true, this response should not be cached. */
|
||||
boolean noStore;
|
||||
|
||||
/**
|
||||
* The duration past the response's served date that it can be served
|
||||
* without validation.
|
||||
*/
|
||||
int maxAgeSeconds = -1;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
int sMaxAgeSeconds = -1;
|
||||
|
||||
/**
|
||||
* This request header 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.
|
||||
*/
|
||||
boolean isPublic;
|
||||
boolean mustRevalidate;
|
||||
String etag;
|
||||
int ageSeconds = -1;
|
||||
|
||||
/** Case-insensitive set of field names. */
|
||||
private Set<String> varyFields = Collections.emptySet();
|
||||
|
||||
private long contentLength = -1;
|
||||
private String contentType;
|
||||
|
||||
private ParsedHeaders(Headers headers) {
|
||||
HeaderParser.CacheControlHandler handler = new HeaderParser.CacheControlHandler() {
|
||||
@Override public void handle(String directive, String parameter) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < headers.size(); i++) {
|
||||
String fieldName = headers.name(i);
|
||||
String value = headers.value(i);
|
||||
if ("Cache-Control".equalsIgnoreCase(fieldName)) {
|
||||
HeaderParser.parseCacheControl(value, handler);
|
||||
} else if ("Date".equalsIgnoreCase(fieldName)) {
|
||||
if ("Date".equalsIgnoreCase(fieldName)) {
|
||||
servedDate = HttpDate.parse(value);
|
||||
} else if ("Expires".equalsIgnoreCase(fieldName)) {
|
||||
expires = HttpDate.parse(value);
|
||||
@@ -419,10 +349,6 @@ public final class Response {
|
||||
lastModified = HttpDate.parse(value);
|
||||
} else if ("ETag".equalsIgnoreCase(fieldName)) {
|
||||
etag = value;
|
||||
} else if ("Pragma".equalsIgnoreCase(fieldName)) {
|
||||
if ("no-cache".equalsIgnoreCase(value)) {
|
||||
noCache = true;
|
||||
}
|
||||
} else if ("Age".equalsIgnoreCase(fieldName)) {
|
||||
ageSeconds = HeaderParser.parseSeconds(value);
|
||||
} else if ("Vary".equalsIgnoreCase(fieldName)) {
|
||||
@@ -433,13 +359,6 @@ public final class Response {
|
||||
for (String varyField : value.split(",")) {
|
||||
varyFields.add(varyField.trim());
|
||||
}
|
||||
} else if ("Content-Length".equalsIgnoreCase(fieldName)) {
|
||||
try {
|
||||
contentLength = Long.parseLong(value);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
} else if ("Content-Type".equalsIgnoreCase(fieldName)) {
|
||||
contentType = value;
|
||||
} else if (OkHeaders.SENT_MILLIS.equalsIgnoreCase(fieldName)) {
|
||||
sentRequestMillis = Long.parseLong(value);
|
||||
} else if (OkHeaders.RECEIVED_MILLIS.equalsIgnoreCase(fieldName)) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.squareup.okhttp.internal.http;
|
||||
|
||||
import com.squareup.okhttp.CacheControl;
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
@@ -7,9 +8,9 @@ import com.squareup.okhttp.ResponseSource;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.squareup.okhttp.internal.Util.EMPTY_INPUT_STREAM;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
/**
|
||||
* Given a request and cached response, this figures out whether to use the
|
||||
@@ -64,7 +65,7 @@ public final class CacheStrategy {
|
||||
? Math.max(0, response.getReceivedResponseMillis() - response.getServedDate().getTime())
|
||||
: 0;
|
||||
long receivedAge = response.getAgeSeconds() != -1
|
||||
? Math.max(apparentReceivedAge, TimeUnit.SECONDS.toMillis(response.getAgeSeconds()))
|
||||
? Math.max(apparentReceivedAge, SECONDS.toMillis(response.getAgeSeconds()))
|
||||
: apparentReceivedAge;
|
||||
long responseDuration = response.getReceivedResponseMillis() - response.getSentRequestMillis();
|
||||
long residentDuration = nowMillis - response.getReceivedResponseMillis();
|
||||
@@ -76,8 +77,9 @@ public final class CacheStrategy {
|
||||
* starting from the served date.
|
||||
*/
|
||||
private static long computeFreshnessLifetime(Response response) {
|
||||
if (response.getMaxAgeSeconds() != -1) {
|
||||
return TimeUnit.SECONDS.toMillis(response.getMaxAgeSeconds());
|
||||
CacheControl responseCaching = response.cacheControl();
|
||||
if (responseCaching.maxAgeSeconds() != -1) {
|
||||
return SECONDS.toMillis(responseCaching.maxAgeSeconds());
|
||||
} else if (response.getExpires() != null) {
|
||||
long servedMillis = response.getServedDate() != null
|
||||
? response.getServedDate().getTime()
|
||||
@@ -104,7 +106,8 @@ public final class CacheStrategy {
|
||||
* to attach a warning.
|
||||
*/
|
||||
private static boolean isFreshnessLifetimeHeuristic(Response response) {
|
||||
return response.getMaxAgeSeconds() == -1 && response.getExpires() == null;
|
||||
return response.cacheControl().maxAgeSeconds() == -1
|
||||
&& response.getExpires() == null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,14 +128,15 @@ public final class CacheStrategy {
|
||||
|
||||
// Responses to authorized requests aren't cacheable unless they include
|
||||
// a 'public', 'must-revalidate' or 's-maxage' directive.
|
||||
CacheControl responseCaching = response.cacheControl();
|
||||
if (request.header("Authorization") != null
|
||||
&& !response.isPublic()
|
||||
&& !response.isMustRevalidate()
|
||||
&& response.getSMaxAgeSeconds() == -1) {
|
||||
&& !responseCaching.isPublic()
|
||||
&& !responseCaching.mustRevalidate()
|
||||
&& responseCaching.sMaxAgeSeconds() == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response.isNoStore()) {
|
||||
if (responseCaching.noStore()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -146,7 +150,7 @@ public final class CacheStrategy {
|
||||
public static CacheStrategy get(long nowMillis, Response response, Request request) {
|
||||
CacheStrategy candidate = getCandidate(nowMillis, response, request);
|
||||
|
||||
if (candidate.source != ResponseSource.CACHE && request.getOnlyIfCached()) {
|
||||
if (candidate.source != ResponseSource.CACHE && request.cacheControl().onlyIfCached()) {
|
||||
// We're forbidden from using the network, but the cache is insufficient.
|
||||
Response noneResponse = new Response.Builder()
|
||||
.request(candidate.request)
|
||||
@@ -179,28 +183,30 @@ public final class CacheStrategy {
|
||||
return new CacheStrategy(request, response, ResponseSource.NETWORK);
|
||||
}
|
||||
|
||||
if (request.getNoCache() || hasConditions(request)) {
|
||||
CacheControl requestCaching = request.cacheControl();
|
||||
if (requestCaching.noCache() || hasConditions(request)) {
|
||||
return new CacheStrategy(request, response, ResponseSource.NETWORK);
|
||||
}
|
||||
|
||||
long ageMillis = computeAge(response, nowMillis);
|
||||
long freshMillis = computeFreshnessLifetime(response);
|
||||
|
||||
if (request.getMaxAgeSeconds() != -1) {
|
||||
freshMillis = Math.min(freshMillis, TimeUnit.SECONDS.toMillis(request.getMaxAgeSeconds()));
|
||||
if (requestCaching.maxAgeSeconds() != -1) {
|
||||
freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
|
||||
}
|
||||
|
||||
long minFreshMillis = 0;
|
||||
if (request.getMinFreshSeconds() != -1) {
|
||||
minFreshMillis = TimeUnit.SECONDS.toMillis(request.getMinFreshSeconds());
|
||||
if (requestCaching.minFreshSeconds() != -1) {
|
||||
minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
|
||||
}
|
||||
|
||||
long maxStaleMillis = 0;
|
||||
if (!response.isMustRevalidate() && request.getMaxStaleSeconds() != -1) {
|
||||
maxStaleMillis = TimeUnit.SECONDS.toMillis(request.getMaxStaleSeconds());
|
||||
CacheControl responseCaching = response.cacheControl();
|
||||
if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
|
||||
maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
|
||||
}
|
||||
|
||||
if (!response.isNoCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
|
||||
if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
|
||||
Response.Builder builder = response.newBuilder()
|
||||
.setResponseSource(ResponseSource.CACHE); // Overwrite any stored response source.
|
||||
if (ageMillis + minFreshMillis >= freshMillis) {
|
||||
|
||||
@@ -17,49 +17,6 @@
|
||||
package com.squareup.okhttp.internal.http;
|
||||
|
||||
public final class HeaderParser {
|
||||
|
||||
public interface CacheControlHandler {
|
||||
void handle(String directive, String parameter);
|
||||
}
|
||||
|
||||
/** Parse a comma-separated list of cache control header values. */
|
||||
public static void parseCacheControl(String value, CacheControlHandler handler) {
|
||||
int pos = 0;
|
||||
while (pos < value.length()) {
|
||||
int tokenStart = pos;
|
||||
pos = skipUntil(value, pos, "=,;");
|
||||
String directive = value.substring(tokenStart, pos).trim();
|
||||
|
||||
if (pos == value.length() || value.charAt(pos) == ',' || value.charAt(pos) == ';') {
|
||||
pos++; // consume ',' or ';' (if necessary)
|
||||
handler.handle(directive, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
pos++; // consume '='
|
||||
pos = skipWhitespace(value, pos);
|
||||
|
||||
String parameter;
|
||||
|
||||
// quoted string
|
||||
if (pos < value.length() && value.charAt(pos) == '\"') {
|
||||
pos++; // consume '"' open quote
|
||||
int parameterStart = pos;
|
||||
pos = skipUntil(value, pos, "\"");
|
||||
parameter = value.substring(parameterStart, pos);
|
||||
pos++; // consume '"' close quote (if necessary)
|
||||
|
||||
// unquoted string
|
||||
} else {
|
||||
int parameterStart = pos;
|
||||
pos = skipUntil(value, pos, ",;");
|
||||
parameter = value.substring(parameterStart, pos).trim();
|
||||
}
|
||||
|
||||
handler.handle(directive, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next index in {@code input} at or after {@code pos} that
|
||||
* contains a character from {@code characters}. Returns the input length if
|
||||
|
||||
Reference in New Issue
Block a user