diff options
author | bengr@chromium.org <bengr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-13 20:42:27 +0000 |
---|---|---|
committer | bengr@chromium.org <bengr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-13 20:42:27 +0000 |
commit | a4fd43af496cfbc4185ee01818122ca7ae6926a9 (patch) | |
tree | db589682a4de95d85b7954a0a86571034c6df435 | |
parent | 716c016d95025a8f4d42baab6639b9dc90498f2d (diff) | |
download | chromium_src-a4fd43af496cfbc4185ee01818122ca7ae6926a9.zip chromium_src-a4fd43af496cfbc4185ee01818122ca7ae6926a9.tar.gz chromium_src-a4fd43af496cfbc4185ee01818122ca7ae6926a9.tar.bz2 |
Accept Chrome-Proxy: block=xxx to bypass all Chrome proxies
BUG=325333
Review URL: https://codereview.chromium.org/98373005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@240737 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/http/http_network_layer_unittest.cc | 61 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 39 | ||||
-rw-r--r-- | net/http/http_response_headers.cc | 61 | ||||
-rw-r--r-- | net/http/http_response_headers.h | 30 | ||||
-rw-r--r-- | net/http/http_response_headers_unittest.cc | 82 | ||||
-rw-r--r-- | net/proxy/proxy_list.cc | 58 | ||||
-rw-r--r-- | net/proxy/proxy_list.h | 22 | ||||
-rw-r--r-- | net/proxy/proxy_server.cc | 19 | ||||
-rw-r--r-- | net/proxy/proxy_service.cc | 9 | ||||
-rw-r--r-- | net/proxy/proxy_service.h | 16 |
10 files changed, 280 insertions, 117 deletions
diff --git a/net/http/http_network_layer_unittest.cc b/net/http/http_network_layer_unittest.cc index fc5b43c..174ae2e 100644 --- a/net/http/http_network_layer_unittest.cc +++ b/net/http/http_network_layer_unittest.cc @@ -48,12 +48,18 @@ class HttpNetworkLayerTest : public PlatformTest { factory_.reset(new HttpNetworkLayer(network_session_.get())); } -#if defined (SPDY_PROXY_AUTH_ORIGIN) +#if defined(SPDY_PROXY_AUTH_ORIGIN) std::string GetChromeProxy() { return HostPortPair::FromURL(GURL(SPDY_PROXY_AUTH_ORIGIN)).ToString(); } #endif +#if defined(SPDY_PROXY_AUTH_ORIGIN) && defined(DATA_REDUCTION_FALLBACK_HOST) + std::string GetChromeFallbackProxy() { + return HostPortPair::FromURL(GURL(DATA_REDUCTION_FALLBACK_HOST)).ToString(); + } +#endif + void ExecuteRequestExpectingContentAndHeader(const std::string& content, const std::string& header, const std::string& value) { @@ -103,17 +109,19 @@ class HttpNetworkLayerTest : public PlatformTest { void TestProxyFallback(const std::string& bad_proxy) { MockRead data_reads[] = { MockRead("HTTP/1.1 200 OK\r\n" - "Connection: proxy-bypass\r\n\r\n"), + "Chrome-Proxy: bypass=0\r\n\r\n"), MockRead("Bypass message"), MockRead(SYNCHRONOUS, OK), }; - TestProxyFallbackWithMockReads(bad_proxy, data_reads, - arraysize(data_reads)); + TestProxyFallbackWithMockReads(bad_proxy, "", data_reads, + arraysize(data_reads), 1u); } void TestProxyFallbackWithMockReads(const std::string& bad_proxy, + const std::string& bad_proxy2, MockRead data_reads[], - int data_reads_size) { + int data_reads_size, + unsigned int expected_retry_info_size) { MockWrite data_writes[] = { MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -145,7 +153,7 @@ class HttpNetworkLayerTest : public PlatformTest { ExecuteRequestExpectingContentAndHeader("content", "server", "not-proxy"); // We should also observe the bad proxy in the retry list. - TestBadProxies(1u, bad_proxy, ""); + TestBadProxies(expected_retry_info_size, bad_proxy, bad_proxy2); } // Simulates a request through a proxy which returns a bypass, which is then @@ -155,7 +163,7 @@ class HttpNetworkLayerTest : public PlatformTest { void TestProxyFallbackToDirect(const std::string& bad_proxy) { MockRead data_reads[] = { MockRead("HTTP/1.1 200 OK\r\n" - "Connection: proxy-bypass\r\n\r\n"), + "Chrome-Proxy: bypass=0\r\n\r\n"), MockRead("Bypass message"), MockRead(SYNCHRONOUS, OK), }; @@ -202,7 +210,7 @@ class HttpNetworkLayerTest : public PlatformTest { const std::string& bad_proxy2) { MockRead data_reads[] = { MockRead("HTTP/1.1 200 OK\r\n" - "Connection: proxy-bypass\r\n\r\n"), + "Chrome-Proxy: bypass=0\r\n\r\n"), MockRead("Bypass message"), MockRead(SYNCHRONOUS, OK), }; @@ -305,7 +313,7 @@ TEST_F(HttpNetworkLayerTest, GET) { } // Proxy bypass tests. These tests run through various server-induced -// proxy-bypass scenarios using both PAC file and fixed proxy params. +// proxy bypass scenarios using both PAC file and fixed proxy params. // The test scenarios are: // - bypass with two proxies configured and the first but not the second // is bypassed. @@ -468,13 +476,13 @@ TEST_F(HttpNetworkLayerTest, ServerFallbackOn5xxError) { #endif // defined(SPDY_PROXY_AUTH_ORIGIN) TEST_F(HttpNetworkLayerTest, ProxyBypassIgnoredOnDirectConnectionPac) { - // Verify that a Connection: proxy-bypass header is ignored when returned - // from a directly connected origin server. + // Verify that a Chrome-Proxy header is ignored when returned from a directly + // connected origin server. ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult("DIRECT")); MockRead data_reads[] = { MockRead("HTTP/1.1 200 OK\r\n" - "Connection: proxy-bypass\r\n\r\n"), + "Chrome-Proxy: bypass=0\r\n\r\n"), MockRead("Bypass message"), MockRead(SYNCHRONOUS, OK), }; @@ -528,10 +536,37 @@ TEST_F(HttpNetworkLayerTest, ServerFallbackWithProxyTimedBypass) { MockRead(SYNCHRONOUS, OK), }; - TestProxyFallbackWithMockReads(bad_proxy, data_reads, arraysize(data_reads)); + TestProxyFallbackWithMockReads(bad_proxy, "", data_reads, + arraysize(data_reads), 1u); + EXPECT_EQ(base::TimeDelta::FromSeconds(86400), + (*proxy_service_->proxy_retry_info().begin()).second.current_delay); +} + +#if defined(DATA_REDUCTION_FALLBACK_HOST) +TEST_F(HttpNetworkLayerTest, ServerFallbackWithProxyTimedBypassAll) { + // Verify that a Chrome-Proxy: block=<seconds> header bypasses a + // a configured Chrome-Proxy and fallback and induces proxy fallback to a + // third proxy, if configured. + std::string bad_proxy = GetChromeProxy(); + std::string fallback_proxy = GetChromeFallbackProxy(); + ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( + "PROXY " + bad_proxy + "; PROXY " + fallback_proxy + + "; PROXY good:8080")); + + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n" + "Connection: keep-alive\r\n" + "Chrome-Proxy: block=86400\r\n\r\n"), + MockRead("Bypass message"), + MockRead(SYNCHRONOUS, OK), + }; + + TestProxyFallbackWithMockReads(bad_proxy, fallback_proxy, data_reads, + arraysize(data_reads), 2u); EXPECT_EQ(base::TimeDelta::FromSeconds(86400), (*proxy_service_->proxy_retry_info().begin()).second.current_delay); } +#endif // defined(DATA_REDUCTION_FALLBACK_HOST) #endif // defined(SPDY_PROXY_AUTH_ORIGIN) TEST_F(HttpNetworkLayerTest, NetworkVerified) { diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 4b55a5f..66d7364 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -61,7 +61,13 @@ #include "net/ssl/ssl_connection_status_flags.h" #include "url/gurl.h" +#if defined(SPDY_PROXY_AUTH_ORIGIN) +#include "net/proxy/proxy_server.h" +#endif + + using base::Time; +using base::TimeDelta; namespace net { @@ -951,7 +957,7 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { if (response_.was_fetched_via_proxy && response_.headers.get() != NULL) { ProxyService::DataReductionProxyBypassEventType proxy_bypass_event = ProxyService::BYPASS_EVENT_TYPE_MAX; - base::TimeDelta bypass_duration; + net::HttpResponseHeaders::ChromeProxyInfo chrome_proxy_info; bool chrome_proxy_used = proxy_info_.proxy_server().isDataReductionProxy(); bool chrome_fallback_proxy_used = false; @@ -963,11 +969,11 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { #endif if (chrome_proxy_used || chrome_fallback_proxy_used) { - if (response_.headers->GetChromeProxyInfo(&bypass_duration)) { - proxy_bypass_event = - (bypass_duration < base::TimeDelta::FromMinutes(30) ? - ProxyService::SHORT_BYPASS : - ProxyService::LONG_BYPASS); + if (response_.headers->GetChromeProxyInfo(&chrome_proxy_info)) { + if (chrome_proxy_info.bypass_duration < TimeDelta::FromMinutes(30)) + proxy_bypass_event = ProxyService::SHORT_BYPASS; + else + proxy_bypass_event = ProxyService::LONG_BYPASS; } else { // Additionally, fallback if a 500 or 502 is returned via the data // reduction proxy. This is conservative, as the 500 or 502 might have @@ -985,8 +991,23 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { proxy_service->RecordDataReductionProxyBypassInfo( chrome_proxy_used, proxy_info_.proxy_server(), proxy_bypass_event); - if (proxy_service->MarkProxyAsBad(proxy_info_, bypass_duration, - net_log_)) { + ProxyServer proxy_server; +#if defined(DATA_REDUCTION_FALLBACK_HOST) + if (chrome_proxy_used && chrome_proxy_info.bypass_all) { + // TODO(bengr): Rename as DATA_REDUCTION_FALLBACK_ORIGIN. + GURL proxy_url(DATA_REDUCTION_FALLBACK_HOST); + if (proxy_url.SchemeIsHTTPOrHTTPS()) { + proxy_server = ProxyServer(proxy_url.SchemeIs("http") ? + ProxyServer::SCHEME_HTTP : + ProxyServer::SCHEME_HTTPS, + HostPortPair::FromURL(proxy_url)); + } + } +#endif + if (proxy_service->MarkProxiesAsBad(proxy_info_, + chrome_proxy_info.bypass_duration, + proxy_server, + net_log_)) { // Only retry in the case of GETs. We don't want to resubmit a POST // if the proxy took some action. if (request_->method == "GET") { @@ -997,7 +1018,7 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { } } } -#endif +#endif // defined(SPDY_PROXY_AUTH_ORIGIN) // Like Net.HttpResponseCode, but only for MAIN_FRAME loads. if (request_->load_flags & LOAD_MAIN_FRAME) { diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc index 26cdd2ab2..9d95153 100644 --- a/net/http/http_response_headers.cc +++ b/net/http/http_response_headers.cc @@ -1355,39 +1355,54 @@ bool HttpResponseHeaders::IsChunkEncoded() const { } #if defined(SPDY_PROXY_AUTH_ORIGIN) -bool HttpResponseHeaders::GetChromeProxyInfo( - base::TimeDelta* bypass_duration) const { - const char kProxyBypass[] = "proxy-bypass"; - *bypass_duration = base::TimeDelta(); - - // Support header of the form Chrome-Proxy: bypass=<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. - std::string name = "chrome-proxy"; - const char kBypassPrefix[] = "bypass="; - const size_t kBypassPrefixLen = arraysize(kBypassPrefix) - 1; - +bool HttpResponseHeaders::GetChromeProxyBypassDuration( + 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() > kBypassPrefixLen) { + if (value.size() > action_prefix.size()) { if (LowerCaseEqualsASCII(value.begin(), - value.begin() + kBypassPrefixLen, - kBypassPrefix)) { + value.begin() + action_prefix.size(), + action_prefix.c_str())) { int64 seconds; - if (!base::StringToInt64(StringPiece(value.begin() + kBypassPrefixLen, - value.end()), - &seconds) || seconds < 0) { - continue; // In case there is a well formed bypass instruction. + if (!base::StringToInt64( + StringPiece(value.begin() + action_prefix.size(), value.end()), + &seconds) || seconds < 0) { + continue; // In case there is a well formed instruction. } - *bypass_duration = TimeDelta::FromSeconds(seconds); + *duration = TimeDelta::FromSeconds(seconds); return true; } } } - // TODO(bengr): Deprecate the use of Connection: Proxy-Bypass. - if (HasHeaderValue("Connection", kProxyBypass)) + return false; +} + +bool HttpResponseHeaders::GetChromeProxyInfo( + ChromeProxyInfo* 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=<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 Chrome proxy, + // whereas 'block' instructs Chrome to bypass all available Chrome proxies. + + // 'block' takes precedence over 'bypass', so look for it first. + // TODO(bengr): Reduce checks for 'block' and 'bypass' to a single loop. + if (GetChromeProxyBypassDuration("block=", &proxy_info->bypass_duration)) { + proxy_info->bypass_all = true; + return true; + } + + // Next, look for 'bypass'. + if (GetChromeProxyBypassDuration("bypass=", &proxy_info->bypass_duration)) return true; return false; diff --git a/net/http/http_response_headers.h b/net/http/http_response_headers.h index a212980..e54ac5d 100644 --- a/net/http/http_response_headers.h +++ b/net/http/http_response_headers.h @@ -251,12 +251,25 @@ class NET_EXPORT HttpResponseHeaders bool IsChunkEncoded() const; #if defined (SPDY_PROXY_AUTH_ORIGIN) + // Contains instructions contained in the Chrome-Proxy header. + struct ChromeProxyInfo { + ChromeProxyInfo() : bypass_all(false) {} + + // True if Chrome should bypass all available Chrome proxies. False if only + // the currently connected Chrome proxy should be bypassed. + bool bypass_all; + + // Amount of time to bypass the Chrome proxy or proxies. + base::TimeDelta bypass_duration; + }; + // Returns true if the Chrome-Proxy header is present and contains a bypass - // delay. Sets |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. |bypass_duration| - // must be non-NULL. - bool GetChromeProxyInfo(base::TimeDelta* bypass_duration) const; + // 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 Chrome proxies should by bypassed, |bypass_all| is set to + // true. |proxy_info| must be non-NULL. + bool GetChromeProxyInfo(ChromeProxyInfo* proxy_info) const; #endif // Creates a Value for use with the NetLog containing the response headers. @@ -357,6 +370,13 @@ class NET_EXPORT HttpResponseHeaders // Adds the set of transport security state headers. static void AddSecurityStateHeaders(HeaderSet* header_names); +#if defined(SPDY_PROXY_AUTH_ORIGIN) + // Searches for the specified Chrome-Proxy action, and if present interprets + // its value as a duration in seconds. + bool GetChromeProxyBypassDuration(const std::string& action_prefix, + base::TimeDelta* duration) const; +#endif + // 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 5904bea..4be7478 100644 --- a/net/http/http_response_headers_unittest.cc +++ b/net/http/http_response_headers_unittest.cc @@ -1884,79 +1884,76 @@ TEST(HttpResponseHeadersTest, GetProxyBypassInfo) { 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, - }, - { "HTTP/1.1 200 OK\n" - "connection: proxy-bypass\n" - "Content-Length: 999\n", - true, - 0, - }, - { "HTTP/1.1 200 OK\n" - "connection: 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: bypass=86400\n" "Content-Length: 999\n", true, - 86400 + 86400, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" "Chrome-Proxy: bypass=0\n" "Content-Length: 999\n", true, - 0 + 0, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" "Chrome-Proxy: bypass=-1\n" "Content-Length: 999\n", false, - 0 + 0, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" "Chrome-Proxy: bypass=xyz\n" "Content-Length: 999\n", false, - 0 + 0, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" "Chrome-Proxy: bypass\n" "Content-Length: 999\n", false, - 0 + 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 + 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 + 86400, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -1964,21 +1961,24 @@ TEST(HttpResponseHeadersTest, GetProxyBypassInfo) { "Chrome-Proxy: bypass=86400\n" "Content-Length: 999\n", true, - 3600 + 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 + 3600, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" "Chrome-Proxy: bypass=, bypass=86400\n" "Content-Length: 999\n", true, - 86400 + 86400, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -1986,7 +1986,32 @@ TEST(HttpResponseHeadersTest, GetProxyBypassInfo) { "Chrome-Proxy: bypass=86400\n" "Content-Length: 999\n", true, - 86400 + 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, }, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { @@ -1995,10 +2020,13 @@ TEST(HttpResponseHeadersTest, GetProxyBypassInfo) { scoped_refptr<net::HttpResponseHeaders> parsed( new net::HttpResponseHeaders(headers)); - base::TimeDelta duration; + net::HttpResponseHeaders::ChromeProxyInfo chrome_proxy_info; EXPECT_EQ(tests[i].expected_result, - parsed->GetChromeProxyInfo(&duration)); - EXPECT_EQ(tests[i].expected_retry_delay, duration.InSeconds()); + parsed->GetChromeProxyInfo(&chrome_proxy_info)); + EXPECT_EQ(tests[i].expected_retry_delay, + chrome_proxy_info.bypass_duration.InSeconds()); + EXPECT_EQ(tests[i].expected_bypass_all, + chrome_proxy_info.bypass_all); } } #endif // defined(SPDY_PROXY_AUTH_ORIGIN) diff --git a/net/proxy/proxy_list.cc b/net/proxy/proxy_list.cc index bd7cbfb..5328402 100644 --- a/net/proxy/proxy_list.cc +++ b/net/proxy/proxy_list.cc @@ -185,16 +185,39 @@ bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info, NOTREACHED(); return false; } - UpdateRetryInfoOnFallback(proxy_retry_info, base::TimeDelta(), net_log); + UpdateRetryInfoOnFallback(proxy_retry_info, base::TimeDelta(), ProxyServer(), + net_log); // Remove this proxy from our list. proxies_.erase(proxies_.begin()); return !proxies_.empty(); } -void ProxyList::UpdateRetryInfoOnFallback(ProxyRetryInfoMap* proxy_retry_info, - base::TimeDelta retry_delay, - const BoundNetLog& net_log) const { +void ProxyList::AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info, + base::TimeDelta retry_delay, + const std::string& proxy_key, + const BoundNetLog& net_log) const { + // Mark this proxy as bad. + ProxyRetryInfoMap::iterator iter = proxy_retry_info->find(proxy_key); + if (iter != proxy_retry_info->end()) { + // TODO(nsylvain): This is not the first time we get this. We should + // double the retry time. Bug 997660. + iter->second.bad_until = TimeTicks::Now() + iter->second.current_delay; + } else { + ProxyRetryInfo retry_info; + retry_info.current_delay = retry_delay; + retry_info.bad_until = TimeTicks().Now() + retry_info.current_delay; + (*proxy_retry_info)[proxy_key] = retry_info; + } + net_log.AddEvent(NetLog::TYPE_PROXY_LIST_FALLBACK, + NetLog::StringCallback("bad_proxy", &proxy_key)); +} + +void ProxyList::UpdateRetryInfoOnFallback( + ProxyRetryInfoMap* proxy_retry_info, + base::TimeDelta retry_delay, + const ProxyServer& another_proxy_to_bypass, + const BoundNetLog& net_log) const { // Time to wait before retrying a bad proxy server. if (retry_delay == base::TimeDelta()) { #if defined(SPDY_PROXY_AUTH_ORIGIN) @@ -214,20 +237,21 @@ void ProxyList::UpdateRetryInfoOnFallback(ProxyRetryInfoMap* proxy_retry_info, if (!proxies_[0].is_direct()) { std::string key = proxies_[0].ToURI(); - // Mark this proxy as bad. - ProxyRetryInfoMap::iterator iter = proxy_retry_info->find(key); - if (iter != proxy_retry_info->end()) { - // TODO(nsylvain): This is not the first time we get this. We should - // double the retry time. Bug 997660. - iter->second.bad_until = TimeTicks::Now() + iter->second.current_delay; - } else { - ProxyRetryInfo retry_info; - retry_info.current_delay = retry_delay; - retry_info.bad_until = TimeTicks().Now() + retry_info.current_delay; - (*proxy_retry_info)[key] = retry_info; + AddProxyToRetryList(proxy_retry_info, retry_delay, key, net_log); + + // If additional proxies to bypass are specified, add these to the retry + // map as well. + if (another_proxy_to_bypass.is_valid()) { + // Start at index 1 because index 0 is already handled above. + for (size_t j = 1; j < proxies_.size(); ++j) { + if (proxies_[j].is_direct()) + break; + if (another_proxy_to_bypass == proxies_[j]) { + key = proxies_[j].ToURI(); + AddProxyToRetryList(proxy_retry_info, retry_delay, key, net_log); + } + } } - net_log.AddEvent(NetLog::TYPE_PROXY_LIST_FALLBACK, - NetLog::StringCallback("bad_proxy", &key)); } } diff --git a/net/proxy/proxy_list.h b/net/proxy/proxy_list.h index 33a78a8..e8df0ba 100644 --- a/net/proxy/proxy_list.h +++ b/net/proxy/proxy_list.h @@ -92,12 +92,26 @@ class NET_EXPORT_PRIVATE ProxyList { // is bad. This is distinct from Fallback(), above, to allow updating proxy // retry information without modifying a given transction's proxy list. Will // retry after |retry_delay| if positive, and will use the default proxy retry - // duration otherwise. - void UpdateRetryInfoOnFallback(ProxyRetryInfoMap* proxy_retry_info, - base::TimeDelta retry_delay, - const BoundNetLog& net_log) const; + // duration otherwise. Additionally updates |proxy_retry_info| with + // |another_proxy_to_bypass| if non-empty. + void UpdateRetryInfoOnFallback( + ProxyRetryInfoMap* proxy_retry_info, + base::TimeDelta retry_delay, + const ProxyServer& another_proxy_to_bypass, + const BoundNetLog& net_log) const; private: + // Updates |proxy_retry_info| to indicate that the proxy in |proxies_| with + // the URI of |proxy_key| is bad. The |proxy_key| must start with the scheme + // (only if https) followed by the host and then an explicit port. For + // example, if the proxy origin is https://proxy.chromium.org:443/ the key is + // https://proxy.chrome.org:443 whereas if the origin is + // http://proxy.chrome.org/, the key is proxy.chrome.org:80. + void AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info, + base::TimeDelta retry_delay, + const std::string& proxy_key, + const BoundNetLog& net_log) const; + // List of proxies. std::vector<ProxyServer> proxies_; }; diff --git a/net/proxy/proxy_server.cc b/net/proxy/proxy_server.cc index 3b8bd03..a6ea5de 100644 --- a/net/proxy/proxy_server.cc +++ b/net/proxy/proxy_server.cc @@ -208,19 +208,20 @@ ProxyServer::Scheme ProxyServer::GetSchemeFromURI(const std::string& scheme) { return GetSchemeFromURIInternal(scheme.begin(), scheme.end()); } +// TODO(bengr): Use |scheme_| to indicate that this is the data reduction proxy. #if defined(SPDY_PROXY_AUTH_ORIGIN) - bool ProxyServer::isDataReductionProxy() const { - return host_port_pair_.Equals( - HostPortPair::FromURL(GURL(SPDY_PROXY_AUTH_ORIGIN))); - } +bool ProxyServer::isDataReductionProxy() const { + return host_port_pair_.Equals( + HostPortPair::FromURL(GURL(SPDY_PROXY_AUTH_ORIGIN))); +} - bool ProxyServer::isDataReductionProxyFallback() const { +bool ProxyServer::isDataReductionProxyFallback() const { #if defined(DATA_REDUCTION_FALLBACK_HOST) - return host_port_pair_.Equals( - HostPortPair::FromURL(GURL(DATA_REDUCTION_FALLBACK_HOST))); + return host_port_pair_.Equals( + HostPortPair::FromURL(GURL(DATA_REDUCTION_FALLBACK_HOST))); #endif // defined(DATA_REDUCTION_FALLBACK_HOST) - return false; - } + return false; +} #endif // defined(SPDY_PROXY_AUTH_ORIGIN) // static diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc index b18b47b..9d4125b 100644 --- a/net/proxy/proxy_service.cc +++ b/net/proxy/proxy_service.cc @@ -1192,10 +1192,13 @@ int ProxyService::ReconsiderProxyAfterError(const GURL& url, return did_fallback ? OK : ERR_FAILED; } -bool ProxyService::MarkProxyAsBad(const ProxyInfo& result, - base::TimeDelta retry_delay, - const BoundNetLog& net_log) { +bool ProxyService::MarkProxiesAsBad( + const ProxyInfo& result, + base::TimeDelta retry_delay, + const ProxyServer& another_bad_proxy, + const BoundNetLog& net_log) { result.proxy_list_.UpdateRetryInfoOnFallback(&proxy_retry_info_, retry_delay, + another_bad_proxy, net_log); return result.proxy_list_.HasUntriedProxies(proxy_retry_info_); } diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h index 3573711..91c713d 100644 --- a/net/proxy/proxy_service.h +++ b/net/proxy/proxy_service.h @@ -148,13 +148,15 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver, const BoundNetLog& net_log); // Explicitly trigger proxy fallback for the given |results| by updating our - // list of bad proxies to include the first entry of |results|. Will retry - // after |retry_delay| if positive, and will use the default proxy retry - // duration otherwise. Returns true if there will be at least one proxy - // remaining in the list after fallback and false otherwise. - bool MarkProxyAsBad(const ProxyInfo& results, - base::TimeDelta retry_delay, - const BoundNetLog& net_log); + // list of bad proxies to include the first entry of |results|, and, + // optionally, another bad proxy. Will retry after |retry_delay| if positive, + // and will use the default proxy retry duration otherwise. Returns true if + // there will be at least one proxy remaining in the list after fallback and + // false otherwise. + bool MarkProxiesAsBad(const ProxyInfo& results, + base::TimeDelta retry_delay, + const ProxyServer& another_bad_proxy, + const BoundNetLog& net_log); // Called to report that the last proxy connection succeeded. If |proxy_info| // has a non empty proxy_retry_info map, the proxies that have been tried (and |