- add sources.
[platform/framework/web/crosswalk.git] / src / net / socket / tcp_client_socket.cc
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
14 namespace net {
15
16 TCPClientSocket::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
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) {
33   DCHECK(socket_);
34
35   socket_->SetDefaultOptionsForClient();
36   use_history_.set_was_ever_connected();
37 }
38
39 TCPClientSocket::~TCPClientSocket() {
40 }
41
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.
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
64 int 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
88 int 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
113 int 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
149 int 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
169 void TCPClientSocket::Disconnect() {
170   DoDisconnect();
171   current_address_index_ = -1;
172   bind_address_.reset();
173 }
174
175 void 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
182 bool TCPClientSocket::IsConnected() const {
183   return socket_->IsConnected();
184 }
185
186 bool TCPClientSocket::IsConnectedAndIdle() const {
187   return socket_->IsConnectedAndIdle();
188 }
189
190 int TCPClientSocket::GetPeerAddress(IPEndPoint* address) const {
191   return socket_->GetPeerAddress(address);
192 }
193
194 int 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
208 const BoundNetLog& TCPClientSocket::NetLog() const {
209   return socket_->net_log();
210 }
211
212 void TCPClientSocket::SetSubresourceSpeculation() {
213   use_history_.set_subresource_speculation();
214 }
215
216 void TCPClientSocket::SetOmniboxSpeculation() {
217   use_history_.set_omnibox_speculation();
218 }
219
220 bool TCPClientSocket::WasEverUsed() const {
221   return use_history_.was_used_to_convey_data();
222 }
223
224 bool TCPClientSocket::UsingTCPFastOpen() const {
225   return socket_->UsingTCPFastOpen();
226 }
227
228 bool TCPClientSocket::WasNpnNegotiated() const {
229   return false;
230 }
231
232 NextProto TCPClientSocket::GetNegotiatedProtocol() const {
233   return kProtoUnknown;
234 }
235
236 bool TCPClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
237   return false;
238 }
239
240 int 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
256 int 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
272 bool TCPClientSocket::SetReceiveBufferSize(int32 size) {
273   return socket_->SetReceiveBufferSize(size);
274 }
275
276 bool TCPClientSocket::SetSendBufferSize(int32 size) {
277     return socket_->SetSendBufferSize(size);
278 }
279
280 bool TCPClientSocket::SetKeepAlive(bool enable, int delay) {
281   return socket_->SetKeepAlive(enable, delay);
282 }
283
284 bool TCPClientSocket::SetNoDelay(bool no_delay) {
285   return socket_->SetNoDelay(no_delay);
286 }
287
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());
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
300 void 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
308 int 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