blob: 52a20c7501cdc040239c5295ddf3d94269295877 [file] [log] [blame]
// Copyright 2016 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 "base/time/clock.h"
#include "base/time/default_clock.h"
#include "chrome/browser/safe_browsing/certificate_reporting_service.h"
#include "content/public/browser/browser_thread.h"
namespace {
// Compare function that orders Reports in reverse chronological order (i.e.
// oldest item is last).
bool ReportCompareFunc(const CertificateReportingService::Report& item1,
const CertificateReportingService::Report& item2) {
return item1.creation_time > item2.creation_time;
}
} // namespace
CertificateReportingService::BoundedReportList::BoundedReportList(
size_t max_size)
: max_size_(max_size) {
CHECK(max_size <= 20)
<< "Current implementation is not efficient for a large list.";
DCHECK(thread_checker_.CalledOnValidThread());
}
CertificateReportingService::BoundedReportList::~BoundedReportList() {}
void CertificateReportingService::BoundedReportList::Add(const Report& item) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(items_.size() <= max_size_);
if (items_.size() == max_size_) {
const Report& last = items_.back();
if (item.creation_time <= last.creation_time) {
// Report older than the oldest item in the queue, ignore.
return;
}
// Reached the maximum item count, remove the oldest item.
items_.pop_back();
}
items_.push_back(item);
std::sort(items_.begin(), items_.end(), ReportCompareFunc);
}
void CertificateReportingService::BoundedReportList::Clear() {
items_.clear();
}
const std::vector<CertificateReportingService::Report>&
CertificateReportingService::BoundedReportList::items() const {
DCHECK(thread_checker_.CalledOnValidThread());
return items_;
}
CertificateReportingService::Reporter::Reporter(
std::unique_ptr<certificate_reporting::ErrorReporter> error_reporter,
std::unique_ptr<BoundedReportList> retry_list,
base::Clock* clock,
base::TimeDelta report_ttl)
: error_reporter_(std::move(error_reporter)),
retry_list_(std::move(retry_list)),
test_clock_(clock),
report_ttl_(report_ttl),
current_report_id_(0),
weak_factory_(this) {}
CertificateReportingService::Reporter::~Reporter() {}
void CertificateReportingService::Reporter::Send(
const std::string& serialized_report) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
base::Time now =
test_clock_ ? test_clock_->Now() : base::Time::NowFromSystemTime();
SendInternal(Report(current_report_id_++, now, serialized_report));
}
void CertificateReportingService::Reporter::SendPending() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
base::Time now =
test_clock_ ? test_clock_->Now() : base::Time::NowFromSystemTime();
// Copy pending reports and clear the retry list.
std::vector<Report> items = retry_list_->items();
retry_list_->Clear();
for (const Report& report : items) {
if (report.creation_time < now - report_ttl_) {
// Report too old, ignore.
continue;
}
SendInternal(report);
}
}
size_t
CertificateReportingService::Reporter::inflight_report_count_for_testing()
const {
return inflight_reports_.size();
}
CertificateReportingService::BoundedReportList*
CertificateReportingService::Reporter::GetQueueForTesting() const {
return retry_list_.get();
}
void CertificateReportingService::Reporter::SendInternal(
const CertificateReportingService::Report& report) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
inflight_reports_.insert(std::make_pair(report.report_id, report));
error_reporter_->SendExtendedReportingReport(
report.serialized_report,
base::Bind(&CertificateReportingService::Reporter::SuccessCallback,
weak_factory_.GetWeakPtr(), report.report_id),
base::Bind(&CertificateReportingService::Reporter::ErrorCallback,
weak_factory_.GetWeakPtr(), report.report_id));
}
void CertificateReportingService::Reporter::ErrorCallback(int report_id,
const GURL& url,
int error) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
auto it = inflight_reports_.find(report_id);
DCHECK(it != inflight_reports_.end());
retry_list_->Add(it->second);
CHECK_GT(inflight_reports_.erase(report_id), 0u);
}
void CertificateReportingService::Reporter::SuccessCallback(int report_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
CHECK_GT(inflight_reports_.erase(report_id), 0u);
}