diff options
author | dkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-09-18 18:46:26 +0000 |
---|---|---|
committer | dkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-09-18 18:46:26 +0000 |
commit | 36987e9fae95a51a980b4f0d71ca2df1a630407e (patch) | |
tree | 8d77b940020eb84b1926b41c8e5cec964299038c /net/base/tcp_client_socket_libevent.cc | |
parent | 5a22409d7b625fa1f2a03194ff6c011f82f150dc (diff) | |
download | chromium_src-36987e9fae95a51a980b4f0d71ca2df1a630407e.zip chromium_src-36987e9fae95a51a980b4f0d71ca2df1a630407e.tar.gz chromium_src-36987e9fae95a51a980b4f0d71ca2df1a630407e.tar.bz2 |
Use libevent, second try. Changes this time:
- remove bogus include of base/completion_callback.h
- add DEPS rules to allow including third_party/libevent
Review URL: http://codereview.chromium.org/2964
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@2371 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base/tcp_client_socket_libevent.cc')
-rw-r--r-- | net/base/tcp_client_socket_libevent.cc | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/net/base/tcp_client_socket_libevent.cc b/net/base/tcp_client_socket_libevent.cc new file mode 100644 index 0000000..7f815c3 --- /dev/null +++ b/net/base/tcp_client_socket_libevent.cc @@ -0,0 +1,288 @@ +// 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/base/tcp_client_socket.h" + +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <sys/socket.h> + +#include "base/message_loop.h" +#include "net/base/net_errors.h" +#include "third_party/libevent/event.h" + + +namespace net { + +const int kInvalidSocket = -1; + +// Return 0 on success +// Too small a function to bother putting in a library? +static int SetNonBlocking(int fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + if (-1 == flags) + flags = 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +// Convert values from <errno.h> to values from "net/base/net_errors.h" +static int MapPosixError(int err) { + // There are numerous posix error codes, but these are the ones we thus far + // find interesting. + // TODO(port): fill this with a real conversion table + switch (err) { + case EWOULDBLOCK: return ERR_IO_PENDING; + default: + return ERR_FAILED; + } +} + +//----------------------------------------------------------------------------- + +TCPClientSocket::TCPClientSocket(const AddressList& addresses) + : socket_(kInvalidSocket), + addresses_(addresses), + current_ai_(addresses_.head()), + wait_state_(NOT_WAITING), + event_(new event) { +} + +TCPClientSocket::~TCPClientSocket() { + Disconnect(); +} + +int TCPClientSocket::Connect(CompletionCallback* callback) { + + // If already connected, then just return OK. + if (socket_ != kInvalidSocket) + return OK; + + DCHECK(wait_state_ == NOT_WAITING); + + const addrinfo* ai = current_ai_; + DCHECK(ai); + + int rv = CreateSocket(ai); + if (rv != OK) + return rv; + + if (!connect(socket_, ai->ai_addr, static_cast<int>(ai->ai_addrlen))) { + // Connected without waiting! + return OK; + } + + // Synchronous operation not supported + DCHECK(callback); + + if (errno != EINPROGRESS && errno != EWOULDBLOCK) { + LOG(ERROR) << "connect failed: " << errno; + return MapPosixError(errno); + } + + // Initialize event_ and link it to our MessagePump. + // POLLOUT is set if the connection is established. + // POLLIN is set if the connection fails, + // so select for both read and write. + MessageLoopForIO::current()->WatchSocket( + socket_, EV_READ|EV_WRITE|EV_PERSIST, event_.get(), this); + + wait_state_ = WAITING_CONNECT; + callback_ = callback; + return ERR_IO_PENDING; +} + +int TCPClientSocket::ReconnectIgnoringLastError(CompletionCallback* callback) { + // No ignorable errors! + return ERR_FAILED; +} + +void TCPClientSocket::Disconnect() { + if (socket_ == kInvalidSocket) + return; + + MessageLoopForIO::current()->UnwatchSocket(event_.get()); + close(socket_); + socket_ = kInvalidSocket; + + // Reset for next time. + current_ai_ = addresses_.head(); +} + +bool TCPClientSocket::IsConnected() const { + if (socket_ == kInvalidSocket || wait_state_ == WAITING_CONNECT) + return false; + + // Check if connection is alive. + char c; + int rv = recv(socket_, &c, 1, MSG_PEEK); + if (rv == 0) + return false; + + return true; +} + +int TCPClientSocket::Read(char* buf, + int buf_len, + CompletionCallback* callback) { + DCHECK(socket_ != kInvalidSocket); + DCHECK(wait_state_ == NOT_WAITING); + DCHECK(!callback_); + // Synchronous operation not supported + DCHECK(callback); + DCHECK(buf_len > 0); + + int nread = read(socket_, buf, buf_len); + if (nread > 0) { + return nread; + } + if (nread == -1 && errno != EWOULDBLOCK) + return MapPosixError(errno); + + MessageLoopForIO::current()->WatchSocket( + socket_, EV_READ|EV_PERSIST, event_.get(), this); + + buf_ = buf; + buf_len_ = buf_len; + wait_state_ = WAITING_READ; + callback_ = callback; + return ERR_IO_PENDING; +} + +int TCPClientSocket::Write(const char* buf, + int buf_len, + CompletionCallback* callback) { + DCHECK(socket_ != kInvalidSocket); + DCHECK(wait_state_ == NOT_WAITING); + DCHECK(!callback_); + // Synchronous operation not supported + DCHECK(callback); + DCHECK(buf_len > 0); + + int nwrite = write(socket_, buf, buf_len); + if (nwrite > 0) { + return nwrite; + } + if (nwrite == -1 && errno != EWOULDBLOCK) + return MapPosixError(errno); + + MessageLoopForIO::current()->WatchSocket( + socket_, EV_WRITE|EV_PERSIST, event_.get(), this); + + buf_ = const_cast<char*>(buf); + buf_len_ = buf_len; + wait_state_ = WAITING_WRITE; + callback_ = callback; + return ERR_IO_PENDING; +} + +int TCPClientSocket::CreateSocket(const addrinfo* ai) { + socket_ = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (socket_ == kInvalidSocket) + return MapPosixError(errno); + + // All our socket I/O is nonblocking + if (SetNonBlocking(socket_)) + return MapPosixError(errno); + + return OK; +} + +void TCPClientSocket::DoCallback(int rv) { + DCHECK(rv != ERR_IO_PENDING); + DCHECK(callback_); + + // since Run may result in Read being called, clear callback_ up front. + CompletionCallback* c = callback_; + callback_ = NULL; + c->Run(rv); +} + +void TCPClientSocket::DidCompleteConnect() { + int result = ERR_UNEXPECTED; + + wait_state_ = NOT_WAITING; + + // Check to see if connect succeeded + int error_code = -1; + socklen_t len = sizeof(error_code); + if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, + reinterpret_cast<char*>(&error_code), &len) < 0) { + result = MapPosixError(errno); + } else if (error_code == EINPROGRESS) { + result = ERR_IO_PENDING; + // And await next callback. Haven't seen this case yet myself. + } 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(callback_); + } else if (error_code) { + result = MapPosixError(error_code); + } else { + result = 0; + MessageLoopForIO::current()->UnwatchSocket(event_.get()); + } + + if (result != ERR_IO_PENDING) + DoCallback(result); +} + +void TCPClientSocket::DidCompleteIO() { + int bytes_transferred; + switch (wait_state_) { + case WAITING_READ: + bytes_transferred = read(socket_, buf_, buf_len_); + break; + case WAITING_WRITE: + bytes_transferred = write(socket_, buf_, buf_len_); + break; + default: + NOTREACHED(); + } + + int result; + if (bytes_transferred > 0) { + result = bytes_transferred; + } else if (bytes_transferred == 0) { + // TODO(port): can we tell why it closed, and return a more informative + // message? And why does the unit test want to see zero? + //result = ERR_CONNECTION_CLOSED; + result = 0; + } else { + result = MapPosixError(errno); + } + + if (result != ERR_IO_PENDING) { + wait_state_ = NOT_WAITING; + MessageLoopForIO::current()->UnwatchSocket(event_.get()); + DoCallback(result); + } +} + +void TCPClientSocket::OnSocketReady(short flags) { + switch (wait_state_) { + case WAITING_CONNECT: + DidCompleteConnect(); + break; + case WAITING_READ: + case WAITING_WRITE: + DidCompleteIO(); + break; + default: + NOTREACHED(); + break; + } +} + +} // namespace net + |