1 // Copyright (c) 2013 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.
5 #include "net/socket/tcp_client_socket.h"
7 #include "base/callback_helpers.h"
8 #include "base/logging.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/profiler/scoped_tracker.h"
11 #include "base/time/time.h"
12 #include "net/base/io_buffer.h"
13 #include "net/base/ip_endpoint.h"
14 #include "net/base/net_errors.h"
15 #include "net/base/net_util.h"
19 TCPClientSocket::TCPClientSocket(const AddressList
& addresses
,
21 const net::NetLog::Source
& source
)
22 : socket_(new TCPSocket(net_log
, source
)),
23 addresses_(addresses
),
24 current_address_index_(-1),
25 next_connect_state_(CONNECT_STATE_NONE
),
26 previously_disconnected_(false) {
29 TCPClientSocket::TCPClientSocket(scoped_ptr
<TCPSocket
> connected_socket
,
30 const IPEndPoint
& peer_address
)
31 : socket_(connected_socket
.Pass()),
32 addresses_(AddressList(peer_address
)),
33 current_address_index_(0),
34 next_connect_state_(CONNECT_STATE_NONE
),
35 previously_disconnected_(false) {
38 socket_
->SetDefaultOptionsForClient();
39 use_history_
.set_was_ever_connected();
42 TCPClientSocket::~TCPClientSocket() {
46 int TCPClientSocket::Bind(const IPEndPoint
& address
) {
47 if (current_address_index_
>= 0 || bind_address_
) {
48 // Cannot bind the socket if we are already connected or connecting.
50 return ERR_UNEXPECTED
;
54 if (!socket_
->IsValid()) {
55 result
= OpenSocket(address
.GetFamily());
60 result
= socket_
->Bind(address
);
64 bind_address_
.reset(new IPEndPoint(address
));
68 int TCPClientSocket::Connect(const CompletionCallback
& callback
) {
69 DCHECK(!callback
.is_null());
71 // If connecting or already connected, then just return OK.
72 if (socket_
->IsValid() && current_address_index_
>= 0)
75 socket_
->StartLoggingMultipleConnectAttempts(addresses_
);
77 // We will try to connect to each address in addresses_. Start with the
78 // first one in the list.
79 next_connect_state_
= CONNECT_STATE_CONNECT
;
80 current_address_index_
= 0;
82 int rv
= DoConnectLoop(OK
);
83 if (rv
== ERR_IO_PENDING
) {
84 connect_callback_
= callback
;
86 socket_
->EndLoggingMultipleConnectAttempts(rv
);
92 int TCPClientSocket::DoConnectLoop(int result
) {
93 DCHECK_NE(next_connect_state_
, CONNECT_STATE_NONE
);
97 ConnectState state
= next_connect_state_
;
98 next_connect_state_
= CONNECT_STATE_NONE
;
100 case CONNECT_STATE_CONNECT
:
104 case CONNECT_STATE_CONNECT_COMPLETE
:
105 rv
= DoConnectComplete(rv
);
108 NOTREACHED() << "bad state " << state
;
112 } while (rv
!= ERR_IO_PENDING
&& next_connect_state_
!= CONNECT_STATE_NONE
);
117 int TCPClientSocket::DoConnect() {
118 DCHECK_GE(current_address_index_
, 0);
119 DCHECK_LT(current_address_index_
, static_cast<int>(addresses_
.size()));
121 const IPEndPoint
& endpoint
= addresses_
[current_address_index_
];
124 // TODO(ricea): Remove ScopedTracker below once crbug.com/436634 is fixed.
125 tracked_objects::ScopedTracker
tracking_profile(
126 FROM_HERE_WITH_EXPLICIT_FUNCTION("436634 TCPClientSocket::DoConnect"));
128 if (previously_disconnected_
) {
129 use_history_
.Reset();
130 connection_attempts_
.clear();
131 previously_disconnected_
= false;
134 next_connect_state_
= CONNECT_STATE_CONNECT_COMPLETE
;
136 if (socket_
->IsValid()) {
137 DCHECK(bind_address_
);
139 int result
= OpenSocket(endpoint
.GetFamily());
144 result
= socket_
->Bind(*bind_address_
);
153 // |socket_| is owned by this class and the callback won't be run once
154 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
155 return socket_
->Connect(endpoint
,
156 base::Bind(&TCPClientSocket::DidCompleteConnect
,
157 base::Unretained(this)));
160 int TCPClientSocket::DoConnectComplete(int result
) {
162 use_history_
.set_was_ever_connected();
166 connection_attempts_
.push_back(
167 ConnectionAttempt(addresses_
[current_address_index_
], result
));
169 // Close whatever partially connected socket we currently have.
172 // Try to fall back to the next address in the list.
173 if (current_address_index_
+ 1 < static_cast<int>(addresses_
.size())) {
174 next_connect_state_
= CONNECT_STATE_CONNECT
;
175 ++current_address_index_
;
179 // Otherwise there is nothing to fall back to, so give up.
183 void TCPClientSocket::Disconnect() {
185 current_address_index_
= -1;
186 bind_address_
.reset();
189 void TCPClientSocket::DoDisconnect() {
190 EmitTCPMetricsHistogramsOnDisconnect();
191 // If connecting or already connected, record that the socket has been
193 previously_disconnected_
= socket_
->IsValid() && current_address_index_
>= 0;
197 bool TCPClientSocket::IsConnected() const {
198 return socket_
->IsConnected();
201 bool TCPClientSocket::IsConnectedAndIdle() const {
202 return socket_
->IsConnectedAndIdle();
205 int TCPClientSocket::GetPeerAddress(IPEndPoint
* address
) const {
206 return socket_
->GetPeerAddress(address
);
209 int TCPClientSocket::GetLocalAddress(IPEndPoint
* address
) const {
212 if (!socket_
->IsValid()) {
214 *address
= *bind_address_
;
217 return ERR_SOCKET_NOT_CONNECTED
;
220 return socket_
->GetLocalAddress(address
);
223 const BoundNetLog
& TCPClientSocket::NetLog() const {
224 return socket_
->net_log();
227 void TCPClientSocket::SetSubresourceSpeculation() {
228 use_history_
.set_subresource_speculation();
231 void TCPClientSocket::SetOmniboxSpeculation() {
232 use_history_
.set_omnibox_speculation();
235 bool TCPClientSocket::WasEverUsed() const {
236 return use_history_
.was_used_to_convey_data();
239 bool TCPClientSocket::UsingTCPFastOpen() const {
240 return socket_
->UsingTCPFastOpen();
243 void TCPClientSocket::EnableTCPFastOpenIfSupported() {
244 socket_
->EnableTCPFastOpenIfSupported();
247 bool TCPClientSocket::WasNpnNegotiated() const {
251 NextProto
TCPClientSocket::GetNegotiatedProtocol() const {
252 return kProtoUnknown
;
255 bool TCPClientSocket::GetSSLInfo(SSLInfo
* ssl_info
) {
259 int TCPClientSocket::Read(IOBuffer
* buf
,
261 const CompletionCallback
& callback
) {
262 DCHECK(!callback
.is_null());
264 // |socket_| is owned by this class and the callback won't be run once
265 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
266 CompletionCallback read_callback
= base::Bind(
267 &TCPClientSocket::DidCompleteReadWrite
, base::Unretained(this), callback
);
268 int result
= socket_
->Read(buf
, buf_len
, read_callback
);
270 use_history_
.set_was_used_to_convey_data();
275 int TCPClientSocket::Write(IOBuffer
* buf
,
277 const CompletionCallback
& callback
) {
278 DCHECK(!callback
.is_null());
280 // |socket_| is owned by this class and the callback won't be run once
281 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
282 CompletionCallback write_callback
= base::Bind(
283 &TCPClientSocket::DidCompleteReadWrite
, base::Unretained(this), callback
);
284 int result
= socket_
->Write(buf
, buf_len
, write_callback
);
286 use_history_
.set_was_used_to_convey_data();
291 int TCPClientSocket::SetReceiveBufferSize(int32 size
) {
292 return socket_
->SetReceiveBufferSize(size
);
295 int TCPClientSocket::SetSendBufferSize(int32 size
) {
296 return socket_
->SetSendBufferSize(size
);
299 bool TCPClientSocket::SetKeepAlive(bool enable
, int delay
) {
300 return socket_
->SetKeepAlive(enable
, delay
);
303 bool TCPClientSocket::SetNoDelay(bool no_delay
) {
304 return socket_
->SetNoDelay(no_delay
);
307 void TCPClientSocket::GetConnectionAttempts(ConnectionAttempts
* out
) const {
308 *out
= connection_attempts_
;
311 void TCPClientSocket::ClearConnectionAttempts() {
312 connection_attempts_
.clear();
315 void TCPClientSocket::AddConnectionAttempts(
316 const ConnectionAttempts
& attempts
) {
317 connection_attempts_
.insert(connection_attempts_
.begin(), attempts
.begin(),
321 void TCPClientSocket::DidCompleteConnect(int result
) {
322 DCHECK_EQ(next_connect_state_
, CONNECT_STATE_CONNECT_COMPLETE
);
323 DCHECK_NE(result
, ERR_IO_PENDING
);
324 DCHECK(!connect_callback_
.is_null());
326 result
= DoConnectLoop(result
);
327 if (result
!= ERR_IO_PENDING
) {
328 socket_
->EndLoggingMultipleConnectAttempts(result
);
329 base::ResetAndReturn(&connect_callback_
).Run(result
);
333 void TCPClientSocket::DidCompleteReadWrite(const CompletionCallback
& callback
,
336 use_history_
.set_was_used_to_convey_data();
338 // TODO(pkasting): Remove ScopedTracker below once crbug.com/462780 is fixed.
339 tracked_objects::ScopedTracker
tracking_profile(
340 FROM_HERE_WITH_EXPLICIT_FUNCTION(
341 "462780 TCPClientSocket::DidCompleteReadWrite"));
342 callback
.Run(result
);
345 int TCPClientSocket::OpenSocket(AddressFamily family
) {
346 DCHECK(!socket_
->IsValid());
348 int result
= socket_
->Open(family
);
352 socket_
->SetDefaultOptionsForClient();
357 void TCPClientSocket::EmitTCPMetricsHistogramsOnDisconnect() {
359 if (socket_
->GetEstimatedRoundTripTime(&rtt
)) {
360 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TcpRtt.AtDisconnect", rtt
,
361 base::TimeDelta::FromMilliseconds(1),
362 base::TimeDelta::FromMinutes(10), 100);