1 // Copyright (c) 2006-2008 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/string_util.h" 11 #include "base/time.h" 12 #include "net/base/load_log.h" 13 #include "net/base/net_errors.h" 14 #include "net/socket/client_socket_factory.h" 15 #include "net/socket/client_socket_handle.h" 16 #include "net/socket/client_socket_pool_base.h" 17 #include "net/socket/tcp_client_socket.h" 18 19 using base::TimeDelta; 20 21 namespace net { 22 23 // TCPConnectJobs will time out after this many seconds. Note this is the total 24 // time, including both host resolution and TCP connect() times. 25 // 26 // TODO(eroman): The use of this constant needs to be re-evaluated. The time 27 // needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since 28 // the address list may contain many alternatives, and most of those may 29 // timeout. Even worse, the per-connect timeout threshold varies greatly 30 // between systems (anywhere from 20 seconds to 190 seconds). 31 // See comment #12 at http://crbug.com/23364 for specifics. 32 static const int kTCPConnectJobTimeoutInSeconds = 240; // 4 minutes. 33 34 TCPConnectJob::TCPConnectJob( 35 const std::string& group_name, 36 const HostResolver::RequestInfo& resolve_info, 37 base::TimeDelta timeout_duration, 38 ClientSocketFactory* client_socket_factory, 39 HostResolver* host_resolver, 40 Delegate* delegate, 41 LoadLog* load_log) 42 : ConnectJob(group_name, timeout_duration, delegate, load_log), 43 resolve_info_(resolve_info), 44 client_socket_factory_(client_socket_factory), 45 ALLOW_THIS_IN_INITIALIZER_LIST( 46 callback_(this, 47 &TCPConnectJob::OnIOComplete)), 48 resolver_(host_resolver) {} 49 50 TCPConnectJob::~TCPConnectJob() { 51 // We don't worry about cancelling the host resolution and TCP connect, since 52 // ~SingleRequestHostResolver and ~ClientSocket will take care of it. 53 } 54 55 LoadState TCPConnectJob::GetLoadState() const { 56 switch (next_state_) { 57 case kStateResolveHost: 58 case kStateResolveHostComplete: 59 return LOAD_STATE_RESOLVING_HOST; 60 case kStateTCPConnect: 61 case kStateTCPConnectComplete: 62 return LOAD_STATE_CONNECTING; 63 default: 64 NOTREACHED(); 65 return LOAD_STATE_IDLE; 66 } 67 } 68 69 int TCPConnectJob::ConnectInternal() { 70 next_state_ = kStateResolveHost; 71 start_time_ = base::TimeTicks::Now(); 72 return DoLoop(OK); 73 } 74 75 void TCPConnectJob::OnIOComplete(int result) { 76 int rv = DoLoop(result); 77 if (rv != ERR_IO_PENDING) 78 NotifyDelegateOfCompletion(rv); // Deletes |this| 79 } 80 81 int TCPConnectJob::DoLoop(int result) { 82 DCHECK_NE(next_state_, kStateNone); 83 84 int rv = result; 85 do { 86 State state = next_state_; 87 next_state_ = kStateNone; 88 switch (state) { 89 case kStateResolveHost: 90 DCHECK_EQ(OK, rv); 91 rv = DoResolveHost(); 92 break; 93 case kStateResolveHostComplete: 94 rv = DoResolveHostComplete(rv); 95 break; 96 case kStateTCPConnect: 97 DCHECK_EQ(OK, rv); 98 rv = DoTCPConnect(); 99 break; 100 case kStateTCPConnectComplete: 101 rv = DoTCPConnectComplete(rv); 102 break; 103 default: 104 NOTREACHED(); 105 rv = ERR_FAILED; 106 break; 107 } 108 } while (rv != ERR_IO_PENDING && next_state_ != kStateNone); 109 110 return rv; 111 } 112 113 int TCPConnectJob::DoResolveHost() { 114 next_state_ = kStateResolveHostComplete; 115 return resolver_.Resolve(resolve_info_, &addresses_, &callback_, load_log()); 116 } 117 118 int TCPConnectJob::DoResolveHostComplete(int result) { 119 if (result == OK) 120 next_state_ = kStateTCPConnect; 121 return result; 122 } 123 124 int TCPConnectJob::DoTCPConnect() { 125 next_state_ = kStateTCPConnectComplete; 126 set_socket(client_socket_factory_->CreateTCPClientSocket(addresses_)); 127 connect_start_time_ = base::TimeTicks::Now(); 128 return socket()->Connect(&callback_, load_log()); 129 } 130 131 int TCPConnectJob::DoTCPConnectComplete(int result) { 132 if (result == OK) { 133 DCHECK(connect_start_time_ != base::TimeTicks()); 134 DCHECK(start_time_ != base::TimeTicks()); 135 base::TimeTicks now = base::TimeTicks::Now(); 136 base::TimeDelta total_duration = now - start_time_; 137 UMA_HISTOGRAM_CUSTOM_TIMES( 138 "Net.DNS_Resolution_And_TCP_Connection_Latency2", 139 total_duration, 140 base::TimeDelta::FromMilliseconds(1), 141 base::TimeDelta::FromMinutes(10), 142 100); 143 144 base::TimeDelta connect_duration = now - connect_start_time_; 145 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency", 146 connect_duration, 147 base::TimeDelta::FromMilliseconds(1), 148 base::TimeDelta::FromMinutes(10), 149 100); 150 } else { 151 // Delete the socket on error. 152 set_socket(NULL); 153 } 154 155 return result; 156 } 157 158 ConnectJob* TCPClientSocketPool::TCPConnectJobFactory::NewConnectJob( 159 const std::string& group_name, 160 const PoolBase::Request& request, 161 ConnectJob::Delegate* delegate, 162 LoadLog* load_log) const { 163 return new TCPConnectJob( 164 group_name, request.params(), 165 base::TimeDelta::FromSeconds(kTCPConnectJobTimeoutInSeconds), 166 client_socket_factory_, host_resolver_, delegate, load_log); 167 } 168 169 TCPClientSocketPool::TCPClientSocketPool( 170 int max_sockets, 171 int max_sockets_per_group, 172 HostResolver* host_resolver, 173 ClientSocketFactory* client_socket_factory, 174 NetworkChangeNotifier* network_change_notifier) 175 : base_(max_sockets, max_sockets_per_group, 176 base::TimeDelta::FromSeconds(kUnusedIdleSocketTimeout), 177 base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout), 178 new TCPConnectJobFactory(client_socket_factory, host_resolver), 179 network_change_notifier) {} 180 181 TCPClientSocketPool::~TCPClientSocketPool() {} 182 183 int TCPClientSocketPool::RequestSocket( 184 const std::string& group_name, 185 const void* resolve_info, 186 RequestPriority priority, 187 ClientSocketHandle* handle, 188 CompletionCallback* callback, 189 LoadLog* load_log) { 190 const HostResolver::RequestInfo* casted_resolve_info = 191 static_cast<const HostResolver::RequestInfo*>(resolve_info); 192 193 if (LoadLog::IsUnbounded(load_log)) { 194 LoadLog::AddString( 195 load_log, 196 StringPrintf("Requested TCP socket to: %s [port %d]", 197 casted_resolve_info->hostname().c_str(), 198 casted_resolve_info->port())); 199 } 200 201 return base_.RequestSocket( 202 group_name, *casted_resolve_info, priority, handle, callback, load_log); 203 } 204 205 void TCPClientSocketPool::CancelRequest( 206 const std::string& group_name, 207 const ClientSocketHandle* handle) { 208 base_.CancelRequest(group_name, handle); 209 } 210 211 void TCPClientSocketPool::ReleaseSocket( 212 const std::string& group_name, 213 ClientSocket* socket) { 214 base_.ReleaseSocket(group_name, socket); 215 } 216 217 void TCPClientSocketPool::CloseIdleSockets() { 218 base_.CloseIdleSockets(); 219 } 220 221 int TCPClientSocketPool::IdleSocketCountInGroup( 222 const std::string& group_name) const { 223 return base_.IdleSocketCountInGroup(group_name); 224 } 225 226 LoadState TCPClientSocketPool::GetLoadState( 227 const std::string& group_name, const ClientSocketHandle* handle) const { 228 return base_.GetLoadState(group_name, handle); 229 } 230 231 } // namespace net 232