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 previously_disconnected_
= false;
130 next_connect_state_
= CONNECT_STATE_CONNECT_COMPLETE
;
132 if (socket_
->IsValid()) {
133 DCHECK(bind_address_
);
135 int result
= OpenSocket(endpoint
.GetFamily());
140 result
= socket_
->Bind(*bind_address_
);
149 // |socket_| is owned by this class and the callback won't be run once
150 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
151 return socket_
->Connect(endpoint
,
152 base::Bind(&TCPClientSocket::DidCompleteConnect
,
153 base::Unretained(this)));
156 int TCPClientSocket::DoConnectComplete(int result
) {
158 use_history_
.set_was_ever_connected();
162 // Close whatever partially connected socket we currently have.
165 // Try to fall back to the next address in the list.
166 if (current_address_index_
+ 1 < static_cast<int>(addresses_
.size())) {
167 next_connect_state_
= CONNECT_STATE_CONNECT
;
168 ++current_address_index_
;
172 // Otherwise there is nothing to fall back to, so give up.
176 void TCPClientSocket::Disconnect() {
178 current_address_index_
= -1;
179 bind_address_
.reset();
182 void TCPClientSocket::DoDisconnect() {
183 // If connecting or already connected, record that the socket has been
185 previously_disconnected_
= socket_
->IsValid() && current_address_index_
>= 0;
189 bool TCPClientSocket::IsConnected() const {
190 return socket_
->IsConnected();
193 bool TCPClientSocket::IsConnectedAndIdle() const {
194 return socket_
->IsConnectedAndIdle();
197 int TCPClientSocket::GetPeerAddress(IPEndPoint
* address
) const {
198 return socket_
->GetPeerAddress(address
);
201 int TCPClientSocket::GetLocalAddress(IPEndPoint
* address
) const {
204 if (!socket_
->IsValid()) {
206 *address
= *bind_address_
;
209 return ERR_SOCKET_NOT_CONNECTED
;
212 return socket_
->GetLocalAddress(address
);
215 const BoundNetLog
& TCPClientSocket::NetLog() const {
216 return socket_
->net_log();
219 void TCPClientSocket::SetSubresourceSpeculation() {
220 use_history_
.set_subresource_speculation();
223 void TCPClientSocket::SetOmniboxSpeculation() {
224 use_history_
.set_omnibox_speculation();
227 bool TCPClientSocket::WasEverUsed() const {
228 return use_history_
.was_used_to_convey_data();
231 bool TCPClientSocket::UsingTCPFastOpen() const {
232 return socket_
->UsingTCPFastOpen();
235 void TCPClientSocket::EnableTCPFastOpenIfSupported() {
236 socket_
->EnableTCPFastOpenIfSupported();
239 bool TCPClientSocket::WasNpnNegotiated() const {
243 NextProto
TCPClientSocket::GetNegotiatedProtocol() const {
244 return kProtoUnknown
;
247 bool TCPClientSocket::GetSSLInfo(SSLInfo
* ssl_info
) {
251 int TCPClientSocket::Read(IOBuffer
* buf
,
253 const CompletionCallback
& callback
) {
254 DCHECK(!callback
.is_null());
256 // |socket_| is owned by this class and the callback won't be run once
257 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
258 CompletionCallback read_callback
= base::Bind(
259 &TCPClientSocket::DidCompleteReadWrite
, base::Unretained(this), callback
);
260 int result
= socket_
->Read(buf
, buf_len
, read_callback
);
262 use_history_
.set_was_used_to_convey_data();
267 int TCPClientSocket::Write(IOBuffer
* buf
,
269 const CompletionCallback
& callback
) {
270 DCHECK(!callback
.is_null());
272 // |socket_| is owned by this class and the callback won't be run once
273 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
274 CompletionCallback write_callback
= base::Bind(
275 &TCPClientSocket::DidCompleteReadWrite
, base::Unretained(this), callback
);
276 int result
= socket_
->Write(buf
, buf_len
, write_callback
);
278 use_history_
.set_was_used_to_convey_data();
283 int TCPClientSocket::SetReceiveBufferSize(int32 size
) {
284 return socket_
->SetReceiveBufferSize(size
);
287 int TCPClientSocket::SetSendBufferSize(int32 size
) {
288 return socket_
->SetSendBufferSize(size
);
291 bool TCPClientSocket::SetKeepAlive(bool enable
, int delay
) {
292 return socket_
->SetKeepAlive(enable
, delay
);
295 bool TCPClientSocket::SetNoDelay(bool no_delay
) {
296 return socket_
->SetNoDelay(no_delay
);
299 void TCPClientSocket::DidCompleteConnect(int result
) {
300 DCHECK_EQ(next_connect_state_
, CONNECT_STATE_CONNECT_COMPLETE
);
301 DCHECK_NE(result
, ERR_IO_PENDING
);
302 DCHECK(!connect_callback_
.is_null());
304 result
= DoConnectLoop(result
);
305 if (result
!= ERR_IO_PENDING
) {
306 socket_
->EndLoggingMultipleConnectAttempts(result
);
307 base::ResetAndReturn(&connect_callback_
).Run(result
);
311 void TCPClientSocket::DidCompleteReadWrite(const CompletionCallback
& callback
,
314 use_history_
.set_was_used_to_convey_data();
316 // TODO(pkasting): Remove ScopedTracker below once crbug.com/462780 is fixed.
317 tracked_objects::ScopedTracker
tracking_profile(
318 FROM_HERE_WITH_EXPLICIT_FUNCTION(
319 "462780 TCPClientSocket::DidCompleteReadWrite"));
320 callback
.Run(result
);
323 int TCPClientSocket::OpenSocket(AddressFamily family
) {
324 DCHECK(!socket_
->IsValid());
326 int result
= socket_
->Open(family
);
330 socket_
->SetDefaultOptionsForClient();