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 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed.
67 tracked_objects::ScopedTracker
tracking_profile(
68 FROM_HERE_WITH_EXPLICIT_FUNCTION("436634 TCPClientSocket::Connect"));
70 DCHECK(!callback
.is_null());
72 // If connecting or already connected, then just return OK.
73 if (socket_
->IsValid() && current_address_index_
>= 0)
76 socket_
->StartLoggingMultipleConnectAttempts(addresses_
);
78 // We will try to connect to each address in addresses_. Start with the
79 // first one in the list.
80 next_connect_state_
= CONNECT_STATE_CONNECT
;
81 current_address_index_
= 0;
83 int rv
= DoConnectLoop(OK
);
84 if (rv
== ERR_IO_PENDING
) {
85 connect_callback_
= callback
;
87 socket_
->EndLoggingMultipleConnectAttempts(rv
);
93 int TCPClientSocket::DoConnectLoop(int result
) {
94 DCHECK_NE(next_connect_state_
, CONNECT_STATE_NONE
);
98 ConnectState state
= next_connect_state_
;
99 next_connect_state_
= CONNECT_STATE_NONE
;
101 case CONNECT_STATE_CONNECT
:
105 case CONNECT_STATE_CONNECT_COMPLETE
:
106 rv
= DoConnectComplete(rv
);
109 NOTREACHED() << "bad state " << state
;
113 } while (rv
!= ERR_IO_PENDING
&& next_connect_state_
!= CONNECT_STATE_NONE
);
118 int TCPClientSocket::DoConnect() {
119 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed.
120 tracked_objects::ScopedTracker
tracking_profile1(
121 FROM_HERE_WITH_EXPLICIT_FUNCTION("436634 TCPClientSocket::DoConnect1"));
123 DCHECK_GE(current_address_index_
, 0);
124 DCHECK_LT(current_address_index_
, static_cast<int>(addresses_
.size()));
126 const IPEndPoint
& endpoint
= addresses_
[current_address_index_
];
128 if (previously_disconnected_
) {
129 use_history_
.Reset();
130 previously_disconnected_
= false;
133 next_connect_state_
= CONNECT_STATE_CONNECT_COMPLETE
;
135 if (socket_
->IsValid()) {
136 DCHECK(bind_address_
);
138 int result
= OpenSocket(endpoint
.GetFamily());
143 result
= socket_
->Bind(*bind_address_
);
151 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed.
152 tracked_objects::ScopedTracker
tracking_profile2(
153 FROM_HERE_WITH_EXPLICIT_FUNCTION("436634 TCPClientSocket::DoConnect2"));
155 // |socket_| is owned by this class and the callback won't be run once
156 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
157 return socket_
->Connect(endpoint
,
158 base::Bind(&TCPClientSocket::DidCompleteConnect
,
159 base::Unretained(this)));
162 int TCPClientSocket::DoConnectComplete(int result
) {
163 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed.
164 tracked_objects::ScopedTracker
tracking_profile(
165 FROM_HERE_WITH_EXPLICIT_FUNCTION(
166 "436634 TCPClientSocket::DoConnectComplete"));
169 use_history_
.set_was_ever_connected();
173 // Close whatever partially connected socket we currently have.
176 // Try to fall back to the next address in the list.
177 if (current_address_index_
+ 1 < static_cast<int>(addresses_
.size())) {
178 next_connect_state_
= CONNECT_STATE_CONNECT
;
179 ++current_address_index_
;
183 // Otherwise there is nothing to fall back to, so give up.
187 void TCPClientSocket::Disconnect() {
189 current_address_index_
= -1;
190 bind_address_
.reset();
193 void TCPClientSocket::DoDisconnect() {
194 // If connecting or already connected, record that the socket has been
196 previously_disconnected_
= socket_
->IsValid() && current_address_index_
>= 0;
200 bool TCPClientSocket::IsConnected() const {
201 return socket_
->IsConnected();
204 bool TCPClientSocket::IsConnectedAndIdle() const {
205 return socket_
->IsConnectedAndIdle();
208 int TCPClientSocket::GetPeerAddress(IPEndPoint
* address
) const {
209 return socket_
->GetPeerAddress(address
);
212 int TCPClientSocket::GetLocalAddress(IPEndPoint
* address
) const {
215 if (!socket_
->IsValid()) {
217 *address
= *bind_address_
;
220 return ERR_SOCKET_NOT_CONNECTED
;
223 return socket_
->GetLocalAddress(address
);
226 const BoundNetLog
& TCPClientSocket::NetLog() const {
227 return socket_
->net_log();
230 void TCPClientSocket::SetSubresourceSpeculation() {
231 use_history_
.set_subresource_speculation();
234 void TCPClientSocket::SetOmniboxSpeculation() {
235 use_history_
.set_omnibox_speculation();
238 bool TCPClientSocket::WasEverUsed() const {
239 return use_history_
.was_used_to_convey_data();
242 bool TCPClientSocket::UsingTCPFastOpen() const {
243 return socket_
->UsingTCPFastOpen();
246 void TCPClientSocket::EnableTCPFastOpenIfSupported() {
247 socket_
->EnableTCPFastOpenIfSupported();
250 bool TCPClientSocket::WasNpnNegotiated() const {
254 NextProto
TCPClientSocket::GetNegotiatedProtocol() const {
255 return kProtoUnknown
;
258 bool TCPClientSocket::GetSSLInfo(SSLInfo
* ssl_info
) {
262 int TCPClientSocket::Read(IOBuffer
* buf
,
264 const CompletionCallback
& callback
) {
265 DCHECK(!callback
.is_null());
267 // |socket_| is owned by this class and the callback won't be run once
268 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
269 CompletionCallback read_callback
= base::Bind(
270 &TCPClientSocket::DidCompleteReadWrite
, base::Unretained(this), callback
);
271 int result
= socket_
->Read(buf
, buf_len
, read_callback
);
273 use_history_
.set_was_used_to_convey_data();
278 int TCPClientSocket::Write(IOBuffer
* buf
,
280 const CompletionCallback
& callback
) {
281 DCHECK(!callback
.is_null());
283 // |socket_| is owned by this class and the callback won't be run once
284 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
285 CompletionCallback write_callback
= base::Bind(
286 &TCPClientSocket::DidCompleteReadWrite
, base::Unretained(this), callback
);
287 int result
= socket_
->Write(buf
, buf_len
, write_callback
);
289 use_history_
.set_was_used_to_convey_data();
294 int TCPClientSocket::SetReceiveBufferSize(int32 size
) {
295 return socket_
->SetReceiveBufferSize(size
);
298 int TCPClientSocket::SetSendBufferSize(int32 size
) {
299 return socket_
->SetSendBufferSize(size
);
302 bool TCPClientSocket::SetKeepAlive(bool enable
, int delay
) {
303 return socket_
->SetKeepAlive(enable
, delay
);
306 bool TCPClientSocket::SetNoDelay(bool no_delay
) {
307 return socket_
->SetNoDelay(no_delay
);
310 void TCPClientSocket::DidCompleteConnect(int result
) {
311 DCHECK_EQ(next_connect_state_
, CONNECT_STATE_CONNECT_COMPLETE
);
312 DCHECK_NE(result
, ERR_IO_PENDING
);
313 DCHECK(!connect_callback_
.is_null());
315 result
= DoConnectLoop(result
);
316 if (result
!= ERR_IO_PENDING
) {
317 socket_
->EndLoggingMultipleConnectAttempts(result
);
318 base::ResetAndReturn(&connect_callback_
).Run(result
);
322 void TCPClientSocket::DidCompleteReadWrite(const CompletionCallback
& callback
,
325 use_history_
.set_was_used_to_convey_data();
327 // TODO(pkasting): Remove ScopedTracker below once crbug.com/462780 is fixed.
328 tracked_objects::ScopedTracker
tracking_profile(
329 FROM_HERE_WITH_EXPLICIT_FUNCTION(
330 "462780 TCPClientSocket::DidCompleteReadWrite"));
331 callback
.Run(result
);
334 int TCPClientSocket::OpenSocket(AddressFamily family
) {
335 DCHECK(!socket_
->IsValid());
337 int result
= socket_
->Open(family
);
341 socket_
->SetDefaultOptionsForClient();