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 |