Home | History | Annotate | Download | only in socket
      1 // Copyright (c) 2006-2008 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/tcp_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/string_util.h"
     11 #include "base/time.h"
     12 #include "net/base/load_log.h"
     13 #include "net/base/net_errors.h"
     14 #include "net/socket/client_socket_factory.h"
     15 #include "net/socket/client_socket_handle.h"
     16 #include "net/socket/client_socket_pool_base.h"
     17 #include "net/socket/tcp_client_socket.h"
     18 
     19 using base::TimeDelta;
     20 
     21 namespace net {
     22 
     23 // TCPConnectJobs will time out after this many seconds.  Note this is the total
     24 // time, including both host resolution and TCP connect() times.
     25 //
     26 // TODO(eroman): The use of this constant needs to be re-evaluated. The time
     27 // needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since
     28 // the address list may contain many alternatives, and most of those may
     29 // timeout. Even worse, the per-connect timeout threshold varies greatly
     30 // between systems (anywhere from 20 seconds to 190 seconds).
     31 // See comment #12 at http://crbug.com/23364 for specifics.
     32 static const int kTCPConnectJobTimeoutInSeconds = 240; // 4 minutes.
     33 
     34 TCPConnectJob::TCPConnectJob(
     35     const std::string& group_name,
     36     const HostResolver::RequestInfo& resolve_info,
     37     base::TimeDelta timeout_duration,
     38     ClientSocketFactory* client_socket_factory,
     39     HostResolver* host_resolver,
     40     Delegate* delegate,
     41     LoadLog* load_log)
     42     : ConnectJob(group_name, timeout_duration, delegate, load_log),
     43       resolve_info_(resolve_info),
     44       client_socket_factory_(client_socket_factory),
     45       ALLOW_THIS_IN_INITIALIZER_LIST(
     46           callback_(this,
     47                     &TCPConnectJob::OnIOComplete)),
     48       resolver_(host_resolver) {}
     49 
     50 TCPConnectJob::~TCPConnectJob() {
     51   // We don't worry about cancelling the host resolution and TCP connect, since
     52   // ~SingleRequestHostResolver and ~ClientSocket will take care of it.
     53 }
     54 
     55 LoadState TCPConnectJob::GetLoadState() const {
     56   switch (next_state_) {
     57     case kStateResolveHost:
     58     case kStateResolveHostComplete:
     59       return LOAD_STATE_RESOLVING_HOST;
     60     case kStateTCPConnect:
     61     case kStateTCPConnectComplete:
     62       return LOAD_STATE_CONNECTING;
     63     default:
     64       NOTREACHED();
     65       return LOAD_STATE_IDLE;
     66   }
     67 }
     68 
     69 int TCPConnectJob::ConnectInternal() {
     70   next_state_ = kStateResolveHost;
     71   start_time_ = base::TimeTicks::Now();
     72   return DoLoop(OK);
     73 }
     74 
     75 void TCPConnectJob::OnIOComplete(int result) {
     76   int rv = DoLoop(result);
     77   if (rv != ERR_IO_PENDING)
     78     NotifyDelegateOfCompletion(rv);  // Deletes |this|
     79 }
     80 
     81 int TCPConnectJob::DoLoop(int result) {
     82   DCHECK_NE(next_state_, kStateNone);
     83 
     84   int rv = result;
     85   do {
     86     State state = next_state_;
     87     next_state_ = kStateNone;
     88     switch (state) {
     89       case kStateResolveHost:
     90         DCHECK_EQ(OK, rv);
     91         rv = DoResolveHost();
     92         break;
     93       case kStateResolveHostComplete:
     94         rv = DoResolveHostComplete(rv);
     95         break;
     96       case kStateTCPConnect:
     97         DCHECK_EQ(OK, rv);
     98         rv = DoTCPConnect();
     99         break;
    100       case kStateTCPConnectComplete:
    101         rv = DoTCPConnectComplete(rv);
    102         break;
    103       default:
    104         NOTREACHED();
    105         rv = ERR_FAILED;
    106         break;
    107     }
    108   } while (rv != ERR_IO_PENDING && next_state_ != kStateNone);
    109 
    110   return rv;
    111 }
    112 
    113 int TCPConnectJob::DoResolveHost() {
    114   next_state_ = kStateResolveHostComplete;
    115   return resolver_.Resolve(resolve_info_, &addresses_, &callback_, load_log());
    116 }
    117 
    118 int TCPConnectJob::DoResolveHostComplete(int result) {
    119   if (result == OK)
    120     next_state_ = kStateTCPConnect;
    121   return result;
    122 }
    123 
    124 int TCPConnectJob::DoTCPConnect() {
    125   next_state_ = kStateTCPConnectComplete;
    126   set_socket(client_socket_factory_->CreateTCPClientSocket(addresses_));
    127   connect_start_time_ = base::TimeTicks::Now();
    128   return socket()->Connect(&callback_, load_log());
    129 }
    130 
    131 int TCPConnectJob::DoTCPConnectComplete(int result) {
    132   if (result == OK) {
    133     DCHECK(connect_start_time_ != base::TimeTicks());
    134     DCHECK(start_time_ != base::TimeTicks());
    135     base::TimeTicks now = base::TimeTicks::Now();
    136     base::TimeDelta total_duration = now - start_time_;
    137     UMA_HISTOGRAM_CUSTOM_TIMES(
    138         "Net.DNS_Resolution_And_TCP_Connection_Latency2",
    139         total_duration,
    140         base::TimeDelta::FromMilliseconds(1),
    141         base::TimeDelta::FromMinutes(10),
    142         100);
    143 
    144     base::TimeDelta connect_duration = now - connect_start_time_;
    145     UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
    146         connect_duration,
    147         base::TimeDelta::FromMilliseconds(1),
    148         base::TimeDelta::FromMinutes(10),
    149         100);
    150   } else {
    151     // Delete the socket on error.
    152     set_socket(NULL);
    153   }
    154 
    155   return result;
    156 }
    157 
    158 ConnectJob* TCPClientSocketPool::TCPConnectJobFactory::NewConnectJob(
    159     const std::string& group_name,
    160     const PoolBase::Request& request,
    161     ConnectJob::Delegate* delegate,
    162     LoadLog* load_log) const {
    163   return new TCPConnectJob(
    164       group_name, request.params(),
    165       base::TimeDelta::FromSeconds(kTCPConnectJobTimeoutInSeconds),
    166       client_socket_factory_, host_resolver_, delegate, load_log);
    167 }
    168 
    169 TCPClientSocketPool::TCPClientSocketPool(
    170     int max_sockets,
    171     int max_sockets_per_group,
    172     HostResolver* host_resolver,
    173     ClientSocketFactory* client_socket_factory,
    174     NetworkChangeNotifier* network_change_notifier)
    175     : base_(max_sockets, max_sockets_per_group,
    176             base::TimeDelta::FromSeconds(kUnusedIdleSocketTimeout),
    177             base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
    178             new TCPConnectJobFactory(client_socket_factory, host_resolver),
    179             network_change_notifier) {}
    180 
    181 TCPClientSocketPool::~TCPClientSocketPool() {}
    182 
    183 int TCPClientSocketPool::RequestSocket(
    184     const std::string& group_name,
    185     const void* resolve_info,
    186     RequestPriority priority,
    187     ClientSocketHandle* handle,
    188     CompletionCallback* callback,
    189     LoadLog* load_log) {
    190   const HostResolver::RequestInfo* casted_resolve_info =
    191       static_cast<const HostResolver::RequestInfo*>(resolve_info);
    192 
    193   if (LoadLog::IsUnbounded(load_log)) {
    194     LoadLog::AddString(
    195         load_log,
    196         StringPrintf("Requested TCP socket to: %s [port %d]",
    197                      casted_resolve_info->hostname().c_str(),
    198                      casted_resolve_info->port()));
    199   }
    200 
    201   return base_.RequestSocket(
    202       group_name, *casted_resolve_info, priority, handle, callback, load_log);
    203 }
    204 
    205 void TCPClientSocketPool::CancelRequest(
    206     const std::string& group_name,
    207     const ClientSocketHandle* handle) {
    208   base_.CancelRequest(group_name, handle);
    209 }
    210 
    211 void TCPClientSocketPool::ReleaseSocket(
    212     const std::string& group_name,
    213     ClientSocket* socket) {
    214   base_.ReleaseSocket(group_name, socket);
    215 }
    216 
    217 void TCPClientSocketPool::CloseIdleSockets() {
    218   base_.CloseIdleSockets();
    219 }
    220 
    221 int TCPClientSocketPool::IdleSocketCountInGroup(
    222     const std::string& group_name) const {
    223   return base_.IdleSocketCountInGroup(group_name);
    224 }
    225 
    226 LoadState TCPClientSocketPool::GetLoadState(
    227     const std::string& group_name, const ClientSocketHandle* handle) const {
    228   return base_.GetLoadState(group_name, handle);
    229 }
    230 
    231 }  // namespace net
    232