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