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