Home | History | Annotate | Download | only in socket
      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
      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/metrics/histogram.h"
     11 #include "base/string_util.h"
     12 #include "base/time.h"
     13 #include "net/base/net_log.h"
     14 #include "net/base/net_errors.h"
     15 #include "net/socket/client_socket_factory.h"
     16 #include "net/socket/client_socket_handle.h"
     17 #include "net/socket/client_socket_pool_base.h"
     18 #include "net/socket/tcp_client_socket.h"
     19 
     20 using base::TimeDelta;
     21 
     22 namespace net {
     23 
     24 TCPSocketParams::TCPSocketParams(const HostPortPair& host_port_pair,
     25                                  RequestPriority priority, const GURL& referrer,
     26                                  bool disable_resolver_cache, bool ignore_limits)
     27     : destination_(host_port_pair)
     28 #ifdef ANDROID
     29       , ignore_limits_(ignore_limits),
     30       valid_uid_(false),
     31       calling_uid_(0)
     32 #endif
     33     {
     34   Initialize(priority, referrer, disable_resolver_cache);
     35 }
     36 
     37 // TODO(willchan): Update all unittests so we don't need this.
     38 TCPSocketParams::TCPSocketParams(const std::string& host, int port,
     39                                  RequestPriority priority, const GURL& referrer,
     40                                  bool disable_resolver_cache)
     41     : destination_(HostPortPair(host, port))
     42 #ifdef ANDROID
     43       , ignore_limits_(false),
     44       valid_uid_(false),
     45       calling_uid_(0)
     46 #endif
     47     {
     48   Initialize(priority, referrer, disable_resolver_cache);
     49 }
     50 
     51 TCPSocketParams::~TCPSocketParams() {}
     52 
     53 void TCPSocketParams::Initialize(RequestPriority priority,
     54                                  const GURL& referrer,
     55                                  bool disable_resolver_cache) {
     56   // The referrer is used by the DNS prefetch system to correlate resolutions
     57   // with the page that triggered them. It doesn't impact the actual addresses
     58   // that we resolve to.
     59   destination_.set_referrer(referrer);
     60   destination_.set_priority(priority);
     61   if (disable_resolver_cache)
     62     destination_.set_allow_cached_response(false);
     63 }
     64 
     65 #ifdef ANDROID
     66 bool TCPSocketParams::getUID(uid_t *uid) const {
     67   if (!valid_uid_) {
     68     return false;
     69   }
     70   *uid = calling_uid_;
     71   return true;
     72 }
     73 
     74 void TCPSocketParams::setUID(uid_t uid) {
     75   valid_uid_ = true;
     76   calling_uid_ = uid;
     77 }
     78 #endif
     79 
     80 // TCPConnectJobs will time out after this many seconds.  Note this is the total
     81 // time, including both host resolution and TCP connect() times.
     82 //
     83 // TODO(eroman): The use of this constant needs to be re-evaluated. The time
     84 // needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since
     85 // the address list may contain many alternatives, and most of those may
     86 // timeout. Even worse, the per-connect timeout threshold varies greatly
     87 // between systems (anywhere from 20 seconds to 190 seconds).
     88 // See comment #12 at http://crbug.com/23364 for specifics.
     89 static const int kTCPConnectJobTimeoutInSeconds = 240;  // 4 minutes.
     90 
     91 TCPConnectJob::TCPConnectJob(
     92     const std::string& group_name,
     93     const scoped_refptr<TCPSocketParams>& params,
     94     base::TimeDelta timeout_duration,
     95     ClientSocketFactory* client_socket_factory,
     96     HostResolver* host_resolver,
     97     Delegate* delegate,
     98     NetLog* net_log)
     99     : ConnectJob(group_name, timeout_duration, delegate,
    100                  BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
    101       params_(params),
    102       client_socket_factory_(client_socket_factory),
    103       ALLOW_THIS_IN_INITIALIZER_LIST(
    104           callback_(this,
    105                     &TCPConnectJob::OnIOComplete)),
    106       resolver_(host_resolver) {}
    107 
    108 TCPConnectJob::~TCPConnectJob() {
    109   // We don't worry about cancelling the host resolution and TCP connect, since
    110   // ~SingleRequestHostResolver and ~ClientSocket will take care of it.
    111 }
    112 
    113 LoadState TCPConnectJob::GetLoadState() const {
    114   switch (next_state_) {
    115     case STATE_RESOLVE_HOST:
    116     case STATE_RESOLVE_HOST_COMPLETE:
    117       return LOAD_STATE_RESOLVING_HOST;
    118     case STATE_TCP_CONNECT:
    119     case STATE_TCP_CONNECT_COMPLETE:
    120       return LOAD_STATE_CONNECTING;
    121     default:
    122       NOTREACHED();
    123       return LOAD_STATE_IDLE;
    124   }
    125 }
    126 
    127 void TCPConnectJob::OnIOComplete(int result) {
    128   int rv = DoLoop(result);
    129   if (rv != ERR_IO_PENDING)
    130     NotifyDelegateOfCompletion(rv);  // Deletes |this|
    131 }
    132 
    133 int TCPConnectJob::DoLoop(int result) {
    134   DCHECK_NE(next_state_, STATE_NONE);
    135 
    136   int rv = result;
    137   do {
    138     State state = next_state_;
    139     next_state_ = STATE_NONE;
    140     switch (state) {
    141       case STATE_RESOLVE_HOST:
    142         DCHECK_EQ(OK, rv);
    143         rv = DoResolveHost();
    144         break;
    145       case STATE_RESOLVE_HOST_COMPLETE:
    146         rv = DoResolveHostComplete(rv);
    147         break;
    148       case STATE_TCP_CONNECT:
    149         DCHECK_EQ(OK, rv);
    150         rv = DoTCPConnect();
    151         break;
    152       case STATE_TCP_CONNECT_COMPLETE:
    153         rv = DoTCPConnectComplete(rv);
    154         break;
    155       default:
    156         NOTREACHED();
    157         rv = ERR_FAILED;
    158         break;
    159     }
    160   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
    161 
    162   return rv;
    163 }
    164 
    165 int TCPConnectJob::DoResolveHost() {
    166   next_state_ = STATE_RESOLVE_HOST_COMPLETE;
    167   return resolver_.Resolve(params_->destination(), &addresses_, &callback_,
    168                            net_log());
    169 }
    170 
    171 int TCPConnectJob::DoResolveHostComplete(int result) {
    172   if (result == OK)
    173     next_state_ = STATE_TCP_CONNECT;
    174   return result;
    175 }
    176 
    177 int TCPConnectJob::DoTCPConnect() {
    178   next_state_ = STATE_TCP_CONNECT_COMPLETE;
    179   set_socket(client_socket_factory_->CreateTCPClientSocket(
    180         addresses_, net_log().net_log(), net_log().source()));
    181   connect_start_time_ = base::TimeTicks::Now();
    182 
    183 #ifdef ANDROID
    184   uid_t calling_uid = 0;
    185   bool valid_uid = params_->getUID(&calling_uid);
    186 #endif
    187 
    188   return socket()->Connect(&callback_,
    189 #ifdef ANDROID
    190                            params_->ignore_limits(),
    191                            valid_uid,
    192                            calling_uid
    193 #endif
    194                           );
    195 }
    196 
    197 int TCPConnectJob::DoTCPConnectComplete(int result) {
    198   if (result == OK) {
    199     DCHECK(connect_start_time_ != base::TimeTicks());
    200     DCHECK(start_time_ != base::TimeTicks());
    201     base::TimeTicks now = base::TimeTicks::Now();
    202     base::TimeDelta total_duration = now - start_time_;
    203     UMA_HISTOGRAM_CUSTOM_TIMES(
    204         "Net.DNS_Resolution_And_TCP_Connection_Latency2",
    205         total_duration,
    206         base::TimeDelta::FromMilliseconds(1),
    207         base::TimeDelta::FromMinutes(10),
    208         100);
    209 
    210     base::TimeDelta connect_duration = now - connect_start_time_;
    211     UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
    212         connect_duration,
    213         base::TimeDelta::FromMilliseconds(1),
    214         base::TimeDelta::FromMinutes(10),
    215         100);
    216   } else {
    217     // Delete the socket on error.
    218     set_socket(NULL);
    219   }
    220 
    221   return result;
    222 }
    223 
    224 int TCPConnectJob::ConnectInternal() {
    225   next_state_ = STATE_RESOLVE_HOST;
    226   start_time_ = base::TimeTicks::Now();
    227   return DoLoop(OK);
    228 }
    229 
    230 ConnectJob* TCPClientSocketPool::TCPConnectJobFactory::NewConnectJob(
    231     const std::string& group_name,
    232     const PoolBase::Request& request,
    233     ConnectJob::Delegate* delegate) const {
    234   return new TCPConnectJob(group_name, request.params(), ConnectionTimeout(),
    235                            client_socket_factory_, host_resolver_, delegate,
    236                            net_log_);
    237 }
    238 
    239 base::TimeDelta
    240     TCPClientSocketPool::TCPConnectJobFactory::ConnectionTimeout() const {
    241   return base::TimeDelta::FromSeconds(kTCPConnectJobTimeoutInSeconds);
    242 }
    243 
    244 TCPClientSocketPool::TCPClientSocketPool(
    245     int max_sockets,
    246     int max_sockets_per_group,
    247     ClientSocketPoolHistograms* histograms,
    248     HostResolver* host_resolver,
    249     ClientSocketFactory* client_socket_factory,
    250     NetLog* net_log)
    251     : base_(max_sockets, max_sockets_per_group, histograms,
    252             base::TimeDelta::FromSeconds(
    253                 ClientSocketPool::unused_idle_socket_timeout()),
    254             base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
    255             new TCPConnectJobFactory(client_socket_factory,
    256                                      host_resolver, net_log)) {
    257   base_.EnableConnectBackupJobs();
    258 }
    259 
    260 TCPClientSocketPool::~TCPClientSocketPool() {}
    261 
    262 int TCPClientSocketPool::RequestSocket(
    263     const std::string& group_name,
    264     const void* params,
    265     RequestPriority priority,
    266     ClientSocketHandle* handle,
    267     CompletionCallback* callback,
    268     const BoundNetLog& net_log) {
    269   const scoped_refptr<TCPSocketParams>* casted_params =
    270       static_cast<const scoped_refptr<TCPSocketParams>*>(params);
    271 
    272   if (net_log.IsLoggingAllEvents()) {
    273     // TODO(eroman): Split out the host and port parameters.
    274     net_log.AddEvent(
    275         NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
    276         make_scoped_refptr(new NetLogStringParameter(
    277             "host_and_port",
    278             casted_params->get()->destination().host_port_pair().ToString())));
    279   }
    280 
    281   return base_.RequestSocket(group_name, *casted_params, priority, handle,
    282                              callback, net_log);
    283 }
    284 
    285 void TCPClientSocketPool::RequestSockets(
    286     const std::string& group_name,
    287     const void* params,
    288     int num_sockets,
    289     const BoundNetLog& net_log) {
    290   const scoped_refptr<TCPSocketParams>* casted_params =
    291       static_cast<const scoped_refptr<TCPSocketParams>*>(params);
    292 
    293   if (net_log.IsLoggingAllEvents()) {
    294     // TODO(eroman): Split out the host and port parameters.
    295     net_log.AddEvent(
    296         NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS,
    297         make_scoped_refptr(new NetLogStringParameter(
    298             "host_and_port",
    299             casted_params->get()->destination().host_port_pair().ToString())));
    300   }
    301 
    302   base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
    303 }
    304 
    305 void TCPClientSocketPool::CancelRequest(
    306     const std::string& group_name,
    307     ClientSocketHandle* handle) {
    308   base_.CancelRequest(group_name, handle);
    309 }
    310 
    311 void TCPClientSocketPool::ReleaseSocket(
    312     const std::string& group_name,
    313     ClientSocket* socket,
    314     int id) {
    315   base_.ReleaseSocket(group_name, socket, id);
    316 }
    317 
    318 void TCPClientSocketPool::Flush() {
    319   base_.Flush();
    320 }
    321 
    322 void TCPClientSocketPool::CloseIdleSockets() {
    323   base_.CloseIdleSockets();
    324 }
    325 
    326 int TCPClientSocketPool::IdleSocketCount() const {
    327   return base_.idle_socket_count();
    328 }
    329 
    330 int TCPClientSocketPool::IdleSocketCountInGroup(
    331     const std::string& group_name) const {
    332   return base_.IdleSocketCountInGroup(group_name);
    333 }
    334 
    335 LoadState TCPClientSocketPool::GetLoadState(
    336     const std::string& group_name, const ClientSocketHandle* handle) const {
    337   return base_.GetLoadState(group_name, handle);
    338 }
    339 
    340 DictionaryValue* TCPClientSocketPool::GetInfoAsValue(
    341     const std::string& name,
    342     const std::string& type,
    343     bool include_nested_pools) const {
    344   return base_.GetInfoAsValue(name, type);
    345 }
    346 
    347 base::TimeDelta TCPClientSocketPool::ConnectionTimeout() const {
    348   return base_.ConnectionTimeout();
    349 }
    350 
    351 ClientSocketPoolHistograms* TCPClientSocketPool::histograms() const {
    352   return base_.histograms();
    353 }
    354 
    355 }  // namespace net
    356