OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 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 | 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/tcp_client_socket_pool.h" | 5 #include "net/socket/tcp_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 TCPSocketParams::TCPSocketParams(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. |
| 29 const int TCPConnectJob::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 TCPSocketParams::TCPSocketParams( |
| 61 const HostPortPair& host_port_pair, |
| 62 RequestPriority priority, |
| 63 const GURL& referrer, |
| 64 bool disable_resolver_cache) |
27 : destination_(host_port_pair) { | 65 : destination_(host_port_pair) { |
28 Initialize(priority, referrer, disable_resolver_cache); | 66 Initialize(priority, referrer, disable_resolver_cache); |
29 } | 67 } |
30 | 68 |
31 // TODO(willchan): Update all unittests so we don't need this. | 69 // TODO(willchan): Update all unittests so we don't need this. |
32 TCPSocketParams::TCPSocketParams(const std::string& host, int port, | 70 TCPSocketParams::TCPSocketParams(const std::string& host, int port, |
33 RequestPriority priority, const GURL& referrer, | 71 RequestPriority priority, const GURL& referrer, |
34 bool disable_resolver_cache) | 72 bool disable_resolver_cache) |
35 : destination_(HostPortPair(host, port)) { | 73 : destination_(HostPortPair(host, port)) { |
36 Initialize(priority, referrer, disable_resolver_cache); | 74 Initialize(priority, referrer, disable_resolver_cache); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
69 HostResolver* host_resolver, | 107 HostResolver* host_resolver, |
70 Delegate* delegate, | 108 Delegate* delegate, |
71 NetLog* net_log) | 109 NetLog* net_log) |
72 : ConnectJob(group_name, timeout_duration, delegate, | 110 : ConnectJob(group_name, timeout_duration, delegate, |
73 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)), | 111 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)), |
74 params_(params), | 112 params_(params), |
75 client_socket_factory_(client_socket_factory), | 113 client_socket_factory_(client_socket_factory), |
76 ALLOW_THIS_IN_INITIALIZER_LIST( | 114 ALLOW_THIS_IN_INITIALIZER_LIST( |
77 callback_(this, | 115 callback_(this, |
78 &TCPConnectJob::OnIOComplete)), | 116 &TCPConnectJob::OnIOComplete)), |
79 resolver_(host_resolver) {} | 117 resolver_(host_resolver), |
| 118 ALLOW_THIS_IN_INITIALIZER_LIST( |
| 119 fallback_callback_( |
| 120 this, |
| 121 &TCPConnectJob::DoIPv6FallbackTCPConnectComplete)) {} |
80 | 122 |
81 TCPConnectJob::~TCPConnectJob() { | 123 TCPConnectJob::~TCPConnectJob() { |
82 // We don't worry about cancelling the host resolution and TCP connect, since | 124 // We don't worry about cancelling the host resolution and TCP connect, since |
83 // ~SingleRequestHostResolver and ~ClientSocket will take care of it. | 125 // ~SingleRequestHostResolver and ~ClientSocket will take care of it. |
84 } | 126 } |
85 | 127 |
86 LoadState TCPConnectJob::GetLoadState() const { | 128 LoadState TCPConnectJob::GetLoadState() const { |
87 switch (next_state_) { | 129 switch (next_state_) { |
88 case STATE_RESOLVE_HOST: | 130 case STATE_RESOLVE_HOST: |
89 case STATE_RESOLVE_HOST_COMPLETE: | 131 case STATE_RESOLVE_HOST_COMPLETE: |
90 return LOAD_STATE_RESOLVING_HOST; | 132 return LOAD_STATE_RESOLVING_HOST; |
91 case STATE_TCP_CONNECT: | 133 case STATE_TCP_CONNECT: |
92 case STATE_TCP_CONNECT_COMPLETE: | 134 case STATE_TCP_CONNECT_COMPLETE: |
93 return LOAD_STATE_CONNECTING; | 135 return LOAD_STATE_CONNECTING; |
94 default: | 136 default: |
95 NOTREACHED(); | 137 NOTREACHED(); |
96 return LOAD_STATE_IDLE; | 138 return LOAD_STATE_IDLE; |
97 } | 139 } |
98 } | 140 } |
99 | 141 |
| 142 // static |
| 143 void TCPConnectJob::MakeAddrListStartWithIPv4(AddressList* addrlist) { |
| 144 if (addrlist->head()->ai_family != AF_INET6) |
| 145 return; |
| 146 bool has_ipv4 = false; |
| 147 for (const struct addrinfo* ai = addrlist->head(); ai; ai = ai->ai_next) { |
| 148 if (ai->ai_family != AF_INET6) { |
| 149 has_ipv4 = true; |
| 150 break; |
| 151 } |
| 152 } |
| 153 if (!has_ipv4) |
| 154 return; |
| 155 |
| 156 struct addrinfo* head = CreateCopyOfAddrinfo(addrlist->head(), true); |
| 157 struct addrinfo* tail = head; |
| 158 while (tail->ai_next) |
| 159 tail = tail->ai_next; |
| 160 char* canonname = head->ai_canonname; |
| 161 head->ai_canonname = NULL; |
| 162 while (head->ai_family == AF_INET6) { |
| 163 tail->ai_next = head; |
| 164 tail = head; |
| 165 head = head->ai_next; |
| 166 tail->ai_next = NULL; |
| 167 } |
| 168 head->ai_canonname = canonname; |
| 169 |
| 170 addrlist->Copy(head, true); |
| 171 FreeCopyOfAddrinfo(head); |
| 172 } |
| 173 |
100 void TCPConnectJob::OnIOComplete(int result) { | 174 void TCPConnectJob::OnIOComplete(int result) { |
101 int rv = DoLoop(result); | 175 int rv = DoLoop(result); |
102 if (rv != ERR_IO_PENDING) | 176 if (rv != ERR_IO_PENDING) |
103 NotifyDelegateOfCompletion(rv); // Deletes |this| | 177 NotifyDelegateOfCompletion(rv); // Deletes |this| |
104 } | 178 } |
105 | 179 |
106 int TCPConnectJob::DoLoop(int result) { | 180 int TCPConnectJob::DoLoop(int result) { |
107 DCHECK_NE(next_state_, STATE_NONE); | 181 DCHECK_NE(next_state_, STATE_NONE); |
108 | 182 |
109 int rv = result; | 183 int rv = result; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
142 } | 216 } |
143 | 217 |
144 int TCPConnectJob::DoResolveHostComplete(int result) { | 218 int TCPConnectJob::DoResolveHostComplete(int result) { |
145 if (result == OK) | 219 if (result == OK) |
146 next_state_ = STATE_TCP_CONNECT; | 220 next_state_ = STATE_TCP_CONNECT; |
147 return result; | 221 return result; |
148 } | 222 } |
149 | 223 |
150 int TCPConnectJob::DoTCPConnect() { | 224 int TCPConnectJob::DoTCPConnect() { |
151 next_state_ = STATE_TCP_CONNECT_COMPLETE; | 225 next_state_ = STATE_TCP_CONNECT_COMPLETE; |
152 set_socket(client_socket_factory_->CreateTCPClientSocket( | 226 transport_socket_.reset(client_socket_factory_->CreateTCPClientSocket( |
153 addresses_, net_log().net_log(), net_log().source())); | 227 addresses_, net_log().net_log(), net_log().source())); |
154 connect_start_time_ = base::TimeTicks::Now(); | 228 connect_start_time_ = base::TimeTicks::Now(); |
155 return socket()->Connect(&callback_); | 229 int rv = transport_socket_->Connect(&callback_); |
| 230 if (rv == ERR_IO_PENDING && |
| 231 AddressListStartsWithIPv6AndHasAnIPv4Addr(addresses_)) { |
| 232 fallback_timer_.Start( |
| 233 base::TimeDelta::FromMilliseconds(kIPv6FallbackTimerInMs), |
| 234 this, &TCPConnectJob::DoIPv6FallbackTCPConnect); |
| 235 } |
| 236 return rv; |
156 } | 237 } |
157 | 238 |
158 int TCPConnectJob::DoTCPConnectComplete(int result) { | 239 int TCPConnectJob::DoTCPConnectComplete(int result) { |
159 if (result == OK) { | 240 if (result == OK) { |
| 241 bool is_ipv4 = addresses_.head()->ai_family != AF_INET6; |
160 DCHECK(connect_start_time_ != base::TimeTicks()); | 242 DCHECK(connect_start_time_ != base::TimeTicks()); |
161 DCHECK(start_time_ != base::TimeTicks()); | 243 DCHECK(start_time_ != base::TimeTicks()); |
162 base::TimeTicks now = base::TimeTicks::Now(); | 244 base::TimeTicks now = base::TimeTicks::Now(); |
163 base::TimeDelta total_duration = now - start_time_; | 245 base::TimeDelta total_duration = now - start_time_; |
164 UMA_HISTOGRAM_CUSTOM_TIMES( | 246 UMA_HISTOGRAM_CUSTOM_TIMES( |
165 "Net.DNS_Resolution_And_TCP_Connection_Latency2", | 247 "Net.DNS_Resolution_And_TCP_Connection_Latency2", |
166 total_duration, | 248 total_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); |
170 | 252 |
171 base::TimeDelta connect_duration = now - connect_start_time_; | 253 base::TimeDelta connect_duration = now - connect_start_time_; |
172 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency", | 254 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency", |
173 connect_duration, | 255 connect_duration, |
174 base::TimeDelta::FromMilliseconds(1), | 256 base::TimeDelta::FromMilliseconds(1), |
175 base::TimeDelta::FromMinutes(10), | 257 base::TimeDelta::FromMinutes(10), |
176 100); | 258 100); |
| 259 |
| 260 if (is_ipv4) { |
| 261 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_No_Race", |
| 262 connect_duration, |
| 263 base::TimeDelta::FromMilliseconds(1), |
| 264 base::TimeDelta::FromMinutes(10), |
| 265 100); |
| 266 } else { |
| 267 if (AddressListOnlyContainsIPv6Addresses(addresses_)) { |
| 268 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Solo", |
| 269 connect_duration, |
| 270 base::TimeDelta::FromMilliseconds(1), |
| 271 base::TimeDelta::FromMinutes(10), |
| 272 100); |
| 273 } else { |
| 274 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Raceable", |
| 275 connect_duration, |
| 276 base::TimeDelta::FromMilliseconds(1), |
| 277 base::TimeDelta::FromMinutes(10), |
| 278 100); |
| 279 } |
| 280 } |
| 281 set_socket(transport_socket_.release()); |
| 282 fallback_timer_.Stop(); |
177 } else { | 283 } else { |
178 // Delete the socket on error. | 284 // Be a bit paranoid and kill off the fallback members to prevent reuse. |
179 set_socket(NULL); | 285 fallback_transport_socket_.reset(); |
| 286 fallback_addresses_.reset(); |
180 } | 287 } |
181 | 288 |
182 return result; | 289 return result; |
183 } | 290 } |
184 | 291 |
| 292 void TCPConnectJob::DoIPv6FallbackTCPConnect() { |
| 293 // The timer should only fire while we're waiting for the main connect to |
| 294 // succeed. |
| 295 if (next_state_ != STATE_TCP_CONNECT_COMPLETE) { |
| 296 NOTREACHED(); |
| 297 return; |
| 298 } |
| 299 |
| 300 DCHECK(!fallback_transport_socket_.get()); |
| 301 DCHECK(!fallback_addresses_.get()); |
| 302 |
| 303 fallback_addresses_.reset(new AddressList(addresses_)); |
| 304 MakeAddrListStartWithIPv4(fallback_addresses_.get()); |
| 305 fallback_transport_socket_.reset( |
| 306 client_socket_factory_->CreateTCPClientSocket( |
| 307 *fallback_addresses_, net_log().net_log(), net_log().source())); |
| 308 fallback_connect_start_time_ = base::TimeTicks::Now(); |
| 309 int rv = fallback_transport_socket_->Connect(&fallback_callback_); |
| 310 if (rv != ERR_IO_PENDING) |
| 311 DoIPv6FallbackTCPConnectComplete(rv); |
| 312 } |
| 313 |
| 314 void TCPConnectJob::DoIPv6FallbackTCPConnectComplete(int result) { |
| 315 // This should only happen when we're waiting for the main connect to succeed. |
| 316 if (next_state_ != STATE_TCP_CONNECT_COMPLETE) { |
| 317 NOTREACHED(); |
| 318 return; |
| 319 } |
| 320 |
| 321 DCHECK_NE(ERR_IO_PENDING, result); |
| 322 DCHECK(fallback_transport_socket_.get()); |
| 323 DCHECK(fallback_addresses_.get()); |
| 324 |
| 325 if (result == OK) { |
| 326 DCHECK(fallback_connect_start_time_ != base::TimeTicks()); |
| 327 DCHECK(start_time_ != base::TimeTicks()); |
| 328 base::TimeTicks now = base::TimeTicks::Now(); |
| 329 base::TimeDelta total_duration = now - start_time_; |
| 330 UMA_HISTOGRAM_CUSTOM_TIMES( |
| 331 "Net.DNS_Resolution_And_TCP_Connection_Latency2", |
| 332 total_duration, |
| 333 base::TimeDelta::FromMilliseconds(1), |
| 334 base::TimeDelta::FromMinutes(10), |
| 335 100); |
| 336 |
| 337 base::TimeDelta connect_duration = now - fallback_connect_start_time_; |
| 338 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency", |
| 339 connect_duration, |
| 340 base::TimeDelta::FromMilliseconds(1), |
| 341 base::TimeDelta::FromMinutes(10), |
| 342 100); |
| 343 |
| 344 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_Wins_Race", |
| 345 connect_duration, |
| 346 base::TimeDelta::FromMilliseconds(1), |
| 347 base::TimeDelta::FromMinutes(10), |
| 348 100); |
| 349 set_socket(fallback_transport_socket_.release()); |
| 350 next_state_ = STATE_NONE; |
| 351 transport_socket_.reset(); |
| 352 } else { |
| 353 // Be a bit paranoid and kill off the fallback members to prevent reuse. |
| 354 fallback_transport_socket_.reset(); |
| 355 fallback_addresses_.reset(); |
| 356 } |
| 357 NotifyDelegateOfCompletion(result); // Deletes |this| |
| 358 } |
| 359 |
185 int TCPConnectJob::ConnectInternal() { | 360 int TCPConnectJob::ConnectInternal() { |
186 next_state_ = STATE_RESOLVE_HOST; | 361 next_state_ = STATE_RESOLVE_HOST; |
187 start_time_ = base::TimeTicks::Now(); | 362 start_time_ = base::TimeTicks::Now(); |
188 return DoLoop(OK); | 363 return DoLoop(OK); |
189 } | 364 } |
190 | 365 |
191 ConnectJob* TCPClientSocketPool::TCPConnectJobFactory::NewConnectJob( | 366 ConnectJob* TCPClientSocketPool::TCPConnectJobFactory::NewConnectJob( |
192 const std::string& group_name, | 367 const std::string& group_name, |
193 const PoolBase::Request& request, | 368 const PoolBase::Request& request, |
194 ConnectJob::Delegate* delegate) const { | 369 ConnectJob::Delegate* delegate) const { |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
307 | 482 |
308 base::TimeDelta TCPClientSocketPool::ConnectionTimeout() const { | 483 base::TimeDelta TCPClientSocketPool::ConnectionTimeout() const { |
309 return base_.ConnectionTimeout(); | 484 return base_.ConnectionTimeout(); |
310 } | 485 } |
311 | 486 |
312 ClientSocketPoolHistograms* TCPClientSocketPool::histograms() const { | 487 ClientSocketPoolHistograms* TCPClientSocketPool::histograms() const { |
313 return base_.histograms(); | 488 return base_.histograms(); |
314 } | 489 } |
315 | 490 |
316 } // namespace net | 491 } // namespace net |
OLD | NEW |