xref: /aosp_15_r20/external/cronet/net/http/broken_alternative_services.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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/http/broken_alternative_services.h"
6 
7 #include "base/containers/adapters.h"
8 #include "base/functional/bind.h"
9 #include "base/memory/singleton.h"
10 #include "base/time/tick_clock.h"
11 #include "base/time/time.h"
12 #include "net/http/http_server_properties.h"
13 
14 namespace net {
15 
16 namespace {
17 
18 // Default broken alternative services, which is used when
19 // exponential_backoff_on_initial_delay is false.
20 constexpr base::TimeDelta kDefaultBrokenAlternativeProtocolDelay =
21     base::Seconds(300);
22 // Subsequent failures result in exponential (base 2) backoff.
23 // Given the shortest broken delay is 1s, limit binary shift to limit delay to
24 // approximately 2 days.
25 const int kBrokenDelayMaxShift = 18;
26 // Lower and upper limits of broken alternative service delay.
27 constexpr base::TimeDelta kMinBrokenAlternativeProtocolDelay = base::Seconds(1);
28 constexpr base::TimeDelta kMaxBrokenAlternativeProtocolDelay = base::Days(2);
29 
ComputeBrokenAlternativeServiceExpirationDelay(int broken_count,base::TimeDelta initial_delay,bool exponential_backoff_on_initial_delay)30 base::TimeDelta ComputeBrokenAlternativeServiceExpirationDelay(
31     int broken_count,
32     base::TimeDelta initial_delay,
33     bool exponential_backoff_on_initial_delay) {
34   DCHECK_GE(broken_count, 0);
35   // Make sure initial delay is within [1s, 300s].
36   if (initial_delay < kMinBrokenAlternativeProtocolDelay) {
37     initial_delay = kMinBrokenAlternativeProtocolDelay;
38   }
39   if (initial_delay > kDefaultBrokenAlternativeProtocolDelay) {
40     initial_delay = kDefaultBrokenAlternativeProtocolDelay;
41   }
42   if (broken_count == 0) {
43     return initial_delay;
44   }
45   // Limit broken_count to avoid overflow.
46   if (broken_count > kBrokenDelayMaxShift) {
47     broken_count = kBrokenDelayMaxShift;
48   }
49   base::TimeDelta delay;
50   if (exponential_backoff_on_initial_delay) {
51     delay = initial_delay * (1 << broken_count);
52   } else {
53     delay = kDefaultBrokenAlternativeProtocolDelay * (1 << (broken_count - 1));
54   }
55   return std::min(delay, kMaxBrokenAlternativeProtocolDelay);
56 }
57 
58 }  // namespace
59 
BrokenAlternativeService(const AlternativeService & alternative_service,const NetworkAnonymizationKey & network_anonymization_key,bool use_network_anonymization_key)60 BrokenAlternativeService::BrokenAlternativeService(
61     const AlternativeService& alternative_service,
62     const NetworkAnonymizationKey& network_anonymization_key,
63     bool use_network_anonymization_key)
64     : alternative_service(alternative_service),
65       network_anonymization_key(use_network_anonymization_key
66                                     ? network_anonymization_key
67                                     : NetworkAnonymizationKey()) {}
68 
69 BrokenAlternativeService::~BrokenAlternativeService() = default;
70 
operator <(const BrokenAlternativeService & other) const71 bool BrokenAlternativeService::operator<(
72     const BrokenAlternativeService& other) const {
73   return std::tie(alternative_service, network_anonymization_key) <
74          std::tie(other.alternative_service, other.network_anonymization_key);
75 }
76 
BrokenAlternativeServices(int max_recently_broken_alternative_service_entries,Delegate * delegate,const base::TickClock * clock)77 BrokenAlternativeServices::BrokenAlternativeServices(
78     int max_recently_broken_alternative_service_entries,
79     Delegate* delegate,
80     const base::TickClock* clock)
81     : delegate_(delegate),
82       clock_(clock),
83       recently_broken_alternative_services_(
84           max_recently_broken_alternative_service_entries),
85       initial_delay_(kDefaultBrokenAlternativeProtocolDelay) {
86   DCHECK(delegate_);
87   DCHECK(clock_);
88 }
89 
90 BrokenAlternativeServices::~BrokenAlternativeServices() = default;
91 
Clear()92 void BrokenAlternativeServices::Clear() {
93   expiration_timer_.Stop();
94   broken_alternative_service_list_.clear();
95   broken_alternative_service_map_.clear();
96   recently_broken_alternative_services_.Clear();
97 }
98 
MarkBrokenUntilDefaultNetworkChanges(const BrokenAlternativeService & broken_alternative_service)99 void BrokenAlternativeServices::MarkBrokenUntilDefaultNetworkChanges(
100     const BrokenAlternativeService& broken_alternative_service) {
101   DCHECK(!broken_alternative_service.alternative_service.host.empty());
102   DCHECK_NE(kProtoUnknown,
103             broken_alternative_service.alternative_service.protocol);
104 
105   // The brokenness will expire on the default network change or based on
106   // timer.
107   broken_alternative_services_on_default_network_.insert(
108       broken_alternative_service);
109   MarkBrokenImpl(broken_alternative_service);
110 }
111 
MarkBroken(const BrokenAlternativeService & broken_alternative_service)112 void BrokenAlternativeServices::MarkBroken(
113     const BrokenAlternativeService& broken_alternative_service) {
114   // The brokenness expires based only on the timer, not on the default network
115   // change.
116   broken_alternative_services_on_default_network_.erase(
117       broken_alternative_service);
118   MarkBrokenImpl(broken_alternative_service);
119 }
120 
MarkBrokenImpl(const BrokenAlternativeService & broken_alternative_service)121 void BrokenAlternativeServices::MarkBrokenImpl(
122     const BrokenAlternativeService& broken_alternative_service) {
123   // Empty host means use host of origin, callers are supposed to substitute.
124   DCHECK(!broken_alternative_service.alternative_service.host.empty());
125   DCHECK_NE(kProtoUnknown,
126             broken_alternative_service.alternative_service.protocol);
127 
128   auto it =
129       recently_broken_alternative_services_.Get(broken_alternative_service);
130   int broken_count = 0;
131   if (it == recently_broken_alternative_services_.end()) {
132     recently_broken_alternative_services_.Put(broken_alternative_service, 1);
133   } else {
134     broken_count = it->second++;
135   }
136   base::TimeTicks expiration =
137       clock_->NowTicks() +
138       ComputeBrokenAlternativeServiceExpirationDelay(
139           broken_count, initial_delay_, exponential_backoff_on_initial_delay_);
140   // Return if alternative service is already in expiration queue.
141   BrokenAlternativeServiceList::iterator list_it;
142   if (!AddToBrokenListAndMap(broken_alternative_service, expiration,
143                              &list_it)) {
144     return;
145   }
146 
147   // If this is now the first entry in the list (i.e.
148   // |broken_alternative_service| is the next alt svc to expire), schedule
149   // an expiration task for it.
150   if (list_it == broken_alternative_service_list_.begin()) {
151     ScheduleBrokenAlternateProtocolMappingsExpiration();
152   }
153 }
154 
MarkRecentlyBroken(const BrokenAlternativeService & broken_alternative_service)155 void BrokenAlternativeServices::MarkRecentlyBroken(
156     const BrokenAlternativeService& broken_alternative_service) {
157   DCHECK_NE(kProtoUnknown,
158             broken_alternative_service.alternative_service.protocol);
159   if (recently_broken_alternative_services_.Get(broken_alternative_service) ==
160       recently_broken_alternative_services_.end()) {
161     recently_broken_alternative_services_.Put(broken_alternative_service, 1);
162   }
163 }
164 
IsBroken(const BrokenAlternativeService & broken_alternative_service) const165 bool BrokenAlternativeServices::IsBroken(
166     const BrokenAlternativeService& broken_alternative_service) const {
167   // Empty host means use host of origin, callers are supposed to substitute.
168   DCHECK(!broken_alternative_service.alternative_service.host.empty());
169   return broken_alternative_service_map_.find(broken_alternative_service) !=
170          broken_alternative_service_map_.end();
171 }
172 
IsBroken(const BrokenAlternativeService & broken_alternative_service,base::TimeTicks * brokenness_expiration) const173 bool BrokenAlternativeServices::IsBroken(
174     const BrokenAlternativeService& broken_alternative_service,
175     base::TimeTicks* brokenness_expiration) const {
176   DCHECK(brokenness_expiration != nullptr);
177   // Empty host means use host of origin, callers are supposed to substitute.
178   DCHECK(!broken_alternative_service.alternative_service.host.empty());
179   auto map_it =
180       broken_alternative_service_map_.find(broken_alternative_service);
181   if (map_it == broken_alternative_service_map_.end()) {
182     return false;
183   }
184   auto list_it = map_it->second;
185   *brokenness_expiration = list_it->second;
186   return true;
187 }
188 
WasRecentlyBroken(const BrokenAlternativeService & broken_alternative_service)189 bool BrokenAlternativeServices::WasRecentlyBroken(
190     const BrokenAlternativeService& broken_alternative_service) {
191   DCHECK(!broken_alternative_service.alternative_service.host.empty());
192   return recently_broken_alternative_services_.Get(
193              broken_alternative_service) !=
194              recently_broken_alternative_services_.end() ||
195          broken_alternative_service_map_.find(broken_alternative_service) !=
196              broken_alternative_service_map_.end();
197 }
198 
Confirm(const BrokenAlternativeService & broken_alternative_service)199 void BrokenAlternativeServices::Confirm(
200     const BrokenAlternativeService& broken_alternative_service) {
201   DCHECK_NE(kProtoUnknown,
202             broken_alternative_service.alternative_service.protocol);
203 
204   // Remove |broken_alternative_service| from
205   // |broken_alternative_service_list_|, |broken_alternative_service_map_| and
206   // |broken_alternative_services_on_default_network_|.
207   auto map_it =
208       broken_alternative_service_map_.find(broken_alternative_service);
209   if (map_it != broken_alternative_service_map_.end()) {
210     broken_alternative_service_list_.erase(map_it->second);
211     broken_alternative_service_map_.erase(map_it);
212   }
213 
214   auto it =
215       recently_broken_alternative_services_.Get(broken_alternative_service);
216   if (it != recently_broken_alternative_services_.end()) {
217     recently_broken_alternative_services_.Erase(it);
218   }
219 
220   broken_alternative_services_on_default_network_.erase(
221       broken_alternative_service);
222 }
223 
OnDefaultNetworkChanged()224 bool BrokenAlternativeServices::OnDefaultNetworkChanged() {
225   bool changed = !broken_alternative_services_on_default_network_.empty();
226   while (!broken_alternative_services_on_default_network_.empty()) {
227     Confirm(*broken_alternative_services_on_default_network_.begin());
228   }
229   return changed;
230 }
231 
SetBrokenAndRecentlyBrokenAlternativeServices(std::unique_ptr<BrokenAlternativeServiceList> broken_alternative_service_list,std::unique_ptr<RecentlyBrokenAlternativeServices> recently_broken_alternative_services)232 void BrokenAlternativeServices::SetBrokenAndRecentlyBrokenAlternativeServices(
233     std::unique_ptr<BrokenAlternativeServiceList>
234         broken_alternative_service_list,
235     std::unique_ptr<RecentlyBrokenAlternativeServices>
236         recently_broken_alternative_services) {
237   DCHECK(broken_alternative_service_list);
238   DCHECK(recently_broken_alternative_services);
239 
240   base::TimeTicks next_expiration =
241       broken_alternative_service_list_.empty()
242           ? base::TimeTicks::Max()
243           : broken_alternative_service_list_.front().second;
244 
245   // Add |recently_broken_alternative_services| to
246   // |recently_broken_alternative_services_|.
247   // If an alt-svc already exists, overwrite its broken-count to the one in
248   // |recently_broken_alternative_services|.
249 
250   recently_broken_alternative_services_.Swap(
251       *recently_broken_alternative_services);
252   // Add back all existing recently broken alt svcs to cache so they're at
253   // front of recency list (LRUCache::Get() does this automatically).
254   for (const auto& [service, broken_count] :
255        base::Reversed(*recently_broken_alternative_services)) {
256     if (recently_broken_alternative_services_.Get(service) ==
257         recently_broken_alternative_services_.end()) {
258       recently_broken_alternative_services_.Put(service, broken_count);
259     }
260   }
261 
262   // Append |broken_alternative_service_list| to
263   // |broken_alternative_service_list_|
264   size_t num_broken_alt_svcs_added = broken_alternative_service_list->size();
265   broken_alternative_service_list_.splice(
266       broken_alternative_service_list_.begin(),
267       *broken_alternative_service_list);
268   // For each newly-appended alt svc in |broken_alternative_service_list_|,
269   // add an entry to |broken_alternative_service_map_| that points to its
270   // list iterator. Also, add an entry for that alt svc in
271   // |recently_broken_alternative_services_| if one doesn't exist.
272   auto list_it = broken_alternative_service_list_.begin();
273   for (size_t i = 0; i < num_broken_alt_svcs_added; ++i) {
274     const BrokenAlternativeService& broken_alternative_service = list_it->first;
275     auto map_it =
276         broken_alternative_service_map_.find(broken_alternative_service);
277     if (map_it != broken_alternative_service_map_.end()) {
278       // Implies this entry already exists somewhere else in
279       // |broken_alternative_service_list_|. Remove the existing entry from
280       // |broken_alternative_service_list_|, and update the
281       // |broken_alternative_service_map_| entry to point to this list entry
282       // instead.
283       auto list_existing_entry_it = map_it->second;
284       broken_alternative_service_list_.erase(list_existing_entry_it);
285       map_it->second = list_it;
286     } else {
287       broken_alternative_service_map_.emplace(broken_alternative_service,
288                                               list_it);
289     }
290 
291     if (recently_broken_alternative_services_.Peek(
292             broken_alternative_service) ==
293         recently_broken_alternative_services_.end()) {
294       recently_broken_alternative_services_.Put(broken_alternative_service, 1);
295     }
296 
297     ++list_it;
298   }
299 
300   // Sort |broken_alternative_service_list_| by expiration time. This operation
301   // does not invalidate list iterators, so |broken_alternative_service_map_|
302   // does not need to be updated.
303   broken_alternative_service_list_.sort(
304       [](const std::pair<BrokenAlternativeService, base::TimeTicks>& lhs,
305          const std::pair<BrokenAlternativeService, base::TimeTicks>& rhs)
306           -> bool { return lhs.second < rhs.second; });
307 
308   base::TimeTicks new_next_expiration =
309       broken_alternative_service_list_.empty()
310           ? base::TimeTicks::Max()
311           : broken_alternative_service_list_.front().second;
312 
313   if (new_next_expiration != next_expiration)
314     ScheduleBrokenAlternateProtocolMappingsExpiration();
315 }
316 
SetDelayParams(std::optional<base::TimeDelta> initial_delay,std::optional<bool> exponential_backoff_on_initial_delay)317 void BrokenAlternativeServices::SetDelayParams(
318     std::optional<base::TimeDelta> initial_delay,
319     std::optional<bool> exponential_backoff_on_initial_delay) {
320   if (initial_delay.has_value()) {
321     initial_delay_ = initial_delay.value();
322   }
323   if (exponential_backoff_on_initial_delay.has_value()) {
324     exponential_backoff_on_initial_delay_ =
325         exponential_backoff_on_initial_delay.value();
326   }
327 }
328 
329 const BrokenAlternativeServiceList&
broken_alternative_service_list() const330 BrokenAlternativeServices::broken_alternative_service_list() const {
331   return broken_alternative_service_list_;
332 }
333 
334 const RecentlyBrokenAlternativeServices&
recently_broken_alternative_services() const335 BrokenAlternativeServices::recently_broken_alternative_services() const {
336   return recently_broken_alternative_services_;
337 }
338 
AddToBrokenListAndMap(const BrokenAlternativeService & broken_alternative_service,base::TimeTicks expiration,BrokenAlternativeServiceList::iterator * it)339 bool BrokenAlternativeServices::AddToBrokenListAndMap(
340     const BrokenAlternativeService& broken_alternative_service,
341     base::TimeTicks expiration,
342     BrokenAlternativeServiceList::iterator* it) {
343   DCHECK(it);
344 
345   auto map_it =
346       broken_alternative_service_map_.find(broken_alternative_service);
347   if (map_it != broken_alternative_service_map_.end())
348     return false;
349 
350   // Iterate from end of |broken_alternative_service_list_| to find where to
351   // insert it to keep the list sorted by expiration time.
352   auto list_it = broken_alternative_service_list_.end();
353   while (list_it != broken_alternative_service_list_.begin()) {
354     --list_it;
355     if (list_it->second <= expiration) {
356       ++list_it;
357       break;
358     }
359   }
360 
361   // Insert |broken_alternative_service| into the list and the map.
362   list_it = broken_alternative_service_list_.insert(
363       list_it, std::pair(broken_alternative_service, expiration));
364   broken_alternative_service_map_.emplace(broken_alternative_service, list_it);
365 
366   *it = list_it;
367   return true;
368 }
369 
ExpireBrokenAlternateProtocolMappings()370 void BrokenAlternativeServices::ExpireBrokenAlternateProtocolMappings() {
371   base::TimeTicks now = clock_->NowTicks();
372 
373   while (!broken_alternative_service_list_.empty()) {
374     auto it = broken_alternative_service_list_.begin();
375     if (now < it->second) {
376       break;
377     }
378 
379     delegate_->OnExpireBrokenAlternativeService(
380         it->first.alternative_service, it->first.network_anonymization_key);
381 
382     broken_alternative_service_map_.erase(it->first);
383     broken_alternative_service_list_.erase(it);
384   }
385 
386   if (!broken_alternative_service_list_.empty())
387     ScheduleBrokenAlternateProtocolMappingsExpiration();
388 }
389 
390 void BrokenAlternativeServices ::
ScheduleBrokenAlternateProtocolMappingsExpiration()391     ScheduleBrokenAlternateProtocolMappingsExpiration() {
392   DCHECK(!broken_alternative_service_list_.empty());
393   base::TimeTicks now = clock_->NowTicks();
394   base::TimeTicks next_expiration =
395       broken_alternative_service_list_.front().second;
396   base::TimeDelta delay =
397       next_expiration > now ? next_expiration - now : base::TimeDelta();
398   expiration_timer_.Stop();
399   expiration_timer_.Start(
400       FROM_HERE, delay,
401       base::BindOnce(
402           &BrokenAlternativeServices ::ExpireBrokenAlternateProtocolMappings,
403           weak_ptr_factory_.GetWeakPtr()));
404 }
405 
406 }  // namespace net
407