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