| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/net/preconnect.h" | 5 #include "chrome/browser/net/preconnect.h" |
| 6 | 6 |
| 7 #include "base/histogram.h" | 7 #include "base/histogram.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
| 10 #include "chrome/browser/profile.h" | 10 #include "chrome/browser/profile.h" |
| 11 #include "chrome/browser/chrome_thread.h" | 11 #include "chrome/browser/chrome_thread.h" |
| 12 #include "chrome/common/net/url_request_context_getter.h" | 12 #include "chrome/common/net/url_request_context_getter.h" |
| 13 #include "net/base/host_port_pair.h" | 13 #include "net/base/host_port_pair.h" |
| 14 #include "net/http/http_network_session.h" | 14 #include "net/http/http_network_session.h" |
| 15 #include "net/http/http_transaction_factory.h" | 15 #include "net/http/http_transaction_factory.h" |
| 16 #include "net/proxy/proxy_service.h" | 16 #include "net/proxy/proxy_service.h" |
| 17 #include "net/url_request/url_request_context.h" | 17 #include "net/url_request/url_request_context.h" |
| 18 | 18 |
| 19 namespace chrome_browser_net { | 19 namespace chrome_browser_net { |
| 20 | 20 |
| 21 // static | 21 // static |
| 22 bool Preconnect::preconnect_despite_proxy_ = false; | 22 bool Preconnect::preconnect_despite_proxy_ = false; |
| 23 | 23 |
| 24 // We will deliberately leak this singular instance, which is used only for | 24 |
| 25 // callbacks. | 25 Preconnect::~Preconnect() { |
| 26 // static | 26 if (!handle_.is_initialized()) |
| 27 Preconnect* Preconnect::callback_instance_; | 27 return; |
| 28 DCHECK(motivation_ == UrlInfo::LEARNED_REFERAL_MOTIVATED || |
| 29 motivation_ == UrlInfo::OMNIBOX_MOTIVATED); |
| 30 if (motivation_ == UrlInfo::OMNIBOX_MOTIVATED) |
| 31 handle_.socket()->SetOmniboxSpeculation(); |
| 32 else |
| 33 handle_.socket()->SetSubresourceSpeculation(); |
| 34 handle_.Reset(); |
| 35 } |
| 28 | 36 |
| 29 // static | 37 // static |
| 30 void Preconnect::PreconnectOnUIThread(const GURL& url, | 38 void Preconnect::PreconnectOnUIThread(const GURL& url, |
| 31 UrlInfo::ResolutionMotivation motivation) { | 39 UrlInfo::ResolutionMotivation motivation) { |
| 32 // Try to do connection warming for this search provider. | |
| 33 URLRequestContextGetter* getter = Profile::GetDefaultRequestContext(); | |
| 34 if (!getter) | |
| 35 return; | |
| 36 // Prewarm connection to Search URL. | 40 // Prewarm connection to Search URL. |
| 37 ChromeThread::PostTask( | 41 ChromeThread::PostTask( |
| 38 ChromeThread::IO, | 42 ChromeThread::IO, |
| 39 FROM_HERE, | 43 FROM_HERE, |
| 40 NewRunnableFunction(Preconnect::PreconnectOnIOThread, url, motivation)); | 44 NewRunnableFunction(Preconnect::PreconnectOnIOThread, url, motivation)); |
| 41 return; | 45 return; |
| 42 } | 46 } |
| 43 | 47 |
| 44 enum ProxyStatus { | 48 enum ProxyStatus { |
| 45 PROXY_STATUS_IGNORED, | 49 PROXY_STATUS_IGNORED, |
| 46 PROXY_UNINITIALIZED, | 50 PROXY_UNINITIALIZED, |
| 47 PROXY_NOT_USED, | 51 PROXY_NOT_USED, |
| 48 PROXY_PAC_RESOLVER, | 52 PROXY_PAC_RESOLVER, |
| 49 PROXY_HAS_RULES, | 53 PROXY_HAS_RULES, |
| 50 PROXY_MAX, | 54 PROXY_MAX, |
| 51 }; | 55 }; |
| 52 | 56 |
| 53 static void HistogramPreconnectStatus(ProxyStatus status) { | 57 static void HistogramPreconnectStatus(ProxyStatus status) { |
| 54 UMA_HISTOGRAM_ENUMERATION("Net.PreconnectProxyStatus", status, PROXY_MAX); | 58 UMA_HISTOGRAM_ENUMERATION("Net.PreconnectProxyStatus", status, PROXY_MAX); |
| 55 } | 59 } |
| 56 | 60 |
| 57 // static | 61 // static |
| 58 void Preconnect::PreconnectOnIOThread(const GURL& url, | 62 void Preconnect::PreconnectOnIOThread(const GURL& url, |
| 59 UrlInfo::ResolutionMotivation motivation) { | 63 UrlInfo::ResolutionMotivation motivation) { |
| 64 scoped_refptr<Preconnect> preconnect = new Preconnect(motivation); |
| 65 // TODO(jar): Should I use PostTask for LearnedSubresources to delay the |
| 66 // preconnection a tad? |
| 67 preconnect->Connect(url); |
| 68 } |
| 69 |
| 70 void Preconnect::Connect(const GURL& url) { |
| 60 URLRequestContextGetter* getter = Profile::GetDefaultRequestContext(); | 71 URLRequestContextGetter* getter = Profile::GetDefaultRequestContext(); |
| 61 if (!getter) | 72 if (!getter) |
| 62 return; | 73 return; |
| 63 if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { | 74 if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { |
| 64 LOG(DFATAL) << "This must be run only on the IO thread."; | 75 LOG(DFATAL) << "This must be run only on the IO thread."; |
| 65 return; | 76 return; |
| 66 } | 77 } |
| 78 |
| 79 AddRef(); // Stay alive until socket is available. |
| 80 |
| 67 URLRequestContext* context = getter->GetURLRequestContext(); | 81 URLRequestContext* context = getter->GetURLRequestContext(); |
| 68 | 82 |
| 69 if (preconnect_despite_proxy_) { | 83 if (preconnect_despite_proxy_) { |
| 70 HistogramPreconnectStatus(PROXY_STATUS_IGNORED); | 84 HistogramPreconnectStatus(PROXY_STATUS_IGNORED); |
| 71 } else { | 85 } else { |
| 72 // Currently we avoid all preconnects if there is a proxy configuration. | 86 // Currently we avoid all preconnects if there is a proxy configuration. |
| 73 net::ProxyService* proxy_service = context->proxy_service(); | 87 net::ProxyService* proxy_service = context->proxy_service(); |
| 74 if (!proxy_service->config_has_been_initialized()) { | 88 if (!proxy_service->config_has_been_initialized()) { |
| 75 HistogramPreconnectStatus(PROXY_UNINITIALIZED); | 89 HistogramPreconnectStatus(PROXY_UNINITIALIZED); |
| 76 } else { | 90 } else { |
| 77 if (proxy_service->config().MayRequirePACResolver()) { | 91 if (proxy_service->config().MayRequirePACResolver()) { |
| 78 HistogramPreconnectStatus(PROXY_PAC_RESOLVER); | 92 HistogramPreconnectStatus(PROXY_PAC_RESOLVER); |
| 79 return; | 93 return; |
| 80 } | 94 } |
| 81 if (!proxy_service->config().proxy_rules().empty()) { | 95 if (!proxy_service->config().proxy_rules().empty()) { |
| 82 HistogramPreconnectStatus(PROXY_HAS_RULES); | 96 HistogramPreconnectStatus(PROXY_HAS_RULES); |
| 83 return; | 97 return; |
| 84 } | 98 } |
| 85 HistogramPreconnectStatus(PROXY_NOT_USED); | 99 HistogramPreconnectStatus(PROXY_NOT_USED); |
| 86 } | 100 } |
| 87 } | 101 } |
| 88 | 102 |
| 89 UMA_HISTOGRAM_ENUMERATION("Net.PreconnectMotivation", motivation, | 103 UMA_HISTOGRAM_ENUMERATION("Net.PreconnectMotivation", motivation_, |
| 90 UrlInfo::MAX_MOTIVATED); | 104 UrlInfo::MAX_MOTIVATED); |
| 91 | 105 |
| 92 net::HttpTransactionFactory* factory = context->http_transaction_factory(); | 106 net::HttpTransactionFactory* factory = context->http_transaction_factory(); |
| 93 net::HttpNetworkSession* session = factory->GetSession(); | 107 net::HttpNetworkSession* session = factory->GetSession(); |
| 94 | 108 |
| 95 net::ClientSocketHandle handle; | |
| 96 if (!callback_instance_) | |
| 97 callback_instance_ = new Preconnect; | |
| 98 | |
| 99 scoped_refptr<net::TCPSocketParams> tcp_params = | 109 scoped_refptr<net::TCPSocketParams> tcp_params = |
| 100 new net::TCPSocketParams(url.host(), url.EffectiveIntPort(), net::LOW, | 110 new net::TCPSocketParams(url.host(), url.EffectiveIntPort(), net::LOW, |
| 101 GURL(), false); | 111 GURL(), false); |
| 102 | 112 |
| 103 net::HostPortPair endpoint(url.host(), url.EffectiveIntPort()); | 113 net::HostPortPair endpoint(url.host(), url.EffectiveIntPort()); |
| 104 std::string group_name = endpoint.ToString(); | 114 std::string group_name = endpoint.ToString(); |
| 105 | 115 |
| 116 // It almost doesn't matter whether we use net::LOWEST or net::HIGHEST |
| 117 // priority here, as we won't make a request, and will surrender the created |
| 118 // socket to the pool as soon as we can. However, we would like to mark the |
| 119 // speculative socket as such, and IF we use a net::LOWEST priority, and if |
| 120 // a navigation asked for a socket (after us) then it would get our socket, |
| 121 // and we'd get its later-arriving socket, which might make us record that |
| 122 // the speculation didn't help :-/. By using net::HIGHEST, we ensure that |
| 123 // a socket is given to us if "we asked first" and this allows us to mark it |
| 124 // as speculative, and better detect stats (if it gets used). |
| 125 // TODO(jar): histogram to see how often we accidentally use a previously- |
| 126 // unused socket, when a previously used socket was available. |
| 127 net::RequestPriority priority = net::HIGHEST; |
| 128 |
| 106 if (url.SchemeIs("https")) { | 129 if (url.SchemeIs("https")) { |
| 107 group_name = StringPrintf("ssl/%s", group_name.c_str()); | 130 group_name = StringPrintf("ssl/%s", group_name.c_str()); |
| 108 | 131 |
| 109 net::SSLConfig ssl_config; | 132 net::SSLConfig ssl_config; |
| 110 session->ssl_config_service()->GetSSLConfig(&ssl_config); | 133 session->ssl_config_service()->GetSSLConfig(&ssl_config); |
| 111 // All preconnects should be for main pages. | 134 // All preconnects should be for main pages. |
| 112 ssl_config.verify_ev_cert = true; | 135 ssl_config.verify_ev_cert = true; |
| 113 | 136 |
| 114 scoped_refptr<net::SSLSocketParams> ssl_params = | 137 scoped_refptr<net::SSLSocketParams> ssl_params = |
| 115 new net::SSLSocketParams(tcp_params, NULL, NULL, | 138 new net::SSLSocketParams(tcp_params, NULL, NULL, |
| 116 net::ProxyServer::SCHEME_DIRECT, | 139 net::ProxyServer::SCHEME_DIRECT, |
| 117 url.HostNoBrackets(), ssl_config, | 140 url.HostNoBrackets(), ssl_config, |
| 118 0, false, false); | 141 0, false, false); |
| 119 | 142 |
| 120 const scoped_refptr<net::SSLClientSocketPool>& pool = | 143 const scoped_refptr<net::SSLClientSocketPool>& pool = |
| 121 session->ssl_socket_pool(); | 144 session->ssl_socket_pool(); |
| 122 | 145 |
| 123 handle.Init(group_name, ssl_params, net::LOWEST, callback_instance_, pool, | 146 handle_.Init(group_name, ssl_params, priority, this, pool, |
| 124 net::BoundNetLog()); | 147 net::BoundNetLog()); |
| 125 handle.Reset(); | |
| 126 return; | 148 return; |
| 127 } | 149 } |
| 128 | 150 |
| 129 const scoped_refptr<net::TCPClientSocketPool>& pool = | 151 const scoped_refptr<net::TCPClientSocketPool>& pool = |
| 130 session->tcp_socket_pool(); | 152 session->tcp_socket_pool(); |
| 131 handle.Init(group_name, tcp_params, net::LOWEST, callback_instance_, pool, | 153 handle_.Init(group_name, tcp_params, priority, this, pool, |
| 132 net::BoundNetLog()); | 154 net::BoundNetLog()); |
| 133 handle.Reset(); | |
| 134 } | 155 } |
| 135 | 156 |
| 136 void Preconnect::RunWithParams(const Tuple1<int>& params) { | 157 void Preconnect::RunWithParams(const Tuple1<int>& params) { |
| 137 // This will rarely be called, as we reset the connection just after creating. | 158 if (params.a < 0 && handle_.socket()) |
| 138 NOTREACHED(); | 159 handle_.socket()->Disconnect(); |
| 160 Release(); |
| 139 } | 161 } |
| 140 } // chrome_browser_net | 162 } // chrome_browser_net |
| OLD | NEW |