diff options
author | bengr@chromium.org <bengr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-15 12:19:52 +0000 |
---|---|---|
committer | bengr@chromium.org <bengr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-15 12:19:52 +0000 |
commit | cefb81130fcc85a5de0b737be52380a22b437574 (patch) | |
tree | a6c1e7b0d16272939e459446dce5d3f6c70e01cb /components/data_reduction_proxy | |
parent | 344a02931fcd9f58e602bd0a1cdf2dde97737d77 (diff) | |
download | chromium_src-cefb81130fcc85a5de0b737be52380a22b437574.zip chromium_src-cefb81130fcc85a5de0b737be52380a22b437574.tar.gz chromium_src-cefb81130fcc85a5de0b737be52380a22b437574.tar.bz2 |
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
Diffstat (limited to 'components/data_reduction_proxy')
5 files changed, 560 insertions, 5 deletions
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 edc4820..7143f7c 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 c050e40..53ece22 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 0000000..773d14c --- /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 <string> + +#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=<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 + // (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 0000000..88b0cd0 --- /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 <string> + +#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 0000000..871d665 --- /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<net::HttpResponseHeaders> 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<net::HttpResponseHeaders> 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<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(headers)); + DataReductionProxyInfo chrome_proxy_info; + EXPECT_EQ(tests[i].expected_result, + GetDataReductionProxyBypassEventType(parsed, &chrome_proxy_info)); + } +} +} // namespace data_reduction_proxy |