text: // Copyright 2013 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "media/blink/cache_util.h"
  
  #include <stddef.h>
  
  #include <string>
  
  #include "base/macros.h"
  #include "base/strings/string_number_conversions.h"
  #include "base/strings/string_util.h"
  #include "base/time/time.h"
  #include "net/http/http_util.h"
  #include "net/http/http_version.h"
  #include "third_party/blink/public/platform/web_string.h"
  #include "third_party/blink/public/platform/web_url_response.h"
  
  using base::Time;
  using base::TimeDelta;
  using net::HttpVersion;
  using blink::WebURLResponse;
  
  namespace media {
  
  enum { kHttpOK = 200, kHttpPartialContent = 206 };
  
  uint32_t GetReasonsForUncacheability(const WebURLResponse& response) {
    uint32_t reasons = 0;
    const int code = response.HttpStatusCode();
    const int version = response.HttpVersion();
    const HttpVersion http_version =
        version == WebURLResponse::kHTTPVersion_2_0
            ? HttpVersion(2, 0)
            : version == WebURLResponse::kHTTPVersion_1_1
                  ? HttpVersion(1, 1)
                  : version == WebURLResponse::kHTTPVersion_1_0
                        ? HttpVersion(1, 0)
                        : version == WebURLResponse::kHTTPVersion_0_9
                              ? HttpVersion(0, 9)
                              : HttpVersion();
    if (code != kHttpOK && code != kHttpPartialContent)
      reasons |= kNoData;
    if (http_version < HttpVersion(1, 1) && code == kHttpPartialContent)
      reasons |= kPre11PartialResponse;
    if (code == kHttpPartialContent &&
        !net::HttpUtil::HasStrongValidators(
            http_version, response.HttpHeaderField("etag").Utf8(),
            response.HttpHeaderField("Last-Modified").Utf8(),
            response.HttpHeaderField("Date").Utf8())) {
      reasons |= kNoStrongValidatorOnPartialResponse;
    }
  
    std::string cache_control_header =
        base::ToLowerASCII(response.HttpHeaderField("cache-control").Utf8());
    if (cache_control_header.find("no-cache") != std::string::npos)
      reasons |= kNoCache;
    if (cache_control_header.find("no-store") != std::string::npos)
      reasons |= kNoStore;
    if (cache_control_header.find("must-revalidate") != std::string::npos)
      reasons |= kHasMustRevalidate;
  
    const TimeDelta kMinimumAgeForUsefulness =
        TimeDelta::FromSeconds(3600);  // Arbitrary value.
  
    const char kMaxAgePrefix[] = "max-age=";
    const size_t kMaxAgePrefixLen = arraysize(kMaxAgePrefix) - 1;
    if (cache_control_header.substr(0, kMaxAgePrefixLen) == kMaxAgePrefix) {
      int64_t max_age_seconds;
      base::StringToInt64(
          base::StringPiece(cache_control_header.begin() + kMaxAgePrefixLen,
                            cache_control_header.end()),
          &max_age_seconds);
      if (TimeDelta::FromSeconds(max_age_seconds) < kMinimumAgeForUsefulness)
        reasons |= kShortMaxAge;
    }
  
    Time date;
    Time expires;
    if (Time::FromString(response.HttpHeaderField("Date").Utf8().data(), &date) &&
        Time::FromString(response.HttpHeaderField("Expires").Utf8().data(),
                         &expires) &&
        date > Time() && expires > Time() &&
        (expires - date) < kMinimumAgeForUsefulness) {
      reasons |= kExpiresTooSoon;
    }
  
    return reasons;
  }
  
  base::TimeDelta GetCacheValidUntil(const WebURLResponse& response) {
    std::string cache_control_header =
        base::ToLowerASCII(response.HttpHeaderField("cache-control").Utf8());
    if (cache_control_header.find("no-cache") != std::string::npos)
      return base::TimeDelta();
    if (cache_control_header.find("must-revalidate") != std::string::npos)
      return base::TimeDelta();
  
    // Max cache timeout ~= 1 month.
    base::TimeDelta ret = base::TimeDelta::FromDays(30);
  
    const char kMaxAgePrefix[] = "max-age=";
    const size_t kMaxAgePrefixLen = arraysize(kMaxAgePrefix) - 1;
    if (cache_control_header.substr(0, kMaxAgePrefixLen) == kMaxAgePrefix) {
      int64_t max_age_seconds;
      base::StringToInt64(
          base::StringPiece(cache_control_header.begin() + kMaxAgePrefixLen,
                            cache_control_header.end()),
          &max_age_seconds);
  
      ret = std::min(ret, TimeDelta::FromSeconds(max_age_seconds));
    } else {
      // Note that |date| may be smaller than |expires|, which means we'll
      // return a timetick some time in the past.
      Time date;
      Time expires;
      if (Time::FromString(response.HttpHeaderField("Date").Utf8().data(),
                           &date) &&
          Time::FromString(response.HttpHeaderField("Expires").Utf8().data(),
                           &expires) &&
          date > Time() && expires > Time()) {
        ret = std::min(ret, expires - date);
      }
    }
  
    return ret;
  }
  
  }  // namespace media
  