diff options
author | gagansingh@google.com <gagansingh@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-27 17:26:41 +0000 |
---|---|---|
committer | gagansingh@google.com <gagansingh@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-27 17:26:41 +0000 |
commit | 5e6efa537dc4c06912f0bb42f16991c7ee7eafbc (patch) | |
tree | bd1336ab3629d3afd20c57c2c9d6de04603e1a10 | |
parent | 0d1293040f1f79e3cddc52ba18fc6ec85f140800 (diff) | |
download | chromium_src-5e6efa537dc4c06912f0bb42f16991c7ee7eafbc.zip chromium_src-5e6efa537dc4c06912f0bb42f16991c7ee7eafbc.tar.gz chromium_src-5e6efa537dc4c06912f0bb42f16991c7ee7eafbc.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
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=90373
Reverted: http://codereview.chromium.org/7255002 :(
Have fixed 2 things since:
1. Removed LOG(ERROR) from http_basic_stream.cc that was causing layout tests to fail.
2. Initialized class variables in http_basic_stream.cc that was causing uninitialized memory bugs in valgrind: http://code.google.com/p/chromium/issues/detail?id=87423
Review URL: http://codereview.chromium.org/7251004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90601 0039d316-1c4b-4281-b951-d872f2087c98
54 files changed, 681 insertions, 12 deletions
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index 6148ed5..2d1bfd1 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -101,6 +101,7 @@ #include "net/base/cookie_monster.h" #include "net/base/net_module.h" #include "net/base/network_change_notifier.h" +#include "net/http/http_basic_stream.h" #include "net/http/http_network_layer.h" #include "net/http/http_stream_factory.h" #include "net/socket/client_socket_pool_base.h" @@ -208,6 +209,18 @@ #include "views/touchui/touch_factory.h" #endif +namespace { +void SetSocketReusePolicy(int warmest_socket_trial_group, + const int socket_policy[], + int num_groups) { + const int* result = std::find(socket_policy, socket_policy + num_groups, + warmest_socket_trial_group); + DCHECK_NE(result, socket_policy + num_groups) + << "Not a valid socket reuse policy group"; + net::SetSocketReusePolicy(result - socket_policy); +} +} + namespace net { class NetLog; } // namespace net @@ -474,6 +487,46 @@ void BrowserMainParts::SpdyFieldTrial() { } } +// If --socket-reuse-policy is not specified, run an A/B test for choosing the +// warmest socket. +void BrowserMainParts::WarmConnectionFieldTrial() { + const CommandLine& command_line = parsed_command_line(); + if (command_line.HasSwitch(switches::kSocketReusePolicy)) { + std::string socket_reuse_policy_str = command_line.GetSwitchValueASCII( + switches::kSocketReusePolicy); + int policy = -1; + base::StringToInt(socket_reuse_policy_str, &policy); + + const int policy_list[] = { 0, 1, 2 }; + VLOG(1) << "Setting socket_reuse_policy = " << policy; + SetSocketReusePolicy(policy, policy_list, arraysize(policy_list)); + return; + } + + const base::FieldTrial::Probability kWarmSocketDivisor = 100; + const base::FieldTrial::Probability kWarmSocketProbability = 33; + + // After January 30, 2013 builds, it will always be in default group. + scoped_refptr<base::FieldTrial> warmest_socket_trial( + new base::FieldTrial( + "WarmSocketImpact", kWarmSocketDivisor, "last_accessed_socket", + 2013, 1, 30)); + + // Default value is USE_LAST_ACCESSED_SOCKET. + const int last_accessed_socket = warmest_socket_trial->kDefaultGroupNumber; + const int warmest_socket = warmest_socket_trial->AppendGroup( + "warmest_socket", kWarmSocketProbability); + const int warm_socket = warmest_socket_trial->AppendGroup( + "warm_socket", kWarmSocketProbability); + + const int warmest_socket_trial_group = warmest_socket_trial->group(); + + const int policy_list[] = { warmest_socket, warm_socket, + last_accessed_socket }; + SetSocketReusePolicy(warmest_socket_trial_group, policy_list, + arraysize(policy_list)); +} + // If neither --enable-connect-backup-jobs or --disable-connect-backup-jobs is // specified, run an A/B test for automatically establishing backup TCP // connections when a certain timeout value is exceeded. @@ -593,6 +646,7 @@ void BrowserMainParts::SetupFieldTrials(bool metrics_recording_enabled) { prerender::ConfigurePrefetchAndPrerender(parsed_command_line()); SpdyFieldTrial(); ConnectBackupJobsFieldTrial(); + WarmConnectionFieldTrial(); } // ----------------------------------------------------------------------------- diff --git a/chrome/browser/browser_main.h b/chrome/browser/browser_main.h index 417c622..e13a12d 100644 --- a/chrome/browser/browser_main.h +++ b/chrome/browser/browser_main.h @@ -7,6 +7,7 @@ #pragma once #include "base/basictypes.h" +#include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "base/metrics/field_trial.h" #include "base/tracked_objects.h" @@ -129,6 +130,9 @@ class BrowserMainParts { // specified timeout value is reached. void ConnectBackupJobsFieldTrial(); + // A/B test for warmest socket vs. most recently used socket. + void WarmConnectionFieldTrial(); + // Used to initialize NSPR where appropriate. virtual void InitializeSSL() = 0; @@ -171,6 +175,9 @@ class BrowserMainParts { // Initialized in SetupMetricsAndFieldTrials. scoped_refptr<FieldTrialSynchronizer> field_trial_synchronizer_; + FRIEND_TEST(BrowserMainTest, WarmConnectionFieldTrial_WarmestSocket); + FRIEND_TEST(BrowserMainTest, WarmConnectionFieldTrial_Random); + FRIEND_TEST(BrowserMainTest, WarmConnectionFieldTrial_Invalid); DISALLOW_COPY_AND_ASSIGN(BrowserMainParts); }; diff --git a/chrome/browser/browser_main_unittest.cc b/chrome/browser/browser_main_unittest.cc new file mode 100644 index 0000000..bc5487d --- /dev/null +++ b/chrome/browser/browser_main_unittest.cc @@ -0,0 +1,68 @@ +// 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. + +#include "chrome/browser/browser_main.h" + +#include <string> +#include <vector> +#include "base/command_line.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/testing_pref_service.h" +#include "content/common/main_function_params.h" +#include "content/common/sandbox_init_wrapper.h" +#include "net/socket/client_socket_pool_base.h" +#include "testing/gtest/include/gtest/gtest.h" + +class BrowserMainTest : public testing::Test { + public: + BrowserMainTest() : command_line_(CommandLine::NO_PROGRAM) {} + protected: + virtual void SetUp() { + sandbox_init_wrapper_.reset(new SandboxInitWrapper()); + } + + scoped_ptr<SandboxInitWrapper> sandbox_init_wrapper_; + TestingPrefService pref_service_; + CommandLine command_line_; +}; + +TEST_F(BrowserMainTest, WarmConnectionFieldTrial_WarmestSocket) { + command_line_.AppendSwitchASCII(switches::kSocketReusePolicy, "0"); + + scoped_ptr<MainFunctionParams> params( + new MainFunctionParams(command_line_, *sandbox_init_wrapper_, NULL)); + scoped_ptr<BrowserMainParts> bw(BrowserMainParts::CreateBrowserMainParts( + *params)); + + bw->WarmConnectionFieldTrial(); + + EXPECT_EQ(0, net::GetSocketReusePolicy()); +} + +TEST_F(BrowserMainTest, WarmConnectionFieldTrial_Random) { + scoped_ptr<MainFunctionParams> params( + new MainFunctionParams(command_line_, *sandbox_init_wrapper_, NULL)); + scoped_ptr<BrowserMainParts> bw(BrowserMainParts::CreateBrowserMainParts( + *params)); + + const int kNumRuns = 1000; + for (int i = 0; i < kNumRuns; i++) { + bw->WarmConnectionFieldTrial(); + int val = net::GetSocketReusePolicy(); + EXPECT_LE(val, 2); + EXPECT_GE(val, 0); + } +} + +TEST_F(BrowserMainTest, WarmConnectionFieldTrial_Invalid) { + command_line_.AppendSwitchASCII(switches::kSocketReusePolicy, "100"); + + scoped_ptr<MainFunctionParams> params( + new MainFunctionParams(command_line_, *sandbox_init_wrapper_, NULL)); + scoped_ptr<BrowserMainParts> bw(BrowserMainParts::CreateBrowserMainParts( + *params)); + + EXPECT_DEBUG_DEATH(bw->WarmConnectionFieldTrial(), + "Not a valid socket reuse policy group"); +} diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 327756a..1214adb 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1292,6 +1292,7 @@ 'browser/bookmarks/bookmark_utils_unittest.cc', 'browser/browser_about_handler_unittest.cc', 'browser/browser_commands_unittest.cc', + 'browser/browser_main_unittest.cc', 'browser/browser_url_handler_unittest.cc', 'browser/browsing_data_appcache_helper_unittest.cc', 'browser/browsing_data_database_helper_unittest.cc', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index d8bacf5..914ed25 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -931,6 +931,10 @@ const char kSilentDumpOnDCHECK[] = "silent-dump-on-dcheck"; // Replaces the buffered data source for <audio> and <video> with a simplified // resource loader that downloads the entire resource into memory. +// Choose the socket reuse policy specified. The value should be of type +// enum ClientSocketReusePolicy. +const char kSocketReusePolicy[] = "socket-reuse-policy"; + // Start the browser maximized, regardless of any previous settings. const char kStartMaximized[] = "start-maximized"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index f84cb58..aa3dcd0 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -252,6 +252,7 @@ extern const char kShowCompositedLayerTree[]; extern const char kShowFPSCounter[]; extern const char kShowIcons[]; extern const char kSilentDumpOnDCHECK[]; +extern const char kSocketReusePolicy[]; extern const char kStartMaximized[]; extern const char kSyncAllowInsecureXmppConnection[]; extern const char kSyncInvalidateXmppLogin[]; diff --git a/content/browser/renderer_host/p2p/socket_host_test_utils.h b/content/browser/renderer_host/p2p/socket_host_test_utils.h index bc829fd..a24d094 100644 --- a/content/browser/renderer_host/p2p/socket_host_test_utils.h +++ b/content/browser/renderer_host/p2p/socket_host_test_utils.h @@ -73,6 +73,8 @@ class FakeSocket : public net::StreamSocket { virtual void SetOmniboxSpeculation() OVERRIDE; virtual bool WasEverUsed() const OVERRIDE; virtual bool UsingTCPFastOpen() const OVERRIDE; + virtual int64 NumBytesRead() const OVERRIDE; + virtual base::TimeDelta GetConnectTimeMicros() const OVERRIDE; private: bool read_pending_; @@ -209,6 +211,14 @@ bool FakeSocket::UsingTCPFastOpen() const { return false; } +int64 FakeSocket::NumBytesRead() const { + return -1; +} + +base::TimeDelta FakeSocket::GetConnectTimeMicros() const { + return base::TimeDelta::FromMicroseconds(-1); +} + void CreateRandomPacket(std::vector<char>* packet) { size_t size = kStunHeaderSize + rand() % 1000; packet->resize(size); diff --git a/jingle/glue/pseudotcp_adapter.cc b/jingle/glue/pseudotcp_adapter.cc index 7136bbc..b86c805 100644 --- a/jingle/glue/pseudotcp_adapter.cc +++ b/jingle/glue/pseudotcp_adapter.cc @@ -463,4 +463,14 @@ bool PseudoTcpAdapter::UsingTCPFastOpen() const { return false; } +int64 PseudoTcpAdapter::NumBytesRead() const { + DCHECK(CalledOnValidThread()); + return -1; +} + +base::TimeDelta PseudoTcpAdapter::GetConnectTimeMicros() const { + DCHECK(CalledOnValidThread()); + return base::TimeDelta::FromMicroseconds(-1); +} + } // namespace jingle_glue diff --git a/jingle/glue/pseudotcp_adapter.h b/jingle/glue/pseudotcp_adapter.h index 977dcf1..e53d722 100644 --- a/jingle/glue/pseudotcp_adapter.h +++ b/jingle/glue/pseudotcp_adapter.h @@ -49,6 +49,8 @@ class PseudoTcpAdapter : public net::StreamSocket, base::NonThreadSafe { virtual void SetOmniboxSpeculation() OVERRIDE; virtual bool WasEverUsed() const OVERRIDE; virtual bool UsingTCPFastOpen() const OVERRIDE; + virtual int64 NumBytesRead() const OVERRIDE; + virtual base::TimeDelta GetConnectTimeMicros() const OVERRIDE; private: class Core; diff --git a/jingle/notifier/base/fake_ssl_client_socket.cc b/jingle/notifier/base/fake_ssl_client_socket.cc index 8838f13..20a3f39 100644 --- a/jingle/notifier/base/fake_ssl_client_socket.cc +++ b/jingle/notifier/base/fake_ssl_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. @@ -336,4 +336,12 @@ bool FakeSSLClientSocket::UsingTCPFastOpen() const { return transport_socket_->UsingTCPFastOpen(); } +int64 FakeSSLClientSocket::NumBytesRead() const { + return transport_socket_->NumBytesRead(); +} + +base::TimeDelta FakeSSLClientSocket::GetConnectTimeMicros() const { + return transport_socket_->GetConnectTimeMicros(); +} + } // namespace notifier diff --git a/jingle/notifier/base/fake_ssl_client_socket.h b/jingle/notifier/base/fake_ssl_client_socket.h index f2282c2..1688b70 100644 --- a/jingle/notifier/base/fake_ssl_client_socket.h +++ b/jingle/notifier/base/fake_ssl_client_socket.h @@ -62,6 +62,8 @@ class FakeSSLClientSocket : public net::StreamSocket { virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; private: enum HandshakeState { diff --git a/jingle/notifier/base/fake_ssl_client_socket_unittest.cc b/jingle/notifier/base/fake_ssl_client_socket_unittest.cc index 6f5c2da..2fe21b4 100644 --- a/jingle/notifier/base/fake_ssl_client_socket_unittest.cc +++ b/jingle/notifier/base/fake_ssl_client_socket_unittest.cc @@ -62,6 +62,8 @@ class MockClientSocket : public net::StreamSocket { MOCK_METHOD0(SetOmniboxSpeculation, void()); MOCK_CONST_METHOD0(WasEverUsed, bool()); MOCK_CONST_METHOD0(UsingTCPFastOpen, bool()); + MOCK_CONST_METHOD0(NumBytesRead, int64()); + MOCK_CONST_METHOD0(GetConnectTimeMicros, base::TimeDelta()); }; // Break up |data| into a bunch of chunked MockReads/Writes and push diff --git a/jingle/notifier/base/proxy_resolving_client_socket.cc b/jingle/notifier/base/proxy_resolving_client_socket.cc index e34ca1c..4deda20 100644 --- a/jingle/notifier/base/proxy_resolving_client_socket.cc +++ b/jingle/notifier/base/proxy_resolving_client_socket.cc @@ -342,6 +342,20 @@ bool ProxyResolvingClientSocket::UsingTCPFastOpen() const { return false; } +int64 ProxyResolvingClientSocket::NumBytesRead() const { + if (transport_.get() && transport_->socket()) + return transport_->socket()->NumBytesRead(); + NOTREACHED(); + return -1; +} + +base::TimeDelta ProxyResolvingClientSocket::GetConnectTimeMicros() const { + if (transport_.get() && transport_->socket()) + return transport_->socket()->GetConnectTimeMicros(); + NOTREACHED(); + return base::TimeDelta::FromMicroseconds(-1); +} + void ProxyResolvingClientSocket::CloseTransportSocket() { if (transport_.get() && transport_->socket()) transport_->socket()->Disconnect(); diff --git a/jingle/notifier/base/proxy_resolving_client_socket.h b/jingle/notifier/base/proxy_resolving_client_socket.h index 8e95f87..b97315b 100644 --- a/jingle/notifier/base/proxy_resolving_client_socket.h +++ b/jingle/notifier/base/proxy_resolving_client_socket.h @@ -56,6 +56,8 @@ class ProxyResolvingClientSocket : public net::StreamSocket { virtual void SetOmniboxSpeculation() OVERRIDE; virtual bool WasEverUsed() const OVERRIDE; virtual bool UsingTCPFastOpen() const OVERRIDE; + virtual int64 NumBytesRead() const OVERRIDE; + virtual base::TimeDelta GetConnectTimeMicros() const OVERRIDE; private: // Proxy resolution and connection functions. diff --git a/net/curvecp/curvecp_client_socket.cc b/net/curvecp/curvecp_client_socket.cc index f28e839..95b36f6 100644 --- a/net/curvecp/curvecp_client_socket.cc +++ b/net/curvecp/curvecp_client_socket.cc @@ -88,6 +88,14 @@ bool CurveCPClientSocket::UsingTCPFastOpen() const { return false; } +int64 CurveCPClientSocket::NumBytesRead() const { + return -1; +} + +base::TimeDelta CurveCPClientSocket::GetConnectTimeMicros() const { + return base::TimeDelta::FromMicroseconds(-1); +} + int CurveCPClientSocket::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { diff --git a/net/curvecp/curvecp_client_socket.h b/net/curvecp/curvecp_client_socket.h index 213b13d..c9faa63 100644 --- a/net/curvecp/curvecp_client_socket.h +++ b/net/curvecp/curvecp_client_socket.h @@ -36,6 +36,8 @@ class CurveCPClientSocket : 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/http/http_basic_stream.cc b/net/http/http_basic_stream.cc index 6501a59..08785b8 100644 --- a/net/http/http_basic_stream.cc +++ b/net/http/http_basic_stream.cc @@ -4,6 +4,8 @@ #include "net/http/http_basic_stream.h" +#include "base/format_macros.h" +#include "base/metrics/histogram.h" #include "base/stringprintf.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" @@ -12,6 +14,7 @@ #include "net/http/http_stream_parser.h" #include "net/http/http_util.h" #include "net/socket/client_socket_handle.h" +#include "net/socket/client_socket_pool_base.h" namespace net { @@ -22,7 +25,9 @@ HttpBasicStream::HttpBasicStream(ClientSocketHandle* connection, parser_(parser), connection_(connection), using_proxy_(using_proxy), - request_info_(NULL) { + request_info_(NULL), + response_(NULL), + bytes_read_offset_(0) { } HttpBasicStream::~HttpBasicStream() {} @@ -34,6 +39,7 @@ int HttpBasicStream::InitializeStream(const HttpRequestInfo* request_info, request_info_ = request_info; parser_.reset(new HttpStreamParser(connection_.get(), request_info, read_buf_, net_log)); + bytes_read_offset_ = connection_->socket()->NumBytesRead(); return OK; } @@ -50,6 +56,7 @@ int HttpBasicStream::SendRequest(const HttpRequestHeaders& headers, request_line_ = base::StringPrintf("%s %s HTTP/1.1\r\n", request_info_->method.c_str(), path.c_str()); + response_ = response; return parser_->SendRequest(request_line_, headers, request_body, response, callback); } @@ -119,4 +126,44 @@ bool HttpBasicStream::IsSpdyHttpStream() const { return false; } +void HttpBasicStream::LogNumRttVsBytesMetrics() const { + int socket_reuse_policy = GetSocketReusePolicy(); + if (socket_reuse_policy > 2 || socket_reuse_policy < 0) { + return; + } + + int64 total_bytes_read = connection_->socket()->NumBytesRead(); + int64 bytes_received = total_bytes_read - bytes_read_offset_; + int64 num_kb = bytes_received / 1024; + double rtt = connection_->socket()->GetConnectTimeMicros().ToInternalValue(); + rtt /= 1000.0; + + if (num_kb < 1024 && rtt > 0) { // Ignore responses > 1MB + base::TimeDelta duration = base::Time::Now() - + response_->request_time; + double num_rtt = static_cast<double>(duration.InMilliseconds()) / rtt; + int64 num_rtt_scaled = (4 * num_rtt); + + static const char* const kGroups[] = { + "warmest_socket", "warm_socket", "last_accessed_socket" + }; + int bucket = (num_kb / 5) * 5; + const std::string histogram(StringPrintf("Net.Num_RTT_vs_KB_%s_%dKB", + kGroups[socket_reuse_policy], + bucket)); + base::Histogram* counter = base::Histogram::FactoryGet( + histogram, 0, 1000, 2, base::Histogram::kUmaTargetedHistogramFlag); + DCHECK_EQ(histogram, counter->histogram_name()); + counter->Add(num_rtt_scaled); + + VLOG(2) << StringPrintf("%s\nrtt = %f\tnum_rtt = %f\t" + "num_kb = %" PRId64 "\t" + "total bytes = %" PRId64 "\t" + "histogram = %s", + request_line_.data(), + rtt, num_rtt, num_kb, total_bytes_read, + histogram.data()); + } +} + } // namespace net diff --git a/net/http/http_basic_stream.h b/net/http/http_basic_stream.h index 267c7c1..303d68f 100644 --- a/net/http/http_basic_stream.h +++ b/net/http/http_basic_stream.h @@ -81,6 +81,8 @@ class HttpBasicStream : public HttpStream { virtual bool IsSpdyHttpStream() const OVERRIDE; + virtual void LogNumRttVsBytesMetrics() const OVERRIDE; + private: scoped_refptr<GrowableIOBuffer> read_buf_; @@ -94,6 +96,10 @@ class HttpBasicStream : public HttpStream { const HttpRequestInfo* request_info_; + const HttpResponseInfo* response_; + + int64 bytes_read_offset_; + DISALLOW_COPY_AND_ASSIGN(HttpBasicStream); }; diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index d7a65e2..403f60f 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -920,6 +920,7 @@ int HttpNetworkTransaction::DoReadBodyComplete(int result) { // Clean up connection if we are done. if (done) { LogTransactionMetrics(); + stream_->LogNumRttVsBytesMetrics(); stream_->Close(!keep_alive); // Note: we don't reset the stream here. We've closed it, but we still // need it around so that callers can call methods such as diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 65d699e..a1de71f 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -12,6 +12,7 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/memory/scoped_ptr.h" +#include "base/metrics/histogram.h" #include "base/utf_string_conversions.h" #include "net/base/auth.h" #include "net/base/capturing_net_log.h" @@ -333,6 +334,34 @@ static const char kExpectedNPNString[] = "\x08http/1.1\x06spdy/2"; static const char kAlternateProtocolHttpHeader[] = "Alternate-Protocol: 443:npn-spdy/2\r\n\r\n"; +TEST_F(HttpNetworkTransactionTest, LogNumRttVsBytesMetrics_WarmestSocket) { + MockRead data_reads[1000]; + data_reads[0] = MockRead("HTTP/1.0 200 OK\r\n\r\n"); + for (int i = 1; i < 999; i++) { + data_reads[i] = MockRead("Gagan is a good boy!"); + } + data_reads[999] = MockRead(false, OK); + + net::SetSocketReusePolicy(0); + SimpleGetHelperResult out = SimpleGetHelper(data_reads, + arraysize(data_reads)); + + base::Histogram* histogram = NULL; + base::StatisticsRecorder::FindHistogram( + "Net.Num_RTT_vs_KB_warmest_socket_15KB", &histogram); + CHECK(histogram); + + base::Histogram::SampleSet sample_set; + histogram->SnapshotSample(&sample_set); + EXPECT_EQ(1, sample_set.TotalCount()); + + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.0 200 OK", out.status_line); +} + +// TODO(gagansingh): Add test for LogNumRttVsBytesMetrics_LastAccessSocket once +// it is possible to clear histograms from previous tests. + TEST_F(HttpNetworkTransactionTest, Basic) { SessionDependencies session_deps; scoped_ptr<HttpTransaction> trans( diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc index 0a41c1f..584cecd 100644 --- a/net/http/http_proxy_client_socket.cc +++ b/net/http/http_proxy_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. @@ -165,6 +165,22 @@ bool HttpProxyClientSocket::UsingTCPFastOpen() const { return false; } +int64 HttpProxyClientSocket::NumBytesRead() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->NumBytesRead(); + } + NOTREACHED(); + return -1; +} + +base::TimeDelta HttpProxyClientSocket::GetConnectTimeMicros() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->GetConnectTimeMicros(); + } + NOTREACHED(); + return base::TimeDelta::FromMicroseconds(-1); +} + int HttpProxyClientSocket::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(!user_callback_); diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h index bd25bab..b1f32a6 100644 --- a/net/http/http_proxy_client_socket.h +++ b/net/http/http_proxy_client_socket.h @@ -78,6 +78,8 @@ class HttpProxyClientSocket : public ProxyClientSocket { 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/http/http_response_body_drainer_unittest.cc b/net/http/http_response_body_drainer_unittest.cc index 45c826e..47602e9 100644 --- a/net/http/http_response_body_drainer_unittest.cc +++ b/net/http/http_response_body_drainer_unittest.cc @@ -117,6 +117,8 @@ class MockHttpStream : public HttpStream { virtual bool IsSpdyHttpStream() const OVERRIDE { return false; } + virtual void LogNumRttVsBytesMetrics() const OVERRIDE {} + // Methods to tweak/observer mock behavior: void StallReadsForever() { stall_reads_forever_ = true; } diff --git a/net/http/http_stream.h b/net/http/http_stream.h index 7ca2203..66fd97c 100644 --- a/net/http/http_stream.h +++ b/net/http/http_stream.h @@ -135,6 +135,10 @@ class NET_TEST HttpStream { // the HttpStream implementation. This is just a quick hack. virtual bool IsSpdyHttpStream() const = 0; + // Record histogram of number of round trips taken to download the full + // response body vs bytes transferred. + virtual void LogNumRttVsBytesMetrics() const = 0; + private: DISALLOW_COPY_AND_ASSIGN(HttpStream); }; 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 3c5ec13..bc6270d 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! @@ -337,6 +339,7 @@ int TCPClientSocketLibevent::DoConnectComplete(int result) { net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, params); if (result == OK) { + connect_time_micros_ = base::TimeTicks::Now() - connect_start_time_; write_socket_watcher_.StopWatchingFileDescriptor(); use_history_.set_was_ever_connected(); return OK; // Done! @@ -439,6 +442,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, @@ -633,6 +637,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, @@ -722,4 +727,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); } diff --git a/net/spdy/spdy_http_stream.h b/net/spdy/spdy_http_stream.h index 88c627d..b6727e5 100644 --- a/net/spdy/spdy_http_stream.h +++ b/net/spdy/spdy_http_stream.h @@ -68,6 +68,7 @@ class NET_TEST SpdyHttpStream : public SpdyStream::Delegate, public HttpStream { virtual void GetSSLCertRequestInfo( SSLCertRequestInfo* cert_request_info) OVERRIDE; virtual bool IsSpdyHttpStream() const OVERRIDE; + virtual void LogNumRttVsBytesMetrics() const OVERRIDE {} // SpdyStream::Delegate methods: virtual bool OnSendHeadersComplete(int status) OVERRIDE; diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc index 1e6e23a..c5733cf 100644 --- a/net/spdy/spdy_proxy_client_socket.cc +++ b/net/spdy/spdy_proxy_client_socket.cc @@ -138,6 +138,14 @@ bool SpdyProxyClientSocket::UsingTCPFastOpen() const { return false; } +int64 SpdyProxyClientSocket::NumBytesRead() const { + return -1; +} + +base::TimeDelta SpdyProxyClientSocket::GetConnectTimeMicros() const { + return base::TimeDelta::FromMicroseconds(-1); +} + int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(!read_callback_); diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h index d7510d1..e6598ea 100644 --- a/net/spdy/spdy_proxy_client_socket.h +++ b/net/spdy/spdy_proxy_client_socket.h @@ -77,6 +77,8 @@ class NET_TEST SpdyProxyClientSocket : public ProxyClientSocket, 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/remoting/jingle_glue/ssl_socket_adapter.cc b/remoting/jingle_glue/ssl_socket_adapter.cc index 101ce3d..59b42f9 100644 --- a/remoting/jingle_glue/ssl_socket_adapter.cc +++ b/remoting/jingle_glue/ssl_socket_adapter.cc @@ -270,6 +270,16 @@ bool TransportSocket::UsingTCPFastOpen() const { return false; } +int64 TransportSocket::NumBytesRead() const { + NOTREACHED(); + return -1; +} + +base::TimeDelta TransportSocket::GetConnectTimeMicros() const { + NOTREACHED(); + return base::TimeDelta::FromMicroseconds(-1); +} + int TransportSocket::Read(net::IOBuffer* buf, int buf_len, net::CompletionCallback* callback) { DCHECK(buf); diff --git a/remoting/jingle_glue/ssl_socket_adapter.h b/remoting/jingle_glue/ssl_socket_adapter.h index 9acd3f7..388bd1c 100644 --- a/remoting/jingle_glue/ssl_socket_adapter.h +++ b/remoting/jingle_glue/ssl_socket_adapter.h @@ -52,6 +52,8 @@ class TransportSocket : public net::StreamSocket, public sigslot::has_slots<> { virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; virtual bool UsingTCPFastOpen() const; + virtual int64 NumBytesRead() const; + virtual base::TimeDelta GetConnectTimeMicros() const; // net::Socket implementation |