diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-18 23:03:32 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-18 23:03:32 +0000 |
commit | 3871252036dec25ea2dca4622be40264a25b61d3 (patch) | |
tree | 67d45091d78abb5234b286437567f217dcd275cf /net/socket | |
parent | 2b4f812537173e2ab27a9f9b94de3b4da4a5d22e (diff) | |
download | chromium_src-3871252036dec25ea2dca4622be40264a25b61d3.zip chromium_src-3871252036dec25ea2dca4622be40264a25b61d3.tar.gz chromium_src-3871252036dec25ea2dca4622be40264a25b61d3.tar.bz2 |
Added TCPServerSocket.
BUG=None
TEST=Unittests
Review URL: http://codereview.chromium.org/6820057
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@82020 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/socket')
-rw-r--r-- | net/socket/server_socket.h | 39 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_libevent.h | 3 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_win.cc | 1 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_win.h | 3 | ||||
-rw-r--r-- | net/socket/tcp_server_socket.h | 26 | ||||
-rw-r--r-- | net/socket/tcp_server_socket_libevent.cc | 194 | ||||
-rw-r--r-- | net/socket/tcp_server_socket_libevent.h | 53 | ||||
-rw-r--r-- | net/socket/tcp_server_socket_unittest.cc | 146 | ||||
-rw-r--r-- | net/socket/tcp_server_socket_win.cc | 185 | ||||
-rw-r--r-- | net/socket/tcp_server_socket_win.h | 55 |
10 files changed, 703 insertions, 2 deletions
diff --git a/net/socket/server_socket.h b/net/socket/server_socket.h new file mode 100644 index 0000000..f037b036 --- /dev/null +++ b/net/socket/server_socket.h @@ -0,0 +1,39 @@ +// 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_SERVER_SOCKET_H_ +#define NET_SOCKET_SERVER_SOCKET_H_ + +#include "base/scoped_ptr.h" +#include "net/base/completion_callback.h" + +namespace net { + +class ClientSocket; +class IPEndPoint; + +class ServerSocket { + public: + ServerSocket() { } + virtual ~ServerSocket() { } + + // Bind the socket and start listening. Destroy the socket to stop + // listening. + virtual int Listen(const net::IPEndPoint& address, int backlog) = 0; + + // Gets current address the socket is bound to. + virtual int GetLocalAddress(IPEndPoint* address) const = 0; + + // Accept connection. Callback is called when new connection is + // accepted. + virtual int Accept(scoped_ptr<ClientSocket>* socket, + CompletionCallback* callback) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ServerSocket); +}; + +} // namespace net + +#endif // NET_SOCKET_TCP_SERVER_SOCKET_H_ diff --git a/net/socket/tcp_client_socket_libevent.h b/net/socket/tcp_client_socket_libevent.h index 2d9ea2b..796a4a8 100644 --- a/net/socket/tcp_client_socket_libevent.h +++ b/net/socket/tcp_client_socket_libevent.h @@ -36,7 +36,8 @@ class TCPClientSocketLibevent : public ClientSocket, base::NonThreadSafe { // AdoptSocket causes the given, connected socket to be adopted as a TCP // socket. This object must not be connected. This object takes ownership of // the given socket and then acts as if Connect() had been called. This - // function is intended for testing only. + // function is used by TCPServerSocket() to adopt accepted connections + // and for testing. void AdoptSocket(int socket); // ClientSocket methods: diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc index 9d28254..eec7dbb 100644 --- a/net/socket/tcp_client_socket_win.cc +++ b/net/socket/tcp_client_socket_win.cc @@ -239,6 +239,7 @@ void TCPClientSocketWin::AdoptSocket(SOCKET socket) { socket_ = socket; int error = SetupSocket(); DCHECK_EQ(0, error); + core_ = new Core(this); current_ai_ = addresses_.head(); use_history_.set_was_ever_connected(); } diff --git a/net/socket/tcp_client_socket_win.h b/net/socket/tcp_client_socket_win.h index aa8abd1..bca0d30 100644 --- a/net/socket/tcp_client_socket_win.h +++ b/net/socket/tcp_client_socket_win.h @@ -32,7 +32,8 @@ class TCPClientSocketWin : public ClientSocket, base::NonThreadSafe { // AdoptSocket causes the given, connected socket to be adopted as a TCP // socket. This object must not be connected. This object takes ownership of // the given socket and then acts as if Connect() had been called. This - // function is intended for testing only. + // function is used by TCPServerSocket() to adopt accepted connections + // and for testing. void AdoptSocket(SOCKET socket); // ClientSocket methods: diff --git a/net/socket/tcp_server_socket.h b/net/socket/tcp_server_socket.h new file mode 100644 index 0000000..4970a15 --- /dev/null +++ b/net/socket/tcp_server_socket.h @@ -0,0 +1,26 @@ +// 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_TCP_SERVER_SOCKET_H_ +#define NET_SOCKET_TCP_SERVER_SOCKET_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "net/socket/tcp_server_socket_win.h" +#elif defined(OS_POSIX) +#include "net/socket/tcp_server_socket_libevent.h" +#endif + +namespace net { + +#if defined(OS_WIN) +typedef TCPServerSocketWin TCPServerSocket; +#elif defined(OS_POSIX) +typedef TCPServerSocketLibevent TCPServerSocket; +#endif + +} // namespace net + +#endif // NET_SOCKET_TCP_SERVER_SOCKET_H_ diff --git a/net/socket/tcp_server_socket_libevent.cc b/net/socket/tcp_server_socket_libevent.cc new file mode 100644 index 0000000..40a01f0 --- /dev/null +++ b/net/socket/tcp_server_socket_libevent.cc @@ -0,0 +1,194 @@ +// 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/socket/tcp_server_socket_libevent.h" + +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <sys/socket.h> + +#include "build/build_config.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 + +#include "base/eintr_wrapper.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/base/net_util.h" +#include "net/socket/tcp_client_socket.h" + +namespace net { + +namespace { + +const int kInvalidSocket = -1; + +} // namespace + +TCPServerSocketLibevent::TCPServerSocketLibevent( + net::NetLog* net_log, + const net::NetLog::Source& source) + : socket_(kInvalidSocket), + accept_socket_(NULL), + accept_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); +} + +TCPServerSocketLibevent::~TCPServerSocketLibevent() { + if (socket_ != kInvalidSocket) + Close(); + net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE, NULL); +} + +int TCPServerSocketLibevent::Listen(const IPEndPoint& address, int backlog) { + DCHECK(CalledOnValidThread()); + DCHECK_GT(backlog, 0); + DCHECK_EQ(socket_, kInvalidSocket); + + socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (socket_ < 0) { + PLOG(ERROR) << "socket() returned an error"; + return MapSystemError(errno); + } + + if (SetNonBlocking(socket_)) { + int result = MapSystemError(errno); + Close(); + return result; + } + + struct sockaddr_storage addr_storage; + size_t addr_len = sizeof(addr_storage); + struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + if (!address.ToSockAddr(addr, &addr_len)) + return ERR_INVALID_ARGUMENT; + + int result = bind(socket_, addr, addr_len); + if (result < 0) { + PLOG(ERROR) << "bind() returned an error"; + result = MapSystemError(errno); + Close(); + return result; + } + + result = listen(socket_, backlog); + if (result < 0) { + PLOG(ERROR) << "listen() returned an error"; + result = MapSystemError(errno); + Close(); + return result; + } + + return OK; +} + +int TCPServerSocketLibevent::GetLocalAddress(IPEndPoint* address) const { + DCHECK(CalledOnValidThread()); + DCHECK(address); + + 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) < 0) + return MapSystemError(errno); + if (!address->FromSockAddr(addr, addr_len)) + return ERR_FAILED; + + return OK; +} + +int TCPServerSocketLibevent::Accept( + scoped_ptr<ClientSocket>* socket, CompletionCallback* callback) { + DCHECK(CalledOnValidThread()); + DCHECK(socket); + DCHECK(callback); + DCHECK(!accept_callback_); + + net_log_.BeginEvent(NetLog::TYPE_TCP_ACCEPT, NULL); + + int result = AcceptInternal(socket); + + if (result == ERR_IO_PENDING) { + if (!MessageLoopForIO::current()->WatchFileDescriptor( + socket_, true, MessageLoopForIO::WATCH_READ, + &accept_socket_watcher_, this)) { + PLOG(ERROR) << "WatchFileDescriptor failed on read"; + return MapSystemError(errno); + } + + accept_socket_ = socket; + accept_callback_ = callback; + } + + return result; +} + +int TCPServerSocketLibevent::AcceptInternal( + scoped_ptr<ClientSocket>* socket) { + struct sockaddr_storage addr_storage; + socklen_t addr_len = sizeof(addr_storage); + struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + + int result = HANDLE_EINTR(accept(socket_, addr, &addr_len)); + if (result < 0) { + int net_error = MapSystemError(errno); + if (net_error != ERR_IO_PENDING) + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, net_error); + return net_error; + } + + IPEndPoint address; + if (!address.FromSockAddr(addr, addr_len)) { + NOTREACHED(); + HANDLE_EINTR(close(result)); + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, ERR_FAILED); + return ERR_FAILED; + } + TCPClientSocket* tcp_socket = new TCPClientSocket( + AddressList(address.address(), address.port(), false), + net_log_.net_log(), net_log_.source()); + tcp_socket->AdoptSocket(result); + socket->reset(tcp_socket); + net_log_.EndEvent(NetLog::TYPE_TCP_ACCEPT, + make_scoped_refptr(new NetLogStringParameter( + "address", address.ToString()))); + return OK; +} + +void TCPServerSocketLibevent::Close() { + if (socket_ != kInvalidSocket) { + HANDLE_EINTR(close(socket_)); + socket_ = kInvalidSocket; + } +} + +void TCPServerSocketLibevent::OnFileCanReadWithoutBlocking(int fd) { + DCHECK(CalledOnValidThread()); + + int result = AcceptInternal(accept_socket_); + if (result != ERR_IO_PENDING) { + CompletionCallback* c = accept_callback_; + accept_callback_ = NULL; + accept_socket_ = NULL; + c->Run(result); + } +} + +void TCPServerSocketLibevent::OnFileCanWriteWithoutBlocking(int fd) { + NOTREACHED(); +} + +} // namespace net diff --git a/net/socket/tcp_server_socket_libevent.h b/net/socket/tcp_server_socket_libevent.h new file mode 100644 index 0000000..9d30953 --- /dev/null +++ b/net/socket/tcp_server_socket_libevent.h @@ -0,0 +1,53 @@ +// 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_TCP_SERVER_SOCKET_LIBEVENT_H_ +#define NET_SOCKET_TCP_SERVER_SOCKET_LIBEVENT_H_ + +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/threading/non_thread_safe.h" +#include "net/base/completion_callback.h" +#include "net/base/net_log.h" +#include "net/socket/server_socket.h" + +namespace net { + +class IPEndPoint; + +class TCPServerSocketLibevent : public ServerSocket, + public base::NonThreadSafe, + public MessageLoopForIO::Watcher { + public: + TCPServerSocketLibevent(net::NetLog* net_log, + const net::NetLog::Source& source); + ~TCPServerSocketLibevent(); + + // net::ServerSocket implementation. + virtual int Listen(const net::IPEndPoint& address, int backlog); + virtual int GetLocalAddress(IPEndPoint* address) const; + virtual int Accept(scoped_ptr<ClientSocket>* socket, + CompletionCallback* callback); + + // MessageLoopForIO::Watcher implementation. + virtual void OnFileCanReadWithoutBlocking(int fd); + virtual void OnFileCanWriteWithoutBlocking(int fd); + + private: + int AcceptInternal(scoped_ptr<ClientSocket>* socket); + void Close(); + + int socket_; + + MessageLoopForIO::FileDescriptorWatcher accept_socket_watcher_; + + scoped_ptr<ClientSocket>* accept_socket_; + CompletionCallback* accept_callback_; + + BoundNetLog net_log_; +}; + +} // namespace net + +#endif // NET_SOCKET_TCP_SERVER_SOCKET_LIBEVENT_H_ diff --git a/net/socket/tcp_server_socket_unittest.cc b/net/socket/tcp_server_socket_unittest.cc new file mode 100644 index 0000000..59fa4c0 --- /dev/null +++ b/net/socket/tcp_server_socket_unittest.cc @@ -0,0 +1,146 @@ +// 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/socket/tcp_server_socket.h" + +#include "base/compiler_specific.h" +#include "base/scoped_ptr.h" +#include "net/base/address_list.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/base/sys_addrinfo.h" +#include "net/base/test_completion_callback.h" +#include "net/socket/tcp_client_socket.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace net { + +namespace { +const int kListenBacklog = 5; + +class TCPServerSocketTest : public PlatformTest { + protected: + TCPServerSocketTest() + : socket_(NULL, NetLog::Source()) { + } + + void SetUp() OVERRIDE { + IPEndPoint address; + ParseAddress("127.0.0.1", 0, &address); + ASSERT_EQ(OK, socket_.Listen(address, kListenBacklog)); + ASSERT_EQ(OK, socket_.GetLocalAddress(&local_address_)); + } + + void ParseAddress(std::string ip_str, int port, IPEndPoint* address) { + IPAddressNumber ip_number; + bool rv = ParseIPLiteralToNumber(ip_str, &ip_number); + if (!rv) + return; + *address = IPEndPoint(ip_number, port); + } + + static IPEndPoint GetPeerAddress(ClientSocket* socket) { + AddressList address; + EXPECT_EQ(OK, socket->GetPeerAddress(&address)); + IPEndPoint endpoint; + EXPECT_TRUE(endpoint.FromSockAddr( + address.head()->ai_addr, address.head()->ai_addrlen)); + return endpoint; + } + + TCPServerSocket socket_; + IPEndPoint local_address_; +}; + +TEST_F(TCPServerSocketTest, Accept) { + TestCompletionCallback connect_callback; + TCPClientSocket connecting_socket(AddressList(local_address_.address(), + local_address_.port(), false), + NULL, NetLog::Source()); + connecting_socket.Connect(&connect_callback); + + TestCompletionCallback accept_callback; + scoped_ptr<ClientSocket> accepted_socket; + int result = socket_.Accept(&accepted_socket, &accept_callback); + if (result == ERR_IO_PENDING) + result = accept_callback.WaitForResult(); + ASSERT_EQ(OK, result); + + ASSERT_TRUE(accepted_socket.get() != NULL); + + // Both sockets should be on the loopback network interface. + EXPECT_EQ(GetPeerAddress(accepted_socket.get()).address(), + local_address_.address()); + + EXPECT_EQ(OK, connect_callback.WaitForResult()); +} + +// Test Accept() callback. +TEST_F(TCPServerSocketTest, AcceptAsync) { + TestCompletionCallback accept_callback; + scoped_ptr<ClientSocket> accepted_socket; + + ASSERT_EQ(ERR_IO_PENDING, socket_.Accept(&accepted_socket, &accept_callback)); + + TestCompletionCallback connect_callback; + TCPClientSocket connecting_socket(AddressList(local_address_.address(), + local_address_.port(), false), + NULL, NetLog::Source()); + connecting_socket.Connect(&connect_callback); + + EXPECT_EQ(OK, connect_callback.WaitForResult()); + EXPECT_EQ(OK, accept_callback.WaitForResult()); + + EXPECT_TRUE(accepted_socket != NULL); + + // Both sockets should be on the loopback network interface. + EXPECT_EQ(GetPeerAddress(accepted_socket.get()).address(), + local_address_.address()); +} + +// Accept two connections simultaneously. +TEST_F(TCPServerSocketTest, Accept2Connections) { + TestCompletionCallback accept_callback; + scoped_ptr<ClientSocket> accepted_socket; + + ASSERT_EQ(ERR_IO_PENDING, + socket_.Accept(&accepted_socket, &accept_callback)); + + TestCompletionCallback connect_callback; + TCPClientSocket connecting_socket(AddressList(local_address_.address(), + local_address_.port(), false), + NULL, NetLog::Source()); + connecting_socket.Connect(&connect_callback); + + TestCompletionCallback connect_callback2; + TCPClientSocket connecting_socket2(AddressList(local_address_.address(), + local_address_.port(), false), + NULL, NetLog::Source()); + connecting_socket2.Connect(&connect_callback2); + + EXPECT_EQ(OK, accept_callback.WaitForResult()); + + TestCompletionCallback accept_callback2; + scoped_ptr<ClientSocket> accepted_socket2; + int result = socket_.Accept(&accepted_socket2, &accept_callback2); + if (result == ERR_IO_PENDING) + result = accept_callback2.WaitForResult(); + ASSERT_EQ(OK, result); + + EXPECT_EQ(OK, connect_callback.WaitForResult()); + + EXPECT_TRUE(accepted_socket != NULL); + EXPECT_TRUE(accepted_socket2 != NULL); + EXPECT_NE(accepted_socket.get(), accepted_socket2.get()); + + EXPECT_EQ(GetPeerAddress(accepted_socket.get()).address(), + local_address_.address()); + EXPECT_EQ(GetPeerAddress(accepted_socket2.get()).address(), + local_address_.address()); +} + +} // namespace + +} // namespace net diff --git a/net/socket/tcp_server_socket_win.cc b/net/socket/tcp_server_socket_win.cc new file mode 100644 index 0000000..0edbe92 --- /dev/null +++ b/net/socket/tcp_server_socket_win.cc @@ -0,0 +1,185 @@ +// 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/socket/tcp_server_socket_win.h" + +#include <mstcpip.h> + +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/base/net_util.h" +#include "net/base/winsock_init.h" +#include "net/base/winsock_util.h" +#include "net/socket/tcp_client_socket.h" + +namespace net { + +TCPServerSocketWin::TCPServerSocketWin(net::NetLog* net_log, + const net::NetLog::Source& source) + : socket_(INVALID_SOCKET), + socket_event_(WSA_INVALID_EVENT), + accept_socket_(NULL), + accept_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); + EnsureWinsockInit(); +} + +TCPServerSocketWin::~TCPServerSocketWin() { + Close(); + net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE, NULL); +} + +int TCPServerSocketWin::Listen(const IPEndPoint& address, int backlog) { + DCHECK(CalledOnValidThread()); + DCHECK_GT(backlog, 0); + DCHECK_EQ(socket_, INVALID_SOCKET); + DCHECK_EQ(socket_event_, WSA_INVALID_EVENT); + + socket_event_ = WSACreateEvent(); + if (socket_event_ == WSA_INVALID_EVENT) { + PLOG(ERROR) << "WSACreateEvent()"; + return ERR_FAILED; + } + + socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (socket_ < 0) { + PLOG(ERROR) << "socket() returned an error"; + return MapSystemError(WSAGetLastError()); + } + + if (SetNonBlocking(socket_)) { + int result = MapSystemError(WSAGetLastError()); + Close(); + return result; + } + + struct sockaddr_storage addr_storage; + size_t addr_len = sizeof(addr_storage); + struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + if (!address.ToSockAddr(addr, &addr_len)) + return ERR_INVALID_ARGUMENT; + + int result = bind(socket_, addr, addr_len); + if (result < 0) { + PLOG(ERROR) << "bind() returned an error"; + result = MapSystemError(WSAGetLastError()); + Close(); + return result; + } + + result = listen(socket_, backlog); + if (result < 0) { + PLOG(ERROR) << "listen() returned an error"; + result = MapSystemError(WSAGetLastError()); + Close(); + return result; + } + + return OK; +} + +int TCPServerSocketWin::GetLocalAddress(IPEndPoint* address) const { + DCHECK(CalledOnValidThread()); + DCHECK(address); + + 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 MapSystemError(WSAGetLastError()); + if (!address->FromSockAddr(addr, addr_len)) + return ERR_FAILED; + + return OK; +} + +int TCPServerSocketWin::Accept( + scoped_ptr<ClientSocket>* socket, CompletionCallback* callback) { + DCHECK(CalledOnValidThread()); + DCHECK(socket); + DCHECK(callback); + DCHECK(!accept_callback_); + + net_log_.BeginEvent(NetLog::TYPE_TCP_ACCEPT, NULL); + + int result = AcceptInternal(socket); + + if (result == ERR_IO_PENDING) { + // Start watching + WSAEventSelect(socket_, socket_event_, FD_ACCEPT); + accept_watcher_.StartWatching(socket_event_, this); + + accept_socket_ = socket; + accept_callback_ = callback; + } + + return result; +} + +int TCPServerSocketWin::AcceptInternal(scoped_ptr<ClientSocket>* socket) { + struct sockaddr_storage addr_storage; + socklen_t addr_len = sizeof(addr_storage); + struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + + int result = accept(socket_, addr, &addr_len); + if (result < 0) { + int net_error = MapSystemError(WSAGetLastError()); + if (net_error != ERR_IO_PENDING) + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, net_error); + return net_error; + } + + IPEndPoint address; + if (!address.FromSockAddr(addr, addr_len)) { + NOTREACHED(); + closesocket(result); + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, ERR_FAILED); + return ERR_FAILED; + } + TCPClientSocket* tcp_socket = new TCPClientSocket( + AddressList(address.address(), address.port(), false), + net_log_.net_log(), net_log_.source()); + tcp_socket->AdoptSocket(result); + socket->reset(tcp_socket); + net_log_.EndEvent(NetLog::TYPE_TCP_ACCEPT, + make_scoped_refptr(new NetLogStringParameter( + "address", address.ToString()))); + return OK; +} + +void TCPServerSocketWin::Close() { + if (socket_ != INVALID_SOCKET) { + closesocket(socket_); + socket_ = INVALID_SOCKET; + } + + if (socket_event_) { + WSACloseEvent(socket_event_); + socket_event_ = WSA_INVALID_EVENT; + } +} + +void TCPServerSocketWin::OnObjectSignaled(HANDLE object) { + WSANETWORKEVENTS ev; + if (WSAEnumNetworkEvents(socket_, socket_event_, &ev) == SOCKET_ERROR) { + PLOG(ERROR) << "WSAEnumNetworkEvents()"; + return; + } + + if (ev.lNetworkEvents & FD_ACCEPT) { + int result = AcceptInternal(accept_socket_); + if (result != ERR_IO_PENDING) { + CompletionCallback* c = accept_callback_; + accept_callback_ = NULL; + accept_socket_ = NULL; + c->Run(result); + } + } +} + +} // namespace net diff --git a/net/socket/tcp_server_socket_win.h b/net/socket/tcp_server_socket_win.h new file mode 100644 index 0000000..fbe61fa --- /dev/null +++ b/net/socket/tcp_server_socket_win.h @@ -0,0 +1,55 @@ +// 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_TCP_SERVER_SOCKET_WIN_H_ +#define NET_SOCKET_TCP_SERVER_SOCKET_WIN_H_ + +#include <winsock2.h> + +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/win/object_watcher.h" +#include "base/threading/non_thread_safe.h" +#include "net/base/net_log.h" +#include "net/socket/server_socket.h" + +namespace net { + +class IPEndPoint; + +class TCPServerSocketWin : public ServerSocket, + public base::NonThreadSafe, + public base::win::ObjectWatcher::Delegate { + public: + TCPServerSocketWin(net::NetLog* net_log, + const net::NetLog::Source& source); + ~TCPServerSocketWin(); + + // net::ServerSocket implementation. + virtual int Listen(const net::IPEndPoint& address, int backlog); + virtual int GetLocalAddress(IPEndPoint* address) const; + virtual int Accept(scoped_ptr<ClientSocket>* socket, + CompletionCallback* callback); + + // base::ObjectWatcher::Delegate implementation. + virtual void OnObjectSignaled(HANDLE object); + + private: + int AcceptInternal(scoped_ptr<ClientSocket>* socket); + void Close(); + + SOCKET socket_; + HANDLE socket_event_; + + base::win::ObjectWatcher accept_watcher_; + + scoped_ptr<ClientSocket>* accept_socket_; + CompletionCallback* accept_callback_; + + BoundNetLog net_log_; +}; + +} // namespace net + +#endif // NET_SOCKET_TCP_SERVER_SOCKET_WIN_H_ |