Home | History | Annotate | Download | only in common
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
      6 
      7 #include <string>
      8 
      9 #include "base/strings/string_number_conversions.h"
     10 #include "base/strings/string_piece.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/time/time.h"
     13 #include "net/http/http_response_headers.h"
     14 #include "net/http/http_status_code.h"
     15 #include "net/proxy/proxy_service.h"
     16 
     17 using base::StringPiece;
     18 using base::TimeDelta;
     19 using net::ProxyService;
     20 
     21 namespace data_reduction_proxy {
     22 
     23 bool GetDataReductionProxyBypassDuration(
     24     const net::HttpResponseHeaders* headers,
     25     const std::string& action_prefix,
     26     base::TimeDelta* duration) {
     27   void* iter = NULL;
     28   std::string value;
     29   std::string name = "chrome-proxy";
     30 
     31   while (headers->EnumerateHeader(&iter, name, &value)) {
     32     if (value.size() > action_prefix.size()) {
     33       if (LowerCaseEqualsASCII(value.begin(),
     34                                value.begin() + action_prefix.size(),
     35                                action_prefix.c_str())) {
     36         int64 seconds;
     37         if (!base::StringToInt64(
     38                 StringPiece(value.begin() + action_prefix.size(), value.end()),
     39                 &seconds) || seconds < 0) {
     40           continue;  // In case there is a well formed instruction.
     41         }
     42         *duration = TimeDelta::FromSeconds(seconds);
     43         return true;
     44       }
     45     }
     46   }
     47   return false;
     48 }
     49 
     50 bool GetDataReductionProxyInfo(const net::HttpResponseHeaders* headers,
     51                                DataReductionProxyInfo* proxy_info) {
     52   DCHECK(proxy_info);
     53   proxy_info->bypass_all = false;
     54   proxy_info->bypass_duration = TimeDelta();
     55   // Support header of the form Chrome-Proxy: bypass|block=<duration>, where
     56   // <duration> is the number of seconds to wait before retrying
     57   // the proxy. If the duration is 0, then the default proxy retry delay
     58   // (specified in |ProxyList::UpdateRetryInfoOnFallback|) will be used.
     59   // 'bypass' instructs Chrome to bypass the currently connected data reduction
     60   // proxy, whereas 'block' instructs Chrome to bypass all available data
     61   // reduction proxies.
     62 
     63   // 'block' takes precedence over 'bypass', so look for it first.
     64   // TODO(bengr): Reduce checks for 'block' and 'bypass' to a single loop.
     65   if (GetDataReductionProxyBypassDuration(
     66       headers, "block=", &proxy_info->bypass_duration)) {
     67     proxy_info->bypass_all = true;
     68     return true;
     69   }
     70 
     71   // Next, look for 'bypass'.
     72   if (GetDataReductionProxyBypassDuration(
     73       headers, "bypass=", &proxy_info->bypass_duration)) {
     74     return true;
     75   }
     76   return false;
     77 }
     78 
     79 bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers) {
     80   const size_t kVersionSize = 4;
     81   const char kDataReductionProxyViaValue[] = "Chrome-Compression-Proxy";
     82   size_t value_len = strlen(kDataReductionProxyViaValue);
     83   void* iter = NULL;
     84   std::string value;
     85 
     86   // Case-sensitive comparison of |value|. Assumes the received protocol and the
     87   // space following it are always |kVersionSize| characters. E.g.,
     88   // 'Via: 1.1 Chrome-Compression-Proxy'
     89   while (headers->EnumerateHeader(&iter, "via", &value)) {
     90     if (value.size() >= kVersionSize + value_len &&
     91         !value.compare(kVersionSize, value_len, kDataReductionProxyViaValue))
     92       return true;
     93   }
     94 
     95   // TODO(bengr): Remove deprecated header value.
     96   const char kDeprecatedDataReductionProxyViaValue[] =
     97       "1.1 Chrome Compression Proxy";
     98   iter = NULL;
     99   while (headers->EnumerateHeader(&iter, "via", &value))
    100     if (value == kDeprecatedDataReductionProxyViaValue)
    101       return true;
    102 
    103   return false;
    104 }
    105 
    106 net::ProxyService::DataReductionProxyBypassEventType
    107 GetDataReductionProxyBypassEventType(
    108     const net::HttpResponseHeaders* headers,
    109     DataReductionProxyInfo* data_reduction_proxy_info) {
    110   DCHECK(data_reduction_proxy_info);
    111   if (GetDataReductionProxyInfo(headers, data_reduction_proxy_info)) {
    112     // A chrome-proxy response header is only present in a 502. For proper
    113     // reporting, this check must come before the 5xx checks below.
    114     if (data_reduction_proxy_info->bypass_duration < TimeDelta::FromMinutes(30))
    115       return ProxyService::SHORT_BYPASS;
    116     return ProxyService::LONG_BYPASS;
    117   }
    118   if (headers->response_code() == net::HTTP_INTERNAL_SERVER_ERROR ||
    119       headers->response_code() == net::HTTP_BAD_GATEWAY ||
    120       headers->response_code() == net::HTTP_SERVICE_UNAVAILABLE) {
    121     // Fall back if a 500, 502 or 503 is returned.
    122     return ProxyService::INTERNAL_SERVER_ERROR_BYPASS;
    123   }
    124   // TODO(kundaji): Bypass if Proxy-Authenticate header value cannot be
    125   // interpreted by data reduction proxy.
    126   if (headers->response_code() == net::HTTP_PROXY_AUTHENTICATION_REQUIRED &&
    127       !headers->HasHeader("Proxy-Authenticate")) {
    128     return ProxyService::MALFORMED_407_BYPASS;
    129   }
    130   if (!HasDataReductionProxyViaHeader(headers) &&
    131       (headers->response_code() != net::HTTP_NOT_MODIFIED)) {
    132     // A Via header might not be present in a 304. Since the goal of a 304
    133     // response is to minimize information transfer, a sender in general
    134     // should not generate representation metadata other than Cache-Control,
    135     // Content-Location, Date, ETag, Expires, and Vary.
    136 
    137     // The proxy Via header might also not be present in a 4xx response.
    138     // Separate this case from other responses that are missing the header.
    139     if (headers->response_code() >= net::HTTP_BAD_REQUEST &&
    140         headers->response_code() < net::HTTP_INTERNAL_SERVER_ERROR) {
    141       return ProxyService::PROXY_4XX_BYPASS;
    142     }
    143     return ProxyService::MISSING_VIA_HEADER;
    144   }
    145   // There is no bypass event.
    146   return ProxyService::BYPASS_EVENT_TYPE_MAX;
    147 }
    148 
    149 }  // namespace data_reduction_proxy
    150