summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-27 20:03:16 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-27 20:03:16 +0000
commita1b95f0eaba4d94ceb0711f2fd3245d8ecd444a0 (patch)
tree3f7208d4742fab73de02b90af827a3f689dae2a5
parent55775cc217406277d7591c22f5d7abec91773edc (diff)
downloadchromium_src-a1b95f0eaba4d94ceb0711f2fd3245d8ecd444a0.zip
chromium_src-a1b95f0eaba4d94ceb0711f2fd3245d8ecd444a0.tar.gz
chromium_src-a1b95f0eaba4d94ceb0711f2fd3245d8ecd444a0.tar.bz2
Bind() methods for TCP sockets
BUG=80245 TEST=None Review URL: http://codereview.chromium.org/7004055 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@87077 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/browser/renderer_host/p2p/socket_host_tcp.cc8
-rw-r--r--net/base/net_errors_posix.cc3
-rw-r--r--net/base/net_errors_win.cc2
-rw-r--r--net/net.gyp1
-rw-r--r--net/socket/tcp_client_socket_libevent.cc128
-rw-r--r--net/socket/tcp_client_socket_libevent.h18
-rw-r--r--net/socket/tcp_client_socket_unittest.cc110
-rw-r--r--net/socket/tcp_client_socket_win.cc256
-rw-r--r--net/socket/tcp_client_socket_win.h19
-rw-r--r--net/socket/tcp_server_socket_libevent.cc22
-rw-r--r--net/socket/tcp_server_socket_unittest.cc48
-rw-r--r--net/socket/tcp_server_socket_win.cc22
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..d08b262 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;
+ 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())));