| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_heade
rs.h" | 5 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_heade
rs.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <string> | 10 #include <string> |
| 11 #include <utility> | 11 #include <utility> |
| 12 #include <vector> | 12 #include <vector> |
| 13 | 13 |
| 14 #include "base/rand_util.h" | 14 #include "base/rand_util.h" |
| 15 #include "base/strings/string_number_conversions.h" | 15 #include "base/strings/string_number_conversions.h" |
| 16 #include "base/strings/string_split.h" | 16 #include "base/strings/string_split.h" |
| 17 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
| 18 #include "base/time/time.h" | 18 #include "base/time/time.h" |
| 19 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event
_creator.h" | 19 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event
_creator.h" |
| 20 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_param
s.h" | 20 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_param
s.h" |
| 21 #include "net/http/http_response_headers.h" | 21 #include "net/http/http_response_headers.h" |
| 22 #include "net/http/http_status_code.h" | 22 #include "net/http/http_status_code.h" |
| 23 #include "net/url_request/url_request.h" |
| 23 | 24 |
| 24 using base::StringPiece; | 25 using base::StringPiece; |
| 25 using base::TimeDelta; | 26 using base::TimeDelta; |
| 26 | 27 |
| 27 namespace { | 28 namespace { |
| 28 | 29 |
| 29 const char kChromeProxyHeader[] = "chrome-proxy"; | 30 const char kChromeProxyHeader[] = "chrome-proxy"; |
| 30 const char kChromeProxyECTHeader[] = "chrome-proxy-ect"; | 31 const char kChromeProxyECTHeader[] = "chrome-proxy-ect"; |
| 31 const char kChromeProxyAcceptTransformHeader[] = | 32 const char kChromeProxyAcceptTransformHeader[] = |
| 32 "chrome-proxy-accept-transform"; | 33 "chrome-proxy-accept-transform"; |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 99 const std::string& transform_type) { | 100 const std::string& transform_type) { |
| 100 std::string header_value; | 101 std::string header_value; |
| 101 if (!headers.GetNormalizedHeader( | 102 if (!headers.GetNormalizedHeader( |
| 102 data_reduction_proxy::chrome_proxy_content_transform_header(), | 103 data_reduction_proxy::chrome_proxy_content_transform_header(), |
| 103 &header_value)) { | 104 &header_value)) { |
| 104 return false; | 105 return false; |
| 105 } | 106 } |
| 106 return IsPreviewTypeInHeaderValue(header_value, transform_type); | 107 return IsPreviewTypeInHeaderValue(header_value, transform_type); |
| 107 } | 108 } |
| 108 | 109 |
| 110 // Returns true if there is a cycle in |url_chain|. |
| 111 bool HasURLRedirectCycle(const std::vector<GURL>& url_chain) { |
| 112 if (url_chain.size() <= 1) |
| 113 return false; |
| 114 |
| 115 // If the last entry occurs earlier in the |url_chain|, then very likely there |
| 116 // is a redirect cycle. |
| 117 return std::find(url_chain.rbegin() + 1, url_chain.rend(), |
| 118 url_chain.back()) != url_chain.rend(); |
| 119 } |
| 120 |
| 109 } // namespace | 121 } // namespace |
| 110 | 122 |
| 111 namespace data_reduction_proxy { | 123 namespace data_reduction_proxy { |
| 112 | 124 |
| 113 const char* chrome_proxy_header() { | 125 const char* chrome_proxy_header() { |
| 114 return kChromeProxyHeader; | 126 return kChromeProxyHeader; |
| 115 } | 127 } |
| 116 | 128 |
| 117 const char* chrome_proxy_ect_header() { | 129 const char* chrome_proxy_ect_header() { |
| 118 return kChromeProxyECTHeader; | 130 return kChromeProxyECTHeader; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 173 while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) { | 185 while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) { |
| 174 if (StartsWithActionPrefix(value, action_prefix)) { | 186 if (StartsWithActionPrefix(value, action_prefix)) { |
| 175 if (action_value) | 187 if (action_value) |
| 176 *action_value = value.substr(action_prefix.size() + 1); | 188 *action_value = value.substr(action_prefix.size() + 1); |
| 177 return true; | 189 return true; |
| 178 } | 190 } |
| 179 } | 191 } |
| 180 return false; | 192 return false; |
| 181 } | 193 } |
| 182 | 194 |
| 183 bool ParseHeadersAndSetBypassDuration(const net::HttpResponseHeaders* headers, | 195 bool ParseHeadersAndSetBypassDuration(const net::HttpResponseHeaders& headers, |
| 184 base::StringPiece action_prefix, | 196 base::StringPiece action_prefix, |
| 185 base::TimeDelta* bypass_duration) { | 197 base::TimeDelta* bypass_duration) { |
| 186 DCHECK(headers); | |
| 187 size_t iter = 0; | 198 size_t iter = 0; |
| 188 std::string value; | 199 std::string value; |
| 189 | 200 |
| 190 while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) { | 201 while (headers.EnumerateHeader(&iter, kChromeProxyHeader, &value)) { |
| 191 if (StartsWithActionPrefix(value, action_prefix)) { | 202 if (StartsWithActionPrefix(value, action_prefix)) { |
| 192 int64_t seconds; | 203 int64_t seconds; |
| 193 if (!base::StringToInt64( | 204 if (!base::StringToInt64( |
| 194 StringPiece(value).substr(action_prefix.size() + 1), &seconds) || | 205 StringPiece(value).substr(action_prefix.size() + 1), &seconds) || |
| 195 seconds < 0) { | 206 seconds < 0) { |
| 196 continue; // In case there is a well formed instruction. | 207 continue; // In case there is a well formed instruction. |
| 197 } | 208 } |
| 198 if (seconds != 0) { | 209 if (seconds != 0) { |
| 199 *bypass_duration = TimeDelta::FromSeconds(seconds); | 210 *bypass_duration = TimeDelta::FromSeconds(seconds); |
| 200 } else { | 211 } else { |
| 201 // Server deferred to us to choose a duration. Default to a random | 212 // Server deferred to us to choose a duration. Default to a random |
| 202 // duration between one and five minutes. | 213 // duration between one and five minutes. |
| 203 *bypass_duration = GetDefaultBypassDuration(); | 214 *bypass_duration = GetDefaultBypassDuration(); |
| 204 } | 215 } |
| 205 return true; | 216 return true; |
| 206 } | 217 } |
| 207 } | 218 } |
| 208 return false; | 219 return false; |
| 209 } | 220 } |
| 210 | 221 |
| 211 bool ParseHeadersForBypassInfo(const net::HttpResponseHeaders* headers, | 222 bool ParseHeadersForBypassInfo(const net::HttpResponseHeaders& headers, |
| 212 DataReductionProxyInfo* proxy_info) { | 223 DataReductionProxyInfo* proxy_info) { |
| 213 DCHECK(proxy_info); | 224 DCHECK(proxy_info); |
| 214 | 225 |
| 215 // Support header of the form Chrome-Proxy: bypass|block=<duration>, where | 226 // Support header of the form Chrome-Proxy: bypass|block=<duration>, where |
| 216 // <duration> is the number of seconds to wait before retrying | 227 // <duration> is the number of seconds to wait before retrying |
| 217 // the proxy. If the duration is 0, then the default proxy retry delay | 228 // the proxy. If the duration is 0, then the default proxy retry delay |
| 218 // (specified in |ProxyList::UpdateRetryInfoOnFallback|) will be used. | 229 // (specified in |ProxyList::UpdateRetryInfoOnFallback|) will be used. |
| 219 // 'bypass' instructs Chrome to bypass the currently connected data reduction | 230 // 'bypass' instructs Chrome to bypass the currently connected data reduction |
| 220 // proxy, whereas 'block' instructs Chrome to bypass all available data | 231 // proxy, whereas 'block' instructs Chrome to bypass all available data |
| 221 // reduction proxies. | 232 // reduction proxies. |
| (...skipping 16 matching lines...) Expand all Loading... |
| 238 proxy_info->mark_proxies_as_bad = true; | 249 proxy_info->mark_proxies_as_bad = true; |
| 239 proxy_info->bypass_action = BYPASS_ACTION_TYPE_BYPASS; | 250 proxy_info->bypass_action = BYPASS_ACTION_TYPE_BYPASS; |
| 240 return true; | 251 return true; |
| 241 } | 252 } |
| 242 | 253 |
| 243 // Lastly, look for 'block-once'. 'block-once' instructs Chrome to retry the | 254 // Lastly, look for 'block-once'. 'block-once' instructs Chrome to retry the |
| 244 // current request (if it's idempotent), bypassing all available data | 255 // current request (if it's idempotent), bypassing all available data |
| 245 // reduction proxies. Unlike 'block', 'block-once' does not cause data | 256 // reduction proxies. Unlike 'block', 'block-once' does not cause data |
| 246 // reduction proxies to be bypassed for an extended period of time; | 257 // reduction proxies to be bypassed for an extended period of time; |
| 247 // 'block-once' only affects the retry of the current request. | 258 // 'block-once' only affects the retry of the current request. |
| 248 if (headers->HasHeaderValue(kChromeProxyHeader, | 259 if (headers.HasHeaderValue(kChromeProxyHeader, kChromeProxyActionBlockOnce)) { |
| 249 kChromeProxyActionBlockOnce)) { | |
| 250 proxy_info->bypass_all = true; | 260 proxy_info->bypass_all = true; |
| 251 proxy_info->mark_proxies_as_bad = false; | 261 proxy_info->mark_proxies_as_bad = false; |
| 252 proxy_info->bypass_duration = TimeDelta(); | 262 proxy_info->bypass_duration = TimeDelta(); |
| 253 proxy_info->bypass_action = BYPASS_ACTION_TYPE_BLOCK_ONCE; | 263 proxy_info->bypass_action = BYPASS_ACTION_TYPE_BLOCK_ONCE; |
| 254 return true; | 264 return true; |
| 255 } | 265 } |
| 256 | 266 |
| 257 return false; | 267 return false; |
| 258 } | 268 } |
| 259 | 269 |
| 260 bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers, | 270 bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders& headers, |
| 261 bool* has_intermediary) { | 271 bool* has_intermediary) { |
| 262 static const size_t kVersionSize = 4; | 272 static const size_t kVersionSize = 4; |
| 263 static const char kDataReductionProxyViaValue[] = "Chrome-Compression-Proxy"; | 273 static const char kDataReductionProxyViaValue[] = "Chrome-Compression-Proxy"; |
| 264 size_t iter = 0; | 274 size_t iter = 0; |
| 265 std::string value; | 275 std::string value; |
| 266 | 276 |
| 267 // Case-sensitive comparison of |value|. Assumes the received protocol and the | 277 // Case-sensitive comparison of |value|. Assumes the received protocol and the |
| 268 // space following it are always |kVersionSize| characters. E.g., | 278 // space following it are always |kVersionSize| characters. E.g., |
| 269 // 'Via: 1.1 Chrome-Compression-Proxy' | 279 // 'Via: 1.1 Chrome-Compression-Proxy' |
| 270 while (headers->EnumerateHeader(&iter, "via", &value)) { | 280 while (headers.EnumerateHeader(&iter, "via", &value)) { |
| 271 if (base::StringPiece(value).substr( | 281 if (base::StringPiece(value).substr( |
| 272 kVersionSize, arraysize(kDataReductionProxyViaValue) - 1) == | 282 kVersionSize, arraysize(kDataReductionProxyViaValue) - 1) == |
| 273 kDataReductionProxyViaValue) { | 283 kDataReductionProxyViaValue) { |
| 274 if (has_intermediary) | 284 if (has_intermediary) |
| 275 // We assume intermediary exists if there is another Via header after | 285 // We assume intermediary exists if there is another Via header after |
| 276 // the data reduction proxy's Via header. | 286 // the data reduction proxy's Via header. |
| 277 *has_intermediary = !(headers->EnumerateHeader(&iter, "via", &value)); | 287 *has_intermediary = !(headers.EnumerateHeader(&iter, "via", &value)); |
| 278 return true; | 288 return true; |
| 279 } | 289 } |
| 280 } | 290 } |
| 281 | 291 |
| 282 return false; | 292 return false; |
| 283 } | 293 } |
| 284 | 294 |
| 285 DataReductionProxyBypassType GetDataReductionProxyBypassType( | 295 DataReductionProxyBypassType GetDataReductionProxyBypassType( |
| 286 const net::HttpResponseHeaders* headers, | 296 const std::vector<GURL>& url_chain, |
| 297 const net::HttpResponseHeaders& headers, |
| 287 DataReductionProxyInfo* data_reduction_proxy_info) { | 298 DataReductionProxyInfo* data_reduction_proxy_info) { |
| 288 DCHECK(data_reduction_proxy_info); | 299 DCHECK(data_reduction_proxy_info); |
| 300 |
| 301 bool has_via_header = HasDataReductionProxyViaHeader(headers, nullptr); |
| 302 |
| 303 if (has_via_header && HasURLRedirectCycle(url_chain)) { |
| 304 data_reduction_proxy_info->bypass_all = true; |
| 305 data_reduction_proxy_info->mark_proxies_as_bad = false; |
| 306 data_reduction_proxy_info->bypass_duration = base::TimeDelta(); |
| 307 data_reduction_proxy_info->bypass_action = BYPASS_ACTION_TYPE_BLOCK_ONCE; |
| 308 return BYPASS_EVENT_TYPE_URL_REDIRECT_CYCLE; |
| 309 } |
| 310 |
| 289 if (ParseHeadersForBypassInfo(headers, data_reduction_proxy_info)) { | 311 if (ParseHeadersForBypassInfo(headers, data_reduction_proxy_info)) { |
| 290 // A chrome-proxy response header is only present in a 502. For proper | 312 // A chrome-proxy response header is only present in a 502. For proper |
| 291 // reporting, this check must come before the 5xx checks below. | 313 // reporting, this check must come before the 5xx checks below. |
| 292 if (!data_reduction_proxy_info->mark_proxies_as_bad) | 314 if (!data_reduction_proxy_info->mark_proxies_as_bad) |
| 293 return BYPASS_EVENT_TYPE_CURRENT; | 315 return BYPASS_EVENT_TYPE_CURRENT; |
| 294 | 316 |
| 295 const TimeDelta& duration = data_reduction_proxy_info->bypass_duration; | 317 const TimeDelta& duration = data_reduction_proxy_info->bypass_duration; |
| 296 if (duration <= TimeDelta::FromSeconds(kShortBypassMaxSeconds)) | 318 if (duration <= TimeDelta::FromSeconds(kShortBypassMaxSeconds)) |
| 297 return BYPASS_EVENT_TYPE_SHORT; | 319 return BYPASS_EVENT_TYPE_SHORT; |
| 298 if (duration <= TimeDelta::FromSeconds(kMediumBypassMaxSeconds)) | 320 if (duration <= TimeDelta::FromSeconds(kMediumBypassMaxSeconds)) |
| 299 return BYPASS_EVENT_TYPE_MEDIUM; | 321 return BYPASS_EVENT_TYPE_MEDIUM; |
| 300 return BYPASS_EVENT_TYPE_LONG; | 322 return BYPASS_EVENT_TYPE_LONG; |
| 301 } | 323 } |
| 302 | 324 |
| 303 // If a bypass is triggered by any of the following cases, then the data | 325 // If a bypass is triggered by any of the following cases, then the data |
| 304 // reduction proxy should be bypassed for a random duration between 1 and 5 | 326 // reduction proxy should be bypassed for a random duration between 1 and 5 |
| 305 // minutes. | 327 // minutes. |
| 306 data_reduction_proxy_info->mark_proxies_as_bad = true; | 328 data_reduction_proxy_info->mark_proxies_as_bad = true; |
| 307 data_reduction_proxy_info->bypass_duration = GetDefaultBypassDuration(); | 329 data_reduction_proxy_info->bypass_duration = GetDefaultBypassDuration(); |
| 308 | 330 |
| 309 // Fall back if a 500, 502 or 503 is returned. | 331 // Fall back if a 500, 502 or 503 is returned. |
| 310 if (headers->response_code() == net::HTTP_INTERNAL_SERVER_ERROR) | 332 if (headers.response_code() == net::HTTP_INTERNAL_SERVER_ERROR) |
| 311 return BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR; | 333 return BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR; |
| 312 if (headers->response_code() == net::HTTP_BAD_GATEWAY) | 334 if (headers.response_code() == net::HTTP_BAD_GATEWAY) |
| 313 return BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY; | 335 return BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY; |
| 314 if (headers->response_code() == net::HTTP_SERVICE_UNAVAILABLE) | 336 if (headers.response_code() == net::HTTP_SERVICE_UNAVAILABLE) |
| 315 return BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE; | 337 return BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE; |
| 316 // TODO(kundaji): Bypass if Proxy-Authenticate header value cannot be | 338 // TODO(kundaji): Bypass if Proxy-Authenticate header value cannot be |
| 317 // interpreted by data reduction proxy. | 339 // interpreted by data reduction proxy. |
| 318 if (headers->response_code() == net::HTTP_PROXY_AUTHENTICATION_REQUIRED && | 340 if (headers.response_code() == net::HTTP_PROXY_AUTHENTICATION_REQUIRED && |
| 319 !headers->HasHeader("Proxy-Authenticate")) { | 341 !headers.HasHeader("Proxy-Authenticate")) { |
| 320 return BYPASS_EVENT_TYPE_MALFORMED_407; | 342 return BYPASS_EVENT_TYPE_MALFORMED_407; |
| 321 } | 343 } |
| 322 if (!HasDataReductionProxyViaHeader(headers, NULL) && | 344 if (!has_via_header && (headers.response_code() != net::HTTP_NOT_MODIFIED)) { |
| 323 (headers->response_code() != net::HTTP_NOT_MODIFIED)) { | |
| 324 // A Via header might not be present in a 304. Since the goal of a 304 | 345 // A Via header might not be present in a 304. Since the goal of a 304 |
| 325 // response is to minimize information transfer, a sender in general | 346 // response is to minimize information transfer, a sender in general |
| 326 // should not generate representation metadata other than Cache-Control, | 347 // should not generate representation metadata other than Cache-Control, |
| 327 // Content-Location, Date, ETag, Expires, and Vary. | 348 // Content-Location, Date, ETag, Expires, and Vary. |
| 328 | 349 |
| 329 // The proxy Via header might also not be present in a 4xx response. | 350 // The proxy Via header might also not be present in a 4xx response. |
| 330 // Separate this case from other responses that are missing the header. | 351 // Separate this case from other responses that are missing the header. |
| 331 if (headers->response_code() >= net::HTTP_BAD_REQUEST && | 352 if (headers.response_code() >= net::HTTP_BAD_REQUEST && |
| 332 headers->response_code() < net::HTTP_INTERNAL_SERVER_ERROR) { | 353 headers.response_code() < net::HTTP_INTERNAL_SERVER_ERROR) { |
| 333 // At this point, any 4xx response that is missing the via header | 354 // At this point, any 4xx response that is missing the via header |
| 334 // indicates an issue that is scoped to only the current request, so only | 355 // indicates an issue that is scoped to only the current request, so only |
| 335 // bypass the data reduction proxy for the current request. | 356 // bypass the data reduction proxy for the current request. |
| 336 data_reduction_proxy_info->bypass_all = true; | 357 data_reduction_proxy_info->bypass_all = true; |
| 337 data_reduction_proxy_info->mark_proxies_as_bad = false; | 358 data_reduction_proxy_info->mark_proxies_as_bad = false; |
| 338 data_reduction_proxy_info->bypass_duration = TimeDelta(); | 359 data_reduction_proxy_info->bypass_duration = TimeDelta(); |
| 339 return BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX; | 360 return BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX; |
| 340 } | 361 } |
| 341 | 362 |
| 342 // Missing the via header should not trigger bypass if the client is | 363 // Missing the via header should not trigger bypass if the client is |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 392 std::string value; | 413 std::string value; |
| 393 size_t iter = 0; | 414 size_t iter = 0; |
| 394 while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) { | 415 while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) { |
| 395 if (StartsWithActionPrefix(value, kChromeProxyActionFingerprintChromeProxy)) | 416 if (StartsWithActionPrefix(value, kChromeProxyActionFingerprintChromeProxy)) |
| 396 continue; | 417 continue; |
| 397 values->push_back(std::move(value)); | 418 values->push_back(std::move(value)); |
| 398 } | 419 } |
| 399 } | 420 } |
| 400 | 421 |
| 401 } // namespace data_reduction_proxy | 422 } // namespace data_reduction_proxy |
| OLD | NEW |