• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2006-2008 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_libevent.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 
13 #include "base/eintr_wrapper.h"
14 #include "base/message_loop.h"
15 #include "base/string_util.h"
16 #include "base/trace_event.h"
17 #include "net/base/io_buffer.h"
18 #include "net/base/load_log.h"
19 #include "net/base/net_errors.h"
20 #if defined(USE_SYSTEM_LIBEVENT)
21 #include <event.h>
22 #else
23 #include "third_party/libevent/event.h"
24 #endif
25 
26 namespace net {
27 
28 namespace {
29 
30 const int kInvalidSocket = -1;
31 
32 // Return 0 on success, -1 on failure.
33 // Too small a function to bother putting in a library?
SetNonBlocking(int fd)34 int SetNonBlocking(int fd) {
35   int flags = fcntl(fd, F_GETFL, 0);
36   if (-1 == flags)
37     return flags;
38   return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
39 }
40 
41 // DisableNagle turns off buffering in the kernel. By default, TCP sockets will
42 // wait up to 200ms for more data to complete a packet before transmitting.
43 // After calling this function, the kernel will not wait. See TCP_NODELAY in
44 // `man 7 tcp`.
DisableNagle(int fd)45 int DisableNagle(int fd) {
46   int on = 1;
47   return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
48 }
49 
50 // Convert values from <errno.h> to values from "net/base/net_errors.h"
MapPosixError(int os_error)51 int MapPosixError(int os_error) {
52   // There are numerous posix error codes, but these are the ones we thus far
53   // find interesting.
54   switch (os_error) {
55     case EAGAIN:
56 #if EWOULDBLOCK != EAGAIN
57     case EWOULDBLOCK:
58 #endif
59       return ERR_IO_PENDING;
60     case EACCES:
61       return ERR_ACCESS_DENIED;
62     case ENETDOWN:
63       return ERR_INTERNET_DISCONNECTED;
64     case ETIMEDOUT:
65       return ERR_TIMED_OUT;
66     case ECONNRESET:
67     case ENETRESET:  // Related to keep-alive
68     case EPIPE:
69       return ERR_CONNECTION_RESET;
70     case ECONNABORTED:
71       return ERR_CONNECTION_ABORTED;
72     case ECONNREFUSED:
73       return ERR_CONNECTION_REFUSED;
74     case EHOSTUNREACH:
75     case ENETUNREACH:
76       return ERR_ADDRESS_UNREACHABLE;
77     case EADDRNOTAVAIL:
78       return ERR_ADDRESS_INVALID;
79     case 0:
80       return OK;
81     default:
82       LOG(WARNING) << "Unknown error " << os_error
83                    << " mapped to net::ERR_FAILED";
84       return ERR_FAILED;
85   }
86 }
87 
MapConnectError(int os_error)88 int MapConnectError(int os_error) {
89   switch (os_error) {
90     case ETIMEDOUT:
91       return ERR_CONNECTION_TIMED_OUT;
92     default: {
93       int net_error = MapPosixError(os_error);
94       if (net_error == ERR_FAILED)
95         return ERR_CONNECTION_FAILED;  // More specific than ERR_FAILED.
96       return net_error;
97     }
98   }
99 }
100 
101 // Given os_error, an errno from a connect() attempt, returns true if
102 // connect() should be retried with another address.
ShouldTryNextAddress(int os_error)103 bool ShouldTryNextAddress(int os_error) {
104   switch (os_error) {
105     case EADDRNOTAVAIL:
106     case EAFNOSUPPORT:
107     case ECONNREFUSED:
108     case ECONNRESET:
109     case EACCES:
110     case EPERM:
111     case ENETUNREACH:
112     case EHOSTUNREACH:
113     case ENETDOWN:
114     case ETIMEDOUT:
115       return true;
116     default:
117       return false;
118   }
119 }
120 
121 }  // namespace
122 
123 //-----------------------------------------------------------------------------
124 
TCPClientSocketLibevent(const AddressList & addresses)125 TCPClientSocketLibevent::TCPClientSocketLibevent(const AddressList& addresses)
126     : socket_(kInvalidSocket),
127       addresses_(addresses),
128       current_ai_(addresses_.head()),
129       waiting_connect_(false),
130       read_watcher_(this),
131       write_watcher_(this),
132       read_callback_(NULL),
133       write_callback_(NULL) {
134 }
135 
~TCPClientSocketLibevent()136 TCPClientSocketLibevent::~TCPClientSocketLibevent() {
137   Disconnect();
138 }
139 
Connect(CompletionCallback * callback,LoadLog * load_log)140 int TCPClientSocketLibevent::Connect(CompletionCallback* callback,
141                                      LoadLog* load_log) {
142   // If already connected, then just return OK.
143   if (socket_ != kInvalidSocket)
144     return OK;
145 
146   DCHECK(!waiting_connect_);
147   DCHECK(!load_log_);
148 
149   TRACE_EVENT_BEGIN("socket.connect", this, "");
150 
151   LoadLog::BeginEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
152 
153   int rv = DoConnect();
154 
155   if (rv == ERR_IO_PENDING) {
156     // Synchronous operation not supported.
157     DCHECK(callback);
158 
159     load_log_ = load_log;
160     waiting_connect_ = true;
161     write_callback_ = callback;
162   } else {
163     TRACE_EVENT_END("socket.connect", this, "");
164     LoadLog::EndEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
165   }
166 
167   return rv;
168 }
169 
DoConnect()170 int TCPClientSocketLibevent::DoConnect() {
171   while (true) {
172     DCHECK(current_ai_);
173 
174     int rv = CreateSocket(current_ai_);
175     if (rv != OK)
176       return rv;
177 
178     if (!HANDLE_EINTR(connect(socket_, current_ai_->ai_addr,
179                               static_cast<int>(current_ai_->ai_addrlen)))) {
180       // Connected without waiting!
181       return OK;
182     }
183 
184     int os_error = errno;
185     if (os_error == EINPROGRESS)
186       break;
187 
188     close(socket_);
189     socket_ = kInvalidSocket;
190 
191     if (current_ai_->ai_next && ShouldTryNextAddress(os_error)) {
192       // connect() can fail synchronously for an address even on a
193       // non-blocking socket.  As an example, this can happen when there is
194       // no route to the host.  Retry using the next address in the list.
195       current_ai_ = current_ai_->ai_next;
196     } else {
197       DLOG(INFO) << "connect failed: " << os_error;
198       return MapConnectError(os_error);
199     }
200   }
201 
202   // Initialize write_socket_watcher_ and link it to our MessagePump.
203   // POLLOUT is set if the connection is established.
204   // POLLIN is set if the connection fails.
205   if (!MessageLoopForIO::current()->WatchFileDescriptor(
206           socket_, true, MessageLoopForIO::WATCH_WRITE, &write_socket_watcher_,
207           &write_watcher_)) {
208     DLOG(INFO) << "WatchFileDescriptor failed: " << errno;
209     close(socket_);
210     socket_ = kInvalidSocket;
211     return MapPosixError(errno);
212   }
213 
214   return ERR_IO_PENDING;
215 }
216 
Disconnect()217 void TCPClientSocketLibevent::Disconnect() {
218   if (socket_ == kInvalidSocket)
219     return;
220 
221   TRACE_EVENT_INSTANT("socket.disconnect", this, "");
222 
223   bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
224   DCHECK(ok);
225   ok = write_socket_watcher_.StopWatchingFileDescriptor();
226   DCHECK(ok);
227   close(socket_);
228   socket_ = kInvalidSocket;
229   waiting_connect_ = false;
230 
231   // Reset for next time.
232   current_ai_ = addresses_.head();
233 }
234 
IsConnected() const235 bool TCPClientSocketLibevent::IsConnected() const {
236   if (socket_ == kInvalidSocket || waiting_connect_)
237     return false;
238 
239   // Check if connection is alive.
240   char c;
241   int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK));
242   if (rv == 0)
243     return false;
244   if (rv == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
245     return false;
246 
247   return true;
248 }
249 
IsConnectedAndIdle() const250 bool TCPClientSocketLibevent::IsConnectedAndIdle() const {
251   if (socket_ == kInvalidSocket || waiting_connect_)
252     return false;
253 
254   // Check if connection is alive and we haven't received any data
255   // unexpectedly.
256   char c;
257   int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK));
258   if (rv >= 0)
259     return false;
260   if (errno != EAGAIN && errno != EWOULDBLOCK)
261     return false;
262 
263   return true;
264 }
265 
Read(IOBuffer * buf,int buf_len,CompletionCallback * callback)266 int TCPClientSocketLibevent::Read(IOBuffer* buf,
267                                   int buf_len,
268                                   CompletionCallback* callback) {
269   DCHECK_NE(kInvalidSocket, socket_);
270   DCHECK(!waiting_connect_);
271   DCHECK(!read_callback_);
272   // Synchronous operation not supported
273   DCHECK(callback);
274   DCHECK_GT(buf_len, 0);
275 
276   TRACE_EVENT_BEGIN("socket.read", this, "");
277   int nread = HANDLE_EINTR(read(socket_, buf->data(), buf_len));
278   if (nread >= 0) {
279     TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", nread));
280     return nread;
281   }
282   if (errno != EAGAIN && errno != EWOULDBLOCK) {
283     DLOG(INFO) << "read failed, errno " << errno;
284     return MapPosixError(errno);
285   }
286 
287   if (!MessageLoopForIO::current()->WatchFileDescriptor(
288           socket_, true, MessageLoopForIO::WATCH_READ,
289           &read_socket_watcher_, &read_watcher_)) {
290     DLOG(INFO) << "WatchFileDescriptor failed on read, errno " << errno;
291     return MapPosixError(errno);
292   }
293 
294   read_buf_ = buf;
295   read_buf_len_ = buf_len;
296   read_callback_ = callback;
297   return ERR_IO_PENDING;
298 }
299 
Write(IOBuffer * buf,int buf_len,CompletionCallback * callback)300 int TCPClientSocketLibevent::Write(IOBuffer* buf,
301                                    int buf_len,
302                                    CompletionCallback* callback) {
303   DCHECK_NE(kInvalidSocket, socket_);
304   DCHECK(!waiting_connect_);
305   DCHECK(!write_callback_);
306   // Synchronous operation not supported
307   DCHECK(callback);
308   DCHECK_GT(buf_len, 0);
309 
310   TRACE_EVENT_BEGIN("socket.write", this, "");
311   int nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len));
312   if (nwrite >= 0) {
313     TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", nwrite));
314     return nwrite;
315   }
316   if (errno != EAGAIN && errno != EWOULDBLOCK)
317     return MapPosixError(errno);
318 
319   if (!MessageLoopForIO::current()->WatchFileDescriptor(
320           socket_, true, MessageLoopForIO::WATCH_WRITE,
321           &write_socket_watcher_, &write_watcher_)) {
322     DLOG(INFO) << "WatchFileDescriptor failed on write, errno " << errno;
323     return MapPosixError(errno);
324   }
325 
326 
327   write_buf_ = buf;
328   write_buf_len_ = buf_len;
329   write_callback_ = callback;
330   return ERR_IO_PENDING;
331 }
332 
SetReceiveBufferSize(int32 size)333 bool TCPClientSocketLibevent::SetReceiveBufferSize(int32 size) {
334   int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
335       reinterpret_cast<const char*>(&size),
336       sizeof(size));
337   DCHECK(!rv) << "Could not set socket receive buffer size: " << errno;
338   return rv == 0;
339 }
340 
SetSendBufferSize(int32 size)341 bool TCPClientSocketLibevent::SetSendBufferSize(int32 size) {
342   int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
343       reinterpret_cast<const char*>(&size),
344       sizeof(size));
345   DCHECK(!rv) << "Could not set socket send buffer size: " << errno;
346   return rv == 0;
347 }
348 
349 
CreateSocket(const addrinfo * ai)350 int TCPClientSocketLibevent::CreateSocket(const addrinfo* ai) {
351   socket_ = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
352   if (socket_ == kInvalidSocket)
353     return MapPosixError(errno);
354 
355   if (SetNonBlocking(socket_)) {
356     const int err = MapPosixError(errno);
357     close(socket_);
358     socket_ = kInvalidSocket;
359     return err;
360   }
361 
362   // This mirrors the behaviour on Windows. See the comment in
363   // tcp_client_socket_win.cc after searching for "NODELAY".
364   DisableNagle(socket_);  // If DisableNagle fails, we don't care.
365 
366   return OK;
367 }
368 
DoReadCallback(int rv)369 void TCPClientSocketLibevent::DoReadCallback(int rv) {
370   DCHECK_NE(rv, ERR_IO_PENDING);
371   DCHECK(read_callback_);
372 
373   // since Run may result in Read being called, clear read_callback_ up front.
374   CompletionCallback* c = read_callback_;
375   read_callback_ = NULL;
376   c->Run(rv);
377 }
378 
DoWriteCallback(int rv)379 void TCPClientSocketLibevent::DoWriteCallback(int rv) {
380   DCHECK_NE(rv, ERR_IO_PENDING);
381   DCHECK(write_callback_);
382 
383   // since Run may result in Write being called, clear write_callback_ up front.
384   CompletionCallback* c = write_callback_;
385   write_callback_ = NULL;
386   c->Run(rv);
387 }
388 
DidCompleteConnect()389 void TCPClientSocketLibevent::DidCompleteConnect() {
390   int result = ERR_UNEXPECTED;
391 
392   // Check to see if connect succeeded
393   int os_error = 0;
394   socklen_t len = sizeof(os_error);
395   if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, &os_error, &len) < 0)
396     os_error = errno;
397 
398   if (os_error == EINPROGRESS || os_error == EALREADY) {
399     NOTREACHED();  // This indicates a bug in libevent or our code.
400     result = ERR_IO_PENDING;
401   } else if (current_ai_->ai_next && ShouldTryNextAddress(os_error)) {
402     // This address failed, try next one in list.
403     const addrinfo* next = current_ai_->ai_next;
404     Disconnect();
405     current_ai_ = next;
406     scoped_refptr<LoadLog> load_log;
407     load_log.swap(load_log_);
408     TRACE_EVENT_END("socket.connect", this, "");
409     LoadLog::EndEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
410     result = Connect(write_callback_, load_log);
411   } else {
412     result = MapConnectError(os_error);
413     bool ok = write_socket_watcher_.StopWatchingFileDescriptor();
414     DCHECK(ok);
415     waiting_connect_ = false;
416     TRACE_EVENT_END("socket.connect", this, "");
417     LoadLog::EndEvent(load_log_, LoadLog::TYPE_TCP_CONNECT);
418     load_log_ = NULL;
419   }
420 
421   if (result != ERR_IO_PENDING) {
422     DoWriteCallback(result);
423   }
424 }
425 
DidCompleteRead()426 void TCPClientSocketLibevent::DidCompleteRead() {
427   int bytes_transferred;
428   bytes_transferred = HANDLE_EINTR(read(socket_, read_buf_->data(),
429                                         read_buf_len_));
430 
431   int result;
432   if (bytes_transferred >= 0) {
433     TRACE_EVENT_END("socket.read", this,
434                     StringPrintf("%d bytes", bytes_transferred));
435     result = bytes_transferred;
436   } else {
437     result = MapPosixError(errno);
438   }
439 
440   if (result != ERR_IO_PENDING) {
441     read_buf_ = NULL;
442     read_buf_len_ = 0;
443     bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
444     DCHECK(ok);
445     DoReadCallback(result);
446   }
447 }
448 
DidCompleteWrite()449 void TCPClientSocketLibevent::DidCompleteWrite() {
450   int bytes_transferred;
451   bytes_transferred = HANDLE_EINTR(write(socket_, write_buf_->data(),
452                                          write_buf_len_));
453 
454   int result;
455   if (bytes_transferred >= 0) {
456     result = bytes_transferred;
457     TRACE_EVENT_END("socket.write", this,
458                     StringPrintf("%d bytes", bytes_transferred));
459   } else {
460     result = MapPosixError(errno);
461   }
462 
463   if (result != ERR_IO_PENDING) {
464     write_buf_ = NULL;
465     write_buf_len_ = 0;
466     write_socket_watcher_.StopWatchingFileDescriptor();
467     DoWriteCallback(result);
468   }
469 }
470 
GetPeerName(struct sockaddr * name,socklen_t * namelen)471 int TCPClientSocketLibevent::GetPeerName(struct sockaddr* name,
472                                          socklen_t* namelen) {
473   return ::getpeername(socket_, name, namelen);
474 }
475 
476 }  // namespace net
477