Home | History | Annotate | Download | only in socket
      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 
     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)
     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 
     74 TransportSocketParams::~TransportSocketParams() {}
     75 
     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
     89 bool TransportSocketParams::getUID(uid_t *uid) const {
     90   if (!valid_uid_) {
     91     return false;
     92   }
     93   *uid = calling_uid_;
     94   return true;
     95 }
     96 
     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 
    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 
    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 
    140 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
    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 
    186 void TransportConnectJob::OnIOComplete(int result) {
    187   int rv = DoLoop(result);
    188   if (rv != ERR_IO_PENDING)
    189     NotifyDelegateOfCompletion(rv);  // Deletes |this|
    190 }
    191 
    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 
    224 int TransportConnectJob::DoResolveHost() {
    225   next_state_ = STATE_RESOLVE_HOST_COMPLETE;
    226   return resolver_.Resolve(params_->destination(), &addresses_, &callback_,
    227                            net_log());
    228 }
    229 
    230 int TransportConnectJob::DoResolveHostComplete(int result) {
    231   if (result == OK)
    232     next_state_ = STATE_TRANSPORT_CONNECT;
    233   return result;
    234 }
    235 
    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 
    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 
    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 
    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 
    396 int TransportConnectJob::ConnectInternal() {
    397   next_state_ = STATE_RESOLVE_HOST;
    398   start_time_ = base::TimeTicks::Now();
    399   return DoLoop(OK);
    400 }
    401 
    402 ConnectJob*
    403     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
    417     TransportClientSocketPool::TransportConnectJobFactory::ConnectionTimeout()
    418     const {
    419   return base::TimeDelta::FromSeconds(kTransportConnectJobTimeoutInSeconds);
    420 }
    421 
    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 
    438 TransportClientSocketPool::~TransportClientSocketPool() {}
    439 
    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 
    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 
    483 void TransportClientSocketPool::CancelRequest(
    484     const std::string& group_name,
    485     ClientSocketHandle* handle) {
    486   base_.CancelRequest(group_name, handle);
    487 }
    488 
    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 
    496 void TransportClientSocketPool::Flush() {
    497   base_.Flush();
    498 }
    499 
    500 void TransportClientSocketPool::CloseIdleSockets() {
    501   base_.CloseIdleSockets();
    502 }
    503 
    504 int TransportClientSocketPool::IdleSocketCount() const {
    505   return base_.idle_socket_count();
    506 }
    507 
    508 int TransportClientSocketPool::IdleSocketCountInGroup(
    509     const std::string& group_name) const {
    510   return base_.IdleSocketCountInGroup(group_name);
    511 }
    512 
    513 LoadState TransportClientSocketPool::GetLoadState(
    514     const std::string& group_name, const ClientSocketHandle* handle) const {
    515   return base_.GetLoadState(group_name, handle);
    516 }
    517 
    518 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 
    525 base::TimeDelta TransportClientSocketPool::ConnectionTimeout() const {
    526   return base_.ConnectionTimeout();
    527 }
    528 
    529 ClientSocketPoolHistograms* TransportClientSocketPool::histograms() const {
    530   return base_.histograms();
    531 }
    532 
    533 }  // namespace net
    534