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