From 83088b2c3cf332a53d866c92b8106c26fb2395a3 Mon Sep 17 00:00:00 2001 From: "pliard@chromium.org" Date: Mon, 23 Apr 2012 18:23:26 +0000 Subject: Upstream changes making ListenSocket an abstract class. This is part of Chrome for Android upstreaming. This CL makes ListenSocket an abstract class instead of a concrete class implementing a TCP Listen Socket. This abstraction will be used later to make HttpServer seamlessly operate on TCP sockets and Unix domain sockets (will be upstreamed in a separate CL). The TCP Listen socket implementation, previously in listen_socket.{cc,h}, is now in tcp_listen_socket.{cc,h}. TEST=net_unittests Review URL: http://codereview.chromium.org/10108015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133480 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome_frame/test/test_server.cc | 6 +- chrome_frame/test/test_server.h | 1 + net/base/listen_socket.cc | 315 +----------------------- net/base/listen_socket.h | 98 +------- net/base/listen_socket_unittest.cc | 248 ------------------- net/base/listen_socket_unittest.h | 128 ---------- net/base/tcp_listen_socket.cc | 322 +++++++++++++++++++++++++ net/base/tcp_listen_socket.h | 112 +++++++++ net/base/tcp_listen_socket_unittest.cc | 251 +++++++++++++++++++ net/base/tcp_listen_socket_unittest.h | 127 ++++++++++ net/net.gyp | 8 +- net/server/http_server.cc | 5 +- net/socket/transport_client_socket_unittest.cc | 16 +- net/tools/fetch/http_listen_socket.cc | 16 +- net/tools/fetch/http_listen_socket.h | 6 +- 15 files changed, 861 insertions(+), 798 deletions(-) delete mode 100644 net/base/listen_socket_unittest.cc delete mode 100644 net/base/listen_socket_unittest.h create mode 100644 net/base/tcp_listen_socket.cc create mode 100644 net/base/tcp_listen_socket.h create mode 100644 net/base/tcp_listen_socket_unittest.cc create mode 100644 net/base/tcp_listen_socket_unittest.h diff --git a/chrome_frame/test/test_server.cc b/chrome_frame/test/test_server.cc index ea4923e..b38b143 100644 --- a/chrome_frame/test/test_server.cc +++ b/chrome_frame/test/test_server.cc @@ -14,6 +14,7 @@ #include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "chrome_frame/test/test_server.h" +#include "net/base/tcp_listen_socket.h" #include "net/base/winsock_init.h" #include "net/http/http_util.h" @@ -132,7 +133,7 @@ SimpleWebServer::SimpleWebServer(int port) { CHECK(MessageLoop::current()) << "SimpleWebServer requires a message loop"; net::EnsureWinsockInit(); AddResponse(&quit_); - server_ = net::ListenSocket::Listen("127.0.0.1", port, this); + server_ = net::TCPListenSocket::CreateAndListen("127.0.0.1", port, this); DCHECK(server_.get() != NULL); } @@ -236,7 +237,8 @@ HTTPTestServer::HTTPTestServer(int port, const std::wstring& address, FilePath root_dir) : port_(port), address_(address), root_dir_(root_dir) { net::EnsureWinsockInit(); - server_ = net::ListenSocket::Listen(WideToUTF8(address), port, this); + server_ = + net::TCPListenSocket::CreateAndListen(WideToUTF8(address), port, this); } HTTPTestServer::~HTTPTestServer() { diff --git a/chrome_frame/test/test_server.h b/chrome_frame/test/test_server.h index 50ddf37..2fef7d2 100644 --- a/chrome_frame/test/test_server.h +++ b/chrome_frame/test/test_server.h @@ -40,6 +40,7 @@ #include "base/basictypes.h" #include "base/file_util.h" +#include "base/message_loop.h" #include "net/base/listen_socket.h" namespace test_server { diff --git a/net/base/listen_socket.cc b/net/base/listen_socket.cc index c600594..499bb76 100644 --- a/net/base/listen_socket.cc +++ b/net/base/listen_socket.cc @@ -2,331 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "build/build_config.h" - -#if defined(OS_WIN) -// winsock2.h must be included first in order to ensure it is included before -// windows.h. -#include -#elif defined(OS_POSIX) -#include -#include -#include -#include -#include -#include "net/base/net_errors.h" -#endif - -#include "base/eintr_wrapper.h" -#include "base/sys_byteorder.h" -#include "base/threading/platform_thread.h" -#include "net/base/net_util.h" #include "net/base/listen_socket.h" -#if defined(OS_WIN) -typedef int socklen_t; -#endif // defined(OS_WIN) - namespace net { -namespace { +ListenSocket::ListenSocket(ListenSocketDelegate* del) : socket_delegate_(del) {} -const int kReadBufSize = 4096; - -} // namespace - -#if defined(OS_WIN) -const SOCKET ListenSocket::kInvalidSocket = INVALID_SOCKET; -const int ListenSocket::kSocketError = SOCKET_ERROR; -#elif defined(OS_POSIX) -const SOCKET ListenSocket::kInvalidSocket = -1; -const int ListenSocket::kSocketError = -1; -#endif - -ListenSocket* ListenSocket::Listen(std::string ip, int port, - ListenSocketDelegate* del) { - SOCKET s = Listen(ip, port); - if (s == kInvalidSocket) { - // TODO(erikkay): error handling - } else { - ListenSocket* sock = new ListenSocket(s, del); - sock->Listen(); - return sock; - } - return NULL; -} +ListenSocket::~ListenSocket() {} void ListenSocket::Send(const char* bytes, int len, bool append_linefeed) { SendInternal(bytes, len); - if (append_linefeed) { + if (append_linefeed) SendInternal("\r\n", 2); - } } void ListenSocket::Send(const std::string& str, bool append_linefeed) { Send(str.data(), static_cast(str.length()), append_linefeed); } -void ListenSocket::PauseReads() { - DCHECK(!reads_paused_); - reads_paused_ = true; -} - -void ListenSocket::ResumeReads() { - DCHECK(reads_paused_); - reads_paused_ = false; - if (has_pending_reads_) { - has_pending_reads_ = false; - Read(); - } -} - -ListenSocket::ListenSocket(SOCKET s, ListenSocketDelegate *del) - : socket_(s), - socket_delegate_(del), - reads_paused_(false), - has_pending_reads_(false) { -#if defined(OS_WIN) - socket_event_ = WSACreateEvent(); - // TODO(ibrar): error handling in case of socket_event_ == WSA_INVALID_EVENT - WatchSocket(NOT_WAITING); -#elif defined(OS_POSIX) - wait_state_ = NOT_WAITING; -#endif -} - -ListenSocket::~ListenSocket() { -#if defined(OS_WIN) - if (socket_event_) { - WSACloseEvent(socket_event_); - socket_event_ = WSA_INVALID_EVENT; - } -#endif - CloseSocket(socket_); -} - -SOCKET ListenSocket::Listen(std::string ip, int port) { - SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (s != kInvalidSocket) { -#if defined(OS_POSIX) - // Allow rapid reuse. - static const int kOn = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); -#endif - sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr(ip.c_str()); - addr.sin_port = base::HostToNet16(port); - if (bind(s, reinterpret_cast(&addr), sizeof(addr))) { -#if defined(OS_WIN) - closesocket(s); -#elif defined(OS_POSIX) - close(s); -#endif - s = kInvalidSocket; - } - } - return s; -} - -SOCKET ListenSocket::Accept(SOCKET s) { - sockaddr_in from; - socklen_t from_len = sizeof(from); - SOCKET conn = - HANDLE_EINTR(accept(s, reinterpret_cast(&from), &from_len)); - if (conn != kInvalidSocket) { - SetNonBlocking(conn); - } - return conn; -} - -void ListenSocket::SendInternal(const char* bytes, int len) { - char* send_buf = const_cast(bytes); - int len_left = len; - while (true) { - int sent = HANDLE_EINTR(send(socket_, send_buf, len_left, 0)); - if (sent == len_left) { // A shortcut to avoid extraneous checks. - break; - } - if (sent == kSocketError) { -#if defined(OS_WIN) - if (WSAGetLastError() != WSAEWOULDBLOCK) { - LOG(ERROR) << "send failed: WSAGetLastError()==" << WSAGetLastError(); -#elif defined(OS_POSIX) - if (errno != EWOULDBLOCK && errno != EAGAIN) { - LOG(ERROR) << "send failed: errno==" << errno; -#endif - break; - } - // Otherwise we would block, and now we have to wait for a retry. - // Fall through to PlatformThread::YieldCurrentThread() - } else { - // sent != len_left according to the shortcut above. - // Shift the buffer start and send the remainder after a short while. - send_buf += sent; - len_left -= sent; - } - base::PlatformThread::YieldCurrentThread(); - } -} - -void ListenSocket::Listen() { - int backlog = 10; // TODO(erikkay): maybe don't allow any backlog? - listen(socket_, backlog); - // TODO(erikkay): error handling -#if defined(OS_POSIX) - WatchSocket(WAITING_ACCEPT); -#endif -} - -void ListenSocket::Accept() { - SOCKET conn = Accept(socket_); - if (conn != kInvalidSocket) { - scoped_refptr sock( - new ListenSocket(conn, socket_delegate_)); - // it's up to the delegate to AddRef if it wants to keep it around -#if defined(OS_POSIX) - sock->WatchSocket(WAITING_READ); -#endif - socket_delegate_->DidAccept(this, sock); - } else { - // TODO(ibrar): some error handling required here - } -} - -void ListenSocket::Read() { - char buf[kReadBufSize + 1]; // +1 for null termination - int len; - do { - len = HANDLE_EINTR(recv(socket_, buf, kReadBufSize, 0)); - if (len == kSocketError) { -#if defined(OS_WIN) - int err = WSAGetLastError(); - if (err == WSAEWOULDBLOCK) { -#elif defined(OS_POSIX) - if (errno == EWOULDBLOCK || errno == EAGAIN) { -#endif - break; - } else { - // TODO(ibrar): some error handling required here - break; - } - } else if (len == 0) { - // In Windows, Close() is called by OnObjectSignaled. In POSIX, we need - // to call it here. -#if defined(OS_POSIX) - Close(); -#endif - } else { - // TODO(ibrar): maybe change DidRead to take a length instead - DCHECK_GT(len, 0); - DCHECK_LE(len, kReadBufSize); - buf[len] = 0; // already create a buffer with +1 length - socket_delegate_->DidRead(this, buf, len); - } - } while (len == kReadBufSize); -} - -void ListenSocket::Close() { -#if defined(OS_POSIX) - if (wait_state_ == NOT_WAITING) - return; - wait_state_ = NOT_WAITING; -#endif - UnwatchSocket(); - socket_delegate_->DidClose(this); -} - -void ListenSocket::CloseSocket(SOCKET s) { - if (s && s != kInvalidSocket) { - UnwatchSocket(); -#if defined(OS_WIN) - closesocket(s); -#elif defined(OS_POSIX) - close(s); -#endif - } -} - -void ListenSocket::WatchSocket(WaitState state) { -#if defined(OS_WIN) - WSAEventSelect(socket_, socket_event_, FD_ACCEPT | FD_CLOSE | FD_READ); - watcher_.StartWatching(socket_event_, this); -#elif defined(OS_POSIX) - // Implicitly calls StartWatchingFileDescriptor(). - MessageLoopForIO::current()->WatchFileDescriptor( - socket_, true, MessageLoopForIO::WATCH_READ, &watcher_, this); - wait_state_ = state; -#endif -} - -void ListenSocket::UnwatchSocket() { -#if defined(OS_WIN) - watcher_.StopWatching(); -#elif defined(OS_POSIX) - watcher_.StopWatchingFileDescriptor(); -#endif -} - -// TODO(ibrar): We can add these functions into OS dependent files -#if defined(OS_WIN) -// MessageLoop watcher callback -void ListenSocket::OnObjectSignaled(HANDLE object) { - WSANETWORKEVENTS ev; - if (kSocketError == WSAEnumNetworkEvents(socket_, socket_event_, &ev)) { - // TODO - return; - } - - // The object was reset by WSAEnumNetworkEvents. Watch for the next signal. - watcher_.StartWatching(object, this); - - if (ev.lNetworkEvents == 0) { - // Occasionally the event is set even though there is no new data. - // The net seems to think that this is ignorable. - return; - } - if (ev.lNetworkEvents & FD_ACCEPT) { - Accept(); - } - if (ev.lNetworkEvents & FD_READ) { - if (reads_paused_) { - has_pending_reads_ = true; - } else { - Read(); - } - } - if (ev.lNetworkEvents & FD_CLOSE) { - Close(); - } -} -#elif defined(OS_POSIX) -void ListenSocket::OnFileCanReadWithoutBlocking(int fd) { - switch (wait_state_) { - case WAITING_ACCEPT: - Accept(); - break; - case WAITING_READ: - if (reads_paused_) { - has_pending_reads_ = true; - } else { - Read(); - } - break; - default: - // Close() is called by Read() in the Linux case. - NOTREACHED(); - break; - } -} - -void ListenSocket::OnFileCanWriteWithoutBlocking(int fd) { - // MessagePumpLibevent callback, we don't listen for write events - // so we shouldn't ever reach here. - NOTREACHED(); -} - -#endif - } // namespace net diff --git a/net/base/listen_socket.h b/net/base/listen_socket.h index 8223767..f102462 100644 --- a/net/base/listen_socket.h +++ b/net/base/listen_socket.h @@ -1,47 +1,26 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. -// TCP/IP server that handles IO asynchronously in the specified MessageLoop. -// These objects are NOT thread safe. They use WSAEVENT handles to monitor -// activity in a given MessageLoop. This means that callbacks will -// happen in that loop's thread always and that all other methods (including -// constructors and destructors) should also be called from the same thread. +// Abstract socket server that handles IO asynchronously in the specified +// MessageLoop. #ifndef NET_BASE_LISTEN_SOCKET_H_ #define NET_BASE_LISTEN_SOCKET_H_ #pragma once -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#endif #include -#if defined(OS_WIN) -#include "base/win/object_watcher.h" -#elif defined(OS_POSIX) -#include "base/message_loop.h" -#endif #include "base/basictypes.h" -#include "base/compiler_specific.h" #include "base/memory/ref_counted.h" +#include "build/build_config.h" #include "net/base/net_export.h" -#if defined(OS_POSIX) -typedef int SOCKET; -#endif - namespace net { -// Implements a raw socket interface -class NET_EXPORT ListenSocket : public base::RefCountedThreadSafe, -#if defined(OS_WIN) - public base::win::ObjectWatcher::Delegate { -#elif defined(OS_POSIX) - public MessageLoopForIO::Watcher { -#endif +// Defines a socket interface for a server. +class NET_EXPORT ListenSocket + : public base::RefCountedThreadSafe { public: // TODO(erikkay): this delegate should really be split into two parts // to split up the listener from the connected socket. Perhaps this class @@ -53,79 +32,28 @@ class NET_EXPORT ListenSocket : public base::RefCountedThreadSafe, // server is the original listening Socket, connection is the new // Socket that was created. Ownership of connection is transferred // to the delegate with this call. - virtual void DidAccept(ListenSocket *server, ListenSocket *connection) = 0; + virtual void DidAccept(ListenSocket *server, + ListenSocket *connection) = 0; virtual void DidRead(ListenSocket *connection, const char* data, int len) = 0; virtual void DidClose(ListenSocket *sock) = 0; }; - // Listen on port for the specified IP address. Use 127.0.0.1 to only - // accept local connections. - static ListenSocket* Listen(std::string ip, int port, - ListenSocketDelegate* del); - // Send data to the socket. void Send(const char* bytes, int len, bool append_linefeed = false); void Send(const std::string& str, bool append_linefeed = false); - // NOTE: This is for unit test use only! - // Pause/Resume calling Read(). Note that ResumeReads() will also call - // Read() if there is anything to read. - void PauseReads(); - void ResumeReads(); - protected: - friend class base::RefCountedThreadSafe; - - enum WaitState { - NOT_WAITING = 0, - WAITING_ACCEPT = 1, - WAITING_READ = 2 - }; - - static const SOCKET kInvalidSocket; - static const int kSocketError; - - ListenSocket(SOCKET s, ListenSocketDelegate* del); + ListenSocket(ListenSocketDelegate* del); virtual ~ListenSocket(); - static SOCKET Listen(std::string ip, int port); - // if valid, returned SOCKET is non-blocking - static SOCKET Accept(SOCKET s); - - virtual void SendInternal(const char* bytes, int len); - virtual void Listen(); - virtual void Accept(); - virtual void Read(); - virtual void Close(); - virtual void CloseSocket(SOCKET s); + virtual void SendInternal(const char* bytes, int len) = 0; - // Pass any value in case of Windows, because in Windows - // we are not using state. - void WatchSocket(WaitState state); - void UnwatchSocket(); - -#if defined(OS_WIN) - // ObjectWatcher delegate - virtual void OnObjectSignaled(HANDLE object); - base::win::ObjectWatcher watcher_; - HANDLE socket_event_; -#elif defined(OS_POSIX) - // Called by MessagePumpLibevent when the socket is ready to do I/O - virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; - virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; - WaitState wait_state_; - // The socket's libevent wrapper - MessageLoopForIO::FileDescriptorWatcher watcher_; -#endif - - SOCKET socket_; - ListenSocketDelegate *socket_delegate_; + ListenSocketDelegate* const socket_delegate_; private: - bool reads_paused_; - bool has_pending_reads_; + friend class base::RefCountedThreadSafe; DISALLOW_COPY_AND_ASSIGN(ListenSocket); }; diff --git a/net/base/listen_socket_unittest.cc b/net/base/listen_socket_unittest.cc deleted file mode 100644 index 5e3811e..0000000 --- a/net/base/listen_socket_unittest.cc +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright (c) 2012 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/listen_socket_unittest.h" - -#include -#include - -#include "base/bind.h" -#include "base/eintr_wrapper.h" -#include "base/sys_byteorder.h" -#include "net/base/net_util.h" -#include "testing/platform_test.h" - -namespace net { - -const int ListenSocketTester::kTestPort = 9999; - -static const int kReadBufSize = 1024; -static const char kHelloWorld[] = "HELLO, WORLD"; -static const int kMaxQueueSize = 20; -static const char kLoopback[] = "127.0.0.1"; -static const int kDefaultTimeoutMs = 5000; - -ListenSocketTester::ListenSocketTester() - : thread_(NULL), - loop_(NULL), - server_(NULL), - connection_(NULL), - cv_(&lock_) { -} - -void ListenSocketTester::SetUp() { - base::Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - thread_.reset(new base::Thread("socketio_test")); - thread_->StartWithOptions(options); - loop_ = reinterpret_cast(thread_->message_loop()); - - loop_->PostTask(FROM_HERE, base::Bind(&ListenSocketTester::Listen, this)); - - // verify Listen succeeded - NextAction(); - ASSERT_FALSE(server_ == NULL); - ASSERT_EQ(ACTION_LISTEN, last_action_.type()); - - // verify the connect/accept and setup test_socket_ - test_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - ASSERT_NE(INVALID_SOCKET, test_socket_); - struct sockaddr_in client; - client.sin_family = AF_INET; - client.sin_addr.s_addr = inet_addr(kLoopback); - client.sin_port = base::HostToNet16(kTestPort); - int ret = HANDLE_EINTR( - connect(test_socket_, reinterpret_cast(&client), - sizeof(client))); - ASSERT_NE(ret, SOCKET_ERROR); - - NextAction(); - ASSERT_EQ(ACTION_ACCEPT, last_action_.type()); -} - -void ListenSocketTester::TearDown() { -#if defined(OS_WIN) - ASSERT_EQ(0, closesocket(test_socket_)); -#elif defined(OS_POSIX) - ASSERT_EQ(0, HANDLE_EINTR(close(test_socket_))); -#endif - NextAction(); - ASSERT_EQ(ACTION_CLOSE, last_action_.type()); - - loop_->PostTask(FROM_HERE, base::Bind(&ListenSocketTester::Shutdown, this)); - NextAction(); - ASSERT_EQ(ACTION_SHUTDOWN, last_action_.type()); - - thread_.reset(); - loop_ = NULL; -} - -void ListenSocketTester::ReportAction(const ListenSocketTestAction& action) { - base::AutoLock locked(lock_); - queue_.push_back(action); - cv_.Broadcast(); -} - -void ListenSocketTester::NextAction() { - base::AutoLock locked(lock_); - while (queue_.empty()) - cv_.Wait(); - last_action_ = queue_.front(); - queue_.pop_front(); -} - -int ListenSocketTester::ClearTestSocket() { - char buf[kReadBufSize]; - int len_ret = 0; - do { - int len = HANDLE_EINTR(recv(test_socket_, buf, kReadBufSize, 0)); - if (len == SOCKET_ERROR || len == 0) { - break; - } else { - len_ret += len; - } - } while (true); - return len_ret; -} - -void ListenSocketTester::Shutdown() { - connection_->Release(); - connection_ = NULL; - server_->Release(); - server_ = NULL; - ReportAction(ListenSocketTestAction(ACTION_SHUTDOWN)); -} - -void ListenSocketTester::Listen() { - server_ = DoListen(); - if (server_) { - server_->AddRef(); - ReportAction(ListenSocketTestAction(ACTION_LISTEN)); - } -} - -void ListenSocketTester::SendFromTester() { - connection_->Send(kHelloWorld); - ReportAction(ListenSocketTestAction(ACTION_SEND)); -} - -void ListenSocketTester::TestClientSend() { - ASSERT_TRUE(Send(test_socket_, kHelloWorld)); - NextAction(); - ASSERT_EQ(ACTION_READ, last_action_.type()); - ASSERT_EQ(last_action_.data(), kHelloWorld); -} - -void ListenSocketTester::TestClientSendLong() { - size_t hello_len = strlen(kHelloWorld); - std::string long_string; - size_t long_len = 0; - for (int i = 0; i < 200; i++) { - long_string += kHelloWorld; - long_len += hello_len; - } - ASSERT_TRUE(Send(test_socket_, long_string)); - size_t read_len = 0; - while (read_len < long_len) { - NextAction(); - ASSERT_EQ(ACTION_READ, last_action_.type()); - std::string last_data = last_action_.data(); - size_t len = last_data.length(); - if (long_string.compare(read_len, len, last_data)) { - ASSERT_EQ(long_string.compare(read_len, len, last_data), 0); - } - read_len += last_data.length(); - } - ASSERT_EQ(read_len, long_len); -} - -void ListenSocketTester::TestServerSend() { - loop_->PostTask(FROM_HERE, base::Bind( - &ListenSocketTester::SendFromTester, this)); - NextAction(); - ASSERT_EQ(ACTION_SEND, last_action_.type()); - const int buf_len = 200; - char buf[buf_len+1]; - unsigned recv_len = 0; - while (recv_len < strlen(kHelloWorld)) { - int r = HANDLE_EINTR(recv(test_socket_, buf, buf_len, 0)); - ASSERT_GE(r, 0); - recv_len += static_cast(r); - if (!r) - break; - } - buf[recv_len] = 0; - ASSERT_STREQ(buf, kHelloWorld); -} - -bool ListenSocketTester::Send(SOCKET sock, const std::string& str) { - int len = static_cast(str.length()); - int send_len = HANDLE_EINTR(send(sock, str.data(), len, 0)); - if (send_len == SOCKET_ERROR) { - LOG(ERROR) << "send failed: " << errno; - return false; - } else if (send_len != len) { - return false; - } - return true; -} - -void ListenSocketTester::DidAccept(ListenSocket *server, - ListenSocket *connection) { - connection_ = connection; - connection_->AddRef(); - ReportAction(ListenSocketTestAction(ACTION_ACCEPT)); -} - -void ListenSocketTester::DidRead(ListenSocket *connection, - const char* data, - int len) { - std::string str(data, len); - ReportAction(ListenSocketTestAction(ACTION_READ, str)); -} - -void ListenSocketTester::DidClose(ListenSocket *sock) { - ReportAction(ListenSocketTestAction(ACTION_CLOSE)); -} - -ListenSocketTester::~ListenSocketTester() {} - -ListenSocket* ListenSocketTester::DoListen() { - return ListenSocket::Listen(kLoopback, kTestPort, this); -} - -class ListenSocketTest: public PlatformTest { - public: - ListenSocketTest() { - tester_ = NULL; - } - - virtual void SetUp() { - PlatformTest::SetUp(); - tester_ = new ListenSocketTester(); - tester_->SetUp(); - } - - virtual void TearDown() { - PlatformTest::TearDown(); - tester_->TearDown(); - tester_ = NULL; - } - - scoped_refptr tester_; -}; - -TEST_F(ListenSocketTest, ClientSend) { - tester_->TestClientSend(); -} - -TEST_F(ListenSocketTest, ClientSendLong) { - tester_->TestClientSendLong(); -} - -TEST_F(ListenSocketTest, ServerSend) { - tester_->TestServerSend(); -} - -} // namespace net diff --git a/net/base/listen_socket_unittest.h b/net/base/listen_socket_unittest.h deleted file mode 100644 index 1f63bc9..0000000 --- a/net/base/listen_socket_unittest.h +++ /dev/null @@ -1,128 +0,0 @@ -// 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_BASE_LISTEN_SOCKET_UNITTEST_H_ -#define NET_BASE_LISTEN_SOCKET_UNITTEST_H_ -#pragma once - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#elif defined(OS_POSIX) -#include -#include -#include -#endif - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "base/string_util.h" -#include "base/synchronization/condition_variable.h" -#include "base/synchronization/lock.h" -#include "base/threading/thread.h" -#include "net/base/listen_socket.h" -#include "net/base/net_util.h" -#include "net/base/winsock_init.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_POSIX) -// Used same name as in Windows to avoid #ifdef where referenced -#define SOCKET int -const int INVALID_SOCKET = -1; -const int SOCKET_ERROR = -1; -#endif - -namespace net { - -enum ActionType { - ACTION_NONE = 0, - ACTION_LISTEN = 1, - ACTION_ACCEPT = 2, - ACTION_READ = 3, - ACTION_SEND = 4, - ACTION_CLOSE = 5, - ACTION_SHUTDOWN = 6 -}; - -class ListenSocketTestAction { - public: - ListenSocketTestAction() : action_(ACTION_NONE) {} - explicit ListenSocketTestAction(ActionType action) : action_(action) {} - ListenSocketTestAction(ActionType action, std::string data) - : action_(action), - data_(data) {} - - const std::string data() const { return data_; } - ActionType type() const { return action_; } - - private: - ActionType action_; - std::string data_; -}; - - -// This had to be split out into a separate class because I couldn't -// make the testing::Test class refcounted. -class ListenSocketTester : - public ListenSocket::ListenSocketDelegate, - public base::RefCountedThreadSafe { - - public: - ListenSocketTester(); - - void SetUp(); - void TearDown(); - - void ReportAction(const ListenSocketTestAction& action); - void NextAction(); - - // read all pending data from the test socket - int ClearTestSocket(); - // Release the connection and server sockets - void Shutdown(); - void Listen(); - void SendFromTester(); - // verify the send/read from client to server - void TestClientSend(); - // verify send/read of a longer string - void TestClientSendLong(); - // verify a send/read from server to client - void TestServerSend(); - - virtual bool Send(SOCKET sock, const std::string& str); - - // ListenSocket::ListenSocketDelegate: - virtual void DidAccept(ListenSocket *server, - ListenSocket *connection) OVERRIDE; - virtual void DidRead(ListenSocket *connection, - const char* data, - int len) OVERRIDE; - virtual void DidClose(ListenSocket *sock) OVERRIDE; - - scoped_ptr thread_; - MessageLoopForIO* loop_; - ListenSocket* server_; - ListenSocket* connection_; - ListenSocketTestAction last_action_; - - SOCKET test_socket_; - static const int kTestPort; - - base::Lock lock_; // protects |queue_| and wraps |cv_| - base::ConditionVariable cv_; - std::deque queue_; - - protected: - friend class base::RefCountedThreadSafe; - - virtual ~ListenSocketTester(); - - virtual ListenSocket* DoListen(); -}; - -} // namespace net - -#endif // NET_BASE_LISTEN_SOCKET_UNITTEST_H_ diff --git a/net/base/tcp_listen_socket.cc b/net/base/tcp_listen_socket.cc new file mode 100644 index 0000000..dfec16f --- /dev/null +++ b/net/base/tcp_listen_socket.cc @@ -0,0 +1,322 @@ +// Copyright (c) 2012 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 "build/build_config.h" + +#if defined(OS_WIN) +// winsock2.h must be included first in order to ensure it is included before +// windows.h. +#include +#elif defined(OS_POSIX) +#include +#include +#include +#include +#include +#include "net/base/net_errors.h" +#endif + +#include "base/eintr_wrapper.h" +#include "base/sys_byteorder.h" +#include "base/threading/platform_thread.h" +#include "net/base/net_util.h" +#include "net/base/tcp_listen_socket.h" + +#if defined(OS_WIN) +typedef int socklen_t; +#endif // defined(OS_WIN) + +namespace net { + +namespace { + +const int kReadBufSize = 4096; + +} // namespace + +#if defined(OS_WIN) +const SOCKET TCPListenSocket::kInvalidSocket = INVALID_SOCKET; +const int TCPListenSocket::kSocketError = SOCKET_ERROR; +#elif defined(OS_POSIX) +const SOCKET TCPListenSocket::kInvalidSocket = -1; +const int TCPListenSocket::kSocketError = -1; +#endif + +TCPListenSocket* TCPListenSocket::CreateAndListen( + std::string ip, int port, ListenSocket::ListenSocketDelegate *del) { + SOCKET s = CreateAndBind(ip, port); + if (s == kInvalidSocket) { + // TODO(erikkay): error handling + } else { + TCPListenSocket* sock = new TCPListenSocket(s, del); + sock->Listen(); + return sock; + } + return NULL; +} + +void TCPListenSocket::PauseReads() { + DCHECK(!reads_paused_); + reads_paused_ = true; +} + +void TCPListenSocket::ResumeReads() { + DCHECK(reads_paused_); + reads_paused_ = false; + if (has_pending_reads_) { + has_pending_reads_ = false; + Read(); + } +} + +TCPListenSocket::TCPListenSocket(SOCKET s, + ListenSocket::ListenSocketDelegate *del) + : ListenSocket(del), + socket_(s), + reads_paused_(false), + has_pending_reads_(false) { +#if defined(OS_WIN) + socket_event_ = WSACreateEvent(); + // TODO(ibrar): error handling in case of socket_event_ == WSA_INVALID_EVENT + WatchSocket(NOT_WAITING); +#elif defined(OS_POSIX) + wait_state_ = NOT_WAITING; +#endif +} + +TCPListenSocket::~TCPListenSocket() { +#if defined(OS_WIN) + if (socket_event_) { + WSACloseEvent(socket_event_); + socket_event_ = WSA_INVALID_EVENT; + } +#endif + CloseSocket(socket_); +} + +SOCKET TCPListenSocket::CreateAndBind(std::string ip, int port) { + SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s != kInvalidSocket) { +#if defined(OS_POSIX) + // Allow rapid reuse. + static const int kOn = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); +#endif + sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(ip.c_str()); + addr.sin_port = base::HostToNet16(port); + if (bind(s, reinterpret_cast(&addr), sizeof(addr))) { +#if defined(OS_WIN) + closesocket(s); +#elif defined(OS_POSIX) + close(s); +#endif + s = kInvalidSocket; + } + } + return s; +} + +SOCKET TCPListenSocket::Accept(SOCKET s) { + sockaddr_in from; + socklen_t from_len = sizeof(from); + SOCKET conn = + HANDLE_EINTR(accept(s, reinterpret_cast(&from), &from_len)); + if (conn != kInvalidSocket) { + SetNonBlocking(conn); + } + return conn; +} + +void TCPListenSocket::SendInternal(const char* bytes, int len) { + char* send_buf = const_cast(bytes); + int len_left = len; + while (true) { + int sent = HANDLE_EINTR(send(socket_, send_buf, len_left, 0)); + if (sent == len_left) { // A shortcut to avoid extraneous checks. + break; + } + if (sent == kSocketError) { +#if defined(OS_WIN) + if (WSAGetLastError() != WSAEWOULDBLOCK) { + LOG(ERROR) << "send failed: WSAGetLastError()==" << WSAGetLastError(); +#elif defined(OS_POSIX) + if (errno != EWOULDBLOCK && errno != EAGAIN) { + LOG(ERROR) << "send failed: errno==" << errno; +#endif + break; + } + // Otherwise we would block, and now we have to wait for a retry. + // Fall through to PlatformThread::YieldCurrentThread() + } else { + // sent != len_left according to the shortcut above. + // Shift the buffer start and send the remainder after a short while. + send_buf += sent; + len_left -= sent; + } + base::PlatformThread::YieldCurrentThread(); + } +} + +void TCPListenSocket::Listen() { + int backlog = 10; // TODO(erikkay): maybe don't allow any backlog? + listen(socket_, backlog); + // TODO(erikkay): error handling +#if defined(OS_POSIX) + WatchSocket(WAITING_ACCEPT); +#endif +} + +void TCPListenSocket::Accept() { + SOCKET conn = Accept(socket_); + if (conn != kInvalidSocket) { + scoped_refptr sock( + new TCPListenSocket(conn, socket_delegate_)); + // it's up to the delegate to AddRef if it wants to keep it around +#if defined(OS_POSIX) + sock->WatchSocket(WAITING_READ); +#endif + socket_delegate_->DidAccept(this, sock); + } else { + // TODO(ibrar): some error handling required here + } +} + +void TCPListenSocket::Read() { + char buf[kReadBufSize + 1]; // +1 for null termination + int len; + do { + len = HANDLE_EINTR(recv(socket_, buf, kReadBufSize, 0)); + if (len == kSocketError) { +#if defined(OS_WIN) + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) { +#elif defined(OS_POSIX) + if (errno == EWOULDBLOCK || errno == EAGAIN) { +#endif + break; + } else { + // TODO(ibrar): some error handling required here + break; + } + } else if (len == 0) { + // In Windows, Close() is called by OnObjectSignaled. In POSIX, we need + // to call it here. +#if defined(OS_POSIX) + Close(); +#endif + } else { + // TODO(ibrar): maybe change DidRead to take a length instead + DCHECK_GT(len, 0); + DCHECK_LE(len, kReadBufSize); + buf[len] = 0; // already create a buffer with +1 length + socket_delegate_->DidRead(this, buf, len); + } + } while (len == kReadBufSize); +} + +void TCPListenSocket::Close() { +#if defined(OS_POSIX) + if (wait_state_ == NOT_WAITING) + return; + wait_state_ = NOT_WAITING; +#endif + UnwatchSocket(); + socket_delegate_->DidClose(this); +} + +void TCPListenSocket::CloseSocket(SOCKET s) { + if (s && s != kInvalidSocket) { + UnwatchSocket(); +#if defined(OS_WIN) + closesocket(s); +#elif defined(OS_POSIX) + close(s); +#endif + } +} + +void TCPListenSocket::WatchSocket(WaitState state) { +#if defined(OS_WIN) + WSAEventSelect(socket_, socket_event_, FD_ACCEPT | FD_CLOSE | FD_READ); + watcher_.StartWatching(socket_event_, this); +#elif defined(OS_POSIX) + // Implicitly calls StartWatchingFileDescriptor(). + MessageLoopForIO::current()->WatchFileDescriptor( + socket_, true, MessageLoopForIO::WATCH_READ, &watcher_, this); + wait_state_ = state; +#endif +} + +void TCPListenSocket::UnwatchSocket() { +#if defined(OS_WIN) + watcher_.StopWatching(); +#elif defined(OS_POSIX) + watcher_.StopWatchingFileDescriptor(); +#endif +} + +// TODO(ibrar): We can add these functions into OS dependent files +#if defined(OS_WIN) +// MessageLoop watcher callback +void TCPListenSocket::OnObjectSignaled(HANDLE object) { + WSANETWORKEVENTS ev; + if (kSocketError == WSAEnumNetworkEvents(socket_, socket_event_, &ev)) { + // TODO + return; + } + + // The object was reset by WSAEnumNetworkEvents. Watch for the next signal. + watcher_.StartWatching(object, this); + + if (ev.lNetworkEvents == 0) { + // Occasionally the event is set even though there is no new data. + // The net seems to think that this is ignorable. + return; + } + if (ev.lNetworkEvents & FD_ACCEPT) { + Accept(); + } + if (ev.lNetworkEvents & FD_READ) { + if (reads_paused_) { + has_pending_reads_ = true; + } else { + Read(); + } + } + if (ev.lNetworkEvents & FD_CLOSE) { + Close(); + } +} +#elif defined(OS_POSIX) +void TCPListenSocket::OnFileCanReadWithoutBlocking(int fd) { + switch (wait_state_) { + case WAITING_ACCEPT: + Accept(); + break; + case WAITING_READ: + if (reads_paused_) { + has_pending_reads_ = true; + } else { + Read(); + } + break; + default: + // Close() is called by Read() in the Linux case. + NOTREACHED(); + break; + } +} + +void TCPListenSocket::OnFileCanWriteWithoutBlocking(int fd) { + // MessagePumpLibevent callback, we don't listen for write events + // so we shouldn't ever reach here. + NOTREACHED(); +} + +#endif + +} // namespace net diff --git a/net/base/tcp_listen_socket.h b/net/base/tcp_listen_socket.h new file mode 100644 index 0000000..57d4679 --- /dev/null +++ b/net/base/tcp_listen_socket.h @@ -0,0 +1,112 @@ +// Copyright (c) 2012 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. + +// TCP/IP server that handles IO asynchronously in the specified MessageLoop. +// These objects are NOT thread safe. They use WSAEVENT handles to monitor +// activity in a given MessageLoop. This means that callbacks will +// happen in that loop's thread always and that all other methods (including +// constructors and destructors) should also be called from the same thread. + +#ifndef NET_BASE_TCP_LISTEN_SOCKET_H_ +#define NET_BASE_TCP_LISTEN_SOCKET_H_ +#pragma once + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif +#include +#if defined(OS_WIN) +#include "base/win/object_watcher.h" +#elif defined(OS_POSIX) +#include "base/message_loop.h" +#endif + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "net/base/listen_socket.h" +#include "net/base/net_export.h" + +#if defined(OS_POSIX) +typedef int SOCKET; +#endif + +namespace net { + +// Implements a TCP socket interface. Note that this is ref counted. +class NET_EXPORT TCPListenSocket : public ListenSocket, +#if defined(OS_WIN) + public base::win::ObjectWatcher::Delegate { +#elif defined(OS_POSIX) + public MessageLoopForIO::Watcher { +#endif + public: + // Listen on port for the specified IP address. Use 127.0.0.1 to only + // accept local connections. + static TCPListenSocket* CreateAndListen(std::string ip, int port, + ListenSocketDelegate* del); + + // NOTE: This is for unit test use only! + // Pause/Resume calling Read(). Note that ResumeReads() will also call + // Read() if there is anything to read. + void PauseReads(); + void ResumeReads(); + + protected: + enum WaitState { + NOT_WAITING = 0, + WAITING_ACCEPT = 1, + WAITING_READ = 2 + }; + + static const SOCKET kInvalidSocket; + static const int kSocketError; + + TCPListenSocket(SOCKET s, ListenSocketDelegate* del); + virtual ~TCPListenSocket(); + static SOCKET CreateAndBind(std::string ip, int port); + // if valid, returned SOCKET is non-blocking + static SOCKET Accept(SOCKET s); + + // Implements ListenSocket::SendInternal. + virtual void SendInternal(const char* bytes, int len) OVERRIDE; + + virtual void Listen(); + virtual void Accept(); + virtual void Read(); + virtual void Close(); + virtual void CloseSocket(SOCKET s); + + // Pass any value in case of Windows, because in Windows + // we are not using state. + void WatchSocket(WaitState state); + void UnwatchSocket(); + +#if defined(OS_WIN) + // ObjectWatcher delegate + virtual void OnObjectSignaled(HANDLE object); + base::win::ObjectWatcher watcher_; + HANDLE socket_event_; +#elif defined(OS_POSIX) + // Called by MessagePumpLibevent when the socket is ready to do I/O + virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; + virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; + WaitState wait_state_; + // The socket's libevent wrapper + MessageLoopForIO::FileDescriptorWatcher watcher_; +#endif + + SOCKET socket_; + + private: + bool reads_paused_; + bool has_pending_reads_; + + DISALLOW_COPY_AND_ASSIGN(TCPListenSocket); +}; + +} // namespace net + +#endif // NET_BASE_TCP_LISTEN_SOCKET_H_ diff --git a/net/base/tcp_listen_socket_unittest.cc b/net/base/tcp_listen_socket_unittest.cc new file mode 100644 index 0000000..30cde3cb1 --- /dev/null +++ b/net/base/tcp_listen_socket_unittest.cc @@ -0,0 +1,251 @@ +// Copyright (c) 2012 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_listen_socket_unittest.h" + +#include +#include + +#include "base/bind.h" +#include "base/eintr_wrapper.h" +#include "base/sys_byteorder.h" +#include "net/base/net_util.h" +#include "testing/platform_test.h" + +namespace net { + +const int TCPListenSocketTester::kTestPort = 9999; + +static const int kReadBufSize = 1024; +static const char kHelloWorld[] = "HELLO, WORLD"; +static const int kMaxQueueSize = 20; +static const char kLoopback[] = "127.0.0.1"; +static const int kDefaultTimeoutMs = 5000; + +TCPListenSocketTester::TCPListenSocketTester() + : thread_(NULL), + loop_(NULL), + server_(NULL), + connection_(NULL), + cv_(&lock_) { +} + +void TCPListenSocketTester::SetUp() { + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + thread_.reset(new base::Thread("socketio_test")); + thread_->StartWithOptions(options); + loop_ = reinterpret_cast(thread_->message_loop()); + + loop_->PostTask(FROM_HERE, base::Bind( + &TCPListenSocketTester::Listen, this)); + + // verify Listen succeeded + NextAction(); + ASSERT_FALSE(server_ == NULL); + ASSERT_EQ(ACTION_LISTEN, last_action_.type()); + + // verify the connect/accept and setup test_socket_ + test_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ASSERT_NE(INVALID_SOCKET, test_socket_); + struct sockaddr_in client; + client.sin_family = AF_INET; + client.sin_addr.s_addr = inet_addr(kLoopback); + client.sin_port = base::HostToNet16(kTestPort); + int ret = HANDLE_EINTR( + connect(test_socket_, reinterpret_cast(&client), + sizeof(client))); + ASSERT_NE(ret, SOCKET_ERROR); + + NextAction(); + ASSERT_EQ(ACTION_ACCEPT, last_action_.type()); +} + +void TCPListenSocketTester::TearDown() { +#if defined(OS_WIN) + ASSERT_EQ(0, closesocket(test_socket_)); +#elif defined(OS_POSIX) + ASSERT_EQ(0, HANDLE_EINTR(close(test_socket_))); +#endif + NextAction(); + ASSERT_EQ(ACTION_CLOSE, last_action_.type()); + + loop_->PostTask(FROM_HERE, base::Bind( + &TCPListenSocketTester::Shutdown, this)); + NextAction(); + ASSERT_EQ(ACTION_SHUTDOWN, last_action_.type()); + + thread_.reset(); + loop_ = NULL; +} + +void TCPListenSocketTester::ReportAction( + const TCPListenSocketTestAction& action) { + base::AutoLock locked(lock_); + queue_.push_back(action); + cv_.Broadcast(); +} + +void TCPListenSocketTester::NextAction() { + base::AutoLock locked(lock_); + while (queue_.empty()) + cv_.Wait(); + last_action_ = queue_.front(); + queue_.pop_front(); +} + +int TCPListenSocketTester::ClearTestSocket() { + char buf[kReadBufSize]; + int len_ret = 0; + do { + int len = HANDLE_EINTR(recv(test_socket_, buf, kReadBufSize, 0)); + if (len == SOCKET_ERROR || len == 0) { + break; + } else { + len_ret += len; + } + } while (true); + return len_ret; +} + +void TCPListenSocketTester::Shutdown() { + connection_->Release(); + connection_ = NULL; + server_->Release(); + server_ = NULL; + ReportAction(TCPListenSocketTestAction(ACTION_SHUTDOWN)); +} + +void TCPListenSocketTester::Listen() { + server_ = DoListen(); + if (server_) { + server_->AddRef(); + ReportAction(TCPListenSocketTestAction(ACTION_LISTEN)); + } +} + +void TCPListenSocketTester::SendFromTester() { + connection_->Send(kHelloWorld); + ReportAction(TCPListenSocketTestAction(ACTION_SEND)); +} + +void TCPListenSocketTester::TestClientSend() { + ASSERT_TRUE(Send(test_socket_, kHelloWorld)); + NextAction(); + ASSERT_EQ(ACTION_READ, last_action_.type()); + ASSERT_EQ(last_action_.data(), kHelloWorld); +} + +void TCPListenSocketTester::TestClientSendLong() { + size_t hello_len = strlen(kHelloWorld); + std::string long_string; + size_t long_len = 0; + for (int i = 0; i < 200; i++) { + long_string += kHelloWorld; + long_len += hello_len; + } + ASSERT_TRUE(Send(test_socket_, long_string)); + size_t read_len = 0; + while (read_len < long_len) { + NextAction(); + ASSERT_EQ(ACTION_READ, last_action_.type()); + std::string last_data = last_action_.data(); + size_t len = last_data.length(); + if (long_string.compare(read_len, len, last_data)) { + ASSERT_EQ(long_string.compare(read_len, len, last_data), 0); + } + read_len += last_data.length(); + } + ASSERT_EQ(read_len, long_len); +} + +void TCPListenSocketTester::TestServerSend() { + loop_->PostTask(FROM_HERE, base::Bind( + &TCPListenSocketTester::SendFromTester, this)); + NextAction(); + ASSERT_EQ(ACTION_SEND, last_action_.type()); + const int buf_len = 200; + char buf[buf_len+1]; + unsigned recv_len = 0; + while (recv_len < strlen(kHelloWorld)) { + int r = HANDLE_EINTR(recv(test_socket_, buf, buf_len, 0)); + ASSERT_GE(r, 0); + recv_len += static_cast(r); + if (!r) + break; + } + buf[recv_len] = 0; + ASSERT_STREQ(buf, kHelloWorld); +} + +bool TCPListenSocketTester::Send(SOCKET sock, const std::string& str) { + int len = static_cast(str.length()); + int send_len = HANDLE_EINTR(send(sock, str.data(), len, 0)); + if (send_len == SOCKET_ERROR) { + LOG(ERROR) << "send failed: " << errno; + return false; + } else if (send_len != len) { + return false; + } + return true; +} + +void TCPListenSocketTester::DidAccept(ListenSocket *server, + ListenSocket *connection) { + connection_ = connection; + connection_->AddRef(); + ReportAction(TCPListenSocketTestAction(ACTION_ACCEPT)); +} + +void TCPListenSocketTester::DidRead(ListenSocket *connection, + const char* data, + int len) { + std::string str(data, len); + ReportAction(TCPListenSocketTestAction(ACTION_READ, str)); +} + +void TCPListenSocketTester::DidClose(ListenSocket *sock) { + ReportAction(TCPListenSocketTestAction(ACTION_CLOSE)); +} + +TCPListenSocketTester::~TCPListenSocketTester() {} + +TCPListenSocket* TCPListenSocketTester::DoListen() { + return TCPListenSocket::CreateAndListen(kLoopback, kTestPort, this); +} + +class TCPListenSocketTest: public PlatformTest { + public: + TCPListenSocketTest() { + tester_ = NULL; + } + + virtual void SetUp() { + PlatformTest::SetUp(); + tester_ = new TCPListenSocketTester(); + tester_->SetUp(); + } + + virtual void TearDown() { + PlatformTest::TearDown(); + tester_->TearDown(); + tester_ = NULL; + } + + scoped_refptr tester_; +}; + +TEST_F(TCPListenSocketTest, ClientSend) { + tester_->TestClientSend(); +} + +TEST_F(TCPListenSocketTest, ClientSendLong) { + tester_->TestClientSendLong(); +} + +TEST_F(TCPListenSocketTest, ServerSend) { + tester_->TestServerSend(); +} + +} // namespace net diff --git a/net/base/tcp_listen_socket_unittest.h b/net/base/tcp_listen_socket_unittest.h new file mode 100644 index 0000000..4e24b30 --- /dev/null +++ b/net/base/tcp_listen_socket_unittest.h @@ -0,0 +1,127 @@ +// Copyright (c) 2012 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_BASE_LISTEN_SOCKET_UNITTEST_H_ +#define NET_BASE_LISTEN_SOCKET_UNITTEST_H_ +#pragma once + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_POSIX) +#include +#include +#include +#endif + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/synchronization/condition_variable.h" +#include "base/synchronization/lock.h" +#include "base/threading/thread.h" +#include "net/base/net_util.h" +#include "net/base/tcp_listen_socket.h" +#include "net/base/winsock_init.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(OS_POSIX) +// Used same name as in Windows to avoid #ifdef where referenced +#define SOCKET int +const int INVALID_SOCKET = -1; +const int SOCKET_ERROR = -1; +#endif + +namespace net { + +enum ActionType { + ACTION_NONE = 0, + ACTION_LISTEN = 1, + ACTION_ACCEPT = 2, + ACTION_READ = 3, + ACTION_SEND = 4, + ACTION_CLOSE = 5, + ACTION_SHUTDOWN = 6 +}; + +class TCPListenSocketTestAction { + public: + TCPListenSocketTestAction() : action_(ACTION_NONE) {} + explicit TCPListenSocketTestAction(ActionType action) : action_(action) {} + TCPListenSocketTestAction(ActionType action, std::string data) + : action_(action), + data_(data) {} + + const std::string data() const { return data_; } + ActionType type() const { return action_; } + + private: + ActionType action_; + std::string data_; +}; + + +// This had to be split out into a separate class because I couldn't +// make the testing::Test class refcounted. +class TCPListenSocketTester : + public ListenSocket::ListenSocketDelegate, + public base::RefCountedThreadSafe { + + public: + TCPListenSocketTester(); + + void SetUp(); + void TearDown(); + + void ReportAction(const TCPListenSocketTestAction& action); + void NextAction(); + + // read all pending data from the test socket + int ClearTestSocket(); + // Release the connection and server sockets + void Shutdown(); + void Listen(); + void SendFromTester(); + // verify the send/read from client to server + void TestClientSend(); + // verify send/read of a longer string + void TestClientSendLong(); + // verify a send/read from server to client + void TestServerSend(); + + virtual bool Send(SOCKET sock, const std::string& str); + + // ListenSocket::ListenSocketDelegate: + virtual void DidAccept(ListenSocket *server, + ListenSocket *connection) OVERRIDE; + virtual void DidRead(ListenSocket *connection, const char* data, + int len) OVERRIDE; + virtual void DidClose(ListenSocket *sock) OVERRIDE; + + scoped_ptr thread_; + MessageLoopForIO* loop_; + TCPListenSocket* server_; + ListenSocket* connection_; + TCPListenSocketTestAction last_action_; + + SOCKET test_socket_; + static const int kTestPort; + + base::Lock lock_; // protects |queue_| and wraps |cv_| + base::ConditionVariable cv_; + std::deque queue_; + + protected: + friend class base::RefCountedThreadSafe; + + virtual ~TCPListenSocketTester(); + + virtual TCPListenSocket* DoListen(); +}; + +} // namespace net + +#endif // NET_BASE_LISTEN_SOCKET_UNITTEST_H_ diff --git a/net/net.gyp b/net/net.gyp index 6a33eda..21556b4 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -112,9 +112,9 @@ 'base/escape.cc', 'base/escape.h', 'base/escape_icu.cc', - 'base/expiring_cache.h', 'base/ev_root_ca_metadata.cc', 'base/ev_root_ca_metadata.h', + 'base/expiring_cache.h', 'base/file_stream.cc', 'base/file_stream.h', 'base/file_stream_metrics.cc', @@ -247,6 +247,8 @@ 'base/static_cookie_policy.cc', 'base/static_cookie_policy.h', 'base/sys_addrinfo.h', + 'base/tcp_listen_socket.cc', + 'base/tcp_listen_socket.h', 'base/test_data_stream.cc', 'base/test_data_stream.h', 'base/test_root_certs.cc', @@ -1057,8 +1059,6 @@ 'base/host_resolver_impl_unittest.cc', 'base/ip_endpoint_unittest.cc', 'base/keygen_handler_unittest.cc', - 'base/listen_socket_unittest.cc', - 'base/listen_socket_unittest.h', 'base/mapped_host_resolver_unittest.cc', 'base/mime_sniffer_unittest.cc', 'base/mime_util_unittest.cc', @@ -1082,6 +1082,8 @@ 'base/ssl_client_auth_cache_unittest.cc', 'base/ssl_config_service_unittest.cc', 'base/static_cookie_policy_unittest.cc', + 'base/tcp_listen_socket_unittest.cc', + 'base/tcp_listen_socket_unittest.h', 'base/test_certificate_data.h', 'base/test_completion_callback_unittest.cc', 'base/transport_security_state_unittest.cc', diff --git a/net/server/http_server.cc b/net/server/http_server.cc index f56ff96..ad1fa6e 100644 --- a/net/server/http_server.cc +++ b/net/server/http_server.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -10,6 +10,7 @@ #include "base/stringprintf.h" #include "base/sys_byteorder.h" #include "build/build_config.h" +#include "net/base/tcp_listen_socket.h" #include "net/server/http_connection.h" #include "net/server/http_server_request_info.h" #include "net/server/web_socket.h" @@ -20,7 +21,7 @@ HttpServer::HttpServer(const std::string& host, int port, HttpServer::Delegate* del) : delegate_(del) { - server_ = ListenSocket::Listen(host, port, this); + server_ = TCPListenSocket::CreateAndListen(host, port, this); } HttpServer::~HttpServer() { diff --git a/net/socket/transport_client_socket_unittest.cc b/net/socket/transport_client_socket_unittest.cc index 4ba4c000..9ab9044 100644 --- a/net/socket/transport_client_socket_unittest.cc +++ b/net/socket/transport_client_socket_unittest.cc @@ -1,17 +1,19 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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.h" #include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" #include "net/base/address_list.h" #include "net/base/host_resolver.h" #include "net/base/io_buffer.h" -#include "net/base/listen_socket.h" #include "net/base/net_log.h" #include "net/base/net_log_unittest.h" #include "net/base/net_errors.h" +#include "net/base/tcp_listen_socket.h" #include "net/base/test_completion_callback.h" #include "net/base/winsock_init.h" #include "net/socket/client_socket_factory.h" @@ -45,7 +47,7 @@ class TransportClientSocketTest // Implement ListenSocketDelegate methods virtual void DidAccept(ListenSocket* server, ListenSocket* connection) { - connected_sock_ = connection; + connected_sock_ = reinterpret_cast(connection); } virtual void DidRead(ListenSocket*, const char* str, int len) { // TODO(dkegel): this might not be long enough to tickle some bugs. @@ -90,8 +92,8 @@ class TransportClientSocketTest scoped_ptr sock_; private: - scoped_refptr listen_sock_; - scoped_refptr connected_sock_; + scoped_refptr listen_sock_; + scoped_refptr connected_sock_; bool close_server_socket_on_next_send_; }; @@ -99,7 +101,7 @@ void TransportClientSocketTest::SetUp() { ::testing::TestWithParam::SetUp(); // Find a free port to listen on - ListenSocket *sock = NULL; + TCPListenSocket *sock = NULL; int port; // Range of ports to listen on. Shouldn't need to try many. const int kMinPort = 10100; @@ -108,7 +110,7 @@ void TransportClientSocketTest::SetUp() { EnsureWinsockInit(); #endif for (port = kMinPort; port < kMaxPort; port++) { - sock = ListenSocket::Listen("127.0.0.1", port, this); + sock = TCPListenSocket::CreateAndListen("127.0.0.1", port, this); if (sock) break; } diff --git a/net/tools/fetch/http_listen_socket.cc b/net/tools/fetch/http_listen_socket.cc index fc9f5f3..60c6f94 100644 --- a/net/tools/fetch/http_listen_socket.cc +++ b/net/tools/fetch/http_listen_socket.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -16,7 +16,7 @@ // must run in the IO thread HttpListenSocket::HttpListenSocket(SOCKET s, HttpListenSocket::Delegate* delegate) - : ALLOW_THIS_IN_INITIALIZER_LIST(net::ListenSocket(s, this)), + : ALLOW_THIS_IN_INITIALIZER_LIST(net::TCPListenSocket(s, this)), delegate_(delegate) { } @@ -25,13 +25,13 @@ HttpListenSocket::~HttpListenSocket() { } void HttpListenSocket::Listen() { - net::ListenSocket::Listen(); + net::TCPListenSocket::Listen(); } void HttpListenSocket::Accept() { - SOCKET conn = net::ListenSocket::Accept(socket_); - DCHECK_NE(conn, net::ListenSocket::kInvalidSocket); - if (conn == net::ListenSocket::kInvalidSocket) { + SOCKET conn = net::TCPListenSocket::Accept(socket_); + DCHECK_NE(conn, net::TCPListenSocket::kInvalidSocket); + if (conn == net::TCPListenSocket::kInvalidSocket) { // TODO } else { scoped_refptr sock( @@ -45,8 +45,8 @@ HttpListenSocket* HttpListenSocket::Listen( const std::string& ip, int port, HttpListenSocket::Delegate* delegate) { - SOCKET s = net::ListenSocket::Listen(ip, port); - if (s == net::ListenSocket::kInvalidSocket) { + SOCKET s = net::TCPListenSocket::CreateAndBind(ip, port); + if (s == net::TCPListenSocket::kInvalidSocket) { // TODO (ibrar): error handling } else { HttpListenSocket *serv = new HttpListenSocket(s, delegate); diff --git a/net/tools/fetch/http_listen_socket.h b/net/tools/fetch/http_listen_socket.h index 18e3a46..8a09a9e 100644 --- a/net/tools/fetch/http_listen_socket.h +++ b/net/tools/fetch/http_listen_socket.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -7,13 +7,13 @@ #pragma once #include "base/message_loop.h" -#include "net/base/listen_socket.h" +#include "net/base/tcp_listen_socket.h" class HttpServerRequestInfo; class HttpServerResponseInfo; // Implements a simple HTTP listen socket on top of the raw socket interface. -class HttpListenSocket : public net::ListenSocket, +class HttpListenSocket : public net::TCPListenSocket, public net::ListenSocket::ListenSocketDelegate { public: class Delegate { -- cgit v1.1