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