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/lazy_instance.h"
     11 #include "base/logging.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/metrics/histogram.h"
     14 #include "base/strings/string_util.h"
     15 #include "base/synchronization/lock.h"
     16 #include "base/time/time.h"
     17 #include "base/values.h"
     18 #include "net/base/ip_endpoint.h"
     19 #include "net/base/net_errors.h"
     20 #include "net/base/net_log.h"
     21 #include "net/socket/client_socket_factory.h"
     22 #include "net/socket/client_socket_handle.h"
     23 #include "net/socket/client_socket_pool_base.h"
     24 #include "net/socket/socket_net_log_params.h"
     25 #include "net/socket/tcp_client_socket.h"
     26 
     27 using base::TimeDelta;
     28 
     29 namespace net {
     30 
     31 // TODO(willchan): Base this off RTT instead of statically setting it. Note we
     32 // choose a timeout that is different from the backup connect job timer so they
     33 // don't synchronize.
     34 const int TransportConnectJobHelper::kIPv6FallbackTimerInMs = 300;
     35 
     36 namespace {
     37 
     38 // Returns true iff all addresses in |list| are in the IPv6 family.
     39 bool AddressListOnlyContainsIPv6(const AddressList& list) {
     40   DCHECK(!list.empty());
     41   for (AddressList::const_iterator iter = list.begin(); iter != list.end();
     42        ++iter) {
     43     if (iter->GetFamily() != ADDRESS_FAMILY_IPV6)
     44       return false;
     45   }
     46   return true;
     47 }
     48 
     49 }  // namespace
     50 
     51 // This lock protects |g_last_connect_time|.
     52 static base::LazyInstance<base::Lock>::Leaky
     53     g_last_connect_time_lock = LAZY_INSTANCE_INITIALIZER;
     54 
     55 // |g_last_connect_time| has the last time a connect() call is made.
     56 static base::LazyInstance<base::TimeTicks>::Leaky
     57     g_last_connect_time = LAZY_INSTANCE_INITIALIZER;
     58 
     59 TransportSocketParams::TransportSocketParams(
     60     const HostPortPair& host_port_pair,
     61     bool disable_resolver_cache,
     62     bool ignore_limits,
     63     const OnHostResolutionCallback& host_resolution_callback,
     64     CombineConnectAndWritePolicy combine_connect_and_write_if_supported)
     65     : destination_(host_port_pair),
     66       ignore_limits_(ignore_limits),
     67       host_resolution_callback_(host_resolution_callback),
     68       combine_connect_and_write_(combine_connect_and_write_if_supported) {
     69   if (disable_resolver_cache)
     70     destination_.set_allow_cached_response(false);
     71   // combine_connect_and_write currently translates to TCP FastOpen.
     72   // Enable TCP FastOpen if user wants it.
     73   if (combine_connect_and_write_ == COMBINE_CONNECT_AND_WRITE_DEFAULT) {
     74     IsTCPFastOpenUserEnabled() ? combine_connect_and_write_ =
     75         COMBINE_CONNECT_AND_WRITE_DESIRED :
     76         COMBINE_CONNECT_AND_WRITE_PROHIBITED;
     77   }
     78 }
     79 
     80 TransportSocketParams::~TransportSocketParams() {}
     81 
     82 // TransportConnectJobs will time out after this many seconds.  Note this is
     83 // the total time, including both host resolution and TCP connect() times.
     84 //
     85 // TODO(eroman): The use of this constant needs to be re-evaluated. The time
     86 // needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since
     87 // the address list may contain many alternatives, and most of those may
     88 // timeout. Even worse, the per-connect timeout threshold varies greatly
     89 // between systems (anywhere from 20 seconds to 190 seconds).
     90 // See comment #12 at http://crbug.com/23364 for specifics.
     91 static const int kTransportConnectJobTimeoutInSeconds = 240;  // 4 minutes.
     92 
     93 TransportConnectJobHelper::TransportConnectJobHelper(
     94     const scoped_refptr<TransportSocketParams>& params,
     95     ClientSocketFactory* client_socket_factory,
     96     HostResolver* host_resolver,
     97     LoadTimingInfo::ConnectTiming* connect_timing)
     98     : params_(params),
     99       client_socket_factory_(client_socket_factory),
    100       resolver_(host_resolver),
    101       next_state_(STATE_NONE),
    102       connect_timing_(connect_timing) {}
    103 
    104 TransportConnectJobHelper::~TransportConnectJobHelper() {}
    105 
    106 int TransportConnectJobHelper::DoResolveHost(RequestPriority priority,
    107                                              const BoundNetLog& net_log) {
    108   next_state_ = STATE_RESOLVE_HOST_COMPLETE;
    109   connect_timing_->dns_start = base::TimeTicks::Now();
    110 
    111   return resolver_.Resolve(
    112       params_->destination(), priority, &addresses_, on_io_complete_, net_log);
    113 }
    114 
    115 int TransportConnectJobHelper::DoResolveHostComplete(
    116     int result,
    117     const BoundNetLog& net_log) {
    118   connect_timing_->dns_end = base::TimeTicks::Now();
    119   // Overwrite connection start time, since for connections that do not go
    120   // through proxies, |connect_start| should not include dns lookup time.
    121   connect_timing_->connect_start = connect_timing_->dns_end;
    122 
    123   if (result == OK) {
    124     // Invoke callback, and abort if it fails.
    125     if (!params_->host_resolution_callback().is_null())
    126       result = params_->host_resolution_callback().Run(addresses_, net_log);
    127 
    128     if (result == OK)
    129       next_state_ = STATE_TRANSPORT_CONNECT;
    130   }
    131   return result;
    132 }
    133 
    134 base::TimeDelta TransportConnectJobHelper::HistogramDuration(
    135     ConnectionLatencyHistogram race_result) {
    136   DCHECK(!connect_timing_->connect_start.is_null());
    137   DCHECK(!connect_timing_->dns_start.is_null());
    138   base::TimeTicks now = base::TimeTicks::Now();
    139   base::TimeDelta total_duration = now - connect_timing_->dns_start;
    140   UMA_HISTOGRAM_CUSTOM_TIMES("Net.DNS_Resolution_And_TCP_Connection_Latency2",
    141                              total_duration,
    142                              base::TimeDelta::FromMilliseconds(1),
    143                              base::TimeDelta::FromMinutes(10),
    144                              100);
    145 
    146   base::TimeDelta connect_duration = now - connect_timing_->connect_start;
    147   UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
    148                              connect_duration,
    149                              base::TimeDelta::FromMilliseconds(1),
    150                              base::TimeDelta::FromMinutes(10),
    151                              100);
    152 
    153   switch (race_result) {
    154     case CONNECTION_LATENCY_IPV4_WINS_RACE:
    155       UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_Wins_Race",
    156                                  connect_duration,
    157                                  base::TimeDelta::FromMilliseconds(1),
    158                                  base::TimeDelta::FromMinutes(10),
    159                                  100);
    160       break;
    161 
    162     case CONNECTION_LATENCY_IPV4_NO_RACE:
    163       UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_No_Race",
    164                                  connect_duration,
    165                                  base::TimeDelta::FromMilliseconds(1),
    166                                  base::TimeDelta::FromMinutes(10),
    167                                  100);
    168       break;
    169 
    170     case CONNECTION_LATENCY_IPV6_RACEABLE:
    171       UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Raceable",
    172                                  connect_duration,
    173                                  base::TimeDelta::FromMilliseconds(1),
    174                                  base::TimeDelta::FromMinutes(10),
    175                                  100);
    176       break;
    177 
    178     case CONNECTION_LATENCY_IPV6_SOLO:
    179       UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Solo",
    180                                  connect_duration,
    181                                  base::TimeDelta::FromMilliseconds(1),
    182                                  base::TimeDelta::FromMinutes(10),
    183                                  100);
    184       break;
    185 
    186     default:
    187       NOTREACHED();
    188       break;
    189   }
    190 
    191   return connect_duration;
    192 }
    193 
    194 TransportConnectJob::TransportConnectJob(
    195     const std::string& group_name,
    196     RequestPriority priority,
    197     const scoped_refptr<TransportSocketParams>& params,
    198     base::TimeDelta timeout_duration,
    199     ClientSocketFactory* client_socket_factory,
    200     HostResolver* host_resolver,
    201     Delegate* delegate,
    202     NetLog* net_log)
    203     : ConnectJob(group_name, timeout_duration, priority, delegate,
    204                  BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
    205       helper_(params, client_socket_factory, host_resolver, &connect_timing_),
    206       interval_between_connects_(CONNECT_INTERVAL_GT_20MS) {
    207   helper_.SetOnIOComplete(this);
    208 }
    209 
    210 TransportConnectJob::~TransportConnectJob() {
    211   // We don't worry about cancelling the host resolution and TCP connect, since
    212   // ~SingleRequestHostResolver and ~StreamSocket will take care of it.
    213 }
    214 
    215 LoadState TransportConnectJob::GetLoadState() const {
    216   switch (helper_.next_state()) {
    217     case TransportConnectJobHelper::STATE_RESOLVE_HOST:
    218     case TransportConnectJobHelper::STATE_RESOLVE_HOST_COMPLETE:
    219       return LOAD_STATE_RESOLVING_HOST;
    220     case TransportConnectJobHelper::STATE_TRANSPORT_CONNECT:
    221     case TransportConnectJobHelper::STATE_TRANSPORT_CONNECT_COMPLETE:
    222       return LOAD_STATE_CONNECTING;
    223     case TransportConnectJobHelper::STATE_NONE:
    224       return LOAD_STATE_IDLE;
    225   }
    226   NOTREACHED();
    227   return LOAD_STATE_IDLE;
    228 }
    229 
    230 // static
    231 void TransportConnectJob::MakeAddressListStartWithIPv4(AddressList* list) {
    232   for (AddressList::iterator i = list->begin(); i != list->end(); ++i) {
    233     if (i->GetFamily() == ADDRESS_FAMILY_IPV4) {
    234       std::rotate(list->begin(), i, list->end());
    235       break;
    236     }
    237   }
    238 }
    239 
    240 int TransportConnectJob::DoResolveHost() {
    241   return helper_.DoResolveHost(priority(), net_log());
    242 }
    243 
    244 int TransportConnectJob::DoResolveHostComplete(int result) {
    245   return helper_.DoResolveHostComplete(result, net_log());
    246 }
    247 
    248 int TransportConnectJob::DoTransportConnect() {
    249   base::TimeTicks now = base::TimeTicks::Now();
    250   base::TimeTicks last_connect_time;
    251   {
    252     base::AutoLock lock(g_last_connect_time_lock.Get());
    253     last_connect_time = g_last_connect_time.Get();
    254     *g_last_connect_time.Pointer() = now;
    255   }
    256   if (last_connect_time.is_null()) {
    257     interval_between_connects_ = CONNECT_INTERVAL_GT_20MS;
    258   } else {
    259     int64 interval = (now - last_connect_time).InMilliseconds();
    260     if (interval <= 10)
    261       interval_between_connects_ = CONNECT_INTERVAL_LE_10MS;
    262     else if (interval <= 20)
    263       interval_between_connects_ = CONNECT_INTERVAL_LE_20MS;
    264     else
    265       interval_between_connects_ = CONNECT_INTERVAL_GT_20MS;
    266   }
    267 
    268   helper_.set_next_state(
    269       TransportConnectJobHelper::STATE_TRANSPORT_CONNECT_COMPLETE);
    270   transport_socket_ =
    271       helper_.client_socket_factory()->CreateTransportClientSocket(
    272           helper_.addresses(), net_log().net_log(), net_log().source());
    273 
    274   // If the list contains IPv6 and IPv4 addresses, the first address will
    275   // be IPv6, and the IPv4 addresses will be tried as fallback addresses,
    276   // per "Happy Eyeballs" (RFC 6555).
    277   bool try_ipv6_connect_with_ipv4_fallback =
    278       helper_.addresses().front().GetFamily() == ADDRESS_FAMILY_IPV6 &&
    279       !AddressListOnlyContainsIPv6(helper_.addresses());
    280 
    281   // Enable TCP FastOpen if indicated by transport socket params.
    282   // Note: We currently do not turn on TCP FastOpen for destinations where
    283   // we try a TCP connect over IPv6 with fallback to IPv4.
    284   if (!try_ipv6_connect_with_ipv4_fallback &&
    285       helper_.params()->combine_connect_and_write() ==
    286           TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DESIRED) {
    287     transport_socket_->EnableTCPFastOpenIfSupported();
    288   }
    289 
    290   int rv = transport_socket_->Connect(helper_.on_io_complete());
    291   if (rv == ERR_IO_PENDING && try_ipv6_connect_with_ipv4_fallback) {
    292     fallback_timer_.Start(
    293         FROM_HERE,
    294         base::TimeDelta::FromMilliseconds(
    295             TransportConnectJobHelper::kIPv6FallbackTimerInMs),
    296         this,
    297         &TransportConnectJob::DoIPv6FallbackTransportConnect);
    298   }
    299   return rv;
    300 }
    301 
    302 int TransportConnectJob::DoTransportConnectComplete(int result) {
    303   if (result == OK) {
    304     bool is_ipv4 =
    305         helper_.addresses().front().GetFamily() == ADDRESS_FAMILY_IPV4;
    306     TransportConnectJobHelper::ConnectionLatencyHistogram race_result =
    307         TransportConnectJobHelper::CONNECTION_LATENCY_UNKNOWN;
    308     if (is_ipv4) {
    309       race_result = TransportConnectJobHelper::CONNECTION_LATENCY_IPV4_NO_RACE;
    310     } else {
    311       if (AddressListOnlyContainsIPv6(helper_.addresses())) {
    312         race_result = TransportConnectJobHelper::CONNECTION_LATENCY_IPV6_SOLO;
    313       } else {
    314         race_result =
    315             TransportConnectJobHelper::CONNECTION_LATENCY_IPV6_RACEABLE;
    316       }
    317     }
    318     base::TimeDelta connect_duration = helper_.HistogramDuration(race_result);
    319     switch (interval_between_connects_) {
    320       case CONNECT_INTERVAL_LE_10MS:
    321         UMA_HISTOGRAM_CUSTOM_TIMES(
    322             "Net.TCP_Connection_Latency_Interval_LessThanOrEqual_10ms",
    323             connect_duration,
    324             base::TimeDelta::FromMilliseconds(1),
    325             base::TimeDelta::FromMinutes(10),
    326             100);
    327         break;
    328       case CONNECT_INTERVAL_LE_20MS:
    329         UMA_HISTOGRAM_CUSTOM_TIMES(
    330             "Net.TCP_Connection_Latency_Interval_LessThanOrEqual_20ms",
    331             connect_duration,
    332             base::TimeDelta::FromMilliseconds(1),
    333             base::TimeDelta::FromMinutes(10),
    334             100);
    335         break;
    336       case CONNECT_INTERVAL_GT_20MS:
    337         UMA_HISTOGRAM_CUSTOM_TIMES(
    338             "Net.TCP_Connection_Latency_Interval_GreaterThan_20ms",
    339             connect_duration,
    340             base::TimeDelta::FromMilliseconds(1),
    341             base::TimeDelta::FromMinutes(10),
    342             100);
    343         break;
    344       default:
    345         NOTREACHED();
    346         break;
    347     }
    348 
    349     SetSocket(transport_socket_.Pass());
    350     fallback_timer_.Stop();
    351   } else {
    352     // Be a bit paranoid and kill off the fallback members to prevent reuse.
    353     fallback_transport_socket_.reset();
    354     fallback_addresses_.reset();
    355   }
    356 
    357   return result;
    358 }
    359 
    360 void TransportConnectJob::DoIPv6FallbackTransportConnect() {
    361   // The timer should only fire while we're waiting for the main connect to
    362   // succeed.
    363   if (helper_.next_state() !=
    364       TransportConnectJobHelper::STATE_TRANSPORT_CONNECT_COMPLETE) {
    365     NOTREACHED();
    366     return;
    367   }
    368 
    369   DCHECK(!fallback_transport_socket_.get());
    370   DCHECK(!fallback_addresses_.get());
    371 
    372   fallback_addresses_.reset(new AddressList(helper_.addresses()));
    373   MakeAddressListStartWithIPv4(fallback_addresses_.get());
    374   fallback_transport_socket_ =
    375       helper_.client_socket_factory()->CreateTransportClientSocket(
    376           *fallback_addresses_, net_log().net_log(), net_log().source());
    377   fallback_connect_start_time_ = base::TimeTicks::Now();
    378   int rv = fallback_transport_socket_->Connect(
    379       base::Bind(
    380           &TransportConnectJob::DoIPv6FallbackTransportConnectComplete,
    381           base::Unretained(this)));
    382   if (rv != ERR_IO_PENDING)
    383     DoIPv6FallbackTransportConnectComplete(rv);
    384 }
    385 
    386 void TransportConnectJob::DoIPv6FallbackTransportConnectComplete(int result) {
    387   // This should only happen when we're waiting for the main connect to succeed.
    388   if (helper_.next_state() !=
    389       TransportConnectJobHelper::STATE_TRANSPORT_CONNECT_COMPLETE) {
    390     NOTREACHED();
    391     return;
    392   }
    393 
    394   DCHECK_NE(ERR_IO_PENDING, result);
    395   DCHECK(fallback_transport_socket_.get());
    396   DCHECK(fallback_addresses_.get());
    397 
    398   if (result == OK) {
    399     DCHECK(!fallback_connect_start_time_.is_null());
    400     connect_timing_.connect_start = fallback_connect_start_time_;
    401     helper_.HistogramDuration(
    402         TransportConnectJobHelper::CONNECTION_LATENCY_IPV4_WINS_RACE);
    403     SetSocket(fallback_transport_socket_.Pass());
    404     helper_.set_next_state(TransportConnectJobHelper::STATE_NONE);
    405     transport_socket_.reset();
    406   } else {
    407     // Be a bit paranoid and kill off the fallback members to prevent reuse.
    408     fallback_transport_socket_.reset();
    409     fallback_addresses_.reset();
    410   }
    411   NotifyDelegateOfCompletion(result);  // Deletes |this|
    412 }
    413 
    414 int TransportConnectJob::ConnectInternal() {
    415   return helper_.DoConnectInternal(this);
    416 }
    417 
    418 scoped_ptr<ConnectJob>
    419 TransportClientSocketPool::TransportConnectJobFactory::NewConnectJob(
    420     const std::string& group_name,
    421     const PoolBase::Request& request,
    422     ConnectJob::Delegate* delegate) const {
    423   return scoped_ptr<ConnectJob>(
    424       new TransportConnectJob(group_name,
    425                               request.priority(),
    426                               request.params(),
    427                               ConnectionTimeout(),
    428                               client_socket_factory_,
    429                               host_resolver_,
    430                               delegate,
    431                               net_log_));
    432 }
    433 
    434 base::TimeDelta
    435     TransportClientSocketPool::TransportConnectJobFactory::ConnectionTimeout()
    436     const {
    437   return base::TimeDelta::FromSeconds(kTransportConnectJobTimeoutInSeconds);
    438 }
    439 
    440 TransportClientSocketPool::TransportClientSocketPool(
    441     int max_sockets,
    442     int max_sockets_per_group,
    443     ClientSocketPoolHistograms* histograms,
    444     HostResolver* host_resolver,
    445     ClientSocketFactory* client_socket_factory,
    446     NetLog* net_log)
    447     : base_(NULL, max_sockets, max_sockets_per_group, histograms,
    448             ClientSocketPool::unused_idle_socket_timeout(),
    449             ClientSocketPool::used_idle_socket_timeout(),
    450             new TransportConnectJobFactory(client_socket_factory,
    451                                            host_resolver, net_log)) {
    452   base_.EnableConnectBackupJobs();
    453 }
    454 
    455 TransportClientSocketPool::~TransportClientSocketPool() {}
    456 
    457 int TransportClientSocketPool::RequestSocket(
    458     const std::string& group_name,
    459     const void* params,
    460     RequestPriority priority,
    461     ClientSocketHandle* handle,
    462     const CompletionCallback& callback,
    463     const BoundNetLog& net_log) {
    464   const scoped_refptr<TransportSocketParams>* casted_params =
    465       static_cast<const scoped_refptr<TransportSocketParams>*>(params);
    466 
    467   NetLogTcpClientSocketPoolRequestedSocket(net_log, casted_params);
    468 
    469   return base_.RequestSocket(group_name, *casted_params, priority, handle,
    470                              callback, net_log);
    471 }
    472 
    473 void TransportClientSocketPool::NetLogTcpClientSocketPoolRequestedSocket(
    474     const BoundNetLog& net_log,
    475     const scoped_refptr<TransportSocketParams>* casted_params) {
    476   if (net_log.IsLogging()) {
    477     // TODO(eroman): Split out the host and port parameters.
    478     net_log.AddEvent(
    479         NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
    480         CreateNetLogHostPortPairCallback(
    481             &casted_params->get()->destination().host_port_pair()));
    482   }
    483 }
    484 
    485 void TransportClientSocketPool::RequestSockets(
    486     const std::string& group_name,
    487     const void* params,
    488     int num_sockets,
    489     const BoundNetLog& net_log) {
    490   const scoped_refptr<TransportSocketParams>* casted_params =
    491       static_cast<const scoped_refptr<TransportSocketParams>*>(params);
    492 
    493   if (net_log.IsLogging()) {
    494     // TODO(eroman): Split out the host and port parameters.
    495     net_log.AddEvent(
    496         NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKETS,
    497         CreateNetLogHostPortPairCallback(
    498             &casted_params->get()->destination().host_port_pair()));
    499   }
    500 
    501   base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
    502 }
    503 
    504 void TransportClientSocketPool::CancelRequest(
    505     const std::string& group_name,
    506     ClientSocketHandle* handle) {
    507   base_.CancelRequest(group_name, handle);
    508 }
    509 
    510 void TransportClientSocketPool::ReleaseSocket(
    511     const std::string& group_name,
    512     scoped_ptr<StreamSocket> socket,
    513     int id) {
    514   base_.ReleaseSocket(group_name, socket.Pass(), id);
    515 }
    516 
    517 void TransportClientSocketPool::FlushWithError(int error) {
    518   base_.FlushWithError(error);
    519 }
    520 
    521 void TransportClientSocketPool::CloseIdleSockets() {
    522   base_.CloseIdleSockets();
    523 }
    524 
    525 int TransportClientSocketPool::IdleSocketCount() const {
    526   return base_.idle_socket_count();
    527 }
    528 
    529 int TransportClientSocketPool::IdleSocketCountInGroup(
    530     const std::string& group_name) const {
    531   return base_.IdleSocketCountInGroup(group_name);
    532 }
    533 
    534 LoadState TransportClientSocketPool::GetLoadState(
    535     const std::string& group_name, const ClientSocketHandle* handle) const {
    536   return base_.GetLoadState(group_name, handle);
    537 }
    538 
    539 base::DictionaryValue* TransportClientSocketPool::GetInfoAsValue(
    540     const std::string& name,
    541     const std::string& type,
    542     bool include_nested_pools) const {
    543   return base_.GetInfoAsValue(name, type);
    544 }
    545 
    546 base::TimeDelta TransportClientSocketPool::ConnectionTimeout() const {
    547   return base_.ConnectionTimeout();
    548 }
    549 
    550 ClientSocketPoolHistograms* TransportClientSocketPool::histograms() const {
    551   return base_.histograms();
    552 }
    553 
    554 bool TransportClientSocketPool::IsStalled() const {
    555   return base_.IsStalled();
    556 }
    557 
    558 void TransportClientSocketPool::AddHigherLayeredPool(
    559     HigherLayeredPool* higher_pool) {
    560   base_.AddHigherLayeredPool(higher_pool);
    561 }
    562 
    563 void TransportClientSocketPool::RemoveHigherLayeredPool(
    564     HigherLayeredPool* higher_pool) {
    565   base_.RemoveHigherLayeredPool(higher_pool);
    566 }
    567 
    568 }  // namespace net
    569