diff options
author | tbansal <tbansal@chromium.org> | 2015-05-22 15:23:00 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-22 22:23:36 +0000 |
commit | ea2fb8c1af36cc540a32217d908fd01a6af40303 (patch) | |
tree | 16b032708c22872b3ac1a6ec1a7be57796424a8d | |
parent | 5d9b0f5d5b1666e058e6572e49ad89735c24c2f1 (diff) | |
download | chromium_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.cc | 6 | ||||
-rw-r--r-- | chrome/browser/io_thread.h | 2 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_impl_io_data.cc | 2 | ||||
-rw-r--r-- | net/base/network_quality.h | 51 | ||||
-rw-r--r-- | net/base/network_quality_estimator.cc | 178 | ||||
-rw-r--r-- | net/base/network_quality_estimator.h | 103 | ||||
-rw-r--r-- | net/base/network_quality_estimator_unittest.cc | 99 | ||||
-rw-r--r-- | net/net.gypi | 4 | ||||
-rw-r--r-- | net/url_request/url_request_context.cc | 36 | ||||
-rw-r--r-- | net/url_request/url_request_context.h | 21 | ||||
-rw-r--r-- | net/url_request/url_request_job.cc | 12 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 54 | ||||
-rw-r--r-- | tools/metrics/histograms/histograms.xml | 36 |
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" |