diff --git a/net/net.gyp b/net/net.gyp index 2c6ef1520f479..1cce2a62858ca 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -909,8 +909,6 @@ 'socket/tcp_client_socket.h', 'socket/tcp_client_socket_libevent.cc', 'socket/tcp_client_socket_libevent.h', - 'socket/tcp_client_socket_win.cc', - 'socket/tcp_client_socket_win.h', 'socket/tcp_listen_socket.cc', 'socket/tcp_listen_socket.h', 'socket/tcp_server_socket.cc', diff --git a/net/socket/tcp_client_socket.cc b/net/socket/tcp_client_socket.cc index a91d33d24c8cb..af1c555caa0bd 100644 --- a/net/socket/tcp_client_socket.cc +++ b/net/socket/tcp_client_socket.cc @@ -4,8 +4,14 @@ #include "net/socket/tcp_client_socket.h" +#include "base/callback_helpers.h" #include "base/file_util.h" #include "base/files/file_path.h" +#include "base/logging.h" +#include "net/base/io_buffer.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/base/net_util.h" namespace net { @@ -56,4 +62,312 @@ bool IsTCPFastOpenEnabled() { return g_tcp_fastopen_enabled; } +#if defined(OS_WIN) + +TCPClientSocket::TCPClientSocket(const AddressList& addresses, + net::NetLog* net_log, + const net::NetLog::Source& source) + : socket_(new TCPSocket(net_log, source)), + addresses_(addresses), + current_address_index_(-1), + next_connect_state_(CONNECT_STATE_NONE), + previously_disconnected_(false) { +} + +TCPClientSocket::TCPClientSocket(scoped_ptr<TCPSocket> connected_socket, + const IPEndPoint& peer_address) + : socket_(connected_socket.Pass()), + addresses_(AddressList(peer_address)), + current_address_index_(0), + next_connect_state_(CONNECT_STATE_NONE), + previously_disconnected_(false) { + DCHECK(socket_); + + socket_->SetDefaultOptionsForClient(); + use_history_.set_was_ever_connected(); +} + +TCPClientSocket::~TCPClientSocket() { +} + +int TCPClientSocket::Bind(const IPEndPoint& address) { + if (current_address_index_ >= 0 || bind_address_) { + // Cannot bind the socket if we are already connected or connecting. + NOTREACHED(); + return ERR_UNEXPECTED; + } + + int result = OK; + if (!socket_->IsValid()) { + result = OpenSocket(address.GetFamily()); + if (result != OK) + return result; + } + + result = socket_->Bind(address); + if (result != OK) + return result; + + bind_address_.reset(new IPEndPoint(address)); + return OK; +} + +int TCPClientSocket::Connect(const CompletionCallback& callback) { + DCHECK(!callback.is_null()); + + // If connecting or already connected, then just return OK. + if (socket_->IsValid() && current_address_index_ >= 0) + return OK; + + socket_->StartLoggingMultipleConnectAttempts(addresses_); + + // We will try to connect to each address in addresses_. Start with the + // first one in the list. + next_connect_state_ = CONNECT_STATE_CONNECT; + current_address_index_ = 0; + + int rv = DoConnectLoop(OK); + if (rv == ERR_IO_PENDING) { + connect_callback_ = callback; + } else { + socket_->EndLoggingMultipleConnectAttempts(rv); + } + + return rv; +} + +int TCPClientSocket::DoConnectLoop(int result) { + DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE); + + int rv = result; + do { + ConnectState state = next_connect_state_; + next_connect_state_ = CONNECT_STATE_NONE; + switch (state) { + case CONNECT_STATE_CONNECT: + DCHECK_EQ(OK, rv); + rv = DoConnect(); + break; + case CONNECT_STATE_CONNECT_COMPLETE: + rv = DoConnectComplete(rv); + break; + default: + NOTREACHED() << "bad state " << state; + rv = ERR_UNEXPECTED; + break; + } + } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE); + + return rv; +} + +int TCPClientSocket::DoConnect() { + DCHECK_GE(current_address_index_, 0); + DCHECK_LT(current_address_index_, static_cast<int>(addresses_.size())); + + const IPEndPoint& endpoint = addresses_[current_address_index_]; + + if (previously_disconnected_) { + use_history_.Reset(); + previously_disconnected_ = false; + } + + next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE; + + if (socket_->IsValid()) { + DCHECK(bind_address_); + } else { + int result = OpenSocket(endpoint.GetFamily()); + if (result != OK) + return result; + + if (bind_address_) { + result = socket_->Bind(*bind_address_); + if (result != OK) { + socket_->Close(); + return result; + } + } + } + + // |socket_| is owned by this class and the callback won't be run once + // |socket_| is gone. Therefore, it is safe to use base::Unretained() here. + return socket_->Connect(endpoint, + base::Bind(&TCPClientSocket::DidCompleteConnect, + base::Unretained(this))); +} + +int TCPClientSocket::DoConnectComplete(int result) { + if (result == OK) { + use_history_.set_was_ever_connected(); + return OK; // Done! + } + + // Close whatever partially connected socket we currently have. + DoDisconnect(); + + // Try to fall back to the next address in the list. + if (current_address_index_ + 1 < static_cast<int>(addresses_.size())) { + next_connect_state_ = CONNECT_STATE_CONNECT; + ++current_address_index_; + return OK; + } + + // Otherwise there is nothing to fall back to, so give up. + return result; +} + +void TCPClientSocket::Disconnect() { + DoDisconnect(); + current_address_index_ = -1; + bind_address_.reset(); +} + +void TCPClientSocket::DoDisconnect() { + // If connecting or already connected, record that the socket has been + // disconnected. + previously_disconnected_ = socket_->IsValid() && current_address_index_ >= 0; + socket_->Close(); +} + +bool TCPClientSocket::IsConnected() const { + return socket_->IsConnected(); +} + +bool TCPClientSocket::IsConnectedAndIdle() const { + return socket_->IsConnectedAndIdle(); +} + +int TCPClientSocket::GetPeerAddress(IPEndPoint* address) const { + return socket_->GetPeerAddress(address); +} + +int TCPClientSocket::GetLocalAddress(IPEndPoint* address) const { + DCHECK(address); + + if (!socket_->IsValid()) { + if (bind_address_) { + *address = *bind_address_; + return OK; + } + return ERR_SOCKET_NOT_CONNECTED; + } + + return socket_->GetLocalAddress(address); +} + +const BoundNetLog& TCPClientSocket::NetLog() const { + return socket_->net_log(); +} + +void TCPClientSocket::SetSubresourceSpeculation() { + use_history_.set_subresource_speculation(); +} + +void TCPClientSocket::SetOmniboxSpeculation() { + use_history_.set_omnibox_speculation(); +} + +bool TCPClientSocket::WasEverUsed() const { + return use_history_.was_used_to_convey_data(); +} + +bool TCPClientSocket::UsingTCPFastOpen() const { + return socket_->UsingTCPFastOpen(); +} + +bool TCPClientSocket::WasNpnNegotiated() const { + return false; +} + +NextProto TCPClientSocket::GetNegotiatedProtocol() const { + return kProtoUnknown; +} + +bool TCPClientSocket::GetSSLInfo(SSLInfo* ssl_info) { + return false; +} + +int TCPClientSocket::Read(IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) { + DCHECK(!callback.is_null()); + + // |socket_| is owned by this class and the callback won't be run once + // |socket_| is gone. Therefore, it is safe to use base::Unretained() here. + CompletionCallback read_callback = base::Bind( + &TCPClientSocket::DidCompleteReadWrite, base::Unretained(this), callback); + int result = socket_->Read(buf, buf_len, read_callback); + if (result > 0) + use_history_.set_was_used_to_convey_data(); + + return result; +} + +int TCPClientSocket::Write(IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) { + DCHECK(!callback.is_null()); + + // |socket_| is owned by this class and the callback won't be run once + // |socket_| is gone. Therefore, it is safe to use base::Unretained() here. + CompletionCallback write_callback = base::Bind( + &TCPClientSocket::DidCompleteReadWrite, base::Unretained(this), callback); + int result = socket_->Write(buf, buf_len, write_callback); + if (result > 0) + use_history_.set_was_used_to_convey_data(); + + return result; +} + +bool TCPClientSocket::SetReceiveBufferSize(int32 size) { + return socket_->SetReceiveBufferSize(size); +} + +bool TCPClientSocket::SetSendBufferSize(int32 size) { + return socket_->SetSendBufferSize(size); +} + +bool TCPClientSocket::SetKeepAlive(bool enable, int delay) { + return socket_->SetKeepAlive(enable, delay); +} + +bool TCPClientSocket::SetNoDelay(bool no_delay) { + return socket_->SetNoDelay(no_delay); +} + +void TCPClientSocket::DidCompleteConnect(int result) { + DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE); + DCHECK_NE(result, ERR_IO_PENDING); + DCHECK(!connect_callback_.is_null()); + + result = DoConnectLoop(result); + if (result != ERR_IO_PENDING) { + socket_->EndLoggingMultipleConnectAttempts(result); + base::ResetAndReturn(&connect_callback_).Run(result); + } +} + +void TCPClientSocket::DidCompleteReadWrite(const CompletionCallback& callback, + int result) { + if (result > 0) + use_history_.set_was_used_to_convey_data(); + + callback.Run(result); +} + +int TCPClientSocket::OpenSocket(AddressFamily family) { + DCHECK(!socket_->IsValid()); + + int result = socket_->Open(family); + if (result != OK) + return result; + + socket_->SetDefaultOptionsForClient(); + + return OK; +} + +#endif + } // namespace net diff --git a/net/socket/tcp_client_socket.h b/net/socket/tcp_client_socket.h index 8a2c0cd73f06f..841bc81b7f31c 100644 --- a/net/socket/tcp_client_socket.h +++ b/net/socket/tcp_client_socket.h @@ -8,21 +8,27 @@ #include "build/build_config.h" #include "net/base/net_export.h" -#if defined(OS_WIN) -#include "net/socket/tcp_client_socket_win.h" -#elif defined(OS_POSIX) +// TODO(yzshen): Switch OS_POSIX to use the same platform-independent +// TCPClientSocket. +#if defined(OS_POSIX) + #include "net/socket/tcp_client_socket_libevent.h" + +#elif defined(OS_WIN) + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/address_list.h" +#include "net/base/completion_callback.h" +#include "net/base/net_log.h" +#include "net/socket/stream_socket.h" +#include "net/socket/tcp_socket.h" + #endif namespace net { -// A client socket that uses TCP as the transport layer. -#if defined(OS_WIN) -typedef TCPClientSocketWin TCPClientSocket; -#elif defined(OS_POSIX) -typedef TCPClientSocketLibevent TCPClientSocket; -#endif - // Enable/disable experimental TCP FastOpen option. // Not thread safe. Must be called during initialization/startup only. NET_EXPORT void SetTCPFastOpenEnabled(bool value); @@ -30,6 +36,111 @@ NET_EXPORT void SetTCPFastOpenEnabled(bool value); // Check if the TCP FastOpen option is enabled. bool IsTCPFastOpenEnabled(); +// A client socket that uses TCP as the transport layer. +#if defined(OS_POSIX) +typedef TCPClientSocketLibevent TCPClientSocket; +#elif defined(OS_WIN) + +class NET_EXPORT TCPClientSocket : public StreamSocket { + public: + // The IP address(es) and port number to connect to. The TCP socket will try + // each IP address in the list until it succeeds in establishing a + // connection. + TCPClientSocket(const AddressList& addresses, + net::NetLog* net_log, + const net::NetLog::Source& source); + + // Adopts the given, connected socket and then acts as if Connect() had been + // called. This function is used by TCPServerSocket and for testing. + TCPClientSocket(scoped_ptr<TCPSocket> connected_socket, + const IPEndPoint& peer_address); + + virtual ~TCPClientSocket(); + + // Binds the socket to a local IP address and port. + int Bind(const IPEndPoint& address); + + // StreamSocket implementation. + virtual int Connect(const CompletionCallback& callback) OVERRIDE; + virtual void Disconnect() OVERRIDE; + virtual bool IsConnected() const OVERRIDE; + virtual bool IsConnectedAndIdle() const OVERRIDE; + virtual int GetPeerAddress(IPEndPoint* address) const OVERRIDE; + virtual int GetLocalAddress(IPEndPoint* address) const OVERRIDE; + virtual const BoundNetLog& NetLog() const OVERRIDE; + virtual void SetSubresourceSpeculation() OVERRIDE; + virtual void SetOmniboxSpeculation() OVERRIDE; + virtual bool WasEverUsed() const OVERRIDE; + virtual bool UsingTCPFastOpen() const OVERRIDE; + virtual bool WasNpnNegotiated() const OVERRIDE; + virtual NextProto GetNegotiatedProtocol() const OVERRIDE; + virtual bool GetSSLInfo(SSLInfo* ssl_info) OVERRIDE; + + // Socket implementation. + // Multiple outstanding requests are not supported. + // Full duplex mode (reading and writing at the same time) is supported. + virtual int Read(IOBuffer* buf, int buf_len, + const CompletionCallback& callback) OVERRIDE; + virtual int Write(IOBuffer* buf, int buf_len, + const CompletionCallback& callback) OVERRIDE; + virtual bool SetReceiveBufferSize(int32 size) OVERRIDE; + virtual bool SetSendBufferSize(int32 size) OVERRIDE; + + virtual bool SetKeepAlive(bool enable, int delay); + virtual bool SetNoDelay(bool no_delay); + + private: + // State machine for connecting the socket. + enum ConnectState { + CONNECT_STATE_CONNECT, + CONNECT_STATE_CONNECT_COMPLETE, + CONNECT_STATE_NONE, + }; + + // State machine used by Connect(). + int DoConnectLoop(int result); + int DoConnect(); + int DoConnectComplete(int result); + + // Helper used by Disconnect(), which disconnects minus resetting + // current_address_index_ and bind_address_. + void DoDisconnect(); + + void DidCompleteConnect(int result); + void DidCompleteReadWrite(const CompletionCallback& callback, int result); + + int OpenSocket(AddressFamily family); + + scoped_ptr<TCPSocket> socket_; + + // Local IP address and port we are bound to. Set to NULL if Bind() + // wasn't called (in that case OS chooses address/port). + scoped_ptr<IPEndPoint> bind_address_; + + // The list of addresses we should try in order to establish a connection. + AddressList addresses_; + + // Where we are in above list. Set to -1 if uninitialized. + int current_address_index_; + + // External callback; called when connect is complete. + CompletionCallback connect_callback_; + + // The next state for the Connect() state machine. + ConnectState next_connect_state_; + + // This socket was previously disconnected and has not been re-connected. + bool previously_disconnected_; + + // Record of connectivity and transmissions, for use in speculative connection + // histograms. + UseHistory use_history_; + + DISALLOW_COPY_AND_ASSIGN(TCPClientSocket); +}; + +#endif + } // namespace net #endif // NET_SOCKET_TCP_CLIENT_SOCKET_H_ diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc deleted file mode 100644 index f1334e78a468b..0000000000000 --- a/net/socket/tcp_client_socket_win.cc +++ /dev/null @@ -1,956 +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/socket/tcp_client_socket_win.h" - -#include <mstcpip.h> - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/metrics/stats_counters.h" -#include "base/strings/string_util.h" -#include "base/win/object_watcher.h" -#include "base/win/windows_version.h" -#include "net/base/connection_type_histograms.h" -#include "net/base/io_buffer.h" -#include "net/base/ip_endpoint.h" -#include "net/base/net_errors.h" -#include "net/base/net_log.h" -#include "net/base/net_util.h" -#include "net/base/network_change_notifier.h" -#include "net/base/winsock_init.h" -#include "net/base/winsock_util.h" -#include "net/socket/socket_descriptor.h" -#include "net/socket/socket_net_log_params.h" - -namespace net { - -namespace { - -const int kTCPKeepAliveSeconds = 45; -bool g_disable_overlapped_reads = false; - -bool SetSocketReceiveBufferSize(SOCKET socket, int32 size) { - int rv = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, - reinterpret_cast<const char*>(&size), sizeof(size)); - DCHECK(!rv) << "Could not set socket receive buffer size: " << GetLastError(); - return rv == 0; -} - -bool SetSocketSendBufferSize(SOCKET socket, int32 size) { - int rv = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, - reinterpret_cast<const char*>(&size), sizeof(size)); - DCHECK(!rv) << "Could not set socket send buffer size: " << GetLastError(); - return rv == 0; -} - -// Disable Nagle. -// The Nagle implementation on windows is governed by RFC 896. The idea -// behind Nagle is to reduce small packets on the network. When Nagle is -// enabled, if a partial packet has been sent, the TCP stack will disallow -// further *partial* packets until an ACK has been received from the other -// side. Good applications should always strive to send as much data as -// possible and avoid partial-packet sends. However, in most real world -// applications, there are edge cases where this does not happen, and two -// partial packets may be sent back to back. For a browser, it is NEVER -// a benefit to delay for an RTT before the second packet is sent. -// -// As a practical example in Chromium today, consider the case of a small -// POST. I have verified this: -// Client writes 649 bytes of header (partial packet #1) -// Client writes 50 bytes of POST data (partial packet #2) -// In the above example, with Nagle, a RTT delay is inserted between these -// two sends due to nagle. RTTs can easily be 100ms or more. The best -// fix is to make sure that for POSTing data, we write as much data as -// possible and minimize partial packets. We will fix that. But disabling -// Nagle also ensure we don't run into this delay in other edge cases. -// See also: -// http://technet.microsoft.com/en-us/library/bb726981.aspx -bool DisableNagle(SOCKET socket, bool disable) { - BOOL val = disable ? TRUE : FALSE; - int rv = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, - reinterpret_cast<const char*>(&val), - sizeof(val)); - DCHECK(!rv) << "Could not disable nagle"; - return rv == 0; -} - -// Enable TCP Keep-Alive to prevent NAT routers from timing out TCP -// connections. See http://crbug.com/27400 for details. -bool SetTCPKeepAlive(SOCKET socket, BOOL enable, int delay_secs) { - int delay = delay_secs * 1000; - struct tcp_keepalive keepalive_vals = { - enable ? 1 : 0, // TCP keep-alive on. - delay, // Delay seconds before sending first TCP keep-alive packet. - delay, // Delay seconds between sending TCP keep-alive packets. - }; - DWORD bytes_returned = 0xABAB; - int rv = WSAIoctl(socket, SIO_KEEPALIVE_VALS, &keepalive_vals, - sizeof(keepalive_vals), NULL, 0, - &bytes_returned, NULL, NULL); - DCHECK(!rv) << "Could not enable TCP Keep-Alive for socket: " << socket - << " [error: " << WSAGetLastError() << "]."; - - // Disregard any failure in disabling nagle or enabling TCP Keep-Alive. - return rv == 0; -} - -// Sets socket parameters. Returns the OS error code (or 0 on -// success). -int SetupSocket(SOCKET socket) { - // Increase the socket buffer sizes from the default sizes for WinXP. In - // performance testing, there is substantial benefit by increasing from 8KB - // to 64KB. - // See also: - // http://support.microsoft.com/kb/823764/EN-US - // On Vista, if we manually set these sizes, Vista turns off its receive - // window auto-tuning feature. - // http://blogs.msdn.com/wndp/archive/2006/05/05/Winhec-blog-tcpip-2.aspx - // Since Vista's auto-tune is better than any static value we can could set, - // only change these on pre-vista machines. - if (base::win::GetVersion() < base::win::VERSION_VISTA) { - const int32 kSocketBufferSize = 64 * 1024; - SetSocketReceiveBufferSize(socket, kSocketBufferSize); - SetSocketSendBufferSize(socket, kSocketBufferSize); - } - - DisableNagle(socket, true); - SetTCPKeepAlive(socket, true, kTCPKeepAliveSeconds); - return 0; -} - -// Creates a new socket and sets default parameters for it. Returns -// the OS error code (or 0 on success). -int CreateSocket(int family, SOCKET* socket) { - *socket = CreatePlatformSocket(family, SOCK_STREAM, IPPROTO_TCP); - if (*socket == INVALID_SOCKET) { - int os_error = WSAGetLastError(); - LOG(ERROR) << "CreatePlatformSocket failed: " << os_error; - return os_error; - } - int error = SetupSocket(*socket); - if (error) { - if (closesocket(*socket) < 0) - PLOG(ERROR) << "closesocket"; - *socket = INVALID_SOCKET; - return error; - } - return 0; -} - -int MapConnectError(int os_error) { - switch (os_error) { - // connect fails with WSAEACCES when Windows Firewall blocks the - // connection. - case WSAEACCES: - return ERR_NETWORK_ACCESS_DENIED; - case WSAETIMEDOUT: - return ERR_CONNECTION_TIMED_OUT; - default: { - int net_error = MapSystemError(os_error); - if (net_error == ERR_FAILED) - return ERR_CONNECTION_FAILED; // More specific than ERR_FAILED. - - // Give a more specific error when the user is offline. - if (net_error == ERR_ADDRESS_UNREACHABLE && - NetworkChangeNotifier::IsOffline()) { - return ERR_INTERNET_DISCONNECTED; - } - - return net_error; - } - } -} - -} // namespace - -//----------------------------------------------------------------------------- - -// This class encapsulates all the state that has to be preserved as long as -// there is a network IO operation in progress. If the owner TCPClientSocketWin -// is destroyed while an operation is in progress, the Core is detached and it -// lives until the operation completes and the OS doesn't reference any resource -// declared on this class anymore. -class TCPClientSocketWin::Core : public base::RefCounted<Core> { - public: - explicit Core(TCPClientSocketWin* socket); - - // Start watching for the end of a read or write operation. - void WatchForRead(); - void WatchForWrite(); - - // The TCPClientSocketWin is going away. - void Detach() { socket_ = NULL; } - - // The separate OVERLAPPED variables for asynchronous operation. - // |read_overlapped_| is used for both Connect() and Read(). - // |write_overlapped_| is only used for Write(); - OVERLAPPED read_overlapped_; - OVERLAPPED write_overlapped_; - - // The buffers used in Read() and Write(). - scoped_refptr<IOBuffer> read_iobuffer_; - scoped_refptr<IOBuffer> write_iobuffer_; - int read_buffer_length_; - int write_buffer_length_; - - bool non_blocking_reads_initialized_; - - private: - friend class base::RefCounted<Core>; - - class ReadDelegate : public base::win::ObjectWatcher::Delegate { - public: - explicit ReadDelegate(Core* core) : core_(core) {} - virtual ~ReadDelegate() {} - - // base::ObjectWatcher::Delegate methods: - virtual void OnObjectSignaled(HANDLE object); - - private: - Core* const core_; - }; - - class WriteDelegate : public base::win::ObjectWatcher::Delegate { - public: - explicit WriteDelegate(Core* core) : core_(core) {} - virtual ~WriteDelegate() {} - - // base::ObjectWatcher::Delegate methods: - virtual void OnObjectSignaled(HANDLE object); - - private: - Core* const core_; - }; - - ~Core(); - - // The socket that created this object. - TCPClientSocketWin* socket_; - - // |reader_| handles the signals from |read_watcher_|. - ReadDelegate reader_; - // |writer_| handles the signals from |write_watcher_|. - WriteDelegate writer_; - - // |read_watcher_| watches for events from Connect() and Read(). - base::win::ObjectWatcher read_watcher_; - // |write_watcher_| watches for events from Write(); - base::win::ObjectWatcher write_watcher_; - - DISALLOW_COPY_AND_ASSIGN(Core); -}; - -TCPClientSocketWin::Core::Core( - TCPClientSocketWin* socket) - : read_buffer_length_(0), - write_buffer_length_(0), - non_blocking_reads_initialized_(false), - socket_(socket), - reader_(this), - writer_(this) { - memset(&read_overlapped_, 0, sizeof(read_overlapped_)); - memset(&write_overlapped_, 0, sizeof(write_overlapped_)); - - read_overlapped_.hEvent = WSACreateEvent(); - write_overlapped_.hEvent = WSACreateEvent(); -} - -TCPClientSocketWin::Core::~Core() { - // Make sure the message loop is not watching this object anymore. - read_watcher_.StopWatching(); - write_watcher_.StopWatching(); - - WSACloseEvent(read_overlapped_.hEvent); - memset(&read_overlapped_, 0xaf, sizeof(read_overlapped_)); - WSACloseEvent(write_overlapped_.hEvent); - memset(&write_overlapped_, 0xaf, sizeof(write_overlapped_)); -} - -void TCPClientSocketWin::Core::WatchForRead() { - // We grab an extra reference because there is an IO operation in progress. - // Balanced in ReadDelegate::OnObjectSignaled(). - AddRef(); - read_watcher_.StartWatching(read_overlapped_.hEvent, &reader_); -} - -void TCPClientSocketWin::Core::WatchForWrite() { - // We grab an extra reference because there is an IO operation in progress. - // Balanced in WriteDelegate::OnObjectSignaled(). - AddRef(); - write_watcher_.StartWatching(write_overlapped_.hEvent, &writer_); -} - -void TCPClientSocketWin::Core::ReadDelegate::OnObjectSignaled( - HANDLE object) { - DCHECK_EQ(object, core_->read_overlapped_.hEvent); - if (core_->socket_) { - if (core_->socket_->waiting_connect()) - core_->socket_->DidCompleteConnect(); - else - core_->socket_->DidSignalRead(); - } - - core_->Release(); -} - -void TCPClientSocketWin::Core::WriteDelegate::OnObjectSignaled( - HANDLE object) { - DCHECK_EQ(object, core_->write_overlapped_.hEvent); - if (core_->socket_) - core_->socket_->DidCompleteWrite(); - - core_->Release(); -} - -//----------------------------------------------------------------------------- - -TCPClientSocketWin::TCPClientSocketWin(const AddressList& addresses, - net::NetLog* net_log, - const net::NetLog::Source& source) - : socket_(INVALID_SOCKET), - bound_socket_(INVALID_SOCKET), - addresses_(addresses), - current_address_index_(-1), - waiting_read_(false), - waiting_write_(false), - next_connect_state_(CONNECT_STATE_NONE), - connect_os_error_(0), - net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)), - previously_disconnected_(false) { - net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, - source.ToEventParametersCallback()); - EnsureWinsockInit(); -} - -TCPClientSocketWin::~TCPClientSocketWin() { - Disconnect(); - net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE); -} - -int TCPClientSocketWin::AdoptSocket(SOCKET socket) { - DCHECK_EQ(socket_, INVALID_SOCKET); - - int error = SetupSocket(socket); - if (error) - return MapSystemError(error); - - socket_ = socket; - SetNonBlocking(socket_); - - core_ = new Core(this); - current_address_index_ = 0; - use_history_.set_was_ever_connected(); - - return OK; -} - -int TCPClientSocketWin::Bind(const IPEndPoint& address) { - if (current_address_index_ >= 0 || bind_address_.get()) { - // Cannot bind the socket if we are already connected or connecting. - return ERR_UNEXPECTED; - } - - SockaddrStorage storage; - if (!address.ToSockAddr(storage.addr, &storage.addr_len)) - return ERR_INVALID_ARGUMENT; - - // Create |bound_socket_| and try to bind it to |address|. - int error = CreateSocket(address.GetSockAddrFamily(), &bound_socket_); - if (error) - return MapSystemError(error); - - if (bind(bound_socket_, storage.addr, storage.addr_len)) { - error = errno; - if (closesocket(bound_socket_) < 0) - PLOG(ERROR) << "closesocket"; - bound_socket_ = INVALID_SOCKET; - return MapSystemError(error); - } - - bind_address_.reset(new IPEndPoint(address)); - - return 0; -} - - -int TCPClientSocketWin::Connect(const CompletionCallback& callback) { - DCHECK(CalledOnValidThread()); - - // If already connected, then just return OK. - if (socket_ != INVALID_SOCKET) - return OK; - - base::StatsCounter connects("tcp.connect"); - connects.Increment(); - - net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT, - addresses_.CreateNetLogCallback()); - - // We will try to connect to each address in addresses_. Start with the - // first one in the list. - next_connect_state_ = CONNECT_STATE_CONNECT; - current_address_index_ = 0; - - int rv = DoConnectLoop(OK); - if (rv == ERR_IO_PENDING) { - // Synchronous operation not supported. - DCHECK(!callback.is_null()); - // TODO(ajwong): Is setting read_callback_ the right thing to do here?? - read_callback_ = callback; - } else { - LogConnectCompletion(rv); - } - - return rv; -} - -int TCPClientSocketWin::DoConnectLoop(int result) { - DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE); - - int rv = result; - do { - ConnectState state = next_connect_state_; - next_connect_state_ = CONNECT_STATE_NONE; - switch (state) { - case CONNECT_STATE_CONNECT: - DCHECK_EQ(OK, rv); - rv = DoConnect(); - break; - case CONNECT_STATE_CONNECT_COMPLETE: - rv = DoConnectComplete(rv); - break; - default: - LOG(DFATAL) << "bad state " << state; - rv = ERR_UNEXPECTED; - break; - } - } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE); - - return rv; -} - -int TCPClientSocketWin::DoConnect() { - DCHECK_GE(current_address_index_, 0); - DCHECK_LT(current_address_index_, static_cast<int>(addresses_.size())); - DCHECK_EQ(0, connect_os_error_); - - const IPEndPoint& endpoint = addresses_[current_address_index_]; - - if (previously_disconnected_) { - use_history_.Reset(); - previously_disconnected_ = false; - } - - net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, - CreateNetLogIPEndPointCallback(&endpoint)); - - next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE; - - if (bound_socket_ != INVALID_SOCKET) { - DCHECK(bind_address_.get()); - socket_ = bound_socket_; - bound_socket_ = INVALID_SOCKET; - } else { - connect_os_error_ = CreateSocket(endpoint.GetSockAddrFamily(), &socket_); - if (connect_os_error_ != 0) - return MapSystemError(connect_os_error_); - - if (bind_address_.get()) { - SockaddrStorage storage; - if (!bind_address_->ToSockAddr(storage.addr, &storage.addr_len)) - return ERR_INVALID_ARGUMENT; - if (bind(socket_, storage.addr, storage.addr_len)) - return MapSystemError(errno); - } - } - - DCHECK(!core_); - core_ = new Core(this); - // WSAEventSelect sets the socket to non-blocking mode as a side effect. - // Our connect() and recv() calls require that the socket be non-blocking. - WSAEventSelect(socket_, core_->read_overlapped_.hEvent, FD_CONNECT); - - SockaddrStorage storage; - if (!endpoint.ToSockAddr(storage.addr, &storage.addr_len)) - return ERR_INVALID_ARGUMENT; - if (!connect(socket_, storage.addr, storage.addr_len)) { - // Connected without waiting! - // - // The MSDN page for connect says: - // With a nonblocking socket, the connection attempt cannot be completed - // immediately. In this case, connect will return SOCKET_ERROR, and - // WSAGetLastError will return WSAEWOULDBLOCK. - // which implies that for a nonblocking socket, connect never returns 0. - // It's not documented whether the event object will be signaled or not - // if connect does return 0. So the code below is essentially dead code - // and we don't know if it's correct. - NOTREACHED(); - - if (ResetEventIfSignaled(core_->read_overlapped_.hEvent)) - return OK; - } else { - int os_error = WSAGetLastError(); - if (os_error != WSAEWOULDBLOCK) { - LOG(ERROR) << "connect failed: " << os_error; - connect_os_error_ = os_error; - return MapConnectError(os_error); - } - } - - core_->WatchForRead(); - return ERR_IO_PENDING; -} - -int TCPClientSocketWin::DoConnectComplete(int result) { - // Log the end of this attempt (and any OS error it threw). - int os_error = connect_os_error_; - connect_os_error_ = 0; - if (result != OK) { - net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, - NetLog::IntegerCallback("os_error", os_error)); - } else { - net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT); - } - - if (result == OK) { - use_history_.set_was_ever_connected(); - return OK; // Done! - } - - // Close whatever partially connected socket we currently have. - DoDisconnect(); - - // Try to fall back to the next address in the list. - if (current_address_index_ + 1 < static_cast<int>(addresses_.size())) { - next_connect_state_ = CONNECT_STATE_CONNECT; - ++current_address_index_; - return OK; - } - - // Otherwise there is nothing to fall back to, so give up. - return result; -} - -void TCPClientSocketWin::Disconnect() { - DCHECK(CalledOnValidThread()); - - DoDisconnect(); - current_address_index_ = -1; - bind_address_.reset(); -} - -void TCPClientSocketWin::DoDisconnect() { - DCHECK(CalledOnValidThread()); - - if (socket_ == INVALID_SOCKET) - return; - - // Note: don't use CancelIo to cancel pending IO because it doesn't work - // when there is a Winsock layered service provider. - - // In most socket implementations, closing a socket results in a graceful - // connection shutdown, but in Winsock we have to call shutdown explicitly. - // See the MSDN page "Graceful Shutdown, Linger Options, and Socket Closure" - // at http://msdn.microsoft.com/en-us/library/ms738547.aspx - shutdown(socket_, SD_SEND); - - // This cancels any pending IO. - closesocket(socket_); - socket_ = INVALID_SOCKET; - - if (waiting_connect()) { - // We closed the socket, so this notification will never come. - // From MSDN' WSAEventSelect documentation: - // "Closing a socket with closesocket also cancels the association and - // selection of network events specified in WSAEventSelect for the socket". - core_->Release(); - } - - waiting_read_ = false; - waiting_write_ = false; - - core_->Detach(); - core_ = NULL; - - previously_disconnected_ = true; -} - -bool TCPClientSocketWin::IsConnected() const { - DCHECK(CalledOnValidThread()); - - if (socket_ == INVALID_SOCKET || waiting_connect()) - return false; - - if (waiting_read_) - return true; - - // Check if connection is alive. - char c; - int rv = recv(socket_, &c, 1, MSG_PEEK); - if (rv == 0) - return false; - if (rv == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) - return false; - - return true; -} - -bool TCPClientSocketWin::IsConnectedAndIdle() const { - DCHECK(CalledOnValidThread()); - - if (socket_ == INVALID_SOCKET || waiting_connect()) - return false; - - if (waiting_read_) - return true; - - // Check if connection is alive and we haven't received any data - // unexpectedly. - char c; - int rv = recv(socket_, &c, 1, MSG_PEEK); - if (rv >= 0) - return false; - if (WSAGetLastError() != WSAEWOULDBLOCK) - return false; - - return true; -} - -int TCPClientSocketWin::GetPeerAddress(IPEndPoint* address) const { - DCHECK(CalledOnValidThread()); - DCHECK(address); - if (!IsConnected()) - return ERR_SOCKET_NOT_CONNECTED; - *address = addresses_[current_address_index_]; - return OK; -} - -int TCPClientSocketWin::GetLocalAddress(IPEndPoint* address) const { - DCHECK(CalledOnValidThread()); - DCHECK(address); - if (socket_ == INVALID_SOCKET) { - if (bind_address_.get()) { - *address = *bind_address_; - return OK; - } - return ERR_SOCKET_NOT_CONNECTED; - } - - 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; -} - -void TCPClientSocketWin::SetSubresourceSpeculation() { - use_history_.set_subresource_speculation(); -} - -void TCPClientSocketWin::SetOmniboxSpeculation() { - use_history_.set_omnibox_speculation(); -} - -bool TCPClientSocketWin::WasEverUsed() const { - return use_history_.was_used_to_convey_data(); -} - -bool TCPClientSocketWin::UsingTCPFastOpen() const { - // Not supported on windows. - return false; -} - -bool TCPClientSocketWin::WasNpnNegotiated() const { - return false; -} - -NextProto TCPClientSocketWin::GetNegotiatedProtocol() const { - return kProtoUnknown; -} - -bool TCPClientSocketWin::GetSSLInfo(SSLInfo* ssl_info) { - return false; -} - -int TCPClientSocketWin::Read(IOBuffer* buf, - int buf_len, - const CompletionCallback& callback) { - DCHECK(CalledOnValidThread()); - DCHECK_NE(socket_, INVALID_SOCKET); - DCHECK(!waiting_read_); - DCHECK(read_callback_.is_null()); - DCHECK(!core_->read_iobuffer_); - - return DoRead(buf, buf_len, callback); -} - -int TCPClientSocketWin::Write(IOBuffer* buf, - int buf_len, - const CompletionCallback& callback) { - DCHECK(CalledOnValidThread()); - DCHECK_NE(socket_, INVALID_SOCKET); - DCHECK(!waiting_write_); - DCHECK(write_callback_.is_null()); - DCHECK_GT(buf_len, 0); - DCHECK(!core_->write_iobuffer_); - - base::StatsCounter writes("tcp.writes"); - writes.Increment(); - - WSABUF write_buffer; - write_buffer.len = buf_len; - write_buffer.buf = buf->data(); - - // TODO(wtc): Remove the assertion after enough testing. - AssertEventNotSignaled(core_->write_overlapped_.hEvent); - DWORD num; - int rv = WSASend(socket_, &write_buffer, 1, &num, 0, - &core_->write_overlapped_, NULL); - if (rv == 0) { - if (ResetEventIfSignaled(core_->write_overlapped_.hEvent)) { - rv = static_cast<int>(num); - if (rv > buf_len || rv < 0) { - // It seems that some winsock interceptors report that more was written - // than was available. Treat this as an error. http://crbug.com/27870 - LOG(ERROR) << "Detected broken LSP: Asked to write " << buf_len - << " bytes, but " << rv << " bytes reported."; - return ERR_WINSOCK_UNEXPECTED_WRITTEN_BYTES; - } - base::StatsCounter write_bytes("tcp.write_bytes"); - write_bytes.Add(rv); - if (rv > 0) - use_history_.set_was_used_to_convey_data(); - net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, rv, - buf->data()); - return rv; - } - } else { - int os_error = WSAGetLastError(); - if (os_error != WSA_IO_PENDING) { - int net_error = MapSystemError(os_error); - net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR, - CreateNetLogSocketErrorCallback(net_error, os_error)); - return net_error; - } - } - waiting_write_ = true; - write_callback_ = callback; - core_->write_iobuffer_ = buf; - core_->write_buffer_length_ = buf_len; - core_->WatchForWrite(); - return ERR_IO_PENDING; -} - -bool TCPClientSocketWin::SetReceiveBufferSize(int32 size) { - DCHECK(CalledOnValidThread()); - return SetSocketReceiveBufferSize(socket_, size); -} - -bool TCPClientSocketWin::SetSendBufferSize(int32 size) { - DCHECK(CalledOnValidThread()); - return SetSocketSendBufferSize(socket_, size); -} - -bool TCPClientSocketWin::SetKeepAlive(bool enable, int delay) { - return SetTCPKeepAlive(socket_, enable, delay); -} - -bool TCPClientSocketWin::SetNoDelay(bool no_delay) { - return DisableNagle(socket_, no_delay); -} - -void TCPClientSocketWin::LogConnectCompletion(int net_error) { - if (net_error == OK) - UpdateConnectionTypeHistograms(CONNECTION_ANY); - - if (net_error != OK) { - net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, net_error); - return; - } - - struct sockaddr_storage source_address; - socklen_t addrlen = sizeof(source_address); - int rv = getsockname( - socket_, reinterpret_cast<struct sockaddr*>(&source_address), &addrlen); - if (rv != 0) { - LOG(ERROR) << "getsockname() [rv: " << rv - << "] error: " << WSAGetLastError(); - NOTREACHED(); - net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, rv); - return; - } - - net_log_.EndEvent( - NetLog::TYPE_TCP_CONNECT, - CreateNetLogSourceAddressCallback( - reinterpret_cast<const struct sockaddr*>(&source_address), - sizeof(source_address))); -} - -int TCPClientSocketWin::DoRead(IOBuffer* buf, int buf_len, - const CompletionCallback& callback) { - if (!core_->non_blocking_reads_initialized_) { - WSAEventSelect(socket_, core_->read_overlapped_.hEvent, - FD_READ | FD_CLOSE); - core_->non_blocking_reads_initialized_ = true; - } - int rv = recv(socket_, buf->data(), buf_len, 0); - if (rv == SOCKET_ERROR) { - int os_error = WSAGetLastError(); - if (os_error != WSAEWOULDBLOCK) { - int net_error = MapSystemError(os_error); - net_log_.AddEvent( - NetLog::TYPE_SOCKET_READ_ERROR, - CreateNetLogSocketErrorCallback(net_error, os_error)); - return net_error; - } - } else { - base::StatsCounter read_bytes("tcp.read_bytes"); - if (rv > 0) { - use_history_.set_was_used_to_convey_data(); - read_bytes.Add(rv); - } - net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, rv, - buf->data()); - return rv; - } - - waiting_read_ = true; - read_callback_ = callback; - core_->read_iobuffer_ = buf; - core_->read_buffer_length_ = buf_len; - core_->WatchForRead(); - return ERR_IO_PENDING; -} - -void TCPClientSocketWin::DoReadCallback(int rv) { - DCHECK_NE(rv, ERR_IO_PENDING); - DCHECK(!read_callback_.is_null()); - - // Since Run may result in Read being called, clear read_callback_ up front. - CompletionCallback c = read_callback_; - read_callback_.Reset(); - c.Run(rv); -} - -void TCPClientSocketWin::DoWriteCallback(int rv) { - DCHECK_NE(rv, ERR_IO_PENDING); - DCHECK(!write_callback_.is_null()); - - // Since Run may result in Write being called, clear write_callback_ up front. - CompletionCallback c = write_callback_; - write_callback_.Reset(); - c.Run(rv); -} - -void TCPClientSocketWin::DidCompleteConnect() { - DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE); - int result; - - WSANETWORKEVENTS events; - int rv = WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent, - &events); - int os_error = 0; - if (rv == SOCKET_ERROR) { - NOTREACHED(); - os_error = WSAGetLastError(); - result = MapSystemError(os_error); - } else if (events.lNetworkEvents & FD_CONNECT) { - os_error = events.iErrorCode[FD_CONNECT_BIT]; - result = MapConnectError(os_error); - } else { - NOTREACHED(); - result = ERR_UNEXPECTED; - } - - connect_os_error_ = os_error; - rv = DoConnectLoop(result); - if (rv != ERR_IO_PENDING) { - LogConnectCompletion(rv); - DoReadCallback(rv); - } -} - -void TCPClientSocketWin::DidCompleteWrite() { - DCHECK(waiting_write_); - - DWORD num_bytes, flags; - BOOL ok = WSAGetOverlappedResult(socket_, &core_->write_overlapped_, - &num_bytes, FALSE, &flags); - WSAResetEvent(core_->write_overlapped_.hEvent); - waiting_write_ = false; - int rv; - if (!ok) { - int os_error = WSAGetLastError(); - rv = MapSystemError(os_error); - net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR, - CreateNetLogSocketErrorCallback(rv, os_error)); - } else { - rv = static_cast<int>(num_bytes); - if (rv > core_->write_buffer_length_ || rv < 0) { - // It seems that some winsock interceptors report that more was written - // than was available. Treat this as an error. http://crbug.com/27870 - LOG(ERROR) << "Detected broken LSP: Asked to write " - << core_->write_buffer_length_ << " bytes, but " << rv - << " bytes reported."; - rv = ERR_WINSOCK_UNEXPECTED_WRITTEN_BYTES; - } else { - base::StatsCounter write_bytes("tcp.write_bytes"); - write_bytes.Add(num_bytes); - if (num_bytes > 0) - use_history_.set_was_used_to_convey_data(); - net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, num_bytes, - core_->write_iobuffer_->data()); - } - } - core_->write_iobuffer_ = NULL; - DoWriteCallback(rv); -} - -void TCPClientSocketWin::DidSignalRead() { - DCHECK(waiting_read_); - int os_error = 0; - WSANETWORKEVENTS network_events; - int rv = WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent, - &network_events); - if (rv == SOCKET_ERROR) { - os_error = WSAGetLastError(); - rv = MapSystemError(os_error); - } else if (network_events.lNetworkEvents) { - DCHECK_EQ(network_events.lNetworkEvents & ~(FD_READ | FD_CLOSE), 0); - // If network_events.lNetworkEvents is FD_CLOSE and - // network_events.iErrorCode[FD_CLOSE_BIT] is 0, it is a graceful - // connection closure. It is tempting to directly set rv to 0 in - // this case, but the MSDN pages for WSAEventSelect and - // WSAAsyncSelect recommend we still call DoRead(): - // FD_CLOSE should only be posted after all data is read from a - // socket, but an application should check for remaining data upon - // receipt of FD_CLOSE to avoid any possibility of losing data. - // - // If network_events.iErrorCode[FD_READ_BIT] or - // network_events.iErrorCode[FD_CLOSE_BIT] is nonzero, still call - // DoRead() because recv() reports a more accurate error code - // (WSAECONNRESET vs. WSAECONNABORTED) when the connection was - // reset. - rv = DoRead(core_->read_iobuffer_, core_->read_buffer_length_, - read_callback_); - if (rv == ERR_IO_PENDING) - return; - } else { - // This may happen because Read() may succeed synchronously and - // consume all the received data without resetting the event object. - core_->WatchForRead(); - return; - } - waiting_read_ = false; - core_->read_iobuffer_ = NULL; - core_->read_buffer_length_ = 0; - DoReadCallback(rv); -} - -} // namespace net diff --git a/net/socket/tcp_client_socket_win.h b/net/socket/tcp_client_socket_win.h deleted file mode 100644 index c899f27e70359..0000000000000 --- a/net/socket/tcp_client_socket_win.h +++ /dev/null @@ -1,157 +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. - -#ifndef NET_SOCKET_TCP_CLIENT_SOCKET_WIN_H_ -#define NET_SOCKET_TCP_CLIENT_SOCKET_WIN_H_ - -#include <winsock2.h> - -#include "base/memory/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/stream_socket.h" - -namespace net { - -class BoundNetLog; - -class NET_EXPORT TCPClientSocketWin : public StreamSocket, - NON_EXPORTED_BASE(base::NonThreadSafe) { - public: - // The IP address(es) and port number to connect to. The TCP socket will try - // each IP address in the list until it succeeds in establishing a - // connection. - TCPClientSocketWin(const AddressList& addresses, - net::NetLog* net_log, - const net::NetLog::Source& source); - - virtual ~TCPClientSocketWin(); - - // 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 used by TCPServerSocket() to adopt accepted connections - // and for testing. - int AdoptSocket(SOCKET socket); - - // Binds the socket to a local IP address and port. - int Bind(const IPEndPoint& address); - - // StreamSocket implementation. - virtual int Connect(const CompletionCallback& callback); - virtual void Disconnect(); - virtual bool IsConnected() const; - virtual bool IsConnectedAndIdle() const; - virtual int GetPeerAddress(IPEndPoint* address) const; - virtual int GetLocalAddress(IPEndPoint* address) const; - virtual const BoundNetLog& NetLog() const { return net_log_; } - virtual void SetSubresourceSpeculation(); - virtual void SetOmniboxSpeculation(); - virtual bool WasEverUsed() const; - virtual bool UsingTCPFastOpen() const; - virtual bool WasNpnNegotiated() const OVERRIDE; - virtual NextProto GetNegotiatedProtocol() const OVERRIDE; - virtual bool GetSSLInfo(SSLInfo* ssl_info) OVERRIDE; - - // Socket implementation. - // Multiple outstanding requests are not supported. - // Full duplex mode (reading and writing at the same time) is supported - virtual int Read(IOBuffer* buf, int buf_len, - const CompletionCallback& callback); - virtual int Write(IOBuffer* buf, int buf_len, - const CompletionCallback& callback); - - virtual bool SetReceiveBufferSize(int32 size); - virtual bool SetSendBufferSize(int32 size); - - virtual bool SetKeepAlive(bool enable, int delay); - virtual bool SetNoDelay(bool no_delay); - - private: - // State machine for connecting the socket. - enum ConnectState { - CONNECT_STATE_CONNECT, - CONNECT_STATE_CONNECT_COMPLETE, - CONNECT_STATE_NONE, - }; - - class Core; - - // State machine used by Connect(). - int DoConnectLoop(int result); - int DoConnect(); - int DoConnectComplete(int result); - - // Helper used by Disconnect(), which disconnects minus the logging and - // resetting of current_address_index_. - void DoDisconnect(); - - // Returns true if a Connect() is in progress. - bool waiting_connect() const { - return next_connect_state_ != CONNECT_STATE_NONE; - } - - // Called after Connect() has completed with |net_error|. - void LogConnectCompletion(int net_error); - - int DoRead(IOBuffer* buf, int buf_len, const CompletionCallback& callback); - void DoReadCallback(int rv); - void DoWriteCallback(int rv); - void DidCompleteConnect(); - void DidCompleteWrite(); - void DidSignalRead(); - - SOCKET socket_; - - // Local IP address and port we are bound to. Set to NULL if Bind() - // was't called (in that cases OS chooses address/port). - scoped_ptr<IPEndPoint> bind_address_; - - // Stores bound socket between Bind() and Connect() calls. - SOCKET bound_socket_; - - // The list of addresses we should try in order to establish a connection. - AddressList addresses_; - - // Where we are in above list. Set to -1 if uninitialized. - int current_address_index_; - - // The various states that the socket could be in. - bool waiting_read_; - bool waiting_write_; - - // The core of the socket that can live longer than the socket itself. We pass - // resources to the Windows async IO functions and we have to make sure that - // they are not destroyed while the OS still references them. - scoped_refptr<Core> core_; - - // External callback; called when connect or read is complete. - CompletionCallback read_callback_; - - // External callback; called when write is complete. - CompletionCallback write_callback_; - - // The next state for the Connect() state machine. - ConnectState next_connect_state_; - - // The OS error that CONNECT_STATE_CONNECT last completed with. - int connect_os_error_; - - BoundNetLog net_log_; - - // This socket was previously disconnected and has not been re-connected. - bool previously_disconnected_; - - // Record of connectivity and transmissions, for use in speculative connection - // histograms. - UseHistory use_history_; - - DISALLOW_COPY_AND_ASSIGN(TCPClientSocketWin); -}; - -} // namespace net - -#endif // NET_SOCKET_TCP_CLIENT_SOCKET_WIN_H_ diff --git a/net/socket/tcp_server_socket.cc b/net/socket/tcp_server_socket.cc index 13e9de1e59638..63cb2cf8875c5 100644 --- a/net/socket/tcp_server_socket.cc +++ b/net/socket/tcp_server_socket.cc @@ -22,7 +22,7 @@ TCPServerSocket::~TCPServerSocket() { } int TCPServerSocket::Listen(const IPEndPoint& address, int backlog) { - int result = socket_.Create(address.GetFamily()); + int result = socket_.Open(address.GetFamily()); if (result != OK) return result; @@ -88,17 +88,17 @@ int TCPServerSocket::ConvertAcceptedSocket( if (result != OK) return result; + // TODO(yzshen): Once we switch TCPClientSocketLibevent to take a connected + // TCPSocket object, we don't need to do platform-specific handling. +#if defined(OS_WIN) + scoped_ptr<TCPClientSocket> client_socket(new TCPClientSocket( + temp_accepted_socket.Pass(), accepted_address_)); +#elif defined(OS_POSIX) scoped_ptr<TCPClientSocket> client_socket(new TCPClientSocket( AddressList(accepted_address_), temp_accepted_socket->net_log().net_log(), temp_accepted_socket->net_log().source())); - // TODO(yzshen): Once we switch TCPClientSocket::AdoptSocket() to take a - // TCPSocket object, we don't need to do platform-specific handling. -#if defined(OS_WIN) - SOCKET raw_socket = temp_accepted_socket->Release(); -#elif defined(OS_POSIX) int raw_socket = temp_accepted_socket->Release(); -#endif result = client_socket->AdoptSocket(raw_socket); if (result != OK) { // |client_socket| won't take ownership of |raw_socket| on failure. @@ -106,6 +106,7 @@ int TCPServerSocket::ConvertAcceptedSocket( temp_accepted_socket->Adopt(raw_socket); return result; } +#endif *output_accepted_socket = client_socket.Pass(); return OK; diff --git a/net/socket/tcp_socket_libevent.cc b/net/socket/tcp_socket_libevent.cc index 0bceaa49a88d1..486113374963d 100644 --- a/net/socket/tcp_socket_libevent.cc +++ b/net/socket/tcp_socket_libevent.cc @@ -41,7 +41,7 @@ TCPSocketLibevent::~TCPSocketLibevent() { net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE); } -int TCPSocketLibevent::Create(AddressFamily family) { +int TCPSocketLibevent::Open(AddressFamily family) { DCHECK(CalledOnValidThread()); DCHECK_EQ(socket_, kInvalidSocket); diff --git a/net/socket/tcp_socket_libevent.h b/net/socket/tcp_socket_libevent.h index 91a3738f9c0c8..610e48939164b 100644 --- a/net/socket/tcp_socket_libevent.h +++ b/net/socket/tcp_socket_libevent.h @@ -28,7 +28,7 @@ class NET_EXPORT TCPSocketLibevent : public base::NonThreadSafe, TCPSocketLibevent(NetLog* net_log, const NetLog::Source& source); virtual ~TCPSocketLibevent(); - int Create(AddressFamily family); + int Open(AddressFamily family); // Takes ownership of |socket|. int Adopt(int socket); // Returns a socket file descriptor. The ownership is transferred to the diff --git a/net/socket/tcp_socket_unittest.cc b/net/socket/tcp_socket_unittest.cc index e20bdd875846b..0eb3083df661d 100644 --- a/net/socket/tcp_socket_unittest.cc +++ b/net/socket/tcp_socket_unittest.cc @@ -4,10 +4,16 @@ #include "net/socket/tcp_socket.h" -#include <string> +#include <string.h> +#include <string> +#include <vector> + +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "build/build_config.h" #include "net/base/address_list.h" +#include "net/base/io_buffer.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/test_completion_callback.h" @@ -29,7 +35,7 @@ class TCPSocketTest : public PlatformTest { IPEndPoint address; ParseAddress("127.0.0.1", 0, &address); - ASSERT_EQ(OK, socket_.Create(ADDRESS_FAMILY_IPV4)); + ASSERT_EQ(OK, socket_.Open(ADDRESS_FAMILY_IPV4)); ASSERT_EQ(OK, socket_.Bind(address)); ASSERT_EQ(OK, socket_.Listen(kListenBacklog)); ASSERT_EQ(OK, socket_.GetLocalAddress(&local_address_)); @@ -40,7 +46,7 @@ class TCPSocketTest : public PlatformTest { IPEndPoint address; ParseAddress("::1", 0, &address); - if (socket_.Create(ADDRESS_FAMILY_IPV6) != OK || + if (socket_.Open(ADDRESS_FAMILY_IPV6) != OK || socket_.Bind(address) != OK || socket_.Listen(kListenBacklog) != OK) { LOG(ERROR) << "Failed to listen on ::1 - probably because IPv6 is " @@ -194,5 +200,71 @@ TEST_F(TCPSocketTest, AcceptIPv6) { EXPECT_EQ(OK, connect_callback.WaitForResult()); } +// TODO(yzshen): Enable it for other platforms once TCPSocketLibevent supports +// client socket operations. +#if defined(OS_WIN) + +TEST_F(TCPSocketTest, ReadWrite) { + ASSERT_NO_FATAL_FAILURE(SetUpListenIPv4()); + + TestCompletionCallback connect_callback; + TCPSocket connecting_socket(NULL, NetLog::Source()); + int result = connecting_socket.Open(ADDRESS_FAMILY_IPV4); + ASSERT_EQ(OK, result); + connecting_socket.Connect(local_address_, connect_callback.callback()); + + TestCompletionCallback accept_callback; + scoped_ptr<TCPSocket> accepted_socket; + IPEndPoint accepted_address; + result = socket_.Accept(&accepted_socket, &accepted_address, + accept_callback.callback()); + ASSERT_EQ(OK, accept_callback.GetResult(result)); + + ASSERT_TRUE(accepted_socket.get()); + + // Both sockets should be on the loopback network interface. + EXPECT_EQ(accepted_address.address(), local_address_.address()); + + EXPECT_EQ(OK, connect_callback.WaitForResult()); + + const std::string message("test message"); + std::vector<char> buffer(message.size()); + + size_t bytes_written = 0; + while (bytes_written < message.size()) { + scoped_refptr<IOBufferWithSize> write_buffer( + new IOBufferWithSize(message.size() - bytes_written)); + memmove(write_buffer->data(), message.data() + bytes_written, + message.size() - bytes_written); + + TestCompletionCallback write_callback; + int write_result = accepted_socket->Write( + write_buffer.get(), write_buffer->size(), write_callback.callback()); + write_result = write_callback.GetResult(write_result); + ASSERT_TRUE(write_result >= 0); + bytes_written += write_result; + ASSERT_TRUE(bytes_written <= message.size()); + } + + size_t bytes_read = 0; + while (bytes_read < message.size()) { + scoped_refptr<IOBufferWithSize> read_buffer( + new IOBufferWithSize(message.size() - bytes_read)); + TestCompletionCallback read_callback; + int read_result = connecting_socket.Read( + read_buffer.get(), read_buffer->size(), read_callback.callback()); + read_result = read_callback.GetResult(read_result); + ASSERT_TRUE(read_result >= 0); + ASSERT_TRUE(bytes_read + read_result <= message.size()); + memmove(&buffer[bytes_read], read_buffer->data(), read_result); + bytes_read += read_result; + } + + std::string received_message(buffer.begin(), buffer.end()); + ASSERT_EQ(message, received_message); +} + +#endif + } // namespace } // namespace net diff --git a/net/socket/tcp_socket_win.cc b/net/socket/tcp_socket_win.cc index f69c7609e7989..1b28124c2536d 100644 --- a/net/socket/tcp_socket_win.cc +++ b/net/socket/tcp_socket_win.cc @@ -6,10 +6,17 @@ #include <mstcpip.h> +#include "base/callback_helpers.h" #include "base/logging.h" +#include "base/metrics/stats_counters.h" +#include "base/win/windows_version.h" +#include "net/base/address_list.h" +#include "net/base/connection_type_histograms.h" +#include "net/base/io_buffer.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" +#include "net/base/network_change_notifier.h" #include "net/base/winsock_init.h" #include "net/base/winsock_util.h" #include "net/socket/socket_descriptor.h" @@ -17,12 +24,251 @@ namespace net { +namespace { + +const int kTCPKeepAliveSeconds = 45; + +bool SetSocketReceiveBufferSize(SOCKET socket, int32 size) { + int rv = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, + reinterpret_cast<const char*>(&size), sizeof(size)); + DCHECK(!rv) << "Could not set socket receive buffer size: " << GetLastError(); + return rv == 0; +} + +bool SetSocketSendBufferSize(SOCKET socket, int32 size) { + int rv = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, + reinterpret_cast<const char*>(&size), sizeof(size)); + DCHECK(!rv) << "Could not set socket send buffer size: " << GetLastError(); + return rv == 0; +} + +// Disable Nagle. +// The Nagle implementation on windows is governed by RFC 896. The idea +// behind Nagle is to reduce small packets on the network. When Nagle is +// enabled, if a partial packet has been sent, the TCP stack will disallow +// further *partial* packets until an ACK has been received from the other +// side. Good applications should always strive to send as much data as +// possible and avoid partial-packet sends. However, in most real world +// applications, there are edge cases where this does not happen, and two +// partial packets may be sent back to back. For a browser, it is NEVER +// a benefit to delay for an RTT before the second packet is sent. +// +// As a practical example in Chromium today, consider the case of a small +// POST. I have verified this: +// Client writes 649 bytes of header (partial packet #1) +// Client writes 50 bytes of POST data (partial packet #2) +// In the above example, with Nagle, a RTT delay is inserted between these +// two sends due to nagle. RTTs can easily be 100ms or more. The best +// fix is to make sure that for POSTing data, we write as much data as +// possible and minimize partial packets. We will fix that. But disabling +// Nagle also ensure we don't run into this delay in other edge cases. +// See also: +// http://technet.microsoft.com/en-us/library/bb726981.aspx +bool DisableNagle(SOCKET socket, bool disable) { + BOOL val = disable ? TRUE : FALSE; + int rv = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, + reinterpret_cast<const char*>(&val), + sizeof(val)); + DCHECK(!rv) << "Could not disable nagle"; + return rv == 0; +} + +// Enable TCP Keep-Alive to prevent NAT routers from timing out TCP +// connections. See http://crbug.com/27400 for details. +bool SetTCPKeepAlive(SOCKET socket, BOOL enable, int delay_secs) { + int delay = delay_secs * 1000; + struct tcp_keepalive keepalive_vals = { + enable ? 1 : 0, // TCP keep-alive on. + delay, // Delay seconds before sending first TCP keep-alive packet. + delay, // Delay seconds between sending TCP keep-alive packets. + }; + DWORD bytes_returned = 0xABAB; + int rv = WSAIoctl(socket, SIO_KEEPALIVE_VALS, &keepalive_vals, + sizeof(keepalive_vals), NULL, 0, + &bytes_returned, NULL, NULL); + DCHECK(!rv) << "Could not enable TCP Keep-Alive for socket: " << socket + << " [error: " << WSAGetLastError() << "]."; + + // Disregard any failure in disabling nagle or enabling TCP Keep-Alive. + return rv == 0; +} + +int MapConnectError(int os_error) { + switch (os_error) { + // connect fails with WSAEACCES when Windows Firewall blocks the + // connection. + case WSAEACCES: + return ERR_NETWORK_ACCESS_DENIED; + case WSAETIMEDOUT: + return ERR_CONNECTION_TIMED_OUT; + default: { + int net_error = MapSystemError(os_error); + if (net_error == ERR_FAILED) + return ERR_CONNECTION_FAILED; // More specific than ERR_FAILED. + + // Give a more specific error when the user is offline. + if (net_error == ERR_ADDRESS_UNREACHABLE && + NetworkChangeNotifier::IsOffline()) { + return ERR_INTERNET_DISCONNECTED; + } + + return net_error; + } + } +} + +} // namespace + +//----------------------------------------------------------------------------- + +// This class encapsulates all the state that has to be preserved as long as +// there is a network IO operation in progress. If the owner TCPSocketWin is +// destroyed while an operation is in progress, the Core is detached and it +// lives until the operation completes and the OS doesn't reference any resource +// declared on this class anymore. +class TCPSocketWin::Core : public base::RefCounted<Core> { + public: + explicit Core(TCPSocketWin* socket); + + // Start watching for the end of a read or write operation. + void WatchForRead(); + void WatchForWrite(); + + // The TCPSocketWin is going away. + void Detach() { socket_ = NULL; } + + // The separate OVERLAPPED variables for asynchronous operation. + // |read_overlapped_| is used for both Connect() and Read(). + // |write_overlapped_| is only used for Write(); + OVERLAPPED read_overlapped_; + OVERLAPPED write_overlapped_; + + // The buffers used in Read() and Write(). + scoped_refptr<IOBuffer> read_iobuffer_; + scoped_refptr<IOBuffer> write_iobuffer_; + int read_buffer_length_; + int write_buffer_length_; + + bool non_blocking_reads_initialized_; + + private: + friend class base::RefCounted<Core>; + + class ReadDelegate : public base::win::ObjectWatcher::Delegate { + public: + explicit ReadDelegate(Core* core) : core_(core) {} + virtual ~ReadDelegate() {} + + // base::ObjectWatcher::Delegate methods: + virtual void OnObjectSignaled(HANDLE object); + + private: + Core* const core_; + }; + + class WriteDelegate : public base::win::ObjectWatcher::Delegate { + public: + explicit WriteDelegate(Core* core) : core_(core) {} + virtual ~WriteDelegate() {} + + // base::ObjectWatcher::Delegate methods: + virtual void OnObjectSignaled(HANDLE object); + + private: + Core* const core_; + }; + + ~Core(); + + // The socket that created this object. + TCPSocketWin* socket_; + + // |reader_| handles the signals from |read_watcher_|. + ReadDelegate reader_; + // |writer_| handles the signals from |write_watcher_|. + WriteDelegate writer_; + + // |read_watcher_| watches for events from Connect() and Read(). + base::win::ObjectWatcher read_watcher_; + // |write_watcher_| watches for events from Write(); + base::win::ObjectWatcher write_watcher_; + + DISALLOW_COPY_AND_ASSIGN(Core); +}; + +TCPSocketWin::Core::Core(TCPSocketWin* socket) + : read_buffer_length_(0), + write_buffer_length_(0), + non_blocking_reads_initialized_(false), + socket_(socket), + reader_(this), + writer_(this) { + memset(&read_overlapped_, 0, sizeof(read_overlapped_)); + memset(&write_overlapped_, 0, sizeof(write_overlapped_)); + + read_overlapped_.hEvent = WSACreateEvent(); + write_overlapped_.hEvent = WSACreateEvent(); +} + +TCPSocketWin::Core::~Core() { + // Make sure the message loop is not watching this object anymore. + read_watcher_.StopWatching(); + write_watcher_.StopWatching(); + + WSACloseEvent(read_overlapped_.hEvent); + memset(&read_overlapped_, 0xaf, sizeof(read_overlapped_)); + WSACloseEvent(write_overlapped_.hEvent); + memset(&write_overlapped_, 0xaf, sizeof(write_overlapped_)); +} + +void TCPSocketWin::Core::WatchForRead() { + // We grab an extra reference because there is an IO operation in progress. + // Balanced in ReadDelegate::OnObjectSignaled(). + AddRef(); + read_watcher_.StartWatching(read_overlapped_.hEvent, &reader_); +} + +void TCPSocketWin::Core::WatchForWrite() { + // We grab an extra reference because there is an IO operation in progress. + // Balanced in WriteDelegate::OnObjectSignaled(). + AddRef(); + write_watcher_.StartWatching(write_overlapped_.hEvent, &writer_); +} + +void TCPSocketWin::Core::ReadDelegate::OnObjectSignaled(HANDLE object) { + DCHECK_EQ(object, core_->read_overlapped_.hEvent); + if (core_->socket_) { + if (core_->socket_->waiting_connect_) + core_->socket_->DidCompleteConnect(); + else + core_->socket_->DidSignalRead(); + } + + core_->Release(); +} + +void TCPSocketWin::Core::WriteDelegate::OnObjectSignaled( + HANDLE object) { + DCHECK_EQ(object, core_->write_overlapped_.hEvent); + if (core_->socket_) + core_->socket_->DidCompleteWrite(); + + core_->Release(); +} + +//----------------------------------------------------------------------------- + TCPSocketWin::TCPSocketWin(net::NetLog* net_log, const net::NetLog::Source& source) : socket_(INVALID_SOCKET), - socket_event_(WSA_INVALID_EVENT), + accept_event_(WSA_INVALID_EVENT), accept_socket_(NULL), accept_address_(NULL), + waiting_connect_(false), + waiting_read_(false), + waiting_write_(false), + connect_os_error_(0), + logging_multiple_connect_attempts_(false), net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)) { net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, source.ToEventParametersCallback()); @@ -34,7 +280,7 @@ TCPSocketWin::~TCPSocketWin() { net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE); } -int TCPSocketWin::Create(AddressFamily family) { +int TCPSocketWin::Open(AddressFamily family) { DCHECK(CalledOnValidThread()); DCHECK_EQ(socket_, INVALID_SOCKET); @@ -54,9 +300,11 @@ int TCPSocketWin::Create(AddressFamily family) { return OK; } -int TCPSocketWin::Adopt(SOCKET socket) { +int TCPSocketWin::AdoptConnectedSocket(SOCKET socket, + const IPEndPoint& peer_address) { DCHECK(CalledOnValidThread()); DCHECK_EQ(socket_, INVALID_SOCKET); + DCHECK(!core_); socket_ = socket; @@ -66,19 +314,12 @@ int TCPSocketWin::Adopt(SOCKET socket) { return result; } + core_ = new Core(this); + peer_address_.reset(new IPEndPoint(peer_address)); + return OK; } -SOCKET TCPSocketWin::Release() { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(socket_event_, WSA_INVALID_EVENT); - DCHECK(accept_callback_.is_null()); - - SOCKET result = socket_; - socket_ = INVALID_SOCKET; - return result; -} - int TCPSocketWin::Bind(const IPEndPoint& address) { DCHECK(CalledOnValidThread()); DCHECK_NE(socket_, INVALID_SOCKET); @@ -96,36 +337,22 @@ int TCPSocketWin::Bind(const IPEndPoint& address) { return OK; } -int TCPSocketWin::GetLocalAddress(IPEndPoint* address) const { - DCHECK(CalledOnValidThread()); - DCHECK(address); - - SockaddrStorage storage; - if (getsockname(socket_, storage.addr, &storage.addr_len)) - return MapSystemError(WSAGetLastError()); - if (!address->FromSockAddr(storage.addr, storage.addr_len)) - return ERR_FAILED; - - return OK; -} - int TCPSocketWin::Listen(int backlog) { DCHECK(CalledOnValidThread()); DCHECK_GT(backlog, 0); DCHECK_NE(socket_, INVALID_SOCKET); - DCHECK_EQ(socket_event_, WSA_INVALID_EVENT); + DCHECK_EQ(accept_event_, WSA_INVALID_EVENT); - socket_event_ = WSACreateEvent(); - if (socket_event_ == WSA_INVALID_EVENT) { + accept_event_ = WSACreateEvent(); + if (accept_event_ == WSA_INVALID_EVENT) { PLOG(ERROR) << "WSACreateEvent()"; - return ERR_FAILED; + return MapSystemError(WSAGetLastError()); } int result = listen(socket_, backlog); if (result < 0) { PLOG(ERROR) << "listen() returned an error"; - result = MapSystemError(WSAGetLastError()); - return result; + return MapSystemError(WSAGetLastError()); } return OK; @@ -146,8 +373,8 @@ int TCPSocketWin::Accept(scoped_ptr<TCPSocketWin>* socket, if (result == ERR_IO_PENDING) { // Start watching. - WSAEventSelect(socket_, socket_event_, FD_ACCEPT); - accept_watcher_.StartWatching(socket_event_, this); + WSAEventSelect(socket_, accept_event_, FD_ACCEPT); + accept_watcher_.StartWatching(accept_event_, this); accept_socket_ = socket; accept_address_ = address; @@ -157,10 +384,195 @@ int TCPSocketWin::Accept(scoped_ptr<TCPSocketWin>* socket, return result; } +int TCPSocketWin::Connect(const IPEndPoint& address, + const CompletionCallback& callback) { + DCHECK(CalledOnValidThread()); + DCHECK_NE(socket_, INVALID_SOCKET); + DCHECK(!waiting_connect_); + + // |peer_address_| and |core_| will be non-NULL if Connect() has been called. + // Unless Close() is called to reset the internal state, a second call to + // Connect() is not allowed. + // Please note that we enforce this even if the previous Connect() has + // completed and failed. Although it is allowed to connect the same |socket_| + // again after a connection attempt failed on Windows, it results in + // unspecified behavior according to POSIX. Therefore, we make it behave in + // the same way as TCPSocketLibevent. + DCHECK(!peer_address_ && !core_); + + if (!logging_multiple_connect_attempts_) + LogConnectBegin(AddressList(address)); + + peer_address_.reset(new IPEndPoint(address)); + + int rv = DoConnect(); + if (rv == ERR_IO_PENDING) { + // Synchronous operation not supported. + DCHECK(!callback.is_null()); + read_callback_ = callback; + waiting_connect_ = true; + } else { + DoConnectComplete(rv); + } + + return rv; +} + +bool TCPSocketWin::IsConnected() const { + DCHECK(CalledOnValidThread()); + + if (socket_ == INVALID_SOCKET || waiting_connect_) + return false; + + if (waiting_read_) + return true; + + // Check if connection is alive. + char c; + int rv = recv(socket_, &c, 1, MSG_PEEK); + if (rv == 0) + return false; + if (rv == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) + return false; + + return true; +} + +bool TCPSocketWin::IsConnectedAndIdle() const { + DCHECK(CalledOnValidThread()); + + if (socket_ == INVALID_SOCKET || waiting_connect_) + return false; + + if (waiting_read_) + return true; + + // Check if connection is alive and we haven't received any data + // unexpectedly. + char c; + int rv = recv(socket_, &c, 1, MSG_PEEK); + if (rv >= 0) + return false; + if (WSAGetLastError() != WSAEWOULDBLOCK) + return false; + + return true; +} + +int TCPSocketWin::Read(IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) { + DCHECK(CalledOnValidThread()); + DCHECK_NE(socket_, INVALID_SOCKET); + DCHECK(!waiting_read_); + DCHECK(read_callback_.is_null()); + DCHECK(!core_->read_iobuffer_); + + return DoRead(buf, buf_len, callback); +} + +int TCPSocketWin::Write(IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) { + DCHECK(CalledOnValidThread()); + DCHECK_NE(socket_, INVALID_SOCKET); + DCHECK(!waiting_write_); + DCHECK(write_callback_.is_null()); + DCHECK_GT(buf_len, 0); + DCHECK(!core_->write_iobuffer_); + + base::StatsCounter writes("tcp.writes"); + writes.Increment(); + + WSABUF write_buffer; + write_buffer.len = buf_len; + write_buffer.buf = buf->data(); + + // TODO(wtc): Remove the assertion after enough testing. + AssertEventNotSignaled(core_->write_overlapped_.hEvent); + DWORD num; + int rv = WSASend(socket_, &write_buffer, 1, &num, 0, + &core_->write_overlapped_, NULL); + if (rv == 0) { + if (ResetEventIfSignaled(core_->write_overlapped_.hEvent)) { + rv = static_cast<int>(num); + if (rv > buf_len || rv < 0) { + // It seems that some winsock interceptors report that more was written + // than was available. Treat this as an error. http://crbug.com/27870 + LOG(ERROR) << "Detected broken LSP: Asked to write " << buf_len + << " bytes, but " << rv << " bytes reported."; + return ERR_WINSOCK_UNEXPECTED_WRITTEN_BYTES; + } + base::StatsCounter write_bytes("tcp.write_bytes"); + write_bytes.Add(rv); + net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, rv, + buf->data()); + return rv; + } + } else { + int os_error = WSAGetLastError(); + if (os_error != WSA_IO_PENDING) { + int net_error = MapSystemError(os_error); + net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR, + CreateNetLogSocketErrorCallback(net_error, os_error)); + return net_error; + } + } + waiting_write_ = true; + write_callback_ = callback; + core_->write_iobuffer_ = buf; + core_->write_buffer_length_ = buf_len; + core_->WatchForWrite(); + return ERR_IO_PENDING; +} + +int TCPSocketWin::GetLocalAddress(IPEndPoint* address) const { + DCHECK(CalledOnValidThread()); + DCHECK(address); + + SockaddrStorage storage; + if (getsockname(socket_, storage.addr, &storage.addr_len)) + return MapSystemError(WSAGetLastError()); + if (!address->FromSockAddr(storage.addr, storage.addr_len)) + return ERR_ADDRESS_INVALID; + + return OK; +} + +int TCPSocketWin::GetPeerAddress(IPEndPoint* address) const { + DCHECK(CalledOnValidThread()); + DCHECK(address); + if (!IsConnected()) + return ERR_SOCKET_NOT_CONNECTED; + *address = *peer_address_; + return OK; +} + int TCPSocketWin::SetDefaultOptionsForServer() { return SetExclusiveAddrUse(); } +void TCPSocketWin::SetDefaultOptionsForClient() { + // Increase the socket buffer sizes from the default sizes for WinXP. In + // performance testing, there is substantial benefit by increasing from 8KB + // to 64KB. + // See also: + // http://support.microsoft.com/kb/823764/EN-US + // On Vista, if we manually set these sizes, Vista turns off its receive + // window auto-tuning feature. + // http://blogs.msdn.com/wndp/archive/2006/05/05/Winhec-blog-tcpip-2.aspx + // Since Vista's auto-tune is better than any static value we can could set, + // only change these on pre-vista machines. + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + const int32 kSocketBufferSize = 64 * 1024; + SetSocketReceiveBufferSize(socket_, kSocketBufferSize); + SetSocketSendBufferSize(socket_, kSocketBufferSize); + } + + DisableNagle(socket_, true); + SetTCPKeepAlive(socket_, true, kTCPKeepAliveSeconds); +} + int TCPSocketWin::SetExclusiveAddrUse() { // On Windows, a bound end point can be hijacked by another process by // setting SO_REUSEADDR. Therefore a Windows-only option SO_EXCLUSIVEADDRUSE @@ -187,16 +599,99 @@ int TCPSocketWin::SetExclusiveAddrUse() { return OK; } +bool TCPSocketWin::SetReceiveBufferSize(int32 size) { + DCHECK(CalledOnValidThread()); + return SetSocketReceiveBufferSize(socket_, size); +} + +bool TCPSocketWin::SetSendBufferSize(int32 size) { + DCHECK(CalledOnValidThread()); + return SetSocketSendBufferSize(socket_, size); +} + +bool TCPSocketWin::SetKeepAlive(bool enable, int delay) { + return SetTCPKeepAlive(socket_, enable, delay); +} + +bool TCPSocketWin::SetNoDelay(bool no_delay) { + return DisableNagle(socket_, no_delay); +} + void TCPSocketWin::Close() { + DCHECK(CalledOnValidThread()); + if (socket_ != INVALID_SOCKET) { + // Note: don't use CancelIo to cancel pending IO because it doesn't work + // when there is a Winsock layered service provider. + + // In most socket implementations, closing a socket results in a graceful + // connection shutdown, but in Winsock we have to call shutdown explicitly. + // See the MSDN page "Graceful Shutdown, Linger Options, and Socket Closure" + // at http://msdn.microsoft.com/en-us/library/ms738547.aspx + shutdown(socket_, SD_SEND); + + // This cancels any pending IO. if (closesocket(socket_) < 0) PLOG(ERROR) << "closesocket"; socket_ = INVALID_SOCKET; } - if (socket_event_) { - WSACloseEvent(socket_event_); - socket_event_ = WSA_INVALID_EVENT; + if (accept_event_) { + WSACloseEvent(accept_event_); + accept_event_ = WSA_INVALID_EVENT; + } + + if (!accept_callback_.is_null()) { + accept_watcher_.StopWatching(); + accept_socket_ = NULL; + accept_address_ = NULL; + accept_callback_.Reset(); + } + + if (core_) { + if (waiting_connect_) { + // We closed the socket, so this notification will never come. + // From MSDN' WSAEventSelect documentation: + // "Closing a socket with closesocket also cancels the association and + // selection of network events specified in WSAEventSelect for the + // socket". + core_->Release(); + } + core_->Detach(); + core_ = NULL; + } + + waiting_connect_ = false; + waiting_read_ = false; + waiting_write_ = false; + + read_callback_.Reset(); + write_callback_.Reset(); + peer_address_.reset(); + connect_os_error_ = 0; +} + +bool TCPSocketWin::UsingTCPFastOpen() const { + // Not supported on windows. + return false; +} + +void TCPSocketWin::StartLoggingMultipleConnectAttempts( + const AddressList& addresses) { + if (!logging_multiple_connect_attempts_) { + logging_multiple_connect_attempts_ = true; + LogConnectBegin(addresses); + } else { + NOTREACHED(); + } +} + +void TCPSocketWin::EndLoggingMultipleConnectAttempts(int net_error) { + if (logging_multiple_connect_attempts_) { + LogConnectEnd(net_error); + logging_multiple_connect_attempts_ = false; + } else { + NOTREACHED(); } } @@ -221,7 +716,7 @@ int TCPSocketWin::AcceptInternal(scoped_ptr<TCPSocketWin>* socket, } scoped_ptr<TCPSocketWin> tcp_socket(new TCPSocketWin( net_log_.net_log(), net_log_.source())); - int adopt_result = tcp_socket->Adopt(new_socket); + int adopt_result = tcp_socket->AdoptConnectedSocket(new_socket, ip_end_point); if (adopt_result != OK) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, adopt_result); return adopt_result; @@ -235,7 +730,7 @@ int TCPSocketWin::AcceptInternal(scoped_ptr<TCPSocketWin>* socket, void TCPSocketWin::OnObjectSignaled(HANDLE object) { WSANETWORKEVENTS ev; - if (WSAEnumNetworkEvents(socket_, socket_event_, &ev) == SOCKET_ERROR) { + if (WSAEnumNetworkEvents(socket_, accept_event_, &ev) == SOCKET_ERROR) { PLOG(ERROR) << "WSAEnumNetworkEvents()"; return; } @@ -245,11 +740,251 @@ void TCPSocketWin::OnObjectSignaled(HANDLE object) { if (result != ERR_IO_PENDING) { accept_socket_ = NULL; accept_address_ = NULL; - CompletionCallback callback = accept_callback_; - accept_callback_.Reset(); - callback.Run(result); + base::ResetAndReturn(&accept_callback_).Run(result); } } } +int TCPSocketWin::DoConnect() { + DCHECK_EQ(connect_os_error_, 0); + DCHECK(!core_); + + net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, + CreateNetLogIPEndPointCallback(peer_address_.get())); + + core_ = new Core(this); + // WSAEventSelect sets the socket to non-blocking mode as a side effect. + // Our connect() and recv() calls require that the socket be non-blocking. + WSAEventSelect(socket_, core_->read_overlapped_.hEvent, FD_CONNECT); + + SockaddrStorage storage; + if (!peer_address_->ToSockAddr(storage.addr, &storage.addr_len)) + return ERR_INVALID_ARGUMENT; + if (!connect(socket_, storage.addr, storage.addr_len)) { + // Connected without waiting! + // + // The MSDN page for connect says: + // With a nonblocking socket, the connection attempt cannot be completed + // immediately. In this case, connect will return SOCKET_ERROR, and + // WSAGetLastError will return WSAEWOULDBLOCK. + // which implies that for a nonblocking socket, connect never returns 0. + // It's not documented whether the event object will be signaled or not + // if connect does return 0. So the code below is essentially dead code + // and we don't know if it's correct. + NOTREACHED(); + + if (ResetEventIfSignaled(core_->read_overlapped_.hEvent)) + return OK; + } else { + int os_error = WSAGetLastError(); + if (os_error != WSAEWOULDBLOCK) { + LOG(ERROR) << "connect failed: " << os_error; + connect_os_error_ = os_error; + return MapConnectError(os_error); + } + } + + core_->WatchForRead(); + return ERR_IO_PENDING; +} + +void TCPSocketWin::DoConnectComplete(int result) { + // Log the end of this attempt (and any OS error it threw). + int os_error = connect_os_error_; + connect_os_error_ = 0; + if (result != OK) { + net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, + NetLog::IntegerCallback("os_error", os_error)); + } else { + net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT); + } + + if (!logging_multiple_connect_attempts_) + LogConnectEnd(result); +} + +void TCPSocketWin::LogConnectBegin(const AddressList& addresses) { + base::StatsCounter connects("tcp.connect"); + connects.Increment(); + + net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT, + addresses.CreateNetLogCallback()); +} + +void TCPSocketWin::LogConnectEnd(int net_error) { + if (net_error == OK) + UpdateConnectionTypeHistograms(CONNECTION_ANY); + + if (net_error != OK) { + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, net_error); + return; + } + + struct sockaddr_storage source_address; + socklen_t addrlen = sizeof(source_address); + int rv = getsockname( + socket_, reinterpret_cast<struct sockaddr*>(&source_address), &addrlen); + if (rv != 0) { + LOG(ERROR) << "getsockname() [rv: " << rv + << "] error: " << WSAGetLastError(); + NOTREACHED(); + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_CONNECT, rv); + return; + } + + net_log_.EndEvent( + NetLog::TYPE_TCP_CONNECT, + CreateNetLogSourceAddressCallback( + reinterpret_cast<const struct sockaddr*>(&source_address), + sizeof(source_address))); +} + +int TCPSocketWin::DoRead(IOBuffer* buf, int buf_len, + const CompletionCallback& callback) { + if (!core_->non_blocking_reads_initialized_) { + WSAEventSelect(socket_, core_->read_overlapped_.hEvent, + FD_READ | FD_CLOSE); + core_->non_blocking_reads_initialized_ = true; + } + int rv = recv(socket_, buf->data(), buf_len, 0); + if (rv == SOCKET_ERROR) { + int os_error = WSAGetLastError(); + if (os_error != WSAEWOULDBLOCK) { + int net_error = MapSystemError(os_error); + net_log_.AddEvent( + NetLog::TYPE_SOCKET_READ_ERROR, + CreateNetLogSocketErrorCallback(net_error, os_error)); + return net_error; + } + } else { + base::StatsCounter read_bytes("tcp.read_bytes"); + if (rv > 0) + read_bytes.Add(rv); + net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, rv, + buf->data()); + return rv; + } + + waiting_read_ = true; + read_callback_ = callback; + core_->read_iobuffer_ = buf; + core_->read_buffer_length_ = buf_len; + core_->WatchForRead(); + return ERR_IO_PENDING; +} + +void TCPSocketWin::DidCompleteConnect() { + DCHECK(waiting_connect_); + DCHECK(!read_callback_.is_null()); + int result; + + WSANETWORKEVENTS events; + int rv = WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent, + &events); + int os_error = 0; + if (rv == SOCKET_ERROR) { + NOTREACHED(); + os_error = WSAGetLastError(); + result = MapSystemError(os_error); + } else if (events.lNetworkEvents & FD_CONNECT) { + os_error = events.iErrorCode[FD_CONNECT_BIT]; + result = MapConnectError(os_error); + } else { + NOTREACHED(); + result = ERR_UNEXPECTED; + } + + connect_os_error_ = os_error; + DoConnectComplete(result); + waiting_connect_ = false; + + DCHECK_NE(result, ERR_IO_PENDING); + base::ResetAndReturn(&read_callback_).Run(result); +} + +void TCPSocketWin::DidCompleteWrite() { + DCHECK(waiting_write_); + DCHECK(!write_callback_.is_null()); + + DWORD num_bytes, flags; + BOOL ok = WSAGetOverlappedResult(socket_, &core_->write_overlapped_, + &num_bytes, FALSE, &flags); + WSAResetEvent(core_->write_overlapped_.hEvent); + waiting_write_ = false; + int rv; + if (!ok) { + int os_error = WSAGetLastError(); + rv = MapSystemError(os_error); + net_log_.AddEvent(NetLog::TYPE_SOCKET_WRITE_ERROR, + CreateNetLogSocketErrorCallback(rv, os_error)); + } else { + rv = static_cast<int>(num_bytes); + if (rv > core_->write_buffer_length_ || rv < 0) { + // It seems that some winsock interceptors report that more was written + // than was available. Treat this as an error. http://crbug.com/27870 + LOG(ERROR) << "Detected broken LSP: Asked to write " + << core_->write_buffer_length_ << " bytes, but " << rv + << " bytes reported."; + rv = ERR_WINSOCK_UNEXPECTED_WRITTEN_BYTES; + } else { + base::StatsCounter write_bytes("tcp.write_bytes"); + write_bytes.Add(num_bytes); + net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, num_bytes, + core_->write_iobuffer_->data()); + } + } + + core_->write_iobuffer_ = NULL; + + DCHECK_NE(rv, ERR_IO_PENDING); + base::ResetAndReturn(&write_callback_).Run(rv); +} + +void TCPSocketWin::DidSignalRead() { + DCHECK(waiting_read_); + DCHECK(!read_callback_.is_null()); + + int os_error = 0; + WSANETWORKEVENTS network_events; + int rv = WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent, + &network_events); + if (rv == SOCKET_ERROR) { + os_error = WSAGetLastError(); + rv = MapSystemError(os_error); + } else if (network_events.lNetworkEvents) { + DCHECK_EQ(network_events.lNetworkEvents & ~(FD_READ | FD_CLOSE), 0); + // If network_events.lNetworkEvents is FD_CLOSE and + // network_events.iErrorCode[FD_CLOSE_BIT] is 0, it is a graceful + // connection closure. It is tempting to directly set rv to 0 in + // this case, but the MSDN pages for WSAEventSelect and + // WSAAsyncSelect recommend we still call DoRead(): + // FD_CLOSE should only be posted after all data is read from a + // socket, but an application should check for remaining data upon + // receipt of FD_CLOSE to avoid any possibility of losing data. + // + // If network_events.iErrorCode[FD_READ_BIT] or + // network_events.iErrorCode[FD_CLOSE_BIT] is nonzero, still call + // DoRead() because recv() reports a more accurate error code + // (WSAECONNRESET vs. WSAECONNABORTED) when the connection was + // reset. + rv = DoRead(core_->read_iobuffer_, core_->read_buffer_length_, + read_callback_); + if (rv == ERR_IO_PENDING) + return; + } else { + // This may happen because Read() may succeed synchronously and + // consume all the received data without resetting the event object. + core_->WatchForRead(); + return; + } + + waiting_read_ = false; + core_->read_iobuffer_ = NULL; + core_->read_buffer_length_ = 0; + + DCHECK_NE(rv, ERR_IO_PENDING); + base::ResetAndReturn(&read_callback_).Run(rv); +} + } // namespace net + diff --git a/net/socket/tcp_socket_win.h b/net/socket/tcp_socket_win.h index 044e5e058520b..df5fbf09aecc9 100644 --- a/net/socket/tcp_socket_win.h +++ b/net/socket/tcp_socket_win.h @@ -9,8 +9,8 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" #include "base/threading/non_thread_safe.h" #include "base/win/object_watcher.h" #include "net/base/address_family.h" @@ -20,50 +20,125 @@ namespace net { +class AddressList; +class IOBuffer; class IPEndPoint; -// TODO(yzshen): This class is incomplete. TCP client operations (Connect/Read/ -// Write/etc.) will be added. And TCPClientSocket will be changed to be a -// wrapper around TCPSocket. class NET_EXPORT TCPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe), public base::win::ObjectWatcher::Delegate { public: TCPSocketWin(NetLog* net_log, const NetLog::Source& source); virtual ~TCPSocketWin(); - int Create(AddressFamily family); + int Open(AddressFamily family); // Takes ownership of |socket|. - int Adopt(SOCKET socket); - // Returns a socket descriptor. The ownership is transferred to the caller. - SOCKET Release(); + int AdoptConnectedSocket(SOCKET socket, const IPEndPoint& peer_address); + int Bind(const IPEndPoint& address); - int GetLocalAddress(IPEndPoint* address) const; + int Listen(int backlog); int Accept(scoped_ptr<TCPSocketWin>* socket, IPEndPoint* address, const CompletionCallback& callback); + + int Connect(const IPEndPoint& address, const CompletionCallback& callback); + bool IsConnected() const; + bool IsConnectedAndIdle() const; + + // Multiple outstanding requests are not supported. + // Full duplex mode (reading and writing at the same time) is supported. + int Read(IOBuffer* buf, int buf_len, const CompletionCallback& callback); + int Write(IOBuffer* buf, int buf_len, const CompletionCallback& callback); + + int GetLocalAddress(IPEndPoint* address) const; + int GetPeerAddress(IPEndPoint* address) const; + + // Sets various socket options. + // The commonly used options for server listening sockets: + // - SetExclusiveAddrUse(). int SetDefaultOptionsForServer(); + // The commonly used options for client sockets and accepted sockets: + // - Increase the socket buffer sizes for WinXP; + // - SetNoDelay(true); + // - SetKeepAlive(true, 45). + void SetDefaultOptionsForClient(); int SetExclusiveAddrUse(); + bool SetReceiveBufferSize(int32 size); + bool SetSendBufferSize(int32 size); + bool SetKeepAlive(bool enable, int delay); + bool SetNoDelay(bool no_delay); + void Close(); + bool UsingTCPFastOpen() const; + bool IsValid() const { return socket_ != INVALID_SOCKET; } + + // Marks the start/end of a series of connect attempts for logging purpose. + // + // TCPClientSocket may attempt to connect to multiple addresses until it + // succeeds in establishing a connection. The corresponding log will have + // multiple NetLog::TYPE_TCP_CONNECT_ATTEMPT entries nested within a + // NetLog::TYPE_TCP_CONNECT. These methods set the start/end of + // NetLog::TYPE_TCP_CONNECT. + // + // TODO(yzshen): Change logging format and let TCPClientSocket log the + // start/end of a series of connect attempts itself. + void StartLoggingMultipleConnectAttempts(const AddressList& addresses); + void EndLoggingMultipleConnectAttempts(int net_error); + const BoundNetLog& net_log() const { return net_log_; } + private: + class Core; + // base::ObjectWatcher::Delegate implementation. virtual void OnObjectSignaled(HANDLE object) OVERRIDE; - private: int AcceptInternal(scoped_ptr<TCPSocketWin>* socket, IPEndPoint* address); - SOCKET socket_; - HANDLE socket_event_; + int DoConnect(); + void DoConnectComplete(int result); + void LogConnectBegin(const AddressList& addresses); + void LogConnectEnd(int net_error); + + int DoRead(IOBuffer* buf, int buf_len, const CompletionCallback& callback); + void DidCompleteConnect(); + void DidCompleteWrite(); + void DidSignalRead(); + + SOCKET socket_; + + HANDLE accept_event_; base::win::ObjectWatcher accept_watcher_; scoped_ptr<TCPSocketWin>* accept_socket_; IPEndPoint* accept_address_; CompletionCallback accept_callback_; + // The various states that the socket could be in. + bool waiting_connect_; + bool waiting_read_; + bool waiting_write_; + + // The core of the socket that can live longer than the socket itself. We pass + // resources to the Windows async IO functions and we have to make sure that + // they are not destroyed while the OS still references them. + scoped_refptr<Core> core_; + + // External callback; called when connect or read is complete. + CompletionCallback read_callback_; + + // External callback; called when write is complete. + CompletionCallback write_callback_; + + scoped_ptr<IPEndPoint> peer_address_; + // The OS error that a connect attempt last completed with. + int connect_os_error_; + + bool logging_multiple_connect_attempts_; + BoundNetLog net_log_; DISALLOW_COPY_AND_ASSIGN(TCPSocketWin); @@ -72,3 +147,4 @@ class NET_EXPORT TCPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe), } // namespace net #endif // NET_SOCKET_TCP_SOCKET_WIN_H_ +