示例#1
0
  /** Returns the source to satisfy {@code request} given this cached response. */
  public ResponseSource chooseResponseSource(long nowMillis, RequestHeaders request) {
    /*
     * If this response shouldn't have been stored, it should never be used
     * as a response source. This check should be redundant as long as the
     * persistence store is well-behaved and the rules are constant.
     */
    if (!isCacheable(request)) {
      return ResponseSource.NETWORK;
    }

    if (request.isNoCache() || request.hasConditions()) {
      return ResponseSource.NETWORK;
    }

    long ageMillis = computeAge(nowMillis);
    long freshMillis = computeFreshnessLifetime();

    if (request.getMaxAgeSeconds() != -1) {
      freshMillis = Math.min(freshMillis, TimeUnit.SECONDS.toMillis(request.getMaxAgeSeconds()));
    }

    long minFreshMillis = 0;
    if (request.getMinFreshSeconds() != -1) {
      minFreshMillis = TimeUnit.SECONDS.toMillis(request.getMinFreshSeconds());
    }

    long maxStaleMillis = 0;
    if (!mustRevalidate && request.getMaxStaleSeconds() != -1) {
      maxStaleMillis = TimeUnit.SECONDS.toMillis(request.getMaxStaleSeconds());
    }

    if (!noCache && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
      if (ageMillis + minFreshMillis >= freshMillis) {
        headers.add("Warning", "110 HttpURLConnection \"Response is stale\"");
      }
      /*
       * not available in API 8
      if (ageMillis > TimeUnit.HOURS.toMillis(24) && isFreshnessLifetimeHeuristic()) {
      */
      if (ageMillis > 24L * 60L * 60L * 1000L && isFreshnessLifetimeHeuristic()) {
        headers.add("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
      }
      return ResponseSource.CACHE;
    }

    if (etag != null) {
      request.setIfNoneMatch(etag);
    } else if (lastModified != null) {
      request.setIfModifiedSince(lastModified);
    } else if (servedDate != null) {
      request.setIfModifiedSince(servedDate);
    }

    return request.hasConditions() ? ResponseSource.CONDITIONAL_CACHE : ResponseSource.NETWORK;
  }
示例#2
0
  /** Returns true if this response can be stored to later serve another request. */
  public boolean isCacheable(RequestHeaders request) {
    /*
     * Always go to network for uncacheable response codes (RFC 2616, 13.4),
     * This implementation doesn't support caching partial content.
     */
    int responseCode = headers.getResponseCode();
    if (responseCode != HttpURLConnection.HTTP_OK
        && responseCode != HttpURLConnection.HTTP_NOT_AUTHORITATIVE
        && responseCode != HttpURLConnection.HTTP_MULT_CHOICE
        && responseCode != HttpURLConnection.HTTP_MOVED_PERM
        && responseCode != HttpURLConnection.HTTP_GONE) {
      return false;
    }

    /*
     * Responses to authorized requests aren't cacheable unless they include
     * a 'public', 'must-revalidate' or 's-maxage' directive.
     */
    if (request.hasAuthorization() && !isPublic && !mustRevalidate && sMaxAgeSeconds == -1) {
      return false;
    }

    if (noStore) {
      return false;
    }

    return true;
  }
示例#3
0
  /** Combines this cached header with a network header as defined by RFC 2616, 13.5.3. */
  public ResponseHeaders combine(ResponseHeaders network) {
    RawHeaders result = new RawHeaders();

    for (int i = 0; i < headers.length(); i++) {
      String fieldName = headers.getFieldName(i);
      String value = headers.getValue(i);
      if (fieldName.equals("Warning") && value.startsWith("1")) {
        continue; // drop 100-level freshness warnings
      }
      if (!isEndToEnd(fieldName) || network.headers.get(fieldName) == null) {
        result.add(fieldName, value);
      }
    }

    for (int i = 0; i < network.headers.length(); i++) {
      String fieldName = network.headers.getFieldName(i);
      if (isEndToEnd(fieldName)) {
        result.add(fieldName, network.headers.getValue(i));
      }
    }

    return new ResponseHeaders(uri, result);
  }
示例#4
0
 public void setLocalTimestamps(long sentRequestMillis, long receivedResponseMillis) {
   this.sentRequestMillis = sentRequestMillis;
   headers.add(SENT_MILLIS, Long.toString(sentRequestMillis));
   this.receivedResponseMillis = receivedResponseMillis;
   headers.add(RECEIVED_MILLIS, Long.toString(receivedResponseMillis));
 }
示例#5
0
 public void stripContentEncoding() {
   contentEncoding = null;
   headers.removeAll("Content-Encoding");
 }
示例#6
0
  public ResponseHeaders(URI uri, RawHeaders headers) {
    this.uri = uri;
    this.headers = headers;

    HeaderParser.CacheControlHandler handler =
        new HeaderParser.CacheControlHandler() {
          @Override
          public void handle(String directive, String parameter) {
            if (directive.equalsIgnoreCase("no-cache")) {
              noCache = true;
            } else if (directive.equalsIgnoreCase("no-store")) {
              noStore = true;
            } else if (directive.equalsIgnoreCase("max-age")) {
              maxAgeSeconds = HeaderParser.parseSeconds(parameter);
            } else if (directive.equalsIgnoreCase("s-maxage")) {
              sMaxAgeSeconds = HeaderParser.parseSeconds(parameter);
            } else if (directive.equalsIgnoreCase("public")) {
              isPublic = true;
            } else if (directive.equalsIgnoreCase("must-revalidate")) {
              mustRevalidate = true;
            }
          }
        };

    for (int i = 0; i < headers.length(); i++) {
      String fieldName = headers.getFieldName(i);
      String value = headers.getValue(i);
      if ("Cache-Control".equalsIgnoreCase(fieldName)) {
        HeaderParser.parseCacheControl(value, handler);
      } else if ("Date".equalsIgnoreCase(fieldName)) {
        servedDate = HttpDate.parse(value);
      } else if ("Expires".equalsIgnoreCase(fieldName)) {
        expires = HttpDate.parse(value);
      } else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
        lastModified = HttpDate.parse(value);
      } else if ("ETag".equalsIgnoreCase(fieldName)) {
        etag = value;
      } else if ("Pragma".equalsIgnoreCase(fieldName)) {
        if (value.equalsIgnoreCase("no-cache")) {
          noCache = true;
        }
      } else if ("Age".equalsIgnoreCase(fieldName)) {
        ageSeconds = HeaderParser.parseSeconds(value);
      } else if ("Vary".equalsIgnoreCase(fieldName)) {
        // Replace the immutable empty set with something we can mutate.
        if (varyFields.isEmpty()) {
          varyFields = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
        }
        for (String varyField : value.split(",")) {
          varyFields.add(varyField.trim());
        }
      } else if ("Content-Encoding".equalsIgnoreCase(fieldName)) {
        contentEncoding = value;
      } else if ("Transfer-Encoding".equalsIgnoreCase(fieldName)) {
        transferEncoding = value;
      } else if ("Content-Length".equalsIgnoreCase(fieldName)) {
        try {
          contentLength = Integer.parseInt(value);
        } catch (NumberFormatException ignored) {
        }
      } else if ("Connection".equalsIgnoreCase(fieldName)) {
        connection = value;
      } else if ("Proxy-Authenticate".equalsIgnoreCase(fieldName)) {
        proxyAuthenticate = value;
      } else if ("WWW-Authenticate".equalsIgnoreCase(fieldName)) {
        wwwAuthenticate = value;
      } else if (SENT_MILLIS.equalsIgnoreCase(fieldName)) {
        sentRequestMillis = Long.parseLong(value);
      } else if (RECEIVED_MILLIS.equalsIgnoreCase(fieldName)) {
        receivedResponseMillis = Long.parseLong(value);
      }
    }
  }