• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ClientSocketHandle()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 
~ClientSocketHandle()27 ClientSocketHandle::~ClientSocketHandle() {
28   Reset();
29 }
30 
Reset()31 void ClientSocketHandle::Reset() {
32   ResetInternal(true);
33   ResetErrorState();
34 }
35 
ResetInternal(bool cancel)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 
ResetErrorState()73 void ClientSocketHandle::ResetErrorState() {
74   is_ssl_error_ = false;
75   ssl_error_response_info_ = HttpResponseInfo();
76   pending_http_proxy_connection_.reset();
77 }
78 
GetLoadState() const79 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 
IsPoolStalled() const89 bool ClientSocketHandle::IsPoolStalled() const {
90   if (!pool_)
91     return false;
92   return pool_->IsStalled();
93 }
94 
AddHigherLayeredPool(HigherLayeredPool * higher_pool)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 
RemoveHigherLayeredPool(HigherLayeredPool * higher_pool)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 
GetLoadTimingInfo(bool is_reused,LoadTimingInfo * load_timing_info) const117 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 
SetSocket(scoped_ptr<StreamSocket> s)135 void ClientSocketHandle::SetSocket(scoped_ptr<StreamSocket> s) {
136   socket_ = s.Pass();
137 }
138 
OnIOComplete(int result)139 void ClientSocketHandle::OnIOComplete(int result) {
140   CompletionCallback callback = user_callback_;
141   user_callback_.Reset();
142   HandleInitCompletion(result);
143   callback.Run(result);
144 }
145 
PassSocket()146 scoped_ptr<StreamSocket> ClientSocketHandle::PassSocket() {
147   return socket_.Pass();
148 }
149 
HandleInitCompletion(int result)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