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/profiler/scoped_tracker.h"
10 #include "net/base/io_buffer.h"
11 #include "net/base/ip_endpoint.h"
12 #include "net/base/net_errors.h"
13 #include "net/base/net_util.h"
17 TCPClientSocket::TCPClientSocket(const AddressList
& addresses
,
19 const net::NetLog::Source
& source
)
20 : socket_(new TCPSocket(net_log
, source
)),
21 addresses_(addresses
),
22 current_address_index_(-1),
23 next_connect_state_(CONNECT_STATE_NONE
),
24 previously_disconnected_(false) {
27 TCPClientSocket::TCPClientSocket(scoped_ptr
<TCPSocket
> connected_socket
,
28 const IPEndPoint
& peer_address
)
29 : socket_(connected_socket
.Pass()),
30 addresses_(AddressList(peer_address
)),
31 current_address_index_(0),
32 next_connect_state_(CONNECT_STATE_NONE
),
33 previously_disconnected_(false) {
36 socket_
->SetDefaultOptionsForClient();
37 use_history_
.set_was_ever_connected();
40 TCPClientSocket::~TCPClientSocket() {
43 int TCPClientSocket::Bind(const IPEndPoint
& address
) {
44 if (current_address_index_
>= 0 || bind_address_
) {
45 // Cannot bind the socket if we are already connected or connecting.
47 return ERR_UNEXPECTED
;
51 if (!socket_
->IsValid()) {
52 result
= OpenSocket(address
.GetFamily());
57 result
= socket_
->Bind(address
);
61 bind_address_
.reset(new IPEndPoint(address
));
65 int TCPClientSocket::Connect(const CompletionCallback
& callback
) {
66 DCHECK(!callback
.is_null());
68 // If connecting or already connected, then just return OK.
69 if (socket_
->IsValid() && current_address_index_
>= 0)
72 socket_
->StartLoggingMultipleConnectAttempts(addresses_
);
74 // We will try to connect to each address in addresses_. Start with the
75 // first one in the list.
76 next_connect_state_
= CONNECT_STATE_CONNECT
;
77 current_address_index_
= 0;
79 int rv
= DoConnectLoop(OK
);
80 if (rv
== ERR_IO_PENDING
) {
81 connect_callback_
= callback
;
83 socket_
->EndLoggingMultipleConnectAttempts(rv
);
89 int TCPClientSocket::DoConnectLoop(int result
) {
90 DCHECK_NE(next_connect_state_
, CONNECT_STATE_NONE
);
94 ConnectState state
= next_connect_state_
;
95 next_connect_state_
= CONNECT_STATE_NONE
;
97 case CONNECT_STATE_CONNECT
:
101 case CONNECT_STATE_CONNECT_COMPLETE
:
102 rv
= DoConnectComplete(rv
);
105 NOTREACHED() << "bad state " << state
;
109 } while (rv
!= ERR_IO_PENDING
&& next_connect_state_
!= CONNECT_STATE_NONE
);
114 int TCPClientSocket::DoConnect() {
115 DCHECK_GE(current_address_index_
, 0);
116 DCHECK_LT(current_address_index_
, static_cast<int>(addresses_
.size()));
118 const IPEndPoint
& endpoint
= addresses_
[current_address_index_
];
121 // TODO(ricea): Remove ScopedTracker below once crbug.com/436634 is fixed.
122 tracked_objects::ScopedTracker
tracking_profile(
123 FROM_HERE_WITH_EXPLICIT_FUNCTION("436634 TCPClientSocket::DoConnect"));
125 if (previously_disconnected_
) {
126 use_history_
.Reset();
127 connection_attempts_
.clear();
128 previously_disconnected_
= false;
131 next_connect_state_
= CONNECT_STATE_CONNECT_COMPLETE
;
133 if (socket_
->IsValid()) {
134 DCHECK(bind_address_
);
136 int result
= OpenSocket(endpoint
.GetFamily());
141 result
= socket_
->Bind(*bind_address_
);
150 // |socket_| is owned by this class and the callback won't be run once
151 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
152 return socket_
->Connect(endpoint
,
153 base::Bind(&TCPClientSocket::DidCompleteConnect
,
154 base::Unretained(this)));
157 int TCPClientSocket::DoConnectComplete(int result
) {
159 use_history_
.set_was_ever_connected();
163 connection_attempts_
.push_back(
164 ConnectionAttempt(addresses_
[current_address_index_
], result
));
166 // Close whatever partially connected socket we currently have.
169 // Try to fall back to the next address in the list.
170 if (current_address_index_
+ 1 < static_cast<int>(addresses_
.size())) {
171 next_connect_state_
= CONNECT_STATE_CONNECT
;
172 ++current_address_index_
;
176 // Otherwise there is nothing to fall back to, so give up.
180 void TCPClientSocket::Disconnect() {
182 current_address_index_
= -1;
183 bind_address_
.reset();
186 void TCPClientSocket::DoDisconnect() {
187 // If connecting or already connected, record that the socket has been
189 previously_disconnected_
= socket_
->IsValid() && current_address_index_
>= 0;
193 bool TCPClientSocket::IsConnected() const {
194 return socket_
->IsConnected();
197 bool TCPClientSocket::IsConnectedAndIdle() const {
198 return socket_
->IsConnectedAndIdle();
201 int TCPClientSocket::GetPeerAddress(IPEndPoint
* address
) const {
202 return socket_
->GetPeerAddress(address
);
205 int TCPClientSocket::GetLocalAddress(IPEndPoint
* address
) const {
208 if (!socket_
->IsValid()) {
210 *address
= *bind_address_
;
213 return ERR_SOCKET_NOT_CONNECTED
;
216 return socket_
->GetLocalAddress(address
);
219 const BoundNetLog
& TCPClientSocket::NetLog() const {
220 return socket_
->net_log();
223 void TCPClientSocket::SetSubresourceSpeculation() {
224 use_history_
.set_subresource_speculation();
227 void TCPClientSocket::SetOmniboxSpeculation() {
228 use_history_
.set_omnibox_speculation();
231 bool TCPClientSocket::WasEverUsed() const {
232 return use_history_
.was_used_to_convey_data();
235 bool TCPClientSocket::UsingTCPFastOpen() const {
236 return socket_
->UsingTCPFastOpen();
239 void TCPClientSocket::EnableTCPFastOpenIfSupported() {
240 socket_
->EnableTCPFastOpenIfSupported();
243 bool TCPClientSocket::WasNpnNegotiated() const {
247 NextProto
TCPClientSocket::GetNegotiatedProtocol() const {
248 return kProtoUnknown
;
251 bool TCPClientSocket::GetSSLInfo(SSLInfo
* ssl_info
) {
255 int TCPClientSocket::Read(IOBuffer
* buf
,
257 const CompletionCallback
& callback
) {
258 DCHECK(!callback
.is_null());
260 // |socket_| is owned by this class and the callback won't be run once
261 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
262 CompletionCallback read_callback
= base::Bind(
263 &TCPClientSocket::DidCompleteReadWrite
, base::Unretained(this), callback
);
264 int result
= socket_
->Read(buf
, buf_len
, read_callback
);
266 use_history_
.set_was_used_to_convey_data();
271 int TCPClientSocket::Write(IOBuffer
* buf
,
273 const CompletionCallback
& callback
) {
274 DCHECK(!callback
.is_null());
276 // |socket_| is owned by this class and the callback won't be run once
277 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
278 CompletionCallback write_callback
= base::Bind(
279 &TCPClientSocket::DidCompleteReadWrite
, base::Unretained(this), callback
);
280 int result
= socket_
->Write(buf
, buf_len
, write_callback
);
282 use_history_
.set_was_used_to_convey_data();
287 int TCPClientSocket::SetReceiveBufferSize(int32 size
) {
288 return socket_
->SetReceiveBufferSize(size
);
291 int TCPClientSocket::SetSendBufferSize(int32 size
) {
292 return socket_
->SetSendBufferSize(size
);
295 bool TCPClientSocket::SetKeepAlive(bool enable
, int delay
) {
296 return socket_
->SetKeepAlive(enable
, delay
);
299 bool TCPClientSocket::SetNoDelay(bool no_delay
) {
300 return socket_
->SetNoDelay(no_delay
);
303 void TCPClientSocket::GetConnectionAttempts(ConnectionAttempts
* out
) const {
304 *out
= connection_attempts_
;
307 void TCPClientSocket::ClearConnectionAttempts() {
308 connection_attempts_
.clear();
311 void TCPClientSocket::AddConnectionAttempts(
312 const ConnectionAttempts
& attempts
) {
313 connection_attempts_
.insert(connection_attempts_
.begin(), attempts
.begin(),
317 void TCPClientSocket::DidCompleteConnect(int result
) {
318 DCHECK_EQ(next_connect_state_
, CONNECT_STATE_CONNECT_COMPLETE
);
319 DCHECK_NE(result
, ERR_IO_PENDING
);
320 DCHECK(!connect_callback_
.is_null());
322 result
= DoConnectLoop(result
);
323 if (result
!= ERR_IO_PENDING
) {
324 socket_
->EndLoggingMultipleConnectAttempts(result
);
325 base::ResetAndReturn(&connect_callback_
).Run(result
);
329 void TCPClientSocket::DidCompleteReadWrite(const CompletionCallback
& callback
,
332 use_history_
.set_was_used_to_convey_data();
334 // TODO(pkasting): Remove ScopedTracker below once crbug.com/462780 is fixed.
335 tracked_objects::ScopedTracker
tracking_profile(
336 FROM_HERE_WITH_EXPLICIT_FUNCTION(
337 "462780 TCPClientSocket::DidCompleteReadWrite"));
338 callback
.Run(result
);
341 int TCPClientSocket::OpenSocket(AddressFamily family
) {
342 DCHECK(!socket_
->IsValid());
344 int result
= socket_
->Open(family
);
348 socket_
->SetDefaultOptionsForClient();