| Index: net/reporting/reporting_persister.cc
|
| diff --git a/net/reporting/reporting_persister.cc b/net/reporting/reporting_persister.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ec2a37b7e48c633b9b3dda03da8d936d582378a0
|
| --- /dev/null
|
| +++ b/net/reporting/reporting_persister.cc
|
| @@ -0,0 +1,358 @@
|
| +// Copyright 2017 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "net/reporting/reporting_persister.h"
|
| +
|
| +#include <vector>
|
| +
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| +#include "base/time/clock.h"
|
| +#include "base/time/tick_clock.h"
|
| +#include "base/time/time.h"
|
| +#include "base/timer/timer.h"
|
| +#include "base/values.h"
|
| +#include "net/reporting/reporting_cache.h"
|
| +#include "net/reporting/reporting_client.h"
|
| +#include "net/reporting/reporting_context.h"
|
| +#include "net/reporting/reporting_delegate.h"
|
| +#include "net/reporting/reporting_observer.h"
|
| +#include "net/reporting/reporting_policy.h"
|
| +#include "net/reporting/reporting_report.h"
|
| +
|
| +namespace net {
|
| +namespace {
|
| +
|
| +std::unique_ptr<base::Value> SerializeOrigin(const url::Origin& origin) {
|
| + auto serialized = base::MakeUnique<base::DictionaryValue>();
|
| +
|
| + serialized->SetString("scheme", origin.scheme());
|
| + serialized->SetString("host", origin.host());
|
| + serialized->SetInteger("port", origin.port());
|
| + serialized->SetString("suborigin", origin.suborigin());
|
| +
|
| + return std::move(serialized);
|
| +}
|
| +
|
| +bool DeserializeOrigin(const base::DictionaryValue& serialized,
|
| + url::Origin* origin_out) {
|
| + std::string scheme;
|
| + if (!serialized.GetString("scheme", &scheme))
|
| + return false;
|
| +
|
| + std::string host;
|
| + if (!serialized.GetString("host", &host))
|
| + return false;
|
| +
|
| + int port_int;
|
| + if (!serialized.GetInteger("port", &port_int))
|
| + return false;
|
| + uint16_t port = static_cast<uint16_t>(port_int);
|
| + if (port_int != port)
|
| + return false;
|
| +
|
| + std::string suborigin;
|
| + if (!serialized.GetString("suborigin", &suborigin))
|
| + return false;
|
| +
|
| + *origin_out = url::Origin::CreateFromNormalizedTupleWithSuborigin(
|
| + scheme, host, port, suborigin);
|
| + return true;
|
| +}
|
| +
|
| +class ReportingPersisterImpl : public ReportingPersister,
|
| + public ReportingObserver {
|
| + public:
|
| + ReportingPersisterImpl(ReportingContext* context)
|
| + : context_(context), timer_(base::MakeUnique<base::OneShotTimer>()) {}
|
| +
|
| + // ReportingPersister implementation:
|
| +
|
| + ~ReportingPersisterImpl() override {
|
| + DCHECK(context_->initialized());
|
| + context_->RemoveObserver(this);
|
| + }
|
| +
|
| + void Initialize() override {
|
| + std::unique_ptr<const base::Value> persisted_data =
|
| + context_->delegate()->GetPersistedData();
|
| + if (persisted_data)
|
| + Deserialize(*persisted_data);
|
| + context_->AddObserver(this);
|
| + }
|
| +
|
| + void SetTimerForTesting(std::unique_ptr<base::Timer> timer) override {
|
| + DCHECK(!context_->initialized());
|
| + timer_ = std::move(timer);
|
| + }
|
| +
|
| + // ReportingObserver implementation:
|
| +
|
| + void OnCacheUpdated() override {
|
| + DCHECK(context_->initialized());
|
| + if (!timer_->IsRunning())
|
| + StartTimer();
|
| + }
|
| +
|
| + private:
|
| + void StartTimer() {
|
| + timer_->Start(
|
| + FROM_HERE, context_->policy().persistence_interval,
|
| + base::Bind(&ReportingPersisterImpl::Persist, base::Unretained(this)));
|
| + }
|
| +
|
| + void Persist() { delegate()->PersistData(Serialize()); }
|
| +
|
| + std::string SerializeTicks(base::TimeTicks time_ticks) {
|
| + base::Time time = time_ticks - tick_clock()->NowTicks() + clock()->Now();
|
| + return base::Int64ToString(time.ToInternalValue());
|
| + }
|
| +
|
| + bool DeserializeTicks(const std::string& serialized,
|
| + base::TimeTicks* time_ticks_out) {
|
| + int64_t internal;
|
| + if (!base::StringToInt64(serialized, &internal))
|
| + return false;
|
| +
|
| + base::Time time = base::Time::FromInternalValue(internal);
|
| + *time_ticks_out = time - clock()->Now() + tick_clock()->NowTicks();
|
| + return true;
|
| + }
|
| +
|
| + std::unique_ptr<base::Value> SerializeReport(const ReportingReport& report) {
|
| + auto serialized = base::MakeUnique<base::DictionaryValue>();
|
| +
|
| + serialized->SetString("url", report.url.spec());
|
| + serialized->SetString("group", report.group);
|
| + serialized->SetString("type", report.type);
|
| + serialized->Set("body", report.body->CreateDeepCopy());
|
| + serialized->SetString("queued", SerializeTicks(report.queued));
|
| + serialized->SetInteger("attempts", report.attempts);
|
| +
|
| + return std::move(serialized);
|
| + }
|
| +
|
| + bool DeserializeReport(const base::DictionaryValue& report) {
|
| + std::string url_string;
|
| + if (!report.GetString("url", &url_string))
|
| + return false;
|
| + GURL url(url_string);
|
| + if (!url.is_valid())
|
| + return false;
|
| +
|
| + std::string group;
|
| + if (!report.GetString("group", &group))
|
| + return false;
|
| +
|
| + std::string type;
|
| + if (!report.GetString("type", &type))
|
| + return false;
|
| +
|
| + const base::Value* body_original;
|
| + if (!report.Get("body", &body_original))
|
| + return false;
|
| + std::unique_ptr<base::Value> body = body_original->CreateDeepCopy();
|
| +
|
| + std::string queued_string;
|
| + if (!report.GetString("queued", &queued_string))
|
| + return false;
|
| + base::TimeTicks queued;
|
| + if (!DeserializeTicks(queued_string, &queued))
|
| + return false;
|
| +
|
| + int attempts;
|
| + if (!report.GetInteger("attempts", &attempts))
|
| + return false;
|
| + if (attempts < 0)
|
| + return false;
|
| +
|
| + cache()->AddReport(url, group, type, std::move(body), queued, attempts);
|
| + return true;
|
| + }
|
| +
|
| + std::unique_ptr<base::Value> SerializeReports() {
|
| + std::vector<const ReportingReport*> reports;
|
| + cache()->GetReports(&reports);
|
| +
|
| + auto serialized = base::MakeUnique<base::ListValue>();
|
| + for (const ReportingReport* report : reports)
|
| + serialized->Append(SerializeReport(*report));
|
| +
|
| + return std::move(serialized);
|
| + }
|
| +
|
| + bool DeserializeReports(const base::ListValue& reports) {
|
| + for (size_t i = 0; i < reports.GetSize(); ++i) {
|
| + const base::DictionaryValue* report;
|
| + if (!reports.GetDictionary(i, &report))
|
| + return false;
|
| + if (!DeserializeReport(*report))
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| + }
|
| +
|
| + std::unique_ptr<base::Value> SerializeClient(const ReportingClient& client) {
|
| + auto serialized = base::MakeUnique<base::DictionaryValue>();
|
| +
|
| + serialized->Set("origin", SerializeOrigin(client.origin));
|
| + serialized->SetString("endpoint", client.endpoint.spec());
|
| + serialized->SetBoolean(
|
| + "subdomains",
|
| + client.subdomains == ReportingClient::Subdomains::INCLUDE);
|
| + serialized->SetString("group", client.group);
|
| + serialized->SetString("expires", SerializeTicks(client.expires));
|
| +
|
| + return std::move(serialized);
|
| + }
|
| +
|
| + bool DeserializeClient(const base::DictionaryValue& client) {
|
| + const base::DictionaryValue* origin_value;
|
| + if (!client.GetDictionary("origin", &origin_value))
|
| + return false;
|
| + url::Origin origin;
|
| + if (!DeserializeOrigin(*origin_value, &origin))
|
| + return false;
|
| +
|
| + std::string endpoint_string;
|
| + if (!client.GetString("endpoint", &endpoint_string))
|
| + return false;
|
| + GURL endpoint(endpoint_string);
|
| + if (!endpoint.is_valid())
|
| + return false;
|
| +
|
| + bool subdomains_bool;
|
| + if (!client.GetBoolean("subdomains", &subdomains_bool))
|
| + return false;
|
| + ReportingClient::Subdomains subdomains =
|
| + subdomains_bool ? ReportingClient::Subdomains::INCLUDE
|
| + : ReportingClient::Subdomains::EXCLUDE;
|
| +
|
| + std::string group;
|
| + if (!client.GetString("group", &group))
|
| + return false;
|
| +
|
| + std::string expires_string;
|
| + if (!client.GetString("expires", &expires_string))
|
| + return false;
|
| + base::TimeTicks expires;
|
| + if (!DeserializeTicks(expires_string, &expires))
|
| + return false;
|
| +
|
| + cache()->SetClient(origin, endpoint, subdomains, group, expires);
|
| + return true;
|
| + }
|
| +
|
| + std::unique_ptr<base::Value> SerializeClients() {
|
| + std::vector<const ReportingClient*> clients;
|
| + cache()->GetClients(&clients);
|
| +
|
| + auto serialized = base::MakeUnique<base::ListValue>();
|
| + for (const ReportingClient* client : clients)
|
| + serialized->Append(SerializeClient(*client));
|
| +
|
| + return std::move(serialized);
|
| + }
|
| +
|
| + bool DeserializeClients(const base::ListValue& clients) {
|
| + for (size_t i = 0; i < clients.GetSize(); ++i) {
|
| + const base::DictionaryValue* client;
|
| + if (!clients.GetDictionary(i, &client))
|
| + return false;
|
| + if (!DeserializeClient(*client))
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| + }
|
| +
|
| + static const int kSupportedVersion = 1;
|
| +
|
| + std::unique_ptr<base::Value> Serialize() {
|
| + auto serialized = base::MakeUnique<base::DictionaryValue>();
|
| +
|
| + serialized->SetInteger("reporting_serialized_cache_version",
|
| + kSupportedVersion);
|
| +
|
| + bool persist_reports = policy().persist_reports_across_restarts;
|
| + serialized->SetBoolean("includes_reports", persist_reports);
|
| + if (persist_reports)
|
| + serialized->Set("reports", SerializeReports());
|
| +
|
| + bool persist_clients = policy().persist_clients_across_restarts;
|
| + serialized->SetBoolean("includes_clients", persist_clients);
|
| + if (persist_clients)
|
| + serialized->Set("clients", SerializeClients());
|
| +
|
| + return std::move(serialized);
|
| + }
|
| +
|
| + bool Deserialize(const base::Value& serialized_value) {
|
| + std::vector<const ReportingReport*> reports;
|
| + cache()->GetReports(&reports);
|
| + DCHECK(reports.empty());
|
| +
|
| + std::vector<const ReportingClient*> clients;
|
| + cache()->GetClients(&clients);
|
| + DCHECK(clients.empty());
|
| +
|
| + int version;
|
| +
|
| + const base::DictionaryValue* serialized;
|
| + if (!serialized_value.GetAsDictionary(&serialized))
|
| + return false;
|
| +
|
| + if (!serialized->GetInteger("reporting_serialized_cache_version", &version))
|
| + return false;
|
| + if (version != kSupportedVersion)
|
| + return false;
|
| +
|
| + bool includes_reports;
|
| + bool includes_clients;
|
| + if (!serialized->GetBoolean("includes_reports", &includes_reports) ||
|
| + !serialized->GetBoolean("includes_clients", &includes_clients)) {
|
| + return false;
|
| + }
|
| +
|
| + if (includes_reports) {
|
| + const base::ListValue* reports;
|
| + if (!serialized->GetList("reports", &reports))
|
| + return false;
|
| + if (!DeserializeReports(*reports))
|
| + return false;
|
| + }
|
| +
|
| + if (includes_clients) {
|
| + const base::ListValue* clients;
|
| + if (!serialized->GetList("clients", &clients))
|
| + return false;
|
| + if (!DeserializeClients(*clients))
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| + }
|
| +
|
| + const ReportingPolicy& policy() { return context_->policy(); }
|
| + ReportingDelegate* delegate() { return context_->delegate(); }
|
| + base::Clock* clock() { return context_->clock(); }
|
| + base::TickClock* tick_clock() { return context_->tick_clock(); }
|
| + ReportingCache* cache() { return context_->cache(); }
|
| +
|
| + ReportingContext* context_;
|
| + std::unique_ptr<base::Timer> timer_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +// static
|
| +std::unique_ptr<ReportingPersister> ReportingPersister::Create(
|
| + ReportingContext* context) {
|
| + return base::MakeUnique<ReportingPersisterImpl>(context);
|
| +}
|
| +
|
| +ReportingPersister::~ReportingPersister() {}
|
| +
|
| +} // namespace net
|
|
|