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