Chromium Code Reviews| 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..ae0e1a8e86761f0e399e6be319ab448b6f0f2509 |
| --- /dev/null |
| +++ b/net/reporting/reporting_persister.cc |
| @@ -0,0 +1,355 @@ |
| +// 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>()) { |
| + context_->AddObserver(this); |
| + } |
| + |
| + // ReportingPersister implementation: |
| + |
| + ~ReportingPersisterImpl() override { context_->RemoveObserver(this); } |
| + |
| + void SetTimerForTesting(std::unique_ptr<base::Timer> timer) override { |
| + DCHECK(!timer_->IsRunning()); |
| + timer_ = std::move(timer); |
| + } |
| + |
| + // ReportingObserver implementation: |
| + |
| + void OnContextInitializing() override { |
| + std::unique_ptr<const base::Value> persisted_data = |
| + context_->delegate()->GetPersistedData(); |
| + if (persisted_data) |
| + Deserialize(*persisted_data); |
| + } |
| + |
| + void OnCacheUpdated() override { |
|
shivanisha
2017/04/10 18:29:29
dcheck that context is initialized. Also in Report
Julia Tuttle
2017/04/11 19:20:46
Done.
|
| + 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 |