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 "net/base/io_buffer.h"
10 #include "net/base/ip_endpoint.h"
11 #include "net/base/net_errors.h"
12 #include "net/base/net_util.h"
16 TCPClientSocket::TCPClientSocket(const AddressList
& addresses
,
18 const net::NetLog::Source
& source
)
19 : socket_(new TCPSocket(net_log
, source
)),
20 addresses_(addresses
),
21 current_address_index_(-1),
22 next_connect_state_(CONNECT_STATE_NONE
),
23 previously_disconnected_(false) {
26 TCPClientSocket::TCPClientSocket(scoped_ptr
<TCPSocket
> connected_socket
,
27 const IPEndPoint
& peer_address
)
28 : socket_(connected_socket
.Pass()),
29 addresses_(AddressList(peer_address
)),
30 current_address_index_(0),
31 next_connect_state_(CONNECT_STATE_NONE
),
32 previously_disconnected_(false) {
35 socket_
->SetDefaultOptionsForClient();
36 use_history_
.set_was_ever_connected();
39 TCPClientSocket::~TCPClientSocket() {
42 int TCPClientSocket::Bind(const IPEndPoint
& address
) {
43 if (current_address_index_
>= 0 || bind_address_
) {
44 // Cannot bind the socket if we are already connected or connecting.
46 return ERR_UNEXPECTED
;
50 if (!socket_
->IsValid()) {
51 result
= OpenSocket(address
.GetFamily());
56 result
= socket_
->Bind(address
);
60 bind_address_
.reset(new IPEndPoint(address
));
64 int TCPClientSocket::Connect(const CompletionCallback
& callback
) {
65 DCHECK(!callback
.is_null());
67 // If connecting or already connected, then just return OK.
68 if (socket_
->IsValid() && current_address_index_
>= 0)
71 socket_
->StartLoggingMultipleConnectAttempts(addresses_
);
73 // We will try to connect to each address in addresses_. Start with the
74 // first one in the list.
75 next_connect_state_
= CONNECT_STATE_CONNECT
;
76 current_address_index_
= 0;
78 int rv
= DoConnectLoop(OK
);
79 if (rv
== ERR_IO_PENDING
) {
80 connect_callback_
= callback
;
82 socket_
->EndLoggingMultipleConnectAttempts(rv
);
88 int TCPClientSocket::DoConnectLoop(int result
) {
89 DCHECK_NE(next_connect_state_
, CONNECT_STATE_NONE
);
93 ConnectState state
= next_connect_state_
;
94 next_connect_state_
= CONNECT_STATE_NONE
;
96 case CONNECT_STATE_CONNECT
:
100 case CONNECT_STATE_CONNECT_COMPLETE
:
101 rv
= DoConnectComplete(rv
);
104 NOTREACHED() << "bad state " << state
;
108 } while (rv
!= ERR_IO_PENDING
&& next_connect_state_
!= CONNECT_STATE_NONE
);
113 int TCPClientSocket::DoConnect() {
114 DCHECK_GE(current_address_index_
, 0);
115 DCHECK_LT(current_address_index_
, static_cast<int>(addresses_
.size()));
117 const IPEndPoint
& endpoint
= addresses_
[current_address_index_
];
119 if (previously_disconnected_
) {
120 use_history_
.Reset();
121 previously_disconnected_
= false;
124 next_connect_state_
= CONNECT_STATE_CONNECT_COMPLETE
;
126 if (socket_
->IsValid()) {
127 DCHECK(bind_address_
);
129 int result
= OpenSocket(endpoint
.GetFamily());
134 result
= socket_
->Bind(*bind_address_
);
142 // |socket_| is owned by this class and the callback won't be run once
143 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
144 return socket_
->Connect(endpoint
,
145 base::Bind(&TCPClientSocket::DidCompleteConnect
,
146 base::Unretained(this)));
149 int TCPClientSocket::DoConnectComplete(int result
) {
151 use_history_
.set_was_ever_connected();
155 // Close whatever partially connected socket we currently have.
158 // Try to fall back to the next address in the list.
159 if (current_address_index_
+ 1 < static_cast<int>(addresses_
.size())) {
160 next_connect_state_
= CONNECT_STATE_CONNECT
;
161 ++current_address_index_
;
165 // Otherwise there is nothing to fall back to, so give up.
169 void TCPClientSocket::Disconnect() {
171 current_address_index_
= -1;
172 bind_address_
.reset();
175 void TCPClientSocket::DoDisconnect() {
176 // If connecting or already connected, record that the socket has been
178 previously_disconnected_
= socket_
->IsValid() && current_address_index_
>= 0;
182 bool TCPClientSocket::IsConnected() const {
183 return socket_
->IsConnected();
186 bool TCPClientSocket::IsConnectedAndIdle() const {
187 return socket_
->IsConnectedAndIdle();
190 int TCPClientSocket::GetPeerAddress(IPEndPoint
* address
) const {
191 return socket_
->GetPeerAddress(address
);
194 int TCPClientSocket::GetLocalAddress(IPEndPoint
* address
) const {
197 if (!socket_
->IsValid()) {
199 *address
= *bind_address_
;
202 return ERR_SOCKET_NOT_CONNECTED
;
205 return socket_
->GetLocalAddress(address
);
208 const BoundNetLog
& TCPClientSocket::NetLog() const {
209 return socket_
->net_log();
212 void TCPClientSocket::SetSubresourceSpeculation() {
213 use_history_
.set_subresource_speculation();
216 void TCPClientSocket::SetOmniboxSpeculation() {
217 use_history_
.set_omnibox_speculation();
220 bool TCPClientSocket::WasEverUsed() const {
221 return use_history_
.was_used_to_convey_data();
224 bool TCPClientSocket::UsingTCPFastOpen() const {
225 return socket_
->UsingTCPFastOpen();
228 bool TCPClientSocket::WasNpnNegotiated() const {
232 NextProto
TCPClientSocket::GetNegotiatedProtocol() const {
233 return kProtoUnknown
;
236 bool TCPClientSocket::GetSSLInfo(SSLInfo
* ssl_info
) {
240 int TCPClientSocket::Read(IOBuffer
* buf
,
242 const CompletionCallback
& callback
) {
243 DCHECK(!callback
.is_null());
245 // |socket_| is owned by this class and the callback won't be run once
246 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
247 CompletionCallback read_callback
= base::Bind(
248 &TCPClientSocket::DidCompleteReadWrite
, base::Unretained(this), callback
);
249 int result
= socket_
->Read(buf
, buf_len
, read_callback
);
251 use_history_
.set_was_used_to_convey_data();
256 int TCPClientSocket::Write(IOBuffer
* buf
,
258 const CompletionCallback
& callback
) {
259 DCHECK(!callback
.is_null());
261 // |socket_| is owned by this class and the callback won't be run once
262 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
263 CompletionCallback write_callback
= base::Bind(
264 &TCPClientSocket::DidCompleteReadWrite
, base::Unretained(this), callback
);
265 int result
= socket_
->Write(buf
, buf_len
, write_callback
);
267 use_history_
.set_was_used_to_convey_data();
272 int TCPClientSocket::SetReceiveBufferSize(int32 size
) {
273 return socket_
->SetReceiveBufferSize(size
);
276 int TCPClientSocket::SetSendBufferSize(int32 size
) {
277 return socket_
->SetSendBufferSize(size
);
280 bool TCPClientSocket::SetKeepAlive(bool enable
, int delay
) {
281 return socket_
->SetKeepAlive(enable
, delay
);
284 bool TCPClientSocket::SetNoDelay(bool no_delay
) {
285 return socket_
->SetNoDelay(no_delay
);
288 void TCPClientSocket::DidCompleteConnect(int result
) {
289 DCHECK_EQ(next_connect_state_
, CONNECT_STATE_CONNECT_COMPLETE
);
290 DCHECK_NE(result
, ERR_IO_PENDING
);
291 DCHECK(!connect_callback_
.is_null());
293 result
= DoConnectLoop(result
);
294 if (result
!= ERR_IO_PENDING
) {
295 socket_
->EndLoggingMultipleConnectAttempts(result
);
296 base::ResetAndReturn(&connect_callback_
).Run(result
);
300 void TCPClientSocket::DidCompleteReadWrite(const CompletionCallback
& callback
,
303 use_history_
.set_was_used_to_convey_data();
305 callback
.Run(result
);
308 int TCPClientSocket::OpenSocket(AddressFamily family
) {
309 DCHECK(!socket_
->IsValid());
311 int result
= socket_
->Open(family
);
315 socket_
->SetDefaultOptionsForClient();