Commit f13c06b9 authored by Sam Burnett's avatar Sam Burnett Committed by Commit Bot

ReportingService: Load state from the PersistentReportingStore

Bug: 895821
Change-Id: If34c7c76d59d713a57fac8d9901f3549f990a7a8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1704680
Commit-Queue: Lily Chen <chlily@chromium.org>
Reviewed-by: default avatarChristian Dullweber <dullweber@chromium.org>
Reviewed-by: default avatarLily Chen <chlily@chromium.org>
Cr-Commit-Position: refs/heads/master@{#682295}
parent b9d88681
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/browsing_data/browsing_data_remover_impl.h"
#include <stddef.h>
#include <stdint.h>
......@@ -27,7 +29,6 @@
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "content/browser/browsing_data/browsing_data_remover_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browsing_data_filter_builder.h"
......@@ -63,26 +64,28 @@
#if BUILDFLAG(ENABLE_REPORTING)
#include "net/network_error_logging/mock_persistent_nel_store.h"
#include "net/network_error_logging/network_error_logging_service.h"
#include "net/reporting/mock_persistent_reporting_store.h"
#include "net/reporting/reporting_cache.h"
#include "net/reporting/reporting_endpoint.h"
#include "net/reporting/reporting_report.h"
#include "net/reporting/reporting_service.h"
#include "net/reporting/reporting_test_util.h"
#endif // BUILDFLAG(ENABLE_REPORTING)
using testing::_;
using testing::ByRef;
using testing::Eq;
using testing::Invoke;
using testing::IsEmpty;
using testing::MakeMatcher;
using testing::MatchResultListener;
using testing::Matcher;
using testing::MatcherInterface;
using testing::MatchResultListener;
using testing::Not;
using testing::Return;
using testing::SizeIs;
using testing::UnorderedElementsAre;
using testing::WithArgs;
using testing::_;
using CookieDeletionFilterPtr = network::mojom::CookieDeletionFilterPtr;
namespace content {
......@@ -1285,7 +1288,64 @@ TEST_F(BrowsingDataRemoverImplTest, RemoveCodeCache) {
}
#if BUILDFLAG(ENABLE_REPORTING)
TEST_F(BrowsingDataRemoverImplTest, RemoveReportingCache) {
TEST_F(BrowsingDataRemoverImplTest, RemoveReportingCache_WithStore) {
// TODO: rewrite this test to work with Network Service objects.
// https://crbug.com/967698
if (base::FeatureList::IsEnabled(network::features::kNetworkService))
return;
net::MockPersistentReportingStore store;
auto reporting_context = std::make_unique<net::TestReportingContext>(
base::DefaultClock::GetInstance(), base::DefaultTickClock::GetInstance(),
net::ReportingPolicy(), &store);
net::ReportingCache* reporting_cache = reporting_context->cache();
std::unique_ptr<net::ReportingService> reporting_service =
net::ReportingService::CreateForTesting(std::move(reporting_context));
reporting_service->ProcessHeader(GURL("https://origin/path"),
R"json(
{"endpoints":[{"url":"https://endpoint/"}],
"group":"group", "max_age":86400}
)json");
store.FinishLoading(true /* load_success */);
store.Flush();
BrowserContext::GetDefaultStoragePartition(GetBrowserContext())
->GetURLRequestContext()
->GetURLRequestContext()
->set_reporting_service(reporting_service.get());
ASSERT_EQ(1u, reporting_cache->GetEndpointCount());
EXPECT_THAT(
store.GetAllCommands(),
testing::IsSupersetOf(
{net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
ADD_REPORTING_ENDPOINT,
GURL("https://origin/"), "group", GURL("https://endpoint/")),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
ADD_REPORTING_ENDPOINT_GROUP,
GURL("https://origin/"), "group")}));
BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(),
BrowsingDataRemover::DATA_TYPE_COOKIES, false);
EXPECT_EQ(0u, reporting_cache->GetEndpointCount());
EXPECT_THAT(
store.GetAllCommands(),
testing::IsSupersetOf(
{net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
DELETE_REPORTING_ENDPOINT,
GURL("https://origin/"), "group", GURL("https://endpoint/")),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
DELETE_REPORTING_ENDPOINT_GROUP,
GURL("https://origin/"), "group")}));
}
TEST_F(BrowsingDataRemoverImplTest, RemoveReportingCache_NoPersistentStore) {
// TODO: rewrite this test to work with Network Service objects.
// https://crbug.com/967698
if (base::FeatureList::IsEnabled(network::features::kNetworkService))
......@@ -1372,6 +1432,118 @@ TEST_F(BrowsingDataRemoverImplTest, RemoveReportingCache_SpecificOrigins) {
url::Origin::Create(domain4)));
}
TEST_F(BrowsingDataRemoverImplTest,
RemoveReportingCache_SpecificOriginsFromStore) {
// TODO: rewrite this test to work with Network Service objects.
// https://crbug.com/967698
if (base::FeatureList::IsEnabled(network::features::kNetworkService))
return;
net::MockPersistentReportingStore store;
auto reporting_context = std::make_unique<net::TestReportingContext>(
base::DefaultClock::GetInstance(), base::DefaultTickClock::GetInstance(),
net::ReportingPolicy(), &store);
net::ReportingCache* reporting_cache = reporting_context->cache();
std::unique_ptr<net::ReportingService> reporting_service =
net::ReportingService::CreateForTesting(std::move(reporting_context));
reporting_service->ProcessHeader(GURL("https://google.com"),
R"json(
{"endpoints":[{"url":"https://google.com"}],
"group":"group", "max_age":86400}
)json");
reporting_service->ProcessHeader(GURL("https://host2.com"),
R"json(
{"endpoints":[{"url":"https://host2.com"}],
"group":"group", "max_age":86400}
)json");
reporting_service->ProcessHeader(GURL("https://host3.com"),
R"json(
{"endpoints":[{"url":"https://host3.com"}],
"group":"group", "max_age":86400}
)json");
reporting_service->ProcessHeader(GURL("https://host4.com"),
R"json(
{"endpoints":[{"url":"https://host4.com"}],
"group":"group", "max_age":86400}
)json");
store.FinishLoading(true /* load_success */);
store.Flush();
BrowserContext::GetDefaultStoragePartition(GetBrowserContext())
->GetURLRequestContext()
->GetURLRequestContext()
->set_reporting_service(reporting_service.get());
ASSERT_EQ(4u, reporting_cache->GetEndpointCount());
EXPECT_THAT(
store.GetAllCommands(),
testing::IsSupersetOf(
{net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
ADD_REPORTING_ENDPOINT,
GURL("https://google.com/"), "group",
GURL("https://google.com/")),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
ADD_REPORTING_ENDPOINT_GROUP,
GURL("https://google.com/"), "group"),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
ADD_REPORTING_ENDPOINT,
GURL("https://host2.com/"), "group", GURL("https://host2.com/")),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
ADD_REPORTING_ENDPOINT_GROUP,
GURL("https://host2.com/"), "group"),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
ADD_REPORTING_ENDPOINT,
GURL("https://host3.com/"), "group", GURL("https://host3.com/")),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
ADD_REPORTING_ENDPOINT_GROUP,
GURL("https://host3.com/"), "group"),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
ADD_REPORTING_ENDPOINT,
GURL("https://host4.com/"), "group", GURL("https://host4.com/")),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
ADD_REPORTING_ENDPOINT_GROUP,
GURL("https://host4.com/"), "group")}));
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder(
BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST));
filter_builder->AddRegisterableDomain("google.com");
filter_builder->AddRegisterableDomain("host3.com");
BlockUntilOriginDataRemoved(base::Time(), base::Time::Max(),
BrowsingDataRemover::DATA_TYPE_COOKIES,
std::move(filter_builder));
EXPECT_EQ(2u, reporting_cache->GetEndpointCount());
EXPECT_THAT(
store.GetAllCommands(),
testing::IsSupersetOf(
{net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
DELETE_REPORTING_ENDPOINT,
GURL("https://google.com/"), "group",
GURL("https://google.com/")),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
DELETE_REPORTING_ENDPOINT_GROUP,
GURL("https://google.com/"), "group"),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
DELETE_REPORTING_ENDPOINT,
GURL("https://host3.com/"), "group", GURL("https://host3.com/")),
net::MockPersistentReportingStore::Command(
net::MockPersistentReportingStore::Command::Type::
DELETE_REPORTING_ENDPOINT_GROUP,
GURL("https://host3.com/"), "group")}));
}
TEST_F(BrowsingDataRemoverImplTest, RemoveReportingCache_NoService) {
// TODO: rewrite this test to work with Network Service objects.
// https://crbug.com/967698
......
......@@ -5,29 +5,80 @@
#include "net/reporting/mock_persistent_reporting_store.h"
#include <algorithm>
#include <memory>
namespace net {
MockPersistentReportingStore::Command::Command(
Type type,
ReportingClientsLoadedCallback loaded_callback)
: type(type), loaded_callback(std::move(loaded_callback)) {}
: type(type), loaded_callback(std::move(loaded_callback)) {
DCHECK(type == Type::LOAD_REPORTING_CLIENTS);
}
MockPersistentReportingStore::Command::Command(
Type type,
const ReportingEndpoint& endpoint)
: type(type), group_key(endpoint.group_key), url(endpoint.info.url) {}
: type(type), group_key(endpoint.group_key), url(endpoint.info.url) {
DCHECK(type == Type::ADD_REPORTING_ENDPOINT ||
type == Type::UPDATE_REPORTING_ENDPOINT_DETAILS ||
type == Type::DELETE_REPORTING_ENDPOINT);
}
MockPersistentReportingStore::Command::Command(Type type,
const GURL& origin,
const std::string& group,
const GURL& endpoint)
: MockPersistentReportingStore::Command(type,
url::Origin::Create(origin),
group,
endpoint) {}
MockPersistentReportingStore::Command::Command(Type type,
const url::Origin& origin,
const std::string& group,
const GURL& endpoint)
: MockPersistentReportingStore::Command(
type,
ReportingEndpoint(origin,
group,
ReportingEndpoint::EndpointInfo{endpoint})) {}
MockPersistentReportingStore::Command::Command(
Type type,
const CachedReportingEndpointGroup& group)
: type(type), group_key(group.group_key) {}
: type(type), group_key(group.group_key) {
DCHECK(type == Type::ADD_REPORTING_ENDPOINT_GROUP ||
type == Type::UPDATE_REPORTING_ENDPOINT_GROUP_DETAILS ||
type == Type::UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME ||
type == Type::DELETE_REPORTING_ENDPOINT_GROUP);
}
MockPersistentReportingStore::Command::Command(Type type) : type(type) {}
MockPersistentReportingStore::Command::Command(Type type) : type(type) {
DCHECK(type == Type::FLUSH || type == Type::LOAD_REPORTING_CLIENTS);
}
MockPersistentReportingStore::Command::Command(const Command& other)
: type(other.type), group_key(other.group_key), url(other.url) {}
MockPersistentReportingStore::Command::Command(Type type,
const GURL& origin,
const std::string& group)
: MockPersistentReportingStore::Command(type,
url::Origin::Create(origin),
group) {}
MockPersistentReportingStore::Command::Command(Type type,
const url::Origin& origin,
const std::string& group)
: MockPersistentReportingStore::Command(
type,
CachedReportingEndpointGroup(origin,
group,
OriginSubdomains::DEFAULT /* unused */,
base::Time() /* unused */,
base::Time() /* unused */)) {}
MockPersistentReportingStore::Command::Command(Command&& other) = default;
MockPersistentReportingStore::Command::~Command() = default;
......@@ -68,6 +119,52 @@ bool operator!=(const MockPersistentReportingStore::Command& lhs,
return !(lhs == rhs);
}
std::ostream& operator<<(std::ostream& out,
const MockPersistentReportingStore::Command& cmd) {
switch (cmd.type) {
case MockPersistentReportingStore::Command::Type::LOAD_REPORTING_CLIENTS:
return out << "LOAD_REPORTING_CLIENTS()";
case MockPersistentReportingStore::Command::Type::FLUSH:
return out << "FLUSH()";
case MockPersistentReportingStore::Command::Type::ADD_REPORTING_ENDPOINT:
return out << "ADD_REPORTING_ENDPOINT("
<< "origin=" << cmd.group_key.origin << ", "
<< "group=" << cmd.group_key.group_name << ", "
<< "endpoint=" << cmd.url << ")";
case MockPersistentReportingStore::Command::Type::
UPDATE_REPORTING_ENDPOINT_DETAILS:
return out << "UPDATE_REPORTING_ENDPOINT_DETAILS("
<< "origin=" << cmd.group_key.origin << ", "
<< "group=" << cmd.group_key.group_name << ", "
<< "endpoint=" << cmd.url << ")";
case MockPersistentReportingStore::Command::Type::DELETE_REPORTING_ENDPOINT:
return out << "DELETE_REPORTING_ENDPOINT("
<< "origin=" << cmd.group_key.origin << ", "
<< "group=" << cmd.group_key.group_name << ", "
<< "endpoint=" << cmd.url << ")";
case MockPersistentReportingStore::Command::Type::
ADD_REPORTING_ENDPOINT_GROUP:
return out << "ADD_REPORTING_ENDPOINT_GROUP("
<< "origin=" << cmd.group_key.origin << ", "
<< "group=" << cmd.group_key.group_name << ")";
case MockPersistentReportingStore::Command::Type::
UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME:
return out << "UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME("
<< "origin=" << cmd.group_key.origin << ", "
<< "group=" << cmd.group_key.group_name << ")";
case MockPersistentReportingStore::Command::Type::
UPDATE_REPORTING_ENDPOINT_GROUP_DETAILS:
return out << "UPDATE_REPORTING_ENDPOINT_GROUP_DETAILS("
<< "origin=" << cmd.group_key.origin << ", "
<< "group=" << cmd.group_key.group_name << ")";
case MockPersistentReportingStore::Command::Type::
DELETE_REPORTING_ENDPOINT_GROUP:
return out << "DELETE_REPORTING_ENDPOINT_GROUP("
<< "origin=" << cmd.group_key.origin << ", "
<< "group=" << cmd.group_key.group_name << ")";
}
}
MockPersistentReportingStore::MockPersistentReportingStore()
: load_started_(false),
endpoint_count_(0),
......
......@@ -40,11 +40,27 @@ class MockPersistentReportingStore
// Constructor for LOAD_REPORTING_CLIENTS commands.
Command(Type type, ReportingClientsLoadedCallback loaded_callback);
// Constructor for endpoint commands.
// Constructors for endpoint commands. |type| must be one of
// ADD_REPORTING_ENDPOINT, UPDATE_REPORTING_ENDPOINT_DETAILS, or
// DELETE_REPORTING_ENDPOINT
Command(Type type, const ReportingEndpoint& endpoint);
// Constructor for endpoint group commands.
Command(Type type,
const GURL& origin,
const std::string& group,
const GURL& endpoint);
Command(Type type,
const url::Origin& origin,
const std::string& group,
const GURL& endpoint);
// Constructors for endpoint group commands. |type| must be one of
// ADD_REPORTING_ENDPOINT_GROUP,
// UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME,
// UPDATE_REPORTING_ENDPOINT_GROUP_DETAILS, or
// DELETE_REPORTING_ENDPOINT_GROUP
Command(Type type, const CachedReportingEndpointGroup& group);
// Constructor for FLUSH commands.
Command(Type type, const GURL& origin, const std::string& group);
Command(Type type, const url::Origin& origin, const std::string& group);
// |type| must be LOAD_REPORTING_CLIENTS or FLUSH.
Command(Type type);
Command(const Command& other);
......@@ -147,6 +163,8 @@ bool operator==(const MockPersistentReportingStore::Command& lhs,
const MockPersistentReportingStore::Command& rhs);
bool operator!=(const MockPersistentReportingStore::Command& lhs,
const MockPersistentReportingStore::Command& rhs);
std::ostream& operator<<(std::ostream& out,
const MockPersistentReportingStore::Command& cmd);
} // namespace net
......
......@@ -38,6 +38,7 @@ void ReportingBrowsingDataRemover::RemoveBrowsingData(
cache->RemoveClient(origin);
}
}
cache->Flush();
}
// static
......@@ -50,6 +51,7 @@ void ReportingBrowsingDataRemover::RemoveAllBrowsingData(ReportingCache* cache,
if ((data_type_mask & DATA_TYPE_CLIENTS) != 0) {
cache->RemoveAllClients();
}
cache->Flush();
}
} // namespace net
......@@ -199,6 +199,9 @@ class NET_EXPORT ReportingCache {
// Gets the total number of endpoints in the cache across all origins.
virtual size_t GetEndpointCount() const = 0;
// Flush the contents of the cache to disk, if applicable.
virtual void Flush() = 0;
// Finds an endpoint for the given |origin|, |group_name|, and |url|,
// otherwise returns an invalid ReportingEndpoint.
virtual ReportingEndpoint GetEndpointForTesting(const url::Origin& origin,
......
......@@ -554,6 +554,11 @@ size_t ReportingCacheImpl::GetEndpointCount() const {
return endpoints_.size();
}
void ReportingCacheImpl::Flush() {
if (context_->IsClientDataPersisted())
store()->Flush();
}
ReportingEndpoint ReportingCacheImpl::GetEndpointForTesting(
const url::Origin& origin,
const std::string& group_name,
......
......@@ -84,6 +84,7 @@ class ReportingCacheImpl : public ReportingCache {
const std::string& group_name) override;
base::Value GetClientsAsValue() const override;
size_t GetEndpointCount() const override;
void Flush() override;
ReportingEndpoint GetEndpointForTesting(const url::Origin& origin,
const std::string& group_name,
const GURL& url) const override;
......
......@@ -87,8 +87,6 @@ bool ReportingContext::IsClientDataPersisted() const {
void ReportingContext::OnShutdown() {
uploader_->OnShutdown();
if (store_)
store_->Flush();
}
ReportingContext::ReportingContext(
......
......@@ -28,14 +28,28 @@ namespace {
constexpr int kMaxJsonSize = 16 * 1024;
constexpr int kMaxJsonDepth = 5;
// If constructed with a PersistentReportingStore, the first call to any of
// QueueReport(), ProcessHeader(), RemoveBrowsingData(), or
// RemoveAllBrowsingData() on a valid input will trigger a load from the store.
// Tasks are queued pending completion of loading from the store.
class ReportingServiceImpl : public ReportingService {
public:
ReportingServiceImpl(std::unique_ptr<ReportingContext> context)
: context_(std::move(context)), shut_down_(false) {}
: context_(std::move(context)),
shut_down_(false),
started_loading_from_store_(false),
initialized_(false),
weak_factory_(this) {
if (!context_->IsClientDataPersisted())
initialized_ = true;
}
// ReportingService implementation:
~ReportingServiceImpl() override = default;
~ReportingServiceImpl() override {
if (initialized_)
context_->cache()->Flush();
}
void QueueReport(const GURL& url,
const std::string& user_agent,
......@@ -43,9 +57,6 @@ class ReportingServiceImpl : public ReportingService {
const std::string& type,
std::unique_ptr<const base::Value> body,
int depth) override {
if (shut_down_)
return;
DCHECK(context_);
DCHECK(context_->delegate());
......@@ -57,16 +68,18 @@ class ReportingServiceImpl : public ReportingService {
if (!sanitized_url.is_valid())
return;
context_->cache()->AddReport(sanitized_url, user_agent, group, type,
std::move(body), depth,
context_->tick_clock().NowTicks(), 0);
base::TimeTicks queued_ticks = context_->tick_clock().NowTicks();
// base::Unretained is safe because the callback is stored in
// |task_backlog_| which will not outlive |this|.
DoOrBacklogTask(base::BindOnce(&ReportingServiceImpl::DoQueueReport,
base::Unretained(this),
std::move(sanitized_url), user_agent, group,
type, std::move(body), depth, queued_ticks));
}
void ProcessHeader(const GURL& url,
const std::string& header_string) override {
if (shut_down_)
return;
if (header_string.size() > kMaxJsonSize) {
ReportingHeaderParser::RecordHeaderDiscardedForJsonTooBig();
return;
......@@ -81,20 +94,23 @@ class ReportingServiceImpl : public ReportingService {
}
DVLOG(1) << "Received Reporting policy for " << url.GetOrigin();
ReportingHeaderParser::ParseHeader(context_.get(), url,
std::move(header_value));
DoOrBacklogTask(base::BindOnce(&ReportingServiceImpl::DoProcessHeader,
base::Unretained(this), url,
std::move(header_value)));
}
void RemoveBrowsingData(int data_type_mask,
const base::RepeatingCallback<bool(const GURL&)>&
origin_filter) override {
ReportingBrowsingDataRemover::RemoveBrowsingData(
context_->cache(), data_type_mask, origin_filter);
DoOrBacklogTask(base::BindOnce(&ReportingServiceImpl::DoRemoveBrowsingData,
base::Unretained(this), data_type_mask,
origin_filter));
}
void RemoveAllBrowsingData(int data_type_mask) override {
ReportingBrowsingDataRemover::RemoveAllBrowsingData(context_->cache(),
data_type_mask);
DoOrBacklogTask(
base::BindOnce(&ReportingServiceImpl::DoRemoveAllBrowsingData,
base::Unretained(this), data_type_mask));
}
void OnShutdown() override {
......@@ -119,8 +135,98 @@ class ReportingServiceImpl : public ReportingService {
}
private:
void DoOrBacklogTask(base::OnceClosure task) {
if (shut_down_)
return;
FetchAllClientsFromStoreIfNecessary();
if (!initialized_) {
task_backlog_.push_back(std::move(task));
return;
}
std::move(task).Run();
}
void DoQueueReport(GURL sanitized_url,
const std::string& user_agent,
const std::string& group,
const std::string& type,
std::unique_ptr<const base::Value> body,
int depth,
base::TimeTicks queued_ticks) {
DCHECK(initialized_);
context_->cache()->AddReport(sanitized_url, user_agent, group, type,
std::move(body), depth, queued_ticks,
0 /* attempts */);
}
void DoProcessHeader(const GURL& url,
std::unique_ptr<base::Value> header_value) {
DCHECK(initialized_);
ReportingHeaderParser::ParseHeader(context_.get(), url,
std::move(header_value));
}
void DoRemoveBrowsingData(
int data_type_mask,
const base::RepeatingCallback<bool(const GURL&)>& origin_filter) {
DCHECK(initialized_);
ReportingBrowsingDataRemover::RemoveBrowsingData(
context_->cache(), data_type_mask, origin_filter);
}
void DoRemoveAllBrowsingData(int data_type_mask) {
DCHECK(initialized_);
ReportingBrowsingDataRemover::RemoveAllBrowsingData(context_->cache(),
data_type_mask);
}
void ExecuteBacklog() {
DCHECK(initialized_);
DCHECK(context_);
if (shut_down_)
return;
for (base::OnceClosure& task : task_backlog_) {
std::move(task).Run();
}
task_backlog_.clear();
}
void FetchAllClientsFromStoreIfNecessary() {
if (!context_->IsClientDataPersisted() || started_loading_from_store_)
return;
started_loading_from_store_ = true;
FetchAllClientsFromStore();
}
void FetchAllClientsFromStore() {
DCHECK(context_->IsClientDataPersisted());
DCHECK(!initialized_);
context_->store()->LoadReportingClients(base::BindOnce(
&ReportingServiceImpl::OnClientsLoaded, weak_factory_.GetWeakPtr()));
}
void OnClientsLoaded(
std::vector<ReportingEndpoint> loaded_endpoints,
std::vector<CachedReportingEndpointGroup> loaded_endpoint_groups) {
initialized_ = true;
context_->cache()->AddClientsLoadedFromStore(
std::move(loaded_endpoints), std::move(loaded_endpoint_groups));
ExecuteBacklog();
}
std::unique_ptr<ReportingContext> context_;
bool shut_down_;
bool started_loading_from_store_;
bool initialized_;
std::vector<base::OnceClosure> task_backlog_;
base::WeakPtrFactory<ReportingServiceImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ReportingServiceImpl);
};
......
......@@ -7,9 +7,12 @@
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/time/tick_clock.h"
#include "base/values.h"
#include "net/reporting/mock_persistent_reporting_store.h"
#include "net/reporting/reporting_browsing_data_remover.h"
#include "net/reporting/reporting_cache.h"
#include "net/reporting/reporting_context.h"
#include "net/reporting/reporting_policy.h"
......@@ -17,26 +20,49 @@
#include "net/reporting/reporting_service.h"
#include "net/reporting/reporting_test_util.h"
#include "net/test/test_with_scoped_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
class ReportingServiceTest : public TestWithScopedTaskEnvironment {
using CommandType = MockPersistentReportingStore::Command::Type;
// The tests are parametrized on a boolean value which represents whether to use
// a MockPersistentReportingStore (if false, no store is used).
class ReportingServiceTest : public ::testing::TestWithParam<bool>,
public WithScopedTaskEnvironment {
protected:
const GURL kUrl_ = GURL("https://origin/path");
const GURL kUrl2_ = GURL("https://origin2/path");
const url::Origin kOrigin_ = url::Origin::Create(kUrl_);
const url::Origin kOrigin2_ = url::Origin::Create(kUrl2_);
const GURL kEndpoint_ = GURL("https://endpoint/");
const std::string kUserAgent_ = "Mozilla/1.0";
const std::string kGroup_ = "group";
const std::string kType_ = "type";
ReportingServiceTest()
: context_(
new TestReportingContext(&clock_, &tick_clock_, ReportingPolicy())),
service_(
ReportingService::CreateForTesting(base::WrapUnique(context_))) {}
ReportingServiceTest() {
if (GetParam())
store_ = std::make_unique<MockPersistentReportingStore>();
else
store_ = nullptr;
auto test_context = std::make_unique<TestReportingContext>(
&clock_, &tick_clock_, ReportingPolicy(), store_.get());
context_ = test_context.get();
service_ = ReportingService::CreateForTesting(std::move(test_context));
}
// If the store exists, simulate finishing loading the store, which should
// make the rest of the test run synchronously.
void FinishLoading(bool load_success) {
if (store_)
store_->FinishLoading(load_success);
}
MockPersistentReportingStore* store() { return store_.get(); }
TestReportingContext* context() { return context_; }
ReportingService* service() { return service_.get(); }
......@@ -44,13 +70,15 @@ class ReportingServiceTest : public TestWithScopedTaskEnvironment {
base::SimpleTestClock clock_;
base::SimpleTestTickClock tick_clock_;
std::unique_ptr<MockPersistentReportingStore> store_;
TestReportingContext* context_;
std::unique_ptr<ReportingService> service_;
};
TEST_F(ReportingServiceTest, QueueReport) {
TEST_P(ReportingServiceTest, QueueReport) {
service()->QueueReport(kUrl_, kUserAgent_, kGroup_, kType_,
std::make_unique<base::DictionaryValue>(), 0);
FinishLoading(true /* load_success */);
std::vector<const ReportingReport*> reports;
context()->cache()->GetReports(&reports);
......@@ -61,11 +89,12 @@ TEST_F(ReportingServiceTest, QueueReport) {
EXPECT_EQ(kType_, reports[0]->type);
}
TEST_F(ReportingServiceTest, QueueReportSanitizeUrl) {
TEST_P(ReportingServiceTest, QueueReportSanitizeUrl) {
// Same as kUrl_ but with username, password, and fragment.
GURL url = GURL("https://username:password@origin/path#fragment");
service()->QueueReport(url, kUserAgent_, kGroup_, kType_,
std::make_unique<base::DictionaryValue>(), 0);
FinishLoading(true /* load_success */);
std::vector<const ReportingReport*> reports;
context()->cache()->GetReports(&reports);
......@@ -76,8 +105,10 @@ TEST_F(ReportingServiceTest, QueueReportSanitizeUrl) {
EXPECT_EQ(kType_, reports[0]->type);
}
TEST_F(ReportingServiceTest, DontQueueReportInvalidUrl) {
TEST_P(ReportingServiceTest, DontQueueReportInvalidUrl) {
GURL url = GURL("https://");
// This does not trigger an attempt to load from the store because the url
// is immediately rejected as invalid.
service()->QueueReport(url, kUserAgent_, kGroup_, kType_,
std::make_unique<base::DictionaryValue>(), 0);
......@@ -86,7 +117,7 @@ TEST_F(ReportingServiceTest, DontQueueReportInvalidUrl) {
ASSERT_EQ(0u, reports.size());
}
TEST_F(ReportingServiceTest, ProcessHeader) {
TEST_P(ReportingServiceTest, ProcessHeader) {
service()->ProcessHeader(kUrl_, "{\"endpoints\":[{\"url\":\"" +
kEndpoint_.spec() +
"\"}],"
......@@ -94,11 +125,12 @@ TEST_F(ReportingServiceTest, ProcessHeader) {
kGroup_ +
"\","
"\"max_age\":86400}");
FinishLoading(true /* load_success */);
EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
}
TEST_F(ReportingServiceTest, ProcessHeader_TooLong) {
TEST_P(ReportingServiceTest, ProcessHeader_TooLong) {
const std::string header_too_long =
"{\"endpoints\":[{\"url\":\"" + kEndpoint_.spec() +
"\"}],"
......@@ -107,12 +139,14 @@ TEST_F(ReportingServiceTest, ProcessHeader_TooLong) {
"\","
"\"max_age\":86400," +
"\"junk\":\"" + std::string(32 * 1024, 'a') + "\"}";
// This does not trigger an attempt to load from the store because the header
// is immediately rejected as invalid.
service()->ProcessHeader(kUrl_, header_too_long);
EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
}
TEST_F(ReportingServiceTest, ProcessHeader_TooDeep) {
TEST_P(ReportingServiceTest, ProcessHeader_TooDeep) {
const std::string header_too_deep = "{\"endpoints\":[{\"url\":\"" +
kEndpoint_.spec() +
"\"}],"
......@@ -121,10 +155,207 @@ TEST_F(ReportingServiceTest, ProcessHeader_TooDeep) {
"\","
"\"max_age\":86400," +
"\"junk\":[[[[[[[[[[]]]]]]]]]]}";
// This does not trigger an attempt to load from the store because the header
// is immediately rejected as invalid.
service()->ProcessHeader(kUrl_, header_too_deep);
EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
}
TEST_P(ReportingServiceTest, WriteToStore) {
if (!store())
return;
MockPersistentReportingStore::CommandList expected_commands;
// This first call to any public method triggers a load. The load will block
// until we call FinishLoading.
service()->ProcessHeader(kUrl_, "{\"endpoints\":[{\"url\":\"" +
kEndpoint_.spec() +
"\"}],"
"\"group\":\"" +
kGroup_ +
"\","
"\"max_age\":86400}");
expected_commands.emplace_back(CommandType::LOAD_REPORTING_CLIENTS);
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
// Unblock the load. The will let the remaining calls to the service complete
// without blocking.
FinishLoading(true /* load_success */);
expected_commands.emplace_back(
CommandType::ADD_REPORTING_ENDPOINT,
ReportingEndpoint(kOrigin_, kGroup_,
ReportingEndpoint::EndpointInfo{kEndpoint_}));
expected_commands.emplace_back(
CommandType::ADD_REPORTING_ENDPOINT_GROUP,
CachedReportingEndpointGroup(
kOrigin_, kGroup_, OriginSubdomains::DEFAULT /* irrelevant */,
base::Time() /* irrelevant */, base::Time() /* irrelevant */));
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
service()->ProcessHeader(kUrl2_, "{\"endpoints\":[{\"url\":\"" +
kEndpoint_.spec() +
"\"}],"
"\"group\":\"" +
kGroup_ +
"\","
"\"max_age\":86400}");
expected_commands.emplace_back(
CommandType::ADD_REPORTING_ENDPOINT,
ReportingEndpoint(kOrigin2_, kGroup_,
ReportingEndpoint::EndpointInfo{kEndpoint_}));
expected_commands.emplace_back(
CommandType::ADD_REPORTING_ENDPOINT_GROUP,
CachedReportingEndpointGroup(
kOrigin2_, kGroup_, OriginSubdomains::DEFAULT /* irrelevant */,
base::Time() /* irrelevant */, base::Time() /* irrelevant */));
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
service()->QueueReport(kUrl_, kUserAgent_, kGroup_, kType_,
std::make_unique<base::DictionaryValue>(), 0);
expected_commands.emplace_back(
CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME,
CachedReportingEndpointGroup(
kOrigin_, kGroup_, OriginSubdomains::DEFAULT /* irrelevant */,
base::Time() /* irrelevant */, base::Time() /* irrelevant */));
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
service()->RemoveBrowsingData(ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS,
base::BindRepeating([](const GURL& url) {
return url.host() == "origin";
}));
expected_commands.emplace_back(
CommandType::DELETE_REPORTING_ENDPOINT,
ReportingEndpoint(kOrigin_, kGroup_,
ReportingEndpoint::EndpointInfo{kEndpoint_}));
expected_commands.emplace_back(
CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
CachedReportingEndpointGroup(
kOrigin_, kGroup_, OriginSubdomains::DEFAULT /* irrelevant */,
base::Time() /* irrelevant */, base::Time() /* irrelevant */));
expected_commands.emplace_back(CommandType::FLUSH);
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
service()->RemoveAllBrowsingData(
ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS);
expected_commands.emplace_back(
CommandType::DELETE_REPORTING_ENDPOINT,
ReportingEndpoint(kOrigin2_, kGroup_,
ReportingEndpoint::EndpointInfo{kEndpoint_}));
expected_commands.emplace_back(
CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
CachedReportingEndpointGroup(
kOrigin2_, kGroup_, OriginSubdomains::DEFAULT /* irrelevant */,
base::Time() /* irrelevant */, base::Time() /* irrelevant */));
expected_commands.emplace_back(CommandType::FLUSH);
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
}
TEST_P(ReportingServiceTest, WaitUntilLoadFinishesBeforeWritingToStore) {
if (!store())
return;
MockPersistentReportingStore::CommandList expected_commands;
// This first call to any public method triggers a load. The load will block
// until we call FinishLoading.
service()->ProcessHeader(kUrl_, "{\"endpoints\":[{\"url\":\"" +
kEndpoint_.spec() +
"\"}],"
"\"group\":\"" +
kGroup_ +
"\","
"\"max_age\":86400}");
expected_commands.emplace_back(CommandType::LOAD_REPORTING_CLIENTS);
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
service()->ProcessHeader(kUrl2_, "{\"endpoints\":[{\"url\":\"" +
kEndpoint_.spec() +
"\"}],"
"\"group\":\"" +
kGroup_ +
"\","
"\"max_age\":86400}");
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
service()->QueueReport(kUrl_, kUserAgent_, kGroup_, kType_,
std::make_unique<base::DictionaryValue>(), 0);
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
service()->RemoveBrowsingData(ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS,
base::BindRepeating([](const GURL& url) {
return url.host() == "origin";
}));
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
service()->RemoveAllBrowsingData(
ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS);
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
// Unblock the load. The will let the remaining calls to the service complete
// without blocking.
FinishLoading(true /* load_success */);
expected_commands.emplace_back(
CommandType::ADD_REPORTING_ENDPOINT,
ReportingEndpoint(kOrigin_, kGroup_,
ReportingEndpoint::EndpointInfo{kEndpoint_}));
expected_commands.emplace_back(
CommandType::ADD_REPORTING_ENDPOINT,
ReportingEndpoint(kOrigin2_, kGroup_,
ReportingEndpoint::EndpointInfo{kEndpoint_}));
expected_commands.emplace_back(
CommandType::ADD_REPORTING_ENDPOINT_GROUP,
CachedReportingEndpointGroup(
kOrigin_, kGroup_, OriginSubdomains::DEFAULT /* irrelevant */,
base::Time() /* irrelevant */, base::Time() /* irrelevant */));
expected_commands.emplace_back(
CommandType::ADD_REPORTING_ENDPOINT_GROUP,
CachedReportingEndpointGroup(
kOrigin2_, kGroup_, OriginSubdomains::DEFAULT /* irrelevant */,
base::Time() /* irrelevant */, base::Time() /* irrelevant */));
expected_commands.emplace_back(
CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME,
CachedReportingEndpointGroup(
kOrigin_, kGroup_, OriginSubdomains::DEFAULT /* irrelevant */,
base::Time() /* irrelevant */, base::Time() /* irrelevant */));
expected_commands.emplace_back(
CommandType::DELETE_REPORTING_ENDPOINT,
ReportingEndpoint(kOrigin_, kGroup_,
ReportingEndpoint::EndpointInfo{kEndpoint_}));
expected_commands.emplace_back(
CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
CachedReportingEndpointGroup(
kOrigin_, kGroup_, OriginSubdomains::DEFAULT /* irrelevant */,
base::Time() /* irrelevant */, base::Time() /* irrelevant */));
expected_commands.emplace_back(CommandType::FLUSH);
expected_commands.emplace_back(
CommandType::DELETE_REPORTING_ENDPOINT,
ReportingEndpoint(kOrigin2_, kGroup_,
ReportingEndpoint::EndpointInfo{kEndpoint_}));
expected_commands.emplace_back(
CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
CachedReportingEndpointGroup(
kOrigin2_, kGroup_, OriginSubdomains::DEFAULT /* irrelevant */,
base::Time() /* irrelevant */, base::Time() /* irrelevant */));
expected_commands.emplace_back(CommandType::FLUSH);
EXPECT_THAT(store()->GetAllCommands(),
testing::UnorderedElementsAreArray(expected_commands));
}
INSTANTIATE_TEST_SUITE_P(ReportingServiceStoreTest,
ReportingServiceTest,
::testing::Bool());
} // namespace
} // namespace net
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment