diff options
-rw-r--r-- | chrome/browser/browser_main.cc | 7 | ||||
-rw-r--r-- | chrome/browser/net/preconnect.cc | 64 | ||||
-rw-r--r-- | chrome/browser/net/preconnect.h | 24 | ||||
-rw-r--r-- | chrome/browser/net/predictor.cc | 14 | ||||
-rw-r--r-- | chrome/browser/net/predictor.h | 16 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 3 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 1 | ||||
-rw-r--r-- | net/net.gyp | 1 | ||||
-rw-r--r-- | net/socket/client_socket.cc | 69 | ||||
-rw-r--r-- | net/socket/client_socket.h | 38 | ||||
-rw-r--r-- | net/socket/client_socket_handle.cc | 1 | ||||
-rw-r--r-- | net/socket/client_socket_handle.h | 14 | ||||
-rw-r--r-- | net/socket/client_socket_pool_base.h | 11 |
13 files changed, 225 insertions, 38 deletions
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index 1f95c1f5..7c35799 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -1205,10 +1205,15 @@ int BrowserMain(const MainFunctionParams& parameters) { // pre-resolution, as well as TCP/IP connection pre-warming. // This also registers an observer to discard data when closing incognito // mode. + bool preconnect_enabled = true; // Default status (easy to change!). + if (parsed_command_line.HasSwitch(switches::kDisablePreconnect)) + preconnect_enabled = false; + else if (parsed_command_line.HasSwitch(switches::kEnablePreconnect)) + preconnect_enabled = true; chrome_browser_net::PredictorInit dns_prefetch( user_prefs, local_state, - parsed_command_line.HasSwitch(switches::kEnablePreconnect), + preconnect_enabled, parsed_command_line.HasSwitch(switches::kPreconnectDespiteProxy)); #if defined(OS_WIN) diff --git a/chrome/browser/net/preconnect.cc b/chrome/browser/net/preconnect.cc index e8038ff..3872d2a 100644 --- a/chrome/browser/net/preconnect.cc +++ b/chrome/browser/net/preconnect.cc @@ -21,18 +21,22 @@ namespace chrome_browser_net { // static bool Preconnect::preconnect_despite_proxy_ = false; -// We will deliberately leak this singular instance, which is used only for -// callbacks. -// static -Preconnect* Preconnect::callback_instance_; + +Preconnect::~Preconnect() { + if (!handle_.is_initialized()) + return; + DCHECK(motivation_ == UrlInfo::LEARNED_REFERAL_MOTIVATED || + motivation_ == UrlInfo::OMNIBOX_MOTIVATED); + if (motivation_ == UrlInfo::OMNIBOX_MOTIVATED) + handle_.socket()->SetOmniboxSpeculation(); + else + handle_.socket()->SetSubresourceSpeculation(); + handle_.Reset(); +} // static void Preconnect::PreconnectOnUIThread(const GURL& url, UrlInfo::ResolutionMotivation motivation) { - // Try to do connection warming for this search provider. - URLRequestContextGetter* getter = Profile::GetDefaultRequestContext(); - if (!getter) - return; // Prewarm connection to Search URL. ChromeThread::PostTask( ChromeThread::IO, @@ -57,6 +61,13 @@ static void HistogramPreconnectStatus(ProxyStatus status) { // static void Preconnect::PreconnectOnIOThread(const GURL& url, UrlInfo::ResolutionMotivation motivation) { + scoped_refptr<Preconnect> preconnect = new Preconnect(motivation); + // TODO(jar): Should I use PostTask for LearnedSubresources to delay the + // preconnection a tad? + preconnect->Connect(url); +} + +void Preconnect::Connect(const GURL& url) { URLRequestContextGetter* getter = Profile::GetDefaultRequestContext(); if (!getter) return; @@ -64,6 +75,9 @@ void Preconnect::PreconnectOnIOThread(const GURL& url, LOG(DFATAL) << "This must be run only on the IO thread."; return; } + + AddRef(); // Stay alive until socket is available. + URLRequestContext* context = getter->GetURLRequestContext(); if (preconnect_despite_proxy_) { @@ -86,16 +100,12 @@ void Preconnect::PreconnectOnIOThread(const GURL& url, } } - UMA_HISTOGRAM_ENUMERATION("Net.PreconnectMotivation", motivation, + UMA_HISTOGRAM_ENUMERATION("Net.PreconnectMotivation", motivation_, UrlInfo::MAX_MOTIVATED); net::HttpTransactionFactory* factory = context->http_transaction_factory(); net::HttpNetworkSession* session = factory->GetSession(); - net::ClientSocketHandle handle; - if (!callback_instance_) - callback_instance_ = new Preconnect; - scoped_refptr<net::TCPSocketParams> tcp_params = new net::TCPSocketParams(url.host(), url.EffectiveIntPort(), net::LOW, GURL(), false); @@ -103,6 +113,19 @@ void Preconnect::PreconnectOnIOThread(const GURL& url, net::HostPortPair endpoint(url.host(), url.EffectiveIntPort()); std::string group_name = endpoint.ToString(); + // It almost doesn't matter whether we use net::LOWEST or net::HIGHEST + // priority here, as we won't make a request, and will surrender the created + // socket to the pool as soon as we can. However, we would like to mark the + // speculative socket as such, and IF we use a net::LOWEST priority, and if + // a navigation asked for a socket (after us) then it would get our socket, + // and we'd get its later-arriving socket, which might make us record that + // the speculation didn't help :-/. By using net::HIGHEST, we ensure that + // a socket is given to us if "we asked first" and this allows us to mark it + // as speculative, and better detect stats (if it gets used). + // TODO(jar): histogram to see how often we accidentally use a previously- + // unused socket, when a previously used socket was available. + net::RequestPriority priority = net::HIGHEST; + if (url.SchemeIs("https")) { group_name = StringPrintf("ssl/%s", group_name.c_str()); @@ -120,21 +143,20 @@ void Preconnect::PreconnectOnIOThread(const GURL& url, const scoped_refptr<net::SSLClientSocketPool>& pool = session->ssl_socket_pool(); - handle.Init(group_name, ssl_params, net::LOWEST, callback_instance_, pool, - net::BoundNetLog()); - handle.Reset(); + handle_.Init(group_name, ssl_params, priority, this, pool, + net::BoundNetLog()); return; } const scoped_refptr<net::TCPClientSocketPool>& pool = session->tcp_socket_pool(); - handle.Init(group_name, tcp_params, net::LOWEST, callback_instance_, pool, - net::BoundNetLog()); - handle.Reset(); + handle_.Init(group_name, tcp_params, priority, this, pool, + net::BoundNetLog()); } void Preconnect::RunWithParams(const Tuple1<int>& params) { - // This will rarely be called, as we reset the connection just after creating. - NOTREACHED(); + if (params.a < 0 && handle_.socket()) + handle_.socket()->Disconnect(); + Release(); } } // chrome_browser_net diff --git a/chrome/browser/net/preconnect.h b/chrome/browser/net/preconnect.h index d773636..f7d574d 100644 --- a/chrome/browser/net/preconnect.h +++ b/chrome/browser/net/preconnect.h @@ -19,7 +19,8 @@ namespace chrome_browser_net { -class Preconnect : public net::CompletionCallback { +class Preconnect : public net::CompletionCallback, + public base::RefCountedThreadSafe<Preconnect> { public: // Try to preconnect. Typically motivated by OMNIBOX to reach search service. static void PreconnectOnUIThread(const GURL& url, @@ -35,11 +36,15 @@ class Preconnect : public net::CompletionCallback { } private: - Preconnect() {} + friend class base::RefCountedThreadSafe<Preconnect>; - // Supply an instance that could have been used in an IO callback, but will - // never actually be used (because we reset the connection so quickly). - static Preconnect* callback_instance_; + explicit Preconnect(UrlInfo::ResolutionMotivation motivation) + : motivation_(motivation) { + } + ~Preconnect(); + + // Request actual connection. + void Connect(const GURL& url); // IO Callback which whould be performed when the connection is established. virtual void RunWithParams(const Tuple1<int>& params); @@ -50,6 +55,15 @@ class Preconnect : public net::CompletionCallback { // much work anway). static bool preconnect_despite_proxy_; + // The handle holding the request. We need this so that we can mark the + // request as speculative when an actual socket is bound to it. + net::ClientSocketHandle handle_; + + // Generally either LEARNED_REFERAL_MOTIVATED or OMNIBOX_MOTIVATED to indicate + // why we were trying to do a preconnection. + const UrlInfo::ResolutionMotivation motivation_; + + DISALLOW_COPY_AND_ASSIGN(Preconnect); }; } // chrome_browser_net diff --git a/chrome/browser/net/predictor.cc b/chrome/browser/net/predictor.cc index eb4992c..5c1b518 100644 --- a/chrome/browser/net/predictor.cc +++ b/chrome/browser/net/predictor.cc @@ -149,6 +149,20 @@ void Predictor::PredictFrameSubresources(const GURL& url) { Referrers::iterator it = referrers_.find(url); if (referrers_.end() == it) return; + ChromeThread::PostTask( + ChromeThread::IO, + FROM_HERE, + NewRunnableMethod(this, + &Predictor::PrepareFrameSubresources, url)); +} + +void Predictor::PrepareFrameSubresources(const GURL& url) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + DCHECK(url.GetWithEmptyPath() == url); + Referrers::iterator it = referrers_.find(url); + if (referrers_.end() == it) + return; + Referrer* referrer = &(it->second); referrer->IncrementUseCount(); const UrlInfo::ResolutionMotivation motivation = diff --git a/chrome/browser/net/predictor.h b/chrome/browser/net/predictor.h index a5b71e7..c613093 100644 --- a/chrome/browser/net/predictor.h +++ b/chrome/browser/net/predictor.h @@ -83,8 +83,11 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { void Resolve(const GURL& url, UrlInfo::ResolutionMotivation motivation); - // Instigate pre-connection to any URLs we predict will be needed after this - // navigation (typically more-embedded resources on a page). + // Instigate pre-connection to any URLs, or pre-resolution of related host, + // that we predict will be needed after this navigation (typically + // more-embedded resources on a page). This method will actually post a task + // to do the actual work, so as not to jump ahead of the frame navigation that + // instigated this activity. void PredictFrameSubresources(const GURL& url); // Record details of a navigation so that we can preresolve the host name @@ -138,8 +141,6 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { FRIEND_TEST_ALL_PREFIXES(PredictorTest, PriorityQueueReorderTest); friend class WaitForResolutionHelper; // For testing. - ~Predictor(); - class LookupRequest; // A simple priority queue for handling host names. @@ -174,6 +175,13 @@ class Predictor : public base::RefCountedThreadSafe<Predictor> { // in a Referrer instance, which is a value in this map. typedef std::map<GURL, Referrer> Referrers; + ~Predictor(); + + // Perform actual resolution or preconnection to subresources now. This is + // an internal worker method that is reached via a post task from + // PredictFrameSubresources(). + void PrepareFrameSubresources(const GURL& url); + // Only for testing. Returns true if hostname has been successfully resolved // (name found). bool WasFound(const GURL& url) const { diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index c12e9a1..97ef576 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -164,6 +164,9 @@ const char kDisableInternalFlash[] = "disable-internal-flash"; // This flag can be overidden by the "enable-ipv6" flag. const char kDisableIPv6[] = "disable-ipv6"; +// Disable speculative TCP/IP preconnection. +const char kDisablePreconnect[] = "disable-preconnect"; + // Don't execute JavaScript (browser JS like the new tab page still runs). const char kDisableJavaScript[] = "disable-javascript"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index b34fbb4..50f75d4 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -61,6 +61,7 @@ extern const char kDisableGeolocation[]; extern const char kDisableHangMonitor[]; extern const char kDisableInternalFlash[]; extern const char kDisableIPv6[]; +extern const char kDisablePreconnect[]; extern const char kDisableJavaScript[]; extern const char kDisableJava[]; extern const char kDisableLocalStorage[]; diff --git a/net/net.gyp b/net/net.gyp index 64eb95a..b7f6245 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -459,6 +459,7 @@ 'proxy/proxy_service.h', 'proxy/sync_host_resolver_bridge.cc', 'proxy/sync_host_resolver_bridge.h', + 'socket/client_socket.cc', 'socket/client_socket.h', 'socket/client_socket_factory.cc', 'socket/client_socket_factory.h', diff --git a/net/socket/client_socket.cc b/net/socket/client_socket.cc new file mode 100644 index 0000000..c0c62cd --- /dev/null +++ b/net/socket/client_socket.cc @@ -0,0 +1,69 @@ +// Copyright (c) 2010 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 "net/socket/client_socket.h" + +#include "base/histogram.h" + +namespace net { + +ClientSocket::ClientSocket() + : was_ever_connected_(false), + omnibox_speculation_(false), + subresource_speculation_(false), + was_used_to_transmit_data_(false) {} + +ClientSocket::~ClientSocket() { + EmitPreconnectionHistograms(); +} + +void ClientSocket::EmitPreconnectionHistograms() const { + DCHECK(!subresource_speculation_ || !omnibox_speculation_); + // 0 ==> non-speculative, never connected. + // 1 ==> non-speculative never used (but connected). + // 2 ==> non-speculative and used. + // 3 ==> omnibox_speculative never connected. + // 4 ==> omnibox_speculative never used (but connected). + // 5 ==> omnibox_speculative and used. + // 6 ==> subresource_speculative never connected. + // 7 ==> subresource_speculative never used (but connected). + // 8 ==> subresource_speculative and used. + int result; + if (was_used_to_transmit_data_) + result = 2; + else if (was_ever_connected_) + result = 1; + else + result = 0; // Never used, and not really connected. + + if (omnibox_speculation_) + result += 3; + else if (subresource_speculation_) + result += 6; + UMA_HISTOGRAM_ENUMERATION("Net.PreconnectUtilization", result, 9); +} + +void ClientSocket::SetSubresourceSpeculation() { + if (was_used_to_transmit_data_) + return; + subresource_speculation_ = true; +} + +void ClientSocket::SetOmniboxSpeculation() { + if (was_used_to_transmit_data_) + return; + omnibox_speculation_ = true; +} + +void ClientSocket::UpdateConnectivityState(bool is_reused) { + // Record if this connection has every actually connected successfully. + // Note that IsConnected() won't be defined at destruction time, so we need + // to record this data now, while the derived class is present. + was_ever_connected_ |= IsConnected(); + // A socket is_reused only after it has transmitted some data. + was_used_to_transmit_data_ |= is_reused; +} + +} // namespace net + diff --git a/net/socket/client_socket.h b/net/socket/client_socket.h index 29d297e..44ee085 100644 --- a/net/socket/client_socket.h +++ b/net/socket/client_socket.h @@ -15,6 +15,26 @@ class BoundNetLog; class ClientSocket : public Socket { public: + ClientSocket(); + + // Destructor emits statistics for this socket's lifetime. + virtual ~ClientSocket(); + + // Set the annotation to indicate this socket was created for speculative + // reasons. Note that if the socket was used before calling this method, then + // the call will be ignored (no annotation will be added). + void SetSubresourceSpeculation(); + void SetOmniboxSpeculation(); + + // Establish values of was_ever_connected_ and was_used_to_transmit_data_. + // The argument indicates if the socket's state, as reported by a + // ClientSocketHandle::is_reused(), should show that the socket has already + // been used to transmit data. + // This is typically called when a transaction finishes, and + // ClientSocketHandle is being destroyed. Calling at that point it allows us + // to aggregates the impact of that connect job into this instance. + void UpdateConnectivityState(bool is_reused); + // Called to establish a connection. Returns OK if the connection could be // established synchronously. Otherwise, ERR_IO_PENDING is returned and the // given callback will run asynchronously when the connection is established @@ -54,6 +74,24 @@ class ClientSocket : public Socket { // Gets the NetLog for this socket. virtual const BoundNetLog& NetLog() const = 0; + + private: + // Publish histogram to help evaluate preconnection utilization. + void EmitPreconnectionHistograms() const; + + // Indicate if any ClientSocketHandle that used this socket was connected as + // would be indicated by the IsConnected() method. This variable set by a + // ClientSocketHandle before releasing this ClientSocket. + bool was_ever_connected_; + + // Indicate if this socket was first created for speculative use, and identify + // the motivation. + bool omnibox_speculation_; + bool subresource_speculation_; + + // Indicate if this socket was ever used. This state is set by a + // ClientSocketHandle before releasing this ClientSocket. + bool was_used_to_transmit_data_; }; } // namespace net diff --git a/net/socket/client_socket_handle.cc b/net/socket/client_socket_handle.cc index d9ccbd5..ee7c233 100644 --- a/net/socket/client_socket_handle.cc +++ b/net/socket/client_socket_handle.cc @@ -22,6 +22,7 @@ ClientSocketHandle::ClientSocketHandle() ClientSocketHandle::~ClientSocketHandle() { Reset(); + UpdateConnectivityStateForSocket(); } void ClientSocketHandle::Reset() { diff --git a/net/socket/client_socket_handle.h b/net/socket/client_socket_handle.h index adccc89..a3de0d6 100644 --- a/net/socket/client_socket_handle.h +++ b/net/socket/client_socket_handle.h @@ -132,7 +132,10 @@ class ClientSocketHandle { const std::string& group_name() const { return group_name_; } int id() const { return pool_id_; } ClientSocket* socket() { return socket_.get(); } - ClientSocket* release_socket() { return socket_.release(); } + ClientSocket* release_socket() { + UpdateConnectivityStateForSocket(); + return socket_.release(); + } bool is_reused() const { return is_reused_; } base::TimeDelta idle_time() const { return idle_time_; } SocketReuseType reuse_type() const { @@ -174,6 +177,15 @@ class ClientSocketHandle { // Resets the supplemental error state. void ResetErrorState(); + // Update the base class to record things like whether we've ever transmitted + // data, and whether the connection was able to be established. We use this + // data to construct histograms indicating whether a speculative connection + // was ever used, etc., when the ClientSocket is eventually discarded. + void UpdateConnectivityStateForSocket() const { + if (socket_.get()) + socket_->UpdateConnectivityState(is_reused()); + } + bool is_initialized_; scoped_refptr<ClientSocketPool> pool_; scoped_ptr<ClientSocket> socket_; diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h index 30cff70..96c78d5 100644 --- a/net/socket/client_socket_pool_base.h +++ b/net/socket/client_socket_pool_base.h @@ -491,7 +491,7 @@ class ClientSocketPoolBase { const scoped_refptr<SocketParams>& params, const BoundNetLog& net_log) : internal::ClientSocketPoolBaseHelper::Request( - handle, callback, priority, net_log), + handle, callback, priority, net_log), params_(params) {} const scoped_refptr<SocketParams>& params() const { return params_; } @@ -531,9 +531,9 @@ class ClientSocketPoolBase { ConnectJobFactory* connect_job_factory) : histograms_(histograms), helper_(new internal::ClientSocketPoolBaseHelper( - max_sockets, max_sockets_per_group, - unused_idle_socket_timeout, used_idle_socket_timeout, - new ConnectJobFactoryAdaptor(connect_job_factory))) {} + max_sockets, max_sockets_per_group, + unused_idle_socket_timeout, used_idle_socket_timeout, + new ConnectJobFactoryAdaptor(connect_job_factory))) {} virtual ~ClientSocketPoolBase() {} @@ -611,8 +611,7 @@ class ClientSocketPoolBase { typedef typename ClientSocketPoolBase<SocketParams>::ConnectJobFactory ConnectJobFactory; - explicit ConnectJobFactoryAdaptor( - ConnectJobFactory* connect_job_factory) + explicit ConnectJobFactoryAdaptor(ConnectJobFactory* connect_job_factory) : connect_job_factory_(connect_job_factory) {} virtual ~ConnectJobFactoryAdaptor() {} |