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