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