13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage com.squareup.okhttp;
23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
33c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.http.HeaderParser;
43c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/**
63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * A Cache-Control header with cache directives from a server or client. These
73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * directives set policy on what responses can be stored, and which requests can
83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * be satisfied by those stored responses.
93c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * <p>See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">RFC
113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 2616, 14.9</a>.
123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */
133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpublic final class CacheControl {
143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final boolean noCache;
153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final boolean noStore;
163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final int maxAgeSeconds;
173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final int sMaxAgeSeconds;
183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final boolean isPublic;
193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final boolean mustRevalidate;
203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final int maxStaleSeconds;
213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final int minFreshSeconds;
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final boolean onlyIfCached;
233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private CacheControl(boolean noCache, boolean noStore, int maxAgeSeconds, int sMaxAgeSeconds,
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      boolean isPublic, boolean mustRevalidate, int maxStaleSeconds, int minFreshSeconds,
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      boolean onlyIfCached) {
273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.noCache = noCache;
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.noStore = noStore;
293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.maxAgeSeconds = maxAgeSeconds;
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.sMaxAgeSeconds = sMaxAgeSeconds;
313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.isPublic = isPublic;
323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.mustRevalidate = mustRevalidate;
333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.maxStaleSeconds = maxStaleSeconds;
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.minFreshSeconds = minFreshSeconds;
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.onlyIfCached = onlyIfCached;
363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * In a response, this field's name "no-cache" is misleading. It doesn't
403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * prevent us from caching the response; it only means we have to validate the
413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * response with the origin server before returning it. We can do this with a
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * conditional GET.
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * <p>In a request, it means do not use a cache to satisfy the request.
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public boolean noCache() {
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return noCache;
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** If true, this response should not be cached. */
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public boolean noStore() {
523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return noStore;
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * The duration past the response's served date that it can be served without
573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * validation.
583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public int maxAgeSeconds() {
603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return maxAgeSeconds;
613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * The "s-maxage" directive is the max age for shared caches. Not to be
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * confused with "max-age" for non-shared caches, As in Firefox and Chrome,
663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * this directive is not honored by this cache.
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public int sMaxAgeSeconds() {
693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return sMaxAgeSeconds;
703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public boolean isPublic() {
733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return isPublic;
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public boolean mustRevalidate() {
773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return mustRevalidate;
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public int maxStaleSeconds() {
813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return maxStaleSeconds;
823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public int minFreshSeconds() {
853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return minFreshSeconds;
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * This field's name "only-if-cached" is misleading. It actually means "do
903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * not use the network". It is set by a client who only wants to make a
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * request if it can be fully satisfied by the cache. Cached responses that
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * would require validation (ie. conditional gets) are not permitted if this
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * header is set.
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public boolean onlyIfCached() {
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return onlyIfCached;
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Returns the cache directives of {@code headers}. This honors both
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Cache-Control and Pragma headers if they are present.
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static CacheControl parse(Headers headers) {
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    boolean noCache = false;
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    boolean noStore = false;
1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int maxAgeSeconds = -1;
1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int sMaxAgeSeconds = -1;
1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    boolean isPublic = false;
1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    boolean mustRevalidate = false;
1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int maxStaleSeconds = -1;
1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int minFreshSeconds = -1;
1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    boolean onlyIfCached = false;
1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (int i = 0; i < headers.size(); i++) {
1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (!headers.name(i).equalsIgnoreCase("Cache-Control")
1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          && !headers.name(i).equalsIgnoreCase("Pragma")) {
1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        continue;
1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String string = headers.value(i);
1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      int pos = 0;
1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      while (pos < string.length()) {
1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        int tokenStart = pos;
1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        pos = HeaderParser.skipUntil(string, pos, "=,;");
1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        String directive = string.substring(tokenStart, pos).trim();
1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        String parameter;
1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (pos == string.length() || string.charAt(pos) == ',' || string.charAt(pos) == ';') {
1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          pos++; // consume ',' or ';' (if necessary)
1303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          parameter = null;
1313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } else {
1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          pos++; // consume '='
1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          pos = HeaderParser.skipWhitespace(string, pos);
1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          // quoted string
1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          if (pos < string.length() && string.charAt(pos) == '\"') {
1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            pos++; // consume '"' open quote
1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            int parameterStart = pos;
1393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            pos = HeaderParser.skipUntil(string, pos, "\"");
1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            parameter = string.substring(parameterStart, pos);
1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            pos++; // consume '"' close quote (if necessary)
1423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            // unquoted string
1443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          } else {
1453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            int parameterStart = pos;
1463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            pos = HeaderParser.skipUntil(string, pos, ",;");
1473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            parameter = string.substring(parameterStart, pos).trim();
1483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          }
1493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
1503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if ("no-cache".equalsIgnoreCase(directive)) {
1523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          noCache = true;
1533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } else if ("no-store".equalsIgnoreCase(directive)) {
1543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          noStore = true;
1553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } else if ("max-age".equalsIgnoreCase(directive)) {
1563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          maxAgeSeconds = HeaderParser.parseSeconds(parameter);
1573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } else if ("s-maxage".equalsIgnoreCase(directive)) {
1583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          sMaxAgeSeconds = HeaderParser.parseSeconds(parameter);
1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } else if ("public".equalsIgnoreCase(directive)) {
1603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          isPublic = true;
1613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } else if ("must-revalidate".equalsIgnoreCase(directive)) {
1623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          mustRevalidate = true;
1633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } else if ("max-stale".equalsIgnoreCase(directive)) {
1643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          maxStaleSeconds = HeaderParser.parseSeconds(parameter);
1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } else if ("min-fresh".equalsIgnoreCase(directive)) {
1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          minFreshSeconds = HeaderParser.parseSeconds(parameter);
1673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } else if ("only-if-cached".equalsIgnoreCase(directive)) {
1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          onlyIfCached = true;
1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
1703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
1713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return new CacheControl(noCache, noStore, maxAgeSeconds, sMaxAgeSeconds, isPublic,
1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        mustRevalidate, maxStaleSeconds, minFreshSeconds, onlyIfCached);
1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller}
177