All Downloads are FREE. Search and download functionalities are using the official Maven repository.

okhttp3.CacheControl Maven / Gradle / Ivy

package okhttp3;

import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import okhttp3.internal.http.HttpHeaders;

/**
 * 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.
 *
 * 

See RFC 7234, 5.2. */ public final class CacheControl { /** * Cache control request directives that require network validation of responses. Note that such * requests may be assisted by the cache via conditional GET requests. */ public static final CacheControl FORCE_NETWORK = new Builder().noCache().build(); /** * Cache control request directives that uses the cache only, even if the cached response is * stale. If the response isn't available in the cache or requires server validation, the call * will fail with a {@code 504 Unsatisfiable Request}. */ public static final CacheControl FORCE_CACHE = new Builder() .onlyIfCached() .maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS) .build(); private final boolean noCache; private final boolean noStore; private final int maxAgeSeconds; private final int sMaxAgeSeconds; private final boolean isPrivate; private final boolean isPublic; private final boolean mustRevalidate; private final int maxStaleSeconds; private final int minFreshSeconds; private final boolean onlyIfCached; private final boolean noTransform; private final boolean immutable; @Nullable String headerValue; // Lazily computed, null if absent. private CacheControl(boolean noCache, boolean noStore, int maxAgeSeconds, int sMaxAgeSeconds, boolean isPrivate, boolean isPublic, boolean mustRevalidate, int maxStaleSeconds, int minFreshSeconds, boolean onlyIfCached, boolean noTransform, boolean immutable, @Nullable String headerValue) { this.noCache = noCache; this.noStore = noStore; this.maxAgeSeconds = maxAgeSeconds; this.sMaxAgeSeconds = sMaxAgeSeconds; this.isPrivate = isPrivate; this.isPublic = isPublic; this.mustRevalidate = mustRevalidate; this.maxStaleSeconds = maxStaleSeconds; this.minFreshSeconds = minFreshSeconds; this.onlyIfCached = onlyIfCached; this.noTransform = noTransform; this.immutable = immutable; this.headerValue = headerValue; } CacheControl(Builder builder) { this.noCache = builder.noCache; this.noStore = builder.noStore; this.maxAgeSeconds = builder.maxAgeSeconds; this.sMaxAgeSeconds = -1; this.isPrivate = false; this.isPublic = false; this.mustRevalidate = false; this.maxStaleSeconds = builder.maxStaleSeconds; this.minFreshSeconds = builder.minFreshSeconds; this.onlyIfCached = builder.onlyIfCached; this.noTransform = builder.noTransform; this.immutable = builder.immutable; } /** * 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. * *

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 isPrivate() { return isPrivate; } 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; } public boolean noTransform() { return noTransform; } public boolean immutable() { return immutable; } /** * 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 isPrivate = false; boolean isPublic = false; boolean mustRevalidate = false; int maxStaleSeconds = -1; int minFreshSeconds = -1; boolean onlyIfCached = false; boolean noTransform = false; boolean immutable = false; boolean canUseHeaderValue = true; String headerValue = null; for (int i = 0, size = headers.size(); i < size; i++) { String name = headers.name(i); String value = headers.value(i); if (name.equalsIgnoreCase("Cache-Control")) { if (headerValue != null) { // Multiple cache-control headers means we can't use the raw value. canUseHeaderValue = false; } else { headerValue = value; } } else if (name.equalsIgnoreCase("Pragma")) { // Might specify additional cache-control params. We invalidate just in case. canUseHeaderValue = false; } else { continue; } int pos = 0; while (pos < value.length()) { int tokenStart = pos; pos = HttpHeaders.skipUntil(value, pos, "=,;"); String directive = value.substring(tokenStart, pos).trim(); String parameter; if (pos == value.length() || value.charAt(pos) == ',' || value.charAt(pos) == ';') { pos++; // consume ',' or ';' (if necessary) parameter = null; } else { pos++; // consume '=' pos = HttpHeaders.skipWhitespace(value, pos); // quoted string if (pos < value.length() && value.charAt(pos) == '\"') { pos++; // consume '"' open quote int parameterStart = pos; pos = HttpHeaders.skipUntil(value, pos, "\""); parameter = value.substring(parameterStart, pos); pos++; // consume '"' close quote (if necessary) // unquoted string } else { int parameterStart = pos; pos = HttpHeaders.skipUntil(value, pos, ",;"); parameter = value.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 = HttpHeaders.parseSeconds(parameter, -1); } else if ("s-maxage".equalsIgnoreCase(directive)) { sMaxAgeSeconds = HttpHeaders.parseSeconds(parameter, -1); } else if ("private".equalsIgnoreCase(directive)) { isPrivate = true; } else if ("public".equalsIgnoreCase(directive)) { isPublic = true; } else if ("must-revalidate".equalsIgnoreCase(directive)) { mustRevalidate = true; } else if ("max-stale".equalsIgnoreCase(directive)) { maxStaleSeconds = HttpHeaders.parseSeconds(parameter, Integer.MAX_VALUE); } else if ("min-fresh".equalsIgnoreCase(directive)) { minFreshSeconds = HttpHeaders.parseSeconds(parameter, -1); } else if ("only-if-cached".equalsIgnoreCase(directive)) { onlyIfCached = true; } else if ("no-transform".equalsIgnoreCase(directive)) { noTransform = true; } else if ("immutable".equalsIgnoreCase(directive)) { immutable = true; } } } if (!canUseHeaderValue) { headerValue = null; } return new CacheControl(noCache, noStore, maxAgeSeconds, sMaxAgeSeconds, isPrivate, isPublic, mustRevalidate, maxStaleSeconds, minFreshSeconds, onlyIfCached, noTransform, immutable, headerValue); } @Override public String toString() { String result = headerValue; return result != null ? result : (headerValue = headerValue()); } private String headerValue() { StringBuilder result = new StringBuilder(); if (noCache) result.append("no-cache, "); if (noStore) result.append("no-store, "); if (maxAgeSeconds != -1) result.append("max-age=").append(maxAgeSeconds).append(", "); if (sMaxAgeSeconds != -1) result.append("s-maxage=").append(sMaxAgeSeconds).append(", "); if (isPrivate) result.append("private, "); if (isPublic) result.append("public, "); if (mustRevalidate) result.append("must-revalidate, "); if (maxStaleSeconds != -1) result.append("max-stale=").append(maxStaleSeconds).append(", "); if (minFreshSeconds != -1) result.append("min-fresh=").append(minFreshSeconds).append(", "); if (onlyIfCached) result.append("only-if-cached, "); if (noTransform) result.append("no-transform, "); if (immutable) result.append("immutable, "); if (result.length() == 0) return ""; result.delete(result.length() - 2, result.length()); return result.toString(); } /** Builds a {@code Cache-Control} request header. */ public static final class Builder { boolean noCache; boolean noStore; int maxAgeSeconds = -1; int maxStaleSeconds = -1; int minFreshSeconds = -1; boolean onlyIfCached; boolean noTransform; boolean immutable; /** Don't accept an unvalidated cached response. */ public Builder noCache() { this.noCache = true; return this; } /** Don't store the server's response in any cache. */ public Builder noStore() { this.noStore = true; return this; } /** * Sets the maximum age of a cached response. If the cache response's age exceeds {@code * maxAge}, it will not be used and a network request will be made. * * @param maxAge a non-negative integer. This is stored and transmitted with {@link * TimeUnit#SECONDS} precision; finer precision will be lost. */ public Builder maxAge(int maxAge, TimeUnit timeUnit) { if (maxAge < 0) throw new IllegalArgumentException("maxAge < 0: " + maxAge); long maxAgeSecondsLong = timeUnit.toSeconds(maxAge); this.maxAgeSeconds = maxAgeSecondsLong > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) maxAgeSecondsLong; return this; } /** * Accept cached responses that have exceeded their freshness lifetime by up to {@code * maxStale}. If unspecified, stale cache responses will not be used. * * @param maxStale a non-negative integer. This is stored and transmitted with {@link * TimeUnit#SECONDS} precision; finer precision will be lost. */ public Builder maxStale(int maxStale, TimeUnit timeUnit) { if (maxStale < 0) throw new IllegalArgumentException("maxStale < 0: " + maxStale); long maxStaleSecondsLong = timeUnit.toSeconds(maxStale); this.maxStaleSeconds = maxStaleSecondsLong > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) maxStaleSecondsLong; return this; } /** * Sets the minimum number of seconds that a response will continue to be fresh for. If the * response will be stale when {@code minFresh} have elapsed, the cached response will not be * used and a network request will be made. * * @param minFresh a non-negative integer. This is stored and transmitted with {@link * TimeUnit#SECONDS} precision; finer precision will be lost. */ public Builder minFresh(int minFresh, TimeUnit timeUnit) { if (minFresh < 0) throw new IllegalArgumentException("minFresh < 0: " + minFresh); long minFreshSecondsLong = timeUnit.toSeconds(minFresh); this.minFreshSeconds = minFreshSecondsLong > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) minFreshSecondsLong; return this; } /** * Only accept the response if it is in the cache. If the response isn't cached, a {@code 504 * Unsatisfiable Request} response will be returned. */ public Builder onlyIfCached() { this.onlyIfCached = true; return this; } /** Don't accept a transformed response. */ public Builder noTransform() { this.noTransform = true; return this; } public Builder immutable() { this.immutable = true; return this; } public CacheControl build() { return new CacheControl(this); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy