diff options
-rw-r--r-- | jingle/notifier/base/proxy_resolving_client_socket.cc | 6 | ||||
-rw-r--r-- | jingle/notifier/base/proxy_resolving_client_socket.h | 1 | ||||
-rw-r--r-- | net/base/net_log_event_type_list.h | 22 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl_job.cc | 1 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl_unittest.cc | 99 | ||||
-rw-r--r-- | net/proxy/proxy_info.cc | 10 | ||||
-rw-r--r-- | net/proxy/proxy_info.h | 10 | ||||
-rw-r--r-- | net/proxy/proxy_list.cc | 8 | ||||
-rw-r--r-- | net/proxy/proxy_list.h | 4 | ||||
-rw-r--r-- | net/proxy/proxy_service.cc | 52 | ||||
-rw-r--r-- | net/proxy/proxy_service.h | 5 | ||||
-rw-r--r-- | net/proxy/proxy_service_unittest.cc | 22 |
12 files changed, 232 insertions, 8 deletions
diff --git a/jingle/notifier/base/proxy_resolving_client_socket.cc b/jingle/notifier/base/proxy_resolving_client_socket.cc index 45d0c11..a4083b0 100644 --- a/jingle/notifier/base/proxy_resolving_client_socket.cc +++ b/jingle/notifier/base/proxy_resolving_client_socket.cc @@ -190,6 +190,8 @@ void ProxyResolvingClientSocket::ProcessConnectDone(int status) { // Proxy reconsideration pending. Return. return; CloseTransportSocket(); + } else { + ReportSuccessfulProxyConnection(); } RunUserConnectCallback(status); } @@ -274,6 +276,10 @@ int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) { return rv; } +void ProxyResolvingClientSocket::ReportSuccessfulProxyConnection() { + network_session_->proxy_service()->ReportSuccess(proxy_info_); +} + void ProxyResolvingClientSocket::Disconnect() { CloseTransportSocket(); if (pac_request_) diff --git a/jingle/notifier/base/proxy_resolving_client_socket.h b/jingle/notifier/base/proxy_resolving_client_socket.h index b97315b..a651403f 100644 --- a/jingle/notifier/base/proxy_resolving_client_socket.h +++ b/jingle/notifier/base/proxy_resolving_client_socket.h @@ -67,6 +67,7 @@ class ProxyResolvingClientSocket : public net::StreamSocket { void CloseTransportSocket(); void RunUserConnectCallback(int status); int ReconsiderProxyAfterError(int error); + void ReportSuccessfulProxyConnection(); // Callbacks passed to net APIs. net::CompletionCallbackImpl<ProxyResolvingClientSocket> diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h index c0ebe19..db26071 100644 --- a/net/base/net_log_event_type_list.h +++ b/net/base/net_log_event_type_list.h @@ -220,6 +220,28 @@ EVENT_TYPE(PROXY_SERVICE_RESOLVED_PROXY_LIST) // proxy settings (since there wasn't a previous value). EVENT_TYPE(PROXY_CONFIG_CHANGED) +// Emitted when a list of bad proxies is reported to the proxy service. +// +// Parameters: +// { +// "bad_proxy_list": <List of bad proxies>, +// } +EVENT_TYPE(BAD_PROXY_LIST_REPORTED) + +// ------------------------------------------------------------------------ +// ProxyList +// ------------------------------------------------------------------------ + +// Emitted when the first proxy server in a list is being marked as +// bad and proxy resolution is going to failover to the next one in +// the list. The fallback is local to the request. +// +// Parameters: +// { +// "bad_proxy": <URI representation of the failed proxy server>, +// } +EVENT_TYPE(PROXY_LIST_FALLBACK) + // ------------------------------------------------------------------------ // Proxy Resolver // ------------------------------------------------------------------------ diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc index dcbf114..2a0d91c 100644 --- a/net/http/http_stream_factory_impl_job.cc +++ b/net/http/http_stream_factory_impl_job.cc @@ -836,6 +836,7 @@ int HttpStreamFactoryImpl::Job::DoCreateStreamComplete(int result) { if (result < 0) return result; + session_->proxy_service()->ReportSuccess(proxy_info_); next_state_ = STATE_NONE; return OK; } diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc index 1b40d5e..6169aa1 100644 --- a/net/http/http_stream_factory_impl_unittest.cc +++ b/net/http/http_stream_factory_impl_unittest.cc @@ -16,6 +16,7 @@ #include "net/http/http_network_session.h" #include "net/http/http_network_session_peer.h" #include "net/http/http_request_info.h" +#include "net/http/http_stream.h" #include "net/proxy/proxy_info.h" #include "net/proxy/proxy_service.h" #include "net/socket/socket_test_util.h" @@ -55,6 +56,62 @@ class MockHttpStreamFactoryImpl : public HttpStreamFactoryImpl { bool waiting_for_preconnect_; }; +class StreamRequestWaiter : public HttpStreamRequest::Delegate { + public: + StreamRequestWaiter() + : waiting_for_stream_(false), + stream_done_(false) {} + + // HttpStreamRequest::Delegate + + virtual void OnStreamReady( + const SSLConfig& used_ssl_config, + const ProxyInfo& used_proxy_info, + HttpStream* stream) OVERRIDE { + stream_done_ = true; + if (waiting_for_stream_) + MessageLoop::current()->Quit(); + stream_.reset(stream); + } + + virtual void OnStreamFailed( + int status, + const SSLConfig& used_ssl_config) OVERRIDE {} + + virtual void OnCertificateError( + int status, + const SSLConfig& used_ssl_config, + const SSLInfo& ssl_info) OVERRIDE {} + + virtual void OnNeedsProxyAuth(const HttpResponseInfo& proxy_response, + const SSLConfig& used_ssl_config, + const ProxyInfo& used_proxy_info, + HttpAuthController* auth_controller) OVERRIDE {} + + virtual void OnNeedsClientAuth(const SSLConfig& used_ssl_config, + SSLCertRequestInfo* cert_info) OVERRIDE {} + + virtual void OnHttpsProxyTunnelResponse(const HttpResponseInfo& response_info, + const SSLConfig& used_ssl_config, + const ProxyInfo& used_proxy_info, + HttpStream* stream) OVERRIDE {} + + void WaitForStream() { + while (!stream_done_) { + waiting_for_stream_ = true; + MessageLoop::current()->Run(); + waiting_for_stream_ = false; + } + } + + private: + bool waiting_for_stream_; + bool stream_done_; + scoped_ptr<HttpStream> stream_; + + DISALLOW_COPY_AND_ASSIGN(StreamRequestWaiter); +}; + struct SessionDependencies { // Custom proxy service dependency. explicit SessionDependencies(ProxyService* proxy_service) @@ -315,6 +372,48 @@ TEST(HttpStreamFactoryTest, PreconnectDirectWithExistingSpdySession) { } } +TEST(HttpStreamFactoryTest, JobNotifiesProxy) { + const char* kProxyString = "PROXY bad:99; PROXY maybe:80; DIRECT"; + SessionDependencies session_deps( + ProxyService::CreateFixedFromPacResult(kProxyString)); + + // First connection attempt fails + StaticSocketDataProvider socket_data1; + socket_data1.set_connect_data(MockConnect(true, ERR_ADDRESS_UNREACHABLE)); + session_deps.socket_factory.AddSocketDataProvider(&socket_data1); + + // Second connection attempt succeeds + StaticSocketDataProvider socket_data2; + socket_data2.set_connect_data(MockConnect(true, OK)); + session_deps.socket_factory.AddSocketDataProvider(&socket_data2); + + CapturingBoundNetLog log(CapturingNetLog::kUnbounded); + EXPECT_TRUE(log.bound().IsLoggingAllEvents()); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // Now request a stream. It should succeed using the second proxy in the + // list. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("http://www.google.com"); + request_info.load_flags = 0; + + SSLConfig ssl_config; + StreamRequestWaiter waiter; + scoped_ptr<HttpStreamRequest> request( + session->http_stream_factory()->RequestStream(request_info, ssl_config, + &waiter, log.bound())); + waiter.WaitForStream(); + + // The proxy that failed should now be known to the proxy_service as bad. + const ProxyRetryInfoMap& retry_info = + session->proxy_service()->proxy_retry_info(); + EXPECT_EQ(1u, retry_info.size()); + ProxyRetryInfoMap::const_iterator iter = retry_info.find("bad:99"); + EXPECT_TRUE(iter != retry_info.end()); +} + } // namespace } // namespace net diff --git a/net/proxy/proxy_info.cc b/net/proxy/proxy_info.cc index ebefa84..85d6cae 100644 --- a/net/proxy/proxy_info.cc +++ b/net/proxy/proxy_info.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -16,26 +16,30 @@ ProxyInfo::~ProxyInfo() { void ProxyInfo::Use(const ProxyInfo& other) { proxy_list_ = other.proxy_list_; + proxy_retry_info_ = other.proxy_retry_info_; } void ProxyInfo::UseDirect() { proxy_list_.SetSingleProxyServer(ProxyServer::Direct()); + proxy_retry_info_.clear(); } void ProxyInfo::UseNamedProxy(const std::string& proxy_uri_list) { proxy_list_.Set(proxy_uri_list); + proxy_retry_info_.clear(); } void ProxyInfo::UseProxyServer(const ProxyServer& proxy_server) { proxy_list_.SetSingleProxyServer(proxy_server); + proxy_retry_info_.clear(); } std::string ProxyInfo::ToPacString() const { return proxy_list_.ToPacString(); } -bool ProxyInfo::Fallback(ProxyRetryInfoMap* proxy_retry_info) { - return proxy_list_.Fallback(proxy_retry_info); +bool ProxyInfo::Fallback(const BoundNetLog& net_log) { + return proxy_list_.Fallback(&proxy_retry_info_, net_log); } void ProxyInfo::DeprioritizeBadProxies( diff --git a/net/proxy/proxy_info.h b/net/proxy/proxy_info.h index f965e6b..2ff8136 100644 --- a/net/proxy/proxy_info.h +++ b/net/proxy/proxy_info.h @@ -9,6 +9,7 @@ #include <string> #include "net/base/net_export.h" +#include "net/base/net_log.h" #include "net/proxy/proxy_config.h" #include "net/proxy/proxy_list.h" #include "net/proxy/proxy_retry_info.h" @@ -86,7 +87,7 @@ class NET_EXPORT ProxyInfo { // Marks the current proxy as bad. Returns true if there is another proxy // available to try in proxy list_. - bool Fallback(ProxyRetryInfoMap* proxy_retry_info); + bool Fallback(const BoundNetLog& net_log); // De-prioritizes the proxies that we have cached as not working, by moving // them to the end of the proxy list. @@ -98,10 +99,17 @@ class NET_EXPORT ProxyInfo { private: friend class ProxyService; + const ProxyRetryInfoMap& proxy_retry_info() const { + return proxy_retry_info_; + } + // The ordered list of proxy servers (including DIRECT attempts) remaining to // try. If proxy_list_ is empty, then there is nothing left to fall back to. ProxyList proxy_list_; + // List of proxies that have been tried already. + ProxyRetryInfoMap proxy_retry_info_; + // This value identifies the proxy config used to initialize this object. ProxyConfig::ID config_id_; }; diff --git a/net/proxy/proxy_list.cc b/net/proxy/proxy_list.cc index 011aab9..cdc8cc2 100644 --- a/net/proxy/proxy_list.cc +++ b/net/proxy/proxy_list.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -115,7 +115,8 @@ std::string ProxyList::ToPacString() const { return proxy_list.empty() ? std::string() : proxy_list; } -bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info) { +bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info, + const BoundNetLog& net_log) { // Number of minutes to wait before retrying a bad proxy server. const TimeDelta kProxyRetryDelay = TimeDelta::FromMinutes(5); @@ -152,6 +153,9 @@ bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info) { retry_info.bad_until = TimeTicks().Now() + retry_info.current_delay; (*proxy_retry_info)[key] = retry_info; } + net_log.AddEvent( + NetLog::TYPE_PROXY_LIST_FALLBACK, + make_scoped_refptr(new NetLogStringParameter("bad_proxy", key))); } // Remove this proxy from our list. diff --git a/net/proxy/proxy_list.h b/net/proxy/proxy_list.h index 707ceb6..a377553 100644 --- a/net/proxy/proxy_list.h +++ b/net/proxy/proxy_list.h @@ -10,6 +10,7 @@ #include <vector> #include "net/base/net_export.h" +#include "net/base/net_log.h" #include "net/proxy/proxy_retry_info.h" namespace net { @@ -61,7 +62,8 @@ class NET_EXPORT_PRIVATE ProxyList { // Marks the current proxy server as bad and deletes it from the list. The // list of known bad proxies is given by proxy_retry_info. Returns true if // there is another server available in the list. - bool Fallback(ProxyRetryInfoMap* proxy_retry_info); + bool Fallback(ProxyRetryInfoMap* proxy_retry_info, + const BoundNetLog& net_log); private: // List of proxies. diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc index 66bf929..643b9be 100644 --- a/net/proxy/proxy_service.cc +++ b/net/proxy/proxy_service.cc @@ -264,6 +264,31 @@ class ProxyConfigChangedNetLogParam : public NetLog::EventParameters { DISALLOW_COPY_AND_ASSIGN(ProxyConfigChangedNetLogParam); }; +class BadProxyListNetLogParam : public NetLog::EventParameters { + public: + BadProxyListNetLogParam(const ProxyRetryInfoMap& retry_info) { + proxy_list_.reserve(retry_info.size()); + for (ProxyRetryInfoMap::const_iterator iter = retry_info.begin(); + iter != retry_info.end(); ++iter) { + proxy_list_.push_back(iter->first); + } + } + + virtual Value* ToValue() const OVERRIDE { + DictionaryValue* dict = new DictionaryValue(); + ListValue* list = new ListValue(); + for (std::vector<std::string>::const_iterator iter = proxy_list_.begin(); + iter != proxy_list_.end(); ++iter) + list->Append(Value::CreateStringValue(*iter)); + dict->Set("bad_proxy_list", list); + return dict; + } + + private: + std::vector<std::string> proxy_list_; + DISALLOW_COPY_AND_ASSIGN(BadProxyListNetLogParam); +}; + } // namespace // ProxyService::PacRequest --------------------------------------------------- @@ -715,13 +740,38 @@ int ProxyService::ReconsiderProxyAfterError(const GURL& url, // We don't have new proxy settings to try, try to fallback to the next proxy // in the list. - bool did_fallback = result->Fallback(&proxy_retry_info_); + bool did_fallback = result->Fallback(net_log); // Return synchronous failure if there is nothing left to fall-back to. // TODO(eroman): This is a yucky API, clean it up. return did_fallback ? OK : ERR_FAILED; } +void ProxyService::ReportSuccess(const ProxyInfo& result) { + DCHECK(CalledOnValidThread()); + + const ProxyRetryInfoMap& new_retry_info = result.proxy_retry_info(); + if (new_retry_info.empty()) + return; + + for (ProxyRetryInfoMap::const_iterator iter = new_retry_info.begin(); + iter != new_retry_info.end(); ++iter) { + ProxyRetryInfoMap::iterator existing = proxy_retry_info_.find(iter->first); + if (existing == proxy_retry_info_.end()) + proxy_retry_info_[iter->first] = iter->second; + else if (existing->second.bad_until < iter->second.bad_until) + existing->second.bad_until = iter->second.bad_until; + } + if (net_log_) { + net_log_->AddEntry(NetLog::TYPE_BAD_PROXY_LIST_REPORTED, + base::TimeTicks::Now(), + NetLog::Source(), + NetLog::PHASE_NONE, + make_scoped_refptr( + new BadProxyListNetLogParam(new_retry_info))); + } +} + void ProxyService::CancelPacRequest(PacRequest* req) { DCHECK(CalledOnValidThread()); DCHECK(req); diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h index ad73b39..cc308dd 100644 --- a/net/proxy/proxy_service.h +++ b/net/proxy/proxy_service.h @@ -97,6 +97,11 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver, PacRequest** pac_request, 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 + // failed) for this request will be marked as bad. + void ReportSuccess(const ProxyInfo& proxy_info); + // Call this method with a non-null |pac_request| to cancel the PAC request. void CancelPacRequest(PacRequest* pac_request); diff --git a/net/proxy/proxy_service_unittest.cc b/net/proxy/proxy_service_unittest.cc index d41f48c..b2345d5 100644 --- a/net/proxy/proxy_service_unittest.cc +++ b/net/proxy/proxy_service_unittest.cc @@ -541,6 +541,9 @@ TEST(ProxyServiceTest, ProxyFallback) { // The second proxy should be specified. EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); + // Report back that the second proxy worked. This will globally mark the + // first proxy as bad. + service.ReportSuccess(info); TestCompletionCallback callback3; rv = service.ResolveProxy(url, &info, &callback3, NULL, BoundNetLog()); @@ -584,6 +587,25 @@ TEST(ProxyServiceTest, ProxyFallback) { EXPECT_FALSE(info.is_direct()); EXPECT_TRUE(info.is_empty()); + // Look up proxies again + TestCompletionCallback callback7; + rv = service.ResolveProxy(url, &info, &callback7, NULL, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + ASSERT_EQ(1u, resolver->pending_requests().size()); + EXPECT_EQ(url, resolver->pending_requests()[0]->url()); + + // This time, the first 3 results have been found to be bad, but only the + // first proxy has been confirmed ... + resolver->pending_requests()[0]->results()->UseNamedProxy( + "foopy1:8080;foopy3:7070;foopy2:9090;foopy4:9091"); + resolver->pending_requests()[0]->CompleteNow(OK); + + // ... therefore, we should see the second proxy first. + EXPECT_EQ(OK, callback7.WaitForResult()); + EXPECT_FALSE(info.is_direct()); + EXPECT_EQ("foopy3:7070", info.proxy_server().ToURI()); + // TODO(nsylvain): Test that the proxy can be retried after the delay. } |