Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / socket / tcp_client_socket.cc
blob78b24de7b15afe0c23c69089b8961ca471017b5c
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"
17 namespace net {
19 TCPClientSocket::TCPClientSocket(const AddressList& addresses,
20 net::NetLog* net_log,
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) {
36 DCHECK(socket_);
38 socket_->SetDefaultOptionsForClient();
39 use_history_.set_was_ever_connected();
42 TCPClientSocket::~TCPClientSocket() {
43 Disconnect();
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.
49 NOTREACHED();
50 return ERR_UNEXPECTED;
53 int result = OK;
54 if (!socket_->IsValid()) {
55 result = OpenSocket(address.GetFamily());
56 if (result != OK)
57 return result;
60 result = socket_->Bind(address);
61 if (result != OK)
62 return result;
64 bind_address_.reset(new IPEndPoint(address));
65 return OK;
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)
73 return OK;
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;
85 } else {
86 socket_->EndLoggingMultipleConnectAttempts(rv);
89 return rv;
92 int TCPClientSocket::DoConnectLoop(int result) {
93 DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE);
95 int rv = result;
96 do {
97 ConnectState state = next_connect_state_;
98 next_connect_state_ = CONNECT_STATE_NONE;
99 switch (state) {
100 case CONNECT_STATE_CONNECT:
101 DCHECK_EQ(OK, rv);
102 rv = DoConnect();
103 break;
104 case CONNECT_STATE_CONNECT_COMPLETE:
105 rv = DoConnectComplete(rv);
106 break;
107 default:
108 NOTREACHED() << "bad state " << state;
109 rv = ERR_UNEXPECTED;
110 break;
112 } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE);
114 return rv;
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_);
138 } else {
139 int result = OpenSocket(endpoint.GetFamily());
140 if (result != OK)
141 return result;
143 if (bind_address_) {
144 result = socket_->Bind(*bind_address_);
145 if (result != OK) {
146 socket_->Close();
147 return result;
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) {
161 if (result == OK) {
162 use_history_.set_was_ever_connected();
163 return OK; // Done!
166 connection_attempts_.push_back(
167 ConnectionAttempt(addresses_[current_address_index_], result));
169 // Close whatever partially connected socket we currently have.
170 DoDisconnect();
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_;
176 return OK;
179 // Otherwise there is nothing to fall back to, so give up.
180 return result;
183 void TCPClientSocket::Disconnect() {
184 DoDisconnect();
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
192 // disconnected.
193 previously_disconnected_ = socket_->IsValid() && current_address_index_ >= 0;
194 socket_->Close();
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 {
210 DCHECK(address);
212 if (!socket_->IsValid()) {
213 if (bind_address_) {
214 *address = *bind_address_;
215 return OK;
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 {
248 return false;
251 NextProto TCPClientSocket::GetNegotiatedProtocol() const {
252 return kProtoUnknown;
255 bool TCPClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
256 return false;
259 int TCPClientSocket::Read(IOBuffer* buf,
260 int buf_len,
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);
269 if (result > 0)
270 use_history_.set_was_used_to_convey_data();
272 return result;
275 int TCPClientSocket::Write(IOBuffer* buf,
276 int buf_len,
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);
285 if (result > 0)
286 use_history_.set_was_used_to_convey_data();
288 return result;
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(),
318 attempts.end());
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,
334 int result) {
335 if (result > 0)
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);
349 if (result != OK)
350 return result;
352 socket_->SetDefaultOptionsForClient();
354 return OK;
357 void TCPClientSocket::EmitTCPMetricsHistogramsOnDisconnect() {
358 base::TimeDelta rtt;
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);
366 } // namespace net