diff options
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_tcp.cc | 8 | ||||
-rw-r--r-- | net/base/net_errors_posix.cc | 3 | ||||
-rw-r--r-- | net/base/net_errors_win.cc | 2 | ||||
-rw-r--r-- | net/net.gyp | 1 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_libevent.cc | 128 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_libevent.h | 18 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_unittest.cc | 110 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_win.cc | 256 | ||||
-rw-r--r-- | net/socket/tcp_client_socket_win.h | 19 | ||||
-rw-r--r-- | net/socket/tcp_server_socket_libevent.cc | 22 | ||||
-rw-r--r-- | net/socket/tcp_server_socket_unittest.cc | 48 | ||||
-rw-r--r-- | net/socket/tcp_server_socket_win.cc | 22 |
12 files changed, 479 insertions, 158 deletions
diff --git a/content/browser/renderer_host/p2p/socket_host_tcp.cc b/content/browser/renderer_host/p2p/socket_host_tcp.cc index 8d824b3..250de53 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp.cc +++ b/content/browser/renderer_host/p2p/socket_host_tcp.cc @@ -55,10 +55,16 @@ bool P2PSocketHostTcp::Init(const net::IPEndPoint& local_address, remote_address_ = remote_address; state_ = STATE_CONNECTING; - socket_.reset(new net::TCPClientSocket( + scoped_ptr<net::TCPClientSocket> tcp_socket(new net::TCPClientSocket( net::AddressList::CreateFromIPAddress( remote_address.address(), remote_address.port()), NULL, net::NetLog::Source())); + if (tcp_socket->Bind(local_address) != net::OK) { + OnError(); + return false; + } + socket_.reset(tcp_socket.release()); + int result = socket_->Connect(&connect_callback_); if (result != net::ERR_IO_PENDING) { OnConnected(result); diff --git a/net/base/net_errors_posix.cc b/net/base/net_errors_posix.cc index e977ef4..6958801 100644 --- a/net/base/net_errors_posix.cc +++ b/net/base/net_errors_posix.cc @@ -36,6 +36,7 @@ Error MapSystemError(int os_error) { case EHOSTUNREACH: case EHOSTDOWN: case ENETUNREACH: + case EAFNOSUPPORT: return ERR_ADDRESS_UNREACHABLE; case EADDRNOTAVAIL: return ERR_ADDRESS_INVALID; @@ -43,6 +44,8 @@ Error MapSystemError(int os_error) { return ERR_MSG_TOO_BIG; case ENOTCONN: return ERR_SOCKET_NOT_CONNECTED; + case EINVAL: + return ERR_INVALID_ARGUMENT; case 0: return OK; default: diff --git a/net/base/net_errors_win.cc b/net/base/net_errors_win.cc index face1cc..fbb5a16 100644 --- a/net/base/net_errors_win.cc +++ b/net/base/net_errors_win.cc @@ -42,6 +42,8 @@ Error MapSystemError(int os_error) { return ERR_SOCKET_NOT_CONNECTED; case WSAEAFNOSUPPORT: return ERR_ADDRESS_UNREACHABLE; + case WSAEINVAL: + return ERR_INVALID_ARGUMENT; case ERROR_SUCCESS: return OK; default: diff --git a/net/net.gyp b/net/net.gyp index 8d4c175..e757129 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -954,6 +954,7 @@ 'socket/ssl_client_socket_unittest.cc', 'socket/ssl_client_socket_pool_unittest.cc', 'socket/ssl_server_socket_unittest.cc', + 'socket/tcp_client_socket_unittest.cc', 'socket/tcp_server_socket_unittest.cc', 'socket/transport_client_socket_pool_unittest.cc', 'socket/transport_client_socket_unittest.cc', diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc index 6e0cb1c..222ff94 100644 --- a/net/socket/tcp_client_socket_libevent.cc +++ b/net/socket/tcp_client_socket_libevent.cc @@ -70,6 +70,36 @@ void SetTCPKeepAlive(int fd) { #endif } +// Sets socket parameters. Returns the OS error code (or 0 on +// success). +int SetupSocket(int socket) { + if (SetNonBlocking(socket)) + return errno; + + // This mirrors the behaviour on Windows. See the comment in + // tcp_client_socket_win.cc after searching for "NODELAY". + DisableNagle(socket); // If DisableNagle fails, we don't care. + SetTCPKeepAlive(socket); + + 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, int* socket) { + *socket = ::socket(family, SOCK_STREAM, IPPROTO_TCP); + if (*socket == kInvalidSocket) + return errno; + int error = SetupSocket(*socket); + if (error) { + if (HANDLE_EINTR(close(*socket)) < 0) + PLOG(ERROR) << "close"; + *socket = kInvalidSocket; + return error; + } + return 0; +} + int MapConnectError(int os_error) { switch (os_error) { case EACCES: @@ -100,6 +130,7 @@ TCPClientSocketLibevent::TCPClientSocketLibevent( net::NetLog* net_log, const net::NetLog::Source& source) : socket_(kInvalidSocket), + bound_socket_(kInvalidSocket), addresses_(addresses), current_ai_(NULL), read_watcher_(this), @@ -126,16 +157,53 @@ TCPClientSocketLibevent::~TCPClientSocketLibevent() { net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE, NULL); } -void TCPClientSocketLibevent::AdoptSocket(int socket) { +int TCPClientSocketLibevent::AdoptSocket(int socket) { DCHECK_EQ(socket_, kInvalidSocket); + + int error = SetupSocket(socket); + if (error) + return MapSystemError(error); + socket_ = socket; - int error = SetupSocket(); - DCHECK_EQ(0, error); - // This is to make GetPeerAddress work. It's up to the test that is calling - // this function to ensure that address_ contains a reasonable address for - // this socket. (i.e. at least match IPv4 vs IPv6!). + + // This is to make GetPeerAddress() work. It's up to the caller ensure + // that |address_| contains a reasonable address for this + // socket. (i.e. at least match IPv4 vs IPv6!). current_ai_ = addresses_.head(); use_history_.set_was_ever_connected(); + + return OK; +} + +int TCPClientSocketLibevent::Bind(const IPEndPoint& address) { + if (current_ai_ != NULL || bind_address_.get()) { + // Cannot bind the socket if we are already bound connected or + // connecting. + return ERR_UNEXPECTED; + } + + sockaddr_storage addr_storage; + sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + size_t addr_len = sizeof(addr_storage); + if (!address.ToSockAddr(addr, &addr_len)) + return ERR_INVALID_ARGUMENT; + + // Create |bound_socket_| and try to bound it to |address|. + int error = CreateSocket(address.GetFamily(), &bound_socket_); + if (error) + return MapSystemError(error); + + if (HANDLE_EINTR(bind(bound_socket_, addr, addr_len))) { + error = errno; + if (HANDLE_EINTR(close(bound_socket_)) < 0) + PLOG(ERROR) << "close"; + bound_socket_ = kInvalidSocket; + return MapSystemError(error); + } + + bind_address_.reset(new IPEndPoint(address)); + + return 0; } int TCPClientSocketLibevent::Connect(CompletionCallback* callback) { @@ -212,10 +280,26 @@ int TCPClientSocketLibevent::DoConnect() { next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE; - // Create a non-blocking socket. - connect_os_error_ = CreateSocket(current_ai_); - if (connect_os_error_) - return MapSystemError(connect_os_error_); + if (bound_socket_ != kInvalidSocket) { + DCHECK(bind_address_.get()); + socket_ = bound_socket_; + bound_socket_ = kInvalidSocket; + } else { + // Create a non-blocking socket. + connect_os_error_ = CreateSocket(current_ai_->ai_family, &socket_); + if (connect_os_error_) + return MapSystemError(connect_os_error_); + + if (bind_address_.get()) { + sockaddr_storage addr_storage; + sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + size_t addr_len = sizeof(addr_storage); + if (!bind_address_->ToSockAddr(addr, &addr_len)) + return ERR_INVALID_ARGUMENT; + if (HANDLE_EINTR(bind(socket_, addr, addr_len))) + return MapSystemError(errno); + } + } // Connect the socket. if (!use_tcp_fastopen_) { @@ -460,30 +544,6 @@ bool TCPClientSocketLibevent::SetSendBufferSize(int32 size) { return rv == 0; } - -int TCPClientSocketLibevent::CreateSocket(const addrinfo* ai) { - socket_ = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (socket_ == kInvalidSocket) - return errno; - return SetupSocket(); -} - -int TCPClientSocketLibevent::SetupSocket() { - if (SetNonBlocking(socket_)) { - const int err = errno; - close(socket_); - socket_ = kInvalidSocket; - return err; - } - - // This mirrors the behaviour on Windows. See the comment in - // tcp_client_socket_win.cc after searching for "NODELAY". - DisableNagle(socket_); // If DisableNagle fails, we don't care. - SetTCPKeepAlive(socket_); - - return 0; -} - void TCPClientSocketLibevent::LogConnectCompletion(int net_error) { if (net_error == OK) UpdateConnectionTypeHistograms(CONNECTION_ANY); diff --git a/net/socket/tcp_client_socket_libevent.h b/net/socket/tcp_client_socket_libevent.h index 45f24c2..b9bc557 100644 --- a/net/socket/tcp_client_socket_libevent.h +++ b/net/socket/tcp_client_socket_libevent.h @@ -38,7 +38,10 @@ class TCPClientSocketLibevent : public StreamSocket, base::NonThreadSafe { // 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. - void AdoptSocket(int socket); + int AdoptSocket(int socket); + + // Binds the socket to a local IP address and port. + int Bind(const IPEndPoint& address); // StreamSocket methods: virtual int Connect(CompletionCallback* callback); @@ -130,12 +133,6 @@ class TCPClientSocketLibevent : public StreamSocket, base::NonThreadSafe { return next_connect_state_ != CONNECT_STATE_NONE; } - // Returns the OS error code (or 0 on success). - int CreateSocket(const struct addrinfo* ai); - - // Returns the OS error code (or 0 on success). - int SetupSocket(); - // Helper to add a TCP_CONNECT (end) event to the NetLog. void LogConnectCompletion(int net_error); @@ -144,6 +141,13 @@ class TCPClientSocketLibevent : public StreamSocket, base::NonThreadSafe { int 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. + int bound_socket_; + // The list of addresses we should try in order to establish a connection. AddressList addresses_; diff --git a/net/socket/tcp_client_socket_unittest.cc b/net/socket/tcp_client_socket_unittest.cc new file mode 100644 index 0000000..edb3c20 --- /dev/null +++ b/net/socket/tcp_client_socket_unittest.cc @@ -0,0 +1,110 @@ +// 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. + +// This file contains some tests for TCPClientSocket. +// transport_client_socket_unittest.cc contans some other tests that +// are common for TCP and other types of sockets. + +#include "net/socket/tcp_client_socket.h" + +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/base/net_util.h" +#include "net/base/sys_addrinfo.h" +#include "net/base/test_completion_callback.h" +#include "net/socket/tcp_server_socket.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace { + +// Try binding a socket to loopback interface and verify that we can +// still connect to a server on the same interface. +TEST(TCPClientSocketTest, BindLoopbackToLoopback) { + IPAddressNumber lo_address; + ASSERT_TRUE(ParseIPLiteralToNumber("127.0.0.1", &lo_address)); + + TCPServerSocket server(NULL, NetLog::Source()); + ASSERT_EQ(OK, server.Listen(IPEndPoint(lo_address, 0), 1)); + IPEndPoint server_address; + ASSERT_EQ(OK, server.GetLocalAddress(&server_address)); + + TCPClientSocket socket( + AddressList::CreateFromIPAddress(server_address.address(), + server_address.port()), + NULL, NetLog::Source()); + + EXPECT_EQ(OK, socket.Bind(IPEndPoint(lo_address, 0))); + + TestCompletionCallback connect_callback; + EXPECT_EQ(ERR_IO_PENDING, socket.Connect(&connect_callback)); + + TestCompletionCallback accept_callback; + scoped_ptr<StreamSocket> accepted_socket; + int result = server.Accept(&accepted_socket, &accept_callback); + if (result == ERR_IO_PENDING) + result = accept_callback.WaitForResult(); + ASSERT_EQ(OK, result); + + EXPECT_EQ(OK, connect_callback.WaitForResult()); +} + +// Try to bind socket to the loopback interface and connect to an +// external address, verify that connection fails. +TEST(TCPClientSocketTest, BindLoopbackToExternal) { + IPAddressNumber external_ip; + ASSERT_TRUE(ParseIPLiteralToNumber("72.14.213.105", &external_ip)); + TCPClientSocket socket(AddressList::CreateFromIPAddress(external_ip, 80), + NULL, NetLog::Source()); + + IPAddressNumber lo_address; + ASSERT_TRUE(ParseIPLiteralToNumber("127.0.0.1", &lo_address)); + EXPECT_EQ(OK, socket.Bind(IPEndPoint(lo_address, 0))); + + TestCompletionCallback connect_callback; + int result = socket.Connect(&connect_callback); + if (result == ERR_IO_PENDING) + result = connect_callback.WaitForResult(); + + // We may get different errors here on different system, but + // connect() is not expected to succeed. + EXPECT_NE(OK, result); +} + +// Bind a socket to the IPv4 loopback interface and try to connect to +// the IPv6 loopback interface, verify that connection fails. +TEST(TCPClientSocketTest, BindLoopbackToIPv6) { + IPAddressNumber ipv6_lo_ip; + ASSERT_TRUE(ParseIPLiteralToNumber("::1", &ipv6_lo_ip)); + TCPServerSocket server(NULL, NetLog::Source()); + int listen_result = server.Listen(IPEndPoint(ipv6_lo_ip, 0), 1); + if (listen_result != OK) { + LOG(ERROR) << "Failed to listen on ::1 - probably because IPv6 is disabled." + " Skipping the test"; + return; + } + + IPEndPoint server_address; + ASSERT_EQ(OK, server.GetLocalAddress(&server_address)); + TCPClientSocket socket( + AddressList::CreateFromIPAddress(server_address.address(), + server_address.port()), + NULL, NetLog::Source()); + + IPAddressNumber ipv4_lo_ip; + ASSERT_TRUE(ParseIPLiteralToNumber("127.0.0.1", &ipv4_lo_ip)); + EXPECT_EQ(OK, socket.Bind(IPEndPoint(ipv4_lo_ip, 0))); + + TestCompletionCallback connect_callback; + int result = socket.Connect(&connect_callback); + if (result == ERR_IO_PENDING) + result = connect_callback.WaitForResult(); + + EXPECT_NE(OK, result); +} + +} // namespace + +} // namespace net diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc index 469fffd..e6dec8f 100644 --- a/net/socket/tcp_client_socket_win.cc +++ b/net/socket/tcp_client_socket_win.cc @@ -29,6 +29,109 @@ namespace net { namespace { +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; +} + +// 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. + int32 major_version, minor_version, fix_version; + base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version, + &fix_version); + if (major_version < 6) { + const int32 kSocketBufferSize = 64 * 1024; + SetSocketReceiveBufferSize(socket, kSocketBufferSize); + SetSocketSendBufferSize(socket, kSocketBufferSize); + } + + // 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 + // partil 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 + const BOOL kDisableNagle = TRUE; + int rv = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, + reinterpret_cast<const char*>(&kDisableNagle), + sizeof(kDisableNagle)); + DCHECK(!rv) << "Could not disable nagle"; + + // Enable TCP Keep-Alive to prevent NAT routers from timing out TCP + // connections. See http://crbug.com/27400 for details. + + struct tcp_keepalive keepalive_vals = { + 1, // TCP keep-alive on. + 45000, // Wait 45s until sending first TCP keep-alive packet. + 45000, // Wait 45s between sending TCP keep-alive packets. + }; + DWORD bytes_returned = 0xABAB; + 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 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 = WSASocket(family, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (*socket == INVALID_SOCKET) { + int os_error = WSAGetLastError(); + LOG(ERROR) << "WSASocket 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 @@ -213,6 +316,7 @@ TCPClientSocketWin::TCPClientSocketWin(const AddressList& addresses, net::NetLog* net_log, const net::NetLog::Source& source) : socket_(INVALID_SOCKET), + bound_socket_(INVALID_SOCKET), addresses_(addresses), current_ai_(NULL), waiting_read_(false), @@ -235,16 +339,52 @@ TCPClientSocketWin::~TCPClientSocketWin() { net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE, NULL); } -void TCPClientSocketWin::AdoptSocket(SOCKET socket) { +int TCPClientSocketWin::AdoptSocket(SOCKET socket) { DCHECK_EQ(socket_, INVALID_SOCKET); + + int error = SetupSocket(socket); + if (error) + return MapSystemError(error); + socket_ = socket; - int error = SetupSocket(); - DCHECK_EQ(0, error); core_ = new Core(this); current_ai_ = addresses_.head(); use_history_.set_was_ever_connected(); + + return OK; +} + +int TCPClientSocketWin::Bind(const IPEndPoint& address) { + if (current_ai_ != NULL || bind_address_.get()) { + // Cannot bind the socket if we are already connected or connecting. + return ERR_UNEXPECTED; + } + + sockaddr_storage addr_storage; + sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + size_t addr_len = sizeof(addr_storage); + if (!address.ToSockAddr(addr, &addr_len)) + return ERR_INVALID_ARGUMENT; + + // Create |bound_socket_| and try to bound it to |address|. + int error = CreateSocket(address.GetFamily(), &bound_socket_); + if (error) + return MapSystemError(error); + + if (bind(bound_socket_, addr, 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(CompletionCallback* callback) { DCHECK(CalledOnValidThread()); @@ -316,9 +456,25 @@ int TCPClientSocketWin::DoConnect() { next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE; - connect_os_error_ = CreateSocket(ai); - if (connect_os_error_ != 0) - return MapSystemError(connect_os_error_); + if (bound_socket_ != INVALID_SOCKET) { + DCHECK(bind_address_.get()); + socket_ = bound_socket_; + bound_socket_ = INVALID_SOCKET; + } else { + connect_os_error_ = CreateSocket(ai->ai_family, &socket_); + if (connect_os_error_ != 0) + return MapSystemError(connect_os_error_); + + if (bind_address_.get()) { + sockaddr_storage addr_storage; + sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + size_t addr_len = sizeof(addr_storage); + if (!bind_address_->ToSockAddr(addr, &addr_len)) + return ERR_INVALID_ARGUMENT; + if (bind(socket_, addr, addr_len)) + return MapSystemError(errno); + } + } DCHECK(!core_); core_ = new Core(this); @@ -607,96 +763,12 @@ int TCPClientSocketWin::Write(IOBuffer* buf, bool TCPClientSocketWin::SetReceiveBufferSize(int32 size) { DCHECK(CalledOnValidThread()); - 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; + return SetSocketReceiveBufferSize(socket_, size); } bool TCPClientSocketWin::SetSendBufferSize(int32 size) { DCHECK(CalledOnValidThread()); - 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; -} - -int TCPClientSocketWin::CreateSocket(const struct addrinfo* ai) { - socket_ = WSASocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, NULL, 0, - WSA_FLAG_OVERLAPPED); - if (socket_ == INVALID_SOCKET) { - int os_error = WSAGetLastError(); - LOG(ERROR) << "WSASocket failed: " << os_error; - return os_error; - } - return SetupSocket(); -} - -int TCPClientSocketWin::SetupSocket() { - // 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. - int32 major_version, minor_version, fix_version; - base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version, - &fix_version); - if (major_version < 6) { - const int32 kSocketBufferSize = 64 * 1024; - SetReceiveBufferSize(kSocketBufferSize); - SetSendBufferSize(kSocketBufferSize); - } - - // 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 - // partil 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 - const BOOL kDisableNagle = TRUE; - int rv = setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, - reinterpret_cast<const char*>(&kDisableNagle), - sizeof(kDisableNagle)); - DCHECK(!rv) << "Could not disable nagle"; - - // Enable TCP Keep-Alive to prevent NAT routers from timing out TCP - // connections. See http://crbug.com/27400 for details. - - struct tcp_keepalive keepalive_vals = { - 1, // TCP keep-alive on. - 45000, // Wait 45s until sending first TCP keep-alive packet. - 45000, // Wait 45s between sending TCP keep-alive packets. - }; - DWORD bytes_returned = 0xABAB; - 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 0; + return SetSocketSendBufferSize(socket_, size); } void TCPClientSocketWin::LogConnectCompletion(int net_error) { diff --git a/net/socket/tcp_client_socket_win.h b/net/socket/tcp_client_socket_win.h index 0c704e9..ed71514 100644 --- a/net/socket/tcp_client_socket_win.h +++ b/net/socket/tcp_client_socket_win.h @@ -8,6 +8,7 @@ #include <winsock2.h> +#include "base/scoped_ptr.h" #include "base/threading/non_thread_safe.h" #include "net/base/address_list.h" #include "net/base/completion_callback.h" @@ -35,7 +36,10 @@ class NET_API TCPClientSocketWin : public StreamSocket, // 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. - void AdoptSocket(SOCKET socket); + int AdoptSocket(SOCKET socket); + + // Binds the socket to a local IP address and port. + int Bind(const IPEndPoint& address); // StreamSocket methods: virtual int Connect(CompletionCallback* callback); @@ -83,12 +87,6 @@ class NET_API TCPClientSocketWin : public StreamSocket, return next_connect_state_ != CONNECT_STATE_NONE; } - // Returns the OS error code (or 0 on success). - int CreateSocket(const struct addrinfo* ai); - - // Returns the OS error code (or 0 on success). - int SetupSocket(); - // Called after Connect() has completed with |net_error|. void LogConnectCompletion(int net_error); @@ -100,6 +98,13 @@ class NET_API TCPClientSocketWin : public StreamSocket, 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_; diff --git a/net/socket/tcp_server_socket_libevent.cc b/net/socket/tcp_server_socket_libevent.cc index daedbf5..89119f6 100644 --- a/net/socket/tcp_server_socket_libevent.cc +++ b/net/socket/tcp_server_socket_libevent.cc @@ -58,7 +58,7 @@ int TCPServerSocketLibevent::Listen(const IPEndPoint& address, int backlog) { DCHECK_GT(backlog, 0); DCHECK_EQ(socket_, kInvalidSocket); - socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + socket_ = socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP); if (socket_ < 0) { PLOG(ERROR) << "socket() returned an error"; return MapSystemError(errno); @@ -142,8 +142,8 @@ int TCPServerSocketLibevent::AcceptInternal( 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 new_socket = HANDLE_EINTR(accept(socket_, addr, &addr_len)); + if (new_socket < 0) { int net_error = MapSystemError(errno); if (net_error != ERR_IO_PENDING) net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, net_error); @@ -153,16 +153,22 @@ int TCPServerSocketLibevent::AcceptInternal( IPEndPoint address; if (!address.FromSockAddr(addr, addr_len)) { NOTREACHED(); - if (HANDLE_EINTR(close(result)) < 0) + if (HANDLE_EINTR(close(new_socket)) < 0) PLOG(ERROR) << "close"; net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, ERR_FAILED); return ERR_FAILED; } - TCPClientSocket* tcp_socket = new TCPClientSocket( + scoped_ptr<TCPClientSocket> tcp_socket(new TCPClientSocket( AddressList::CreateFromIPAddress(address.address(), address.port()), - net_log_.net_log(), net_log_.source()); - tcp_socket->AdoptSocket(result); - socket->reset(tcp_socket); + net_log_.net_log(), net_log_.source())); + int adopt_result = tcp_socket->AdoptSocket(new_socket); + if (adopt_result != OK) { + if (HANDLE_EINTR(close(new_socket)) < 0) + PLOG(ERROR) << "close"; + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, adopt_result); + return adopt_result; + } + socket->reset(tcp_socket.release()); net_log_.EndEvent(NetLog::TYPE_TCP_ACCEPT, make_scoped_refptr(new NetLogStringParameter( "address", address.ToString()))); diff --git a/net/socket/tcp_server_socket_unittest.cc b/net/socket/tcp_server_socket_unittest.cc index d4abe9d..c14f22e 100644 --- a/net/socket/tcp_server_socket_unittest.cc +++ b/net/socket/tcp_server_socket_unittest.cc @@ -26,13 +26,26 @@ class TCPServerSocketTest : public PlatformTest { : socket_(NULL, NetLog::Source()) { } - void SetUp() OVERRIDE { + void SetUpIPv4() { IPEndPoint address; ParseAddress("127.0.0.1", 0, &address); ASSERT_EQ(OK, socket_.Listen(address, kListenBacklog)); ASSERT_EQ(OK, socket_.GetLocalAddress(&local_address_)); } + void SetUpIPv6(bool* success) { + *success = false; + IPEndPoint address; + ParseAddress("::1", 0, &address); + if (socket_.Listen(address, kListenBacklog) != 0) { + LOG(ERROR) << "Failed to listen on ::1 - probably because IPv6 is " + "disabled. Skipping the test"; + return; + } + ASSERT_EQ(OK, socket_.GetLocalAddress(&local_address_)); + *success = true; + } + void ParseAddress(std::string ip_str, int port, IPEndPoint* address) { IPAddressNumber ip_number; bool rv = ParseIPLiteralToNumber(ip_str, &ip_number); @@ -60,6 +73,8 @@ class TCPServerSocketTest : public PlatformTest { }; TEST_F(TCPServerSocketTest, Accept) { + ASSERT_NO_FATAL_FAILURE(SetUpIPv4()); + TestCompletionCallback connect_callback; TCPClientSocket connecting_socket(local_address_list(), NULL, NetLog::Source()); @@ -83,6 +98,8 @@ TEST_F(TCPServerSocketTest, Accept) { // Test Accept() callback. TEST_F(TCPServerSocketTest, AcceptAsync) { + ASSERT_NO_FATAL_FAILURE(SetUpIPv4()); + TestCompletionCallback accept_callback; scoped_ptr<StreamSocket> accepted_socket; @@ -105,6 +122,8 @@ TEST_F(TCPServerSocketTest, AcceptAsync) { // Accept two connections simultaneously. TEST_F(TCPServerSocketTest, Accept2Connections) { + ASSERT_NO_FATAL_FAILURE(SetUpIPv4()); + TestCompletionCallback accept_callback; scoped_ptr<StreamSocket> accepted_socket; @@ -142,6 +161,33 @@ TEST_F(TCPServerSocketTest, Accept2Connections) { local_address_.address()); } +TEST_F(TCPServerSocketTest, AcceptIPv6) { + bool initialized = false; + ASSERT_NO_FATAL_FAILURE(SetUpIPv6(&initialized)); + if (!initialized) + return; + + TestCompletionCallback connect_callback; + TCPClientSocket connecting_socket(local_address_list(), + NULL, NetLog::Source()); + connecting_socket.Connect(&connect_callback); + + TestCompletionCallback accept_callback; + scoped_ptr<StreamSocket> 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()); +} + } // namespace } // namespace net diff --git a/net/socket/tcp_server_socket_win.cc b/net/socket/tcp_server_socket_win.cc index a850a8a..55c2439 100644 --- a/net/socket/tcp_server_socket_win.cc +++ b/net/socket/tcp_server_socket_win.cc @@ -46,7 +46,7 @@ int TCPServerSocketWin::Listen(const IPEndPoint& address, int backlog) { return ERR_FAILED; } - socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + socket_ = socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP); if (socket_ < 0) { PLOG(ERROR) << "socket() returned an error"; return MapSystemError(WSAGetLastError()); @@ -126,8 +126,8 @@ int TCPServerSocketWin::AcceptInternal(scoped_ptr<StreamSocket>* socket) { 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 new_socket = accept(socket_, addr, &addr_len); + if (new_socket < 0) { int net_error = MapSystemError(WSAGetLastError()); if (net_error != ERR_IO_PENDING) net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, net_error); @@ -137,16 +137,22 @@ int TCPServerSocketWin::AcceptInternal(scoped_ptr<StreamSocket>* socket) { IPEndPoint address; if (!address.FromSockAddr(addr, addr_len)) { NOTREACHED(); - if (closesocket(result) < 0) + if (closesocket(new_socket) < 0) PLOG(ERROR) << "closesocket"; net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, ERR_FAILED); return ERR_FAILED; } - TCPClientSocket* tcp_socket = new TCPClientSocket( + scoped_ptr<TCPClientSocket> tcp_socket(new TCPClientSocket( AddressList::CreateFromIPAddress(address.address(), address.port()), - net_log_.net_log(), net_log_.source()); - tcp_socket->AdoptSocket(result); - socket->reset(tcp_socket); + net_log_.net_log(), net_log_.source())); + int adopt_result = tcp_socket->AdoptSocket(new_socket); + if (adopt_result != OK) { + if (closesocket(new_socket) < 0) + PLOG(ERROR) << "closesocket"; + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_TCP_ACCEPT, adopt_result); + return adopt_result; + } + socket->reset(tcp_socket.release()); net_log_.EndEvent(NetLog::TYPE_TCP_ACCEPT, make_scoped_refptr(new NetLogStringParameter( "address", address.ToString()))); |