Ignore non-active fullscreen windows for shelf state.
[chromium-blink-merge.git] / net / socket / transport_client_socket_pool.cc
blobec56dcba65cfa0ebb59101cb0b29f00f5c35bb87
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/socket/transport_client_socket_pool.h"
7 #include <algorithm>
9 #include "base/compiler_specific.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_util.h"
15 #include "base/synchronization/lock.h"
16 #include "base/time/time.h"
17 #include "base/values.h"
18 #include "net/base/ip_endpoint.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/net_log.h"
21 #include "net/socket/client_socket_factory.h"
22 #include "net/socket/client_socket_handle.h"
23 #include "net/socket/client_socket_pool_base.h"
24 #include "net/socket/socket_net_log_params.h"
25 #include "net/socket/tcp_client_socket.h"
27 using base::TimeDelta;
29 namespace net {
31 // TODO(willchan): Base this off RTT instead of statically setting it. Note we
32 // choose a timeout that is different from the backup connect job timer so they
33 // don't synchronize.
34 const int TransportConnectJob::kIPv6FallbackTimerInMs = 300;
36 namespace {
38 // Returns true iff all addresses in |list| are in the IPv6 family.
39 bool AddressListOnlyContainsIPv6(const AddressList& list) {
40 DCHECK(!list.empty());
41 for (AddressList::const_iterator iter = list.begin(); iter != list.end();
42 ++iter) {
43 if (iter->GetFamily() != ADDRESS_FAMILY_IPV6)
44 return false;
46 return true;
49 } // namespace
51 // This lock protects |g_last_connect_time|.
52 static base::LazyInstance<base::Lock>::Leaky
53 g_last_connect_time_lock = LAZY_INSTANCE_INITIALIZER;
55 // |g_last_connect_time| has the last time a connect() call is made.
56 static base::LazyInstance<base::TimeTicks>::Leaky
57 g_last_connect_time = LAZY_INSTANCE_INITIALIZER;
59 TransportSocketParams::TransportSocketParams(
60 const HostPortPair& host_port_pair,
61 bool disable_resolver_cache,
62 bool ignore_limits,
63 const OnHostResolutionCallback& host_resolution_callback)
64 : destination_(host_port_pair),
65 ignore_limits_(ignore_limits),
66 host_resolution_callback_(host_resolution_callback) {
67 if (disable_resolver_cache)
68 destination_.set_allow_cached_response(false);
71 TransportSocketParams::~TransportSocketParams() {}
73 // TransportConnectJobs will time out after this many seconds. Note this is
74 // the total time, including both host resolution and TCP connect() times.
76 // TODO(eroman): The use of this constant needs to be re-evaluated. The time
77 // needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since
78 // the address list may contain many alternatives, and most of those may
79 // timeout. Even worse, the per-connect timeout threshold varies greatly
80 // between systems (anywhere from 20 seconds to 190 seconds).
81 // See comment #12 at http://crbug.com/23364 for specifics.
82 static const int kTransportConnectJobTimeoutInSeconds = 240; // 4 minutes.
84 TransportConnectJob::TransportConnectJob(
85 const std::string& group_name,
86 RequestPriority priority,
87 const scoped_refptr<TransportSocketParams>& params,
88 base::TimeDelta timeout_duration,
89 ClientSocketFactory* client_socket_factory,
90 HostResolver* host_resolver,
91 Delegate* delegate,
92 NetLog* net_log)
93 : ConnectJob(group_name, timeout_duration, priority, delegate,
94 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
95 params_(params),
96 client_socket_factory_(client_socket_factory),
97 resolver_(host_resolver),
98 next_state_(STATE_NONE),
99 interval_between_connects_(CONNECT_INTERVAL_GT_20MS) {
102 TransportConnectJob::~TransportConnectJob() {
103 // We don't worry about cancelling the host resolution and TCP connect, since
104 // ~SingleRequestHostResolver and ~StreamSocket will take care of it.
107 LoadState TransportConnectJob::GetLoadState() const {
108 switch (next_state_) {
109 case STATE_RESOLVE_HOST:
110 case STATE_RESOLVE_HOST_COMPLETE:
111 return LOAD_STATE_RESOLVING_HOST;
112 case STATE_TRANSPORT_CONNECT:
113 case STATE_TRANSPORT_CONNECT_COMPLETE:
114 return LOAD_STATE_CONNECTING;
115 case STATE_NONE:
116 return LOAD_STATE_IDLE;
118 NOTREACHED();
119 return LOAD_STATE_IDLE;
122 // static
123 void TransportConnectJob::MakeAddressListStartWithIPv4(AddressList* list) {
124 for (AddressList::iterator i = list->begin(); i != list->end(); ++i) {
125 if (i->GetFamily() == ADDRESS_FAMILY_IPV4) {
126 std::rotate(list->begin(), i, list->end());
127 break;
132 void TransportConnectJob::OnIOComplete(int result) {
133 int rv = DoLoop(result);
134 if (rv != ERR_IO_PENDING)
135 NotifyDelegateOfCompletion(rv); // Deletes |this|
138 int TransportConnectJob::DoLoop(int result) {
139 DCHECK_NE(next_state_, STATE_NONE);
141 int rv = result;
142 do {
143 State state = next_state_;
144 next_state_ = STATE_NONE;
145 switch (state) {
146 case STATE_RESOLVE_HOST:
147 DCHECK_EQ(OK, rv);
148 rv = DoResolveHost();
149 break;
150 case STATE_RESOLVE_HOST_COMPLETE:
151 rv = DoResolveHostComplete(rv);
152 break;
153 case STATE_TRANSPORT_CONNECT:
154 DCHECK_EQ(OK, rv);
155 rv = DoTransportConnect();
156 break;
157 case STATE_TRANSPORT_CONNECT_COMPLETE:
158 rv = DoTransportConnectComplete(rv);
159 break;
160 default:
161 NOTREACHED();
162 rv = ERR_FAILED;
163 break;
165 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
167 return rv;
170 int TransportConnectJob::DoResolveHost() {
171 next_state_ = STATE_RESOLVE_HOST_COMPLETE;
172 connect_timing_.dns_start = base::TimeTicks::Now();
174 return resolver_.Resolve(
175 params_->destination(),
176 priority(),
177 &addresses_,
178 base::Bind(&TransportConnectJob::OnIOComplete, base::Unretained(this)),
179 net_log());
182 int TransportConnectJob::DoResolveHostComplete(int result) {
183 connect_timing_.dns_end = base::TimeTicks::Now();
184 // Overwrite connection start time, since for connections that do not go
185 // through proxies, |connect_start| should not include dns lookup time.
186 connect_timing_.connect_start = connect_timing_.dns_end;
188 if (result == OK) {
189 // Invoke callback, and abort if it fails.
190 if (!params_->host_resolution_callback().is_null())
191 result = params_->host_resolution_callback().Run(addresses_, net_log());
193 if (result == OK)
194 next_state_ = STATE_TRANSPORT_CONNECT;
196 return result;
199 int TransportConnectJob::DoTransportConnect() {
200 base::TimeTicks now = base::TimeTicks::Now();
201 base::TimeTicks last_connect_time;
203 base::AutoLock lock(g_last_connect_time_lock.Get());
204 last_connect_time = g_last_connect_time.Get();
205 *g_last_connect_time.Pointer() = now;
207 if (last_connect_time.is_null()) {
208 interval_between_connects_ = CONNECT_INTERVAL_GT_20MS;
209 } else {
210 int64 interval = (now - last_connect_time).InMilliseconds();
211 if (interval <= 10)
212 interval_between_connects_ = CONNECT_INTERVAL_LE_10MS;
213 else if (interval <= 20)
214 interval_between_connects_ = CONNECT_INTERVAL_LE_20MS;
215 else
216 interval_between_connects_ = CONNECT_INTERVAL_GT_20MS;
219 next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
220 transport_socket_ = client_socket_factory_->CreateTransportClientSocket(
221 addresses_, net_log().net_log(), net_log().source());
222 int rv = transport_socket_->Connect(
223 base::Bind(&TransportConnectJob::OnIOComplete, base::Unretained(this)));
224 if (rv == ERR_IO_PENDING &&
225 addresses_.front().GetFamily() == ADDRESS_FAMILY_IPV6 &&
226 !AddressListOnlyContainsIPv6(addresses_)) {
227 fallback_timer_.Start(FROM_HERE,
228 base::TimeDelta::FromMilliseconds(kIPv6FallbackTimerInMs),
229 this, &TransportConnectJob::DoIPv6FallbackTransportConnect);
231 return rv;
234 int TransportConnectJob::DoTransportConnectComplete(int result) {
235 if (result == OK) {
236 bool is_ipv4 = addresses_.front().GetFamily() == ADDRESS_FAMILY_IPV4;
237 DCHECK(!connect_timing_.connect_start.is_null());
238 DCHECK(!connect_timing_.dns_start.is_null());
239 base::TimeTicks now = base::TimeTicks::Now();
240 base::TimeDelta total_duration = now - connect_timing_.dns_start;
241 UMA_HISTOGRAM_CUSTOM_TIMES(
242 "Net.DNS_Resolution_And_TCP_Connection_Latency2",
243 total_duration,
244 base::TimeDelta::FromMilliseconds(1),
245 base::TimeDelta::FromMinutes(10),
246 100);
248 base::TimeDelta connect_duration = now - connect_timing_.connect_start;
249 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
250 connect_duration,
251 base::TimeDelta::FromMilliseconds(1),
252 base::TimeDelta::FromMinutes(10),
253 100);
255 switch (interval_between_connects_) {
256 case CONNECT_INTERVAL_LE_10MS:
257 UMA_HISTOGRAM_CUSTOM_TIMES(
258 "Net.TCP_Connection_Latency_Interval_LessThanOrEqual_10ms",
259 connect_duration,
260 base::TimeDelta::FromMilliseconds(1),
261 base::TimeDelta::FromMinutes(10),
262 100);
263 break;
264 case CONNECT_INTERVAL_LE_20MS:
265 UMA_HISTOGRAM_CUSTOM_TIMES(
266 "Net.TCP_Connection_Latency_Interval_LessThanOrEqual_20ms",
267 connect_duration,
268 base::TimeDelta::FromMilliseconds(1),
269 base::TimeDelta::FromMinutes(10),
270 100);
271 break;
272 case CONNECT_INTERVAL_GT_20MS:
273 UMA_HISTOGRAM_CUSTOM_TIMES(
274 "Net.TCP_Connection_Latency_Interval_GreaterThan_20ms",
275 connect_duration,
276 base::TimeDelta::FromMilliseconds(1),
277 base::TimeDelta::FromMinutes(10),
278 100);
279 break;
280 default:
281 NOTREACHED();
282 break;
285 if (is_ipv4) {
286 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_No_Race",
287 connect_duration,
288 base::TimeDelta::FromMilliseconds(1),
289 base::TimeDelta::FromMinutes(10),
290 100);
291 } else {
292 if (AddressListOnlyContainsIPv6(addresses_)) {
293 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Solo",
294 connect_duration,
295 base::TimeDelta::FromMilliseconds(1),
296 base::TimeDelta::FromMinutes(10),
297 100);
298 } else {
299 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Raceable",
300 connect_duration,
301 base::TimeDelta::FromMilliseconds(1),
302 base::TimeDelta::FromMinutes(10),
303 100);
306 SetSocket(transport_socket_.Pass());
307 fallback_timer_.Stop();
308 } else {
309 // Be a bit paranoid and kill off the fallback members to prevent reuse.
310 fallback_transport_socket_.reset();
311 fallback_addresses_.reset();
314 return result;
317 void TransportConnectJob::DoIPv6FallbackTransportConnect() {
318 // The timer should only fire while we're waiting for the main connect to
319 // succeed.
320 if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
321 NOTREACHED();
322 return;
325 DCHECK(!fallback_transport_socket_.get());
326 DCHECK(!fallback_addresses_.get());
328 fallback_addresses_.reset(new AddressList(addresses_));
329 MakeAddressListStartWithIPv4(fallback_addresses_.get());
330 fallback_transport_socket_ =
331 client_socket_factory_->CreateTransportClientSocket(
332 *fallback_addresses_, net_log().net_log(), net_log().source());
333 fallback_connect_start_time_ = base::TimeTicks::Now();
334 int rv = fallback_transport_socket_->Connect(
335 base::Bind(
336 &TransportConnectJob::DoIPv6FallbackTransportConnectComplete,
337 base::Unretained(this)));
338 if (rv != ERR_IO_PENDING)
339 DoIPv6FallbackTransportConnectComplete(rv);
342 void TransportConnectJob::DoIPv6FallbackTransportConnectComplete(int result) {
343 // This should only happen when we're waiting for the main connect to succeed.
344 if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
345 NOTREACHED();
346 return;
349 DCHECK_NE(ERR_IO_PENDING, result);
350 DCHECK(fallback_transport_socket_.get());
351 DCHECK(fallback_addresses_.get());
353 if (result == OK) {
354 DCHECK(!fallback_connect_start_time_.is_null());
355 DCHECK(!connect_timing_.dns_start.is_null());
356 base::TimeTicks now = base::TimeTicks::Now();
357 base::TimeDelta total_duration = now - connect_timing_.dns_start;
358 UMA_HISTOGRAM_CUSTOM_TIMES(
359 "Net.DNS_Resolution_And_TCP_Connection_Latency2",
360 total_duration,
361 base::TimeDelta::FromMilliseconds(1),
362 base::TimeDelta::FromMinutes(10),
363 100);
365 base::TimeDelta connect_duration = now - fallback_connect_start_time_;
366 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
367 connect_duration,
368 base::TimeDelta::FromMilliseconds(1),
369 base::TimeDelta::FromMinutes(10),
370 100);
372 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_Wins_Race",
373 connect_duration,
374 base::TimeDelta::FromMilliseconds(1),
375 base::TimeDelta::FromMinutes(10),
376 100);
377 SetSocket(fallback_transport_socket_.Pass());
378 next_state_ = STATE_NONE;
379 transport_socket_.reset();
380 } else {
381 // Be a bit paranoid and kill off the fallback members to prevent reuse.
382 fallback_transport_socket_.reset();
383 fallback_addresses_.reset();
385 NotifyDelegateOfCompletion(result); // Deletes |this|
388 int TransportConnectJob::ConnectInternal() {
389 next_state_ = STATE_RESOLVE_HOST;
390 return DoLoop(OK);
393 scoped_ptr<ConnectJob>
394 TransportClientSocketPool::TransportConnectJobFactory::NewConnectJob(
395 const std::string& group_name,
396 const PoolBase::Request& request,
397 ConnectJob::Delegate* delegate) const {
398 return scoped_ptr<ConnectJob>(
399 new TransportConnectJob(group_name,
400 request.priority(),
401 request.params(),
402 ConnectionTimeout(),
403 client_socket_factory_,
404 host_resolver_,
405 delegate,
406 net_log_));
409 base::TimeDelta
410 TransportClientSocketPool::TransportConnectJobFactory::ConnectionTimeout()
411 const {
412 return base::TimeDelta::FromSeconds(kTransportConnectJobTimeoutInSeconds);
415 TransportClientSocketPool::TransportClientSocketPool(
416 int max_sockets,
417 int max_sockets_per_group,
418 ClientSocketPoolHistograms* histograms,
419 HostResolver* host_resolver,
420 ClientSocketFactory* client_socket_factory,
421 NetLog* net_log)
422 : base_(NULL, max_sockets, max_sockets_per_group, histograms,
423 ClientSocketPool::unused_idle_socket_timeout(),
424 ClientSocketPool::used_idle_socket_timeout(),
425 new TransportConnectJobFactory(client_socket_factory,
426 host_resolver, net_log)) {
427 base_.EnableConnectBackupJobs();
430 TransportClientSocketPool::~TransportClientSocketPool() {}
432 int TransportClientSocketPool::RequestSocket(
433 const std::string& group_name,
434 const void* params,
435 RequestPriority priority,
436 ClientSocketHandle* handle,
437 const CompletionCallback& callback,
438 const BoundNetLog& net_log) {
439 const scoped_refptr<TransportSocketParams>* casted_params =
440 static_cast<const scoped_refptr<TransportSocketParams>*>(params);
442 if (net_log.IsLoggingAllEvents()) {
443 // TODO(eroman): Split out the host and port parameters.
444 net_log.AddEvent(
445 NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
446 CreateNetLogHostPortPairCallback(
447 &casted_params->get()->destination().host_port_pair()));
450 return base_.RequestSocket(group_name, *casted_params, priority, handle,
451 callback, net_log);
454 void TransportClientSocketPool::RequestSockets(
455 const std::string& group_name,
456 const void* params,
457 int num_sockets,
458 const BoundNetLog& net_log) {
459 const scoped_refptr<TransportSocketParams>* casted_params =
460 static_cast<const scoped_refptr<TransportSocketParams>*>(params);
462 if (net_log.IsLoggingAllEvents()) {
463 // TODO(eroman): Split out the host and port parameters.
464 net_log.AddEvent(
465 NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS,
466 CreateNetLogHostPortPairCallback(
467 &casted_params->get()->destination().host_port_pair()));
470 base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
473 void TransportClientSocketPool::CancelRequest(
474 const std::string& group_name,
475 ClientSocketHandle* handle) {
476 base_.CancelRequest(group_name, handle);
479 void TransportClientSocketPool::ReleaseSocket(
480 const std::string& group_name,
481 scoped_ptr<StreamSocket> socket,
482 int id) {
483 base_.ReleaseSocket(group_name, socket.Pass(), id);
486 void TransportClientSocketPool::FlushWithError(int error) {
487 base_.FlushWithError(error);
490 void TransportClientSocketPool::CloseIdleSockets() {
491 base_.CloseIdleSockets();
494 int TransportClientSocketPool::IdleSocketCount() const {
495 return base_.idle_socket_count();
498 int TransportClientSocketPool::IdleSocketCountInGroup(
499 const std::string& group_name) const {
500 return base_.IdleSocketCountInGroup(group_name);
503 LoadState TransportClientSocketPool::GetLoadState(
504 const std::string& group_name, const ClientSocketHandle* handle) const {
505 return base_.GetLoadState(group_name, handle);
508 base::DictionaryValue* TransportClientSocketPool::GetInfoAsValue(
509 const std::string& name,
510 const std::string& type,
511 bool include_nested_pools) const {
512 return base_.GetInfoAsValue(name, type);
515 base::TimeDelta TransportClientSocketPool::ConnectionTimeout() const {
516 return base_.ConnectionTimeout();
519 ClientSocketPoolHistograms* TransportClientSocketPool::histograms() const {
520 return base_.histograms();
523 bool TransportClientSocketPool::IsStalled() const {
524 return base_.IsStalled();
527 void TransportClientSocketPool::AddHigherLayeredPool(
528 HigherLayeredPool* higher_pool) {
529 base_.AddHigherLayeredPool(higher_pool);
532 void TransportClientSocketPool::RemoveHigherLayeredPool(
533 HigherLayeredPool* higher_pool) {
534 base_.RemoveHigherLayeredPool(higher_pool);
537 } // namespace net