blob: 83283da29f907db375d94e524ef7691af9701ee5 [file] [log] [blame]
// Copyright 2019 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 NET_REPORTING_REPORTING_CACHE_IMPL_H_
#define NET_REPORTING_REPORTING_CACHE_IMPL_H_
#include <map>
#include <memory>
#include <set>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include "base/containers/flat_set.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/macros.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/values.h"
#include "net/reporting/reporting_cache.h"
#include "net/reporting/reporting_context.h"
#include "net/reporting/reporting_endpoint.h"
#include "net/reporting/reporting_header_parser.h"
#include "net/reporting/reporting_report.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace net {
class ReportingCacheImpl : public ReportingCache {
public:
ReportingCacheImpl(ReportingContext* context);
~ReportingCacheImpl() override;
// ReportingCache implementation
void AddReport(const NetworkIsolationKey& network_isolation_key,
const GURL& url,
const std::string& user_agent,
const std::string& group_name,
const std::string& type,
std::unique_ptr<const base::Value> body,
int depth,
base::TimeTicks queued,
int attempts) override;
void GetReports(
std::vector<const ReportingReport*>* reports_out) const override;
base::Value GetReportsAsValue() const override;
std::vector<const ReportingReport*> GetReportsToDeliver() override;
void ClearReportsPending(
const std::vector<const ReportingReport*>& reports) override;
void IncrementReportsAttempts(
const std::vector<const ReportingReport*>& reports) override;
void IncrementEndpointDeliveries(const ReportingEndpointGroupKey& group_key,
const GURL& url,
int reports_delivered,
bool successful) override;
void RemoveReports(
const std::vector<const ReportingReport*>& reports) override;
void RemoveAllReports() override;
size_t GetFullReportCountForTesting() const override;
bool IsReportPendingForTesting(const ReportingReport* report) const override;
bool IsReportDoomedForTesting(const ReportingReport* report) const override;
void OnParsedHeader(
const NetworkIsolationKey& network_isolation_key,
const url::Origin& origin,
std::vector<ReportingEndpointGroup> parsed_header) override;
std::set<url::Origin> GetAllOrigins() const override;
void RemoveClient(const NetworkIsolationKey& network_isolation_key,
const url::Origin& origin) override;
void RemoveClientsForOrigin(const url::Origin& origin) override;
void RemoveAllClients() override;
void RemoveEndpointGroup(const ReportingEndpointGroupKey& group_key) override;
void RemoveEndpointsForUrl(const GURL& url) override;
void AddClientsLoadedFromStore(
std::vector<ReportingEndpoint> loaded_endpoints,
std::vector<CachedReportingEndpointGroup> loaded_endpoint_groups)
override;
std::vector<ReportingEndpoint> GetCandidateEndpointsForDelivery(
const ReportingEndpointGroupKey& group_key) override;
base::Value GetClientsAsValue() const override;
size_t GetEndpointCount() const override;
void Flush() override;
ReportingEndpoint GetEndpointForTesting(
const ReportingEndpointGroupKey& group_key,
const GURL& url) const override;
bool EndpointGroupExistsForTesting(const ReportingEndpointGroupKey& group_key,
OriginSubdomains include_subdomains,
base::Time expires) const override;
bool ClientExistsForTesting(const NetworkIsolationKey& network_isolation_key,
const url::Origin& origin) const override;
size_t GetEndpointGroupCountForTesting() const override;
size_t GetClientCountForTesting() const override;
void SetEndpointForTesting(const ReportingEndpointGroupKey& group_key,
const GURL& url,
OriginSubdomains include_subdomains,
base::Time expires,
int priority,
int weight) override;
private:
// Represents the entire Report-To configuration for a (NIK, origin) pair.
struct Client {
Client(const NetworkIsolationKey& network_isolation_key,
const url::Origin& origin);
Client(const Client& other);
Client(Client&& other);
Client& operator=(const Client& other);
Client& operator=(Client&& other);
~Client();
// NIK of the context associated with this client. Needed to prevent leaking
// third party contexts across sites.
NetworkIsolationKey network_isolation_key;
// Origin that configured this client.
url::Origin origin;
// Total number of endpoints for this origin. Should stay in sync with the
// sum of endpoint counts for all the groups within this client.
size_t endpoint_count = 0;
// Last time that any of the groups for this origin was accessed for a
// delivery or updated via a new header. Should stay in sync with the latest
// |last_used| of all the groups within this client.
base::Time last_used;
// Set of endpoint group names for this origin.
std::set<std::string> endpoint_group_names;
};
using ReportSet = base::flat_set<std::unique_ptr<ReportingReport>,
base::UniquePtrComparator>;
using ClientMap = std::multimap<std::string, Client>;
using EndpointGroupMap =
std::map<ReportingEndpointGroupKey, CachedReportingEndpointGroup>;
using EndpointMap =
std::multimap<ReportingEndpointGroupKey, ReportingEndpoint>;
ReportSet::const_iterator FindReportToEvict() const;
// Consistency-checks the entire data structure of clients, groups, and
// endpoints, if DCHECK is on. The cached clients should pass this consistency
// check after completely parsing a header (i.e. not after the intermediate
// steps), and before and after any of the public methods that remove or
// retrieve client info. Also calls |sequence_checker_| to DCHECK that we are
// being called on a valid sequence.
void ConsistencyCheckClients() const;
// Helper methods for ConsistencyCheckClients():
#if DCHECK_IS_ON()
// Returns number of endpoint groups found in |client|.
size_t ConsistencyCheckClient(const std::string& domain,
const Client& client) const;
// Returns the number of endpoints found in |group|.
size_t ConsistencyCheckEndpointGroup(
const ReportingEndpointGroupKey& key,
const CachedReportingEndpointGroup& group) const;
void ConsistencyCheckEndpoint(const ReportingEndpointGroupKey& key,
const ReportingEndpoint& endpoint,
EndpointMap::const_iterator endpoint_it) const;
#endif // DCHECK_IS_ON()
// Finds iterator to the client with the given |network_isolation_key| and
// |origin|, if one exists. Returns |clients_.end()| if none is found.
ClientMap::iterator FindClientIt(
const NetworkIsolationKey& network_isolation_key,
const url::Origin& origin);
// Overload that takes a ReportingEndpointGroupKey and finds the client
// to which a group specified by the |group_key| would belong. The group name
// of the key is ignored.
ClientMap::iterator FindClientIt(const ReportingEndpointGroupKey& group_key);
// Finds iterator to the endpoint group identified by |group_key| (origin and
// name), if one exists. Returns |endpoint_groups_.end()| if none is found.
EndpointGroupMap::iterator FindEndpointGroupIt(
const ReportingEndpointGroupKey& group_key);
// Finds iterator to the endpoint for the given |group_key| (origin and group
// name) and |url|, if one exists. Returns |endpoints_.end()| if none is
// found.
EndpointMap::iterator FindEndpointIt(
const ReportingEndpointGroupKey& group_key,
const GURL& url);
// Adds a new client, endpoint group, or endpoint to the cache, if none
// exists. If one already exists, updates the existing entry to match the new
// one. Returns iterator to newly added client.
ClientMap::iterator AddOrUpdateClient(Client new_client);
void AddOrUpdateEndpointGroup(CachedReportingEndpointGroup new_group);
void AddOrUpdateEndpoint(ReportingEndpoint new_endpoint);
// Remove all the endpoints configured for |origin| and |group| whose urls are
// not in |endpoints_to_keep_urls|. Does not guarantee that all the endpoints
// in |endpoints_to_keep_urls| exist in the cache for that group.
void RemoveEndpointsInGroupOtherThan(
const ReportingEndpointGroupKey& group_key,
const std::set<GURL>& endpoints_to_keep_urls);
// Remove all the endpoint groups for the NIK and origin whose names are not
// in |groups_to_keep_names|. Does not guarantee that all the groups in
// |groups_to_keep_names| exist in the cache for that client.
void RemoveEndpointGroupsForClientOtherThan(
const NetworkIsolationKey& network_isolation_key,
const url::Origin& origin,
const std::set<std::string>& groups_to_keep_names);
// Gets the endpoints in the given group.
std::vector<ReportingEndpoint> GetEndpointsInGroup(
const ReportingEndpointGroupKey& group_key) const;
// Gets the number of endpoints for the given origin and group.
size_t GetEndpointCountInGroup(
const ReportingEndpointGroupKey& group_key) const;
// Updates the last_used time for the given origin and endpoint group.
void MarkEndpointGroupAndClientUsed(ClientMap::iterator client_it,
EndpointGroupMap::iterator group_it,
base::Time now);
// Removes the endpoint at the given iterator, which must exist in the cache.
// Also takes iterators to the client and endpoint group to avoid repeated
// lookups. May cause the client and/or group to be removed if they become
// empty, which would invalidate those iterators.
// Returns the iterator following the endpoint removed, or absl::nullopt if
// either of |group_it| or |client_it| were invalidated. (If |client_it| is
// invalidated, then so must |group_it|).
absl::optional<EndpointMap::iterator> RemoveEndpointInternal(
ClientMap::iterator client_it,
EndpointGroupMap::iterator group_it,
EndpointMap::iterator endpoint_it);
// Removes the endpoint group at the given iterator (which must exist in the
// cache). Also takes iterator to the client to avoid repeated lookups. May
// cause the client to be removed if it becomes empty, which would
// invalidate |client_it|. If |num_endpoints_removed| is not null, then
// |*num_endpoints_removed| is incremented by the number of endpoints
// removed.
// Returns the iterator following the endpoint group removed, or absl::nullopt
// if |client_it| was invalidated.
absl::optional<EndpointGroupMap::iterator> RemoveEndpointGroupInternal(
ClientMap::iterator client_it,
EndpointGroupMap::iterator group_it,
size_t* num_endpoints_removed = nullptr);
// Removes the client at the given iterator (which must exist in the cache),
// along with all of its endpoint groups and endpoints. Invalidates
// |client_it|.
// Returns the iterator following the client removed.
ClientMap::iterator RemoveClientInternal(ClientMap::iterator client_it);
// Evict endpoints from the specified client and globally, if necessary to
// obey the per-client and global endpoint limits set in the ReportingPolicy.
//
// To evict from a client: First evicts any stale or expired groups for that
// origin. If that removes enough endpoints, then stop. Otherwise, find the
// stalest group (which has not been accessed for a delivery in the longest
// time) with the most endpoints, and evict the least important endpoints from
// that group.
// To evict globally: Find the stalest client with the most endpoints and do
// the above.
void EnforcePerClientAndGlobalEndpointLimits(ClientMap::iterator client_it);
// Evicts endpoints from a client until it has evicted |endpoints_to_evict|
// endpoints. First tries to remove expired and stale groups. If that fails to
// satisfy the limit, finds the stalest group with the most endpoints and
// evicts the least important endpoints from it.
void EvictEndpointsFromClient(ClientMap::iterator client_it,
size_t endpoints_to_evict);
// Evicts the least important endpoint from a group (the endpoint with lowest
// priority and lowest weight). May cause the group and/or client to be
// deleted and the iterators invalidated.
void EvictEndpointFromGroup(ClientMap::iterator client_it,
EndpointGroupMap::iterator group_it);
// Removes all expired or stale groups from the given client. May delete the
// client and invalidate |client_it| if it becomes empty.
// Increments |*num_endpoints_removed| by the number of endpoints removed.
// Returns true if |client_it| was invalidated.
bool RemoveExpiredOrStaleGroups(ClientMap::iterator client_it,
size_t* num_endpoints_removed);
// Adds/removes (if it exists) |endpoint_it| from |endpoint_its_by_url_|.
void AddEndpointItToIndex(EndpointMap::iterator endpoint_it);
void RemoveEndpointItFromIndex(EndpointMap::iterator endpoint_it);
// Helper methods for GetClientsAsValue().
base::Value GetClientAsValue(const Client& client) const;
base::Value GetEndpointGroupAsValue(
const CachedReportingEndpointGroup& group) const;
base::Value GetEndpointAsValue(const ReportingEndpoint& endpoint) const;
// Convenience methods for fetching things from the context_.
const base::Clock& clock() const { return context_->clock(); }
const base::TickClock& tick_clock() const { return context_->tick_clock(); }
PersistentReportingStore* store() { return context_->store(); }
ReportingContext* context_;
// Reports that have not yet been successfully uploaded.
ReportSet reports_;
// Map of clients for all configured origins and NIKs, keyed on domain name
// (there may be multiple NIKs and origins per domain name).
ClientMap clients_;
// Map of endpoint groups, keyed on origin and group name.
EndpointGroupMap endpoint_groups_;
// Map of endpoints, keyed on origin and group name (there may be multiple
// endpoints for a given origin and group, with different urls).
EndpointMap endpoints_;
// Index of endpoints stored in |endpoints_| keyed on URL, for easier lookup
// during RemoveEndpointsForUrl(). Should stay in sync with |endpoints_|.
std::multimap<GURL, EndpointMap::iterator> endpoint_its_by_url_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(ReportingCacheImpl);
};
} // namespace net
#endif // NET_REPORTING_REPORTING_CACHE_IMPL_H_