diff options
author | twifkak <twifkak@chromium.org> | 2016-02-29 14:42:06 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-02-29 22:43:34 +0000 |
commit | ddf019b2bf5bb413268a970ed359eaca61467aa4 (patch) | |
tree | e5356b6cc693e1788725e321e78afcd522cdebc3 | |
parent | 07d6344d650052669d923fecbe26758416799e77 (diff) | |
download | chromium_src-ddf019b2bf5bb413268a970ed359eaca61467aa4.zip chromium_src-ddf019b2bf5bb413268a970ed359eaca61467aa4.tar.gz chromium_src-ddf019b2bf5bb413268a970ed359eaca61467aa4.tar.bz2 |
Add configurable precache size caps.
Add three new fields to PrecacheConfigurationSettings proto:
- top_resources_count (number of resources per host)
- max_bytes_per_resources (limit on network transfer per resource)
- max_bytes_total (limit on total size of precache)
In order to test this change, I had to expose FakeURLFetcher's WeakPtr support
so that I could test for its deletion.
While I was here, I removed unnecessary specific_include_rules that were
obviated by the net/http addition to include_rules in
http://crrev.com/7953387c.
BUG=309216
Review URL: https://codereview.chromium.org/1724563002
Cr-Commit-Position: refs/heads/master@{#378297}
-rw-r--r-- | components/precache.gypi | 1 | ||||
-rw-r--r-- | components/precache/core/DEPS | 6 | ||||
-rw-r--r-- | components/precache/core/precache_fetcher.cc | 141 | ||||
-rw-r--r-- | components/precache/core/precache_fetcher.h | 40 | ||||
-rw-r--r-- | components/precache/core/precache_fetcher_unittest.cc | 217 | ||||
-rw-r--r-- | components/precache/core/proto/precache.proto | 15 |
6 files changed, 323 insertions, 97 deletions
diff --git a/components/precache.gypi b/components/precache.gypi index 263627d..e73ed78 100644 --- a/components/precache.gypi +++ b/components/precache.gypi @@ -57,6 +57,7 @@ 'type': 'static_library', 'dependencies': [ 'precache_core', + 'precache_core_proto', '../base/base.gyp:base', '../components/components.gyp:sync_driver', '../content/content.gyp:content_browser', diff --git a/components/precache/core/DEPS b/components/precache/core/DEPS index 9eafbf5..3c3e604 100644 --- a/components/precache/core/DEPS +++ b/components/precache/core/DEPS @@ -4,9 +4,3 @@ include_rules = [ "+net/url_request", "+sql", ] - -specific_include_rules = { - '.*_[a-z]*test\.cc': [ - "+net/http", - ], -} diff --git a/components/precache/core/precache_fetcher.cc b/components/precache/core/precache_fetcher.cc index fc52258a..c531b74 100644 --- a/components/precache/core/precache_fetcher.cc +++ b/components/precache/core/precache_fetcher.cc @@ -4,6 +4,8 @@ #include "components/precache/core/precache_fetcher.h" +#include <algorithm> +#include <limits> #include <string> #include <utility> #include <vector> @@ -13,6 +15,7 @@ #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/containers/hash_tables.h" +#include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "components/precache/core/precache_switches.h" #include "components/precache/core/proto/precache.pb.h" @@ -140,12 +143,14 @@ class URLFetcherNullWriter : public net::URLFetcherResponseWriter { PrecacheFetcher::Fetcher::Fetcher( net::URLRequestContextGetter* request_context, const GURL& url, - const base::Callback<void(const URLFetcher&)>& callback, - bool is_resource_request) + const base::Callback<void(const URLFetcher*)>& callback, + bool is_resource_request, + size_t max_bytes) : request_context_(request_context), url_(url), callback_(callback), is_resource_request_(is_resource_request), + max_bytes_(max_bytes), response_bytes_(0), network_response_bytes_(0) { if (is_resource_request_) @@ -187,6 +192,27 @@ void PrecacheFetcher::Fetcher::LoadFromNetwork() { url_fetcher_network_->Start(); } +void PrecacheFetcher::Fetcher::OnURLFetchDownloadProgress( + const URLFetcher* source, + int64_t current, + int64_t total) { + // If going over the per-resource download cap. + if (fetch_stage_ == FetchStage::NETWORK && + // |current| is guaranteed to be non-negative, so this cast is safe. + static_cast<size_t>(std::max(current, total)) > max_bytes_) { + VLOG(1) << "Cancelling " << url_ << ": (" << current << "/" << total + << ") is over " << max_bytes_; + + // Delete the URLFetcher to cancel the download. + url_fetcher_network_.reset(); + + // Call the completion callback, to attempt the next download, or to trigger + // cleanup in precache_delegate_->OnDone(). + response_bytes_ = network_response_bytes_ = current; + callback_.Run(nullptr); + } +} + void PrecacheFetcher::Fetcher::OnURLFetchComplete(const URLFetcher* source) { if (fetch_stage_ == FetchStage::CACHE && (source->GetStatus().error() == net::ERR_CACHE_MISS || @@ -213,7 +239,7 @@ void PrecacheFetcher::Fetcher::OnURLFetchComplete(const URLFetcher* source) { // Then Fetcher is done with this URL and can return control to the caller. response_bytes_ = source->GetReceivedResponseContentLength(); network_response_bytes_ = source->GetTotalReceivedBytes(); - callback_.Run(*source); + callback_.Run(source); } PrecacheFetcher::PrecacheFetcher( @@ -227,6 +253,7 @@ PrecacheFetcher::PrecacheFetcher( config_url_(config_url), manifest_url_prefix_(manifest_url_prefix), precache_delegate_(precache_delegate), + config_(new PrecacheConfigurationSettings), total_response_bytes_(0), network_response_bytes_(0), num_manifest_urls_to_fetch_(0) { @@ -278,20 +305,30 @@ void PrecacheFetcher::Start() { fetcher_.reset(new Fetcher(request_context_.get(), config_url, base::Bind(&PrecacheFetcher::OnConfigFetchComplete, base::Unretained(this)), - false /* is_resource_request */)); + false /* is_resource_request */, + std::numeric_limits<int32_t>::max())); } void PrecacheFetcher::StartNextFetch() { total_response_bytes_ += fetcher_->response_bytes(); network_response_bytes_ += fetcher_->network_response_bytes(); + // If over the precache total size cap, then stop prefetching. + if (total_response_bytes_ > config_->max_bytes_total()) { + resource_urls_to_fetch_.clear(); + manifest_urls_to_fetch_.clear(); + } + if (!resource_urls_to_fetch_.empty()) { // Fetch the next resource URL. + size_t max_bytes = + std::min(config_->max_bytes_per_resource(), + config_->max_bytes_total() - total_response_bytes_); fetcher_.reset( new Fetcher(request_context_.get(), resource_urls_to_fetch_.front(), base::Bind(&PrecacheFetcher::OnResourceFetchComplete, base::Unretained(this)), - true /* is_resource_request */)); + true /* is_resource_request */, max_bytes)); resource_urls_to_fetch_.pop_front(); return; @@ -299,11 +336,11 @@ void PrecacheFetcher::StartNextFetch() { if (!manifest_urls_to_fetch_.empty()) { // Fetch the next manifest URL. - fetcher_.reset( - new Fetcher(request_context_.get(), manifest_urls_to_fetch_.front(), - base::Bind(&PrecacheFetcher::OnManifestFetchComplete, - base::Unretained(this)), - false /* is_resource_request */)); + fetcher_.reset(new Fetcher( + request_context_.get(), manifest_urls_to_fetch_.front(), + base::Bind(&PrecacheFetcher::OnManifestFetchComplete, + base::Unretained(this)), + false /* is_resource_request */, std::numeric_limits<int32_t>::max())); manifest_urls_to_fetch_.pop_front(); return; @@ -320,50 +357,56 @@ void PrecacheFetcher::StartNextFetch() { // is called. } -void PrecacheFetcher::OnConfigFetchComplete(const URLFetcher& source) { - // Attempt to parse the config proto. On failure, continue on with the default - // configuration. - PrecacheConfigurationSettings config; - ParseProtoFromFetchResponse(source, &config); - - std::string prefix = manifest_url_prefix_.empty() - ? GetDefaultManifestURLPrefix() - : manifest_url_prefix_; - DCHECK_NE(std::string(), prefix) - << "Could not determine the precache manifest URL prefix."; - - // Keep track of manifest URLs that are being fetched, in order to remove - // duplicates. - base::hash_set<std::string> unique_manifest_urls; - - // Attempt to fetch manifests for starting hosts up to the maximum top sites - // count. If a manifest does not exist for a particular starting host, then - // the fetch will fail, and that starting host will be ignored. - int64_t rank = 0; - for (const std::string& host : starting_hosts_) { - ++rank; - if (rank > config.top_sites_count()) - break; - unique_manifest_urls.insert(ConstructManifestURL(prefix, host)); - } +void PrecacheFetcher::OnConfigFetchComplete(const URLFetcher* source) { + if (source) { + // Attempt to parse the config proto. On failure, continue on with the + // default + // configuration. + ParseProtoFromFetchResponse(*source, config_.get()); + + std::string prefix = manifest_url_prefix_.empty() + ? GetDefaultManifestURLPrefix() + : manifest_url_prefix_; + DCHECK_NE(std::string(), prefix) + << "Could not determine the precache manifest URL prefix."; + + // Keep track of manifest URLs that are being fetched, in order to remove + // duplicates. + base::hash_set<std::string> unique_manifest_urls; + + // Attempt to fetch manifests for starting hosts up to the maximum top sites + // count. If a manifest does not exist for a particular starting host, then + // the fetch will fail, and that starting host will be ignored. + int64_t rank = 0; + for (const std::string& host : starting_hosts_) { + ++rank; + if (rank > config_->top_sites_count()) + break; + unique_manifest_urls.insert(ConstructManifestURL(prefix, host)); + } - for (const std::string& url : config.forced_site()) - unique_manifest_urls.insert(ConstructManifestURL(prefix, url)); + for (const std::string& url : config_->forced_site()) + unique_manifest_urls.insert(ConstructManifestURL(prefix, url)); - for (const std::string& manifest_url : unique_manifest_urls) - manifest_urls_to_fetch_.push_back(GURL(manifest_url)); - num_manifest_urls_to_fetch_ = manifest_urls_to_fetch_.size(); + for (const std::string& manifest_url : unique_manifest_urls) + manifest_urls_to_fetch_.push_back(GURL(manifest_url)); + num_manifest_urls_to_fetch_ = manifest_urls_to_fetch_.size(); + } StartNextFetch(); } -void PrecacheFetcher::OnManifestFetchComplete(const URLFetcher& source) { - PrecacheManifest manifest; - - if (ParseProtoFromFetchResponse(source, &manifest)) { - for (int i = 0; i < manifest.resource_size(); ++i) { - if (manifest.resource(i).has_url()) { - resource_urls_to_fetch_.push_back(GURL(manifest.resource(i).url())); +void PrecacheFetcher::OnManifestFetchComplete(const URLFetcher* source) { + if (source) { + PrecacheManifest manifest; + + if (ParseProtoFromFetchResponse(*source, &manifest)) { + const int32_t len = + std::min(manifest.resource_size(), config_->top_resources_count()); + for (int i = 0; i < len; ++i) { + if (manifest.resource(i).has_url()) { + resource_urls_to_fetch_.push_back(GURL(manifest.resource(i).url())); + } } } } @@ -371,7 +414,7 @@ void PrecacheFetcher::OnManifestFetchComplete(const URLFetcher& source) { StartNextFetch(); } -void PrecacheFetcher::OnResourceFetchComplete(const URLFetcher& source) { +void PrecacheFetcher::OnResourceFetchComplete(const URLFetcher* source) { // The resource has already been put in the cache during the fetch process, so // nothing more needs to be done for the resource. StartNextFetch(); diff --git a/components/precache/core/precache_fetcher.h b/components/precache/core/precache_fetcher.h index 6eca926..ff32f0b 100644 --- a/components/precache/core/precache_fetcher.h +++ b/components/precache/core/precache_fetcher.h @@ -26,6 +26,8 @@ class URLRequestContextGetter; namespace precache { +class PrecacheConfigurationSettings; + // Visible for testing. extern const int kNoTracking; @@ -107,15 +109,15 @@ class PrecacheFetcher { // Determines the list of manifest URLs to fetch according to the list of // |starting_hosts_| and information from the precache configuration settings. // If the fetch of the configuration settings fails, then precaching ends. - void OnConfigFetchComplete(const net::URLFetcher& source); + void OnConfigFetchComplete(const net::URLFetcher* source); // Called when a precache manifest has been fetched. Builds the list of // resource URLs to fetch according to the URLs in the manifest. If the fetch // of a manifest fails, then it skips to the next manifest. - void OnManifestFetchComplete(const net::URLFetcher& source); + void OnManifestFetchComplete(const net::URLFetcher* source); // Called when a resource has been fetched. - void OnResourceFetchComplete(const net::URLFetcher& source); + void OnResourceFetchComplete(const net::URLFetcher* source); // The prioritized list of starting hosts that the server will pick resource // URLs to be precached for. @@ -135,15 +137,17 @@ class PrecacheFetcher { // Non-owning pointer. Should not be NULL. PrecacheDelegate* precache_delegate_; + scoped_ptr<PrecacheConfigurationSettings> config_; + // Tally of the total number of bytes contained in URL fetches, including // config, manifests, and resources. This the number of bytes as they would be // compressed over the network. - int total_response_bytes_; + size_t total_response_bytes_; // Tally of the total number of bytes received over the network from URL // fetches (the same ones as in total_response_bytes_). This includes response // headers and intermediate responses such as 30xs. - int network_response_bytes_; + size_t network_response_bytes_; scoped_ptr<Fetcher> fetcher_; @@ -162,15 +166,34 @@ class PrecacheFetcher { // response to different kinds of fetches, e.g. OnConfigFetchComplete when // configuration settings are fetched, OnManifestFetchComplete when a manifest // is fetched, etc. +// +// This class tries to increase freshness while limiting network usage, by using +// the following strategy: +// 1. Fetch the URL from the cache. +// 2a. If it's present and lacks revalidation headers, then stop. +// 2b. If it's not present, or it's present and has revalidation headers, then +// refetch over the network. +// +// This allows the precache to "refresh" cache entries by increasing their +// expiration date, but minimizes the network impact of doing so, by performing +// only conditional GETs. +// +// On completion it calls the given callback. This class cancels requests whose +// responses are or will be larger than max_bytes. In such a case, |callback| +// will be called with nullptr. class PrecacheFetcher::Fetcher : public net::URLFetcherDelegate { public: // Construct a new Fetcher. This will create and start a new URLFetcher for // the specified URL using the specified request context. Fetcher(net::URLRequestContextGetter* request_context, const GURL& url, - const base::Callback<void(const net::URLFetcher&)>& callback, - bool is_resource_request); + const base::Callback<void(const net::URLFetcher*)>& callback, + bool is_resource_request, + size_t max_bytes); ~Fetcher() override; + void OnURLFetchDownloadProgress(const net::URLFetcher* source, + int64_t current, + int64_t total) override; void OnURLFetchComplete(const net::URLFetcher* source) override; int64_t response_bytes() const { return response_bytes_; } int64_t network_response_bytes() const { return network_response_bytes_; } @@ -183,8 +206,9 @@ class PrecacheFetcher::Fetcher : public net::URLFetcherDelegate { net::URLRequestContextGetter* const request_context_; const GURL url_; - const base::Callback<void(const net::URLFetcher&)> callback_; + const base::Callback<void(const net::URLFetcher*)> callback_; const bool is_resource_request_; + const size_t max_bytes_; FetchStage fetch_stage_; // The url_fetcher_cache_ is kept alive until Fetcher destruction for testing. diff --git a/components/precache/core/precache_fetcher_unittest.cc b/components/precache/core/precache_fetcher_unittest.cc index e0468cb..03de9cc 100644 --- a/components/precache/core/precache_fetcher_unittest.cc +++ b/components/precache/core/precache_fetcher_unittest.cc @@ -4,6 +4,9 @@ #include "components/precache/core/precache_fetcher.h" +#include <stdint.h> + +#include <cstring> #include <set> #include <string> #include <vector> @@ -14,6 +17,7 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/run_loop.h" #include "base/test/histogram_tester.h" #include "base/thread_task_runner_handle.h" @@ -34,6 +38,7 @@ namespace precache { namespace { using ::testing::_; +using ::testing::NotNull; const char kConfigURL[] = "http://config-url.com"; const char kManifestURLPrefix[] = "http://manifest-url-prefix.com/"; @@ -151,11 +156,13 @@ class MockURLFetcherFactory : public net::URLFetcherFactory { const GURL&, net::URLFetcher::RequestType, net::URLFetcherDelegate*>& args) { - *fetcher_ = new net::FakeURLFetcher( + auto fetcher = new net::FakeURLFetcher( testing::get<1>(args), testing::get<3>(args), body_, net::HTTP_OK, net::URLRequestStatus::SUCCESS); - modifier_(*fetcher_); - return *fetcher_; + modifier_(fetcher); + if (fetcher_) + *fetcher_ = fetcher; + return fetcher; } private: @@ -172,36 +179,45 @@ class PrecacheFetcherFetcherTest : public testing::Test { base::ThreadTaskRunnerHandle::Get())), scoped_url_fetcher_factory_(&factory_), callback_(base::Bind(&PrecacheFetcherFetcherTest::Callback, - base::Unretained(this))), - callback_called_(false) {} + base::Unretained(this))) {} - void Callback(const net::URLFetcher&) { callback_called_ = true; } + MOCK_METHOD1(Callback, void(const net::URLFetcher*)); protected: base::MessageLoopForUI loop_; scoped_refptr<net::TestURLRequestContextGetter> request_context_; MockURLFetcherFactory factory_; net::ScopedURLFetcherFactory scoped_url_fetcher_factory_; - base::Callback<void(const net::URLFetcher&)> callback_; - bool callback_called_; + base::Callback<void(const net::URLFetcher*)> callback_; }; +void CacheMiss(net::FakeURLFetcher* fetcher) { + fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED, + net::ERR_CACHE_MISS)); +} + +void HasETag(net::FakeURLFetcher* fetcher) { + std::string raw_headers("HTTP/1.1 200 OK\0ETag: foo\0\0", 27); + fetcher->set_response_headers( + make_scoped_refptr(new net::HttpResponseHeaders(raw_headers))); +} + TEST_F(PrecacheFetcherFetcherTest, Config) { GURL url(kConfigURL); net::FakeURLFetcher* fetcher = nullptr; EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) .WillOnce(factory_.RespondWith("", &fetcher)); + EXPECT_CALL(*this, Callback(NotNull())); PrecacheFetcher::Fetcher precache_fetcher( - request_context_.get(), url, callback_, false /* is_resource_request */); + request_context_.get(), url, callback_, false /* is_resource_request */, + SIZE_MAX); loop_.RunUntilIdle(); ASSERT_NE(nullptr, fetcher); EXPECT_EQ(kNoTracking, fetcher->GetLoadFlags()); - - EXPECT_EQ(true, callback_called_); } TEST_F(PrecacheFetcherFetcherTest, ResourceNotInCache) { @@ -209,17 +225,13 @@ TEST_F(PrecacheFetcherFetcherTest, ResourceNotInCache) { net::FakeURLFetcher *fetcher1 = nullptr, *fetcher2 = nullptr; EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) - .WillOnce(factory_.RespondWith( - "", - [](net::FakeURLFetcher* fetcher) { - fetcher->set_status(net::URLRequestStatus( - net::URLRequestStatus::FAILED, net::ERR_CACHE_MISS)); - }, - &fetcher1)) + .WillOnce(factory_.RespondWith("", CacheMiss, &fetcher1)) .WillOnce(factory_.RespondWith("", &fetcher2)); + EXPECT_CALL(*this, Callback(NotNull())); PrecacheFetcher::Fetcher precache_fetcher( - request_context_.get(), url, callback_, true /* is_resource_request */); + request_context_.get(), url, callback_, true /* is_resource_request */, + SIZE_MAX); loop_.RunUntilIdle(); @@ -227,8 +239,6 @@ TEST_F(PrecacheFetcherFetcherTest, ResourceNotInCache) { EXPECT_EQ(net::LOAD_ONLY_FROM_CACHE | kNoTracking, fetcher1->GetLoadFlags()); ASSERT_NE(nullptr, fetcher2); EXPECT_EQ(net::LOAD_VALIDATE_CACHE | kNoTracking, fetcher2->GetLoadFlags()); - - EXPECT_EQ(true, callback_called_); } TEST_F(PrecacheFetcherFetcherTest, ResourceHasValidators) { @@ -236,18 +246,13 @@ TEST_F(PrecacheFetcherFetcherTest, ResourceHasValidators) { net::FakeURLFetcher *fetcher1 = nullptr, *fetcher2 = nullptr; EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) - .WillOnce(factory_.RespondWith( - "", - [](net::FakeURLFetcher* fetcher) { - std::string raw_headers("HTTP/1.1 200 OK\0ETag: foo\0\0", 27); - fetcher->set_response_headers( - make_scoped_refptr(new net::HttpResponseHeaders(raw_headers))); - }, - &fetcher1)) + .WillOnce(factory_.RespondWith("", HasETag, &fetcher1)) .WillOnce(factory_.RespondWith("", &fetcher2)); + EXPECT_CALL(*this, Callback(NotNull())); PrecacheFetcher::Fetcher precache_fetcher( - request_context_.get(), url, callback_, true /* is_resource_request */); + request_context_.get(), url, callback_, true /* is_resource_request */, + SIZE_MAX); loop_.RunUntilIdle(); @@ -255,8 +260,6 @@ TEST_F(PrecacheFetcherFetcherTest, ResourceHasValidators) { EXPECT_EQ(net::LOAD_ONLY_FROM_CACHE | kNoTracking, fetcher1->GetLoadFlags()); ASSERT_NE(nullptr, fetcher2); EXPECT_EQ(net::LOAD_VALIDATE_CACHE | kNoTracking, fetcher2->GetLoadFlags()); - - EXPECT_EQ(true, callback_called_); } TEST_F(PrecacheFetcherFetcherTest, ResourceHasNoValidators) { @@ -265,15 +268,36 @@ TEST_F(PrecacheFetcherFetcherTest, ResourceHasNoValidators) { net::FakeURLFetcher* fetcher; EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) .WillOnce(factory_.RespondWith("", &fetcher)); + EXPECT_CALL(*this, Callback(NotNull())); PrecacheFetcher::Fetcher precache_fetcher( - request_context_.get(), url, callback_, true /* is_resource_request */); + request_context_.get(), url, callback_, true /* is_resource_request */, + SIZE_MAX); loop_.RunUntilIdle(); EXPECT_EQ(net::LOAD_ONLY_FROM_CACHE | kNoTracking, fetcher->GetLoadFlags()); +} + +TEST_F(PrecacheFetcherFetcherTest, ResourceTooBig) { + GURL url(kGoodResourceURL); + + EXPECT_CALL(factory_, DoCreateURLFetcher(_, url, net::URLFetcher::GET, _)) + // Cache request will fail, so that a network request is made. Only + // network requests are byte-capped. + .WillOnce(factory_.RespondWith("", CacheMiss, nullptr)) + .WillOnce(factory_.RespondWith(std::string(100, '.'), nullptr)); - EXPECT_EQ(true, callback_called_); + // The callback should be called even though the download was cancelled, so + // that the next download can start. The arg should be null, to signify that + // the response is not present. + EXPECT_CALL(*this, Callback(nullptr)); + + PrecacheFetcher::Fetcher precache_fetcher( + request_context_.get(), url, callback_, true /* is_resource_request */, + 99 /* max_bytes */); + + loop_.RunUntilIdle(); } class PrecacheFetcherTest : public testing::Test { @@ -559,6 +583,131 @@ TEST_F(PrecacheFetcherTest, PrecacheUsingDefaultManifestURLPrefix) { #endif // PRECACHE_MANIFEST_URL_PREFIX +TEST_F(PrecacheFetcherTest, TopResourcesCount) { + SetDefaultFlags(); + + std::vector<std::string> starting_hosts; + starting_hosts.push_back("good-manifest.com"); + + PrecacheConfigurationSettings config; + config.set_top_resources_count(3); + + PrecacheManifest good_manifest; + good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved"); + good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved"); + good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved"); + good_manifest.add_resource()->set_url("http://good-manifest.com/skipped"); + good_manifest.add_resource()->set_url("http://good-manifest.com/skipped"); + + factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), + net::HTTP_OK, net::URLRequestStatus::SUCCESS); + factory_.SetFakeResponse(GURL(kGoodManifestURL), + good_manifest.SerializeAsString(), net::HTTP_OK, + net::URLRequestStatus::SUCCESS); + factory_.SetFakeResponse(GURL("http://good-manifest.com/retrieved"), "good", + net::HTTP_OK, net::URLRequestStatus::SUCCESS); + + base::HistogramTester histogram; + + { + PrecacheFetcher precache_fetcher(starting_hosts, request_context_.get(), + GURL(), std::string(), + &precache_delegate_); + precache_fetcher.Start(); + + loop_.RunUntilIdle(); + + // Destroy the PrecacheFetcher after it has finished, to record metrics. + } + + std::multiset<GURL> expected_requested_urls; + expected_requested_urls.insert(GURL(kConfigURL)); + expected_requested_urls.insert(GURL(kGoodManifestURL)); + expected_requested_urls.insert(GURL("http://good-manifest.com/retrieved")); + expected_requested_urls.insert(GURL("http://good-manifest.com/retrieved")); + expected_requested_urls.insert(GURL("http://good-manifest.com/retrieved")); + + EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); + + EXPECT_TRUE(precache_delegate_.was_on_done_called()); + + histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1); + histogram.ExpectUniqueSample("Precache.Fetch.ResponseBytes.Total", + url_callback_.total_response_bytes(), 1); + histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); +} + +// MaxBytesPerResource is impossible to test with net::FakeURLFetcherFactory: +// +// - The PrecacheFetcher::Fetcher's max_bytes logic only applies to network +// requests, and not cached requests. +// - Forcing PrecacheFetcher::Fetcher to do a network request (i.e. a second +// request for the same URL) requires either setting a custom error of +// ERR_CACHE_MISS or setting a custom ETag response header, neither of which +// is possible under FakeURLFetcherFactory. +// +// PrecacheFetcherFetcherTest.ResourceTooBig tests the bulk of the code. We'll +// assume that PrecacheFetcher passes the right max_bytes to the +// PrecacheFetcher::Fetcher constructor. +// +// TODO(twifkak): Port these tests from FakeURLFetcherFactory to +// MockURLFetcherFactory or EmbeddedTestServer, and add a test that fetches are +// cancelled midstream. + +TEST_F(PrecacheFetcherTest, MaxBytesTotal) { + SetDefaultFlags(); + + std::vector<std::string> starting_hosts; + starting_hosts.push_back("good-manifest.com"); + + PrecacheConfigurationSettings config; + config.set_max_bytes_total(1000); + + PrecacheManifest good_manifest; + good_manifest.add_resource()->set_url("http://good-manifest.com/retrieved"); + good_manifest.add_resource()->set_url("http://good-manifest.com/cancelled"); + good_manifest.add_resource()->set_url("http://good-manifest.com/skipped"); + + factory_.SetFakeResponse(GURL(kConfigURL), config.SerializeAsString(), + net::HTTP_OK, net::URLRequestStatus::SUCCESS); + factory_.SetFakeResponse(GURL(kGoodManifestURL), + good_manifest.SerializeAsString(), net::HTTP_OK, + net::URLRequestStatus::SUCCESS); + factory_.SetFakeResponse(GURL("http://good-manifest.com/retrieved"), "good", + net::HTTP_OK, net::URLRequestStatus::SUCCESS); + factory_.SetFakeResponse(GURL("http://good-manifest.com/cancelled"), + std::string(1000, '.'), net::HTTP_OK, + net::URLRequestStatus::SUCCESS); + factory_.SetFakeResponse(GURL("http://good-manifest.com/skipped"), "superbad", + net::HTTP_OK, net::URLRequestStatus::SUCCESS); + + base::HistogramTester histogram; + + { + PrecacheFetcher precache_fetcher(starting_hosts, request_context_.get(), + GURL(), std::string(), + &precache_delegate_); + precache_fetcher.Start(); + + loop_.RunUntilIdle(); + + // Destroy the PrecacheFetcher after it has finished, to record metrics. + } + + std::multiset<GURL> expected_requested_urls; + expected_requested_urls.insert(GURL(kConfigURL)); + expected_requested_urls.insert(GURL(kGoodManifestURL)); + expected_requested_urls.insert(GURL("http://good-manifest.com/retrieved")); + expected_requested_urls.insert(GURL("http://good-manifest.com/cancelled")); + + EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls()); + + EXPECT_TRUE(precache_delegate_.was_on_done_called()); + + histogram.ExpectUniqueSample("Precache.Fetch.PercentCompleted", 100, 1); + histogram.ExpectTotalCount("Precache.Fetch.TimeToComplete", 1); +} + } // namespace } // namespace precache diff --git a/components/precache/core/proto/precache.proto b/components/precache/core/proto/precache.proto index 5cdfbad..bfd4c50 100644 --- a/components/precache/core/proto/precache.proto +++ b/components/precache/core/proto/precache.proto @@ -37,4 +37,19 @@ message PrecacheConfigurationSettings { // These are hosts that the server predicts that the user will visit, as a // result of server-side analytics. repeated string forced_site = 2; + + // The number of resources to fetch for each site. Only the top + // |top_resources_count| URLs from each manifest are fetched. + optional int32 top_resources_count = 3 [default = 100]; + + // The maximum number of bytes to download per resource. Downloads of + // resources larger than this will be cancelled. This max applies only to new + // downloads; cached resources are not capped. + optional uint64 max_bytes_per_resource = 4 [default = 500000 /* 500 KB */]; + + // The maximum number of bytes per precache run. While precaching, the total + // number of bytes used for resources is tallied -- this includes new + // downloads as well as cached resources. After this limit is reached, no + // other resources will be downloaded. + optional uint64 max_bytes_total = 5 [default = 10000000 /* 10 MB */]; }; |