|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/socket/client_socket_handle.h" | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/metrics/histogram.h" | 
|  | #include "base/logging.h" | 
|  | #include "net/base/net_errors.h" | 
|  | #include "net/socket/client_socket_pool.h" | 
|  | #include "net/socket/client_socket_pool_histograms.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | ClientSocketHandle::ClientSocketHandle() | 
|  | : is_initialized_(false), | 
|  | pool_(NULL), | 
|  | layered_pool_(NULL), | 
|  | is_reused_(false), | 
|  | ALLOW_THIS_IN_INITIALIZER_LIST(callback_( | 
|  | base::Bind(&ClientSocketHandle::OnIOComplete, | 
|  | base::Unretained(this)))), | 
|  | is_ssl_error_(false) {} | 
|  |  | 
|  | ClientSocketHandle::~ClientSocketHandle() { | 
|  | Reset(); | 
|  | } | 
|  |  | 
|  | void ClientSocketHandle::Reset() { | 
|  | ResetInternal(true); | 
|  | ResetErrorState(); | 
|  | } | 
|  |  | 
|  | void ClientSocketHandle::ResetInternal(bool cancel) { | 
|  | if (group_name_.empty())  // Was Init called? | 
|  | return; | 
|  | if (is_initialized()) { | 
|  | // Because of http://crbug.com/37810 we may not have a pool, but have | 
|  | // just a raw socket. | 
|  | socket_->NetLog().EndEvent(NetLog::TYPE_SOCKET_IN_USE); | 
|  | if (pool_) | 
|  | // If we've still got a socket, release it back to the ClientSocketPool so | 
|  | // it can be deleted or reused. | 
|  | pool_->ReleaseSocket(group_name_, release_socket(), pool_id_); | 
|  | } else if (cancel) { | 
|  | // If we did not get initialized yet, we've got a socket request pending. | 
|  | // Cancel it. | 
|  | pool_->CancelRequest(group_name_, this); | 
|  | } | 
|  | is_initialized_ = false; | 
|  | group_name_.clear(); | 
|  | is_reused_ = false; | 
|  | user_callback_.Reset(); | 
|  | if (layered_pool_) { | 
|  | pool_->RemoveLayeredPool(layered_pool_); | 
|  | layered_pool_ = NULL; | 
|  | } | 
|  | pool_ = NULL; | 
|  | idle_time_ = base::TimeDelta(); | 
|  | init_time_ = base::TimeTicks(); | 
|  | setup_time_ = base::TimeDelta(); | 
|  | pool_id_ = -1; | 
|  | } | 
|  |  | 
|  | void ClientSocketHandle::ResetErrorState() { | 
|  | is_ssl_error_ = false; | 
|  | ssl_error_response_info_ = HttpResponseInfo(); | 
|  | pending_http_proxy_connection_.reset(); | 
|  | } | 
|  |  | 
|  | LoadState ClientSocketHandle::GetLoadState() const { | 
|  | CHECK(!is_initialized()); | 
|  | CHECK(!group_name_.empty()); | 
|  | // Because of http://crbug.com/37810  we may not have a pool, but have | 
|  | // just a raw socket. | 
|  | if (!pool_) | 
|  | return LOAD_STATE_IDLE; | 
|  | return pool_->GetLoadState(group_name_, this); | 
|  | } | 
|  |  | 
|  | bool ClientSocketHandle::IsPoolStalled() const { | 
|  | return pool_->IsStalled(); | 
|  | } | 
|  |  | 
|  | void ClientSocketHandle::AddLayeredPool(LayeredPool* layered_pool) { | 
|  | CHECK(layered_pool); | 
|  | CHECK(!layered_pool_); | 
|  | if (pool_) { | 
|  | pool_->AddLayeredPool(layered_pool); | 
|  | layered_pool_ = layered_pool; | 
|  | } | 
|  | } | 
|  |  | 
|  | void ClientSocketHandle::RemoveLayeredPool(LayeredPool* layered_pool) { | 
|  | CHECK(layered_pool); | 
|  | CHECK(layered_pool_); | 
|  | if (pool_) { | 
|  | pool_->RemoveLayeredPool(layered_pool); | 
|  | layered_pool_ = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | void ClientSocketHandle::OnIOComplete(int result) { | 
|  | CompletionCallback callback = user_callback_; | 
|  | user_callback_.Reset(); | 
|  | HandleInitCompletion(result); | 
|  | callback.Run(result); | 
|  | } | 
|  |  | 
|  | void ClientSocketHandle::HandleInitCompletion(int result) { | 
|  | CHECK_NE(ERR_IO_PENDING, result); | 
|  | if (result != OK) { | 
|  | if (!socket_.get()) | 
|  | ResetInternal(false);  // Nothing to cancel since the request failed. | 
|  | else | 
|  | is_initialized_ = true; | 
|  | return; | 
|  | } | 
|  | is_initialized_ = true; | 
|  | CHECK_NE(-1, pool_id_) << "Pool should have set |pool_id_| to a valid value."; | 
|  | setup_time_ = base::TimeTicks::Now() - init_time_; | 
|  |  | 
|  | ClientSocketPoolHistograms* histograms = pool_->histograms(); | 
|  | histograms->AddSocketType(reuse_type()); | 
|  | switch (reuse_type()) { | 
|  | case ClientSocketHandle::UNUSED: | 
|  | histograms->AddRequestTime(setup_time()); | 
|  | break; | 
|  | case ClientSocketHandle::UNUSED_IDLE: | 
|  | histograms->AddUnusedIdleTime(idle_time()); | 
|  | break; | 
|  | case ClientSocketHandle::REUSED_IDLE: | 
|  | histograms->AddReusedIdleTime(idle_time()); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Broadcast that the socket has been acquired. | 
|  | // TODO(eroman): This logging is not complete, in particular set_socket() and | 
|  | // release() socket. It ends up working though, since those methods are being | 
|  | // used to layer sockets (and the destination sources are the same). | 
|  | DCHECK(socket_.get()); | 
|  | socket_->NetLog().BeginEvent( | 
|  | NetLog::TYPE_SOCKET_IN_USE, | 
|  | requesting_source_.ToEventParametersCallback()); | 
|  | } | 
|  |  | 
|  | }  // namespace net |