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