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