• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "net/socket/transport_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/ip_endpoint.h"
14 #include "net/base/net_log.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/sys_addrinfo.h"
17 #include "net/socket/client_socket_factory.h"
18 #include "net/socket/client_socket_handle.h"
19 #include "net/socket/client_socket_pool_base.h"
20 #include "net/socket/tcp_client_socket.h"
21 
22 using base::TimeDelta;
23 
24 namespace net {
25 
26 // TODO(willchan): Base this off RTT instead of statically setting it. Note we
27 // choose a timeout that is different from the backup connect job timer so they
28 // don't synchronize.
29 const int TransportConnectJob::kIPv6FallbackTimerInMs = 300;
30 
31 namespace {
32 
AddressListStartsWithIPv6AndHasAnIPv4Addr(const AddressList & addrlist)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 
AddressListOnlyContainsIPv6Addresses(const AddressList & addrlist)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 
TransportSocketParams(const HostPortPair & host_port_pair,RequestPriority priority,const GURL & referrer,bool disable_resolver_cache,bool ignore_limits)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)
66     : destination_(host_port_pair), ignore_limits_(ignore_limits)
67 #ifdef ANDROID
68     , valid_uid_(false), calling_uid_(0)
69 #endif
70     {
71   Initialize(priority, referrer, disable_resolver_cache);
72 }
73 
~TransportSocketParams()74 TransportSocketParams::~TransportSocketParams() {}
75 
Initialize(RequestPriority priority,const GURL & referrer,bool disable_resolver_cache)76 void TransportSocketParams::Initialize(RequestPriority priority,
77                                        const GURL& referrer,
78                                        bool disable_resolver_cache) {
79   // The referrer is used by the DNS prefetch system to correlate resolutions
80   // with the page that triggered them. It doesn't impact the actual addresses
81   // that we resolve to.
82   destination_.set_referrer(referrer);
83   destination_.set_priority(priority);
84   if (disable_resolver_cache)
85     destination_.set_allow_cached_response(false);
86 }
87 
88 #ifdef ANDROID
getUID(uid_t * uid) const89 bool TransportSocketParams::getUID(uid_t *uid) const {
90   if (!valid_uid_) {
91     return false;
92   }
93   *uid = calling_uid_;
94   return true;
95 }
96 
setUID(uid_t uid)97 void TransportSocketParams::setUID(uid_t uid) {
98   valid_uid_ = true;
99   calling_uid_ = uid;
100 }
101 #endif
102 
103 // TransportConnectJobs will time out after this many seconds.  Note this is
104 // the total time, including both host resolution and TCP connect() times.
105 //
106 // TODO(eroman): The use of this constant needs to be re-evaluated. The time
107 // needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since
108 // the address list may contain many alternatives, and most of those may
109 // timeout. Even worse, the per-connect timeout threshold varies greatly
110 // between systems (anywhere from 20 seconds to 190 seconds).
111 // See comment #12 at http://crbug.com/23364 for specifics.
112 static const int kTransportConnectJobTimeoutInSeconds = 240;  // 4 minutes.
113 
TransportConnectJob(const std::string & group_name,const scoped_refptr<TransportSocketParams> & params,base::TimeDelta timeout_duration,ClientSocketFactory * client_socket_factory,HostResolver * host_resolver,Delegate * delegate,NetLog * net_log)114 TransportConnectJob::TransportConnectJob(
115     const std::string& group_name,
116     const scoped_refptr<TransportSocketParams>& params,
117     base::TimeDelta timeout_duration,
118     ClientSocketFactory* client_socket_factory,
119     HostResolver* host_resolver,
120     Delegate* delegate,
121     NetLog* net_log)
122     : ConnectJob(group_name, timeout_duration, delegate,
123                  BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
124       params_(params),
125       client_socket_factory_(client_socket_factory),
126       ALLOW_THIS_IN_INITIALIZER_LIST(
127           callback_(this,
128                     &TransportConnectJob::OnIOComplete)),
129       resolver_(host_resolver),
130       ALLOW_THIS_IN_INITIALIZER_LIST(
131           fallback_callback_(
132               this,
133               &TransportConnectJob::DoIPv6FallbackTransportConnectComplete)) {}
134 
~TransportConnectJob()135 TransportConnectJob::~TransportConnectJob() {
136   // We don't worry about cancelling the host resolution and TCP connect, since
137   // ~SingleRequestHostResolver and ~ClientSocket will take care of it.
138 }
139 
GetLoadState() const140 LoadState TransportConnectJob::GetLoadState() const {
141   switch (next_state_) {
142     case STATE_RESOLVE_HOST:
143     case STATE_RESOLVE_HOST_COMPLETE:
144       return LOAD_STATE_RESOLVING_HOST;
145     case STATE_TRANSPORT_CONNECT:
146     case STATE_TRANSPORT_CONNECT_COMPLETE:
147       return LOAD_STATE_CONNECTING;
148     default:
149       NOTREACHED();
150       return LOAD_STATE_IDLE;
151   }
152 }
153 
154 // static
MakeAddrListStartWithIPv4(AddressList * addrlist)155 void TransportConnectJob::MakeAddrListStartWithIPv4(AddressList* addrlist) {
156   if (addrlist->head()->ai_family != AF_INET6)
157     return;
158   bool has_ipv4 = false;
159   for (const struct addrinfo* ai = addrlist->head(); ai; ai = ai->ai_next) {
160     if (ai->ai_family != AF_INET6) {
161       has_ipv4 = true;
162       break;
163     }
164   }
165   if (!has_ipv4)
166     return;
167 
168   struct addrinfo* head = CreateCopyOfAddrinfo(addrlist->head(), true);
169   struct addrinfo* tail = head;
170   while (tail->ai_next)
171     tail = tail->ai_next;
172   char* canonname = head->ai_canonname;
173   head->ai_canonname = NULL;
174   while (head->ai_family == AF_INET6) {
175     tail->ai_next = head;
176     tail = head;
177     head = head->ai_next;
178     tail->ai_next = NULL;
179   }
180   head->ai_canonname = canonname;
181 
182   addrlist->Copy(head, true);
183   FreeCopyOfAddrinfo(head);
184 }
185 
OnIOComplete(int result)186 void TransportConnectJob::OnIOComplete(int result) {
187   int rv = DoLoop(result);
188   if (rv != ERR_IO_PENDING)
189     NotifyDelegateOfCompletion(rv);  // Deletes |this|
190 }
191 
DoLoop(int result)192 int TransportConnectJob::DoLoop(int result) {
193   DCHECK_NE(next_state_, STATE_NONE);
194 
195   int rv = result;
196   do {
197     State state = next_state_;
198     next_state_ = STATE_NONE;
199     switch (state) {
200       case STATE_RESOLVE_HOST:
201         DCHECK_EQ(OK, rv);
202         rv = DoResolveHost();
203         break;
204       case STATE_RESOLVE_HOST_COMPLETE:
205         rv = DoResolveHostComplete(rv);
206         break;
207       case STATE_TRANSPORT_CONNECT:
208         DCHECK_EQ(OK, rv);
209         rv = DoTransportConnect();
210         break;
211       case STATE_TRANSPORT_CONNECT_COMPLETE:
212         rv = DoTransportConnectComplete(rv);
213         break;
214       default:
215         NOTREACHED();
216         rv = ERR_FAILED;
217         break;
218     }
219   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
220 
221   return rv;
222 }
223 
DoResolveHost()224 int TransportConnectJob::DoResolveHost() {
225   next_state_ = STATE_RESOLVE_HOST_COMPLETE;
226   return resolver_.Resolve(params_->destination(), &addresses_, &callback_,
227                            net_log());
228 }
229 
DoResolveHostComplete(int result)230 int TransportConnectJob::DoResolveHostComplete(int result) {
231   if (result == OK)
232     next_state_ = STATE_TRANSPORT_CONNECT;
233   return result;
234 }
235 
DoTransportConnect()236 int TransportConnectJob::DoTransportConnect() {
237   next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
238   transport_socket_.reset(client_socket_factory_->CreateTransportClientSocket(
239         addresses_, net_log().net_log(), net_log().source()));
240   connect_start_time_ = base::TimeTicks::Now();
241 
242 #ifdef ANDROID
243   uid_t calling_uid = 0;
244   bool valid_uid = params_->getUID(&calling_uid);
245 #endif
246 
247   int rv = transport_socket_->Connect(&callback_
248 #ifdef ANDROID
249                                       , params_->ignore_limits()
250                                       , valid_uid
251                                       , calling_uid
252 #endif
253                                       );
254   if (rv == ERR_IO_PENDING &&
255       AddressListStartsWithIPv6AndHasAnIPv4Addr(addresses_)) {
256     fallback_timer_.Start(
257         base::TimeDelta::FromMilliseconds(kIPv6FallbackTimerInMs),
258         this, &TransportConnectJob::DoIPv6FallbackTransportConnect);
259   }
260   return rv;
261 }
262 
DoTransportConnectComplete(int result)263 int TransportConnectJob::DoTransportConnectComplete(int result) {
264   if (result == OK) {
265     bool is_ipv4 = addresses_.head()->ai_family != AF_INET6;
266     DCHECK(connect_start_time_ != base::TimeTicks());
267     DCHECK(start_time_ != base::TimeTicks());
268     base::TimeTicks now = base::TimeTicks::Now();
269     base::TimeDelta total_duration = now - start_time_;
270     UMA_HISTOGRAM_CUSTOM_TIMES(
271         "Net.DNS_Resolution_And_TCP_Connection_Latency2",
272         total_duration,
273         base::TimeDelta::FromMilliseconds(1),
274         base::TimeDelta::FromMinutes(10),
275         100);
276 
277     base::TimeDelta connect_duration = now - connect_start_time_;
278     UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
279         connect_duration,
280         base::TimeDelta::FromMilliseconds(1),
281         base::TimeDelta::FromMinutes(10),
282         100);
283 
284     if (is_ipv4) {
285       UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_No_Race",
286                                  connect_duration,
287                                  base::TimeDelta::FromMilliseconds(1),
288                                  base::TimeDelta::FromMinutes(10),
289                                  100);
290     } else {
291       if (AddressListOnlyContainsIPv6Addresses(addresses_)) {
292         UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Solo",
293                                    connect_duration,
294                                    base::TimeDelta::FromMilliseconds(1),
295                                    base::TimeDelta::FromMinutes(10),
296                                    100);
297       } else {
298         UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Raceable",
299                                    connect_duration,
300                                    base::TimeDelta::FromMilliseconds(1),
301                                    base::TimeDelta::FromMinutes(10),
302                                    100);
303       }
304     }
305     set_socket(transport_socket_.release());
306     fallback_timer_.Stop();
307   } else {
308     // Be a bit paranoid and kill off the fallback members to prevent reuse.
309     fallback_transport_socket_.reset();
310     fallback_addresses_.reset();
311   }
312 
313   return result;
314 }
315 
DoIPv6FallbackTransportConnect()316 void TransportConnectJob::DoIPv6FallbackTransportConnect() {
317   // The timer should only fire while we're waiting for the main connect to
318   // succeed.
319   if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
320     NOTREACHED();
321     return;
322   }
323 
324   DCHECK(!fallback_transport_socket_.get());
325   DCHECK(!fallback_addresses_.get());
326 
327   fallback_addresses_.reset(new AddressList(addresses_));
328   MakeAddrListStartWithIPv4(fallback_addresses_.get());
329   fallback_transport_socket_.reset(
330       client_socket_factory_->CreateTransportClientSocket(
331           *fallback_addresses_, net_log().net_log(), net_log().source()));
332   fallback_connect_start_time_ = base::TimeTicks::Now();
333 
334 #ifdef ANDROID
335   uid_t calling_uid = 0;
336   bool valid_uid = params_->getUID(&calling_uid);
337 #endif
338 
339   int rv = fallback_transport_socket_->Connect(&fallback_callback_
340 #ifdef ANDROID
341                                                , params_->ignore_limits()
342                                                , valid_uid
343                                                , calling_uid
344 #endif
345                                               );
346   if (rv != ERR_IO_PENDING)
347     DoIPv6FallbackTransportConnectComplete(rv);
348 }
349 
DoIPv6FallbackTransportConnectComplete(int result)350 void TransportConnectJob::DoIPv6FallbackTransportConnectComplete(int result) {
351   // This should only happen when we're waiting for the main connect to succeed.
352   if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
353     NOTREACHED();
354     return;
355   }
356 
357   DCHECK_NE(ERR_IO_PENDING, result);
358   DCHECK(fallback_transport_socket_.get());
359   DCHECK(fallback_addresses_.get());
360 
361   if (result == OK) {
362     DCHECK(fallback_connect_start_time_ != base::TimeTicks());
363     DCHECK(start_time_ != base::TimeTicks());
364     base::TimeTicks now = base::TimeTicks::Now();
365     base::TimeDelta total_duration = now - start_time_;
366     UMA_HISTOGRAM_CUSTOM_TIMES(
367         "Net.DNS_Resolution_And_TCP_Connection_Latency2",
368         total_duration,
369         base::TimeDelta::FromMilliseconds(1),
370         base::TimeDelta::FromMinutes(10),
371         100);
372 
373     base::TimeDelta connect_duration = now - fallback_connect_start_time_;
374     UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
375         connect_duration,
376         base::TimeDelta::FromMilliseconds(1),
377         base::TimeDelta::FromMinutes(10),
378         100);
379 
380     UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_Wins_Race",
381         connect_duration,
382         base::TimeDelta::FromMilliseconds(1),
383         base::TimeDelta::FromMinutes(10),
384         100);
385     set_socket(fallback_transport_socket_.release());
386     next_state_ = STATE_NONE;
387     transport_socket_.reset();
388   } else {
389     // Be a bit paranoid and kill off the fallback members to prevent reuse.
390     fallback_transport_socket_.reset();
391     fallback_addresses_.reset();
392   }
393   NotifyDelegateOfCompletion(result);  // Deletes |this|
394 }
395 
ConnectInternal()396 int TransportConnectJob::ConnectInternal() {
397   next_state_ = STATE_RESOLVE_HOST;
398   start_time_ = base::TimeTicks::Now();
399   return DoLoop(OK);
400 }
401 
402 ConnectJob*
NewConnectJob(const std::string & group_name,const PoolBase::Request & request,ConnectJob::Delegate * delegate) const403     TransportClientSocketPool::TransportConnectJobFactory::NewConnectJob(
404     const std::string& group_name,
405     const PoolBase::Request& request,
406     ConnectJob::Delegate* delegate) const {
407   return new TransportConnectJob(group_name,
408                                  request.params(),
409                                  ConnectionTimeout(),
410                                  client_socket_factory_,
411                                  host_resolver_,
412                                  delegate,
413                                  net_log_);
414 }
415 
416 base::TimeDelta
ConnectionTimeout() const417     TransportClientSocketPool::TransportConnectJobFactory::ConnectionTimeout()
418     const {
419   return base::TimeDelta::FromSeconds(kTransportConnectJobTimeoutInSeconds);
420 }
421 
TransportClientSocketPool(int max_sockets,int max_sockets_per_group,ClientSocketPoolHistograms * histograms,HostResolver * host_resolver,ClientSocketFactory * client_socket_factory,NetLog * net_log)422 TransportClientSocketPool::TransportClientSocketPool(
423     int max_sockets,
424     int max_sockets_per_group,
425     ClientSocketPoolHistograms* histograms,
426     HostResolver* host_resolver,
427     ClientSocketFactory* client_socket_factory,
428     NetLog* net_log)
429     : base_(max_sockets, max_sockets_per_group, histograms,
430             base::TimeDelta::FromSeconds(
431                 ClientSocketPool::unused_idle_socket_timeout()),
432             base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
433             new TransportConnectJobFactory(client_socket_factory,
434                                      host_resolver, net_log)) {
435   base_.EnableConnectBackupJobs();
436 }
437 
~TransportClientSocketPool()438 TransportClientSocketPool::~TransportClientSocketPool() {}
439 
RequestSocket(const std::string & group_name,const void * params,RequestPriority priority,ClientSocketHandle * handle,CompletionCallback * callback,const BoundNetLog & net_log)440 int TransportClientSocketPool::RequestSocket(
441     const std::string& group_name,
442     const void* params,
443     RequestPriority priority,
444     ClientSocketHandle* handle,
445     CompletionCallback* callback,
446     const BoundNetLog& net_log) {
447   const scoped_refptr<TransportSocketParams>* casted_params =
448       static_cast<const scoped_refptr<TransportSocketParams>*>(params);
449 
450   if (net_log.IsLoggingAllEvents()) {
451     // TODO(eroman): Split out the host and port parameters.
452     net_log.AddEvent(
453         NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
454         make_scoped_refptr(new NetLogStringParameter(
455             "host_and_port",
456             casted_params->get()->destination().host_port_pair().ToString())));
457   }
458 
459   return base_.RequestSocket(group_name, *casted_params, priority, handle,
460                              callback, net_log);
461 }
462 
RequestSockets(const std::string & group_name,const void * params,int num_sockets,const BoundNetLog & net_log)463 void TransportClientSocketPool::RequestSockets(
464     const std::string& group_name,
465     const void* params,
466     int num_sockets,
467     const BoundNetLog& net_log) {
468   const scoped_refptr<TransportSocketParams>* casted_params =
469       static_cast<const scoped_refptr<TransportSocketParams>*>(params);
470 
471   if (net_log.IsLoggingAllEvents()) {
472     // TODO(eroman): Split out the host and port parameters.
473     net_log.AddEvent(
474         NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS,
475         make_scoped_refptr(new NetLogStringParameter(
476             "host_and_port",
477             casted_params->get()->destination().host_port_pair().ToString())));
478   }
479 
480   base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
481 }
482 
CancelRequest(const std::string & group_name,ClientSocketHandle * handle)483 void TransportClientSocketPool::CancelRequest(
484     const std::string& group_name,
485     ClientSocketHandle* handle) {
486   base_.CancelRequest(group_name, handle);
487 }
488 
ReleaseSocket(const std::string & group_name,ClientSocket * socket,int id)489 void TransportClientSocketPool::ReleaseSocket(
490     const std::string& group_name,
491     ClientSocket* socket,
492     int id) {
493   base_.ReleaseSocket(group_name, socket, id);
494 }
495 
Flush()496 void TransportClientSocketPool::Flush() {
497   base_.Flush();
498 }
499 
CloseIdleSockets()500 void TransportClientSocketPool::CloseIdleSockets() {
501   base_.CloseIdleSockets();
502 }
503 
IdleSocketCount() const504 int TransportClientSocketPool::IdleSocketCount() const {
505   return base_.idle_socket_count();
506 }
507 
IdleSocketCountInGroup(const std::string & group_name) const508 int TransportClientSocketPool::IdleSocketCountInGroup(
509     const std::string& group_name) const {
510   return base_.IdleSocketCountInGroup(group_name);
511 }
512 
GetLoadState(const std::string & group_name,const ClientSocketHandle * handle) const513 LoadState TransportClientSocketPool::GetLoadState(
514     const std::string& group_name, const ClientSocketHandle* handle) const {
515   return base_.GetLoadState(group_name, handle);
516 }
517 
GetInfoAsValue(const std::string & name,const std::string & type,bool include_nested_pools) const518 DictionaryValue* TransportClientSocketPool::GetInfoAsValue(
519     const std::string& name,
520     const std::string& type,
521     bool include_nested_pools) const {
522   return base_.GetInfoAsValue(name, type);
523 }
524 
ConnectionTimeout() const525 base::TimeDelta TransportClientSocketPool::ConnectionTimeout() const {
526   return base_.ConnectionTimeout();
527 }
528 
histograms() const529 ClientSocketPoolHistograms* TransportClientSocketPool::histograms() const {
530   return base_.histograms();
531 }
532 
533 }  // namespace net
534