summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpenghuang@chromium.org <penghuang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-29 23:15:06 +0000
committerpenghuang@chromium.org <penghuang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-29 23:15:06 +0000
commit820b984a7efeb99f6672565483016a9e920ee8b9 (patch)
tree6a2cf702de516d7dd3bdf43e538ed830026d3e3a
parent57018f97a1ebf369add876529dc38f270b29bd3f (diff)
downloadchromium_src-820b984a7efeb99f6672565483016a9e920ee8b9.zip
chromium_src-820b984a7efeb99f6672565483016a9e920ee8b9.tar.gz
chromium_src-820b984a7efeb99f6672565483016a9e920ee8b9.tar.bz2
Add socket.setKeepAlive() and socket.setNoDelay().
BUG=124950,124951 TEST=unit_tests --gtest_filter=SocketTest.* TEST=browser_tests --gtest_filter=SocketApiTest.* Review URL: https://chromiumcodereview.appspot.com/10453012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@139416 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/api/socket/socket.cc8
-rw-r--r--chrome/browser/extensions/api/socket/socket.h4
-rw-r--r--chrome/browser/extensions/api/socket/socket_api.cc47
-rw-r--r--chrome/browser/extensions/api/socket/socket_api.h34
-rw-r--r--chrome/browser/extensions/api/socket/tcp_socket.cc12
-rw-r--r--chrome/browser/extensions/api/socket/tcp_socket.h2
-rw-r--r--chrome/browser/extensions/api/socket/tcp_socket_unittest.cc54
-rw-r--r--chrome/common/extensions/api/experimental_socket.idl25
-rw-r--r--chrome/test/data/extensions/api_test/socket/api/background.js44
-rw-r--r--net/socket/tcp_client_socket_libevent.cc46
-rw-r--r--net/socket/tcp_client_socket_libevent.h3
-rw-r--r--net/socket/tcp_client_socket_win.cc107
-rw-r--r--net/socket/tcp_client_socket_win.h3
13 files changed, 314 insertions, 75 deletions
diff --git a/chrome/browser/extensions/api/socket/socket.cc b/chrome/browser/extensions/api/socket/socket.cc
index e1e3004..286fbbd6 100644
--- a/chrome/browser/extensions/api/socket/socket.cc
+++ b/chrome/browser/extensions/api/socket/socket.cc
@@ -74,6 +74,14 @@ void Socket::OnWriteComplete(int result) {
WriteData();
}
+bool Socket::SetKeepAlive(bool enable, int delay) {
+ return false;
+}
+
+bool Socket::SetNoDelay(bool no_delay) {
+ return false;
+}
+
// static
bool Socket::StringAndPortToIPEndPoint(const std::string& ip_address_str,
int port,
diff --git a/chrome/browser/extensions/api/socket/socket.h b/chrome/browser/extensions/api/socket/socket.h
index c6ee7e3..60feeeb 100644
--- a/chrome/browser/extensions/api/socket/socket.h
+++ b/chrome/browser/extensions/api/socket/socket.h
@@ -60,6 +60,10 @@ class Socket : public APIResource {
const std::string& address,
int port,
const CompletionCallback& callback) = 0;
+
+ virtual bool SetKeepAlive(bool enable, int delay);
+ virtual bool SetNoDelay(bool no_delay);
+
static bool StringAndPortToAddressList(const std::string& ip_address_str,
int port,
net::AddressList* address_list);
diff --git a/chrome/browser/extensions/api/socket/socket_api.cc b/chrome/browser/extensions/api/socket/socket_api.cc
index 98fa6f2..268423b 100644
--- a/chrome/browser/extensions/api/socket/socket_api.cc
+++ b/chrome/browser/extensions/api/socket/socket_api.cc
@@ -307,4 +307,51 @@ void SocketSendToFunction::OnCompleted(int bytes_written) {
AsyncWorkCompleted();
}
+SocketSetKeepAliveFunction::SocketSetKeepAliveFunction()
+ : params_(NULL) {
+}
+
+SocketSetKeepAliveFunction::~SocketSetKeepAliveFunction() {}
+
+bool SocketSetKeepAliveFunction::Prepare() {
+ params_ = api::experimental_socket::SetKeepAlive::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketSetKeepAliveFunction::Work() {
+ bool result = false;
+ Socket* socket = controller()->GetSocket(params_->socket_id);
+ if (socket) {
+ int delay = 0;
+ if (params_->delay.get())
+ delay = *params_->delay;
+ result = socket->SetKeepAlive(params_->enable, delay);
+ } else {
+ error_ = kSocketNotFoundError;
+ }
+ result_.reset(Value::CreateBooleanValue(result));
+}
+
+SocketSetNoDelayFunction::SocketSetNoDelayFunction()
+ : params_(NULL) {
+}
+
+SocketSetNoDelayFunction::~SocketSetNoDelayFunction() {}
+
+bool SocketSetNoDelayFunction::Prepare() {
+ params_ = api::experimental_socket::SetNoDelay::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketSetNoDelayFunction::Work() {
+ bool result = false;
+ Socket* socket = controller()->GetSocket(params_->socket_id);
+ if (socket)
+ result = socket->SetNoDelay(params_->no_delay);
+ else
+ error_ = kSocketNotFoundError;
+ result_.reset(Value::CreateBooleanValue(result));
+}
} // namespace extensions
diff --git a/chrome/browser/extensions/api/socket/socket_api.h b/chrome/browser/extensions/api/socket/socket_api.h
index de6f685..7f31f00 100644
--- a/chrome/browser/extensions/api/socket/socket_api.h
+++ b/chrome/browser/extensions/api/socket/socket_api.h
@@ -207,6 +207,40 @@ class SocketSendToFunction : public SocketExtensionFunction {
int port_;
};
+class SocketSetKeepAliveFunction : public SocketExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.setKeepAlive")
+
+ SocketSetKeepAliveFunction();
+
+ protected:
+ virtual ~SocketSetKeepAliveFunction();
+
+ // AsyncAPIFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<api::experimental_socket::SetKeepAlive::Params> params_;
+};
+
+class SocketSetNoDelayFunction : public SocketExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.setNoDelay")
+
+ SocketSetNoDelayFunction();
+
+ protected:
+ virtual ~SocketSetNoDelayFunction();
+
+ // AsyncAPIFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<api::experimental_socket::SetNoDelay::Params> params_;
+};
+
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_H_
diff --git a/chrome/browser/extensions/api/socket/tcp_socket.cc b/chrome/browser/extensions/api/socket/tcp_socket.cc
index 6d3d24d..429c6ac 100644
--- a/chrome/browser/extensions/api/socket/tcp_socket.cc
+++ b/chrome/browser/extensions/api/socket/tcp_socket.cc
@@ -128,6 +128,18 @@ void TCPSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer,
callback.Run(net::ERR_FAILED);
}
+bool TCPSocket::SetKeepAlive(bool enable, int delay) {
+ if (!socket_.get())
+ return false;
+ return socket_->SetKeepAlive(enable, delay);
+}
+
+bool TCPSocket::SetNoDelay(bool no_delay) {
+ if (!socket_.get())
+ return false;
+ return socket_->SetNoDelay(no_delay);
+}
+
int TCPSocket::WriteImpl(net::IOBuffer* io_buffer,
int io_buffer_size,
const net::CompletionCallback& callback) {
diff --git a/chrome/browser/extensions/api/socket/tcp_socket.h b/chrome/browser/extensions/api/socket/tcp_socket.h
index 08c1f19..a536dbe 100644
--- a/chrome/browser/extensions/api/socket/tcp_socket.h
+++ b/chrome/browser/extensions/api/socket/tcp_socket.h
@@ -41,6 +41,8 @@ class TCPSocket : public Socket {
const std::string& address,
int port,
const CompletionCallback& callback) OVERRIDE;
+ virtual bool SetKeepAlive(bool enable, int delay) OVERRIDE;
+ virtual bool SetNoDelay(bool no_delay) OVERRIDE;
static TCPSocket* CreateSocketForTesting(
net::TCPClientSocket* tcp_client_socket,
diff --git a/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc
index 7066d6b..3e71115 100644
--- a/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc
+++ b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc
@@ -31,6 +31,8 @@ class MockTCPSocket : public net::TCPClientSocket {
const net::CompletionCallback& callback));
MOCK_METHOD3(Write, int(net::IOBuffer* buf, int buf_len,
const net::CompletionCallback& callback));
+ MOCK_METHOD2(SetKeepAlive, bool(bool enable, int delay));
+ MOCK_METHOD1(SetNoDelay, bool(bool no_delay));
virtual bool IsConnected() const OVERRIDE {
return true;
}
@@ -165,4 +167,56 @@ TEST(SocketTest, TestTCPSocketBlockedWriteReentry) {
}
}
+TEST(SocketTest, TestTCPSocketSetNoDelay) {
+ net::AddressList address_list;
+ MockTCPSocket* tcp_client_socket = new MockTCPSocket(address_list);
+ MockAPIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
+
+ scoped_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting(
+ tcp_client_socket, notifier));
+
+ bool no_delay = false;
+ EXPECT_CALL(*tcp_client_socket, SetNoDelay(_))
+ .WillOnce(testing::DoAll(SaveArg<0>(&no_delay), Return(true)));
+ int result = socket->SetNoDelay(true);
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(no_delay);
+
+ EXPECT_CALL(*tcp_client_socket, SetNoDelay(_))
+ .WillOnce(testing::DoAll(SaveArg<0>(&no_delay), Return(false)));
+
+ result = socket->SetNoDelay(false);
+ EXPECT_FALSE(result);
+ EXPECT_FALSE(no_delay);
+}
+
+TEST(SocketTest, TestTCPSocketSetKeepAlive) {
+ net::AddressList address_list;
+ MockTCPSocket* tcp_client_socket = new MockTCPSocket(address_list);
+ MockAPIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
+
+ scoped_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting(
+ tcp_client_socket, notifier));
+
+ bool enable = false;
+ int delay = 0;
+ EXPECT_CALL(*tcp_client_socket, SetKeepAlive(_, _))
+ .WillOnce(testing::DoAll(SaveArg<0>(&enable),
+ SaveArg<1>(&delay),
+ Return(true)));
+ int result = socket->SetKeepAlive(true, 4500);
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(enable);
+ EXPECT_EQ(4500, delay);
+
+ EXPECT_CALL(*tcp_client_socket, SetKeepAlive(_, _))
+ .WillOnce(testing::DoAll(SaveArg<0>(&enable),
+ SaveArg<1>(&delay),
+ Return(false)));
+ result = socket->SetKeepAlive(false, 0);
+ EXPECT_FALSE(result);
+ EXPECT_FALSE(enable);
+ EXPECT_EQ(0, delay);
+}
+
} // namespace extensions
diff --git a/chrome/common/extensions/api/experimental_socket.idl b/chrome/common/extensions/api/experimental_socket.idl
index 17774ed..98e265e 100644
--- a/chrome/common/extensions/api/experimental_socket.idl
+++ b/chrome/common/extensions/api/experimental_socket.idl
@@ -57,6 +57,10 @@
callback SendToCallback = void (WriteInfo writeInfo);
+ callback SetKeepAliveCallback = void (boolean result);
+
+ callback SetNoDelayCallback = void (boolean result);
+
interface Functions {
// Creates a socket of the specified type that will connect to the specified
// remote machine.
@@ -87,7 +91,7 @@
// |socketId| : The socketId.
// |address| : The address of the remote machine.
// |port| : The port of the remote machine.
- // |callback| : Called when the connection attempt is complete.
+ // |callback| : Called when the bind attempt is complete.
static void bind(long socketId,
DOMString address,
long port,
@@ -141,6 +145,25 @@
DOMString address,
long port,
SendToCallback callback);
+
+ // Enable/disable keep-alive functionality for a TCP connection.
+ // |socketId| : The socketId.
+ // |enable| : If true, enable keep-alive functionality.
+ // |delay| : Set the delay seconds between the last data packet received
+ // and the first keepalive probe. Default is 0.
+ // |callback| : Called when the setKeepAlive attempt is complete.
+ static void setKeepAlive(long socketId,
+ boolean enable,
+ optional long delay,
+ SetKeepAliveCallback callback);
+
+ // Enable/disable Nagle algorithm.
+ // |socketId| : The socketId.
+ // |noDelay| : If true, disable Nagle algorithm.
+ // |callback| : Called when the setNoDelay attempt is complete.
+ static void setNoDelay(long socketId,
+ boolean noDelay,
+ SetNoDelayCallback callback);
};
};
diff --git a/chrome/test/data/extensions/api_test/socket/api/background.js b/chrome/test/data/extensions/api_test/socket/api/background.js
index 3ed924e..f6a29fd 100644
--- a/chrome/test/data/extensions/api_test/socket/api/background.js
+++ b/chrome/test/data/extensions/api_test/socket/api/background.js
@@ -64,10 +64,10 @@ function onDataRead(readInfo) {
arrayBuffer2String(readInfo.data, function(s) {
dataAsString = s; // save this for error reporting
- if (s.match(expectedResponsePattern)) {
- succeeded = true;
- chrome.test.succeed();
- }
+ var match = !!s.match(expectedResponsePattern);
+ chrome.test.assertTrue(match, "Received data does not match.");
+ succeeded = true;
+ chrome.test.succeed();
});
}
@@ -81,15 +81,33 @@ function onWriteOrSendToComplete(writeInfo) {
}
}
-function onConnectOrBindComplete(connectResult) {
- if (connectResult == 0) {
- string2ArrayBuffer(request, function(arrayBuffer) {
- if (protocol == "tcp")
- socket.write(socketId, arrayBuffer, onWriteOrSendToComplete);
- else
- socket.sendTo(socketId, arrayBuffer, address, port,
- onWriteOrSendToComplete);
- });
+function onConnectOrBindComplete(result) {
+ chrome.test.assertEq(0, result, "Connect or bind failed.");
+ if (result == 0) {
+ var onSetKeepAlive = function(result) {
+ if (protocol == "tcp")
+ chrome.test.assertTrue(result, "setKeepAlive failed for TCP.");
+ else
+ chrome.test.assertFalse(result, "setKeepAlive did not fail for UDP.");
+
+ string2ArrayBuffer(request, function(arrayBuffer) {
+ if (protocol == "tcp")
+ socket.write(socketId, arrayBuffer, onWriteOrSendToComplete);
+ else
+ socket.sendTo(socketId, arrayBuffer, address, port,
+ onWriteOrSendToComplete);
+ });
+ };
+
+ var onSetNoDelay = function(result) {
+ if (protocol == "tcp")
+ chrome.test.assertTrue(result, "setNoDelay failed for TCP.");
+ else
+ chrome.test.assertFalse(result, "setNoDelay did not fail for UDP.");
+ socket.setKeepAlive(socketId, true, 1000, onSetKeepAlive);
+ };
+
+ socket.setNoDelay(socketId, true, onSetNoDelay);
}
}
diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc
index 972d75d..5254c1f 100644
--- a/net/socket/tcp_client_socket_libevent.cc
+++ b/net/socket/tcp_client_socket_libevent.cc
@@ -32,37 +32,39 @@ namespace net {
namespace {
const int kInvalidSocket = -1;
+const int kTCPKeepAliveSeconds = 45;
-// DisableNagle turns off buffering in the kernel. By default, TCP sockets will
-// wait up to 200ms for more data to complete a packet before transmitting.
+// SetTCPNoDelay turns on/off buffering in the kernel. By default, TCP sockets
+// will wait up to 200ms for more data to complete a packet before transmitting.
// After calling this function, the kernel will not wait. See TCP_NODELAY in
// `man 7 tcp`.
-int DisableNagle(int fd) {
- int on = 1;
- return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+bool SetTCPNoDelay(int fd, bool no_delay) {
+ int on = no_delay ? 1 : 0;
+ int error = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on,
+ sizeof(on));
+ return error == 0;
}
// SetTCPKeepAlive sets SO_KEEPALIVE.
-void SetTCPKeepAlive(int fd) {
- int optval = 1;
- socklen_t optlen = sizeof(optval);
- if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen)) {
+bool SetTCPKeepAlive(int fd, bool enable, int delay) {
+ int on = enable ? 1 : 0;
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))) {
PLOG(ERROR) << "Failed to set SO_KEEPALIVE on fd: " << fd;
- return;
+ return false;
}
#if defined(OS_LINUX)
// Set seconds until first TCP keep alive.
- optval = 45;
- if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &optval, optlen)) {
+ if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) {
PLOG(ERROR) << "Failed to set TCP_KEEPIDLE on fd: " << fd;
- return;
+ return false;
}
// Set seconds between TCP keep alives.
- if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &optval, optlen)) {
+ if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &delay, sizeof(delay))) {
PLOG(ERROR) << "Failed to set TCP_KEEPINTVL on fd: " << fd;
- return;
+ return false;
}
#endif
+ return true;
}
// Sets socket parameters. Returns the OS error code (or 0 on
@@ -73,8 +75,8 @@ int SetupSocket(int socket) {
// 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);
+ SetTCPNoDelay(socket, true); // If SetTCPNoDelay fails, we don't care.
+ SetTCPKeepAlive(socket, true, kTCPKeepAliveSeconds);
return 0;
}
@@ -560,6 +562,16 @@ bool TCPClientSocketLibevent::SetSendBufferSize(int32 size) {
return rv == 0;
}
+bool TCPClientSocketLibevent::SetKeepAlive(bool enable, int delay) {
+ int socket = socket_ != kInvalidSocket ? socket_ : bound_socket_;
+ return SetTCPKeepAlive(socket, enable, delay);
+}
+
+bool TCPClientSocketLibevent::SetNoDelay(bool no_delay) {
+ int socket = socket_ != kInvalidSocket ? socket_ : bound_socket_;
+ return SetTCPNoDelay(socket, no_delay);
+}
+
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 b984649..f66c8c1 100644
--- a/net/socket/tcp_client_socket_libevent.h
+++ b/net/socket/tcp_client_socket_libevent.h
@@ -70,6 +70,9 @@ class NET_EXPORT_PRIVATE TCPClientSocketLibevent : public StreamSocket,
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 {
diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc
index 4990887..6ffd821 100644
--- a/net/socket/tcp_client_socket_win.cc
+++ b/net/socket/tcp_client_socket_win.cc
@@ -27,6 +27,8 @@ 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));
@@ -41,6 +43,57 @@ bool SetSocketSendBufferSize(SOCKET socket, int32 size) {
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) {
@@ -60,50 +113,8 @@ int SetupSocket(SOCKET socket) {
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.
+ DisableNagle(socket, true);
+ SetTCPKeepAlive(socket, true, kTCPKeepAliveSeconds);
return 0;
}
@@ -782,6 +793,14 @@ bool TCPClientSocketWin::SetSendBufferSize(int32 size) {
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);
diff --git a/net/socket/tcp_client_socket_win.h b/net/socket/tcp_client_socket_win.h
index c17a0b9..14720ae 100644
--- a/net/socket/tcp_client_socket_win.h
+++ b/net/socket/tcp_client_socket_win.h
@@ -68,6 +68,9 @@ class NET_EXPORT TCPClientSocketWin : public StreamSocket,
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 {