blob: 92cdfbdd856d6c9389885bffec56e31dc7ac747d [file] [log] [blame]
// 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 <utility>
#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_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:
ReportingPersisterImpl(ReportingContext* context) : context_(context) {}
// ReportingPersister implementation:
~ReportingPersisterImpl() override {}
private:
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(); }
base::Clock* clock() { return context_->clock(); }
base::TickClock* tick_clock() { return context_->tick_clock(); }
ReportingCache* cache() { return context_->cache(); }
ReportingContext* context_;
};
} // namespace
// static
std::unique_ptr<ReportingPersister> ReportingPersister::Create(
ReportingContext* context) {
return base::MakeUnique<ReportingPersisterImpl>(context);
}
ReportingPersister::~ReportingPersister() {}
} // namespace net