From cefb81130fcc85a5de0b737be52380a22b437574 Mon Sep 17 00:00:00 2001 From: "bengr@chromium.org" Date: Sun, 15 Jun 2014 12:19:52 +0000 Subject: [PATCH] Moved data reduction proxy header processing out of net BUG=367221 Review URL: https://codereview.chromium.org/318753005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@277314 0039d316-1c4b-4281-b951-d872f2087c98 --- .../intercept_download_resource_throttle.cc | 5 +- chrome/chrome_renderer.gypi | 1 + chrome/renderer/DEPS | 1 + chrome/renderer/page_load_histograms.cc | 3 +- components/components_tests.gyp | 1 + components/data_reduction_proxy.gypi | 2 + .../browser/data_reduction_proxy_metrics.cc | 3 +- .../browser/data_reduction_proxy_protocol.cc | 8 +- .../common/data_reduction_proxy_headers.cc | 143 +++++++++ .../common/data_reduction_proxy_headers.h | 59 ++++ .../data_reduction_proxy_headers_unittest.cc | 352 +++++++++++++++++++++ net/http/http_response_headers.cc | 120 ------- net/http/http_response_headers.h | 37 --- net/http/http_response_headers_unittest.cc | 333 ------------------- 14 files changed, 570 insertions(+), 498 deletions(-) create mode 100644 components/data_reduction_proxy/common/data_reduction_proxy_headers.cc create mode 100644 components/data_reduction_proxy/common/data_reduction_proxy_headers.h create mode 100644 components/data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc diff --git a/chrome/browser/android/intercept_download_resource_throttle.cc b/chrome/browser/android/intercept_download_resource_throttle.cc index 689306f046fd..ba1f34d63a94 100644 --- a/chrome/browser/android/intercept_download_resource_throttle.cc +++ b/chrome/browser/android/intercept_download_resource_throttle.cc @@ -4,6 +4,7 @@ #include "chrome/browser/android/intercept_download_resource_throttle.h" +#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" #include "content/public/browser/android/download_controller_android.h" #include "content/public/browser/resource_controller.h" #include "net/http/http_request_headers.h" @@ -52,8 +53,8 @@ void InterceptDownloadResourceThrottle::ProcessDownloadRequest() { request_->GetFullRequestHeaders(&headers); if (headers.HasHeader(net::HttpRequestHeaders::kAuthorization) || !(request_->response_info().headers && - request_->response_info().headers-> - IsDataReductionProxyResponse())) { + data_reduction_proxy::HasDataReductionProxyViaHeader( + request_->response_info().headers))) { return; } #else diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index 2721f69dfe42..f8fb833e8aec 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -16,6 +16,7 @@ '../third_party/re2/re2.gyp:re2', '../components/components.gyp:autofill_content_renderer', '../components/components.gyp:cdm_renderer', + '../components/components.gyp:data_reduction_proxy_common', '../components/components.gyp:startup_metric_utils', '../components/components.gyp:plugins_renderer', '../components/components.gyp:translate_core_common', diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS index d2a8594aa026..d806f3048d74 100644 --- a/chrome/renderer/DEPS +++ b/chrome/renderer/DEPS @@ -4,6 +4,7 @@ include_rules = [ "+components/autofill/content/renderer", "+components/autofill/core/common", "+components/cdm/renderer", + "+components/data_reduction_proxy/common", "+components/nacl/renderer", "+components/plugins/renderer", "+components/signin/core/common", diff --git a/chrome/renderer/page_load_histograms.cc b/chrome/renderer/page_load_histograms.cc index bce3fc469874..76f6a2249e89 100644 --- a/chrome/renderer/page_load_histograms.cc +++ b/chrome/renderer/page_load_histograms.cc @@ -18,6 +18,7 @@ #include "base/time/time.h" #include "chrome/common/chrome_switches.h" #include "chrome/renderer/chrome_content_renderer_client.h" +#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" #include "content/public/common/content_constants.h" #include "content/public/renderer/document_state.h" #include "content/public/renderer/render_thread.h" @@ -183,7 +184,7 @@ bool DataReductionProxyWasUsed(WebFrame* frame) { std::replace(headers.begin(), headers.end(), '\n', '\0'); scoped_refptr response_headers( new net::HttpResponseHeaders(headers)); - return response_headers->IsDataReductionProxyResponse(); + return data_reduction_proxy::HasDataReductionProxyViaHeader(response_headers); } // Returns true if the provided URL is a referrer string that came from diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 03d7b3258554..1ec98df6aaff 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -79,6 +79,7 @@ 'data_reduction_proxy/browser/data_reduction_proxy_protocol_unittest.cc', 'data_reduction_proxy/browser/data_reduction_proxy_settings_unittest.cc', 'data_reduction_proxy/browser/http_auth_handler_data_reduction_proxy_unittest.cc', + 'data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc', 'dom_distiller/core/article_entry_unittest.cc', 'dom_distiller/core/distilled_content_store_unittest.cc', 'dom_distiller/core/distiller_unittest.cc', diff --git a/components/data_reduction_proxy.gypi b/components/data_reduction_proxy.gypi index 89eced8854d9..10bd62e453b5 100644 --- a/components/data_reduction_proxy.gypi +++ b/components/data_reduction_proxy.gypi @@ -48,6 +48,8 @@ '..', ], 'sources': [ + 'data_reduction_proxy/common/data_reduction_proxy_headers.cc', + 'data_reduction_proxy/common/data_reduction_proxy_headers.h', 'data_reduction_proxy/common/data_reduction_proxy_pref_names.cc', 'data_reduction_proxy/common/data_reduction_proxy_pref_names.h', 'data_reduction_proxy/common/data_reduction_proxy_switches.cc', diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_metrics.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_metrics.cc index edc48203e3be..7143f7c209f5 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_metrics.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_metrics.cc @@ -10,6 +10,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_settings.h" +#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" #include "components/data_reduction_proxy/common/data_reduction_proxy_pref_names.h" #include "net/base/host_port_pair.h" #include "net/http/http_response_headers.h" @@ -355,7 +356,7 @@ DataReductionProxyRequestType GetDataReductionProxyRequestType( LONG_BYPASS : SHORT_BYPASS; } if (request->response_info().headers && - request->response_info().headers->IsDataReductionProxyResponse()) { + HasDataReductionProxyViaHeader(request->response_info().headers)) { return VIA_DATA_REDUCTION_PROXY; } return UNKNOWN_TYPE; diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.cc index c050e4048ce3..53ece226733e 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.cc @@ -7,6 +7,7 @@ #include "base/memory/ref_counted.h" #include "base/time/time.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h" +#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" #include "net/base/load_flags.h" #include "net/http/http_response_headers.h" #include "net/proxy/proxy_info.h" @@ -54,11 +55,10 @@ bool MaybeBypassProxyAndPrepareToRetry( if (data_reduction_proxies.first.is_empty()) return false; - net::HttpResponseHeaders::DataReductionProxyInfo data_reduction_proxy_info; + DataReductionProxyInfo data_reduction_proxy_info; net::ProxyService::DataReductionProxyBypassEventType bypass_type = - original_response_headers->GetDataReductionProxyBypassEventType( - &data_reduction_proxy_info); - + GetDataReductionProxyBypassEventType( + original_response_headers, &data_reduction_proxy_info); if (bypass_type == net::ProxyService::BYPASS_EVENT_TYPE_MAX) { return false; } diff --git a/components/data_reduction_proxy/common/data_reduction_proxy_headers.cc b/components/data_reduction_proxy/common/data_reduction_proxy_headers.cc new file mode 100644 index 000000000000..773d14c7042f --- /dev/null +++ b/components/data_reduction_proxy/common/data_reduction_proxy_headers.cc @@ -0,0 +1,143 @@ +// Copyright 2014 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 "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" + +#include + +#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 data_reduction_proxy { + +bool GetDataReductionProxyBypassDuration( + const net::HttpResponseHeaders* headers, + const std::string& action_prefix, + base::TimeDelta* duration) { + void* iter = NULL; + std::string value; + std::string name = "chrome-proxy"; + + while (headers->EnumerateHeader(&iter, name, &value)) { + if (value.size() > action_prefix.size()) { + if (LowerCaseEqualsASCII(value.begin(), + value.begin() + action_prefix.size(), + action_prefix.c_str())) { + int64 seconds; + if (!base::StringToInt64( + StringPiece(value.begin() + action_prefix.size(), value.end()), + &seconds) || seconds < 0) { + continue; // In case there is a well formed instruction. + } + *duration = TimeDelta::FromSeconds(seconds); + return true; + } + } + } + return false; +} + +bool GetDataReductionProxyInfo(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=, where + // is the number of seconds to wait before retrying + // the proxy. If the duration is 0, then the default proxy retry delay + // (specified in |ProxyList::UpdateRetryInfoOnFallback|) will be used. + // 'bypass' instructs Chrome to bypass the currently connected data reduction + // proxy, whereas 'block' instructs Chrome to bypass all available data + // reduction proxies. + + // 'block' takes precedence over 'bypass', 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)) { + proxy_info->bypass_all = true; + return true; + } + + // Next, look for 'bypass'. + if (GetDataReductionProxyBypassDuration( + headers, "bypass=", &proxy_info->bypass_duration)) { + return true; + } + return false; +} + +bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers) { + const size_t kVersionSize = 4; + const char kDataReductionProxyViaValue[] = "Chrome-Compression-Proxy"; + size_t value_len = strlen(kDataReductionProxyViaValue); + void* iter = NULL; + std::string value; + + // Case-sensitive comparison of |value|. Assumes the received protocol and the + // space following it are always |kVersionSize| characters. E.g., + // 'Via: 1.1 Chrome-Compression-Proxy' + while (headers->EnumerateHeader(&iter, "via", &value)) { + if (value.size() >= kVersionSize + value_len && + !value.compare(kVersionSize, value_len, kDataReductionProxyViaValue)) + return true; + } + + // TODO(bengr): Remove deprecated header value. + const char kDeprecatedDataReductionProxyViaValue[] = + "1.1 Chrome Compression Proxy"; + iter = NULL; + while (headers->EnumerateHeader(&iter, "via", &value)) + if (value == kDeprecatedDataReductionProxyViaValue) + return true; + + return false; +} + +net::ProxyService::DataReductionProxyBypassEventType +GetDataReductionProxyBypassEventType( + const net::HttpResponseHeaders* headers, + DataReductionProxyInfo* data_reduction_proxy_info) { + DCHECK(data_reduction_proxy_info); + if (GetDataReductionProxyInfo(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 (!HasDataReductionProxyViaHeader(headers) && + (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 + // should not generate representation metadata other than Cache-Control, + // Content-Location, Date, ETag, Expires, and Vary. + + // The proxy Via header might also not be present in a 4xx response. + // 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 ProxyService::MISSING_VIA_HEADER; + } + // There is no bypass event. + return ProxyService::BYPASS_EVENT_TYPE_MAX; +} + +} // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/common/data_reduction_proxy_headers.h b/components/data_reduction_proxy/common/data_reduction_proxy_headers.h new file mode 100644 index 000000000000..88b0cd054b84 --- /dev/null +++ b/components/data_reduction_proxy/common/data_reduction_proxy_headers.h @@ -0,0 +1,59 @@ +// Copyright 2014 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. + +#ifndef COMPONENTS_DATA_REDUCTION_PROXY_COMMON_DATA_REDUCTION_PROXY_HEADERS_H_ +#define COMPONENTS_DATA_REDUCTION_PROXY_COMMON_DATA_REDUCTION_PROXY_HEADERS_H_ + +#include + +#include "base/macros.h" +#include "base/time/time.h" +#include "net/http/http_response_headers.h" +#include "net/proxy/proxy_service.h" + +namespace data_reduction_proxy { + +// Contains instructions contained in the Chrome-Proxy header. +struct DataReductionProxyInfo { + DataReductionProxyInfo() : bypass_all(false) {} + + // True if Chrome should bypass all available data reduction proxies. False + // if only the currently connected data reduction proxy should be bypassed. + bool bypass_all; + + // Amount of time to bypass the data reduction proxy or proxies. + base::TimeDelta bypass_duration; +}; + +// Returns true if the Chrome-Proxy header is present and contains a bypass +// delay. Sets |proxy_info->bypass_duration| to the specified delay if greater +// than 0, and to 0 otherwise to indicate that the default proxy delay +// (as specified in |ProxyList::UpdateRetryInfoOnFallback|) should be used. +// If all available data reduction proxies should by bypassed, |bypass_all| is +// set to true. |proxy_info| must be non-NULL. +bool GetDataReductionProxyInfo( + const net::HttpResponseHeaders* headers, + DataReductionProxyInfo* proxy_info); + +// Returns true if the response contain the data reduction proxy Via header +// value. Used to check the integrity of data reduction proxy responses. +bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers); + +// Returns the reason why the Chrome proxy should be bypassed or not, and +// populates |proxy_info| with information on how long to bypass if +// applicable. +net::ProxyService::DataReductionProxyBypassEventType +GetDataReductionProxyBypassEventType( + const net::HttpResponseHeaders* headers, + DataReductionProxyInfo* proxy_info); + +// Searches for the specified Chrome-Proxy action, and if present interprets +// its value as a duration in seconds. +bool GetDataReductionProxyBypassDuration( + const net::HttpResponseHeaders* headers, + const std::string& action_prefix, + base::TimeDelta* duration); + +} // namespace data_reduction_proxy +#endif // COMPONENTS_DATA_REDUCTION_PROXY_COMMON_DATA_REDUCTION_PROXY_HEADERS_H_ diff --git a/components/data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc b/components/data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc new file mode 100644 index 000000000000..871d66589e0d --- /dev/null +++ b/components/data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc @@ -0,0 +1,352 @@ +// Copyright 2014 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 "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" + +#include "net/http/http_response_headers.h" +#include "net/proxy/proxy_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// Transform "normal"-looking headers (\n-separated) to the appropriate +// input format for ParseRawHeaders (\0-separated). +void HeadersToRaw(std::string* headers) { + std::replace(headers->begin(), headers->end(), '\n', '\0'); + if (!headers->empty()) + *headers += '\0'; +} + +} // namespace + +namespace data_reduction_proxy { + +class DataReductionProxyHeadersTest : public testing::Test {}; + +TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { + const struct { + const char* headers; + bool expected_result; + int64 expected_retry_delay; + bool expected_bypass_all; + } tests[] = { + { "HTTP/1.1 200 OK\n" + "Content-Length: 999\n", + false, + 0, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Content-Length: 999\n", + false, + 0, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=86400\n" + "Content-Length: 999\n", + true, + 86400, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=0\n" + "Content-Length: 999\n", + true, + 0, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=-1\n" + "Content-Length: 999\n", + false, + 0, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=xyz\n" + "Content-Length: 999\n", + false, + 0, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass\n" + "Content-Length: 999\n", + false, + 0, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: foo=abc, bypass=86400\n" + "Content-Length: 999\n", + true, + 86400, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=86400, bar=abc\n" + "Content-Length: 999\n", + true, + 86400, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=3600\n" + "Chrome-Proxy: bypass=86400\n" + "Content-Length: 999\n", + true, + 3600, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=3600, bypass=86400\n" + "Content-Length: 999\n", + true, + 3600, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=, bypass=86400\n" + "Content-Length: 999\n", + true, + 86400, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass\n" + "Chrome-Proxy: bypass=86400\n" + "Content-Length: 999\n", + true, + 86400, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: block=, block=3600\n" + "Content-Length: 999\n", + true, + 3600, + true, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=86400, block=3600\n" + "Content-Length: 999\n", + true, + 3600, + true, + }, + { "HTTP/1.1 200 OK\n" + "connection: proxy-bypass\n" + "Chrome-Proxy: block=, bypass=86400\n" + "Content-Length: 999\n", + true, + 86400, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: proxy-bypass\n" + "Chrome-Proxy: block=-1\n" + "Content-Length: 999\n", + false, + 0, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: proxy-bypass\n" + "Chrome-Proxy: block=99999999999999999999\n" + "Content-Length: 999\n", + false, + 0, + false, + }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + std::string headers(tests[i].headers); + HeadersToRaw(&headers); + scoped_refptr parsed( + new net::HttpResponseHeaders(headers)); + + DataReductionProxyInfo data_reduction_proxy_info; + EXPECT_EQ(tests[i].expected_result, + GetDataReductionProxyInfo(parsed, &data_reduction_proxy_info)); + EXPECT_EQ(tests[i].expected_retry_delay, + data_reduction_proxy_info.bypass_duration.InSeconds()); + EXPECT_EQ(tests[i].expected_bypass_all, + data_reduction_proxy_info.bypass_all); + } +} + +TEST_F(DataReductionProxyHeadersTest, HasDataReductionProxyViaHeader) { + const struct { + const char* headers; + bool expected_result; + } tests[] = { + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome-Proxy\n", + false, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1\n", + false, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + true, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.0 Chrome-Compression-Proxy\n", + true, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Foo-Bar, 1.1 Chrome-Compression-Proxy\n", + true, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome-Compression-Proxy, 1.1 Bar-Foo\n", + true, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 chrome-compression-proxy\n", + false, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Foo-Bar\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + true, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome-Proxy\n", + false, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome Compression Proxy\n", + true, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Foo-Bar, 1.1 Chrome Compression Proxy\n", + true, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome Compression Proxy, 1.1 Bar-Foo\n", + true, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 chrome compression proxy\n", + false, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Foo-Bar\n" + "Via: 1.1 Chrome Compression Proxy\n", + true, + }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + std::string headers(tests[i].headers); + HeadersToRaw(&headers); + scoped_refptr parsed( + new net::HttpResponseHeaders(headers)); + + EXPECT_EQ(tests[i].expected_result, + HasDataReductionProxyViaHeader(parsed)); + } +} + +TEST_F(DataReductionProxyHeadersTest, GetDataReductionProxyBypassEventType) { + const struct { + const char* headers; + net::ProxyService::DataReductionProxyBypassEventType expected_result; + } tests[] = { + { "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=0\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + net::ProxyService::SHORT_BYPASS, + }, + { "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=1799\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + net::ProxyService::SHORT_BYPASS, + }, + { "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=1800\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + net::ProxyService::LONG_BYPASS, + }, + { "HTTP/1.1 500 Internal Server Error\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + net::ProxyService::INTERNAL_SERVER_ERROR_BYPASS, + }, + { "HTTP/1.1 501 Not Implemented\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + net::ProxyService::BYPASS_EVENT_TYPE_MAX, + }, + { "HTTP/1.1 502 Bad Gateway\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + net::ProxyService::INTERNAL_SERVER_ERROR_BYPASS, + }, + { "HTTP/1.1 503 Service Unavailable\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + net::ProxyService::INTERNAL_SERVER_ERROR_BYPASS, + }, + { "HTTP/1.1 504 Gateway Timeout\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + net::ProxyService::BYPASS_EVENT_TYPE_MAX, + }, + { "HTTP/1.1 505 HTTP Version Not Supported\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + net::ProxyService::BYPASS_EVENT_TYPE_MAX, + }, + { "HTTP/1.1 304 Not Modified\n", + net::ProxyService::BYPASS_EVENT_TYPE_MAX, + }, + { "HTTP/1.1 200 OK\n", + net::ProxyService::MISSING_VIA_HEADER, + }, + { "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=1799\n", + net::ProxyService::SHORT_BYPASS, + }, + { "HTTP/1.1 502 Bad Gateway\n", + net::ProxyService::INTERNAL_SERVER_ERROR_BYPASS, + }, + { "HTTP/1.1 502 Bad Gateway\n" + "Chrome-Proxy: bypass=1799\n", + net::ProxyService::SHORT_BYPASS, + }, + { "HTTP/1.1 502 Bad Gateway\n" + "Chrome-Proxy: bypass=1799\n", + net::ProxyService::SHORT_BYPASS, + }, + { "HTTP/1.1 414 Request-URI Too Long\n", + net::ProxyService::PROXY_4XX_BYPASS, + }, + { "HTTP/1.1 414 Request-URI Too Long\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + net::ProxyService::BYPASS_EVENT_TYPE_MAX, + } + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + std::string headers(tests[i].headers); + HeadersToRaw(&headers); + scoped_refptr parsed( + new net::HttpResponseHeaders(headers)); + DataReductionProxyInfo chrome_proxy_info; + EXPECT_EQ(tests[i].expected_result, + GetDataReductionProxyBypassEventType(parsed, &chrome_proxy_info)); + } +} +} // namespace data_reduction_proxy diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc index 0ed473e13fd9..b7ef98a59806 100644 --- a/net/http/http_response_headers.cc +++ b/net/http/http_response_headers.cc @@ -24,9 +24,7 @@ #include "net/base/escape.h" #include "net/http/http_byte_range.h" #include "net/http/http_log_util.h" -#include "net/http/http_status_code.h" #include "net/http/http_util.h" -#include "net/proxy/proxy_service.h" using base::StringPiece; using base::Time; @@ -1393,122 +1391,4 @@ bool HttpResponseHeaders::IsChunkEncoded() const { HasHeaderValue("Transfer-Encoding", "chunked"); } -bool HttpResponseHeaders::GetDataReductionProxyBypassDuration( - const std::string& action_prefix, - base::TimeDelta* duration) const { - void* iter = NULL; - std::string value; - std::string name = "chrome-proxy"; - - while (EnumerateHeader(&iter, name, &value)) { - if (value.size() > action_prefix.size()) { - if (LowerCaseEqualsASCII(value.begin(), - value.begin() + action_prefix.size(), - action_prefix.c_str())) { - int64 seconds; - if (!base::StringToInt64( - StringPiece(value.begin() + action_prefix.size(), value.end()), - &seconds) || seconds < 0) { - continue; // In case there is a well formed instruction. - } - *duration = TimeDelta::FromSeconds(seconds); - return true; - } - } - } - return false; -} - -bool HttpResponseHeaders::GetDataReductionProxyInfo( - DataReductionProxyInfo* proxy_info) const { - DCHECK(proxy_info); - proxy_info->bypass_all = false; - proxy_info->bypass_duration = base::TimeDelta(); - // Support header of the form Chrome-Proxy: bypass|block=, where - // is the number of seconds to wait before retrying - // the proxy. If the duration is 0, then the default proxy retry delay - // (specified in |ProxyList::UpdateRetryInfoOnFallback|) will be used. - // 'bypass' instructs Chrome to bypass the currently connected data reduction - // proxy, whereas 'block' instructs Chrome to bypass all available data - // reduction proxies. - - // 'block' takes precedence over 'bypass', so look for it first. - // TODO(bengr): Reduce checks for 'block' and 'bypass' to a single loop. - if (GetDataReductionProxyBypassDuration( - "block=", &proxy_info->bypass_duration)) { - proxy_info->bypass_all = true; - return true; - } - - // Next, look for 'bypass'. - if (GetDataReductionProxyBypassDuration( - "bypass=", &proxy_info->bypass_duration)) { - return true; - } - return false; -} - -bool HttpResponseHeaders::IsDataReductionProxyResponse() const { - const size_t kVersionSize = 4; - const char kDataReductionProxyViaValue[] = "Chrome-Compression-Proxy"; - size_t value_len = strlen(kDataReductionProxyViaValue); - void* iter = NULL; - std::string value; - - // Case-sensitive comparison of |value|. Assumes the received protocol and the - // space following it are always |kVersionSize| characters. E.g., - // 'Via: 1.1 Chrome-Compression-Proxy' - while (EnumerateHeader(&iter, "via", &value)) { - if (value.size() >= kVersionSize + value_len && - !value.compare(kVersionSize, value_len, kDataReductionProxyViaValue)) - return true; - } - - // TODO(bengr): Remove deprecated header value. - const char kDeprecatedDataReductionProxyViaValue[] = - "1.1 Chrome Compression Proxy"; - iter = NULL; - while (EnumerateHeader(&iter, "via", &value)) - if (value == kDeprecatedDataReductionProxyViaValue) - return true; - - return false; -} - -ProxyService::DataReductionProxyBypassEventType -HttpResponseHeaders::GetDataReductionProxyBypassEventType( - DataReductionProxyInfo* data_reduction_proxy_info) const { - DCHECK(data_reduction_proxy_info); - if (GetDataReductionProxyInfo(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 (response_code() == HTTP_INTERNAL_SERVER_ERROR || - response_code() == HTTP_BAD_GATEWAY || - response_code() == HTTP_SERVICE_UNAVAILABLE) { - // Fall back if a 500, 502 or 503 is returned. - return ProxyService::INTERNAL_SERVER_ERROR_BYPASS; - } - if (!IsDataReductionProxyResponse() && - (response_code() != 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 - // should not generate representation metadata other than Cache-Control, - // Content-Location, Date, ETag, Expires, and Vary. - - // The proxy Via header might also not be present in a 4xx response. - // Separate this case from other responses that are missing the header. - if (response_code() >= HTTP_BAD_REQUEST && - response_code() < HTTP_INTERNAL_SERVER_ERROR) { - return ProxyService::PROXY_4XX_BYPASS; - } - return ProxyService::MISSING_VIA_HEADER; - } - // There is no bypass event. - return ProxyService::BYPASS_EVENT_TYPE_MAX; -} - } // namespace net diff --git a/net/http/http_response_headers.h b/net/http/http_response_headers.h index 4580ef7b820e..2d306c95ca04 100644 --- a/net/http/http_response_headers.h +++ b/net/http/http_response_headers.h @@ -15,7 +15,6 @@ #include "net/base/net_export.h" #include "net/base/net_log.h" #include "net/http/http_version.h" -#include "net/proxy/proxy_service.h" class Pickle; class PickleIterator; @@ -264,37 +263,6 @@ class NET_EXPORT HttpResponseHeaders // Returns true if the response is chunk-encoded. bool IsChunkEncoded() const; - // Contains instructions contained in the Chrome-Proxy header. - struct DataReductionProxyInfo { - DataReductionProxyInfo() : bypass_all(false) {} - - // True if Chrome should bypass all available data reduction proxies. False - // if only the currently connected data reduction proxy should be bypassed. - bool bypass_all; - - // Amount of time to bypass the data reduction proxy or proxies. - base::TimeDelta bypass_duration; - }; - - // Returns true if the Chrome-Proxy header is present and contains a bypass - // delay. Sets |proxy_info->bypass_duration| to the specified delay if greater - // than 0, and to 0 otherwise to indicate that the default proxy delay - // (as specified in |ProxyList::UpdateRetryInfoOnFallback|) should be used. - // If all available data reduction proxies should by bypassed, |bypass_all| is - // set to true. |proxy_info| must be non-NULL. - bool GetDataReductionProxyInfo(DataReductionProxyInfo* proxy_info) const; - - // Returns the reason why the Chrome proxy should be bypassed or not, and - // populates |proxy_info| with information on how long to bypass if - // applicable. - ProxyService::DataReductionProxyBypassEventType - GetDataReductionProxyBypassEventType( - DataReductionProxyInfo* proxy_info) const; - - // Returns true if response headers contain the data reduction proxy Via - // header value. - bool IsDataReductionProxyResponse() const; - // Creates a Value for use with the NetLog containing the response headers. base::Value* NetLogCallback(NetLog::LogLevel log_level) const; @@ -393,11 +361,6 @@ class NET_EXPORT HttpResponseHeaders // Adds the set of transport security state headers. static void AddSecurityStateHeaders(HeaderSet* header_names); - // Searches for the specified Chrome-Proxy action, and if present interprets - // its value as a duration in seconds. - bool GetDataReductionProxyBypassDuration(const std::string& action_prefix, - base::TimeDelta* duration) const; - // We keep a list of ParsedHeader objects. These tell us where to locate the // header-value pairs within raw_headers_. HeaderList parsed_; diff --git a/net/http/http_response_headers_unittest.cc b/net/http/http_response_headers_unittest.cc index b097a184a1b8..cc236d183e8b 100644 --- a/net/http/http_response_headers_unittest.cc +++ b/net/http/http_response_headers_unittest.cc @@ -13,10 +13,6 @@ #include "net/http/http_response_headers.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(SPDY_PROXY_AUTH_ORIGIN) -#include "net/proxy/proxy_service.h" -#endif - namespace { struct TestData { @@ -1939,332 +1935,3 @@ TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) { parsed->GetNormalizedHeaders(&normalized_recreated); EXPECT_EQ(normalized_parsed, normalized_recreated); } - -#if defined(SPDY_PROXY_AUTH_ORIGIN) -TEST(HttpResponseHeadersTest, GetProxyBypassInfo) { - const struct { - const char* headers; - bool expected_result; - int64 expected_retry_delay; - bool expected_bypass_all; - } tests[] = { - { "HTTP/1.1 200 OK\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=86400\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=0\n" - "Content-Length: 999\n", - true, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=-1\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=xyz\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: foo=abc, bypass=86400\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=86400, bar=abc\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=3600\n" - "Chrome-Proxy: bypass=86400\n" - "Content-Length: 999\n", - true, - 3600, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=3600, bypass=86400\n" - "Content-Length: 999\n", - true, - 3600, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=, bypass=86400\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass\n" - "Chrome-Proxy: bypass=86400\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: block=, block=3600\n" - "Content-Length: 999\n", - true, - 3600, - true, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=86400, block=3600\n" - "Content-Length: 999\n", - true, - 3600, - true, - }, - { "HTTP/1.1 200 OK\n" - "connection: proxy-bypass\n" - "Chrome-Proxy: block=, bypass=86400\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: proxy-bypass\n" - "Chrome-Proxy: block=-1\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: proxy-bypass\n" - "Chrome-Proxy: block=99999999999999999999\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers(tests[i].headers); - HeadersToRaw(&headers); - scoped_refptr parsed( - new net::HttpResponseHeaders(headers)); - - net::HttpResponseHeaders::DataReductionProxyInfo data_reduction_proxy_info; - EXPECT_EQ(tests[i].expected_result, - parsed->GetDataReductionProxyInfo(&data_reduction_proxy_info)); - EXPECT_EQ(tests[i].expected_retry_delay, - data_reduction_proxy_info.bypass_duration.InSeconds()); - EXPECT_EQ(tests[i].expected_bypass_all, - data_reduction_proxy_info.bypass_all); - } -} -#endif // defined(SPDY_PROXY_AUTH_ORIGIN) - -TEST(HttpResponseHeadersTest, IsDataReductionProxyResponse) { - const struct { - const char* headers; - bool expected_result; - } tests[] = { - { "HTTP/1.1 200 OK\n" - "Via: 1.1 Chrome-Proxy\n", - false, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1\n", - false, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - true, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.0 Chrome-Compression-Proxy\n", - true, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.1 Foo-Bar, 1.1 Chrome-Compression-Proxy\n", - true, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.1 Chrome-Compression-Proxy, 1.1 Bar-Foo\n", - true, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.1 chrome-compression-proxy\n", - false, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.1 Foo-Bar\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - true, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.1 Chrome-Proxy\n", - false, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.1 Chrome Compression Proxy\n", - true, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.1 Foo-Bar, 1.1 Chrome Compression Proxy\n", - true, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.1 Chrome Compression Proxy, 1.1 Bar-Foo\n", - true, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.1 chrome compression proxy\n", - false, - }, - { "HTTP/1.1 200 OK\n" - "Via: 1.1 Foo-Bar\n" - "Via: 1.1 Chrome Compression Proxy\n", - true, - }, - }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers(tests[i].headers); - HeadersToRaw(&headers); - scoped_refptr parsed( - new net::HttpResponseHeaders(headers)); - - EXPECT_EQ(tests[i].expected_result, parsed->IsDataReductionProxyResponse()); - } -} - -#if defined(SPDY_PROXY_AUTH_ORIGIN) -TEST(HttpResponseHeadersTest, GetDataReductionProxyBypassEventType) { - const struct { - const char* headers; - net::ProxyService::DataReductionProxyBypassEventType expected_result; - } tests[] = { - { "HTTP/1.1 200 OK\n" - "Chrome-Proxy: bypass=0\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::SHORT_BYPASS, - }, - { "HTTP/1.1 200 OK\n" - "Chrome-Proxy: bypass=1799\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::SHORT_BYPASS, - }, - { "HTTP/1.1 200 OK\n" - "Chrome-Proxy: bypass=1800\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::LONG_BYPASS, - }, - { "HTTP/1.1 500 Internal Server Error\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::INTERNAL_SERVER_ERROR_BYPASS, - }, - { "HTTP/1.1 501 Not Implemented\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::BYPASS_EVENT_TYPE_MAX, - }, - { "HTTP/1.1 502 Bad Gateway\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::INTERNAL_SERVER_ERROR_BYPASS, - }, - { "HTTP/1.1 503 Service Unavailable\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::INTERNAL_SERVER_ERROR_BYPASS, - }, - { "HTTP/1.1 504 Gateway Timeout\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::BYPASS_EVENT_TYPE_MAX, - }, - { "HTTP/1.1 505 HTTP Version Not Supported\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::BYPASS_EVENT_TYPE_MAX, - }, - { "HTTP/1.1 304 Not Modified\n", - net::ProxyService::BYPASS_EVENT_TYPE_MAX, - }, - { "HTTP/1.1 200 OK\n", - net::ProxyService::MISSING_VIA_HEADER, - }, - { "HTTP/1.1 200 OK\n" - "Chrome-Proxy: bypass=1799\n", - net::ProxyService::SHORT_BYPASS, - }, - { "HTTP/1.1 502 Bad Gateway\n", - net::ProxyService::INTERNAL_SERVER_ERROR_BYPASS, - }, - { "HTTP/1.1 502 Bad Gateway\n" - "Chrome-Proxy: bypass=1799\n", - net::ProxyService::SHORT_BYPASS, - }, - { "HTTP/1.1 502 Bad Gateway\n" - "Chrome-Proxy: bypass=1799\n", - net::ProxyService::SHORT_BYPASS, - }, - { "HTTP/1.1 414 Request-URI Too Long\n", - net::ProxyService::PROXY_4XX_BYPASS, - }, - { "HTTP/1.1 414 Request-URI Too Long\n" - "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::BYPASS_EVENT_TYPE_MAX, - } - }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers(tests[i].headers); - HeadersToRaw(&headers); - scoped_refptr parsed( - new net::HttpResponseHeaders(headers)); - net::HttpResponseHeaders::DataReductionProxyInfo chrome_proxy_info; - EXPECT_EQ(tests[i].expected_result, - parsed->GetDataReductionProxyBypassEventType(&chrome_proxy_info)); - } -} -#endif // defined(SPDY_PROXY_AUTH_ORIGIN) -- 2.11.4.GIT