diff options
author | gagansingh@google.com <gagansingh@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-24 16:47:06 +0000 |
---|---|---|
committer | gagansingh@google.com <gagansingh@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-24 16:47:06 +0000 |
commit | 0ca76cb6e3d1ca12d55bab9900a8f0a650c6db09 (patch) | |
tree | f03dd8bc67014c738f70a1c1bb20f62af72902e6 /net/socket | |
parent | 591805d2915ce33ccd61eb624924f14f95b5c15d (diff) | |
download | chromium_src-0ca76cb6e3d1ca12d55bab9900a8f0a650c6db09.zip chromium_src-0ca76cb6e3d1ca12d55bab9900a8f0a650c6db09.tar.gz chromium_src-0ca76cb6e3d1ca12d55bab9900a8f0a650c6db09.tar.bz2 |
Warmth of a connection (cwnd) is estimated by the amount of data written to the socket.
Choosing the warmest connection would mean faster resource load times.
idle time is the time a socket has remained idle (no http requests being served on it).
Probability of server resetting a connection increases with idle time duration.
Using a cost function that takes into account bytes transferred and idle time to pick best connection to schedule http requests on.
CODEREVIEW done in http://codereview.chromium.org/6990036/
Contributed by gagansingh@google.com
Review URL: http://codereview.chromium.org/7189055
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90373 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/socket')
25 files changed, 353 insertions, 9 deletions
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc index ddcbba6..d39890b 100644 --- a/net/socket/client_socket_pool_base.cc +++ b/net/socket/client_socket_pool_base.cc @@ -4,11 +4,14 @@ #include "net/socket/client_socket_pool_base.h" +#include <math.h> #include "base/compiler_specific.h" #include "base/format_macros.h" +#include "base/logging.h" #include "base/message_loop.h" #include "base/metrics/stats_counters.h" #include "base/stl_util-inl.h" +#include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/time.h" #include "base/values.h" @@ -32,10 +35,33 @@ const int kCleanupInterval = 10; // DO NOT INCREASE THIS TIMEOUT. // after a certain timeout has passed without receiving an ACK. bool g_connect_backup_jobs_enabled = true; +double g_socket_reuse_policy_penalty_exponent = -1; +int g_socket_reuse_policy = -1; + } // namespace namespace net { +int GetSocketReusePolicy() { + return g_socket_reuse_policy; +} + +void SetSocketReusePolicy(int policy) { + DCHECK_GE(policy, 0); + DCHECK_LE(policy, 2); + if (policy > 2 || policy < 0) { + LOG(ERROR) << "Invalid socket reuse policy"; + return; + } + + double exponents[] = { 0, 0.25, -1 }; + g_socket_reuse_policy_penalty_exponent = exponents[policy]; + g_socket_reuse_policy = policy; + + VLOG(1) << "Setting g_socket_reuse_policy_penalty_exponent = " + << g_socket_reuse_policy_penalty_exponent; +} + ConnectJob::ConnectJob(const std::string& group_name, base::TimeDelta timeout_duration, Delegate* delegate, @@ -363,6 +389,7 @@ bool ClientSocketPoolBaseHelper::AssignIdleSocketToGroup( const Request* request, Group* group) { std::list<IdleSocket>* idle_sockets = group->mutable_idle_sockets(); std::list<IdleSocket>::iterator idle_socket_it = idle_sockets->end(); + double max_score = -1; // Iterate through the idle sockets forwards (oldest to newest) // * Delete any disconnected ones. @@ -379,7 +406,22 @@ bool ClientSocketPoolBaseHelper::AssignIdleSocketToGroup( if (it->socket->WasEverUsed()) { // We found one we can reuse! - idle_socket_it = it; + double score = 0; + int64 bytes_read = it->socket->NumBytesRead(); + double num_kb = static_cast<double>(bytes_read) / 1024.0; + int idle_time_sec = (base::TimeTicks::Now() - it->start_time).InSeconds(); + idle_time_sec = std::max(1, idle_time_sec); + + if (g_socket_reuse_policy_penalty_exponent >= 0 && num_kb >= 0) { + score = num_kb / pow(idle_time_sec, + g_socket_reuse_policy_penalty_exponent); + } + + // Equality to prefer recently used connection. + if (score >= max_score) { + idle_socket_it = it; + max_score = score; + } } ++it; diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h index a5defdf..2490c02 100644 --- a/net/socket/client_socket_pool_base.h +++ b/net/socket/client_socket_pool_base.h @@ -50,6 +50,13 @@ namespace net { class ClientSocketHandle; +// Returns the client socket reuse policy. +int GetSocketReusePolicy(); + +// Sets the client socket reuse policy. +// NOTE: 'policy' should be a valid ClientSocketReusePolicy enum value. +NET_API void SetSocketReusePolicy(int policy); + // ConnectJob provides an abstract interface for "connecting" a socket. // The connection may involve host resolution, tcp connection, ssl connection, // etc. @@ -167,6 +174,17 @@ class NET_TEST ClientSocketPoolBaseHelper NO_IDLE_SOCKETS = 0x1, // Do not return an idle socket. Create a new one. }; + enum ClientSocketReusePolicy { + // Socket with largest amount of bytes transferred. + USE_WARMEST_SOCKET = 0, + + // Socket which scores highest on large bytes transferred and low idle time. + USE_WARM_SOCKET = 1, + + // Socket which was most recently used. + USE_LAST_ACCESSED_SOCKET = 2, + }; + class NET_TEST Request { public: Request(ClientSocketHandle* handle, diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc index 4864001..6cdbdd3 100644 --- a/net/socket/client_socket_pool_base_unittest.cc +++ b/net/socket/client_socket_pool_base_unittest.cc @@ -45,12 +45,14 @@ typedef ClientSocketPoolBase<TestSocketParams> TestClientSocketPoolBase; class MockClientSocket : public StreamSocket { public: - MockClientSocket() : connected_(false), was_used_to_convey_data_(false) {} + MockClientSocket() : connected_(false), was_used_to_convey_data_(false), + num_bytes_read_(0) {} // Socket methods: virtual int Read( - IOBuffer* /* buf */, int /* len */, CompletionCallback* /* callback */) { - return ERR_UNEXPECTED; + IOBuffer* /* buf */, int len, CompletionCallback* /* callback */) { + num_bytes_read_ += len; + return len; } virtual int Write( @@ -86,13 +88,22 @@ class MockClientSocket : public StreamSocket { virtual void SetSubresourceSpeculation() {} virtual void SetOmniboxSpeculation() {} - virtual bool WasEverUsed() const { return was_used_to_convey_data_; } + virtual bool WasEverUsed() const { + return was_used_to_convey_data_ || num_bytes_read_ > 0; + } virtual bool UsingTCPFastOpen() const { return false; } + virtual int64 NumBytesRead() const { return num_bytes_read_; } + virtual base::TimeDelta GetConnectTimeMicros() const { + static const base::TimeDelta kDummyConnectTimeMicros = + base::TimeDelta::FromMicroseconds(10); + return kDummyConnectTimeMicros; // Dummy value. + } private: bool connected_; BoundNetLog net_log_; bool was_used_to_convey_data_; + int num_bytes_read_; DISALLOW_COPY_AND_ASSIGN(MockClientSocket); }; @@ -604,6 +615,71 @@ class ClientSocketPoolBaseTest : public testing::Test { ClientSocketPoolTest test_base_; }; +TEST_F(ClientSocketPoolBaseTest, AssignIdleSocketToGroup_WarmestSocket) { + CreatePool(4, 4); + net::SetSocketReusePolicy(0); + + EXPECT_EQ(OK, StartRequest("a", kDefaultPriority)); + EXPECT_EQ(OK, StartRequest("a", kDefaultPriority)); + EXPECT_EQ(OK, StartRequest("a", kDefaultPriority)); + EXPECT_EQ(OK, StartRequest("a", kDefaultPriority)); + + std::map<int, StreamSocket*> sockets_; + for (size_t i = 0; i < test_base_.requests_size(); i++) { + TestSocketRequest* req = test_base_.request(i); + StreamSocket* s = req->handle()->socket(); + MockClientSocket* sock = static_cast<MockClientSocket*>(s); + CHECK(sock); + sockets_[i] = sock; + sock->Read(NULL, 1024 - i, NULL); + } + + ReleaseAllConnections(ClientSocketPoolTest::KEEP_ALIVE); + + EXPECT_EQ(OK, StartRequest("a", kDefaultPriority)); + TestSocketRequest* req = test_base_.request(test_base_.requests_size() - 1); + + // First socket is warmest. + EXPECT_EQ(sockets_[0], req->handle()->socket()); + + // Test that NumBytes are as expected. + EXPECT_EQ(1024, sockets_[0]->NumBytesRead()); + EXPECT_EQ(1023, sockets_[1]->NumBytesRead()); + EXPECT_EQ(1022, sockets_[2]->NumBytesRead()); + EXPECT_EQ(1021, sockets_[3]->NumBytesRead()); + + ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE); +} + +TEST_F(ClientSocketPoolBaseTest, AssignIdleSocketToGroup_LastAccessedSocket) { + CreatePool(4, 4); + net::SetSocketReusePolicy(2); + + EXPECT_EQ(OK, StartRequest("a", kDefaultPriority)); + EXPECT_EQ(OK, StartRequest("a", kDefaultPriority)); + EXPECT_EQ(OK, StartRequest("a", kDefaultPriority)); + EXPECT_EQ(OK, StartRequest("a", kDefaultPriority)); + + std::map<int, StreamSocket*> sockets_; + for (size_t i = 0; i < test_base_.requests_size(); i++) { + TestSocketRequest* req = test_base_.request(i); + StreamSocket* s = req->handle()->socket(); + MockClientSocket* sock = static_cast<MockClientSocket*>(s); + CHECK(sock); + sockets_[i] = sock; + sock->Read(NULL, 1024 - i, NULL); + } + + ReleaseAllConnections(ClientSocketPoolTest::KEEP_ALIVE); + + EXPECT_EQ(OK, StartRequest("a", kDefaultPriority)); + TestSocketRequest* req = test_base_.request(test_base_.requests_size() - 1); + + // Last socket is most recently accessed. + EXPECT_EQ(sockets_[3], req->handle()->socket()); + ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE); +} + // Even though a timeout is specified, it doesn't time out on a synchronous // completion. TEST_F(ClientSocketPoolBaseTest, ConnectJob_NoTimeoutOnSynchronousCompletion) { diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc index 4e4e8fe..0573f73 100644 --- a/net/socket/socket_test_util.cc +++ b/net/socket/socket_test_util.cc @@ -707,6 +707,7 @@ MockTCPClientSocket::MockTCPClientSocket(const net::AddressList& addresses, addresses_(addresses), data_(data), read_offset_(0), + num_bytes_read_(0), read_data_(false, net::ERR_UNEXPECTED), need_read_data_(true), peer_closed_connection_(false), @@ -811,6 +812,17 @@ bool MockTCPClientSocket::UsingTCPFastOpen() const { return false; } +int64 MockTCPClientSocket::NumBytesRead() const { + return num_bytes_read_; +} + +base::TimeDelta MockTCPClientSocket::GetConnectTimeMicros() const { + // Dummy value. + static const base::TimeDelta kTestingConnectTimeMicros = + base::TimeDelta::FromMicroseconds(20); + return kTestingConnectTimeMicros; +} + void MockTCPClientSocket::OnReadComplete(const MockRead& data) { // There must be a read pending. DCHECK(pending_buf_); @@ -853,6 +865,7 @@ int MockTCPClientSocket::CompleteRead() { result = std::min(buf_len, read_data_.data_len - read_offset_); memcpy(buf->data(), read_data_.data + read_offset_, result); read_offset_ += result; + num_bytes_read_ += result; if (read_offset_ == read_data_.data_len) { need_read_data_ = true; read_offset_ = 0; @@ -1001,6 +1014,14 @@ bool DeterministicMockTCPClientSocket::UsingTCPFastOpen() const { return false; } +int64 DeterministicMockTCPClientSocket::NumBytesRead() const { + return -1; +} + +base::TimeDelta DeterministicMockTCPClientSocket::GetConnectTimeMicros() const { + return base::TimeDelta::FromMicroseconds(-1); +} + void DeterministicMockTCPClientSocket::OnReadComplete(const MockRead& data) {} class MockSSLClientSocket::ConnectCallback @@ -1094,6 +1115,14 @@ bool MockSSLClientSocket::UsingTCPFastOpen() const { return transport_->socket()->UsingTCPFastOpen(); } +int64 MockSSLClientSocket::NumBytesRead() const { + return -1; +} + +base::TimeDelta MockSSLClientSocket::GetConnectTimeMicros() const { + return base::TimeDelta::FromMicroseconds(-1); +} + void MockSSLClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) { ssl_info->Reset(); ssl_info->cert = data_->cert_; diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h index 9a31288..d1f4816 100644 --- a/net/socket/socket_test_util.h +++ b/net/socket/socket_test_util.h @@ -631,6 +631,8 @@ class MockTCPClientSocket : public MockClientSocket { virtual int GetPeerAddress(AddressList* address) const; virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; // MockClientSocket: virtual void OnReadComplete(const MockRead& data); @@ -642,6 +644,7 @@ class MockTCPClientSocket : public MockClientSocket { net::SocketDataProvider* data_; int read_offset_; + int num_bytes_read_; net::MockRead read_data_; bool need_read_data_; @@ -683,6 +686,8 @@ class DeterministicMockTCPClientSocket : public MockClientSocket, virtual bool IsConnectedAndIdle() const; virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; // MockClientSocket: virtual void OnReadComplete(const MockRead& data); @@ -724,6 +729,8 @@ class MockSSLClientSocket : public MockClientSocket { virtual bool IsConnected() const; virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; // SSLClientSocket methods: virtual void GetSSLInfo(net::SSLInfo* ssl_info); diff --git a/net/socket/socks5_client_socket.cc b/net/socket/socks5_client_socket.cc index 7a1c10d..2f5cee4 100644 --- a/net/socket/socks5_client_socket.cc +++ b/net/socket/socks5_client_socket.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -142,6 +142,22 @@ bool SOCKS5ClientSocket::UsingTCPFastOpen() const { return false; } +int64 SOCKS5ClientSocket::NumBytesRead() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->NumBytesRead(); + } + NOTREACHED(); + return -1; +} + +base::TimeDelta SOCKS5ClientSocket::GetConnectTimeMicros() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->GetConnectTimeMicros(); + } + NOTREACHED(); + return base::TimeDelta::FromMicroseconds(-1); +} + // Read is called by the transport layer above to read. This can only be done // if the SOCKS handshake is complete. int SOCKS5ClientSocket::Read(IOBuffer* buf, int buf_len, diff --git a/net/socket/socks5_client_socket.h b/net/socket/socks5_client_socket.h index 955ad90..a9d30df 100644 --- a/net/socket/socks5_client_socket.h +++ b/net/socket/socks5_client_socket.h @@ -60,6 +60,8 @@ class NET_TEST SOCKS5ClientSocket : public StreamSocket { virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/socks_client_socket.cc b/net/socket/socks_client_socket.cc index d885f15..4a45b01 100644 --- a/net/socket/socks_client_socket.cc +++ b/net/socket/socks_client_socket.cc @@ -169,6 +169,22 @@ bool SOCKSClientSocket::UsingTCPFastOpen() const { return false; } +int64 SOCKSClientSocket::NumBytesRead() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->NumBytesRead(); + } + NOTREACHED(); + return -1; +} + +base::TimeDelta SOCKSClientSocket::GetConnectTimeMicros() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->GetConnectTimeMicros(); + } + NOTREACHED(); + return base::TimeDelta::FromMicroseconds(-1); +} + // Read is called by the transport layer above to read. This can only be done // if the SOCKS handshake is complete. diff --git a/net/socket/socks_client_socket.h b/net/socket/socks_client_socket.h index 7c4ba35..286538f 100644 --- a/net/socket/socks_client_socket.h +++ b/net/socket/socks_client_socket.h @@ -58,6 +58,8 @@ class NET_TEST SOCKSClientSocket : public StreamSocket { virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/ssl_client_socket_mac.cc b/net/socket/ssl_client_socket_mac.cc index 2fc11ec..c38b78a 100644 --- a/net/socket/ssl_client_socket_mac.cc +++ b/net/socket/ssl_client_socket_mac.cc @@ -658,6 +658,22 @@ bool SSLClientSocketMac::UsingTCPFastOpen() const { return false; } +int64 SSLClientSocketMac::NumBytesRead() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->NumBytesRead(); + } + NOTREACHED(); + return -1; +} + +base::TimeDelta SSLClientSocketMac::GetConnectTimeMicros() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->GetConnectTimeMicros(); + } + NOTREACHED(); + return base::TimeDelta::FromMicroseconds(-1); +} + int SSLClientSocketMac::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(completed_handshake()); diff --git a/net/socket/ssl_client_socket_mac.h b/net/socket/ssl_client_socket_mac.h index 4dbffe6..8ef33e9 100644 --- a/net/socket/ssl_client_socket_mac.h +++ b/net/socket/ssl_client_socket_mac.h @@ -57,6 +57,8 @@ class SSLClientSocketMac : public SSLClientSocket { virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 18b4e58..9657026 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -714,6 +714,22 @@ bool SSLClientSocketNSS::UsingTCPFastOpen() const { return false; } +int64 SSLClientSocketNSS::NumBytesRead() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->NumBytesRead(); + } + NOTREACHED(); + return -1; +} + +base::TimeDelta SSLClientSocketNSS::GetConnectTimeMicros() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->GetConnectTimeMicros(); + } + NOTREACHED(); + return base::TimeDelta::FromMicroseconds(-1); +} + int SSLClientSocketNSS::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { EnterFunction(buf_len); diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h index 1c5d80e..7d2f7cf 100644 --- a/net/socket/ssl_client_socket_nss.h +++ b/net/socket/ssl_client_socket_nss.h @@ -75,6 +75,8 @@ class SSLClientSocketNSS : public SSLClientSocket { virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/ssl_client_socket_win.cc b/net/socket/ssl_client_socket_win.cc index 1392e1d..4cc3103 100644 --- a/net/socket/ssl_client_socket_win.cc +++ b/net/socket/ssl_client_socket_win.cc @@ -731,6 +731,22 @@ bool SSLClientSocketWin::UsingTCPFastOpen() const { return false; } +int64 SSLClientSocketWin::NumBytesRead() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->NumBytesRead(); + } + NOTREACHED(); + return -1; +} + +base::TimeDelta SSLClientSocketWin::GetConnectTimeMicros() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->GetConnectTimeMicros(); + } + NOTREACHED(); + return base::TimeDelta::FromMicroseconds(-1); +} + int SSLClientSocketWin::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(completed_handshake()); diff --git a/net/socket/ssl_client_socket_win.h b/net/socket/ssl_client_socket_win.h index fb54c43..59f403a 100644 --- a/net/socket/ssl_client_socket_win.h +++ b/net/socket/ssl_client_socket_win.h @@ -62,6 +62,8 @@ class SSLClientSocketWin : public SSLClientSocket { virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/ssl_server_socket_nss.cc b/net/socket/ssl_server_socket_nss.cc index 0f35ce9c..b272f8e 100644 --- a/net/socket/ssl_server_socket_nss.cc +++ b/net/socket/ssl_server_socket_nss.cc @@ -226,6 +226,14 @@ bool SSLServerSocketNSS::UsingTCPFastOpen() const { return transport_socket_->UsingTCPFastOpen(); } +int64 SSLServerSocketNSS::NumBytesRead() const { + return transport_socket_->NumBytesRead(); +} + +base::TimeDelta SSLServerSocketNSS::GetConnectTimeMicros() const { + return transport_socket_->GetConnectTimeMicros(); +} + int SSLServerSocketNSS::InitializeSSLOptions() { // Transport connected, now hook it up to nss // TODO(port): specify rx and tx buffer sizes separately diff --git a/net/socket/ssl_server_socket_nss.h b/net/socket/ssl_server_socket_nss.h index 366a915..5903c17 100644 --- a/net/socket/ssl_server_socket_nss.h +++ b/net/socket/ssl_server_socket_nss.h @@ -54,6 +54,8 @@ class SSLServerSocketNSS : public SSLServerSocket { virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; private: enum State { diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc index 02f333b..de89c20 100644 --- a/net/socket/ssl_server_socket_unittest.cc +++ b/net/socket/ssl_server_socket_unittest.cc @@ -171,6 +171,14 @@ class FakeSocket : public StreamSocket { return false; } + virtual int64 NumBytesRead() const { + return -1; + } + + virtual base::TimeDelta GetConnectTimeMicros() const { + return base::TimeDelta::FromMicroseconds(-1); + } + private: net::BoundNetLog net_log_; FakeDataChannel* incoming_; diff --git a/net/socket/stream_socket.h b/net/socket/stream_socket.h index 544fbcd..ef2f698 100644 --- a/net/socket/stream_socket.h +++ b/net/socket/stream_socket.h @@ -6,6 +6,7 @@ #define NET_SOCKET_STREAM_SOCKET_H_ #pragma once +#include "base/time.h" #include "net/base/net_log.h" #include "net/socket/socket.h" @@ -79,6 +80,12 @@ class NET_TEST StreamSocket : public Socket { // TCP FastOpen is an experiment with sending data in the TCP SYN packet. virtual bool UsingTCPFastOpen() const = 0; + // Returns the number of bytes successfully read from this socket. + virtual int64 NumBytesRead() const = 0; + + // Returns the connection setup time of this socket. + virtual base::TimeDelta GetConnectTimeMicros() const = 0; + protected: // The following class is only used to gather statistics about the history of // a socket. It is only instantiated and used in basic sockets, such as diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc index 38c3446..27c106c 100644 --- a/net/socket/tcp_client_socket_libevent.cc +++ b/net/socket/tcp_client_socket_libevent.cc @@ -137,7 +137,8 @@ TCPClientSocketLibevent::TCPClientSocketLibevent( net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)), previously_disconnected_(false), use_tcp_fastopen_(false), - tcp_fastopen_connected_(false) { + tcp_fastopen_connected_(false), + num_bytes_read_(0) { scoped_refptr<NetLog::EventParameters> params; if (source.is_valid()) params = new NetLogSourceParameter("source_dependency", source); @@ -298,6 +299,7 @@ int TCPClientSocketLibevent::DoConnect() { // Connect the socket. if (!use_tcp_fastopen_) { + connect_start_time_ = base::TimeTicks::Now(); if (!HANDLE_EINTR(connect(socket_, current_ai_->ai_addr, static_cast<int>(current_ai_->ai_addrlen)))) { // Connected without waiting! @@ -339,6 +341,7 @@ int TCPClientSocketLibevent::DoConnectComplete(int result) { write_socket_watcher_.StopWatchingFileDescriptor(); if (result == OK) { + connect_time_micros_ = base::TimeTicks::Now() - connect_start_time_; use_history_.set_was_ever_connected(); return OK; // Done! } @@ -440,6 +443,7 @@ int TCPClientSocketLibevent::Read(IOBuffer* buf, if (nread >= 0) { base::StatsCounter read_bytes("tcp.read_bytes"); read_bytes.Add(nread); + num_bytes_read_ += static_cast<int64>(nread); if (nread > 0) use_history_.set_was_used_to_convey_data(); net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, nread, @@ -634,6 +638,7 @@ void TCPClientSocketLibevent::DidCompleteRead() { result = bytes_transferred; base::StatsCounter read_bytes("tcp.read_bytes"); read_bytes.Add(bytes_transferred); + num_bytes_read_ += static_cast<int64>(bytes_transferred); if (bytes_transferred > 0) use_history_.set_was_used_to_convey_data(); net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, result, @@ -723,4 +728,12 @@ bool TCPClientSocketLibevent::UsingTCPFastOpen() const { return use_tcp_fastopen_; } +int64 TCPClientSocketLibevent::NumBytesRead() const { + return num_bytes_read_; +} + +base::TimeDelta TCPClientSocketLibevent::GetConnectTimeMicros() const { + return connect_time_micros_; +} + } // namespace net diff --git a/net/socket/tcp_client_socket_libevent.h b/net/socket/tcp_client_socket_libevent.h index 607b9ee..f5e4a28 100644 --- a/net/socket/tcp_client_socket_libevent.h +++ b/net/socket/tcp_client_socket_libevent.h @@ -53,6 +53,8 @@ class TCPClientSocketLibevent : public StreamSocket, base::NonThreadSafe { virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; // Socket methods: // Multiple outstanding requests are not supported. @@ -195,6 +197,10 @@ class TCPClientSocketLibevent : public StreamSocket, base::NonThreadSafe { // True when TCP FastOpen is in use and we have done the connect. bool tcp_fastopen_connected_; + base::TimeTicks connect_start_time_; + base::TimeDelta connect_time_micros_; + int64 num_bytes_read_; + DISALLOW_COPY_AND_ASSIGN(TCPClientSocketLibevent); }; diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc index 71187a1..f4026b7 100644 --- a/net/socket/tcp_client_socket_win.cc +++ b/net/socket/tcp_client_socket_win.cc @@ -323,7 +323,8 @@ TCPClientSocketWin::TCPClientSocketWin(const AddressList& addresses, next_connect_state_(CONNECT_STATE_NONE), connect_os_error_(0), net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)), - previously_disconnected_(false) { + previously_disconnected_(false), + num_bytes_read_(0) { scoped_refptr<NetLog::EventParameters> params; if (source.is_valid()) params = new NetLogSourceParameter("source_dependency", source); @@ -484,6 +485,7 @@ int TCPClientSocketWin::DoConnect() { core_->write_overlapped_.hEvent = WSACreateEvent(); + connect_start_time_ = base::TimeTicks::Now(); if (!connect(socket_, ai->ai_addr, static_cast<int>(ai->ai_addrlen))) { // Connected without waiting! // @@ -522,6 +524,7 @@ int TCPClientSocketWin::DoConnectComplete(int result) { net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, params); if (result == OK) { + connect_time_micros_ = base::TimeTicks::Now() - connect_start_time_; use_history_.set_was_ever_connected(); return OK; // Done! } @@ -658,6 +661,14 @@ bool TCPClientSocketWin::UsingTCPFastOpen() const { return false; } +int64 TCPClientSocketWin::NumBytesRead() const { + return num_bytes_read_; +} + +base::TimeDelta TCPClientSocketWin::GetConnectTimeMicros() const { + return connect_time_micros_; +} + int TCPClientSocketWin::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { @@ -688,6 +699,7 @@ int TCPClientSocketWin::Read(IOBuffer* buf, base::MemoryDebug::MarkAsInitialized(core_->read_buffer_.buf, num); base::StatsCounter read_bytes("tcp.read_bytes"); read_bytes.Add(num); + num_bytes_read_ += num; if (num > 0) use_history_.set_was_used_to_convey_data(); net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, num, @@ -858,6 +870,7 @@ void TCPClientSocketWin::DidCompleteRead() { if (ok) { base::StatsCounter read_bytes("tcp.read_bytes"); read_bytes.Add(num_bytes); + num_bytes_read_ += num_bytes; if (num_bytes > 0) use_history_.set_was_used_to_convey_data(); net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, diff --git a/net/socket/tcp_client_socket_win.h b/net/socket/tcp_client_socket_win.h index 3282772..b1f9f3b 100644 --- a/net/socket/tcp_client_socket_win.h +++ b/net/socket/tcp_client_socket_win.h @@ -53,6 +53,8 @@ class NET_API TCPClientSocketWin : public StreamSocket, virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; // Socket methods: // Multiple outstanding requests are not supported. @@ -141,6 +143,10 @@ class NET_API TCPClientSocketWin : public StreamSocket, // histograms. UseHistory use_history_; + base::TimeTicks connect_start_time_; + base::TimeDelta connect_time_micros_; + int64 num_bytes_read_; + DISALLOW_COPY_AND_ASSIGN(TCPClientSocketWin); }; diff --git a/net/socket/transport_client_socket_pool_unittest.cc b/net/socket/transport_client_socket_pool_unittest.cc index 5716a4d..d12cd9d 100644 --- a/net/socket/transport_client_socket_pool_unittest.cc +++ b/net/socket/transport_client_socket_pool_unittest.cc @@ -85,6 +85,10 @@ class MockClientSocket : public StreamSocket { virtual void SetOmniboxSpeculation() {} virtual bool WasEverUsed() const { return false; } virtual bool UsingTCPFastOpen() const { return false; } + virtual int64 NumBytesRead() const { return -1; } + virtual base::TimeDelta GetConnectTimeMicros() const { + return base::TimeDelta::FromMicroseconds(-1); + } // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, @@ -135,6 +139,10 @@ class MockFailingClientSocket : public StreamSocket { virtual void SetOmniboxSpeculation() {} virtual bool WasEverUsed() const { return false; } virtual bool UsingTCPFastOpen() const { return false; } + virtual int64 NumBytesRead() const { return -1; } + virtual base::TimeDelta GetConnectTimeMicros() const { + return base::TimeDelta::FromMicroseconds(-1); + } // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, @@ -209,6 +217,10 @@ class MockPendingClientSocket : public StreamSocket { virtual void SetOmniboxSpeculation() {} virtual bool WasEverUsed() const { return false; } virtual bool UsingTCPFastOpen() const { return false; } + virtual int64 NumBytesRead() const { return -1; } + virtual base::TimeDelta GetConnectTimeMicros() const { + return base::TimeDelta::FromMicroseconds(-1); + } // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, diff --git a/net/socket/transport_client_socket_unittest.cc b/net/socket/transport_client_socket_unittest.cc index bb0be35..8f377e5 100644 --- a/net/socket/transport_client_socket_unittest.cc +++ b/net/socket/transport_client_socket_unittest.cc @@ -277,6 +277,8 @@ TEST_P(TransportClientSocketTest, Read) { rv = sock_->Read(buf, 4096, &callback); ASSERT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(static_cast<int64>(std::string(kServerReply).size()), + sock_->NumBytesRead()); CloseServerSocket(); EXPECT_EQ(0, callback.WaitForResult()); } @@ -309,6 +311,8 @@ TEST_P(TransportClientSocketTest, Read_SmallChunks) { // then close the server socket, and note the close. rv = sock_->Read(buf, 1, &callback); + EXPECT_EQ(static_cast<int64>(std::string(kServerReply).size()), + sock_->NumBytesRead()); ASSERT_EQ(ERR_IO_PENDING, rv); CloseServerSocket(); EXPECT_EQ(0, callback.WaitForResult()); @@ -329,9 +333,12 @@ TEST_P(TransportClientSocketTest, Read_Interrupted) { scoped_refptr<IOBuffer> buf(new IOBuffer(16)); rv = sock_->Read(buf, 16, &callback); EXPECT_TRUE(rv >= 0 || rv == ERR_IO_PENDING); + EXPECT_EQ(0, sock_->NumBytesRead()); - if (rv == ERR_IO_PENDING) + if (rv == ERR_IO_PENDING) { rv = callback.WaitForResult(); + EXPECT_EQ(16, sock_->NumBytesRead()); + } EXPECT_NE(0, rv); } |