summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortbansal <tbansal@chromium.org>2015-05-22 15:23:00 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-22 22:23:36 +0000
commitea2fb8c1af36cc540a32217d908fd01a6af40303 (patch)
tree16b032708c22872b3ac1a6ec1a7be57796424a8d
parent5d9b0f5d5b1666e058e6572e49ad89735c24c2f1 (diff)
downloadchromium_src-ea2fb8c1af36cc540a32217d908fd01a6af40303.zip
chromium_src-ea2fb8c1af36cc540a32217d908fd01a6af40303.tar.gz
chromium_src-ea2fb8c1af36cc540a32217d908fd01a6af40303.tar.bz2
Add the NetworkQualityEstimator class that is notified
when net stack receives data. Expose PeakKbps and FastestRTT Statistics through GetEstimate() API which may be used by clients like Lo-Fi. PeakKbps and FastestRTT computation is now based on prefilter bytes which correctly represent the amount of data that was transferred over the wire. BUG=472678, 478162 Review URL: https://codereview.chromium.org/1061583002 Cr-Commit-Position: refs/heads/master@{#331192}
-rw-r--r--chrome/browser/io_thread.cc6
-rw-r--r--chrome/browser/io_thread.h2
-rw-r--r--chrome/browser/profiles/profile_impl_io_data.cc2
-rw-r--r--net/base/network_quality.h51
-rw-r--r--net/base/network_quality_estimator.cc178
-rw-r--r--net/base/network_quality_estimator.h103
-rw-r--r--net/base/network_quality_estimator_unittest.cc99
-rw-r--r--net/net.gypi4
-rw-r--r--net/url_request/url_request_context.cc36
-rw-r--r--net/url_request/url_request_context.h21
-rw-r--r--net/url_request/url_request_job.cc12
-rw-r--r--net/url_request/url_request_unittest.cc54
-rw-r--r--tools/metrics/histograms/histograms.xml36
13 files changed, 583 insertions, 21 deletions
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index 46d0e3e..0a306c7 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -48,6 +48,7 @@
#include "content/public/browser/cookie_store_factory.h"
#include "net/base/host_mapping_rules.h"
#include "net/base/net_util.h"
+#include "net/base/network_quality_estimator.h"
#include "net/base/sdch_manager.h"
#include "net/cert/cert_policy_enforcer.h"
#include "net/cert/cert_verifier.h"
@@ -275,6 +276,8 @@ ConstructSystemRequestContext(IOThread::Globals* globals,
context->set_network_delegate(globals->system_network_delegate.get());
context->set_http_user_agent_settings(
globals->http_user_agent_settings.get());
+ context->set_network_quality_estimator(
+ globals->network_quality_estimator.get());
return context;
}
@@ -670,6 +673,9 @@ void IOThread::InitAsync() {
"466432 IOThread::InitAsync::CreateGlobalHostResolver"));
globals_->system_network_delegate = chrome_network_delegate.Pass();
globals_->host_resolver = CreateGlobalHostResolver(net_log_);
+
+ globals_->network_quality_estimator.reset(new net::NetworkQualityEstimator());
+
// TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466432
// is fixed.
tracked_objects::ScopedTracker tracking_profile5(
diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h
index 0b94438..7396cb0 100644
--- a/chrome/browser/io_thread.h
+++ b/chrome/browser/io_thread.h
@@ -56,6 +56,7 @@ class HttpServerProperties;
class HttpTransactionFactory;
class HttpUserAgentSettings;
class NetworkDelegate;
+class NetworkQualityEstimator;
class ProxyConfigService;
class ProxyService;
class SSLConfigService;
@@ -161,6 +162,7 @@ class IOThread : public content::BrowserThreadDelegate {
#endif
scoped_ptr<net::HostMappingRules> host_mapping_rules;
scoped_ptr<net::HttpUserAgentSettings> http_user_agent_settings;
+ scoped_ptr<net::NetworkQualityEstimator> network_quality_estimator;
bool ignore_certificate_errors;
bool use_stale_while_revalidate;
uint16 testing_fixed_http_port;
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index 827c441..8bf401e 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -571,6 +571,8 @@ void ProfileImplIOData::InitializeInternal(
main_context->network_delegate(),
ftp_factory_.get());
main_context->set_job_factory(main_job_factory_.get());
+ main_context->set_network_quality_estimator(
+ io_thread_globals->network_quality_estimator.get());
#if defined(ENABLE_EXTENSIONS)
InitializeExtensionsRequestContext(profile_params);
diff --git a/net/base/network_quality.h b/net/base/network_quality.h
new file mode 100644
index 0000000..4e29857
--- /dev/null
+++ b/net/base/network_quality.h
@@ -0,0 +1,51 @@
+// Copyright 2015 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.
+
+#ifndef NET_BASE_NETWORK_QUALITY_H_
+#define NET_BASE_NETWORK_QUALITY_H_
+
+#include <stdint.h>
+
+#include "base/time/time.h"
+
+namespace net {
+
+// API that is used to report the current network quality as estimated by the
+// NetworkQualityEstimator.
+struct NET_EXPORT_PRIVATE NetworkQuality {
+ NetworkQuality(const base::TimeDelta& fastest_rtt,
+ double fastest_rtt_confidence,
+ uint64_t peak_throughput_kbps,
+ double peak_throughput_kbps_confidence)
+ : fastest_rtt(fastest_rtt),
+ fastest_rtt_confidence(fastest_rtt_confidence),
+ peak_throughput_kbps(peak_throughput_kbps),
+ peak_throughput_kbps_confidence(peak_throughput_kbps_confidence) {}
+
+ ~NetworkQuality() {}
+
+ // The fastest round trip time observed for the current connection.
+ const base::TimeDelta fastest_rtt;
+
+ // Confidence of the |fastest_rtt| estimate. Value lies between 0.0 and 1.0
+ // with 0.0 being no confidence and 1.0 implying that estimates are same as
+ // ground truth.
+ // TODO(tbansal): Define units so values intermediate between 0.0 and 1.0 are
+ // meaningful.
+ const double fastest_rtt_confidence;
+
+ // Peak throughput in Kbps observed for the current connection.
+ const uint64_t peak_throughput_kbps;
+
+ // Confidence of the |peak_throughput_kbps| estimate. Value lies between 0.0
+ // and 1.0 with 0.0 being no confidence and 1.0 implying that estimates are
+ // same as ground truth.
+ // TODO(tbansal): Define units so values intermediate between 0.0 and 1.0 are
+ // meaningful.
+ const double peak_throughput_kbps_confidence;
+};
+
+} // namespace net
+
+#endif // NET_BASE_NETWORK_QUALITY_H_
diff --git a/net/base/network_quality_estimator.cc b/net/base/network_quality_estimator.cc
new file mode 100644
index 0000000..e012a99
--- /dev/null
+++ b/net/base/network_quality_estimator.cc
@@ -0,0 +1,178 @@
+// Copyright 2015 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 "net/base/network_quality_estimator.h"
+
+#include <string>
+
+#include "base/metrics/histogram.h"
+#include "net/base/net_util.h"
+#include "net/base/network_quality.h"
+#include "net/url_request/url_request.h"
+#include "url/gurl.h"
+
+namespace net {
+
+NetworkQualityEstimator::NetworkQualityEstimator()
+ : NetworkQualityEstimator(false) {
+}
+
+NetworkQualityEstimator::NetworkQualityEstimator(
+ bool allow_local_host_requests_for_tests)
+ : allow_localhost_requests_(allow_local_host_requests_for_tests),
+ last_connection_change_(base::TimeTicks::Now()),
+ current_connection_type_(NetworkChangeNotifier::GetConnectionType()),
+ bytes_read_since_last_connection_change_(false),
+ peak_kbps_since_last_connection_change_(0) {
+ static_assert(kMinRequestDurationMicroseconds > 0,
+ "Minimum request duration must be > 0");
+ NetworkChangeNotifier::AddConnectionTypeObserver(this);
+}
+
+NetworkQualityEstimator::~NetworkQualityEstimator() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
+}
+
+void NetworkQualityEstimator::NotifyDataReceived(const URLRequest& request,
+ int64_t prefilter_bytes_read) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_GT(prefilter_bytes_read, 0);
+
+ if (!request.url().is_valid() ||
+ (!allow_localhost_requests_ && IsLocalhost(request.url().host())) ||
+ !request.url().SchemeIsHTTPOrHTTPS() ||
+ // Verify that response headers are received, so it can be ensured that
+ // response is not cached.
+ request.response_info().response_time.is_null() || request.was_cached())
+ return;
+
+ base::TimeTicks now = base::TimeTicks::Now();
+ base::TimeDelta request_duration = now - request.creation_time();
+ DCHECK_GE(request_duration, base::TimeDelta());
+ if (!bytes_read_since_last_connection_change_)
+ fastest_RTT_since_last_connection_change_ = request_duration;
+
+ bytes_read_since_last_connection_change_ = true;
+ if (request_duration < fastest_RTT_since_last_connection_change_)
+ fastest_RTT_since_last_connection_change_ = request_duration;
+
+ // Ignore tiny transfers which will not produce accurate rates.
+ // Ignore short duration transfers.
+ if (prefilter_bytes_read >= kMinTransferSizeInBytes &&
+ request_duration >=
+ base::TimeDelta::FromMicroseconds(kMinRequestDurationMicroseconds) &&
+ request.creation_time() > last_connection_change_) {
+ uint64_t kbps = static_cast<uint64_t>(prefilter_bytes_read * 8 * 1000 /
+ request_duration.InMicroseconds());
+ if (kbps > peak_kbps_since_last_connection_change_)
+ peak_kbps_since_last_connection_change_ = kbps;
+ }
+}
+
+void NetworkQualityEstimator::OnConnectionTypeChanged(
+ NetworkChangeNotifier::ConnectionType type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (bytes_read_since_last_connection_change_) {
+ switch (current_connection_type_) {
+ case NetworkChangeNotifier::CONNECTION_UNKNOWN:
+ UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Unknown",
+ fastest_RTT_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_ETHERNET:
+ UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Ethernet",
+ fastest_RTT_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_WIFI:
+ UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Wifi",
+ fastest_RTT_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_2G:
+ UMA_HISTOGRAM_TIMES("NQE.FastestRTT.2G",
+ fastest_RTT_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_3G:
+ UMA_HISTOGRAM_TIMES("NQE.FastestRTT.3G",
+ fastest_RTT_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_4G:
+ UMA_HISTOGRAM_TIMES("NQE.FastestRTT.4G",
+ fastest_RTT_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_NONE:
+ UMA_HISTOGRAM_TIMES("NQE.FastestRTT.None",
+ fastest_RTT_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
+ UMA_HISTOGRAM_TIMES("NQE.FastestRTT.Bluetooth",
+ fastest_RTT_since_last_connection_change_);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ if (peak_kbps_since_last_connection_change_) {
+ switch (current_connection_type_) {
+ case NetworkChangeNotifier::CONNECTION_UNKNOWN:
+ UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Unknown",
+ peak_kbps_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_ETHERNET:
+ UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Ethernet",
+ peak_kbps_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_WIFI:
+ UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Wifi",
+ peak_kbps_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_2G:
+ UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.2G",
+ peak_kbps_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_3G:
+ UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.3G",
+ peak_kbps_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_4G:
+ UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.4G",
+ peak_kbps_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_NONE:
+ UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.None",
+ peak_kbps_since_last_connection_change_);
+ break;
+ case NetworkChangeNotifier::CONNECTION_BLUETOOTH:
+ UMA_HISTOGRAM_COUNTS("NQE.PeakKbps.Bluetooth",
+ peak_kbps_since_last_connection_change_);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ last_connection_change_ = base::TimeTicks::Now();
+ bytes_read_since_last_connection_change_ = false;
+ peak_kbps_since_last_connection_change_ = 0;
+ current_connection_type_ = type;
+}
+
+NetworkQuality NetworkQualityEstimator::GetEstimate() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!bytes_read_since_last_connection_change_) {
+ return NetworkQuality(fastest_RTT_since_last_connection_change_, 0,
+ peak_kbps_since_last_connection_change_, 0);
+ }
+ if (!peak_kbps_since_last_connection_change_) {
+ return NetworkQuality(fastest_RTT_since_last_connection_change_, 0.1,
+ peak_kbps_since_last_connection_change_, 0);
+ }
+ return NetworkQuality(fastest_RTT_since_last_connection_change_, 0.1,
+ peak_kbps_since_last_connection_change_, 0.1);
+}
+
+} // namespace net
diff --git a/net/base/network_quality_estimator.h b/net/base/network_quality_estimator.h
new file mode 100644
index 0000000..ecfa08a
--- /dev/null
+++ b/net/base/network_quality_estimator.h
@@ -0,0 +1,103 @@
+// Copyright 2015 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.
+
+#ifndef NET_BASE_NETWORK_QUALITY_ESTIMATOR_H_
+#define NET_BASE_NETWORK_QUALITY_ESTIMATOR_H_
+
+#include <stdint.h>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "net/base/network_change_notifier.h"
+
+namespace net {
+
+struct NetworkQuality;
+
+// NetworkQualityEstimator provides network quality estimates (quality of the
+// full paths to all origins that have been connected to).
+// The estimates are based on the observed organic traffic.
+// A NetworkQualityEstimator instance is attached to URLRequestContexts and
+// observes the traffic of URLRequests spawned from the URLRequestContexts.
+// A single instance of NQE can be attached to multiple URLRequestContexts,
+// thereby increasing the single NQE instance's accuracy by providing more
+// observed traffic characteristics.
+class NET_EXPORT_PRIVATE NetworkQualityEstimator
+ : public NetworkChangeNotifier::ConnectionTypeObserver {
+ public:
+ // Creates a new NetworkQualityEstimator.
+ NetworkQualityEstimator();
+
+ ~NetworkQualityEstimator() override;
+
+ // Returns an estimate of the current network quality.
+ NetworkQuality GetEstimate() const;
+
+ // Notifies NetworkQualityEstimator that a response has been received.
+ // |prefilter_bytes_read| is the count of the bytes received prior to
+ // applying filters (e.g. decompression, SDCH) from request creation time
+ // until now.
+ void NotifyDataReceived(const URLRequest& request,
+ int64_t prefilter_bytes_read);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest,
+ TestPeakKbpsFastestRTTUpdates);
+ FRIEND_TEST_ALL_PREFIXES(URLRequestTestHTTP, NetworkQualityEstimator);
+
+ // Tiny transfer sizes may give inaccurate throughput results.
+ // Minimum size of the transfer over which the throughput is computed.
+ static const int kMinTransferSizeInBytes = 10000;
+
+ // Minimum duration (in microseconds) of the transfer over which the
+ // throughput is computed.
+ static const int kMinRequestDurationMicroseconds = 1000;
+
+ // Construct a NetworkQualityEstimator instance allowing for test
+ // configuration.
+ // Registers for network type change notifications so estimates can be kept
+ // network specific.
+ // |allow_local_host_requests_for_tests| should only be true when testing
+ // against local HTTP server and allows the requests to local host to be
+ // used for network quality estimation.
+ explicit NetworkQualityEstimator(bool allow_local_host_requests_for_tests);
+
+ // NetworkChangeNotifier::ConnectionTypeObserver implementation.
+ void OnConnectionTypeChanged(
+ NetworkChangeNotifier::ConnectionType type) override;
+
+ // Determines if the requests to local host can be used in estimating the
+ // network quality. Set to true only for tests.
+ const bool allow_localhost_requests_;
+
+ // Time when last connection change was observed.
+ base::TimeTicks last_connection_change_;
+
+ // Last value passed to |OnConnectionTypeChanged|. This indicates the
+ // current connection type.
+ NetworkChangeNotifier::ConnectionType current_connection_type_;
+
+ // Set if any network data has been received since last connectivity change.
+ bool bytes_read_since_last_connection_change_;
+
+ // Fastest round-trip-time (RTT) since last connectivity change. RTT measured
+ // from URLRequest creation until first byte received.
+ base::TimeDelta fastest_RTT_since_last_connection_change_;
+
+ // Rough measurement of downlink peak Kbps witnessed since last connectivity
+ // change. The accuracy is decreased by ignoring these factors:
+ // 1) Multiple URLRequests can occur concurrently.
+ // 2) The transfer time includes at least one RTT while no bytes are read.
+ uint64_t peak_kbps_since_last_connection_change_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkQualityEstimator);
+};
+
+} // namespace net
+
+#endif // NET_BASE_NETWORK_QUALITY_ESTIMATOR_H_
diff --git a/net/base/network_quality_estimator_unittest.cc b/net/base/network_quality_estimator_unittest.cc
new file mode 100644
index 0000000..69b86fe
--- /dev/null
+++ b/net/base/network_quality_estimator_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright 2015 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 "net/base/network_quality_estimator.h"
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "base/test/histogram_tester.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "net/base/network_change_notifier.h"
+#include "net/base/network_quality.h"
+#include "net/test/spawned_test_server/spawned_test_server.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace net {
+
+// SpawnedTestServer not supported on iOS (see http://crbug.com/148666).
+#if !defined(OS_IOS)
+TEST(NetworkQualityEstimatorTest, TestPeakKbpsFastestRTTUpdates) {
+ SpawnedTestServer test_server_(
+ SpawnedTestServer::TYPE_HTTP, SpawnedTestServer::kLocalhost,
+ base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
+ ASSERT_TRUE(test_server_.Start());
+
+ // Enable requests to local host to be used for network quality estimation.
+ NetworkQualityEstimator estimator(true);
+ {
+ NetworkQuality network_quality = estimator.GetEstimate();
+ EXPECT_EQ(network_quality.fastest_rtt_confidence, 0);
+ EXPECT_EQ(network_quality.peak_throughput_kbps_confidence, 0);
+ }
+
+ TestDelegate d;
+ TestURLRequestContext context(false);
+
+ uint64 min_transfer_size_in_bytes =
+ NetworkQualityEstimator::kMinTransferSizeInBytes;
+ base::TimeDelta request_duration = base::TimeDelta::FromMicroseconds(
+ NetworkQualityEstimator::kMinRequestDurationMicroseconds);
+
+ scoped_ptr<URLRequest> request(context.CreateRequest(
+ test_server_.GetURL("echo.html"), DEFAULT_PRIORITY, &d));
+ request->Start();
+
+ base::RunLoop().Run();
+
+ base::PlatformThread::Sleep(request_duration);
+
+ // With smaller transfer, |fastest_rtt| will be updated but not
+ // |peak_throughput_kbps|.
+ estimator.NotifyDataReceived(*(request.get()),
+ min_transfer_size_in_bytes - 1);
+ {
+ NetworkQuality network_quality = estimator.GetEstimate();
+ EXPECT_GT(network_quality.fastest_rtt_confidence, 0);
+ EXPECT_EQ(network_quality.peak_throughput_kbps_confidence, 0);
+ }
+
+ // With large transfer, both |fastest_rtt| and |peak_throughput_kbps| will be
+ // updated.
+ estimator.NotifyDataReceived(*(request.get()), min_transfer_size_in_bytes);
+ {
+ NetworkQuality network_quality = estimator.GetEstimate();
+ EXPECT_GT(network_quality.fastest_rtt_confidence, 0);
+ EXPECT_GT(network_quality.peak_throughput_kbps_confidence, 0);
+ EXPECT_GE(network_quality.fastest_rtt, request_duration);
+ EXPECT_GT(network_quality.peak_throughput_kbps, uint32(0));
+ EXPECT_LE(
+ network_quality.peak_throughput_kbps,
+ min_transfer_size_in_bytes * 8.0 / request_duration.InMilliseconds());
+ }
+ EXPECT_EQ(estimator.bytes_read_since_last_connection_change_, true);
+
+ // Check UMA histograms.
+ base::HistogramTester histogram_tester;
+ histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 0);
+ histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 0);
+
+ estimator.OnConnectionTypeChanged(
+ NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI);
+ histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 1);
+ histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 1);
+ {
+ NetworkQuality network_quality = estimator.GetEstimate();
+ EXPECT_EQ(estimator.current_connection_type_,
+ NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI);
+ EXPECT_EQ(network_quality.fastest_rtt_confidence, 0);
+ EXPECT_EQ(network_quality.peak_throughput_kbps_confidence, 0);
+ }
+}
+#endif // !defined(OS_IOS)
+
+} // namespace net
diff --git a/net/net.gypi b/net/net.gypi
index 87b8e8d..f4ba211 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -276,6 +276,9 @@
'base/network_delegate.h',
'base/network_delegate_impl.cc',
'base/network_delegate_impl.h',
+ 'base/network_quality.h',
+ 'base/network_quality_estimator.cc',
+ 'base/network_quality_estimator.h',
'base/nss_memio.c',
'base/nss_memio.h',
'base/platform_mime_util.h',
@@ -1307,6 +1310,7 @@
'base/network_activity_monitor_unittest.cc',
'base/network_change_notifier_unittest.cc',
'base/network_change_notifier_win_unittest.cc',
+ 'base/network_quality_estimator_unittest.cc',
'base/prioritized_dispatcher_unittest.cc',
'base/priority_queue_unittest.cc',
'base/registry_controlled_domains/registry_controlled_domain_unittest.cc',
diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc
index eb6a35c..bf299b7 100644
--- a/net/url_request/url_request_context.cc
+++ b/net/url_request/url_request_context.cc
@@ -17,23 +17,24 @@
namespace net {
URLRequestContext::URLRequestContext()
- : net_log_(NULL),
- host_resolver_(NULL),
- cert_verifier_(NULL),
- channel_id_service_(NULL),
- fraudulent_certificate_reporter_(NULL),
- http_auth_handler_factory_(NULL),
- proxy_service_(NULL),
- network_delegate_(NULL),
- http_user_agent_settings_(NULL),
- transport_security_state_(NULL),
- cert_transparency_verifier_(NULL),
- http_transaction_factory_(NULL),
- job_factory_(NULL),
- throttler_manager_(NULL),
+ : net_log_(nullptr),
+ host_resolver_(nullptr),
+ cert_verifier_(nullptr),
+ channel_id_service_(nullptr),
+ fraudulent_certificate_reporter_(nullptr),
+ http_auth_handler_factory_(nullptr),
+ proxy_service_(nullptr),
+ network_delegate_(nullptr),
+ http_user_agent_settings_(nullptr),
+ transport_security_state_(nullptr),
+ cert_transparency_verifier_(nullptr),
+ http_transaction_factory_(nullptr),
+ job_factory_(nullptr),
+ throttler_manager_(nullptr),
// For investigation of http://crbug.com/454198; restore when resolved.
- // sdch_manager_(NULL),
+ // sdch_manager_(nullptr),
have_sdch_manager_(false),
+ network_quality_estimator_(nullptr),
url_requests_(new std::set<const URLRequest*>) {
}
@@ -63,16 +64,17 @@ void URLRequestContext::CopyFrom(const URLRequestContext* other) {
CHECK(!other->have_sdch_manager_ || other->sdch_manager_.get());
set_sdch_manager(other->sdch_manager_.get());
set_http_user_agent_settings(other->http_user_agent_settings_);
+ set_network_quality_estimator(other->network_quality_estimator_);
}
const HttpNetworkSession::Params* URLRequestContext::GetNetworkSessionParams(
) const {
HttpTransactionFactory* transaction_factory = http_transaction_factory();
if (!transaction_factory)
- return NULL;
+ return nullptr;
HttpNetworkSession* network_session = transaction_factory->GetSession();
if (!network_session)
- return NULL;
+ return nullptr;
return &network_session->params();
}
diff --git a/net/url_request/url_request_context.h b/net/url_request/url_request_context.h
index 1be89c4..6c6f67f 100644
--- a/net/url_request/url_request_context.h
+++ b/net/url_request/url_request_context.h
@@ -38,6 +38,7 @@ class HttpAuthHandlerFactory;
class HttpTransactionFactory;
class HttpUserAgentSettings;
class NetworkDelegate;
+class NetworkQualityEstimator;
class SdchManager;
class ProxyService;
class URLRequest;
@@ -57,7 +58,8 @@ class NET_EXPORT URLRequestContext
// Copies the state from |other| into this context.
void CopyFrom(const URLRequestContext* other);
- // May return NULL if this context doesn't have an associated network session.
+ // May return nullptr if this context doesn't have an associated network
+ // session.
const HttpNetworkSession::Params* GetNetworkSessionParams() const;
scoped_ptr<URLRequest> CreateRequest(const GURL& url,
@@ -174,7 +176,7 @@ class NET_EXPORT URLRequestContext
job_factory_ = job_factory;
}
- // May be NULL.
+ // May return nullptr.
URLRequestThrottlerManager* throttler_manager() const {
return throttler_manager_;
}
@@ -182,11 +184,11 @@ class NET_EXPORT URLRequestContext
throttler_manager_ = throttler_manager;
}
- // May be NULL.
+ // May return nullptr.
SdchManager* sdch_manager() const {
// For investigation of http://crbug.com/454198; remove ?: when resolved.
CHECK(!have_sdch_manager_ || sdch_manager_.get());
- return have_sdch_manager_ ? sdch_manager_.get() : NULL;
+ return have_sdch_manager_ ? sdch_manager_.get() : nullptr;
}
void set_sdch_manager(SdchManager* sdch_manager) {
// For investigation of http://crbug.com/454198; simplify when resolved.
@@ -218,6 +220,16 @@ class NET_EXPORT URLRequestContext
http_user_agent_settings_ = http_user_agent_settings;
}
+ // Gets the NetworkQualityEstimator associated with this context.
+ // May return nullptr.
+ NetworkQualityEstimator* network_quality_estimator() const {
+ return network_quality_estimator_;
+ }
+ void set_network_quality_estimator(
+ NetworkQualityEstimator* network_quality_estimator) {
+ network_quality_estimator_ = network_quality_estimator;
+ }
+
private:
// ---------------------------------------------------------------------------
// Important: When adding any new members below, consider whether they need to
@@ -246,6 +258,7 @@ class NET_EXPORT URLRequestContext
// For investigation of http://crbug.com/454198; remove WeakPtr when resolved.
bool have_sdch_manager_;
base::WeakPtr<SdchManager> sdch_manager_;
+ NetworkQualityEstimator* network_quality_estimator_;
// ---------------------------------------------------------------------------
// Important: When adding any new members below, consider whether they need to
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index aed4923..40672cb 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -18,8 +18,10 @@
#include "net/base/load_states.h"
#include "net/base/net_errors.h"
#include "net/base/network_delegate.h"
+#include "net/base/network_quality_estimator.h"
#include "net/filter/filter.h"
#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request_context.h"
namespace net {
@@ -808,7 +810,17 @@ void URLRequestJob::OnRawReadComplete(int bytes_read) {
}
void URLRequestJob::RecordBytesRead(int bytes_read) {
+ DCHECK_GT(bytes_read, 0);
prefilter_bytes_read_ += bytes_read;
+
+ // Notify NetworkQualityEstimator.
+ // TODO(tbansal): Move this to url_request_http_job.cc. This may catch
+ // Service Worker jobs twice.
+ if (request_ && request_->context()->network_quality_estimator()) {
+ request_->context()->network_quality_estimator()->NotifyDataReceived(
+ *request_, prefilter_bytes_read_);
+ }
+
if (!filter_.get())
postfilter_bytes_read_ += bytes_read;
DVLOG(2) << __FUNCTION__ << "() "
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index fd9a650..a47ca99 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -9,6 +9,8 @@
#include <shlobj.h>
#endif
+#include <stdint.h>
+
#include <algorithm>
#include "base/basictypes.h"
@@ -29,6 +31,7 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/histogram_tester.h"
#include "net/base/chunked_upload_data_stream.h"
#include "net/base/elements_upload_data_stream.h"
#include "net/base/load_flags.h"
@@ -37,6 +40,8 @@
#include "net/base/net_errors.h"
#include "net/base/net_module.h"
#include "net/base/net_util.h"
+#include "net/base/network_quality.h"
+#include "net/base/network_quality_estimator.h"
#include "net/base/request_priority.h"
#include "net/base/test_data_directory.h"
#include "net/base/upload_bytes_element_reader.h"
@@ -4068,6 +4073,55 @@ TEST_F(URLRequestTestHTTP, GetZippedTest) {
}
}
+TEST_F(URLRequestTestHTTP, NetworkQualityEstimator) {
+ ASSERT_TRUE(test_server_.Start());
+ // Enable requests to local host to be used for network quality estimation.
+ NetworkQualityEstimator estimator(true);
+
+ TestDelegate d;
+ TestNetworkDelegate network_delegate; // Must outlive URLRequest.
+ TestURLRequestContext context(true);
+ context.set_network_quality_estimator(&estimator);
+ context.set_network_delegate(&network_delegate);
+ context.Init();
+
+ uint64_t min_transfer_size_in_bytes =
+ NetworkQualityEstimator::kMinTransferSizeInBytes;
+ // Create a long enough URL such that response size exceeds network quality
+ // estimator's minimum transfer size.
+ std::string url = "echo.html?";
+ url.append(min_transfer_size_in_bytes, 'x');
+
+ scoped_ptr<URLRequest> r(
+ context.CreateRequest(test_server_.GetURL(url), DEFAULT_PRIORITY, &d));
+ int sleep_duration_milliseconds = 1;
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMilliseconds(sleep_duration_milliseconds));
+ r->Start();
+
+ base::RunLoop().Run();
+
+ NetworkQuality network_quality =
+ context.network_quality_estimator()->GetEstimate();
+ EXPECT_GE(network_quality.fastest_rtt,
+ base::TimeDelta::FromMilliseconds(sleep_duration_milliseconds));
+ EXPECT_GT(network_quality.fastest_rtt_confidence, 0);
+ EXPECT_GT(network_quality.peak_throughput_kbps, uint64_t(0));
+ EXPECT_GT(network_quality.peak_throughput_kbps_confidence, 0);
+
+ // Verify that histograms are not populated. They should populate only when
+ // there is a change in ConnectionType.
+ base::HistogramTester histogram_tester;
+ histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 0);
+ histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 0);
+
+ NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
+ NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI);
+ base::MessageLoop::current()->RunUntilIdle();
+ histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 1);
+ histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 1);
+}
+
TEST_F(URLRequestTestHTTP, RedirectLoadTiming) {
ASSERT_TRUE(test_server_.Start());
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 53a91bd..be22885 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -25571,6 +25571,29 @@ Therefore, the affected-histogram name has to have at least one dot in it.
</summary>
</histogram>
+<histogram name="NQE.FastestRTT" units="milliseconds">
+ <owner>tbansal@chromium.org</owner>
+ <summary>
+ Rough estimate of the fastest round-trip-time, before a connectivity change
+ is detected.
+
+ This metric is recorded when a connectivity change is detected. This will
+ miss data from users whose connection type never changes and will be biased
+ to users whose connection type changes frequently.
+ </summary>
+</histogram>
+
+<histogram name="NQE.PeakKbps" units="Kbps">
+ <owner>tbansal@chromium.org</owner>
+ <summary>
+ Rough estimate of peak throughput, before a connectivity change is detected.
+
+ This metric is recorded when a connectivity change is detected. This will
+ miss data from users whose connection type never changes and will be biased
+ to users whose connection type changes frequently.
+ </summary>
+</histogram>
+
<histogram name="ntp.searchurls.total">
<owner>Please list the metric's owners. Add more owner tags as needed.</owner>
<summary>TBD</summary>
@@ -69532,6 +69555,19 @@ To add a new entry, add it with any value and run test to compute valid value.
<affected-histogram name="NewTabPage.SuggestionsImpression"/>
</histogram_suffixes>
+<histogram_suffixes name="NQE.NetworkTypes" separator=".">
+ <suffix name="Unknown" label="On Unknown network"/>
+ <suffix name="Ethernet" label="On Ethernet network"/>
+ <suffix name="Wifi" label="On WiFi network"/>
+ <suffix name="2G" label="On 2G network"/>
+ <suffix name="3G" label="On 3G Network"/>
+ <suffix name="4G" label="On 4G network"/>
+ <suffix name="None" label="With no detected network"/>
+ <suffix name="Bluetooth" label="On Bluetooth network"/>
+ <affected-histogram name="NQE.FastestRTT"/>
+ <affected-histogram name="NQE.PeakKbps"/>
+</histogram_suffixes>
+
<histogram_suffixes name="OffDomainInclusionAbortReason" separator=".">
<suffix name="EmptyMainFrameURL" label="The main frame URL was empty."/>
<suffix name="HistoryLookupFailed"