tcp_client_socket_pool.cc revision 7b9ca917061470268bf3395c8925d4b9cc52d8e1
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 3// found in the LICENSE file. 4 5#include "net/socket/tcp_client_socket_pool.h" 6 7#include "base/compiler_specific.h" 8#include "base/logging.h" 9#include "base/message_loop.h" 10#include "base/metrics/histogram.h" 11#include "base/string_util.h" 12#include "base/time.h" 13#include "net/base/net_log.h" 14#include "net/base/net_errors.h" 15#include "net/socket/client_socket_factory.h" 16#include "net/socket/client_socket_handle.h" 17#include "net/socket/client_socket_pool_base.h" 18#include "net/socket/tcp_client_socket.h" 19 20using base::TimeDelta; 21 22namespace net { 23 24TCPSocketParams::TCPSocketParams(const HostPortPair& host_port_pair, 25 RequestPriority priority, const GURL& referrer, 26 bool disable_resolver_cache, bool ignore_limits) 27 : destination_(host_port_pair) 28#ifdef ANDROID 29 , ignore_limits_(ignore_limits) 30#endif 31 { 32 Initialize(priority, referrer, disable_resolver_cache); 33} 34 35// TODO(willchan): Update all unittests so we don't need this. 36TCPSocketParams::TCPSocketParams(const std::string& host, int port, 37 RequestPriority priority, const GURL& referrer, 38 bool disable_resolver_cache) 39 : destination_(HostPortPair(host, port)) 40#ifdef ANDROID 41 , ignore_limits_(false) 42#endif 43 { 44 Initialize(priority, referrer, disable_resolver_cache); 45} 46 47TCPSocketParams::~TCPSocketParams() {} 48 49// TCPConnectJobs will time out after this many seconds. Note this is the total 50// time, including both host resolution and TCP connect() times. 51// 52// TODO(eroman): The use of this constant needs to be re-evaluated. The time 53// needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since 54// the address list may contain many alternatives, and most of those may 55// timeout. Even worse, the per-connect timeout threshold varies greatly 56// between systems (anywhere from 20 seconds to 190 seconds). 57// See comment #12 at http://crbug.com/23364 for specifics. 58static const int kTCPConnectJobTimeoutInSeconds = 240; // 4 minutes. 59 60TCPConnectJob::TCPConnectJob( 61 const std::string& group_name, 62 const scoped_refptr<TCPSocketParams>& params, 63 base::TimeDelta timeout_duration, 64 ClientSocketFactory* client_socket_factory, 65 HostResolver* host_resolver, 66 Delegate* delegate, 67 NetLog* net_log) 68 : ConnectJob(group_name, timeout_duration, delegate, 69 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)), 70 params_(params), 71 client_socket_factory_(client_socket_factory), 72 ALLOW_THIS_IN_INITIALIZER_LIST( 73 callback_(this, 74 &TCPConnectJob::OnIOComplete)), 75 resolver_(host_resolver) {} 76 77TCPConnectJob::~TCPConnectJob() { 78 // We don't worry about cancelling the host resolution and TCP connect, since 79 // ~SingleRequestHostResolver and ~ClientSocket will take care of it. 80} 81 82LoadState TCPConnectJob::GetLoadState() const { 83 switch (next_state_) { 84 case STATE_RESOLVE_HOST: 85 case STATE_RESOLVE_HOST_COMPLETE: 86 return LOAD_STATE_RESOLVING_HOST; 87 case STATE_TCP_CONNECT: 88 case STATE_TCP_CONNECT_COMPLETE: 89 return LOAD_STATE_CONNECTING; 90 default: 91 NOTREACHED(); 92 return LOAD_STATE_IDLE; 93 } 94} 95 96int TCPConnectJob::ConnectInternal() { 97 next_state_ = STATE_RESOLVE_HOST; 98 start_time_ = base::TimeTicks::Now(); 99 return DoLoop(OK); 100} 101 102void TCPConnectJob::OnIOComplete(int result) { 103 int rv = DoLoop(result); 104 if (rv != ERR_IO_PENDING) 105 NotifyDelegateOfCompletion(rv); // Deletes |this| 106} 107 108int TCPConnectJob::DoLoop(int result) { 109 DCHECK_NE(next_state_, STATE_NONE); 110 111 int rv = result; 112 do { 113 State state = next_state_; 114 next_state_ = STATE_NONE; 115 switch (state) { 116 case STATE_RESOLVE_HOST: 117 DCHECK_EQ(OK, rv); 118 rv = DoResolveHost(); 119 break; 120 case STATE_RESOLVE_HOST_COMPLETE: 121 rv = DoResolveHostComplete(rv); 122 break; 123 case STATE_TCP_CONNECT: 124 DCHECK_EQ(OK, rv); 125 rv = DoTCPConnect(); 126 break; 127 case STATE_TCP_CONNECT_COMPLETE: 128 rv = DoTCPConnectComplete(rv); 129 break; 130 default: 131 NOTREACHED(); 132 rv = ERR_FAILED; 133 break; 134 } 135 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); 136 137 return rv; 138} 139 140int TCPConnectJob::DoResolveHost() { 141 next_state_ = STATE_RESOLVE_HOST_COMPLETE; 142 return resolver_.Resolve(params_->destination(), &addresses_, &callback_, 143 net_log()); 144} 145 146int TCPConnectJob::DoResolveHostComplete(int result) { 147 if (result == OK) 148 next_state_ = STATE_TCP_CONNECT; 149 return result; 150} 151 152int TCPConnectJob::DoTCPConnect() { 153 next_state_ = STATE_TCP_CONNECT_COMPLETE; 154 set_socket(client_socket_factory_->CreateTCPClientSocket( 155 addresses_, net_log().net_log(), net_log().source())); 156 connect_start_time_ = base::TimeTicks::Now(); 157 return socket()->Connect(&callback_, 158#ifdef ANDROID 159 params_->ignore_limits() 160#endif 161 ); 162} 163 164int TCPConnectJob::DoTCPConnectComplete(int result) { 165 if (result == OK) { 166 DCHECK(connect_start_time_ != base::TimeTicks()); 167 DCHECK(start_time_ != base::TimeTicks()); 168 base::TimeTicks now = base::TimeTicks::Now(); 169 base::TimeDelta total_duration = now - start_time_; 170 UMA_HISTOGRAM_CUSTOM_TIMES( 171 "Net.DNS_Resolution_And_TCP_Connection_Latency2", 172 total_duration, 173 base::TimeDelta::FromMilliseconds(1), 174 base::TimeDelta::FromMinutes(10), 175 100); 176 177 base::TimeDelta connect_duration = now - connect_start_time_; 178 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency", 179 connect_duration, 180 base::TimeDelta::FromMilliseconds(1), 181 base::TimeDelta::FromMinutes(10), 182 100); 183 } else { 184 // Delete the socket on error. 185 set_socket(NULL); 186 } 187 188 return result; 189} 190 191ConnectJob* TCPClientSocketPool::TCPConnectJobFactory::NewConnectJob( 192 const std::string& group_name, 193 const PoolBase::Request& request, 194 ConnectJob::Delegate* delegate) const { 195 return new TCPConnectJob(group_name, request.params(), ConnectionTimeout(), 196 client_socket_factory_, host_resolver_, delegate, 197 net_log_); 198} 199 200base::TimeDelta 201 TCPClientSocketPool::TCPConnectJobFactory::ConnectionTimeout() const { 202 return base::TimeDelta::FromSeconds(kTCPConnectJobTimeoutInSeconds); 203} 204 205TCPClientSocketPool::TCPClientSocketPool( 206 int max_sockets, 207 int max_sockets_per_group, 208 ClientSocketPoolHistograms* histograms, 209 HostResolver* host_resolver, 210 ClientSocketFactory* client_socket_factory, 211 NetLog* net_log) 212 : base_(max_sockets, max_sockets_per_group, histograms, 213 base::TimeDelta::FromSeconds( 214 ClientSocketPool::unused_idle_socket_timeout()), 215 base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout), 216 new TCPConnectJobFactory(client_socket_factory, 217 host_resolver, net_log)) { 218 base_.EnableConnectBackupJobs(); 219} 220 221TCPClientSocketPool::~TCPClientSocketPool() {} 222 223int TCPClientSocketPool::RequestSocket( 224 const std::string& group_name, 225 const void* params, 226 RequestPriority priority, 227 ClientSocketHandle* handle, 228 CompletionCallback* callback, 229 const BoundNetLog& net_log) { 230 const scoped_refptr<TCPSocketParams>* casted_params = 231 static_cast<const scoped_refptr<TCPSocketParams>*>(params); 232 233 if (net_log.IsLoggingAllEvents()) { 234 // TODO(eroman): Split out the host and port parameters. 235 net_log.AddEvent( 236 NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET, 237 make_scoped_refptr(new NetLogStringParameter( 238 "host_and_port", 239 casted_params->get()->destination().host_port_pair().ToString()))); 240 } 241 242 return base_.RequestSocket(group_name, *casted_params, priority, handle, 243 callback, net_log); 244} 245 246void TCPClientSocketPool::RequestSockets( 247 const std::string& group_name, 248 const void* params, 249 int num_sockets, 250 const BoundNetLog& net_log) { 251 const scoped_refptr<TCPSocketParams>* casted_params = 252 static_cast<const scoped_refptr<TCPSocketParams>*>(params); 253 254 if (net_log.IsLoggingAllEvents()) { 255 // TODO(eroman): Split out the host and port parameters. 256 net_log.AddEvent( 257 NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS, 258 make_scoped_refptr(new NetLogStringParameter( 259 "host_and_port", 260 casted_params->get()->destination().host_port_pair().ToString()))); 261 } 262 263 base_.RequestSockets(group_name, *casted_params, num_sockets, net_log); 264} 265 266void TCPClientSocketPool::CancelRequest( 267 const std::string& group_name, 268 ClientSocketHandle* handle) { 269 base_.CancelRequest(group_name, handle); 270} 271 272void TCPClientSocketPool::ReleaseSocket( 273 const std::string& group_name, 274 ClientSocket* socket, 275 int id) { 276 base_.ReleaseSocket(group_name, socket, id); 277} 278 279void TCPClientSocketPool::Flush() { 280 base_.Flush(); 281} 282 283void TCPClientSocketPool::CloseIdleSockets() { 284 base_.CloseIdleSockets(); 285} 286 287int TCPClientSocketPool::IdleSocketCountInGroup( 288 const std::string& group_name) const { 289 return base_.IdleSocketCountInGroup(group_name); 290} 291 292LoadState TCPClientSocketPool::GetLoadState( 293 const std::string& group_name, const ClientSocketHandle* handle) const { 294 return base_.GetLoadState(group_name, handle); 295} 296 297} // namespace net 298