Home | History | Annotate | Download | only in socket
      1 // Copyright (c) 2012 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 <algorithm>
      8 
      9 #include "base/compiler_specific.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/time/time.h"
     15 #include "base/values.h"
     16 #include "net/base/ip_endpoint.h"
     17 #include "net/base/net_errors.h"
     18 #include "net/base/net_log.h"
     19 #include "net/socket/client_socket_factory.h"
     20 #include "net/socket/client_socket_handle.h"
     21 #include "net/socket/client_socket_pool_base.h"
     22 #include "net/socket/socket_net_log_params.h"
     23 #include "net/socket/tcp_client_socket.h"
     24 
     25 using base::TimeDelta;
     26 
     27 namespace net {
     28 
     29 // TODO(willchan): Base this off RTT instead of statically setting it. Note we
     30 // choose a timeout that is different from the backup connect job timer so they
     31 // don't synchronize.
     32 const int TransportConnectJob::kIPv6FallbackTimerInMs = 300;
     33 
     34 namespace {
     35 
     36 // Returns true iff all addresses in |list| are in the IPv6 family.
     37 bool AddressListOnlyContainsIPv6(const AddressList& list) {
     38   DCHECK(!list.empty());
     39   for (AddressList::const_iterator iter = list.begin(); iter != list.end();
     40        ++iter) {
     41     if (iter->GetFamily() != ADDRESS_FAMILY_IPV6)
     42       return false;
     43   }
     44   return true;
     45 }
     46 
     47 }  // namespace
     48 
     49 TransportSocketParams::TransportSocketParams(
     50     const HostPortPair& host_port_pair,
     51     RequestPriority priority,
     52     bool disable_resolver_cache,
     53     bool ignore_limits,
     54     const OnHostResolutionCallback& host_resolution_callback)
     55     : destination_(host_port_pair),
     56       ignore_limits_(ignore_limits),
     57       host_resolution_callback_(host_resolution_callback) {
     58   Initialize(priority, disable_resolver_cache);
     59 }
     60 
     61 TransportSocketParams::~TransportSocketParams() {}
     62 
     63 void TransportSocketParams::Initialize(RequestPriority priority,
     64                                        bool disable_resolver_cache) {
     65   destination_.set_priority(priority);
     66   if (disable_resolver_cache)
     67     destination_.set_allow_cached_response(false);
     68 }
     69 
     70 // TransportConnectJobs will time out after this many seconds.  Note this is
     71 // the total time, including both host resolution and TCP connect() times.
     72 //
     73 // TODO(eroman): The use of this constant needs to be re-evaluated. The time
     74 // needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since
     75 // the address list may contain many alternatives, and most of those may
     76 // timeout. Even worse, the per-connect timeout threshold varies greatly
     77 // between systems (anywhere from 20 seconds to 190 seconds).
     78 // See comment #12 at http://crbug.com/23364 for specifics.
     79 static const int kTransportConnectJobTimeoutInSeconds = 240;  // 4 minutes.
     80 
     81 TransportConnectJob::TransportConnectJob(
     82     const std::string& group_name,
     83     const scoped_refptr<TransportSocketParams>& params,
     84     base::TimeDelta timeout_duration,
     85     ClientSocketFactory* client_socket_factory,
     86     HostResolver* host_resolver,
     87     Delegate* delegate,
     88     NetLog* net_log)
     89     : ConnectJob(group_name, timeout_duration, delegate,
     90                  BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
     91       params_(params),
     92       client_socket_factory_(client_socket_factory),
     93       resolver_(host_resolver),
     94       next_state_(STATE_NONE) {
     95 }
     96 
     97 TransportConnectJob::~TransportConnectJob() {
     98   // We don't worry about cancelling the host resolution and TCP connect, since
     99   // ~SingleRequestHostResolver and ~StreamSocket will take care of it.
    100 }
    101 
    102 LoadState TransportConnectJob::GetLoadState() const {
    103   switch (next_state_) {
    104     case STATE_RESOLVE_HOST:
    105     case STATE_RESOLVE_HOST_COMPLETE:
    106       return LOAD_STATE_RESOLVING_HOST;
    107     case STATE_TRANSPORT_CONNECT:
    108     case STATE_TRANSPORT_CONNECT_COMPLETE:
    109       return LOAD_STATE_CONNECTING;
    110     default:
    111       NOTREACHED();
    112       return LOAD_STATE_IDLE;
    113   }
    114 }
    115 
    116 // static
    117 void TransportConnectJob::MakeAddressListStartWithIPv4(AddressList* list) {
    118   for (AddressList::iterator i = list->begin(); i != list->end(); ++i) {
    119     if (i->GetFamily() == ADDRESS_FAMILY_IPV4) {
    120       std::rotate(list->begin(), i, list->end());
    121       break;
    122     }
    123   }
    124 }
    125 
    126 void TransportConnectJob::OnIOComplete(int result) {
    127   int rv = DoLoop(result);
    128   if (rv != ERR_IO_PENDING)
    129     NotifyDelegateOfCompletion(rv);  // Deletes |this|
    130 }
    131 
    132 int TransportConnectJob::DoLoop(int result) {
    133   DCHECK_NE(next_state_, STATE_NONE);
    134 
    135   int rv = result;
    136   do {
    137     State state = next_state_;
    138     next_state_ = STATE_NONE;
    139     switch (state) {
    140       case STATE_RESOLVE_HOST:
    141         DCHECK_EQ(OK, rv);
    142         rv = DoResolveHost();
    143         break;
    144       case STATE_RESOLVE_HOST_COMPLETE:
    145         rv = DoResolveHostComplete(rv);
    146         break;
    147       case STATE_TRANSPORT_CONNECT:
    148         DCHECK_EQ(OK, rv);
    149         rv = DoTransportConnect();
    150         break;
    151       case STATE_TRANSPORT_CONNECT_COMPLETE:
    152         rv = DoTransportConnectComplete(rv);
    153         break;
    154       default:
    155         NOTREACHED();
    156         rv = ERR_FAILED;
    157         break;
    158     }
    159   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
    160 
    161   return rv;
    162 }
    163 
    164 int TransportConnectJob::DoResolveHost() {
    165   next_state_ = STATE_RESOLVE_HOST_COMPLETE;
    166   connect_timing_.dns_start = base::TimeTicks::Now();
    167 
    168   return resolver_.Resolve(
    169       params_->destination(), &addresses_,
    170       base::Bind(&TransportConnectJob::OnIOComplete, base::Unretained(this)),
    171       net_log());
    172 }
    173 
    174 int TransportConnectJob::DoResolveHostComplete(int result) {
    175   connect_timing_.dns_end = base::TimeTicks::Now();
    176   // Overwrite connection start time, since for connections that do not go
    177   // through proxies, |connect_start| should not include dns lookup time.
    178   connect_timing_.connect_start = connect_timing_.dns_end;
    179 
    180   if (result == OK) {
    181     // Invoke callback, and abort if it fails.
    182     if (!params_->host_resolution_callback().is_null())
    183       result = params_->host_resolution_callback().Run(addresses_, net_log());
    184 
    185     if (result == OK)
    186       next_state_ = STATE_TRANSPORT_CONNECT;
    187   }
    188   return result;
    189 }
    190 
    191 int TransportConnectJob::DoTransportConnect() {
    192   next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
    193   transport_socket_.reset(client_socket_factory_->CreateTransportClientSocket(
    194         addresses_, net_log().net_log(), net_log().source()));
    195   int rv = transport_socket_->Connect(
    196       base::Bind(&TransportConnectJob::OnIOComplete, base::Unretained(this)));
    197   if (rv == ERR_IO_PENDING &&
    198       addresses_.front().GetFamily() == ADDRESS_FAMILY_IPV6 &&
    199       !AddressListOnlyContainsIPv6(addresses_)) {
    200     fallback_timer_.Start(FROM_HERE,
    201         base::TimeDelta::FromMilliseconds(kIPv6FallbackTimerInMs),
    202         this, &TransportConnectJob::DoIPv6FallbackTransportConnect);
    203   }
    204   return rv;
    205 }
    206 
    207 int TransportConnectJob::DoTransportConnectComplete(int result) {
    208   if (result == OK) {
    209     bool is_ipv4 = addresses_.front().GetFamily() == ADDRESS_FAMILY_IPV4;
    210     DCHECK(!connect_timing_.connect_start.is_null());
    211     DCHECK(!connect_timing_.dns_start.is_null());
    212     base::TimeTicks now = base::TimeTicks::Now();
    213     base::TimeDelta total_duration = now - connect_timing_.dns_start;
    214     UMA_HISTOGRAM_CUSTOM_TIMES(
    215         "Net.DNS_Resolution_And_TCP_Connection_Latency2",
    216         total_duration,
    217         base::TimeDelta::FromMilliseconds(1),
    218         base::TimeDelta::FromMinutes(10),
    219         100);
    220 
    221     base::TimeDelta connect_duration = now - connect_timing_.connect_start;
    222     UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
    223         connect_duration,
    224         base::TimeDelta::FromMilliseconds(1),
    225         base::TimeDelta::FromMinutes(10),
    226         100);
    227 
    228     if (is_ipv4) {
    229       UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_No_Race",
    230                                  connect_duration,
    231                                  base::TimeDelta::FromMilliseconds(1),
    232                                  base::TimeDelta::FromMinutes(10),
    233                                  100);
    234     } else {
    235       if (AddressListOnlyContainsIPv6(addresses_)) {
    236         UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Solo",
    237                                    connect_duration,
    238                                    base::TimeDelta::FromMilliseconds(1),
    239                                    base::TimeDelta::FromMinutes(10),
    240                                    100);
    241       } else {
    242         UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Raceable",
    243                                    connect_duration,
    244                                    base::TimeDelta::FromMilliseconds(1),
    245                                    base::TimeDelta::FromMinutes(10),
    246                                    100);
    247       }
    248     }
    249     set_socket(transport_socket_.release());
    250     fallback_timer_.Stop();
    251   } else {
    252     // Be a bit paranoid and kill off the fallback members to prevent reuse.
    253     fallback_transport_socket_.reset();
    254     fallback_addresses_.reset();
    255   }
    256 
    257   return result;
    258 }
    259 
    260 void TransportConnectJob::DoIPv6FallbackTransportConnect() {
    261   // The timer should only fire while we're waiting for the main connect to
    262   // succeed.
    263   if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
    264     NOTREACHED();
    265     return;
    266   }
    267 
    268   DCHECK(!fallback_transport_socket_.get());
    269   DCHECK(!fallback_addresses_.get());
    270 
    271   fallback_addresses_.reset(new AddressList(addresses_));
    272   MakeAddressListStartWithIPv4(fallback_addresses_.get());
    273   fallback_transport_socket_.reset(
    274       client_socket_factory_->CreateTransportClientSocket(
    275           *fallback_addresses_, net_log().net_log(), net_log().source()));
    276   fallback_connect_start_time_ = base::TimeTicks::Now();
    277   int rv = fallback_transport_socket_->Connect(
    278       base::Bind(
    279           &TransportConnectJob::DoIPv6FallbackTransportConnectComplete,
    280           base::Unretained(this)));
    281   if (rv != ERR_IO_PENDING)
    282     DoIPv6FallbackTransportConnectComplete(rv);
    283 }
    284 
    285 void TransportConnectJob::DoIPv6FallbackTransportConnectComplete(int result) {
    286   // This should only happen when we're waiting for the main connect to succeed.
    287   if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
    288     NOTREACHED();
    289     return;
    290   }
    291 
    292   DCHECK_NE(ERR_IO_PENDING, result);
    293   DCHECK(fallback_transport_socket_.get());
    294   DCHECK(fallback_addresses_.get());
    295 
    296   if (result == OK) {
    297     DCHECK(!fallback_connect_start_time_.is_null());
    298     DCHECK(!connect_timing_.dns_start.is_null());
    299     base::TimeTicks now = base::TimeTicks::Now();
    300     base::TimeDelta total_duration = now - connect_timing_.dns_start;
    301     UMA_HISTOGRAM_CUSTOM_TIMES(
    302         "Net.DNS_Resolution_And_TCP_Connection_Latency2",
    303         total_duration,
    304         base::TimeDelta::FromMilliseconds(1),
    305         base::TimeDelta::FromMinutes(10),
    306         100);
    307 
    308     base::TimeDelta connect_duration = now - fallback_connect_start_time_;
    309     UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
    310         connect_duration,
    311         base::TimeDelta::FromMilliseconds(1),
    312         base::TimeDelta::FromMinutes(10),
    313         100);
    314 
    315     UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_Wins_Race",
    316         connect_duration,
    317         base::TimeDelta::FromMilliseconds(1),
    318         base::TimeDelta::FromMinutes(10),
    319         100);
    320     set_socket(fallback_transport_socket_.release());
    321     next_state_ = STATE_NONE;
    322     transport_socket_.reset();
    323   } else {
    324     // Be a bit paranoid and kill off the fallback members to prevent reuse.
    325     fallback_transport_socket_.reset();
    326     fallback_addresses_.reset();
    327   }
    328   NotifyDelegateOfCompletion(result);  // Deletes |this|
    329 }
    330 
    331 int TransportConnectJob::ConnectInternal() {
    332   next_state_ = STATE_RESOLVE_HOST;
    333   return DoLoop(OK);
    334 }
    335 
    336 ConnectJob*
    337     TransportClientSocketPool::TransportConnectJobFactory::NewConnectJob(
    338     const std::string& group_name,
    339     const PoolBase::Request& request,
    340     ConnectJob::Delegate* delegate) const {
    341   return new TransportConnectJob(group_name,
    342                                  request.params(),
    343                                  ConnectionTimeout(),
    344                                  client_socket_factory_,
    345                                  host_resolver_,
    346                                  delegate,
    347                                  net_log_);
    348 }
    349 
    350 base::TimeDelta
    351     TransportClientSocketPool::TransportConnectJobFactory::ConnectionTimeout()
    352     const {
    353   return base::TimeDelta::FromSeconds(kTransportConnectJobTimeoutInSeconds);
    354 }
    355 
    356 TransportClientSocketPool::TransportClientSocketPool(
    357     int max_sockets,
    358     int max_sockets_per_group,
    359     ClientSocketPoolHistograms* histograms,
    360     HostResolver* host_resolver,
    361     ClientSocketFactory* client_socket_factory,
    362     NetLog* net_log)
    363     : base_(max_sockets, max_sockets_per_group, histograms,
    364             ClientSocketPool::unused_idle_socket_timeout(),
    365             ClientSocketPool::used_idle_socket_timeout(),
    366             new TransportConnectJobFactory(client_socket_factory,
    367                                      host_resolver, net_log)) {
    368   base_.EnableConnectBackupJobs();
    369 }
    370 
    371 TransportClientSocketPool::~TransportClientSocketPool() {}
    372 
    373 int TransportClientSocketPool::RequestSocket(
    374     const std::string& group_name,
    375     const void* params,
    376     RequestPriority priority,
    377     ClientSocketHandle* handle,
    378     const CompletionCallback& callback,
    379     const BoundNetLog& net_log) {
    380   const scoped_refptr<TransportSocketParams>* casted_params =
    381       static_cast<const scoped_refptr<TransportSocketParams>*>(params);
    382 
    383   if (net_log.IsLoggingAllEvents()) {
    384     // TODO(eroman): Split out the host and port parameters.
    385     net_log.AddEvent(
    386         NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
    387         CreateNetLogHostPortPairCallback(
    388             &casted_params->get()->destination().host_port_pair()));
    389   }
    390 
    391   return base_.RequestSocket(group_name, *casted_params, priority, handle,
    392                              callback, net_log);
    393 }
    394 
    395 void TransportClientSocketPool::RequestSockets(
    396     const std::string& group_name,
    397     const void* params,
    398     int num_sockets,
    399     const BoundNetLog& net_log) {
    400   const scoped_refptr<TransportSocketParams>* casted_params =
    401       static_cast<const scoped_refptr<TransportSocketParams>*>(params);
    402 
    403   if (net_log.IsLoggingAllEvents()) {
    404     // TODO(eroman): Split out the host and port parameters.
    405     net_log.AddEvent(
    406         NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS,
    407         CreateNetLogHostPortPairCallback(
    408             &casted_params->get()->destination().host_port_pair()));
    409   }
    410 
    411   base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
    412 }
    413 
    414 void TransportClientSocketPool::CancelRequest(
    415     const std::string& group_name,
    416     ClientSocketHandle* handle) {
    417   base_.CancelRequest(group_name, handle);
    418 }
    419 
    420 void TransportClientSocketPool::ReleaseSocket(
    421     const std::string& group_name,
    422     StreamSocket* socket,
    423     int id) {
    424   base_.ReleaseSocket(group_name, socket, id);
    425 }
    426 
    427 void TransportClientSocketPool::FlushWithError(int error) {
    428   base_.FlushWithError(error);
    429 }
    430 
    431 bool TransportClientSocketPool::IsStalled() const {
    432   return base_.IsStalled();
    433 }
    434 
    435 void TransportClientSocketPool::CloseIdleSockets() {
    436   base_.CloseIdleSockets();
    437 }
    438 
    439 int TransportClientSocketPool::IdleSocketCount() const {
    440   return base_.idle_socket_count();
    441 }
    442 
    443 int TransportClientSocketPool::IdleSocketCountInGroup(
    444     const std::string& group_name) const {
    445   return base_.IdleSocketCountInGroup(group_name);
    446 }
    447 
    448 LoadState TransportClientSocketPool::GetLoadState(
    449     const std::string& group_name, const ClientSocketHandle* handle) const {
    450   return base_.GetLoadState(group_name, handle);
    451 }
    452 
    453 void TransportClientSocketPool::AddLayeredPool(LayeredPool* layered_pool) {
    454   base_.AddLayeredPool(layered_pool);
    455 }
    456 
    457 void TransportClientSocketPool::RemoveLayeredPool(LayeredPool* layered_pool) {
    458   base_.RemoveLayeredPool(layered_pool);
    459 }
    460 
    461 base::DictionaryValue* TransportClientSocketPool::GetInfoAsValue(
    462     const std::string& name,
    463     const std::string& type,
    464     bool include_nested_pools) const {
    465   return base_.GetInfoAsValue(name, type);
    466 }
    467 
    468 base::TimeDelta TransportClientSocketPool::ConnectionTimeout() const {
    469   return base_.ConnectionTimeout();
    470 }
    471 
    472 ClientSocketPoolHistograms* TransportClientSocketPool::histograms() const {
    473   return base_.histograms();
    474 }
    475 
    476 }  // namespace net
    477