summaryrefslogtreecommitdiffstats
path: root/net/udp
diff options
context:
space:
mode:
authormbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-02 22:56:18 +0000
committermbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-02 22:56:18 +0000
commita2798d9808595a3b9d3a3b5fe6a3d0109a13aabd (patch)
tree50b849bc622aa4825f4eff165a655778678ccf84 /net/udp
parent4b9baf9d5a55852ab9613a04d745e891a6d083f0 (diff)
downloadchromium_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.h27
-rw-r--r--net/udp/datagram_server_socket.h65
-rw-r--r--net/udp/datagram_socket.h32
-rw-r--r--net/udp/udp_client_socket.cc49
-rw-r--r--net/udp/udp_client_socket.h41
-rw-r--r--net/udp/udp_server_socket.cc50
-rw-r--r--net/udp/udp_server_socket.h48
-rw-r--r--net/udp/udp_socket.h28
-rw-r--r--net/udp/udp_socket_libevent.cc366
-rw-r--r--net/udp/udp_socket_libevent.h212
-rw-r--r--net/udp/udp_socket_unittest.cc305
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();
+}