diff options
author | mbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-02 22:56:18 +0000 |
---|---|---|
committer | mbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-02 22:56:18 +0000 |
commit | a2798d9808595a3b9d3a3b5fe6a3d0109a13aabd (patch) | |
tree | 50b849bc622aa4825f4eff165a655778678ccf84 /net/udp | |
parent | 4b9baf9d5a55852ab9613a04d745e891a6d083f0 (diff) | |
download | chromium_src-a2798d9808595a3b9d3a3b5fe6a3d0109a13aabd.zip chromium_src-a2798d9808595a3b9d3a3b5fe6a3d0109a13aabd.tar.gz chromium_src-a2798d9808595a3b9d3a3b5fe6a3d0109a13aabd.tar.bz2 |
Add an initial API and implementation for UDP Sockets.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6597039
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76644 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/udp')
-rw-r--r-- | net/udp/datagram_client_socket.h | 27 | ||||
-rw-r--r-- | net/udp/datagram_server_socket.h | 65 | ||||
-rw-r--r-- | net/udp/datagram_socket.h | 32 | ||||
-rw-r--r-- | net/udp/udp_client_socket.cc | 49 | ||||
-rw-r--r-- | net/udp/udp_client_socket.h | 41 | ||||
-rw-r--r-- | net/udp/udp_server_socket.cc | 50 | ||||
-rw-r--r-- | net/udp/udp_server_socket.h | 48 | ||||
-rw-r--r-- | net/udp/udp_socket.h | 28 | ||||
-rw-r--r-- | net/udp/udp_socket_libevent.cc | 366 | ||||
-rw-r--r-- | net/udp/udp_socket_libevent.h | 212 | ||||
-rw-r--r-- | net/udp/udp_socket_unittest.cc | 305 |
11 files changed, 1223 insertions, 0 deletions
diff --git a/net/udp/datagram_client_socket.h b/net/udp/datagram_client_socket.h new file mode 100644 index 0000000..e961952 --- /dev/null +++ b/net/udp/datagram_client_socket.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011 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. + +#ifndef NET_UDP_DATAGRAM_CLIENT_SOCKET_H_ +#define NET_UDP_DATAGRAM_CLIENT_SOCKET_H_ +#pragma once + +#include "net/socket/socket.h" +#include "net/udp/datagram_socket.h" + +namespace net { + +class AddressList; + +class DatagramClientSocket : public DatagramSocket, public Socket { + public: + virtual ~DatagramClientSocket() {} + + // Initialize this socket as a client socket to server at |address|. + // Returns a network error code. + virtual int Connect(const AddressList& address) = 0; +}; + +} // namespace net + +#endif // NET_UDP_DATAGRAM_CLIENT_SOCKET_H_ diff --git a/net/udp/datagram_server_socket.h b/net/udp/datagram_server_socket.h new file mode 100644 index 0000000..d4b0244 --- /dev/null +++ b/net/udp/datagram_server_socket.h @@ -0,0 +1,65 @@ +// Copyright (c) 2011 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. + +#ifndef NET_UDP_DATAGRAM_SERVER_SOCKET_H_ +#define NET_UDP_DATAGRAM_SERVER_SOCKET_H_ +#pragma once + +#include <sys/socket.h> + +#include "net/base/completion_callback.h" +#include "net/udp/datagram_socket.h" + +namespace net { + +class AddressList; +class IOBuffer; + +// A UDP Socket. +class DatagramServerSocket : public DatagramSocket { + public: + virtual ~DatagramServerSocket() {} + + // Initialize this socket as a server socket listening at |address|. + // Returns a network error code. + virtual int Listen(const AddressList& address) = 0; + + // Read from a socket and receive sender address information. + // |buf| is the buffer to read data into. + // |buf_len| is the maximum amount of data to read. + // |address| is a buffer provided by the caller for receiving the sender + // address information about the received data. This buffer must be kept + // alive by the caller until the callback is placed. + // |address_length| is a ptr to the length of the |address| buffer. This + // is an input parameter containing the maximum size |address| can hold + // and also an output parameter for the size of |address| upon completion. + // |callback| the callback on completion of the Recv. + // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. + // If ERR_IO_PENDING is returned, the caller must keep |buf|, |address|, + // and |address_length| alive until the callback is called. + virtual int RecvFrom(IOBuffer* buf, + int buf_len, + struct sockaddr* address, + socklen_t* address_length, + CompletionCallback* callback) = 0; + + // Send to a socket with a particular destination. + // |buf| is the buffer to send + // |buf_len| is the number of bytes to send + // |address| is the recipient address. + // |address_length| is the size of the recipient address + // |callback| is the user callback function to call on complete. + // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. + // If ERR_IO_PENDING is returned, the caller must keep |buf| and |address| + // alive until the callback is called. + virtual int SendTo(IOBuffer* buf, + int buf_len, + const struct sockaddr* address, + socklen_t address_length, + CompletionCallback* callback) = 0; +}; + +} // namespace net + +#endif // NET_UDP_DATAGRAM_SERVER_SOCKET_H_ diff --git a/net/udp/datagram_socket.h b/net/udp/datagram_socket.h new file mode 100644 index 0000000..0398f39 --- /dev/null +++ b/net/udp/datagram_socket.h @@ -0,0 +1,32 @@ +// Copyright (c) 2011 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. + +#ifndef NET_UDP_DATAGRAM_SOCKET_H_ +#define NET_UDP_DATAGRAM_SOCKET_H_ +#pragma once + +namespace net { + +class AddressList; + +// A datagram socket is an interface to a protocol which exchanges +// datagrams, like UDP. +class DatagramSocket { + public: + virtual ~DatagramSocket() {} + + // Close the socket. + virtual void Close() = 0; + + // Copy the remote udp address into |address| and return a network error code. + virtual int GetPeerAddress(AddressList* address) const = 0; + + // Copy the local udp address into |address| and return a network error code. + // (similar to getsockname) + virtual int GetLocalAddress(AddressList* address) const = 0; +}; + +} // namespace net + +#endif // NET_UDP_DATAGRAM_SOCKET_H_ diff --git a/net/udp/udp_client_socket.cc b/net/udp/udp_client_socket.cc new file mode 100644 index 0000000..5cc498d --- /dev/null +++ b/net/udp/udp_client_socket.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2011 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/udp/udp_client_socket.h" + +#include "net/base/net_log.h" + +namespace net { + +UDPClientSocket::UDPClientSocket( + net::NetLog* net_log, + const net::NetLog::Source& source) + : socket_(net_log, source) { +} + +UDPClientSocket::~UDPClientSocket() { +} + +int UDPClientSocket::Connect(const AddressList& address) { + return socket_.Connect(address); +} + +int UDPClientSocket::Read(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) { + return socket_.Read(buf, buf_len, callback); +} + +int UDPClientSocket::Write(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) { + return socket_.Write(buf, buf_len, callback); +} + +void UDPClientSocket::Close() { + socket_.Close(); +} + +int UDPClientSocket::GetPeerAddress(AddressList* address) const { + return socket_.GetPeerAddress(address); +} + +int UDPClientSocket::GetLocalAddress(AddressList* address) const { + return socket_.GetLocalAddress(address); +} + + +} // namespace net diff --git a/net/udp/udp_client_socket.h b/net/udp/udp_client_socket.h new file mode 100644 index 0000000..8ba07e5 --- /dev/null +++ b/net/udp/udp_client_socket.h @@ -0,0 +1,41 @@ +// Copyright (c) 2011 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. + +#ifndef NET_SOCKET_UDP_CLIENT_SOCKET_H_ +#define NET_SOCKET_UDP_CLIENT_SOCKET_H_ +#pragma once + +#include "net/base/net_log.h" +#include "net/udp/datagram_client_socket.h" +#include "net/udp/udp_socket.h" + +namespace net { + +class BoundNetLog; + +// A client socket that uses UDP as the transport layer. +class UDPClientSocket : public DatagramClientSocket { + public: + UDPClientSocket(net::NetLog* net_log, + const net::NetLog::Source& source); + virtual ~UDPClientSocket(); + + // Implement DatagramClientSocket: + virtual int Connect(const AddressList& address); + virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); + virtual int Write(IOBuffer* buf, int buf_len, CompletionCallback* callback); + virtual void Close(); + virtual int GetPeerAddress(AddressList* address) const; + virtual int GetLocalAddress(AddressList* address) const; + virtual bool SetReceiveBufferSize(int32 size) { return true; } + virtual bool SetSendBufferSize(int32 size) { return true; } + + private: + UDPSocket socket_; + DISALLOW_COPY_AND_ASSIGN(UDPClientSocket); +}; + +} // namespace net + +#endif // NET_SOCKET_UDP_CLIENT_SOCKET_H_ diff --git a/net/udp/udp_server_socket.cc b/net/udp/udp_server_socket.cc new file mode 100644 index 0000000..4b1c175 --- /dev/null +++ b/net/udp/udp_server_socket.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2011 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/udp/udp_server_socket.h" + +namespace net { + +UDPServerSocket::UDPServerSocket(net::NetLog* net_log, + const net::NetLog::Source& source) + : socket_(net_log, source) { +} + +UDPServerSocket::~UDPServerSocket() { +} + +int UDPServerSocket::Listen(const AddressList& address) { + return socket_.Bind(address); +} + +int UDPServerSocket::RecvFrom(IOBuffer* buf, + int buf_len, + struct sockaddr* address, + socklen_t* address_length, + CompletionCallback* callback) { + return socket_.RecvFrom(buf, buf_len, address, address_length, callback); +} + +int UDPServerSocket::SendTo(IOBuffer* buf, + int buf_len, + const struct sockaddr* address, + socklen_t address_length, + CompletionCallback* callback) { + return socket_.SendTo(buf, buf_len, address, address_length, callback); +} + +void UDPServerSocket::Close() { + socket_.Close(); +} + +int UDPServerSocket::GetPeerAddress(AddressList* address) const { + return socket_.GetPeerAddress(address); +} + +int UDPServerSocket::GetLocalAddress(AddressList* address) const { + return socket_.GetLocalAddress(address); +} + + +} // namespace net diff --git a/net/udp/udp_server_socket.h b/net/udp/udp_server_socket.h new file mode 100644 index 0000000..6c152a7 --- /dev/null +++ b/net/udp/udp_server_socket.h @@ -0,0 +1,48 @@ +// Copyright (c) 2011 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. + +#ifndef NET_SOCKET_UDP_SERVER_SOCKET_H_ +#define NET_SOCKET_UDP_SERVER_SOCKET_H_ +#pragma once + +#include "net/base/completion_callback.h" +#include "net/udp/datagram_server_socket.h" +#include "net/udp/udp_socket.h" + +namespace net { + +class AddressList; +class BoundNetLog; + +// A client socket that uses UDP as the transport layer. +class UDPServerSocket : public DatagramServerSocket { + public: + UDPServerSocket(net::NetLog* net_log, + const net::NetLog::Source& source); + virtual ~UDPServerSocket(); + + // Implement DatagramServerSocket: + virtual int Listen(const AddressList& address); + virtual int RecvFrom(IOBuffer* buf, + int buf_len, + struct sockaddr* address, + socklen_t* address_length, + CompletionCallback* callback); + virtual int SendTo(IOBuffer* buf, + int buf_len, + const struct sockaddr* address, + socklen_t address_length, + CompletionCallback* callback); + virtual void Close(); + virtual int GetPeerAddress(AddressList* address) const; + virtual int GetLocalAddress(AddressList* address) const; + + private: + UDPSocket socket_; + DISALLOW_COPY_AND_ASSIGN(UDPServerSocket); +}; + +} // namespace net + +#endif // NET_SOCKET_UDP_SERVER_SOCKET_H_ diff --git a/net/udp/udp_socket.h b/net/udp/udp_socket.h new file mode 100644 index 0000000..9e5f826 --- /dev/null +++ b/net/udp/udp_socket.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011 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. + +#ifndef NET_UDP_UDP_SOCKET_H_ +#define NET_UDP_UDP_SOCKET_H_ +#pragma once + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "net/udp/udp_socket_win.h" +#elif defined(OS_POSIX) +#include "net/udp/udp_socket_libevent.h" +#endif + +namespace net { + +// A client socket that uses UDP as the transport layer. +#if defined(OS_WIN) +typedef UDPSocketWin UDPSocket; +#elif defined(OS_POSIX) +typedef UDPSocketLibevent UDPSocket; +#endif + +} // namespace net + +#endif // NET_UDP_UDP_SOCKET_H_ diff --git a/net/udp/udp_socket_libevent.cc b/net/udp/udp_socket_libevent.cc new file mode 100644 index 0000000..8c0ad4f --- /dev/null +++ b/net/udp/udp_socket_libevent.cc @@ -0,0 +1,366 @@ +// Copyright (c) 2011 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/udp/udp_socket_libevent.h" + +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <sys/socket.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/metrics/stats_counters.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/base/net_log.h" +#include "net/base/net_util.h" +#if defined(OS_POSIX) +#include <netinet/in.h> +#endif +#if defined(USE_SYSTEM_LIBEVENT) +#include <event.h> +#else +#include "third_party/libevent/event.h" +#endif + +namespace net { + +namespace { + +// Convert values from <errno.h> to values from "net/base/net_errors.h" +int MapPosixError(int os_error) { + // There are numerous posix error codes, but these are the ones we thus far + // find interesting. + switch (os_error) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + return ERR_IO_PENDING; + case EACCES: + return ERR_ACCESS_DENIED; + case ENETDOWN: + return ERR_INTERNET_DISCONNECTED; + case ETIMEDOUT: + return ERR_TIMED_OUT; + case ECONNRESET: + case ENETRESET: // Related to keep-alive + case EPIPE: + return ERR_CONNECTION_RESET; + case ECONNABORTED: + return ERR_CONNECTION_ABORTED; + case ECONNREFUSED: + return ERR_CONNECTION_REFUSED; + case EHOSTUNREACH: + case EHOSTDOWN: + case ENETUNREACH: + return ERR_ADDRESS_UNREACHABLE; + case EADDRNOTAVAIL: + return ERR_ADDRESS_INVALID; + case EMSGSIZE: + return ERR_MSG_TOO_BIG; + case 0: + return OK; + default: + LOG(WARNING) << "Unknown error " << os_error + << " mapped to net::ERR_FAILED"; + return ERR_FAILED; + } +} + +} // namespace + +//----------------------------------------------------------------------------- + +UDPSocketLibevent::UDPSocketLibevent(net::NetLog* net_log, + const net::NetLog::Source& source) + : socket_(kInvalidSocket), + read_watcher_(this), + write_watcher_(this), + read_buf_len_(0), + recv_from_address_(NULL), + recv_from_address_length_(NULL), + write_buf_len_(0), + send_to_address_(NULL), + send_to_address_length_(0), + read_callback_(NULL), + write_callback_(NULL), + net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)) { + scoped_refptr<NetLog::EventParameters> params; + if (source.is_valid()) + params = new NetLogSourceParameter("source_dependency", source); + net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, params); +} + +UDPSocketLibevent::~UDPSocketLibevent() { + Close(); + net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE, NULL); +} + +void UDPSocketLibevent::Close() { + if (!is_connected()) + return; + + if (HANDLE_EINTR(close(socket_)) < 0) + PLOG(ERROR) << "close"; + + socket_ = kInvalidSocket; +} + +int UDPSocketLibevent::GetPeerAddress(AddressList* address) const { + DCHECK(CalledOnValidThread()); + DCHECK(address); + if (!is_connected()) + return ERR_SOCKET_NOT_CONNECTED; + + if (!remote_address_.get()) { + struct sockaddr_storage addr_storage; + socklen_t addr_len = sizeof(addr_storage); + struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + if (getpeername(socket_, addr, &addr_len)) + return MapPosixError(errno); + remote_address_.reset(AddressList::CreateAddressListFromSockaddr( + addr, + addr_len, + SOCK_DGRAM, + IPPROTO_UDP)); + } + + address->Copy(remote_address_->head(), false); + return OK; +} + +int UDPSocketLibevent::GetLocalAddress(AddressList* address) const { + DCHECK(CalledOnValidThread()); + DCHECK(address); + if (!is_connected()) + return ERR_SOCKET_NOT_CONNECTED; + + if (!local_address_.get()) { + struct sockaddr_storage addr_storage; + socklen_t addr_len = sizeof(addr_storage); + struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + if (getsockname(socket_, addr, &addr_len)) + return MapPosixError(errno); + local_address_.reset(AddressList::CreateAddressListFromSockaddr( + addr, + addr_len, + SOCK_DGRAM, + IPPROTO_UDP)); + } + + address->Copy(local_address_->head(), false); + return OK; +} + +int UDPSocketLibevent::Read(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) { + DCHECK(CalledOnValidThread()); + DCHECK_NE(kInvalidSocket, socket_); + DCHECK(!read_callback_); + DCHECK(callback); // Synchronous operation not supported + DCHECK_GT(buf_len, 0); + + read_buf_ = buf; + read_buf_len_ = buf_len; + + int nread = InternalRead(); + if (nread != ERR_IO_PENDING) + return nread; + + if (!MessageLoopForIO::current()->WatchFileDescriptor( + socket_, true, MessageLoopForIO::WATCH_READ, + &read_socket_watcher_, &read_watcher_)) { + PLOG(ERROR) << "WatchFileDescriptor failed on read"; + return MapPosixError(errno); + } + + read_callback_ = callback; + return ERR_IO_PENDING; +} + +int UDPSocketLibevent::RecvFrom(IOBuffer* buf, + int buf_len, + struct sockaddr* address, + socklen_t* address_length, + CompletionCallback* callback) { + DCHECK(!recv_from_address_); + DCHECK(!recv_from_address_length_); + recv_from_address_ = address; + recv_from_address_length_ = address_length; + return Read(buf, buf_len, callback); +} + +int UDPSocketLibevent::SendTo(IOBuffer* buf, + int buf_len, + const struct sockaddr* address, + socklen_t address_length, + CompletionCallback* callback) { + DCHECK(!send_to_address_); + DCHECK(!send_to_address_length_); + send_to_address_ = address; + send_to_address_length_ = address_length; + return Write(buf, buf_len, callback); +} + +int UDPSocketLibevent::Write(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) { + DCHECK(CalledOnValidThread()); + DCHECK_NE(kInvalidSocket, socket_); + DCHECK(!write_callback_); + DCHECK(callback); // Synchronous operation not supported + DCHECK_GT(buf_len, 0); + + int nwrite = InternalWrite(buf, buf_len); + if (nwrite >= 0) { + static base::StatsCounter write_bytes("udp.write_bytes"); + write_bytes.Add(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_)) { + DVLOG(1) << "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 UDPSocketLibevent::Connect(const AddressList& address) { + DCHECK(!is_connected()); + DCHECK(!remote_address_.get()); + const struct addrinfo* ai = address.head(); + int rv = CreateSocket(ai); + if (rv < 0) + return MapPosixError(rv); + + rv = HANDLE_EINTR(connect(socket_, ai->ai_addr, ai->ai_addrlen)); + if (rv < 0) + return MapPosixError(rv); + + remote_address_.reset(new AddressList(address)); + return rv; +} + +int UDPSocketLibevent::Bind(const AddressList& address) { + DCHECK(!is_connected()); + DCHECK(!local_address_.get()); + const struct addrinfo* ai = address.head(); + int rv = CreateSocket(ai); + if (rv < 0) + return MapPosixError(rv); + + rv = bind(socket_, ai->ai_addr, ai->ai_addrlen); + if (rv < 0) + return MapPosixError(rv); + + local_address_.reset(new AddressList(address)); + return rv; +} + +void UDPSocketLibevent::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; + recv_from_address_ = NULL; + recv_from_address_length_ = NULL; + c->Run(rv); +} + +void UDPSocketLibevent::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; + send_to_address_ = NULL; + send_to_address_length_ = 0; + c->Run(rv); +} + +void UDPSocketLibevent::DidCompleteRead() { + int result = InternalRead(); + if (result != ERR_IO_PENDING) { + read_buf_ = NULL; + read_buf_len_ = 0; + bool ok = read_socket_watcher_.StopWatchingFileDescriptor(); + DCHECK(ok); + DoReadCallback(result); + } +} + +int UDPSocketLibevent::CreateSocket(const addrinfo* ai) { + socket_ = socket(ai->ai_family, SOCK_DGRAM, 0); + if (socket_ == kInvalidSocket) + return errno; + if (SetNonBlocking(socket_)) { + const int err = errno; + Close(); + return err; + } + return OK; +} + +void UDPSocketLibevent::DidCompleteWrite() { + int result = InternalWrite(write_buf_, write_buf_len_); + if (result >= 0) { + static base::StatsCounter write_bytes("udp.write_bytes"); + write_bytes.Add(result); + } else { + result = MapPosixError(errno); + } + + if (result != ERR_IO_PENDING) { + write_buf_ = NULL; + write_buf_len_ = 0; + write_socket_watcher_.StopWatchingFileDescriptor(); + DoWriteCallback(result); + } +} + +int UDPSocketLibevent::InternalRead() { + int bytes_transferred; + int flags = 0; + bytes_transferred = + HANDLE_EINTR(recvfrom(socket_, + read_buf_->data(), + read_buf_len_, + flags, + recv_from_address_, + recv_from_address_length_)); + int result; + if (bytes_transferred >= 0) { + result = bytes_transferred; + static base::StatsCounter read_bytes("udp.read_bytes"); + read_bytes.Add(bytes_transferred); + } else { + result = MapPosixError(errno); + } + return result; +} +int UDPSocketLibevent::InternalWrite(IOBuffer* buf, int buf_len) { + return HANDLE_EINTR(sendto(socket_, + buf->data(), + buf_len, + 0, + send_to_address_, + send_to_address_length_)); +} + +} // namespace net diff --git a/net/udp/udp_socket_libevent.h b/net/udp/udp_socket_libevent.h new file mode 100644 index 0000000..bea4887 --- /dev/null +++ b/net/udp/udp_socket_libevent.h @@ -0,0 +1,212 @@ +// Copyright (c) 2011 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. + +#ifndef NET_UDP_UDP_SOCKET_LIBEVENT_H_ +#define NET_UDP_UDP_SOCKET_LIBEVENT_H_ +#pragma once + +// UDPSocketLibevent +// Accessor API for a UDP socket in either client or server form. +// +// Client form: +// In this case, we're connecting to a specific server, so the client will +// usually use: +// Connect(address) // Connect to a UDP server +// Read/Write // Reads/Writes all go to a single destination +// +// Server form: +// In this case, we want to read/write to many clients which are connecting +// to this server. First the server 'binds' to an addres, then we read from +// clients and write responses to them. +// Example: +// Bind(address/port) // Binds to port for reading from clients +// RecvFrom/SendTo // Each read can come from a different client +// // Writes need to be directed to a specific +// // address. + +#include "base/message_loop.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/threading/non_thread_safe.h" +#include "net/base/address_list.h" +#include "net/base/completion_callback.h" +#include "net/base/net_log.h" +#include "net/socket/client_socket.h" +#include "net/udp/datagram_socket.h" + +namespace net { + +class BoundNetLog; + +class UDPSocketLibevent : public base::NonThreadSafe { + public: + UDPSocketLibevent(net::NetLog* net_log, + const net::NetLog::Source& source); + virtual ~UDPSocketLibevent(); + + // Connect the socket to connect with a certain |address|. + // Returns a net error code. + int Connect(const AddressList& address); + + // Bind the address/port for this socket to |address|. This is generally + // only used on a server. + // Returns a net error code. + int Bind(const AddressList& address); + + // Close the socket. + void Close(); + + // Copy the remote udp address into |address| and return a network error code. + int GetPeerAddress(AddressList* address) const; + + // Copy the local udp address into |address| and return a network error code. + // (similar to getsockname) + int GetLocalAddress(AddressList* address) const; + + // IO: + // Multiple outstanding read requests are not supported. + // Full duplex mode (reading and writing at the same time) is supported + + // Read from the socket. + // Only usable from the client-side of a UDP socket, after the socket + // has been connected. + int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); + + // Write to the socket. + // Only usable from the client-side of a UDP socket, after the socket + // has been connected. + int Write(IOBuffer* buf, int buf_len, CompletionCallback* callback); + + // Read from a socket and receive sender address information. + // |buf| is the buffer to read data into. + // |buf_len| is the maximum amount of data to read. + // |address| is a buffer provided by the caller for receiving the sender + // address information about the received data. This buffer must be kept + // alive by the caller until the callback is placed. + // |address_length| is a ptr to the length of the |address| buffer. This + // is an input parameter containing the maximum size |address| can hold + // and also an output parameter for the size of |address| upon completion. + // |callback| the callback on completion of the Recv. + // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. + // If ERR_IO_PENDING is returned, the caller must keep |buf|, |address|, + // and |address_length| alive until the callback is called. + int RecvFrom(IOBuffer* buf, + int buf_len, + struct sockaddr* address, + socklen_t* address_length, + CompletionCallback* callback); + + // Send to a socket with a particular destination. + // |buf| is the buffer to send + // |buf_len| is the number of bytes to send + // |address| is the recipient address. + // |address_length| is the size of the recipient address + // |callback| is the user callback function to call on complete. + // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. + // If ERR_IO_PENDING is returned, the caller must keep |buf| and |address| + // alive until the callback is called. + int SendTo(IOBuffer* buf, + int buf_len, + const struct sockaddr* address, + socklen_t address_length, + CompletionCallback* callback); + + // Returns true if the socket is already connected or bound. + bool is_connected() const { return socket_ != kInvalidSocket; } + + AddressList* local_address() { return local_address_.get(); } + + private: + static const int kInvalidSocket = -1; + + class ReadWatcher : public MessageLoopForIO::Watcher { + public: + explicit ReadWatcher(UDPSocketLibevent* socket) : socket_(socket) {} + + // MessageLoopForIO::Watcher methods + + virtual void OnFileCanReadWithoutBlocking(int /* fd */) { + if (socket_->read_callback_) + socket_->DidCompleteRead(); + } + + virtual void OnFileCanWriteWithoutBlocking(int /* fd */) {} + + private: + UDPSocketLibevent* const socket_; + + DISALLOW_COPY_AND_ASSIGN(ReadWatcher); + }; + + class WriteWatcher : public MessageLoopForIO::Watcher { + public: + explicit WriteWatcher(UDPSocketLibevent* socket) : socket_(socket) {} + + // MessageLoopForIO::Watcher methods + + virtual void OnFileCanReadWithoutBlocking(int /* fd */) {} + + virtual void OnFileCanWriteWithoutBlocking(int /* fd */) { + if (socket_->write_callback_) + socket_->DidCompleteWrite(); + } + + private: + UDPSocketLibevent* const socket_; + + DISALLOW_COPY_AND_ASSIGN(WriteWatcher); + }; + + void DoReadCallback(int rv); + void DoWriteCallback(int rv); + void DidCompleteRead(); + void DidCompleteWrite(); + + // Returns the OS error code (or 0 on success). + int CreateSocket(const struct addrinfo* ai); + + int InternalRead(); + int InternalWrite(IOBuffer* buf, int buf_len); + + int socket_; + + // These are mutable since they're just cached copies to make + // GetPeerAddress/GetLocalAddress smarter. + mutable scoped_ptr<AddressList> local_address_; + mutable scoped_ptr<AddressList> remote_address_; + + // The socket's libevent wrappers + MessageLoopForIO::FileDescriptorWatcher read_socket_watcher_; + MessageLoopForIO::FileDescriptorWatcher write_socket_watcher_; + + // The corresponding watchers for reads and writes. + ReadWatcher read_watcher_; + WriteWatcher write_watcher_; + + // The buffer used by OnSocketReady to retry Read requests + scoped_refptr<IOBuffer> read_buf_; + int read_buf_len_; + struct sockaddr* recv_from_address_; + socklen_t* recv_from_address_length_; + + // The buffer used by OnSocketReady to retry Write requests + scoped_refptr<IOBuffer> write_buf_; + int write_buf_len_; + const struct sockaddr* send_to_address_; + socklen_t send_to_address_length_; + + // External callback; called when read is complete. + CompletionCallback* read_callback_; + + // External callback; called when write is complete. + CompletionCallback* write_callback_; + + BoundNetLog net_log_; + + DISALLOW_COPY_AND_ASSIGN(UDPSocketLibevent); +}; + +} // namespace net + +#endif // NET_UDP_UDP_SOCKET_LIBEVENT_H_ diff --git a/net/udp/udp_socket_unittest.cc b/net/udp/udp_socket_unittest.cc new file mode 100644 index 0000000..aaf3dfa --- /dev/null +++ b/net/udp/udp_socket_unittest.cc @@ -0,0 +1,305 @@ +// Copyright (c) 2011 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/udp/udp_client_socket.h" +#include "net/udp/udp_server_socket.h" + +#include "base/basictypes.h" +#include "base/metrics/histogram.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/base/net_test_suite.h" +#include "net/base/net_util.h" +#include "net/base/sys_addrinfo.h" +#include "net/base/test_completion_callback.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace net { + +namespace { + +class UDPSocketTest : public PlatformTest { + public: + UDPSocketTest() + : buffer_(new IOBufferWithSize(kMaxRead)) { + recv_from_address_length_ = sizeof(recv_from_storage_); + recv_from_address_ = + reinterpret_cast<struct sockaddr*>(&recv_from_storage_); + } + + // Blocks until data is read from the socket. + std::string RecvFromSocket(UDPServerSocket* socket) { + TestCompletionCallback callback; + + recv_from_address_length_ = sizeof(struct sockaddr_in6); + int rv = socket->RecvFrom(buffer_, kMaxRead, recv_from_address_, + &recv_from_address_length_, &callback); + if (rv == ERR_IO_PENDING) + rv = callback.WaitForResult(); + if (rv < 0) + return ""; // error! + return std::string(buffer_->data(), rv); + } + + // Loop until |msg| has been written to the socket or until an + // error occurs. + // If |sockaddr| is non-NULL, then |sockaddr| and |sockaddr_len| are used + // for the destination to send to. Otherwise, will send to the last socket + // this server received from. + int SendToSocket(UDPServerSocket* socket, + std::string msg, + struct sockaddr* sockaddr, + socklen_t sockaddr_len) { + TestCompletionCallback callback; + + if (sockaddr == NULL) { + sockaddr = recv_from_address_; + sockaddr_len = recv_from_address_length_; + } + + int length = msg.length(); + scoped_refptr<StringIOBuffer> io_buffer(new StringIOBuffer(msg)); + scoped_refptr<DrainableIOBuffer> buffer( + new DrainableIOBuffer(io_buffer, length)); + + int bytes_sent = 0; + while (buffer->BytesRemaining()) { + int rv = socket->SendTo(buffer, buffer->BytesRemaining(), + sockaddr, sockaddr_len, + &callback); + if (rv == ERR_IO_PENDING) + rv = callback.WaitForResult(); + if (rv <= 0) + return bytes_sent > 0 ? bytes_sent : rv; + bytes_sent += rv; + buffer->DidConsume(rv); + } + return bytes_sent; + } + + std::string ReadSocket(UDPClientSocket* socket) { + TestCompletionCallback callback; + + recv_from_address_length_ = sizeof(struct sockaddr_in6); + int rv = socket->Read(buffer_, kMaxRead, &callback); + if (rv == ERR_IO_PENDING) + rv = callback.WaitForResult(); + if (rv < 0) + return ""; // error! + return std::string(buffer_->data(), rv); + } + + // Loop until |msg| has been written to the socket or until an + // error occurs. + int WriteSocket(UDPClientSocket* socket, std::string msg) { + TestCompletionCallback callback; + + int length = msg.length(); + scoped_refptr<StringIOBuffer> io_buffer(new StringIOBuffer(msg)); + scoped_refptr<DrainableIOBuffer> buffer( + new DrainableIOBuffer(io_buffer, length)); + + int bytes_sent = 0; + while (buffer->BytesRemaining()) { + int rv = socket->Write(buffer, buffer->BytesRemaining(), &callback); + if (rv == ERR_IO_PENDING) + rv = callback.WaitForResult(); + if (rv <= 0) + return bytes_sent > 0 ? bytes_sent : rv; + bytes_sent += rv; + buffer->DidConsume(rv); + } + return bytes_sent; + } + + private: + static const int kMaxRead = 1024; + scoped_refptr<IOBufferWithSize> buffer_; + struct sockaddr_storage recv_from_storage_; + struct sockaddr* recv_from_address_; + socklen_t recv_from_address_length_; +}; + +// Creates and address from an ip/port and returns it in |address|. +void CreateUDPAddress(std::string ip_str, int port, AddressList* address) { + IPAddressNumber ip_number; + bool rv = ParseIPLiteralToNumber(ip_str, &ip_number); + if (!rv) + return; + *address = AddressList(ip_number, port, false); +} + +TEST_F(UDPSocketTest, Connect) { + const int kPort = 9999; + std::string simple_message("hello world!"); + + // Setup the server to listen. + AddressList bind_address; + CreateUDPAddress("0.0.0.0", kPort, &bind_address); + UDPServerSocket server(NULL, NetLog::Source()); + int rv = server.Listen(bind_address); + EXPECT_EQ(OK, rv); + + // Setup the client. + AddressList server_address; + CreateUDPAddress("127.0.0.1", kPort, &server_address); + UDPClientSocket client(NULL, NetLog::Source()); + rv = client.Connect(server_address); + EXPECT_EQ(OK, rv); + + // Client sends to the server. + rv = WriteSocket(&client, simple_message); + EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv)); + + // Server waits for message. + std::string str = RecvFromSocket(&server); + DCHECK(simple_message == str); + + // Server echoes reply. + rv = SendToSocket(&server, simple_message, NULL, 0); + EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv)); + + // Client waits for response. + str = ReadSocket(&client); + DCHECK(simple_message == str); +} + +// In this test, we verify that connect() on a socket will have the effect +// of filtering reads on this socket only to data read from the destination +// we connected to. +// +// The purpose of this test is that some documentation indicates that connect +// binds the client's sends to send to a particular server endpoint, but does +// not bind the client's reads to only be from that endpoint, and that we need +// to always use recvfrom() to disambiguate. +TEST_F(UDPSocketTest, VerifyConnectBindsAddr) { + const int kPort1 = 9999; + const int kPort2 = 10000; + std::string simple_message("hello world!"); + std::string foreign_message("BAD MESSAGE TO GET!!"); + + // Setup the first server to listen. + AddressList bind_address; + CreateUDPAddress("0.0.0.0", kPort1, &bind_address); + UDPServerSocket server1(NULL, NetLog::Source()); + int rv = server1.Listen(bind_address); + EXPECT_EQ(OK, rv); + + // Setup the second server to listen. + CreateUDPAddress("0.0.0.0", kPort2, &bind_address); + UDPServerSocket server2(NULL, NetLog::Source()); + rv = server2.Listen(bind_address); + EXPECT_EQ(OK, rv); + + // Setup the client, connected to server 1. + AddressList server_address; + CreateUDPAddress("127.0.0.1", kPort1, &server_address); + UDPClientSocket client(NULL, NetLog::Source()); + rv = client.Connect(server_address); + EXPECT_EQ(OK, rv); + + // Client sends to server1. + rv = WriteSocket(&client, simple_message); + EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv)); + + // Server1 waits for message. + std::string str = RecvFromSocket(&server1); + DCHECK(simple_message == str); + + // Get the client's address. + AddressList client_address; + rv = client.GetLocalAddress(&client_address); + EXPECT_EQ(OK, rv); + + // Server2 sends reply. + rv = SendToSocket(&server2, foreign_message, + client_address.head()->ai_addr, + client_address.head()->ai_addrlen); + EXPECT_EQ(foreign_message.length(), static_cast<size_t>(rv)); + + // Server1 sends reply. + rv = SendToSocket(&server1, simple_message, + client_address.head()->ai_addr, + client_address.head()->ai_addrlen); + EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv)); + + // Client waits for response. + str = ReadSocket(&client); + DCHECK(simple_message == str); +} + +TEST_F(UDPSocketTest, ClientGetLocalPeerAddresses) { + struct TestData { + std::string remote_address; + std::string local_address; + bool is_ipv6; + } tests[] = { + { "127.0.00.1", "127.0.0.1", false }, + { "192.168.1.1", "127.0.0.1", false }, + { "::1", "::1", true }, + { "2001:db8:0::42", "::1", true }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); i++) { + net::IPAddressNumber ip_number; + net::ParseIPLiteralToNumber(tests[i].remote_address, &ip_number); + net::AddressList remote_address(ip_number, 80, true); + net::ParseIPLiteralToNumber(tests[i].local_address, &ip_number); + net::AddressList local_address(ip_number, 80, true); + + UDPClientSocket client(NULL, NetLog::Source()); + int rv = client.Connect(remote_address); + EXPECT_LE(ERR_IO_PENDING, rv); + + AddressList fetched_local_address; + rv = client.GetLocalAddress(&fetched_local_address); + EXPECT_EQ(OK, rv); + + const struct addrinfo* a1 = local_address.head(); + const struct addrinfo* a2 = fetched_local_address.head(); + EXPECT_TRUE(a1 != NULL); + EXPECT_TRUE(a2 != NULL); + + EXPECT_EQ(a1->ai_family, a2->ai_family); + EXPECT_EQ(a1->ai_addrlen, a2->ai_addrlen); + // TODO(mbelshe): figure out how to verify the IP and port. + // The port is dynamically generated by the udp stack. + // The IP is the real IP of the client, not necessarily + // loopback. + //EXPECT_EQ(NetAddressToString(a1), NetAddressToString(a2)); + + AddressList fetched_remote_address; + rv = client.GetPeerAddress(&fetched_remote_address); + EXPECT_EQ(OK, rv); + + a1 = remote_address.head(); + a2 = fetched_remote_address.head(); + EXPECT_TRUE(a1 != NULL); + EXPECT_TRUE(a2 != NULL); + + EXPECT_EQ(a1->ai_family, a2->ai_family); + EXPECT_EQ(a1->ai_addrlen, a2->ai_addrlen); + EXPECT_EQ(NetAddressToString(a1), NetAddressToString(a2)); + } +} + +TEST_F(UDPSocketTest, ServerGetLocalAddress) { + // TODO(mbelshe): implement me +} + +TEST_F(UDPSocketTest, ServerGetPeerAddress) { + // TODO(mbelshe): implement me +} + +} // namespace + +} // namespace net + +int main(int argc, char** argv) { + // Record histograms, so we can get histograms data in tests. + base::StatisticsRecorder recorder; + NetTestSuite test_suite(argc, argv); + + return test_suite.Run(); +} |