summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortwifkak <twifkak@chromium.org>2016-02-29 14:42:06 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-29 22:43:34 +0000
commitddf019b2bf5bb413268a970ed359eaca61467aa4 (patch)
treee5356b6cc693e1788725e321e78afcd522cdebc3
parent07d6344d650052669d923fecbe26758416799e77 (diff)
downloadchromium_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.gypi1
-rw-r--r--components/precache/core/DEPS6
-rw-r--r--components/precache/core/precache_fetcher.cc141
-rw-r--r--components/precache/core/precache_fetcher.h40
-rw-r--r--components/precache/core/precache_fetcher_unittest.cc217
-rw-r--r--components/precache/core/proto/precache.proto15
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 */];
};