• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/reporting/reporting_endpoint_manager.h"
6 
7 #include <map>
8 #include <set>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/check.h"
14 #include "base/containers/lru_cache.h"
15 #include "base/memory/raw_ptr.h"
16 #include "base/notreached.h"
17 #include "base/rand_util.h"
18 #include "base/time/tick_clock.h"
19 #include "net/base/backoff_entry.h"
20 #include "net/base/network_anonymization_key.h"
21 #include "net/base/rand_callback.h"
22 #include "net/reporting/reporting_cache.h"
23 #include "net/reporting/reporting_delegate.h"
24 #include "net/reporting/reporting_endpoint.h"
25 #include "net/reporting/reporting_policy.h"
26 #include "url/gurl.h"
27 #include "url/origin.h"
28 
29 namespace net {
30 
31 namespace {
32 
33 class ReportingEndpointManagerImpl : public ReportingEndpointManager {
34  public:
ReportingEndpointManagerImpl(const ReportingPolicy * policy,const base::TickClock * tick_clock,const ReportingDelegate * delegate,ReportingCache * cache,const RandIntCallback & rand_callback)35   ReportingEndpointManagerImpl(const ReportingPolicy* policy,
36                                const base::TickClock* tick_clock,
37                                const ReportingDelegate* delegate,
38                                ReportingCache* cache,
39                                const RandIntCallback& rand_callback)
40       : policy_(policy),
41         tick_clock_(tick_clock),
42         delegate_(delegate),
43         cache_(cache),
44         rand_callback_(rand_callback),
45         endpoint_backoff_(kMaxEndpointBackoffCacheSize) {
46     DCHECK(policy);
47     DCHECK(tick_clock);
48     DCHECK(delegate);
49     DCHECK(cache);
50   }
51 
52   ReportingEndpointManagerImpl(const ReportingEndpointManagerImpl&) = delete;
53   ReportingEndpointManagerImpl& operator=(const ReportingEndpointManagerImpl&) =
54       delete;
55 
56   ~ReportingEndpointManagerImpl() override = default;
57 
FindEndpointForDelivery(const ReportingEndpointGroupKey & group_key)58   const ReportingEndpoint FindEndpointForDelivery(
59       const ReportingEndpointGroupKey& group_key) override {
60     // Get unexpired endpoints that apply to a delivery to |origin| and |group|.
61     // May have been configured by a superdomain of |origin|.
62     std::vector<ReportingEndpoint> endpoints =
63         cache_->GetCandidateEndpointsForDelivery(group_key);
64 
65     // Highest-priority endpoint(s) that are not expired, failing, or
66     // forbidden for use by the ReportingDelegate.
67     std::vector<ReportingEndpoint> available_endpoints;
68     // Total weight of endpoints in |available_endpoints|.
69     int total_weight = 0;
70 
71     for (const ReportingEndpoint& endpoint : endpoints) {
72       if (!delegate_->CanUseClient(endpoint.group_key.origin,
73                                    endpoint.info.url)) {
74         continue;
75       }
76 
77       // If this client is lower priority than the ones we've found, skip it.
78       if (!available_endpoints.empty() &&
79           endpoint.info.priority > available_endpoints[0].info.priority) {
80         continue;
81       }
82 
83       // This brings each match to the front of the MRU cache, so if an entry
84       // frequently matches requests, it's more likely to stay in the cache.
85       auto endpoint_backoff_it = endpoint_backoff_.Get(EndpointBackoffKey(
86           group_key.network_anonymization_key, endpoint.info.url));
87       if (endpoint_backoff_it != endpoint_backoff_.end() &&
88           endpoint_backoff_it->second->ShouldRejectRequest()) {
89         continue;
90       }
91 
92       // If this client is higher priority than the ones we've found (or we
93       // haven't found any), forget about those ones and remember this one.
94       if (available_endpoints.empty() ||
95           endpoint.info.priority < available_endpoints[0].info.priority) {
96         available_endpoints.clear();
97         total_weight = 0;
98       }
99 
100       available_endpoints.push_back(endpoint);
101       total_weight += endpoint.info.weight;
102     }
103 
104     if (available_endpoints.empty()) {
105       return ReportingEndpoint();
106     }
107 
108     if (total_weight == 0) {
109       int random_index = rand_callback_.Run(0, available_endpoints.size() - 1);
110       return available_endpoints[random_index];
111     }
112 
113     int random_index = rand_callback_.Run(0, total_weight - 1);
114     int weight_so_far = 0;
115     for (const auto& endpoint : available_endpoints) {
116       weight_so_far += endpoint.info.weight;
117       if (random_index < weight_so_far) {
118         return endpoint;
119       }
120     }
121 
122     // TODO(juliatuttle): Can we reach this in some weird overflow case?
123     NOTREACHED();
124     return ReportingEndpoint();
125   }
126 
InformOfEndpointRequest(const NetworkAnonymizationKey & network_anonymization_key,const GURL & endpoint,bool succeeded)127   void InformOfEndpointRequest(
128       const NetworkAnonymizationKey& network_anonymization_key,
129       const GURL& endpoint,
130       bool succeeded) override {
131     EndpointBackoffKey endpoint_backoff_key(network_anonymization_key,
132                                             endpoint);
133     // This will bring the entry to the front of the cache, if it exists.
134     auto endpoint_backoff_it = endpoint_backoff_.Get(endpoint_backoff_key);
135     if (endpoint_backoff_it == endpoint_backoff_.end()) {
136       endpoint_backoff_it = endpoint_backoff_.Put(
137           std::move(endpoint_backoff_key),
138           std::make_unique<BackoffEntry>(&policy_->endpoint_backoff_policy,
139                                          tick_clock_));
140     }
141     endpoint_backoff_it->second->InformOfRequest(succeeded);
142   }
143 
144  private:
145   using EndpointBackoffKey = std::pair<NetworkAnonymizationKey, GURL>;
146 
147   const raw_ptr<const ReportingPolicy> policy_;
148   const raw_ptr<const base::TickClock> tick_clock_;
149   const raw_ptr<const ReportingDelegate> delegate_;
150   const raw_ptr<ReportingCache> cache_;
151 
152   RandIntCallback rand_callback_;
153 
154   // Note: Currently the ReportingBrowsingDataRemover does not clear this data
155   // because it's not persisted to disk. If it's ever persisted, it will need
156   // to be cleared as well.
157   // TODO(chlily): clear this data when endpoints are deleted to avoid unbounded
158   // growth of this map.
159   base::LRUCache<EndpointBackoffKey, std::unique_ptr<net::BackoffEntry>>
160       endpoint_backoff_;
161 };
162 
163 }  // namespace
164 
165 // static
Create(const ReportingPolicy * policy,const base::TickClock * tick_clock,const ReportingDelegate * delegate,ReportingCache * cache,const RandIntCallback & rand_callback)166 std::unique_ptr<ReportingEndpointManager> ReportingEndpointManager::Create(
167     const ReportingPolicy* policy,
168     const base::TickClock* tick_clock,
169     const ReportingDelegate* delegate,
170     ReportingCache* cache,
171     const RandIntCallback& rand_callback) {
172   return std::make_unique<ReportingEndpointManagerImpl>(
173       policy, tick_clock, delegate, cache, rand_callback);
174 }
175 
176 ReportingEndpointManager::~ReportingEndpointManager() = default;
177 
178 }  // namespace net
179