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 [email protected]

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
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc
index 6148ed59..2d1bfd12 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 @@
   }
 }
 
+// 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 @@
   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 @@
   // 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 @@
   // 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..1214adb1 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 @@
 // 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 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..a24d0944 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 @@
   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 @@
   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 @@
   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 977dcf1b..e53d722e 100644
--- a/jingle/glue/pseudotcp_adapter.h
+++ b/jingle/glue/pseudotcp_adapter.h
@@ -49,6 +49,8 @@
   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 @@
   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 f2282c233..1688b70 100644
--- a/jingle/notifier/base/fake_ssl_client_socket.h
+++ b/jingle/notifier/base/fake_ssl_client_socket.h
@@ -62,6 +62,8 @@
   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 6f5c2da4..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 @@
   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 @@
   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 @@
   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 f28e8394..95b36f6 100644
--- a/net/curvecp/curvecp_client_socket.cc
+++ b/net/curvecp/curvecp_client_socket.cc
@@ -88,6 +88,14 @@
   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 213b13d8..c9faa633 100644
--- a/net/curvecp/curvecp_client_socket.h
+++ b/net/curvecp/curvecp_client_socket.h
@@ -36,6 +36,8 @@
   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 @@
       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 @@
   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 @@
   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 @@
   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..303d68f2 100644
--- a/net/http/http_basic_stream.h
+++ b/net/http/http_basic_stream.h
@@ -81,6 +81,8 @@
 
   virtual bool IsSpdyHttpStream() const OVERRIDE;
 
+  virtual void LogNumRttVsBytesMetrics() const OVERRIDE;
+
  private:
   scoped_refptr<GrowableIOBuffer> read_buf_;
 
@@ -94,6 +96,10 @@
 
   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 @@
   // 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 65d699eb8..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 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 0a41c1f7..584cecda 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 @@
   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 @@
   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..47602e9b 100644
--- a/net/http/http_response_body_drainer_unittest.cc
+++ b/net/http/http_response_body_drainer_unittest.cc
@@ -117,6 +117,8 @@
 
   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..66fd97c5 100644
--- a/net/http/http_stream.h
+++ b/net/http/http_stream.h
@@ -135,6 +135,10 @@
   // 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 @@
 // 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 @@
     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 @@
 
     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 @@
 
 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 @@
     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 @@
 
 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 @@
 
   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 @@
   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..0573f7320 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -707,6 +707,7 @@
       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 @@
   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 @@
       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 @@
   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 @@
   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 @@
   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 @@
 
   net::SocketDataProvider* data_;
   int read_offset_;
+  int num_bytes_read_;
   net::MockRead read_data_;
   bool need_read_data_;
 
@@ -683,6 +686,8 @@
   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 @@
   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 7a1c10d7..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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 18b4e588..9657026 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -714,6 +714,22 @@
   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 1c5d80e21..7d2f7cf 100644
--- a/net/socket/ssl_client_socket_nss.h
+++ b/net/socket/ssl_client_socket_nss.h
@@ -75,6 +75,8 @@
   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 1392e1db..4cc3103 100644
--- a/net/socket/ssl_client_socket_win.cc
+++ b/net/socket/ssl_client_socket_win.cc
@@ -731,6 +731,22 @@
   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..59f403a4 100644
--- a/net/socket/ssl_client_socket_win.h
+++ b/net/socket/ssl_client_socket_win.h
@@ -62,6 +62,8 @@
   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 @@
   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..5903c177 100644
--- a/net/socket/ssl_server_socket_nss.h
+++ b/net/socket/ssl_server_socket_nss.h
@@ -54,6 +54,8 @@
   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 02f333b1..de89c207 100644
--- a/net/socket/ssl_server_socket_unittest.cc
+++ b/net/socket/ssl_server_socket_unittest.cc
@@ -171,6 +171,14 @@
     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 @@
   // 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 @@
       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 @@
 
   // 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 @@
   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 @@
   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 @@
     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 @@
   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 @@
   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 @@
   // 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 @@
       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 @@
 
   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 @@
   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 @@
   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 @@
       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 @@
   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 @@
   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 @@
   // 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 5716a4df..d12cd9d 100644
--- a/net/socket/transport_client_socket_pool_unittest.cc
+++ b/net/socket/transport_client_socket_pool_unittest.cc
@@ -85,6 +85,10 @@
   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 @@
   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 @@
   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 bb0be355..8f377e5b 100644
--- a/net/socket/transport_client_socket_unittest.cc
+++ b/net/socket/transport_client_socket_unittest.cc
@@ -277,6 +277,8 @@
 
   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 @@
   // 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 @@
   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 @@
   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..c5733cf9 100644
--- a/net/spdy/spdy_proxy_client_socket.cc
+++ b/net/spdy/spdy_proxy_client_socket.cc
@@ -138,6 +138,14 @@
   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..e6598eab 100644
--- a/net/spdy/spdy_proxy_client_socket.h
+++ b/net/spdy/spdy_proxy_client_socket.h
@@ -77,6 +77,8 @@
   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 @@
   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 @@
   virtual void SetOmniboxSpeculation();
   virtual bool WasEverUsed() const;
   virtual bool UsingTCPFastOpen() const;
+  virtual int64 NumBytesRead() const;
+  virtual base::TimeDelta GetConnectTimeMicros() const;
 
   // net::Socket implementation