| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "net/socket/transport_client_socket_pool.h" | 5 #include "net/socket/transport_client_socket_pool.h" |
| 6 | 6 |
| 7 #include "base/compiler_specific.h" | 7 #include "base/compiler_specific.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
| 10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| 11 #include "base/string_util.h" | 11 #include "base/string_util.h" |
| 12 #include "base/time.h" | 12 #include "base/time.h" |
| 13 #include "net/base/ip_endpoint.h" |
| 13 #include "net/base/net_log.h" | 14 #include "net/base/net_log.h" |
| 14 #include "net/base/net_errors.h" | 15 #include "net/base/net_errors.h" |
| 16 #include "net/base/sys_addrinfo.h" |
| 15 #include "net/socket/client_socket_factory.h" | 17 #include "net/socket/client_socket_factory.h" |
| 16 #include "net/socket/client_socket_handle.h" | 18 #include "net/socket/client_socket_handle.h" |
| 17 #include "net/socket/client_socket_pool_base.h" | 19 #include "net/socket/client_socket_pool_base.h" |
| 18 #include "net/socket/tcp_client_socket.h" | 20 #include "net/socket/tcp_client_socket.h" |
| 19 | 21 |
| 20 using base::TimeDelta; | 22 using base::TimeDelta; |
| 21 | 23 |
| 22 namespace net { | 24 namespace net { |
| 23 | 25 |
| 24 TransportSocketParams::TransportSocketParams(const HostPortPair& host_port_pair, | 26 // TODO(willchan): Base this off RTT instead of statically setting it. Note we |
| 25 RequestPriority priority, const GURL& referrer, | 27 // choose a timeout that is different from the backup connect job timer so they |
| 26 bool disable_resolver_cache, | 28 // don't synchronize. |
| 27 bool ignore_limits) | 29 const int TransportConnectJob::kIPv6FallbackTimerInMs = 300; |
| 30 |
| 31 namespace { |
| 32 |
| 33 bool AddressListStartsWithIPv6AndHasAnIPv4Addr(const AddressList& addrlist) { |
| 34 const struct addrinfo* ai = addrlist.head(); |
| 35 if (ai->ai_family != AF_INET6) |
| 36 return false; |
| 37 |
| 38 ai = ai->ai_next; |
| 39 while (ai) { |
| 40 if (ai->ai_family != AF_INET6) |
| 41 return true; |
| 42 ai = ai->ai_next; |
| 43 } |
| 44 |
| 45 return false; |
| 46 } |
| 47 |
| 48 bool AddressListOnlyContainsIPv6Addresses(const AddressList& addrlist) { |
| 49 DCHECK(addrlist.head()); |
| 50 for (const struct addrinfo* ai = addrlist.head(); ai; ai = ai->ai_next) { |
| 51 if (ai->ai_family != AF_INET6) |
| 52 return false; |
| 53 } |
| 54 |
| 55 return true; |
| 56 } |
| 57 |
| 58 } // namespace |
| 59 |
| 60 TransportSocketParams::TransportSocketParams( |
| 61 const HostPortPair& host_port_pair, |
| 62 RequestPriority priority, |
| 63 const GURL& referrer, |
| 64 bool disable_resolver_cache, |
| 65 bool ignore_limits) |
| 28 : destination_(host_port_pair), ignore_limits_(ignore_limits) { | 66 : destination_(host_port_pair), ignore_limits_(ignore_limits) { |
| 29 Initialize(priority, referrer, disable_resolver_cache); | 67 Initialize(priority, referrer, disable_resolver_cache); |
| 30 } | 68 } |
| 31 | 69 |
| 32 TransportSocketParams::~TransportSocketParams() {} | 70 TransportSocketParams::~TransportSocketParams() {} |
| 33 | 71 |
| 34 void TransportSocketParams::Initialize(RequestPriority priority, | 72 void TransportSocketParams::Initialize(RequestPriority priority, |
| 35 const GURL& referrer, | 73 const GURL& referrer, |
| 36 bool disable_resolver_cache) { | 74 bool disable_resolver_cache) { |
| 37 // The referrer is used by the DNS prefetch system to correlate resolutions | 75 // The referrer is used by the DNS prefetch system to correlate resolutions |
| 38 // with the page that triggered them. It doesn't impact the actual addresses | 76 // with the page that triggered them. It doesn't impact the actual addresses |
| 39 // that we resolve to. | 77 // that we resolve to. |
| 40 destination_.set_referrer(referrer); | 78 destination_.set_referrer(referrer); |
| 41 destination_.set_priority(priority); | 79 destination_.set_priority(priority); |
| 42 if (disable_resolver_cache) | 80 if (disable_resolver_cache) |
| 43 destination_.set_allow_cached_response(false); | 81 destination_.set_allow_cached_response(false); |
| 44 } | 82 } |
| 45 | 83 |
| 46 // TransportConnectJobs will time out after this many seconds. Note this is | 84 // TransportConnectJobs will time out after this many seconds. Note this is |
| (...skipping 15 matching lines...) Expand all Loading... |
| 62 HostResolver* host_resolver, | 100 HostResolver* host_resolver, |
| 63 Delegate* delegate, | 101 Delegate* delegate, |
| 64 NetLog* net_log) | 102 NetLog* net_log) |
| 65 : ConnectJob(group_name, timeout_duration, delegate, | 103 : ConnectJob(group_name, timeout_duration, delegate, |
| 66 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)), | 104 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)), |
| 67 params_(params), | 105 params_(params), |
| 68 client_socket_factory_(client_socket_factory), | 106 client_socket_factory_(client_socket_factory), |
| 69 ALLOW_THIS_IN_INITIALIZER_LIST( | 107 ALLOW_THIS_IN_INITIALIZER_LIST( |
| 70 callback_(this, | 108 callback_(this, |
| 71 &TransportConnectJob::OnIOComplete)), | 109 &TransportConnectJob::OnIOComplete)), |
| 72 resolver_(host_resolver) {} | 110 resolver_(host_resolver), |
| 111 ALLOW_THIS_IN_INITIALIZER_LIST( |
| 112 fallback_callback_( |
| 113 this, |
| 114 &TransportConnectJob::DoIPv6FallbackTransportConnectComplete)) {} |
| 73 | 115 |
| 74 TransportConnectJob::~TransportConnectJob() { | 116 TransportConnectJob::~TransportConnectJob() { |
| 75 // We don't worry about cancelling the host resolution and TCP connect, since | 117 // We don't worry about cancelling the host resolution and TCP connect, since |
| 76 // ~SingleRequestHostResolver and ~ClientSocket will take care of it. | 118 // ~SingleRequestHostResolver and ~ClientSocket will take care of it. |
| 77 } | 119 } |
| 78 | 120 |
| 79 LoadState TransportConnectJob::GetLoadState() const { | 121 LoadState TransportConnectJob::GetLoadState() const { |
| 80 switch (next_state_) { | 122 switch (next_state_) { |
| 81 case STATE_RESOLVE_HOST: | 123 case STATE_RESOLVE_HOST: |
| 82 case STATE_RESOLVE_HOST_COMPLETE: | 124 case STATE_RESOLVE_HOST_COMPLETE: |
| 83 return LOAD_STATE_RESOLVING_HOST; | 125 return LOAD_STATE_RESOLVING_HOST; |
| 84 case STATE_TRANSPORT_CONNECT: | 126 case STATE_TRANSPORT_CONNECT: |
| 85 case STATE_TRANSPORT_CONNECT_COMPLETE: | 127 case STATE_TRANSPORT_CONNECT_COMPLETE: |
| 86 return LOAD_STATE_CONNECTING; | 128 return LOAD_STATE_CONNECTING; |
| 87 default: | 129 default: |
| 88 NOTREACHED(); | 130 NOTREACHED(); |
| 89 return LOAD_STATE_IDLE; | 131 return LOAD_STATE_IDLE; |
| 90 } | 132 } |
| 91 } | 133 } |
| 92 | 134 |
| 135 // static |
| 136 void TransportConnectJob::MakeAddrListStartWithIPv4(AddressList* addrlist) { |
| 137 if (addrlist->head()->ai_family != AF_INET6) |
| 138 return; |
| 139 bool has_ipv4 = false; |
| 140 for (const struct addrinfo* ai = addrlist->head(); ai; ai = ai->ai_next) { |
| 141 if (ai->ai_family != AF_INET6) { |
| 142 has_ipv4 = true; |
| 143 break; |
| 144 } |
| 145 } |
| 146 if (!has_ipv4) |
| 147 return; |
| 148 |
| 149 struct addrinfo* head = CreateCopyOfAddrinfo(addrlist->head(), true); |
| 150 struct addrinfo* tail = head; |
| 151 while (tail->ai_next) |
| 152 tail = tail->ai_next; |
| 153 char* canonname = head->ai_canonname; |
| 154 head->ai_canonname = NULL; |
| 155 while (head->ai_family == AF_INET6) { |
| 156 tail->ai_next = head; |
| 157 tail = head; |
| 158 head = head->ai_next; |
| 159 tail->ai_next = NULL; |
| 160 } |
| 161 head->ai_canonname = canonname; |
| 162 |
| 163 addrlist->Copy(head, true); |
| 164 FreeCopyOfAddrinfo(head); |
| 165 } |
| 166 |
| 93 void TransportConnectJob::OnIOComplete(int result) { | 167 void TransportConnectJob::OnIOComplete(int result) { |
| 94 int rv = DoLoop(result); | 168 int rv = DoLoop(result); |
| 95 if (rv != ERR_IO_PENDING) | 169 if (rv != ERR_IO_PENDING) |
| 96 NotifyDelegateOfCompletion(rv); // Deletes |this| | 170 NotifyDelegateOfCompletion(rv); // Deletes |this| |
| 97 } | 171 } |
| 98 | 172 |
| 99 int TransportConnectJob::DoLoop(int result) { | 173 int TransportConnectJob::DoLoop(int result) { |
| 100 DCHECK_NE(next_state_, STATE_NONE); | 174 DCHECK_NE(next_state_, STATE_NONE); |
| 101 | 175 |
| 102 int rv = result; | 176 int rv = result; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 135 } | 209 } |
| 136 | 210 |
| 137 int TransportConnectJob::DoResolveHostComplete(int result) { | 211 int TransportConnectJob::DoResolveHostComplete(int result) { |
| 138 if (result == OK) | 212 if (result == OK) |
| 139 next_state_ = STATE_TRANSPORT_CONNECT; | 213 next_state_ = STATE_TRANSPORT_CONNECT; |
| 140 return result; | 214 return result; |
| 141 } | 215 } |
| 142 | 216 |
| 143 int TransportConnectJob::DoTransportConnect() { | 217 int TransportConnectJob::DoTransportConnect() { |
| 144 next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE; | 218 next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE; |
| 145 set_socket(client_socket_factory_->CreateTransportClientSocket( | 219 transport_socket_.reset(client_socket_factory_->CreateTransportClientSocket( |
| 146 addresses_, net_log().net_log(), net_log().source())); | 220 addresses_, net_log().net_log(), net_log().source())); |
| 147 connect_start_time_ = base::TimeTicks::Now(); | 221 connect_start_time_ = base::TimeTicks::Now(); |
| 148 return socket()->Connect(&callback_); | 222 int rv = transport_socket_->Connect(&callback_); |
| 223 if (rv == ERR_IO_PENDING && |
| 224 AddressListStartsWithIPv6AndHasAnIPv4Addr(addresses_)) { |
| 225 fallback_timer_.Start( |
| 226 base::TimeDelta::FromMilliseconds(kIPv6FallbackTimerInMs), |
| 227 this, &TransportConnectJob::DoIPv6FallbackTransportConnect); |
| 228 } |
| 229 return rv; |
| 149 } | 230 } |
| 150 | 231 |
| 151 int TransportConnectJob::DoTransportConnectComplete(int result) { | 232 int TransportConnectJob::DoTransportConnectComplete(int result) { |
| 152 if (result == OK) { | 233 if (result == OK) { |
| 234 bool is_ipv4 = addresses_.head()->ai_family != AF_INET6; |
| 153 DCHECK(connect_start_time_ != base::TimeTicks()); | 235 DCHECK(connect_start_time_ != base::TimeTicks()); |
| 154 DCHECK(start_time_ != base::TimeTicks()); | 236 DCHECK(start_time_ != base::TimeTicks()); |
| 155 base::TimeTicks now = base::TimeTicks::Now(); | 237 base::TimeTicks now = base::TimeTicks::Now(); |
| 156 base::TimeDelta total_duration = now - start_time_; | 238 base::TimeDelta total_duration = now - start_time_; |
| 157 UMA_HISTOGRAM_CUSTOM_TIMES( | 239 UMA_HISTOGRAM_CUSTOM_TIMES( |
| 158 "Net.DNS_Resolution_And_TCP_Connection_Latency2", | 240 "Net.DNS_Resolution_And_TCP_Connection_Latency2", |
| 159 total_duration, | 241 total_duration, |
| 160 base::TimeDelta::FromMilliseconds(1), | 242 base::TimeDelta::FromMilliseconds(1), |
| 161 base::TimeDelta::FromMinutes(10), | 243 base::TimeDelta::FromMinutes(10), |
| 162 100); | 244 100); |
| 163 | 245 |
| 164 base::TimeDelta connect_duration = now - connect_start_time_; | 246 base::TimeDelta connect_duration = now - connect_start_time_; |
| 165 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency", | 247 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency", |
| 166 connect_duration, | 248 connect_duration, |
| 167 base::TimeDelta::FromMilliseconds(1), | 249 base::TimeDelta::FromMilliseconds(1), |
| 168 base::TimeDelta::FromMinutes(10), | 250 base::TimeDelta::FromMinutes(10), |
| 169 100); | 251 100); |
| 252 |
| 253 if (is_ipv4) { |
| 254 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_No_Race", |
| 255 connect_duration, |
| 256 base::TimeDelta::FromMilliseconds(1), |
| 257 base::TimeDelta::FromMinutes(10), |
| 258 100); |
| 259 } else { |
| 260 if (AddressListOnlyContainsIPv6Addresses(addresses_)) { |
| 261 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Solo", |
| 262 connect_duration, |
| 263 base::TimeDelta::FromMilliseconds(1), |
| 264 base::TimeDelta::FromMinutes(10), |
| 265 100); |
| 266 } else { |
| 267 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Raceable", |
| 268 connect_duration, |
| 269 base::TimeDelta::FromMilliseconds(1), |
| 270 base::TimeDelta::FromMinutes(10), |
| 271 100); |
| 272 } |
| 273 } |
| 274 set_socket(transport_socket_.release()); |
| 275 fallback_timer_.Stop(); |
| 170 } else { | 276 } else { |
| 171 // Delete the socket on error. | 277 // Be a bit paranoid and kill off the fallback members to prevent reuse. |
| 172 set_socket(NULL); | 278 fallback_transport_socket_.reset(); |
| 279 fallback_addresses_.reset(); |
| 173 } | 280 } |
| 174 | 281 |
| 175 return result; | 282 return result; |
| 176 } | 283 } |
| 177 | 284 |
| 285 void TransportConnectJob::DoIPv6FallbackTransportConnect() { |
| 286 // The timer should only fire while we're waiting for the main connect to |
| 287 // succeed. |
| 288 if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) { |
| 289 NOTREACHED(); |
| 290 return; |
| 291 } |
| 292 |
| 293 DCHECK(!fallback_transport_socket_.get()); |
| 294 DCHECK(!fallback_addresses_.get()); |
| 295 |
| 296 fallback_addresses_.reset(new AddressList(addresses_)); |
| 297 MakeAddrListStartWithIPv4(fallback_addresses_.get()); |
| 298 fallback_transport_socket_.reset( |
| 299 client_socket_factory_->CreateTransportClientSocket( |
| 300 *fallback_addresses_, net_log().net_log(), net_log().source())); |
| 301 fallback_connect_start_time_ = base::TimeTicks::Now(); |
| 302 int rv = fallback_transport_socket_->Connect(&fallback_callback_); |
| 303 if (rv != ERR_IO_PENDING) |
| 304 DoIPv6FallbackTransportConnectComplete(rv); |
| 305 } |
| 306 |
| 307 void TransportConnectJob::DoIPv6FallbackTransportConnectComplete(int result) { |
| 308 // This should only happen when we're waiting for the main connect to succeed. |
| 309 if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) { |
| 310 NOTREACHED(); |
| 311 return; |
| 312 } |
| 313 |
| 314 DCHECK_NE(ERR_IO_PENDING, result); |
| 315 DCHECK(fallback_transport_socket_.get()); |
| 316 DCHECK(fallback_addresses_.get()); |
| 317 |
| 318 if (result == OK) { |
| 319 DCHECK(fallback_connect_start_time_ != base::TimeTicks()); |
| 320 DCHECK(start_time_ != base::TimeTicks()); |
| 321 base::TimeTicks now = base::TimeTicks::Now(); |
| 322 base::TimeDelta total_duration = now - start_time_; |
| 323 UMA_HISTOGRAM_CUSTOM_TIMES( |
| 324 "Net.DNS_Resolution_And_TCP_Connection_Latency2", |
| 325 total_duration, |
| 326 base::TimeDelta::FromMilliseconds(1), |
| 327 base::TimeDelta::FromMinutes(10), |
| 328 100); |
| 329 |
| 330 base::TimeDelta connect_duration = now - fallback_connect_start_time_; |
| 331 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency", |
| 332 connect_duration, |
| 333 base::TimeDelta::FromMilliseconds(1), |
| 334 base::TimeDelta::FromMinutes(10), |
| 335 100); |
| 336 |
| 337 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_Wins_Race", |
| 338 connect_duration, |
| 339 base::TimeDelta::FromMilliseconds(1), |
| 340 base::TimeDelta::FromMinutes(10), |
| 341 100); |
| 342 set_socket(fallback_transport_socket_.release()); |
| 343 next_state_ = STATE_NONE; |
| 344 transport_socket_.reset(); |
| 345 } else { |
| 346 // Be a bit paranoid and kill off the fallback members to prevent reuse. |
| 347 fallback_transport_socket_.reset(); |
| 348 fallback_addresses_.reset(); |
| 349 } |
| 350 NotifyDelegateOfCompletion(result); // Deletes |this| |
| 351 } |
| 352 |
| 178 int TransportConnectJob::ConnectInternal() { | 353 int TransportConnectJob::ConnectInternal() { |
| 179 next_state_ = STATE_RESOLVE_HOST; | 354 next_state_ = STATE_RESOLVE_HOST; |
| 180 start_time_ = base::TimeTicks::Now(); | 355 start_time_ = base::TimeTicks::Now(); |
| 181 return DoLoop(OK); | 356 return DoLoop(OK); |
| 182 } | 357 } |
| 183 | 358 |
| 184 ConnectJob* | 359 ConnectJob* |
| 185 TransportClientSocketPool::TransportConnectJobFactory::NewConnectJob( | 360 TransportClientSocketPool::TransportConnectJobFactory::NewConnectJob( |
| 186 const std::string& group_name, | 361 const std::string& group_name, |
| 187 const PoolBase::Request& request, | 362 const PoolBase::Request& request, |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 306 | 481 |
| 307 base::TimeDelta TransportClientSocketPool::ConnectionTimeout() const { | 482 base::TimeDelta TransportClientSocketPool::ConnectionTimeout() const { |
| 308 return base_.ConnectionTimeout(); | 483 return base_.ConnectionTimeout(); |
| 309 } | 484 } |
| 310 | 485 |
| 311 ClientSocketPoolHistograms* TransportClientSocketPool::histograms() const { | 486 ClientSocketPoolHistograms* TransportClientSocketPool::histograms() const { |
| 312 return base_.histograms(); | 487 return base_.histograms(); |
| 313 } | 488 } |
| 314 | 489 |
| 315 } // namespace net | 490 } // namespace net |
| OLD | NEW |