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 <utility>
8 
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/check_op.h"
12 #include "base/compiler_specific.h"
13 #include "base/notreached.h"
14 #include "base/trace_event/trace_event.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/trace_constants.h"
17 #include "net/log/net_log_event_type.h"
18 #include "net/socket/client_socket_pool.h"
19 #include "net/socket/connect_job.h"
20 
21 namespace net {
22 
ClientSocketHandle()23 ClientSocketHandle::ClientSocketHandle()
24     : is_initialized_(false),
25       pool_(nullptr),
26       higher_pool_(nullptr),
27       reuse_type_(ClientSocketHandle::UNUSED),
28       group_generation_(-1),
29       resolve_error_info_(ResolveErrorInfo(OK)),
30       is_ssl_error_(false) {}
31 
~ClientSocketHandle()32 ClientSocketHandle::~ClientSocketHandle() {
33   Reset();
34 }
35 
Init(const ClientSocketPool::GroupId & group_id,scoped_refptr<ClientSocketPool::SocketParams> socket_params,const base::Optional<NetworkTrafficAnnotationTag> & proxy_annotation_tag,RequestPriority priority,const SocketTag & socket_tag,ClientSocketPool::RespectLimits respect_limits,CompletionOnceCallback callback,const ClientSocketPool::ProxyAuthCallback & proxy_auth_callback,ClientSocketPool * pool,const NetLogWithSource & net_log)36 int ClientSocketHandle::Init(
37     const ClientSocketPool::GroupId& group_id,
38     scoped_refptr<ClientSocketPool::SocketParams> socket_params,
39     const base::Optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag,
40     RequestPriority priority,
41     const SocketTag& socket_tag,
42     ClientSocketPool::RespectLimits respect_limits,
43     CompletionOnceCallback callback,
44     const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback,
45     ClientSocketPool* pool,
46     const NetLogWithSource& net_log) {
47   requesting_source_ = net_log.source();
48 
49   CHECK(!group_id.destination().IsEmpty());
50   ResetInternal(true /* cancel */, false /* cancel_connect_job */);
51   ResetErrorState();
52   pool_ = pool;
53   group_id_ = group_id;
54   CompletionOnceCallback io_complete_callback =
55       base::BindOnce(&ClientSocketHandle::OnIOComplete, base::Unretained(this));
56   int rv = pool_->RequestSocket(
57       group_id, std::move(socket_params), proxy_annotation_tag, priority,
58       socket_tag, respect_limits, this, std::move(io_complete_callback),
59       proxy_auth_callback, net_log);
60   if (rv == ERR_IO_PENDING) {
61     callback_ = std::move(callback);
62   } else {
63     HandleInitCompletion(rv);
64   }
65   return rv;
66 }
67 
SetPriority(RequestPriority priority)68 void ClientSocketHandle::SetPriority(RequestPriority priority) {
69   if (socket_) {
70     // The priority of the handle is no longer relevant to the socket pool;
71     // just return.
72     return;
73   }
74 
75   if (pool_)
76     pool_->SetPriority(group_id_, this, priority);
77 }
78 
Reset()79 void ClientSocketHandle::Reset() {
80   ResetInternal(true /* cancel */, false /* cancel_connect_job */);
81   ResetErrorState();
82 }
83 
ResetAndCloseSocket()84 void ClientSocketHandle::ResetAndCloseSocket() {
85   if (is_initialized() && socket_)
86     socket_->Disconnect();
87   ResetInternal(true /* cancel */, true /* cancel_connect_job */);
88   ResetErrorState();
89 }
90 
GetLoadState() const91 LoadState ClientSocketHandle::GetLoadState() const {
92   CHECK(!is_initialized());
93   CHECK(!group_id_.destination().IsEmpty());
94   // Because of http://crbug.com/37810  we may not have a pool, but have
95   // just a raw socket.
96   if (!pool_)
97     return LOAD_STATE_IDLE;
98   return pool_->GetLoadState(group_id_, this);
99 }
100 
IsPoolStalled() const101 bool ClientSocketHandle::IsPoolStalled() const {
102   if (!pool_)
103     return false;
104   return pool_->IsStalled();
105 }
106 
AddHigherLayeredPool(HigherLayeredPool * higher_pool)107 void ClientSocketHandle::AddHigherLayeredPool(HigherLayeredPool* higher_pool) {
108   CHECK(higher_pool);
109   CHECK(!higher_pool_);
110   // TODO(mmenke):  |pool_| should only be NULL in tests.  Maybe stop doing that
111   // so this be be made into a DCHECK, and the same can be done in
112   // RemoveHigherLayeredPool?
113   if (pool_) {
114     pool_->AddHigherLayeredPool(higher_pool);
115     higher_pool_ = higher_pool;
116   }
117 }
118 
RemoveHigherLayeredPool(HigherLayeredPool * higher_pool)119 void ClientSocketHandle::RemoveHigherLayeredPool(
120     HigherLayeredPool* higher_pool) {
121   CHECK(higher_pool_);
122   CHECK_EQ(higher_pool_, higher_pool);
123   if (pool_) {
124     pool_->RemoveHigherLayeredPool(higher_pool);
125     higher_pool_ = nullptr;
126   }
127 }
128 
CloseIdleSocketsInGroup(const char * net_log_reason_utf8)129 void ClientSocketHandle::CloseIdleSocketsInGroup(
130     const char* net_log_reason_utf8) {
131   if (pool_)
132     pool_->CloseIdleSocketsInGroup(group_id_, net_log_reason_utf8);
133 }
134 
GetLoadTimingInfo(bool is_reused,LoadTimingInfo * load_timing_info) const135 bool ClientSocketHandle::GetLoadTimingInfo(
136     bool is_reused,
137     LoadTimingInfo* load_timing_info) const {
138   if (socket_) {
139     load_timing_info->socket_log_id = socket_->NetLog().source().id;
140   } else {
141     // Only return load timing information when there's a socket.
142     return false;
143   }
144 
145   load_timing_info->socket_reused = is_reused;
146 
147   // No times if the socket is reused.
148   if (is_reused)
149     return true;
150 
151   load_timing_info->connect_timing = connect_timing_;
152   return true;
153 }
154 
DumpMemoryStats(StreamSocket::SocketMemoryStats * stats) const155 void ClientSocketHandle::DumpMemoryStats(
156     StreamSocket::SocketMemoryStats* stats) const {
157   if (!socket_)
158     return;
159   socket_->DumpMemoryStats(stats);
160 }
161 
SetSocket(std::unique_ptr<StreamSocket> s)162 void ClientSocketHandle::SetSocket(std::unique_ptr<StreamSocket> s) {
163   socket_ = std::move(s);
164 }
165 
SetAdditionalErrorState(ConnectJob * connect_job)166 void ClientSocketHandle::SetAdditionalErrorState(ConnectJob* connect_job) {
167   connection_attempts_ = connect_job->GetConnectionAttempts();
168 
169   resolve_error_info_ = connect_job->GetResolveErrorInfo();
170   is_ssl_error_ = connect_job->IsSSLError();
171   ssl_cert_request_info_ = connect_job->GetCertRequestInfo();
172 }
173 
PassSocket()174 std::unique_ptr<StreamSocket> ClientSocketHandle::PassSocket() {
175   return std::move(socket_);
176 }
177 
OnIOComplete(int result)178 void ClientSocketHandle::OnIOComplete(int result) {
179   TRACE_EVENT0(NetTracingCategory(), "ClientSocketHandle::OnIOComplete");
180   CompletionOnceCallback callback = std::move(callback_);
181   callback_.Reset();
182   HandleInitCompletion(result);
183   std::move(callback).Run(result);
184 }
185 
HandleInitCompletion(int result)186 void ClientSocketHandle::HandleInitCompletion(int result) {
187   CHECK_NE(ERR_IO_PENDING, result);
188   if (result != OK) {
189     if (!socket_.get())
190       ResetInternal(false /* cancel */,
191                     false /* cancel_connect_job */);  // Nothing to cancel since
192                                                       // the request failed.
193     else
194       is_initialized_ = true;
195     return;
196   }
197   is_initialized_ = true;
198   CHECK_NE(-1, group_generation_)
199       << "Pool should have set |group_generation_| to a valid value.";
200 
201   // Broadcast that the socket has been acquired.
202   // TODO(eroman): This logging is not complete, in particular set_socket() and
203   // release() socket. It ends up working though, since those methods are being
204   // used to layer sockets (and the destination sources are the same).
205   DCHECK(socket_.get());
206   socket_->NetLog().BeginEventReferencingSource(NetLogEventType::SOCKET_IN_USE,
207                                                 requesting_source_);
208 }
209 
ResetInternal(bool cancel,bool cancel_connect_job)210 void ClientSocketHandle::ResetInternal(bool cancel, bool cancel_connect_job) {
211   DCHECK(cancel || !cancel_connect_job);
212 
213   // Was Init called?
214   if (!group_id_.destination().IsEmpty()) {
215     // If so, we must have a pool.
216     CHECK(pool_);
217     if (is_initialized()) {
218       if (socket_) {
219         socket_->NetLog().EndEvent(NetLogEventType::SOCKET_IN_USE);
220         // Release the socket back to the ClientSocketPool so it can be
221         // deleted or reused.
222         pool_->ReleaseSocket(group_id_, std::move(socket_), group_generation_);
223       } else {
224         // If the handle has been initialized, we should still have a
225         // socket.
226         NOTREACHED();
227       }
228     } else if (cancel) {
229       // If we did not get initialized yet and we have a socket
230       // request pending, cancel it.
231       pool_->CancelRequest(group_id_, this, cancel_connect_job);
232     }
233   }
234   is_initialized_ = false;
235   socket_.reset();
236   group_id_ = ClientSocketPool::GroupId();
237   reuse_type_ = ClientSocketHandle::UNUSED;
238   callback_.Reset();
239   if (higher_pool_)
240     RemoveHigherLayeredPool(higher_pool_);
241   pool_ = nullptr;
242   idle_time_ = base::TimeDelta();
243   connect_timing_ = LoadTimingInfo::ConnectTiming();
244   group_generation_ = -1;
245 }
246 
ResetErrorState()247 void ClientSocketHandle::ResetErrorState() {
248   resolve_error_info_ = ResolveErrorInfo(OK);
249   is_ssl_error_ = false;
250   ssl_cert_request_info_ = nullptr;
251 }
252 
253 }  // namespace net
254