Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / components / data_reduction_proxy / common / data_reduction_proxy_headers.cc
index b7ad308..377db65 100644 (file)
 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
 
 #include <string>
+#include <vector>
 
+#include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
-#include "net/proxy/proxy_service.h"
 
 using base::StringPiece;
 using base::TimeDelta;
-using net::ProxyService;
+
+namespace {
+
+const char kChromeProxyHeader[] = "chrome-proxy";
+const char kActionValueDelimiter = '=';
+
+const char kChromeProxyActionBlockOnce[] = "block-once";
+const char kChromeProxyActionBlock[] = "block";
+const char kChromeProxyActionBypass[] = "bypass";
+
+// Actions for tamper detection fingerprints.
+const char kChromeProxyActionFingerprintChromeProxy[]   = "fcp";
+const char kChromeProxyActionFingerprintVia[]           = "fvia";
+const char kChromeProxyActionFingerprintOtherHeaders[]  = "foh";
+const char kChromeProxyActionFingerprintContentLength[] = "fcl";
+
+const int kShortBypassMaxSeconds = 59;
+const int kMediumBypassMaxSeconds = 300;
+
+// Returns a random bypass duration between 1 and 5 minutes.
+base::TimeDelta GetDefaultBypassDuration() {
+  const int64 delta_ms =
+      base::RandInt(base::TimeDelta::FromMinutes(1).InMilliseconds(),
+                    base::TimeDelta::FromMinutes(5).InMilliseconds());
+  return TimeDelta::FromMilliseconds(delta_ms);
+}
+
+}  // namespace
 
 namespace data_reduction_proxy {
 
-bool GetDataReductionProxyBypassDuration(
+bool GetDataReductionProxyActionValue(
     const net::HttpResponseHeaders* headers,
     const std::string& action_prefix,
-    base::TimeDelta* duration) {
+    std::string* action_value) {
+  DCHECK(headers);
+  DCHECK(!action_prefix.empty());
+  // A valid action does not include a trailing '='.
+  DCHECK(action_prefix[action_prefix.size() - 1] != kActionValueDelimiter);
   void* iter = NULL;
   std::string value;
-  std::string name = "chrome-proxy";
+  std::string prefix = action_prefix + kActionValueDelimiter;
 
-  while (headers->EnumerateHeader(&iter, name, &value)) {
-    if (value.size() > action_prefix.size()) {
+  while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) {
+    if (value.size() > prefix.size()) {
       if (LowerCaseEqualsASCII(value.begin(),
-                               value.begin() + action_prefix.size(),
-                               action_prefix.c_str())) {
+                               value.begin() + prefix.size(),
+                               prefix.c_str())) {
+        if (action_value)
+          *action_value = value.substr(prefix.size());
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool ParseHeadersAndSetBypassDuration(const net::HttpResponseHeaders* headers,
+                                      const std::string& action_prefix,
+                                      base::TimeDelta* bypass_duration) {
+  DCHECK(headers);
+  DCHECK(!action_prefix.empty());
+  // A valid action does not include a trailing '='.
+  DCHECK(action_prefix[action_prefix.size() - 1] != kActionValueDelimiter);
+  void* iter = NULL;
+  std::string value;
+  std::string prefix = action_prefix + kActionValueDelimiter;
+
+  while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) {
+    if (value.size() > prefix.size()) {
+      if (LowerCaseEqualsASCII(value.begin(),
+                               value.begin() + prefix.size(),
+                               prefix.c_str())) {
         int64 seconds;
         if (!base::StringToInt64(
-                StringPiece(value.begin() + action_prefix.size(), value.end()),
+                StringPiece(value.begin() + prefix.size(), value.end()),
                 &seconds) || seconds < 0) {
           continue;  // In case there is a well formed instruction.
         }
-        *duration = TimeDelta::FromSeconds(seconds);
+        if (seconds != 0) {
+          *bypass_duration = TimeDelta::FromSeconds(seconds);
+        } else {
+          // Server deferred to us to choose a duration. Default to a random
+          // duration between one and five minutes.
+          *bypass_duration = GetDefaultBypassDuration();
+        }
         return true;
       }
     }
@@ -47,11 +110,10 @@ bool GetDataReductionProxyBypassDuration(
   return false;
 }
 
-bool GetDataReductionProxyInfo(const net::HttpResponseHeaders* headers,
-                               DataReductionProxyInfo* proxy_info) {
+bool ParseHeadersAndSetProxyInfo(const net::HttpResponseHeaders* headers,
+                                 DataReductionProxyInfo* proxy_info) {
   DCHECK(proxy_info);
-  proxy_info->bypass_all = false;
-  proxy_info->bypass_duration = TimeDelta();
+
   // Support header of the form Chrome-Proxy: bypass|block=<duration>, where
   // <duration> is the number of seconds to wait before retrying
   // the proxy. If the duration is 0, then the default proxy retry delay
@@ -60,23 +122,42 @@ bool GetDataReductionProxyInfo(const net::HttpResponseHeaders* headers,
   // proxy, whereas 'block' instructs Chrome to bypass all available data
   // reduction proxies.
 
-  // 'block' takes precedence over 'bypass', so look for it first.
+  // 'block' takes precedence over 'bypass' and 'block-once', so look for it
+  // first.
   // TODO(bengr): Reduce checks for 'block' and 'bypass' to a single loop.
-  if (GetDataReductionProxyBypassDuration(
-      headers, "block=", &proxy_info->bypass_duration)) {
+  if (ParseHeadersAndSetBypassDuration(
+          headers, kChromeProxyActionBlock, &proxy_info->bypass_duration)) {
     proxy_info->bypass_all = true;
+    proxy_info->mark_proxies_as_bad = true;
     return true;
   }
 
   // Next, look for 'bypass'.
-  if (GetDataReductionProxyBypassDuration(
-      headers, "bypass=", &proxy_info->bypass_duration)) {
+  if (ParseHeadersAndSetBypassDuration(
+          headers, kChromeProxyActionBypass, &proxy_info->bypass_duration)) {
+    proxy_info->bypass_all = false;
+    proxy_info->mark_proxies_as_bad = true;
     return true;
   }
+
+  // Lastly, look for 'block-once'. 'block-once' instructs Chrome to retry the
+  // current request (if it's idempotent), bypassing all available data
+  // reduction proxies. Unlike 'block', 'block-once' does not cause data
+  // reduction proxies to be bypassed for an extended period of time;
+  // 'block-once' only affects the retry of the current request.
+  if (headers->HasHeaderValue(kChromeProxyHeader,
+                              kChromeProxyActionBlockOnce)) {
+    proxy_info->bypass_all = true;
+    proxy_info->mark_proxies_as_bad = false;
+    proxy_info->bypass_duration = TimeDelta();
+    return true;
+  }
+
   return false;
 }
 
-bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers) {
+bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers,
+                                    bool* has_intermediary) {
   const size_t kVersionSize = 4;
   const char kDataReductionProxyViaValue[] = "Chrome-Compression-Proxy";
   size_t value_len = strlen(kDataReductionProxyViaValue);
@@ -88,8 +169,13 @@ bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers) {
   // 'Via: 1.1 Chrome-Compression-Proxy'
   while (headers->EnumerateHeader(&iter, "via", &value)) {
     if (value.size() >= kVersionSize + value_len &&
-        !value.compare(kVersionSize, value_len, kDataReductionProxyViaValue))
+        !value.compare(kVersionSize, value_len, kDataReductionProxyViaValue)) {
+      if (has_intermediary)
+        // We assume intermediary exists if there is another Via header after
+        // the data reduction proxy's Via header.
+        *has_intermediary = !(headers->EnumerateHeader(&iter, "via", &value));
       return true;
+    }
   }
 
   // TODO(bengr): Remove deprecated header value.
@@ -97,37 +183,53 @@ bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers) {
       "1.1 Chrome Compression Proxy";
   iter = NULL;
   while (headers->EnumerateHeader(&iter, "via", &value))
-    if (value == kDeprecatedDataReductionProxyViaValue)
+    if (value == kDeprecatedDataReductionProxyViaValue) {
+      if (has_intermediary)
+        *has_intermediary = !(headers->EnumerateHeader(&iter, "via", &value));
       return true;
+    }
 
   return false;
 }
 
-net::ProxyService::DataReductionProxyBypassEventType
-GetDataReductionProxyBypassEventType(
+DataReductionProxyBypassType GetDataReductionProxyBypassType(
     const net::HttpResponseHeaders* headers,
     DataReductionProxyInfo* data_reduction_proxy_info) {
   DCHECK(data_reduction_proxy_info);
-  if (GetDataReductionProxyInfo(headers, data_reduction_proxy_info)) {
+  if (ParseHeadersAndSetProxyInfo(headers, data_reduction_proxy_info)) {
     // A chrome-proxy response header is only present in a 502. For proper
     // reporting, this check must come before the 5xx checks below.
-    if (data_reduction_proxy_info->bypass_duration < TimeDelta::FromMinutes(30))
-      return ProxyService::SHORT_BYPASS;
-    return ProxyService::LONG_BYPASS;
-  }
-  if (headers->response_code() == net::HTTP_INTERNAL_SERVER_ERROR ||
-      headers->response_code() == net::HTTP_BAD_GATEWAY ||
-      headers->response_code() == net::HTTP_SERVICE_UNAVAILABLE) {
-    // Fall back if a 500, 502 or 503 is returned.
-    return ProxyService::INTERNAL_SERVER_ERROR_BYPASS;
+    if (!data_reduction_proxy_info->mark_proxies_as_bad)
+      return BYPASS_EVENT_TYPE_CURRENT;
+
+    const TimeDelta& duration = data_reduction_proxy_info->bypass_duration;
+    if (duration <= TimeDelta::FromSeconds(kShortBypassMaxSeconds))
+      return BYPASS_EVENT_TYPE_SHORT;
+    if (duration <= TimeDelta::FromSeconds(kMediumBypassMaxSeconds))
+      return BYPASS_EVENT_TYPE_MEDIUM;
+    return BYPASS_EVENT_TYPE_LONG;
   }
+
+  // If a bypass is triggered by any of the following cases, then the data
+  // reduction proxy should be bypassed for a random duration between 1 and 5
+  // minutes.
+  data_reduction_proxy_info->mark_proxies_as_bad = true;
+  data_reduction_proxy_info->bypass_duration = GetDefaultBypassDuration();
+
+  // Fall back if a 500, 502 or 503 is returned.
+  if (headers->response_code() == net::HTTP_INTERNAL_SERVER_ERROR)
+    return BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR;
+  if (headers->response_code() == net::HTTP_BAD_GATEWAY)
+    return BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY;
+  if (headers->response_code() == net::HTTP_SERVICE_UNAVAILABLE)
+    return BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE;
   // TODO(kundaji): Bypass if Proxy-Authenticate header value cannot be
   // interpreted by data reduction proxy.
   if (headers->response_code() == net::HTTP_PROXY_AUTHENTICATION_REQUIRED &&
       !headers->HasHeader("Proxy-Authenticate")) {
-    return ProxyService::MALFORMED_407_BYPASS;
+    return BYPASS_EVENT_TYPE_MALFORMED_407;
   }
-  if (!HasDataReductionProxyViaHeader(headers) &&
+  if (!HasDataReductionProxyViaHeader(headers, NULL) &&
       (headers->response_code() != net::HTTP_NOT_MODIFIED)) {
     // A Via header might not be present in a 304. Since the goal of a 304
     // response is to minimize information transfer, a sender in general
@@ -138,12 +240,70 @@ GetDataReductionProxyBypassEventType(
     // Separate this case from other responses that are missing the header.
     if (headers->response_code() >= net::HTTP_BAD_REQUEST &&
         headers->response_code() < net::HTTP_INTERNAL_SERVER_ERROR) {
-      return ProxyService::PROXY_4XX_BYPASS;
+      return BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX;
     }
-    return ProxyService::MISSING_VIA_HEADER;
+    return BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER;
   }
   // There is no bypass event.
-  return ProxyService::BYPASS_EVENT_TYPE_MAX;
+  return BYPASS_EVENT_TYPE_MAX;
+}
+
+bool GetDataReductionProxyActionFingerprintChromeProxy(
+    const net::HttpResponseHeaders* headers,
+    std::string* chrome_proxy_fingerprint) {
+  return GetDataReductionProxyActionValue(
+      headers,
+      kChromeProxyActionFingerprintChromeProxy,
+      chrome_proxy_fingerprint);
+}
+
+bool GetDataReductionProxyActionFingerprintVia(
+    const net::HttpResponseHeaders* headers,
+    std::string* via_fingerprint) {
+  return GetDataReductionProxyActionValue(
+      headers,
+      kChromeProxyActionFingerprintVia,
+      via_fingerprint);
+}
+
+bool GetDataReductionProxyActionFingerprintOtherHeaders(
+    const net::HttpResponseHeaders* headers,
+    std::string* other_headers_fingerprint) {
+  return GetDataReductionProxyActionValue(
+      headers,
+      kChromeProxyActionFingerprintOtherHeaders,
+      other_headers_fingerprint);
+}
+
+bool GetDataReductionProxyActionFingerprintContentLength(
+    const net::HttpResponseHeaders* headers,
+    std::string* content_length_fingerprint) {
+  return GetDataReductionProxyActionValue(
+      headers,
+      kChromeProxyActionFingerprintContentLength,
+      content_length_fingerprint);
+}
+
+void GetDataReductionProxyHeaderWithFingerprintRemoved(
+    const net::HttpResponseHeaders* headers,
+    std::vector<std::string>* values) {
+  DCHECK(values);
+  std::string chrome_proxy_fingerprint_prefix = std::string(
+      kChromeProxyActionFingerprintChromeProxy) + kActionValueDelimiter;
+
+  std::string value;
+  void* iter = NULL;
+  while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) {
+    if (value.size() > chrome_proxy_fingerprint_prefix.size()) {
+      if (LowerCaseEqualsASCII(
+          value.begin(),
+          value.begin() + chrome_proxy_fingerprint_prefix.size(),
+          chrome_proxy_fingerprint_prefix.c_str())) {
+        continue;
+      }
+    }
+    values->push_back(value);
+  }
 }
 
 }  // namespace data_reduction_proxy