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