blob: dba025525baa29918b4e612bcfa24e41d0d5162c [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.
#ifndef CHROME_BROWSER_SAFE_BROWSING_CERTIFICATE_REPORTING_SERVICE_H_
#define CHROME_BROWSER_SAFE_BROWSING_CERTIFICATE_REPORTING_SERVICE_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/callback_list.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "chrome/browser/ssl/certificate_error_reporter.h"
#include "components/keyed_service/core/keyed_service.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
class PrefService;
class Profile;
namespace base {
class Clock;
}
namespace network {
class SharedURLLoaderFactory;
}
namespace safe_browsing {
class SafeBrowsingService;
}
// This service initiates uploads of invalid certificate reports and retries any
// failed uploads. Each report is retried until it's older than a certain time
// to live (TTL). Reports older than this TTL are dropped and no more retried,
// so that the retry list doesn't grow indefinitely.
//
// Lifetime and dependencies:
//
// CertificateReportingService uses the URLLoaderFactory from SafeBrowsing
// service. SafeBrowsingService is created before CertificateReportingService,
// but is also shut down before any KeyedService is shut down.
//
// This class observes SafeBrowsing preference changes to enable/disable
// reporting. It does this by subscribing to changes in SafeBrowsing and
// extended reporting preferences.
class CertificateReportingService : public KeyedService {
public:
// Events for UMA. Do not rename or remove values, add new values to the end.
// Public for testing.
enum class ReportOutcome {
// A report is submitted. This includes failed and successful uploads as
// well as uploads that never return a response.
SUBMITTED = 0,
// A report submission failed, either because of a net error or a non-HTTP
// 200 response from the server.
FAILED = 1,
// A report submission was successfully sent, receiving an HTTP 200 response
// from the server.
SUCCESSFUL = 2,
// A report was dropped from the reporting queue because it was older
// than report TTL, or it was ignored because the queue was full and the
// report was older than the oldest report in the queue. Does not include
// reports that were cleared because of a SafeBrowsing preference change.
DROPPED_OR_IGNORED = 3,
EVENT_COUNT = 4
};
// Represents a report to be sent.
struct Report {
int report_id;
base::Time creation_time;
std::string serialized_report;
bool is_retried;
Report(int report_id,
base::Time creation_time,
const std::string& serialized_report)
: report_id(report_id),
creation_time(creation_time),
serialized_report(serialized_report),
is_retried(false) {}
};
// This class contains a number of reports, sorted by the first time the
// report was to be sent. Oldest reports are at the end of the list. The
// number of reports are bounded by |max_size|. The implementation sorts all
// items in the list whenever a new item is added. This should be fine for
// small values of |max_size| (e.g. fewer than 100 items). In case this is not
// sufficient in the future, an array based implementation should be
// considered where the array is maintained as a heap.
class BoundedReportList {
public:
explicit BoundedReportList(size_t max_size);
~BoundedReportList();
void Add(const Report& report);
void Clear();
const std::vector<Report>& items() const;
private:
// Maximum number of reports in the list. If the number of reports in the
// list is smaller than this number, a new item is immediately added to the
// list. Otherwise, the item is compared to the items in the list and only
// added when it's newer than the oldest item in the list.
const size_t max_size_;
std::vector<Report> items_;
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(BoundedReportList);
};
// Class that handles report uploads and implements the upload retry logic.
class Reporter {
public:
Reporter(std::unique_ptr<CertificateErrorReporter> error_reporter_,
std::unique_ptr<BoundedReportList> retry_list,
base::Clock* const clock,
base::TimeDelta report_ttl,
bool retries_enabled);
~Reporter();
// Sends a report. If the send fails, the report will be added to the retry
// list.
void Send(const std::string& serialized_report);
// Sends all pending reports. Skips reports older than the |report_ttl|
// provided in the constructor. Failed reports will be added to the retry
// list.
void SendPending();
// Getter and setters for testing:
size_t inflight_report_count_for_testing() const;
BoundedReportList* GetQueueForTesting() const;
// Sets a closure that is called when there are no more inflight reports.
void SetClosureWhenNoInflightReportsForTesting(
const base::Closure& closure);
private:
void SendInternal(const Report& report);
// Called when a report upload fails either because of a net error or a
// non-HTTP 200 response code. See
// TransportSecurityState::ReportSenderInterface for parameters.
void ErrorCallback(int report_id,
int net_error,
int http_response_code);
// Called when a report upload is successful.
void SuccessCallback(int report_id);
std::unique_ptr<CertificateErrorReporter> error_reporter_;
std::unique_ptr<BoundedReportList> retry_list_;
base::Clock* const clock_;
// Maximum age of a queued report. Reports older than this are discarded in
// the next SendPending() call.
const base::TimeDelta report_ttl_;
const bool retries_enabled_;
// Current report id, starting from zero and monotonically incrementing.
int current_report_id_;
std::map<int, Report> inflight_reports_;
base::Closure no_in_flight_reports_;
base::WeakPtrFactory<Reporter> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Reporter);
};
// Public for testing.
static const char kReportEventHistogram[];
CertificateReportingService(
safe_browsing::SafeBrowsingService* safe_browsing_service,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
Profile* profile,
uint8_t server_public_key[/* 32 */],
uint32_t server_public_key_version,
size_t max_queued_report_count,
base::TimeDelta max_report_age,
base::Clock* clock,
const base::Callback<void()>& reset_callback);
~CertificateReportingService() override;
// KeyedService implementation:
void Shutdown() override;
// Sends a serialized report. If the report upload fails, the upload is
// retried at a future time.
void Send(const std::string& serialized_report);
// Sends pending reports that are in the retry queue.
void SendPending();
// Enables or disables reporting. When disabled, pending report queue is
// cleared and incoming reports are ignored. Reporting is enabled by default
// once the service is initialized.
void SetEnabled(bool enabled);
// Getters for testing.
Reporter* GetReporterForTesting() const;
static GURL GetReportingURLForTesting();
private:
// Resets the reporter. Changes in SafeBrowsing or extended reporting enabled
// states cause the reporter to be reset. If |enabled| is false, report is set
// to null, effectively cancelling all in flight uploads and clearing the
// pending reports queue.
void Reset(bool enabled);
void OnPreferenceChanged();
const PrefService& pref_service_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
std::unique_ptr<Reporter> reporter_;
// Subscription for state changes. When this subscription is notified, it
// means SafeBrowsingService is enabled/disabled or one of the preferences
// related to it is changed.
std::unique_ptr<base::CallbackList<void(void)>::Subscription>
safe_browsing_state_subscription_;
// Maximum number of reports to be queued for retry.
const size_t max_queued_report_count_;
// Maximum age of the reports to be queued for retry, from the time the
// certificate error was first encountered by the user. Any report older than
// this age is ignored and is not re-uploaded.
const base::TimeDelta max_report_age_;
base::Clock* const clock_;
// Called when the service is reset. Used for testing.
base::Callback<void()> reset_callback_;
// Encryption parameters.
uint8_t* server_public_key_;
uint32_t server_public_key_version_;
DISALLOW_COPY_AND_ASSIGN(CertificateReportingService);
};
#endif // CHROME_BROWSER_SAFE_BROWSING_CERTIFICATE_REPORTING_SERVICE_H_