summaryrefslogtreecommitdiffstats
path: root/net/socket/tcp_client_socket_libevent.cc
diff options
context:
space:
mode:
authorwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-22 23:26:44 +0000
committerwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-22 23:26:44 +0000
commitf7984fc67f3c88b6ff1c738700a8229f387d732d (patch)
tree094f6be7633d60b0413370462bf6bd04b906ac00 /net/socket/tcp_client_socket_libevent.cc
parent8c1be4e0311d52f07fe16fc091862957757dc002 (diff)
downloadchromium_src-f7984fc67f3c88b6ff1c738700a8229f387d732d.zip
chromium_src-f7984fc67f3c88b6ff1c738700a8229f387d732d.tar.gz
chromium_src-f7984fc67f3c88b6ff1c738700a8229f387d732d.tar.bz2
Move socket related files from net/base to net/socket.
Review URL: http://codereview.chromium.org/144009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18985 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/socket/tcp_client_socket_libevent.cc')
-rw-r--r--net/socket/tcp_client_socket_libevent.cc373
1 files changed, 373 insertions, 0 deletions
diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc
new file mode 100644
index 0000000..a8e4387
--- /dev/null
+++ b/net/socket/tcp_client_socket_libevent.cc
@@ -0,0 +1,373 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/socket/tcp_client_socket_libevent.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <sys/socket.h>
+
+#include "base/eintr_wrapper.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/trace_event.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "third_party/libevent/event.h"
+
+
+namespace net {
+
+namespace {
+
+const int kInvalidSocket = -1;
+
+// Return 0 on success, -1 on failure.
+// Too small a function to bother putting in a library?
+int SetNonBlocking(int fd) {
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (-1 == flags)
+ return flags;
+ return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+// Convert values from <errno.h> to values from "net/base/net_errors.h"
+int MapPosixError(int err) {
+ // There are numerous posix error codes, but these are the ones we thus far
+ // find interesting.
+ switch (err) {
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ return ERR_IO_PENDING;
+ case ENETDOWN:
+ return ERR_INTERNET_DISCONNECTED;
+ case ETIMEDOUT:
+ return ERR_TIMED_OUT;
+ case ECONNRESET:
+ case ENETRESET: // Related to keep-alive
+ return ERR_CONNECTION_RESET;
+ case ECONNABORTED:
+ return ERR_CONNECTION_ABORTED;
+ case ECONNREFUSED:
+ return ERR_CONNECTION_REFUSED;
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ return ERR_ADDRESS_UNREACHABLE;
+ case EADDRNOTAVAIL:
+ return ERR_ADDRESS_INVALID;
+ case 0:
+ return OK;
+ default:
+ LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
+ return ERR_FAILED;
+ }
+}
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+
+TCPClientSocketLibevent::TCPClientSocketLibevent(const AddressList& addresses)
+ : socket_(kInvalidSocket),
+ addresses_(addresses),
+ current_ai_(addresses_.head()),
+ waiting_connect_(false),
+ read_watcher_(this),
+ write_watcher_(this),
+ read_callback_(NULL),
+ write_callback_(NULL) {
+}
+
+TCPClientSocketLibevent::~TCPClientSocketLibevent() {
+ Disconnect();
+}
+
+int TCPClientSocketLibevent::Connect(CompletionCallback* callback) {
+ // If already connected, then just return OK.
+ if (socket_ != kInvalidSocket)
+ return OK;
+
+ DCHECK(!waiting_connect_);
+
+ TRACE_EVENT_BEGIN("socket.connect", this, "");
+ const addrinfo* ai = current_ai_;
+ DCHECK(ai);
+
+ int rv = CreateSocket(ai);
+ if (rv != OK)
+ return rv;
+
+ if (!HANDLE_EINTR(connect(socket_, ai->ai_addr,
+ static_cast<int>(ai->ai_addrlen)))) {
+ TRACE_EVENT_END("socket.connect", this, "");
+ // Connected without waiting!
+ return OK;
+ }
+
+ // Synchronous operation not supported
+ DCHECK(callback);
+
+ if (errno != EINPROGRESS) {
+ DLOG(INFO) << "connect failed: " << errno;
+ close(socket_);
+ socket_ = kInvalidSocket;
+ return MapPosixError(errno);
+ }
+
+ // Initialize write_socket_watcher_ and link it to our MessagePump.
+ // POLLOUT is set if the connection is established.
+ // POLLIN is set if the connection fails.
+ if (!MessageLoopForIO::current()->WatchFileDescriptor(
+ socket_, true, MessageLoopForIO::WATCH_WRITE, &write_socket_watcher_,
+ &write_watcher_)) {
+ DLOG(INFO) << "WatchFileDescriptor failed: " << errno;
+ close(socket_);
+ socket_ = kInvalidSocket;
+ return MapPosixError(errno);
+ }
+
+ waiting_connect_ = true;
+ write_callback_ = callback;
+ return ERR_IO_PENDING;
+}
+
+void TCPClientSocketLibevent::Disconnect() {
+ if (socket_ == kInvalidSocket)
+ return;
+
+ TRACE_EVENT_INSTANT("socket.disconnect", this, "");
+
+ bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
+ DCHECK(ok);
+ ok = write_socket_watcher_.StopWatchingFileDescriptor();
+ DCHECK(ok);
+ close(socket_);
+ socket_ = kInvalidSocket;
+ waiting_connect_ = false;
+
+ // Reset for next time.
+ current_ai_ = addresses_.head();
+}
+
+bool TCPClientSocketLibevent::IsConnected() const {
+ if (socket_ == kInvalidSocket || waiting_connect_)
+ return false;
+
+ // Check if connection is alive.
+ char c;
+ int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK));
+ if (rv == 0)
+ return false;
+ if (rv == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
+ return false;
+
+ return true;
+}
+
+bool TCPClientSocketLibevent::IsConnectedAndIdle() const {
+ if (socket_ == kInvalidSocket || waiting_connect_)
+ return false;
+
+ // Check if connection is alive and we haven't received any data
+ // unexpectedly.
+ char c;
+ int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK));
+ if (rv >= 0)
+ return false;
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+ return false;
+
+ return true;
+}
+
+int TCPClientSocketLibevent::Read(IOBuffer* buf,
+ int buf_len,
+ CompletionCallback* callback) {
+ DCHECK_NE(kInvalidSocket, socket_);
+ DCHECK(!waiting_connect_);
+ DCHECK(!read_callback_);
+ // Synchronous operation not supported
+ DCHECK(callback);
+ DCHECK_GT(buf_len, 0);
+
+ TRACE_EVENT_BEGIN("socket.read", this, "");
+ int nread = HANDLE_EINTR(read(socket_, buf->data(), buf_len));
+ if (nread >= 0) {
+ TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", nread));
+ return nread;
+ }
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ DLOG(INFO) << "read failed, errno " << errno;
+ return MapPosixError(errno);
+ }
+
+ if (!MessageLoopForIO::current()->WatchFileDescriptor(
+ socket_, true, MessageLoopForIO::WATCH_READ,
+ &read_socket_watcher_, &read_watcher_)) {
+ DLOG(INFO) << "WatchFileDescriptor failed on read, errno " << errno;
+ return MapPosixError(errno);
+ }
+
+ read_buf_ = buf;
+ read_buf_len_ = buf_len;
+ read_callback_ = callback;
+ return ERR_IO_PENDING;
+}
+
+int TCPClientSocketLibevent::Write(IOBuffer* buf,
+ int buf_len,
+ CompletionCallback* callback) {
+ DCHECK_NE(kInvalidSocket, socket_);
+ DCHECK(!waiting_connect_);
+ DCHECK(!write_callback_);
+ // Synchronous operation not supported
+ DCHECK(callback);
+ DCHECK_GT(buf_len, 0);
+
+ TRACE_EVENT_BEGIN("socket.write", this, "");
+ int nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len));
+ if (nwrite >= 0) {
+ TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", nwrite));
+ return nwrite;
+ }
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+ return MapPosixError(errno);
+
+ if (!MessageLoopForIO::current()->WatchFileDescriptor(
+ socket_, true, MessageLoopForIO::WATCH_WRITE,
+ &write_socket_watcher_, &write_watcher_)) {
+ DLOG(INFO) << "WatchFileDescriptor failed on write, errno " << errno;
+ return MapPosixError(errno);
+ }
+
+
+ write_buf_ = buf;
+ write_buf_len_ = buf_len;
+ write_callback_ = callback;
+ return ERR_IO_PENDING;
+}
+
+int TCPClientSocketLibevent::CreateSocket(const addrinfo* ai) {
+ socket_ = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (socket_ == kInvalidSocket)
+ return MapPosixError(errno);
+
+ if (SetNonBlocking(socket_))
+ return MapPosixError(errno);
+
+ return OK;
+}
+
+void TCPClientSocketLibevent::DoReadCallback(int rv) {
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ DCHECK(read_callback_);
+
+ // since Run may result in Read being called, clear read_callback_ up front.
+ CompletionCallback* c = read_callback_;
+ read_callback_ = NULL;
+ c->Run(rv);
+}
+
+void TCPClientSocketLibevent::DoWriteCallback(int rv) {
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ DCHECK(write_callback_);
+
+ // since Run may result in Write being called, clear write_callback_ up front.
+ CompletionCallback* c = write_callback_;
+ write_callback_ = NULL;
+ c->Run(rv);
+}
+
+void TCPClientSocketLibevent::DidCompleteConnect() {
+ int result = ERR_UNEXPECTED;
+
+ TRACE_EVENT_END("socket.connect", this, "");
+
+ // Check to see if connect succeeded
+ int error_code = 0;
+ socklen_t len = sizeof(error_code);
+ if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, &error_code, &len) < 0)
+ error_code = errno;
+
+ if (error_code == EINPROGRESS || error_code == EALREADY) {
+ NOTREACHED(); // This indicates a bug in libevent or our code.
+ result = ERR_IO_PENDING;
+ } else if (current_ai_->ai_next && (
+ error_code == EADDRNOTAVAIL ||
+ error_code == EAFNOSUPPORT ||
+ error_code == ECONNREFUSED ||
+ error_code == ENETUNREACH ||
+ error_code == EHOSTUNREACH ||
+ error_code == ETIMEDOUT)) {
+ // This address failed, try next one in list.
+ const addrinfo* next = current_ai_->ai_next;
+ Disconnect();
+ current_ai_ = next;
+ result = Connect(write_callback_);
+ } else {
+ result = MapPosixError(error_code);
+ bool ok = write_socket_watcher_.StopWatchingFileDescriptor();
+ DCHECK(ok);
+ waiting_connect_ = false;
+ }
+
+ if (result != ERR_IO_PENDING) {
+ DoWriteCallback(result);
+ }
+}
+
+void TCPClientSocketLibevent::DidCompleteRead() {
+ int bytes_transferred;
+ bytes_transferred = HANDLE_EINTR(read(socket_, read_buf_->data(),
+ read_buf_len_));
+
+ int result;
+ if (bytes_transferred >= 0) {
+ TRACE_EVENT_END("socket.read", this,
+ StringPrintf("%d bytes", bytes_transferred));
+ result = bytes_transferred;
+ } else {
+ result = MapPosixError(errno);
+ }
+
+ if (result != ERR_IO_PENDING) {
+ read_buf_ = NULL;
+ read_buf_len_ = 0;
+ bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
+ DCHECK(ok);
+ DoReadCallback(result);
+ }
+}
+
+void TCPClientSocketLibevent::DidCompleteWrite() {
+ int bytes_transferred;
+ bytes_transferred = HANDLE_EINTR(write(socket_, write_buf_->data(),
+ write_buf_len_));
+
+ int result;
+ if (bytes_transferred >= 0) {
+ result = bytes_transferred;
+ TRACE_EVENT_END("socket.write", this,
+ StringPrintf("%d bytes", bytes_transferred));
+ } else {
+ result = MapPosixError(errno);
+ }
+
+ if (result != ERR_IO_PENDING) {
+ write_buf_ = NULL;
+ write_buf_len_ = 0;
+ write_socket_watcher_.StopWatchingFileDescriptor();
+ DoWriteCallback(result);
+ }
+}
+
+int TCPClientSocketLibevent::GetPeerName(struct sockaddr *name,
+ socklen_t *namelen) {
+ return ::getpeername(socket_, name, namelen);
+}
+
+} // namespace net