OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 <errno.h> | |
8 #include <fcntl.h> | |
9 #include <netdb.h> | |
10 #include <sys/socket.h> | |
11 #include <netinet/tcp.h> | |
12 #if defined(OS_POSIX) | |
13 #include <netinet/in.h> | |
14 #endif | |
15 | |
16 #include "base/logging.h" | |
17 #include "base/message_loop/message_loop.h" | |
18 #include "base/metrics/histogram.h" | |
19 #include "base/metrics/stats_counters.h" | |
20 #include "base/posix/eintr_wrapper.h" | |
21 #include "base/strings/string_util.h" | |
22 #include "net/base/connection_type_histograms.h" | |
23 #include "net/base/io_buffer.h" | |
24 #include "net/base/ip_endpoint.h" | |
25 #include "net/base/net_errors.h" | |
26 #include "net/base/net_log.h" | |
27 #include "net/base/net_util.h" | |
28 #include "net/base/network_change_notifier.h" | |
29 #include "net/socket/socket_descriptor.h" | |
30 #include "net/socket/socket_net_log_params.h" | |
31 | |
32 // If we don't have a definition for TCPI_OPT_SYN_DATA, create one. | |
33 #ifndef TCPI_OPT_SYN_DATA | |
34 #define TCPI_OPT_SYN_DATA 32 | |
35 #endif | |
36 | |
37 namespace net { | |
38 | |
39 namespace { | |
40 | |
41 const int kTCPKeepAliveSeconds = 45; | |
42 | |
43 // SetTCPNoDelay turns on/off buffering in the kernel. By default, TCP sockets | |
44 // will wait up to 200ms for more data to complete a packet before transmitting. | |
45 // After calling this function, the kernel will not wait. See TCP_NODELAY in | |
46 // `man 7 tcp`. | |
47 bool SetTCPNoDelay(int fd, bool no_delay) { | |
48 int on = no_delay ? 1 : 0; | |
49 int error = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, | |
50 sizeof(on)); | |
51 return error == 0; | |
52 } | |
53 | |
54 // SetTCPKeepAlive sets SO_KEEPALIVE. | |
55 bool SetTCPKeepAlive(int fd, bool enable, int delay) { | |
56 int on = enable ? 1 : 0; | |
57 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))) { | |
58 PLOG(ERROR) << "Failed to set SO_KEEPALIVE on fd: " << fd; | |
59 return false; | |
60 } | |
61 #if defined(OS_LINUX) || defined(OS_ANDROID) | |
62 // Set seconds until first TCP keep alive. | |
63 if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) { | |
64 PLOG(ERROR) << "Failed to set TCP_KEEPIDLE on fd: " << fd; | |
65 return false; | |
66 } | |
67 // Set seconds between TCP keep alives. | |
68 if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &delay, sizeof(delay))) { | |
69 PLOG(ERROR) << "Failed to set TCP_KEEPINTVL on fd: " << fd; | |
70 return false; | |
71 } | |
72 #endif | |
73 return true; | |
74 } | |
75 | |
76 // Sets socket parameters. Returns the OS error code (or 0 on | |
77 // success). | |
78 int SetupSocket(int socket) { | |
79 if (SetNonBlocking(socket)) | |
80 return errno; | |
81 | |
82 // This mirrors the behaviour on Windows. See the comment in | |
83 // tcp_client_socket_win.cc after searching for "NODELAY". | |
84 SetTCPNoDelay(socket, true); // If SetTCPNoDelay fails, we don't care. | |
85 SetTCPKeepAlive(socket, true, kTCPKeepAliveSeconds); | |
86 | |
87 return 0; | |
88 } | |
89 | |
90 // Creates a new socket and sets default parameters for it. Returns | |
91 // the OS error code (or 0 on success). | |
92 int CreateSocket(int family, int* socket) { | |
93 *socket = CreatePlatformSocket(family, SOCK_STREAM, IPPROTO_TCP); | |
94 if (*socket == kInvalidSocket) | |
95 return errno; | |
96 int error = SetupSocket(*socket); | |
97 if (error) { | |
98 if (HANDLE_EINTR(close(*socket)) < 0) | |
99 PLOG(ERROR) << "close"; | |
100 *socket = kInvalidSocket; | |
101 return error; | |
102 } | |
103 return 0; | |
104 } | |
105 | |
106 int MapConnectError(int os_error) { | |
107 switch (os_error) { | |
108 case EACCES: | |
109 return ERR_NETWORK_ACCESS_DENIED; | |
110 case ETIMEDOUT: | |
111 return ERR_CONNECTION_TIMED_OUT; | |
112 default: { | |
113 int net_error = MapSystemError(os_error); | |
114 if (net_error == ERR_FAILED) | |
115 return ERR_CONNECTION_FAILED; // More specific than ERR_FAILED. | |
116 | |
117 // Give a more specific error when the user is offline. | |
118 if (net_error == ERR_ADDRESS_UNREACHABLE && | |
119 NetworkChangeNotifier::IsOffline()) { | |
120 return ERR_INTERNET_DISCONNECTED; | |
121 } | |
122 return net_error; | |
123 } | |
124 } | |
125 } | |
126 | |
127 } // namespace | |
128 | |
129 //----------------------------------------------------------------------------- | |
130 | |
131 TCPClientSocketLibevent::TCPClientSocketLibevent( | |
132 const AddressList& addresses, | |
133 net::NetLog* net_log, | |
134 const net::NetLog::Source& source) | |
135 : socket_(kInvalidSocket), | |
136 bound_socket_(kInvalidSocket), | |
137 addresses_(addresses), | |
138 current_address_index_(-1), | |
139 read_watcher_(this), | |
140 write_watcher_(this), | |
141 next_connect_state_(CONNECT_STATE_NONE), | |
142 connect_os_error_(0), | |
143 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)), | |
144 previously_disconnected_(false), | |
145 use_tcp_fastopen_(IsTCPFastOpenEnabled()), | |
146 tcp_fastopen_connected_(false), | |
147 fast_open_status_(FAST_OPEN_STATUS_UNKNOWN) { | |
148 net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, | |
149 source.ToEventParametersCallback()); | |
150 } | |
151 | |
152 TCPClientSocketLibevent::~TCPClientSocketLibevent() { | |
153 Disconnect(); | |
154 net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE); | |
155 if (tcp_fastopen_connected_) { | |
156 UMA_HISTOGRAM_ENUMERATION("Net.TcpFastOpenSocketConnection", | |
157 fast_open_status_, FAST_OPEN_MAX_VALUE); | |
158 } | |
159 } | |
160 | |
161 int TCPClientSocketLibevent::AdoptSocket(int socket) { | |
162 DCHECK_EQ(socket_, kInvalidSocket); | |
163 | |
164 int error = SetupSocket(socket); | |
165 if (error) | |
166 return MapSystemError(error); | |
167 | |
168 socket_ = socket; | |
169 | |
170 // This is to make GetPeerAddress() work. It's up to the caller ensure | |
171 // that |address_| contains a reasonable address for this | |
172 // socket. (i.e. at least match IPv4 vs IPv6!). | |
173 current_address_index_ = 0; | |
174 use_history_.set_was_ever_connected(); | |
175 | |
176 return OK; | |
177 } | |
178 | |
179 int TCPClientSocketLibevent::Bind(const IPEndPoint& address) { | |
180 if (current_address_index_ >= 0 || bind_address_.get()) { | |
181 // Cannot bind the socket if we are already bound connected or | |
182 // connecting. | |
183 return ERR_UNEXPECTED; | |
184 } | |
185 | |
186 SockaddrStorage storage; | |
187 if (!address.ToSockAddr(storage.addr, &storage.addr_len)) | |
188 return ERR_INVALID_ARGUMENT; | |
189 | |
190 // Create |bound_socket_| and try to bind it to |address|. | |
191 int error = CreateSocket(address.GetSockAddrFamily(), &bound_socket_); | |
192 if (error) | |
193 return MapSystemError(error); | |
194 | |
195 if (HANDLE_EINTR(bind(bound_socket_, storage.addr, storage.addr_len))) { | |
196 error = errno; | |
197 if (HANDLE_EINTR(close(bound_socket_)) < 0) | |
198 PLOG(ERROR) << "close"; | |
199 bound_socket_ = kInvalidSocket; | |
200 return MapSystemError(error); | |
201 } | |
202 | |
203 bind_address_.reset(new IPEndPoint(address)); | |
204 | |
205 return 0; | |
206 } | |
207 | |
208 int TCPClientSocketLibevent::Connect(const CompletionCallback& callback) { | |
209 DCHECK(CalledOnValidThread()); | |
210 | |
211 // If already connected, then just return OK. | |
212 if (socket_ != kInvalidSocket) | |
213 return OK; | |
214 | |
215 base::StatsCounter connects("tcp.connect"); | |
216 connects.Increment(); | |
217 | |
218 DCHECK(!waiting_connect()); | |
219 | |
220 net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT, | |
221 addresses_.CreateNetLogCallback()); | |
222 | |
223 // We will try to connect to each address in addresses_. Start with the | |
224 // first one in the list. | |
225 next_connect_state_ = CONNECT_STATE_CONNECT; | |
226 current_address_index_ = 0; | |
227 | |
228 int rv = DoConnectLoop(OK); | |
229 if (rv == ERR_IO_PENDING) { | |
230 // Synchronous operation not supported. | |
231 DCHECK(!callback.is_null()); | |
232 write_callback_ = callback; | |
233 } else { | |
234 LogConnectCompletion(rv); | |
235 } | |
236 | |
237 return rv; | |
238 } | |
239 | |
240 int TCPClientSocketLibevent::DoConnectLoop(int result) { | |
241 DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE); | |
242 | |
243 int rv = result; | |
244 do { | |
245 ConnectState state = next_connect_state_; | |
246 next_connect_state_ = CONNECT_STATE_NONE; | |
247 switch (state) { | |
248 case CONNECT_STATE_CONNECT: | |
249 DCHECK_EQ(OK, rv); | |
250 rv = DoConnect(); | |
251 break; | |
252 case CONNECT_STATE_CONNECT_COMPLETE: | |
253 rv = DoConnectComplete(rv); | |
254 break; | |
255 default: | |
256 LOG(DFATAL) << "bad state"; | |
257 rv = ERR_UNEXPECTED; | |
258 break; | |
259 } | |
260 } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE); | |
261 | |
262 return rv; | |
263 } | |
264 | |
265 int TCPClientSocketLibevent::DoConnect() { | |
266 DCHECK_GE(current_address_index_, 0); | |
267 DCHECK_LT(current_address_index_, static_cast<int>(addresses_.size())); | |
268 DCHECK_EQ(0, connect_os_error_); | |
269 | |
270 const IPEndPoint& endpoint = addresses_[current_address_index_]; | |
271 | |
272 if (previously_disconnected_) { | |
273 use_history_.Reset(); | |
274 previously_disconnected_ = false; | |
275 } | |
276 | |
277 net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, | |
278 CreateNetLogIPEndPointCallback(&endpoint)); | |
279 | |
280 next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE; | |
281 | |
282 if (bound_socket_ != kInvalidSocket) { | |
283 DCHECK(bind_address_.get()); | |
284 socket_ = bound_socket_; | |
285 bound_socket_ = kInvalidSocket; | |
286 } else { | |
287 // Create a non-blocking socket. | |
288 connect_os_error_ = CreateSocket(endpoint.GetSockAddrFamily(), &socket_); | |
289 if (connect_os_error_) | |
290 return MapSystemError(connect_os_error_); | |
291 | |
292 if (bind_address_.get()) { | |
293 SockaddrStorage storage; | |
294 if (!bind_address_->ToSockAddr(storage.addr, &storage.addr_len)) | |
295 return ERR_INVALID_ARGUMENT; | |
296 if (HANDLE_EINTR(bind(socket_, storage.addr, storage.addr_len))) | |
297 return MapSystemError(errno); | |
298 } | |
299 } | |
300 | |
301 // Connect the socket. | |
302 if (!use_tcp_fastopen_) { | |
303 SockaddrStorage storage; | |
304 if (!endpoint.ToSockAddr(storage.addr, &storage.addr_len)) | |
305 return ERR_INVALID_ARGUMENT; | |
306 | |
307 if (!HANDLE_EINTR(connect(socket_, storage.addr, storage.addr_len))) { | |
308 // Connected without waiting! | |
309 return OK; | |
310 } | |
311 } else { | |
312 // With TCP FastOpen, we pretend that the socket is connected. | |
313 DCHECK(!tcp_fastopen_connected_); | |
314 return OK; | |
315 } | |
316 | |
317 // Check if the connect() failed synchronously. | |
318 connect_os_error_ = errno; | |
319 if (connect_os_error_ != EINPROGRESS) | |
320 return MapConnectError(connect_os_error_); | |
321 | |
322 // Otherwise the connect() is going to complete asynchronously, so watch | |
323 // for its completion. | |
324 if (!base::MessageLoopForIO::current()->WatchFileDescriptor( | |
325 socket_, true, base::MessageLoopForIO::WATCH_WRITE, | |
326 &write_socket_watcher_, &write_watcher_)) { | |
327 connect_os_error_ = errno; | |
328 DVLOG(1) << "WatchFileDescriptor failed: " << connect_os_error_; | |
329 return MapSystemError(connect_os_error_); | |
330 } | |
331 | |
332 return ERR_IO_PENDING; | |
333 } | |
334 | |
335 int TCPClientSocketLibevent::DoConnectComplete(int result) { | |
336 // Log the end of this attempt (and any OS error it threw). | |
337 int os_error = connect_os_error_; | |
338 connect_os_error_ = 0; | |
339 if (result != OK) { | |
340 net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, | |
341 NetLog::IntegerCallback("os_error", os_error)); | |
342 } else { | |
343 net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT); | |
344 } | |
345 | |
346 if (result == OK) { | |
347 write_socket_watcher_.StopWatchingFileDescriptor(); | |
348 use_history_.set_was_ever_connected(); | |
349 return OK; // Done! | |
350 } | |
351 | |
352 // Close whatever partially connected socket we currently have. | |
353 DoDisconnect(); | |
354 | |
355 // Try to fall back to the next address in the list. | |
356 if (current_address_index_ + 1 < static_cast<int>(addresses_.size())) { | |
357 next_connect_state_ = CONNECT_STATE_CONNECT; | |
358 ++current_address_index_; | |
359 return OK; | |
360 } | |
361 | |
362 // Otherwise there is nothing to fall back to, so give up. | |
363 return result; | |
364 } | |
365 | |
366 void TCPClientSocketLibevent::Disconnect() { | |
367 DCHECK(CalledOnValidThread()); | |
368 | |
369 DoDisconnect(); | |
370 current_address_index_ = -1; | |
371 bind_address_.reset(); | |
372 } | |
373 | |
374 void TCPClientSocketLibevent::DoDisconnect() { | |
375 if (socket_ == kInvalidSocket) | |
376 return; | |
377 | |
378 bool ok = read_socket_watcher_.StopWatchingFileDescriptor(); | |
379 DCHECK(ok); | |
380 ok = write_socket_watcher_.StopWatchingFileDescriptor(); | |
381 DCHECK(ok); | |
382 if (HANDLE_EINTR(close(socket_)) < 0) | |
383 PLOG(ERROR) << "close"; | |
384 socket_ = kInvalidSocket; | |
385 previously_disconnected_ = true; | |
386 } | |
387 | |
388 bool TCPClientSocketLibevent::IsConnected() const { | |
389 DCHECK(CalledOnValidThread()); | |
390 | |
391 if (socket_ == kInvalidSocket || waiting_connect()) | |
392 return false; | |
393 | |
394 if (use_tcp_fastopen_ && !tcp_fastopen_connected_) { | |
395 // With TCP FastOpen, we pretend that the socket is connected. | |
396 // This allows GetPeerAddress() to return current_ai_ as the peer | |
397 // address. Since we don't fail over to the next address if | |
398 // sendto() fails, current_ai_ is the only possible peer address. | |
399 CHECK_LT(current_address_index_, static_cast<int>(addresses_.size())); | |
400 return true; | |
401 } | |
402 | |
403 // Check if connection is alive. | |
404 char c; | |
405 int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK)); | |
406 if (rv == 0) | |
407 return false; | |
408 if (rv == -1 && errno != EAGAIN && errno != EWOULDBLOCK) | |
409 return false; | |
410 | |
411 return true; | |
412 } | |
413 | |
414 bool TCPClientSocketLibevent::IsConnectedAndIdle() const { | |
415 DCHECK(CalledOnValidThread()); | |
416 | |
417 if (socket_ == kInvalidSocket || waiting_connect()) | |
418 return false; | |
419 | |
420 // TODO(wtc): should we also handle the TCP FastOpen case here, | |
421 // as we do in IsConnected()? | |
422 | |
423 // Check if connection is alive and we haven't received any data | |
424 // unexpectedly. | |
425 char c; | |
426 int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK)); | |
427 if (rv >= 0) | |
428 return false; | |
429 if (errno != EAGAIN && errno != EWOULDBLOCK) | |
430 return false; | |
431 | |
432 return true; | |
433 } | |
434 | |
435 int TCPClientSocketLibevent::Read(IOBuffer* buf, | |
436 int buf_len, | |
437 const CompletionCallback& callback) { | |
438 DCHECK(CalledOnValidThread()); | |
439 DCHECK_NE(kInvalidSocket, socket_); | |
440 DCHECK(!waiting_connect()); | |
441 DCHECK(read_callback_.is_null()); | |
442 // Synchronous operation not supported | |
443 DCHECK(!callback.is_null()); | |
444 DCHECK_GT(buf_len, 0); | |
445 | |
446 int nread = HANDLE_EINTR(read(socket_, buf->data(), buf_len)); | |
447 if (nread >= 0) { | |
448 base::StatsCounter read_bytes("tcp.read_bytes"); | |
449 read_bytes.Add(nread); | |
450 if (nread > 0) | |
451 use_history_.set_was_used_to_convey_data(); | |
452 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, nread, | |
453 buf->data()); | |
454 RecordFastOpenStatus(); | |
455 return nread; | |
456 } | |
457 if (errno != EAGAIN && errno != EWOULDBLOCK) { | |
458 int net_error = MapSystemError(errno); | |
459 net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR, | |
460 CreateNetLogSocketErrorCallback(net_error, errno)); | |
461 return net_error; | |
462 } | |
463 | |
464 if (!base::MessageLoopForIO::current()->WatchFileDescriptor( | |
465 socket_, true, base::MessageLoopForIO::WATCH_READ, | |
466 &read_socket_watcher_, &read_watcher_)) { | |
467 DVLOG(1) << "WatchFileDescriptor failed on read, errno " << errno; | |
468 return MapSystemError(errno); | |
469 } | |
470 | |
471 read_buf_ = buf; | |
472 read_buf_len_ = buf_len; | |
473 read_callback_ = callback; | |
474 return ERR_IO_PENDING; | |
475 } | |
476 | |
477 int TCPClientSocketLibevent::Write(IOBuffer* buf, | |
478 int buf_len, | |
479 const CompletionCallback& callback) { | |
480 DCHECK(CalledOnValidThread()); | |
481 DCHECK_NE(kInvalidSocket, socket_); | |
482 DCHECK(!waiting_connect()); | |
483 DCHECK(write_callback_.is_null()); | |
484 // Synchronous operation not supported | |
485 DCHECK(!callback.is_null()); | |
486 DCHECK_GT(buf_len, 0); | |
487 | |
488 int nwrite = InternalWrite(buf, buf_len); | |
489 if (nwrite >= 0) { | |
490 base::StatsCounter write_bytes("tcp.write_bytes"); | |
491 write_bytes.Add(nwrite); | |
492 if (nwrite > 0) | |
493 use_history_.set_was_used_to_convey_data(); | |
494 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, nwrite, | |
495 buf->data()); | |
496 return nwrite; | |
497 } | |
498 if (errno != EAGAIN && errno != EWOULDBLOCK) { | |
499 int net_error = MapSystemError(errno); | |
500 net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR, | |
501 CreateNetLogSocketErrorCallback(net_error, errno)); | |
502 return net_error; | |
503 } | |
504 | |
505 if (!base::MessageLoopForIO::current()->WatchFileDescriptor( | |
506 socket_, true, base::MessageLoopForIO::WATCH_WRITE, | |
507 &write_socket_watcher_, &write_watcher_)) { | |
508 DVLOG(1) << "WatchFileDescriptor failed on write, errno " << errno; | |
509 return MapSystemError(errno); | |
510 } | |
511 | |
512 write_buf_ = buf; | |
513 write_buf_len_ = buf_len; | |
514 write_callback_ = callback; | |
515 return ERR_IO_PENDING; | |
516 } | |
517 | |
518 int TCPClientSocketLibevent::InternalWrite(IOBuffer* buf, int buf_len) { | |
519 int nwrite; | |
520 if (use_tcp_fastopen_ && !tcp_fastopen_connected_) { | |
521 SockaddrStorage storage; | |
522 if (!addresses_[current_address_index_].ToSockAddr(storage.addr, | |
523 &storage.addr_len)) { | |
524 errno = EINVAL; | |
525 return -1; | |
526 } | |
527 | |
528 int flags = 0x20000000; // Magic flag to enable TCP_FASTOPEN. | |
529 #if defined(OS_LINUX) | |
530 // sendto() will fail with EPIPE when the system doesn't support TCP Fast | |
531 // Open. Theoretically that shouldn't happen since the caller should check | |
532 // for system support on startup, but users may dynamically disable TCP Fast | |
533 // Open via sysctl. | |
534 flags |= MSG_NOSIGNAL; | |
535 #endif // defined(OS_LINUX) | |
536 nwrite = HANDLE_EINTR(sendto(socket_, | |
537 buf->data(), | |
538 buf_len, | |
539 flags, | |
540 storage.addr, | |
541 storage.addr_len)); | |
542 tcp_fastopen_connected_ = true; | |
543 | |
544 if (nwrite < 0) { | |
545 DCHECK_NE(EPIPE, errno); | |
546 | |
547 // If errno == EINPROGRESS, that means the kernel didn't have a cookie | |
548 // and would block. The kernel is internally doing a connect() though. | |
549 // Remap EINPROGRESS to EAGAIN so we treat this the same as our other | |
550 // asynchronous cases. Note that the user buffer has not been copied to | |
551 // kernel space. | |
552 if (errno == EINPROGRESS) { | |
553 errno = EAGAIN; | |
554 fast_open_status_ = FAST_OPEN_SLOW_CONNECT_RETURN; | |
555 } else { | |
556 fast_open_status_ = FAST_OPEN_ERROR; | |
557 } | |
558 } else { | |
559 fast_open_status_ = FAST_OPEN_FAST_CONNECT_RETURN; | |
560 } | |
561 } else { | |
562 nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len)); | |
563 } | |
564 return nwrite; | |
565 } | |
566 | |
567 bool TCPClientSocketLibevent::SetReceiveBufferSize(int32 size) { | |
568 DCHECK(CalledOnValidThread()); | |
569 int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF, | |
570 reinterpret_cast<const char*>(&size), | |
571 sizeof(size)); | |
572 DCHECK(!rv) << "Could not set socket receive buffer size: " << errno; | |
573 return rv == 0; | |
574 } | |
575 | |
576 bool TCPClientSocketLibevent::SetSendBufferSize(int32 size) { | |
577 DCHECK(CalledOnValidThread()); | |
578 int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, | |
579 reinterpret_cast<const char*>(&size), | |
580 sizeof(size)); | |
581 DCHECK(!rv) << "Could not set socket send buffer size: " << errno; | |
582 return rv == 0; | |
583 } | |
584 | |
585 bool TCPClientSocketLibevent::SetKeepAlive(bool enable, int delay) { | |
586 int socket = socket_ != kInvalidSocket ? socket_ : bound_socket_; | |
587 return SetTCPKeepAlive(socket, enable, delay); | |
588 } | |
589 | |
590 bool TCPClientSocketLibevent::SetNoDelay(bool no_delay) { | |
591 int socket = socket_ != kInvalidSocket ? socket_ : bound_socket_; | |
592 return SetTCPNoDelay(socket, no_delay); | |
593 } | |
594 | |
595 void TCPClientSocketLibevent::ReadWatcher::OnFileCanReadWithoutBlocking(int) { | |
596 socket_->RecordFastOpenStatus(); | |
597 if (!socket_->read_callback_.is_null()) | |
598 socket_->DidCompleteRead(); | |
599 } | |
600 | |
601 void TCPClientSocketLibevent::WriteWatcher::OnFileCanWriteWithoutBlocking(int) { | |
602 if (socket_->waiting_connect()) { | |
603 socket_->DidCompleteConnect(); | |
604 } else if (!socket_->write_callback_.is_null()) { | |
605 socket_->DidCompleteWrite(); | |
606 } | |
607 } | |
608 | |
609 void TCPClientSocketLibevent::LogConnectCompletion(int net_error) { | |
610 if (net_error == OK) | |
611 UpdateConnectionTypeHistograms(CONNECTION_ANY); | |
612 | |
613 if (net_error != OK) { | |
614 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, net_error); | |
615 return; | |
616 } | |
617 | |
618 SockaddrStorage storage; | |
619 int rv = getsockname(socket_, storage.addr, &storage.addr_len); | |
620 if (rv != 0) { | |
621 PLOG(ERROR) << "getsockname() [rv: " << rv << "] error: "; | |
622 NOTREACHED(); | |
623 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, rv); | |
624 return; | |
625 } | |
626 | |
627 net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT, | |
628 CreateNetLogSourceAddressCallback(storage.addr, | |
629 storage.addr_len)); | |
630 } | |
631 | |
632 void TCPClientSocketLibevent::DoReadCallback(int rv) { | |
633 DCHECK_NE(rv, ERR_IO_PENDING); | |
634 DCHECK(!read_callback_.is_null()); | |
635 | |
636 // since Run may result in Read being called, clear read_callback_ up front. | |
637 CompletionCallback c = read_callback_; | |
638 read_callback_.Reset(); | |
639 c.Run(rv); | |
640 } | |
641 | |
642 void TCPClientSocketLibevent::DoWriteCallback(int rv) { | |
643 DCHECK_NE(rv, ERR_IO_PENDING); | |
644 DCHECK(!write_callback_.is_null()); | |
645 | |
646 // since Run may result in Write being called, clear write_callback_ up front. | |
647 CompletionCallback c = write_callback_; | |
648 write_callback_.Reset(); | |
649 c.Run(rv); | |
650 } | |
651 | |
652 void TCPClientSocketLibevent::DidCompleteConnect() { | |
653 DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE); | |
654 | |
655 // Get the error that connect() completed with. | |
656 int os_error = 0; | |
657 socklen_t len = sizeof(os_error); | |
658 if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, &os_error, &len) < 0) | |
659 os_error = errno; | |
660 | |
661 // TODO(eroman): Is this check really necessary? | |
662 if (os_error == EINPROGRESS || os_error == EALREADY) { | |
663 NOTREACHED(); // This indicates a bug in libevent or our code. | |
664 return; | |
665 } | |
666 | |
667 connect_os_error_ = os_error; | |
668 int rv = DoConnectLoop(MapConnectError(os_error)); | |
669 if (rv != ERR_IO_PENDING) { | |
670 LogConnectCompletion(rv); | |
671 DoWriteCallback(rv); | |
672 } | |
673 } | |
674 | |
675 void TCPClientSocketLibevent::DidCompleteRead() { | |
676 int bytes_transferred; | |
677 bytes_transferred = HANDLE_EINTR(read(socket_, read_buf_->data(), | |
678 read_buf_len_)); | |
679 | |
680 int result; | |
681 if (bytes_transferred >= 0) { | |
682 result = bytes_transferred; | |
683 base::StatsCounter read_bytes("tcp.read_bytes"); | |
684 read_bytes.Add(bytes_transferred); | |
685 if (bytes_transferred > 0) | |
686 use_history_.set_was_used_to_convey_data(); | |
687 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, result, | |
688 read_buf_->data()); | |
689 } else { | |
690 result = MapSystemError(errno); | |
691 if (result != ERR_IO_PENDING) { | |
692 net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR, | |
693 CreateNetLogSocketErrorCallback(result, errno)); | |
694 } | |
695 } | |
696 | |
697 if (result != ERR_IO_PENDING) { | |
698 read_buf_ = NULL; | |
699 read_buf_len_ = 0; | |
700 bool ok = read_socket_watcher_.StopWatchingFileDescriptor(); | |
701 DCHECK(ok); | |
702 DoReadCallback(result); | |
703 } | |
704 } | |
705 | |
706 void TCPClientSocketLibevent::DidCompleteWrite() { | |
707 int bytes_transferred; | |
708 bytes_transferred = HANDLE_EINTR(write(socket_, write_buf_->data(), | |
709 write_buf_len_)); | |
710 | |
711 int result; | |
712 if (bytes_transferred >= 0) { | |
713 result = bytes_transferred; | |
714 base::StatsCounter write_bytes("tcp.write_bytes"); | |
715 write_bytes.Add(bytes_transferred); | |
716 if (bytes_transferred > 0) | |
717 use_history_.set_was_used_to_convey_data(); | |
718 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, result, | |
719 write_buf_->data()); | |
720 } else { | |
721 result = MapSystemError(errno); | |
722 if (result != ERR_IO_PENDING) { | |
723 net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR, | |
724 CreateNetLogSocketErrorCallback(result, errno)); | |
725 } | |
726 } | |
727 | |
728 if (result != ERR_IO_PENDING) { | |
729 write_buf_ = NULL; | |
730 write_buf_len_ = 0; | |
731 write_socket_watcher_.StopWatchingFileDescriptor(); | |
732 DoWriteCallback(result); | |
733 } | |
734 } | |
735 | |
736 int TCPClientSocketLibevent::GetPeerAddress(IPEndPoint* address) const { | |
737 DCHECK(CalledOnValidThread()); | |
738 DCHECK(address); | |
739 if (!IsConnected()) | |
740 return ERR_SOCKET_NOT_CONNECTED; | |
741 *address = addresses_[current_address_index_]; | |
742 return OK; | |
743 } | |
744 | |
745 int TCPClientSocketLibevent::GetLocalAddress(IPEndPoint* address) const { | |
746 DCHECK(CalledOnValidThread()); | |
747 DCHECK(address); | |
748 if (socket_ == kInvalidSocket) { | |
749 if (bind_address_.get()) { | |
750 *address = *bind_address_; | |
751 return OK; | |
752 } | |
753 return ERR_SOCKET_NOT_CONNECTED; | |
754 } | |
755 | |
756 SockaddrStorage storage; | |
757 if (getsockname(socket_, storage.addr, &storage.addr_len)) | |
758 return MapSystemError(errno); | |
759 if (!address->FromSockAddr(storage.addr, storage.addr_len)) | |
760 return ERR_FAILED; | |
761 | |
762 return OK; | |
763 } | |
764 | |
765 void TCPClientSocketLibevent::RecordFastOpenStatus() { | |
766 if (use_tcp_fastopen_ && | |
767 (fast_open_status_ == FAST_OPEN_FAST_CONNECT_RETURN || | |
768 fast_open_status_ == FAST_OPEN_SLOW_CONNECT_RETURN)) { | |
769 DCHECK_NE(FAST_OPEN_STATUS_UNKNOWN, fast_open_status_); | |
770 bool getsockopt_success(false); | |
771 bool server_acked_data(false); | |
772 #if defined(TCP_INFO) | |
773 // Probe to see the if the socket used TCP Fast Open. | |
774 tcp_info info; | |
775 socklen_t info_len = sizeof(tcp_info); | |
776 getsockopt_success = | |
777 getsockopt(socket_, IPPROTO_TCP, TCP_INFO, &info, &info_len) == 0 && | |
778 info_len == sizeof(tcp_info); | |
779 server_acked_data = getsockopt_success && | |
780 (info.tcpi_options & TCPI_OPT_SYN_DATA); | |
781 #endif | |
782 if (getsockopt_success) { | |
783 if (fast_open_status_ == FAST_OPEN_FAST_CONNECT_RETURN) { | |
784 fast_open_status_ = (server_acked_data ? FAST_OPEN_SYN_DATA_ACK : | |
785 FAST_OPEN_SYN_DATA_NACK); | |
786 } else { | |
787 fast_open_status_ = (server_acked_data ? FAST_OPEN_NO_SYN_DATA_ACK : | |
788 FAST_OPEN_NO_SYN_DATA_NACK); | |
789 } | |
790 } else { | |
791 fast_open_status_ = (fast_open_status_ == FAST_OPEN_FAST_CONNECT_RETURN ? | |
792 FAST_OPEN_SYN_DATA_FAILED : | |
793 FAST_OPEN_NO_SYN_DATA_FAILED); | |
794 } | |
795 } | |
796 } | |
797 | |
798 const BoundNetLog& TCPClientSocketLibevent::NetLog() const { | |
799 return net_log_; | |
800 } | |
801 | |
802 void TCPClientSocketLibevent::SetSubresourceSpeculation() { | |
803 use_history_.set_subresource_speculation(); | |
804 } | |
805 | |
806 void TCPClientSocketLibevent::SetOmniboxSpeculation() { | |
807 use_history_.set_omnibox_speculation(); | |
808 } | |
809 | |
810 bool TCPClientSocketLibevent::WasEverUsed() const { | |
811 return use_history_.was_used_to_convey_data(); | |
812 } | |
813 | |
814 bool TCPClientSocketLibevent::UsingTCPFastOpen() const { | |
815 return use_tcp_fastopen_; | |
816 } | |
817 | |
818 bool TCPClientSocketLibevent::WasNpnNegotiated() const { | |
819 return false; | |
820 } | |
821 | |
822 NextProto TCPClientSocketLibevent::GetNegotiatedProtocol() const { | |
823 return kProtoUnknown; | |
824 } | |
825 | |
826 bool TCPClientSocketLibevent::GetSSLInfo(SSLInfo* ssl_info) { | |
827 return false; | |
828 } | |
829 | |
830 } // namespace net | |
OLD | NEW |