Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Side by Side Diff: net/http/broken_alternative_services.cc

Issue 2898983006: Fix and refactor HttpServerPropertiesImpl's alternative services brokenness expiration behavior (Closed)
Patch Set: Fixed some mistakes in unit-test code Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2017 The Chromium Authors. All rights reserved.
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/memory/singleton.h"
8 #include "base/time/time.h"
9 #include "net/http/http_server_properties_impl.h"
10
11 namespace net {
12
13 namespace {
14
15 // Initial delay for broken alternative services.
16 const uint64_t kBrokenAlternativeProtocolDelaySecs = 300;
17 // Subsequent failures result in exponential (base 2) backoff.
18 // Limit binary shift to limit delay to approximately 2 days.
19 const int kBrokenDelayMaxShift = 9;
20
21 base::TimeDelta ComputeBrokenAlternativeServiceExpirationDelay(
22 int broken_count) {
23 DCHECK(broken_count >= 0);
24 if (broken_count > kBrokenDelayMaxShift)
25 broken_count = kBrokenDelayMaxShift;
26 return base::TimeDelta::FromSeconds(kBrokenAlternativeProtocolDelaySecs) *
27 (1 << broken_count);
28 }
29
30 // Default implementation of BrokenAlternativeServices::Clock.
31 class DefaultClock : public BrokenAlternativeServices::Clock {
32 public:
33 static DefaultClock* GetInstance() {
34 return base::Singleton<DefaultClock>::get();
35 }
36
37 DefaultClock() {}
38 ~DefaultClock() override {}
Ryan Hamilton 2017/05/26 03:52:08 Do you need these since they're default?
wangyix1 2017/05/27 01:20:08 Done.
39
40 base::TimeTicks Now() const override { return base::TimeTicks::Now(); }
41
42 private:
43 DefaultClock(const DefaultClock&) = delete;
44 void operator=(const DefaultClock&) = delete;
Ryan Hamilton 2017/05/26 03:52:08 these don't need to be made private since they're
wangyix1 2017/05/27 01:20:09 Done.
45 };
46
47 } // namespace
48
49 BrokenAlternativeServices::Clock*
50 BrokenAlternativeServices::Clock::GetDefaultClockInstance() {
51 return DefaultClock::GetInstance();
Ryan Hamilton 2017/05/26 03:52:08 I would just inline the base::Singleton<> call her
wangyix1 2017/05/27 01:20:08 Tried this and got compiler error: 'get' is a priv
Ryan Hamilton 2017/05/27 13:28:15 *mind blown*
52 }
53
54 BrokenAlternativeServices::BrokenAlternativeServices(Delegate* delegate,
55 Clock* clock)
56 : delegate_(delegate),
57 recently_broken_alternative_services_(
58 RecentlyBrokenAlternativeServices::NO_AUTO_EVICT),
59 clock_(clock),
60 weak_ptr_factory_(this) {
61 DCHECK(delegate_);
62 }
63
64 BrokenAlternativeServices::~BrokenAlternativeServices() {}
65
66 void BrokenAlternativeServices::MarkAlternativeServiceBroken(
67 const AlternativeService& alternative_service) {
68 // Empty host means use host of origin, callers are supposed to substitute.
69 DCHECK(!alternative_service.host.empty());
70 if (alternative_service.protocol == kProtoUnknown) {
71 LOG(DFATAL) << "Trying to mark unknown alternate protocol broken.";
72 return;
73 }
Ryan Hamilton 2017/05/26 03:52:08 I think I'd just DCHECK this.
wangyix1 2017/05/27 01:20:09 Done.
74 auto it = recently_broken_alternative_services_.Get(alternative_service);
75 int broken_count = 0;
76 if (it == recently_broken_alternative_services_.end()) {
77 recently_broken_alternative_services_.Put(alternative_service, 1);
78 } else {
79 broken_count = it->second++;
80 }
81 base::TimeTicks expiration =
82 clock_->Now() +
83 ComputeBrokenAlternativeServiceExpirationDelay(broken_count);
84 // Return if alternative service is already in expiration queue.
85 BrokenAlternativeServiceList::iterator list_it;
86 if (!AddToBrokenAlternativeServiceListAndMap(alternative_service, expiration,
87 &list_it)) {
88 return;
89 }
90
91 // If this is now the first entry in the list (i.e. |alternative_service| is
92 // the next alt svc to expire), schedule an expiration task for it.
93 if (list_it == broken_alternative_service_list_.begin()) {
94 ScheduleBrokenAlternateProtocolMappingsExpiration();
95 }
96 }
97
98 void BrokenAlternativeServices::MarkAlternativeServiceRecentlyBroken(
99 const AlternativeService& alternative_service) {
100 if (recently_broken_alternative_services_.Get(alternative_service) ==
101 recently_broken_alternative_services_.end()) {
102 recently_broken_alternative_services_.Put(alternative_service, 1);
103 }
104 }
105
106 bool BrokenAlternativeServices::IsAlternativeServiceBroken(
107 const AlternativeService& alternative_service) const {
108 // Empty host means use host of origin, callers are supposed to substitute.
109 DCHECK(!alternative_service.host.empty());
110 return broken_alternative_service_map_.find(alternative_service) !=
111 broken_alternative_service_map_.end();
112 }
113
114 bool BrokenAlternativeServices::WasAlternativeServiceRecentlyBroken(
115 const AlternativeService& alternative_service) {
116 if (alternative_service.protocol == kProtoUnknown)
117 return false;
Ryan Hamilton 2017/05/26 03:52:08 I suspect we don't need this since it's already fo
wangyix1 2017/05/27 01:20:10 Done.
118
119 return recently_broken_alternative_services_.Get(alternative_service) !=
120 recently_broken_alternative_services_.end();
121 }
122
123 void BrokenAlternativeServices::ConfirmAlternativeService(
124 const AlternativeService& alternative_service) {
125 if (alternative_service.protocol == kProtoUnknown)
126 return;
127
128 // Remove |alternative_service| from |alternative_service_list_| and
129 // |alternative_service_map_|.
130 auto map_it = broken_alternative_service_map_.find(alternative_service);
131 if (map_it != broken_alternative_service_map_.end()) {
132 broken_alternative_service_list_.erase(map_it->second);
133 broken_alternative_service_map_.erase(map_it);
134 }
135
136 auto it = recently_broken_alternative_services_.Get(alternative_service);
137 if (it != recently_broken_alternative_services_.end()) {
138 recently_broken_alternative_services_.Erase(it);
139 }
140 }
141
142 bool BrokenAlternativeServices ::AddToBrokenAlternativeServiceListAndMap(
Ryan Hamilton 2017/05/26 03:52:08 nit: no space before "::"
wangyix1 2017/05/27 01:20:08 Done.
143 const AlternativeService& alternative_service,
144 base::TimeTicks expiration,
145 BrokenAlternativeServiceList::iterator* it) {
146 DCHECK(it);
147
148 auto map_it = broken_alternative_service_map_.find(alternative_service);
149 if (map_it != broken_alternative_service_map_.end())
150 return false;
151
152 // Iterate from end of |broken_alternative_service_list_| to find where to
153 // insert it to keep the list sorted by expiration time.
154 auto list_it = broken_alternative_service_list_.end();
Ryan Hamilton 2017/05/26 03:52:08 Is it safe to do -- on an end() iterator? I think
wangyix1 2017/05/27 01:20:09 The reason I used an iterator instead of reverse_i
Ryan Hamilton 2017/05/27 13:28:15 Ah, I see. Fwiw, reverse_iterator has a base() met
155 while (list_it != broken_alternative_service_list_.begin()) {
156 --list_it;
157 if (list_it->expiration <= expiration) {
158 ++list_it;
159 break;
160 }
161 }
Ryan Hamilton 2017/05/26 03:52:08 Could std::lower_bound be used here?
wangyix1 2017/05/27 01:20:10 Looks like std::lower_bound was designed for conta
Ryan Hamilton 2017/05/27 13:28:15 Fair enough. Interestingly it appears to be merely
wangyix1 2017/05/30 21:18:17 Oh interesting. If it's linear, it should be fine
162
163 // Insert |alternative_service| into the list and the map
164 list_it = broken_alternative_service_list_.insert(
165 list_it, BrokenAltSvcExpireInfo(alternative_service, expiration));
166 broken_alternative_service_map_.insert(
167 std::make_pair(alternative_service, list_it));
168
169 *it = list_it;
170 return true;
171 }
172
173 void BrokenAlternativeServices ::ExpireBrokenAlternateProtocolMappings() {
174 base::TimeTicks now = clock_->Now();
175 ;
Ryan Hamilton 2017/05/26 03:52:07 nit: remove this?
wangyix1 2017/05/27 01:20:09 Done.
176 while (!broken_alternative_service_list_.empty()) {
177 auto it = broken_alternative_service_list_.begin();
178 if (now < it->expiration) {
179 break;
180 }
181
182 const AlternativeService expired_alternative_service =
183 it->alternative_service;
184 broken_alternative_service_map_.erase(expired_alternative_service);
185 broken_alternative_service_list_.erase(it);
186
187 delegate_->OnExpireBrokenAlternativeService(expired_alternative_service);
Ryan Hamilton 2017/05/26 03:52:08 not that it super matters, but since On...() takes
wangyix1 2017/05/27 01:20:09 Done.
188 }
189
190 if (!broken_alternative_service_list_.empty())
191 ScheduleBrokenAlternateProtocolMappingsExpiration();
192 }
193
194 void BrokenAlternativeServices ::
195 ScheduleBrokenAlternateProtocolMappingsExpiration() {
196 DCHECK(!broken_alternative_service_list_.empty());
197 base::TimeTicks now = clock_->Now();
198 base::TimeTicks when = broken_alternative_service_list_.front().expiration;
199 base::TimeDelta delay = when > now ? when - now : base::TimeDelta();
200 expiration_timer_.Stop();
201 expiration_timer_.Start(
202 FROM_HERE, delay,
203 base::Bind(
204 &BrokenAlternativeServices ::ExpireBrokenAlternateProtocolMappings,
205 weak_ptr_factory_.GetWeakPtr()));
206 }
207
208 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698