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