1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef NET_REPORTING_REPORTING_CACHE_IMPL_H_
6 #define NET_REPORTING_REPORTING_CACHE_IMPL_H_
7 
8 #include <map>
9 #include <memory>
10 #include <set>
11 #include <string>
12 #include <unordered_map>
13 #include <unordered_set>
14 #include <utility>
15 #include <vector>
16 
17 #include "base/containers/flat_set.h"
18 #include "base/containers/unique_ptr_adapters.h"
19 #include "base/macros.h"
20 #include "base/optional.h"
21 #include "base/sequence_checker.h"
22 #include "base/time/time.h"
23 #include "base/values.h"
24 #include "net/reporting/reporting_cache.h"
25 #include "net/reporting/reporting_context.h"
26 #include "net/reporting/reporting_endpoint.h"
27 #include "net/reporting/reporting_header_parser.h"
28 #include "net/reporting/reporting_report.h"
29 #include "url/gurl.h"
30 #include "url/origin.h"
31 
32 namespace net {
33 
34 class ReportingCacheImpl : public ReportingCache {
35  public:
36   ReportingCacheImpl(ReportingContext* context);
37 
38   ~ReportingCacheImpl() override;
39 
40   // ReportingCache implementation
41   void AddReport(const GURL& url,
42                  const std::string& user_agent,
43                  const std::string& group_name,
44                  const std::string& type,
45                  std::unique_ptr<const base::Value> body,
46                  int depth,
47                  base::TimeTicks queued,
48                  int attempts) override;
49   void GetReports(
50       std::vector<const ReportingReport*>* reports_out) const override;
51   base::Value GetReportsAsValue() const override;
52   std::vector<const ReportingReport*> GetReportsToDeliver() override;
53   void ClearReportsPending(
54       const std::vector<const ReportingReport*>& reports) override;
55   void IncrementReportsAttempts(
56       const std::vector<const ReportingReport*>& reports) override;
57   void IncrementEndpointDeliveries(const ReportingEndpointGroupKey& group_key,
58                                    const GURL& url,
59                                    int reports_delivered,
60                                    bool successful) override;
61   void RemoveReports(const std::vector<const ReportingReport*>& reports,
62                      ReportingReport::Outcome outcome) override;
63   void RemoveAllReports(ReportingReport::Outcome outcome) override;
64   size_t GetFullReportCountForTesting() const override;
65   bool IsReportPendingForTesting(const ReportingReport* report) const override;
66   bool IsReportDoomedForTesting(const ReportingReport* report) const override;
67   void OnParsedHeader(
68       const url::Origin& origin,
69       std::vector<ReportingEndpointGroup> parsed_header) override;
70   std::set<url::Origin> GetAllOrigins() const override;
71   void RemoveClient(const NetworkIsolationKey& network_isolation_key,
72                     const url::Origin& origin) override;
73   void RemoveClientsForOrigin(const url::Origin& origin) override;
74   void RemoveAllClients() override;
75   void RemoveEndpointGroup(const ReportingEndpointGroupKey& group_key) override;
76   void RemoveEndpointsForUrl(const GURL& url) override;
77   void AddClientsLoadedFromStore(
78       std::vector<ReportingEndpoint> loaded_endpoints,
79       std::vector<CachedReportingEndpointGroup> loaded_endpoint_groups)
80       override;
81   std::vector<ReportingEndpoint> GetCandidateEndpointsForDelivery(
82       const ReportingEndpointGroupKey& group_key) override;
83   base::Value GetClientsAsValue() const override;
84   size_t GetEndpointCount() const override;
85   void Flush() override;
86   ReportingEndpoint GetEndpointForTesting(
87       const ReportingEndpointGroupKey& group_key,
88       const GURL& url) const override;
89   bool EndpointGroupExistsForTesting(const ReportingEndpointGroupKey& group_key,
90                                      OriginSubdomains include_subdomains,
91                                      base::Time expires) const override;
92   bool ClientExistsForTesting(const NetworkIsolationKey& network_isolation_key,
93                               const url::Origin& origin) const override;
94   size_t GetEndpointGroupCountForTesting() const override;
95   size_t GetClientCountForTesting() const override;
96   void SetEndpointForTesting(const ReportingEndpointGroupKey& group_key,
97                              const GURL& url,
98                              OriginSubdomains include_subdomains,
99                              base::Time expires,
100                              int priority,
101                              int weight) override;
102 
103  private:
104   // Represents the entire Report-To configuration for a (NIK, origin) pair.
105   struct Client {
106     Client(const NetworkIsolationKey& network_isolation_key,
107            const url::Origin& origin);
108 
109     Client(const Client& other);
110     Client(Client&& other);
111 
112     Client& operator=(const Client& other);
113     Client& operator=(Client&& other);
114 
115     ~Client();
116 
117     // NIK of the context associated with this client. Needed to prevent leaking
118     // third party contexts across sites.
119     NetworkIsolationKey network_isolation_key;
120 
121     // Origin that configured this client.
122     url::Origin origin;
123 
124     // Total number of endpoints for this origin. Should stay in sync with the
125     // sum of endpoint counts for all the groups within this client.
126     size_t endpoint_count = 0;
127 
128     // Last time that any of the groups for this origin was accessed for a
129     // delivery or updated via a new header. Should stay in sync with the latest
130     // |last_used| of all the groups within this client.
131     base::Time last_used;
132 
133     // Set of endpoint group names for this origin.
134     std::set<std::string> endpoint_group_names;
135   };
136 
137   using ReportSet = base::flat_set<std::unique_ptr<ReportingReport>,
138                                    base::UniquePtrComparator>;
139   using ClientMap = std::multimap<std::string, Client>;
140   using EndpointGroupMap =
141       std::map<ReportingEndpointGroupKey, CachedReportingEndpointGroup>;
142   using EndpointMap =
143       std::multimap<ReportingEndpointGroupKey, ReportingEndpoint>;
144 
145   ReportSet::const_iterator FindReportToEvict() const;
146 
147   // Sanity-checks the entire data structure of clients, groups, and endpoints,
148   // if DCHECK is on. The cached clients should pass this sanity check after
149   // completely parsing a header (i.e. not after the intermediate steps), and
150   // before and after any of the public methods that remove or retrieve client
151   // info. Also calls |sequence_checker_| to DCHECK that we are being called on
152   // a valid sequence.
153   void SanityCheckClients() const;
154 
155   // Helper methods for SanityCheckClients():
156 #if DCHECK_IS_ON()
157   // Returns number of endpoint groups found in |client|.
158   size_t SanityCheckClient(const std::string& domain,
159                            const Client& client) const;
160 
161   // Returns the number of endpoints found in |group|.
162   size_t SanityCheckEndpointGroup(
163       const ReportingEndpointGroupKey& key,
164       const CachedReportingEndpointGroup& group) const;
165 
166   void SanityCheckEndpoint(const ReportingEndpointGroupKey& key,
167                            const ReportingEndpoint& endpoint,
168                            EndpointMap::const_iterator endpoint_it) const;
169 #endif  // DCHECK_IS_ON()
170 
171   // Finds iterator to the client with the given |network_isolation_key| and
172   // |origin|, if one exists. Returns |clients_.end()| if none is found.
173   ClientMap::iterator FindClientIt(
174       const NetworkIsolationKey& network_isolation_key,
175       const url::Origin& origin);
176 
177   // Overload that takes a ReportingEndpointGroupKey and finds the client
178   // to which a group specified by the |group_key| would belong. The group name
179   // of the key is ignored.
180   ClientMap::iterator FindClientIt(const ReportingEndpointGroupKey& group_key);
181 
182   // Finds iterator to the endpoint group identified by |group_key| (origin and
183   // name), if one exists. Returns |endpoint_groups_.end()| if none is found.
184   EndpointGroupMap::iterator FindEndpointGroupIt(
185       const ReportingEndpointGroupKey& group_key);
186 
187   // Finds iterator to the endpoint for the given |group_key| (origin and group
188   // name) and |url|, if one exists. Returns |endpoints_.end()| if none is
189   // found.
190   EndpointMap::iterator FindEndpointIt(
191       const ReportingEndpointGroupKey& group_key,
192       const GURL& url);
193 
194   // Adds a new client, endpoint group, or endpoint to the cache, if none
195   // exists. If one already exists, updates the existing entry to match the new
196   // one. Returns iterator to newly added client.
197   ClientMap::iterator AddOrUpdateClient(Client new_client);
198   void AddOrUpdateEndpointGroup(CachedReportingEndpointGroup new_group);
199   void AddOrUpdateEndpoint(ReportingEndpoint new_endpoint);
200 
201   // Remove all the endpoints configured for |origin| and |group| whose urls are
202   // not in |endpoints_to_keep_urls|. Does not guarantee that all the endpoints
203   // in |endpoints_to_keep_urls| exist in the cache for that group.
204   void RemoveEndpointsInGroupOtherThan(
205       const ReportingEndpointGroupKey& group_key,
206       const std::set<GURL>& endpoints_to_keep_urls);
207 
208   // Remove all the endpoint groups for the NIK and origin whose names are not
209   // in |groups_to_keep_names|. Does not guarantee that all the groups in
210   // |groups_to_keep_names| exist in the cache for that client.
211   void RemoveEndpointGroupsForClientOtherThan(
212       const NetworkIsolationKey& network_isolation_key,
213       const url::Origin& origin,
214       const std::set<std::string>& groups_to_keep_names);
215 
216   // Gets the endpoints in the given group.
217   std::vector<ReportingEndpoint> GetEndpointsInGroup(
218       const ReportingEndpointGroupKey& group_key) const;
219 
220   // Gets the number of endpoints for the given origin and group.
221   size_t GetEndpointCountInGroup(
222       const ReportingEndpointGroupKey& group_key) const;
223 
224   // Updates the last_used time for the given origin and endpoint group.
225   void MarkEndpointGroupAndClientUsed(ClientMap::iterator client_it,
226                                       EndpointGroupMap::iterator group_it,
227                                       base::Time now);
228 
229   // Removes the endpoint at the given iterator, which must exist in the cache.
230   // Also takes iterators to the client and endpoint group to avoid repeated
231   // lookups. May cause the client and/or group to be removed if they become
232   // empty, which would invalidate those iterators.
233   // Returns the iterator following the endpoint removed, or base::nullopt if
234   // either of |group_it| or |client_it| were invalidated. (If |client_it| is
235   // invalidated, then so must |group_it|).
236   base::Optional<EndpointMap::iterator> RemoveEndpointInternal(
237       ClientMap::iterator client_it,
238       EndpointGroupMap::iterator group_it,
239       EndpointMap::iterator endpoint_it);
240 
241   // Removes the endpoint group at the given iterator (which must exist in the
242   // cache). Also takes iterator to the client to avoid repeated lookups. May
243   // cause the client to be removed if it becomes empty, which would
244   // invalidate |client_it|. If |num_endpoints_removed| is not null, then
245   // |*num_endpoints_removed| is incremented by the number of endpoints
246   // removed.
247   // Returns the iterator following the endpoint group removed, or base::nullopt
248   // if |client_it| was invalidated.
249   base::Optional<EndpointGroupMap::iterator> RemoveEndpointGroupInternal(
250       ClientMap::iterator client_it,
251       EndpointGroupMap::iterator group_it,
252       size_t* num_endpoints_removed = nullptr);
253 
254   // Removes the client at the given iterator (which must exist in the cache),
255   // along with all of its endpoint groups and endpoints. Invalidates
256   // |client_it|.
257   // Returns the iterator following the client removed.
258   ClientMap::iterator RemoveClientInternal(ClientMap::iterator client_it);
259 
260   // Evict endpoints from the specified client and globally, if necessary to
261   // obey the per-client and global endpoint limits set in the ReportingPolicy.
262   //
263   // To evict from a client: First evicts any stale or expired groups for that
264   // origin. If that removes enough endpoints, then stop. Otherwise, find the
265   // stalest group (which has not been accessed for a delivery in the longest
266   // time) with the most endpoints, and evict the least important endpoints from
267   // that group.
268   // To evict globally: Find the stalest client with the most endpoints and do
269   // the above.
270   void EnforcePerClientAndGlobalEndpointLimits(ClientMap::iterator client_it);
271 
272   // Evicts endpoints from a client until it has evicted |endpoints_to_evict|
273   // endpoints. First tries to remove expired and stale groups. If that fails to
274   // satisfy the limit, finds the stalest group with the most endpoints and
275   // evicts the least important endpoints from it.
276   void EvictEndpointsFromClient(ClientMap::iterator client_it,
277                                 size_t endpoints_to_evict);
278 
279   // Evicts the least important endpoint from a group (the endpoint with lowest
280   // priority and lowest weight). May cause the group and/or client to be
281   // deleted and the iterators invalidated.
282   void EvictEndpointFromGroup(ClientMap::iterator client_it,
283                               EndpointGroupMap::iterator group_it);
284 
285   // Removes all expired or stale groups from the given client. May delete the
286   // client and invalidate |client_it| if it becomes empty.
287   // Increments |*num_endpoints_removed| by the number of endpoints removed.
288   // Returns true if |client_it| was invalidated.
289   bool RemoveExpiredOrStaleGroups(ClientMap::iterator client_it,
290                                   size_t* num_endpoints_removed);
291 
292   // Adds/removes (if it exists) |endpoint_it| from |endpoint_its_by_url_|.
293   void AddEndpointItToIndex(EndpointMap::iterator endpoint_it);
294   void RemoveEndpointItFromIndex(EndpointMap::iterator endpoint_it);
295 
296   // Helper methods for GetClientsAsValue().
297   base::Value GetClientAsValue(const Client& client) const;
298   base::Value GetEndpointGroupAsValue(
299       const CachedReportingEndpointGroup& group) const;
300   base::Value GetEndpointAsValue(const ReportingEndpoint& endpoint) const;
301 
302   // Convenience methods for fetching things from the context_.
clock()303   const base::Clock& clock() const { return context_->clock(); }
tick_clock()304   const base::TickClock& tick_clock() const { return context_->tick_clock(); }
store()305   PersistentReportingStore* store() { return context_->store(); }
306 
307   ReportingContext* context_;
308 
309   // Reports that have not yet been successfully uploaded.
310   ReportSet reports_;
311 
312   // Map of clients for all configured origins and NIKs, keyed on domain name
313   // (there may be multiple NIKs and origins per domain name).
314   ClientMap clients_;
315 
316   // Map of endpoint groups, keyed on origin and group name.
317   EndpointGroupMap endpoint_groups_;
318 
319   // Map of endpoints, keyed on origin and group name (there may be multiple
320   // endpoints for a given origin and group, with different urls).
321   EndpointMap endpoints_;
322 
323   // Index of endpoints stored in |endpoints_| keyed on URL, for easier lookup
324   // during RemoveEndpointsForUrl(). Should stay in sync with |endpoints_|.
325   std::multimap<GURL, EndpointMap::iterator> endpoint_its_by_url_;
326 
327   SEQUENCE_CHECKER(sequence_checker_);
328 
329   DISALLOW_COPY_AND_ASSIGN(ReportingCacheImpl);
330 };
331 
332 }  // namespace net
333 
334 #endif  // NET_REPORTING_REPORTING_CACHE_IMPL_H_
335