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/client_socket_handle.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/compiler_specific.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/logging.h"
     12 #include "net/base/net_errors.h"
     13 #include "net/socket/client_socket_pool.h"
     14 #include "net/socket/client_socket_pool_histograms.h"
     15 
     16 namespace net {
     17 
     18 ClientSocketHandle::ClientSocketHandle()
     19     : is_initialized_(false),
     20       pool_(NULL),
     21       higher_pool_(NULL),
     22       reuse_type_(ClientSocketHandle::UNUSED),
     23       callback_(base::Bind(&ClientSocketHandle::OnIOComplete,
     24                            base::Unretained(this))),
     25       is_ssl_error_(false) {}
     26 
     27 ClientSocketHandle::~ClientSocketHandle() {
     28   Reset();
     29 }
     30 
     31 void ClientSocketHandle::Reset() {
     32   ResetInternal(true);
     33   ResetErrorState();
     34 }
     35 
     36 void ClientSocketHandle::ResetInternal(bool cancel) {
     37   // Was Init called?
     38   if (!group_name_.empty()) {
     39     // If so, we must have a pool.
     40     CHECK(pool_);
     41     if (is_initialized()) {
     42       if (socket_) {
     43         socket_->NetLog().EndEvent(NetLog::TYPE_SOCKET_IN_USE);
     44         // Release the socket back to the ClientSocketPool so it can be
     45         // deleted or reused.
     46         pool_->ReleaseSocket(group_name_, socket_.Pass(), pool_id_);
     47       } else {
     48         // If the handle has been initialized, we should still have a
     49         // socket.
     50         NOTREACHED();
     51       }
     52     } else if (cancel) {
     53       // If we did not get initialized yet and we have a socket
     54       // request pending, cancel it.
     55       pool_->CancelRequest(group_name_, this);
     56     }
     57   }
     58   is_initialized_ = false;
     59   socket_.reset();
     60   group_name_.clear();
     61   reuse_type_ = ClientSocketHandle::UNUSED;
     62   user_callback_.Reset();
     63   if (higher_pool_)
     64     RemoveHigherLayeredPool(higher_pool_);
     65   pool_ = NULL;
     66   idle_time_ = base::TimeDelta();
     67   init_time_ = base::TimeTicks();
     68   setup_time_ = base::TimeDelta();
     69   connect_timing_ = LoadTimingInfo::ConnectTiming();
     70   pool_id_ = -1;
     71 }
     72 
     73 void ClientSocketHandle::ResetErrorState() {
     74   is_ssl_error_ = false;
     75   ssl_error_response_info_ = HttpResponseInfo();
     76   pending_http_proxy_connection_.reset();
     77 }
     78 
     79 LoadState ClientSocketHandle::GetLoadState() const {
     80   CHECK(!is_initialized());
     81   CHECK(!group_name_.empty());
     82   // Because of http://crbug.com/37810  we may not have a pool, but have
     83   // just a raw socket.
     84   if (!pool_)
     85     return LOAD_STATE_IDLE;
     86   return pool_->GetLoadState(group_name_, this);
     87 }
     88 
     89 bool ClientSocketHandle::IsPoolStalled() const {
     90   if (!pool_)
     91     return false;
     92   return pool_->IsStalled();
     93 }
     94 
     95 void ClientSocketHandle::AddHigherLayeredPool(HigherLayeredPool* higher_pool) {
     96   CHECK(higher_pool);
     97   CHECK(!higher_pool_);
     98   // TODO(mmenke):  |pool_| should only be NULL in tests.  Maybe stop doing that
     99   // so this be be made into a DCHECK, and the same can be done in
    100   // RemoveHigherLayeredPool?
    101   if (pool_) {
    102     pool_->AddHigherLayeredPool(higher_pool);
    103     higher_pool_ = higher_pool;
    104   }
    105 }
    106 
    107 void ClientSocketHandle::RemoveHigherLayeredPool(
    108     HigherLayeredPool* higher_pool) {
    109   CHECK(higher_pool_);
    110   CHECK_EQ(higher_pool_, higher_pool);
    111   if (pool_) {
    112     pool_->RemoveHigherLayeredPool(higher_pool);
    113     higher_pool_ = NULL;
    114   }
    115 }
    116 
    117 bool ClientSocketHandle::GetLoadTimingInfo(
    118     bool is_reused,
    119     LoadTimingInfo* load_timing_info) const {
    120   // Only return load timing information when there's a socket.
    121   if (!socket_)
    122     return false;
    123 
    124   load_timing_info->socket_log_id = socket_->NetLog().source().id;
    125   load_timing_info->socket_reused = is_reused;
    126 
    127   // No times if the socket is reused.
    128   if (is_reused)
    129     return true;
    130 
    131   load_timing_info->connect_timing = connect_timing_;
    132   return true;
    133 }
    134 
    135 void ClientSocketHandle::SetSocket(scoped_ptr<StreamSocket> s) {
    136   socket_ = s.Pass();
    137 }
    138 
    139 void ClientSocketHandle::OnIOComplete(int result) {
    140   CompletionCallback callback = user_callback_;
    141   user_callback_.Reset();
    142   HandleInitCompletion(result);
    143   callback.Run(result);
    144 }
    145 
    146 scoped_ptr<StreamSocket> ClientSocketHandle::PassSocket() {
    147   return socket_.Pass();
    148 }
    149 
    150 void ClientSocketHandle::HandleInitCompletion(int result) {
    151   CHECK_NE(ERR_IO_PENDING, result);
    152   ClientSocketPoolHistograms* histograms = pool_->histograms();
    153   histograms->AddErrorCode(result);
    154   if (result != OK) {
    155     if (!socket_.get())
    156       ResetInternal(false);  // Nothing to cancel since the request failed.
    157     else
    158       is_initialized_ = true;
    159     return;
    160   }
    161   is_initialized_ = true;
    162   CHECK_NE(-1, pool_id_) << "Pool should have set |pool_id_| to a valid value.";
    163   setup_time_ = base::TimeTicks::Now() - init_time_;
    164 
    165   histograms->AddSocketType(reuse_type());
    166   switch (reuse_type()) {
    167     case ClientSocketHandle::UNUSED:
    168       histograms->AddRequestTime(setup_time());
    169       break;
    170     case ClientSocketHandle::UNUSED_IDLE:
    171       histograms->AddUnusedIdleTime(idle_time());
    172       break;
    173     case ClientSocketHandle::REUSED_IDLE:
    174       histograms->AddReusedIdleTime(idle_time());
    175       break;
    176     default:
    177       NOTREACHED();
    178       break;
    179   }
    180 
    181   // Broadcast that the socket has been acquired.
    182   // TODO(eroman): This logging is not complete, in particular set_socket() and
    183   // release() socket. It ends up working though, since those methods are being
    184   // used to layer sockets (and the destination sources are the same).
    185   DCHECK(socket_.get());
    186   socket_->NetLog().BeginEvent(
    187       NetLog::TYPE_SOCKET_IN_USE,
    188       requesting_source_.ToEventParametersCallback());
    189 }
    190 
    191 }  // namespace net
    192