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. 4 5#include "net/socket/tcp_client_socket.h" 6 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" 13 14namespace net { 15 16TCPClientSocket::TCPClientSocket(const AddressList& addresses, 17 net::NetLog* net_log, 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) { 24} 25 26TCPClientSocket::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) { 33 DCHECK(socket_); 34 35 socket_->SetDefaultOptionsForClient(); 36 use_history_.set_was_ever_connected(); 37} 38 39TCPClientSocket::~TCPClientSocket() { 40} 41 42int 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. 45 NOTREACHED(); 46 return ERR_UNEXPECTED; 47 } 48 49 int result = OK; 50 if (!socket_->IsValid()) { 51 result = OpenSocket(address.GetFamily()); 52 if (result != OK) 53 return result; 54 } 55 56 result = socket_->Bind(address); 57 if (result != OK) 58 return result; 59 60 bind_address_.reset(new IPEndPoint(address)); 61 return OK; 62} 63 64int TCPClientSocket::Connect(const CompletionCallback& callback) { 65 DCHECK(!callback.is_null()); 66 67 // If connecting or already connected, then just return OK. 68 if (socket_->IsValid() && current_address_index_ >= 0) 69 return OK; 70 71 socket_->StartLoggingMultipleConnectAttempts(addresses_); 72 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; 77 78 int rv = DoConnectLoop(OK); 79 if (rv == ERR_IO_PENDING) { 80 connect_callback_ = callback; 81 } else { 82 socket_->EndLoggingMultipleConnectAttempts(rv); 83 } 84 85 return rv; 86} 87 88int TCPClientSocket::DoConnectLoop(int result) { 89 DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE); 90 91 int rv = result; 92 do { 93 ConnectState state = next_connect_state_; 94 next_connect_state_ = CONNECT_STATE_NONE; 95 switch (state) { 96 case CONNECT_STATE_CONNECT: 97 DCHECK_EQ(OK, rv); 98 rv = DoConnect(); 99 break; 100 case CONNECT_STATE_CONNECT_COMPLETE: 101 rv = DoConnectComplete(rv); 102 break; 103 default: 104 NOTREACHED() << "bad state " << state; 105 rv = ERR_UNEXPECTED; 106 break; 107 } 108 } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE); 109 110 return rv; 111} 112 113int TCPClientSocket::DoConnect() { 114 DCHECK_GE(current_address_index_, 0); 115 DCHECK_LT(current_address_index_, static_cast<int>(addresses_.size())); 116 117 const IPEndPoint& endpoint = addresses_[current_address_index_]; 118 119 if (previously_disconnected_) { 120 use_history_.Reset(); 121 previously_disconnected_ = false; 122 } 123 124 next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE; 125 126 if (socket_->IsValid()) { 127 DCHECK(bind_address_); 128 } else { 129 int result = OpenSocket(endpoint.GetFamily()); 130 if (result != OK) 131 return result; 132 133 if (bind_address_) { 134 result = socket_->Bind(*bind_address_); 135 if (result != OK) { 136 socket_->Close(); 137 return result; 138 } 139 } 140 } 141 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))); 147} 148 149int TCPClientSocket::DoConnectComplete(int result) { 150 if (result == OK) { 151 use_history_.set_was_ever_connected(); 152 return OK; // Done! 153 } 154 155 // Close whatever partially connected socket we currently have. 156 DoDisconnect(); 157 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_; 162 return OK; 163 } 164 165 // Otherwise there is nothing to fall back to, so give up. 166 return result; 167} 168 169void TCPClientSocket::Disconnect() { 170 DoDisconnect(); 171 current_address_index_ = -1; 172 bind_address_.reset(); 173} 174 175void TCPClientSocket::DoDisconnect() { 176 // If connecting or already connected, record that the socket has been 177 // disconnected. 178 previously_disconnected_ = socket_->IsValid() && current_address_index_ >= 0; 179 socket_->Close(); 180} 181 182bool TCPClientSocket::IsConnected() const { 183 return socket_->IsConnected(); 184} 185 186bool TCPClientSocket::IsConnectedAndIdle() const { 187 return socket_->IsConnectedAndIdle(); 188} 189 190int TCPClientSocket::GetPeerAddress(IPEndPoint* address) const { 191 return socket_->GetPeerAddress(address); 192} 193 194int TCPClientSocket::GetLocalAddress(IPEndPoint* address) const { 195 DCHECK(address); 196 197 if (!socket_->IsValid()) { 198 if (bind_address_) { 199 *address = *bind_address_; 200 return OK; 201 } 202 return ERR_SOCKET_NOT_CONNECTED; 203 } 204 205 return socket_->GetLocalAddress(address); 206} 207 208const BoundNetLog& TCPClientSocket::NetLog() const { 209 return socket_->net_log(); 210} 211 212void TCPClientSocket::SetSubresourceSpeculation() { 213 use_history_.set_subresource_speculation(); 214} 215 216void TCPClientSocket::SetOmniboxSpeculation() { 217 use_history_.set_omnibox_speculation(); 218} 219 220bool TCPClientSocket::WasEverUsed() const { 221 return use_history_.was_used_to_convey_data(); 222} 223 224bool TCPClientSocket::UsingTCPFastOpen() const { 225 return socket_->UsingTCPFastOpen(); 226} 227 228bool TCPClientSocket::WasNpnNegotiated() const { 229 return false; 230} 231 232NextProto TCPClientSocket::GetNegotiatedProtocol() const { 233 return kProtoUnknown; 234} 235 236bool TCPClientSocket::GetSSLInfo(SSLInfo* ssl_info) { 237 return false; 238} 239 240int TCPClientSocket::Read(IOBuffer* buf, 241 int buf_len, 242 const CompletionCallback& callback) { 243 DCHECK(!callback.is_null()); 244 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); 250 if (result > 0) 251 use_history_.set_was_used_to_convey_data(); 252 253 return result; 254} 255 256int TCPClientSocket::Write(IOBuffer* buf, 257 int buf_len, 258 const CompletionCallback& callback) { 259 DCHECK(!callback.is_null()); 260 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); 266 if (result > 0) 267 use_history_.set_was_used_to_convey_data(); 268 269 return result; 270} 271 272bool TCPClientSocket::SetReceiveBufferSize(int32 size) { 273 return socket_->SetReceiveBufferSize(size); 274} 275 276bool TCPClientSocket::SetSendBufferSize(int32 size) { 277 return socket_->SetSendBufferSize(size); 278} 279 280bool TCPClientSocket::SetKeepAlive(bool enable, int delay) { 281 return socket_->SetKeepAlive(enable, delay); 282} 283 284bool TCPClientSocket::SetNoDelay(bool no_delay) { 285 return socket_->SetNoDelay(no_delay); 286} 287 288void 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()); 292 293 result = DoConnectLoop(result); 294 if (result != ERR_IO_PENDING) { 295 socket_->EndLoggingMultipleConnectAttempts(result); 296 base::ResetAndReturn(&connect_callback_).Run(result); 297 } 298} 299 300void TCPClientSocket::DidCompleteReadWrite(const CompletionCallback& callback, 301 int result) { 302 if (result > 0) 303 use_history_.set_was_used_to_convey_data(); 304 305 callback.Run(result); 306} 307 308int TCPClientSocket::OpenSocket(AddressFamily family) { 309 DCHECK(!socket_->IsValid()); 310 311 int result = socket_->Open(family); 312 if (result != OK) 313 return result; 314 315 socket_->SetDefaultOptionsForClient(); 316 317 return OK; 318} 319 320} // namespace net 321