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 |