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

Side by Side Diff: chrome/browser/policy/cloud/external_policy_data_updater.cc

Issue 109743002: Move policy code into components/policy. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: moar fixes Created 7 years 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013 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 "chrome/browser/policy/cloud/external_policy_data_updater.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/sha1.h"
14 #include "base/stl_util.h"
15 #include "chrome/browser/policy/cloud/external_policy_data_fetcher.h"
16 #include "net/base/backoff_entry.h"
17 #include "url/gurl.h"
18
19 namespace policy {
20
21 namespace {
22
23 // Policies for exponential backoff of failed requests. There are 3 policies for
24 // different classes of errors.
25
26 // For temporary errors (HTTP 500, RST, etc).
27 const net::BackoffEntry::Policy kRetrySoonPolicy = {
28 // Number of initial errors to ignore before starting to back off.
29 0,
30
31 // Initial delay in ms: 60 seconds.
32 1000 * 60,
33
34 // Factor by which the waiting time is multiplied.
35 2,
36
37 // Fuzzing percentage; this spreads delays randomly between 80% and 100%
38 // of the calculated time.
39 0.20,
40
41 // Maximum delay in ms: 12 hours.
42 1000 * 60 * 60 * 12,
43
44 // When to discard an entry: never.
45 -1,
46
47 // |always_use_initial_delay|; false means that the initial delay is
48 // applied after the first error, and starts backing off from there.
49 false,
50 };
51
52 // For other errors (request failed, server errors).
53 const net::BackoffEntry::Policy kRetryLaterPolicy = {
54 // Number of initial errors to ignore before starting to back off.
55 0,
56
57 // Initial delay in ms: 1 hour.
58 1000 * 60 * 60,
59
60 // Factor by which the waiting time is multiplied.
61 2,
62
63 // Fuzzing percentage; this spreads delays randomly between 80% and 100%
64 // of the calculated time.
65 0.20,
66
67 // Maximum delay in ms: 12 hours.
68 1000 * 60 * 60 * 12,
69
70 // When to discard an entry: never.
71 -1,
72
73 // |always_use_initial_delay|; false means that the initial delay is
74 // applied after the first error, and starts backing off from there.
75 false,
76 };
77
78 // When the data fails validation (maybe because the policy URL and the data
79 // served at that URL are out of sync). This essentially retries every 12 hours,
80 // with some random jitter.
81 const net::BackoffEntry::Policy kRetryMuchLaterPolicy = {
82 // Number of initial errors to ignore before starting to back off.
83 0,
84
85 // Initial delay in ms: 12 hours.
86 1000 * 60 * 60 * 12,
87
88 // Factor by which the waiting time is multiplied.
89 2,
90
91 // Fuzzing percentage; this spreads delays randomly between 80% and 100%
92 // of the calculated time.
93 0.20,
94
95 // Maximum delay in ms: 12 hours.
96 1000 * 60 * 60 * 12,
97
98 // When to discard an entry: never.
99 -1,
100
101 // |always_use_initial_delay|; false means that the initial delay is
102 // applied after the first error, and starts backing off from there.
103 false,
104 };
105
106 // Maximum number of retries for requests that aren't likely to get a
107 // different response (e.g. HTTP 4xx replies).
108 const int kMaxLimitedRetries = 3;
109
110 } // namespace
111
112 class ExternalPolicyDataUpdater::FetchJob
113 : public base::SupportsWeakPtr<FetchJob> {
114 public:
115 FetchJob(ExternalPolicyDataUpdater* updater,
116 const std::string& key,
117 const ExternalPolicyDataUpdater::Request& request,
118 const ExternalPolicyDataUpdater::FetchSuccessCallback& callback);
119 virtual ~FetchJob();
120
121 const std::string& key() const;
122 const ExternalPolicyDataUpdater::Request& request() const;
123
124 void Start();
125
126 void OnFetchFinished(ExternalPolicyDataFetcher::Result result,
127 scoped_ptr<std::string> data);
128
129 private:
130 void OnFailed(net::BackoffEntry* backoff_entry);
131 void Reschedule();
132
133 // Always valid as long as |this| is alive.
134 ExternalPolicyDataUpdater* updater_;
135
136 const std::string key_;
137 const ExternalPolicyDataUpdater::Request request_;
138 ExternalPolicyDataUpdater::FetchSuccessCallback callback_;
139
140 // If the job is currently running, a corresponding |fetch_job_| exists in the
141 // |external_policy_data_fetcher_|. The job must eventually call back to the
142 // |updater_|'s OnJobSucceeded() or OnJobFailed() method in this case.
143 // If the job is currently not running, |fetch_job_| is NULL and no callbacks
144 // should be invoked.
145 ExternalPolicyDataFetcher::Job* fetch_job_; // Not owned.
146
147 // Some errors should trigger a limited number of retries, even with backoff.
148 // This counts down the number of such retries to stop retrying once the limit
149 // is reached.
150 int limited_retries_remaining_;
151
152 // Various delays to retry a failed download, depending on the failure reason.
153 net::BackoffEntry retry_soon_entry_;
154 net::BackoffEntry retry_later_entry_;
155 net::BackoffEntry retry_much_later_entry_;
156
157 DISALLOW_COPY_AND_ASSIGN(FetchJob);
158 };
159
160 ExternalPolicyDataUpdater::Request::Request() {
161 }
162
163 ExternalPolicyDataUpdater::Request::Request(const std::string& url,
164 const std::string& hash,
165 int64 max_size)
166 : url(url), hash(hash), max_size(max_size) {
167 }
168
169 bool ExternalPolicyDataUpdater::Request::operator==(
170 const Request& other) const {
171 return url == other.url && hash == other.hash && max_size == other.max_size;
172 }
173
174 ExternalPolicyDataUpdater::FetchJob::FetchJob(
175 ExternalPolicyDataUpdater* updater,
176 const std::string& key,
177 const ExternalPolicyDataUpdater::Request& request,
178 const ExternalPolicyDataUpdater::FetchSuccessCallback& callback)
179 : updater_(updater),
180 key_(key),
181 request_(request),
182 callback_(callback),
183 fetch_job_(NULL),
184 limited_retries_remaining_(kMaxLimitedRetries),
185 retry_soon_entry_(&kRetrySoonPolicy),
186 retry_later_entry_(&kRetryLaterPolicy),
187 retry_much_later_entry_(&kRetryMuchLaterPolicy) {
188 }
189
190 ExternalPolicyDataUpdater::FetchJob::~FetchJob() {
191 if (fetch_job_) {
192 // Cancel the fetch job in the |external_policy_data_fetcher_|.
193 updater_->external_policy_data_fetcher_->CancelJob(fetch_job_);
194 // Inform the |updater_| that the job was canceled.
195 updater_->OnJobFailed(this);
196 }
197 }
198
199 const std::string& ExternalPolicyDataUpdater::FetchJob::key() const {
200 return key_;
201 }
202
203 const ExternalPolicyDataUpdater::Request&
204 ExternalPolicyDataUpdater::FetchJob::request() const {
205 return request_;
206 }
207
208 void ExternalPolicyDataUpdater::FetchJob::Start() {
209 DCHECK(!fetch_job_);
210 // Start a fetch job in the |external_policy_data_fetcher_|. This will
211 // eventually call back to OnFetchFinished() with the result.
212 fetch_job_ = updater_->external_policy_data_fetcher_->StartJob(
213 GURL(request_.url), request_.max_size,
214 base::Bind(&ExternalPolicyDataUpdater::FetchJob::OnFetchFinished,
215 base::Unretained(this)));
216 }
217
218 void ExternalPolicyDataUpdater::FetchJob::OnFetchFinished(
219 ExternalPolicyDataFetcher::Result result,
220 scoped_ptr<std::string> data) {
221 // The fetch job in the |external_policy_data_fetcher_| is finished.
222 fetch_job_ = NULL;
223
224 switch (result) {
225 case ExternalPolicyDataFetcher::CONNECTION_INTERRUPTED:
226 // The connection was interrupted. Try again soon.
227 OnFailed(&retry_soon_entry_);
228 return;
229 case ExternalPolicyDataFetcher::NETWORK_ERROR:
230 // Another network error occurred. Try again later.
231 OnFailed(&retry_later_entry_);
232 return;
233 case ExternalPolicyDataFetcher::SERVER_ERROR:
234 // Problem at the server. Try again soon.
235 OnFailed(&retry_soon_entry_);
236 return;
237 case ExternalPolicyDataFetcher::CLIENT_ERROR:
238 // Client error. This is unlikely to go away. Try again later, and give up
239 // retrying after 3 attempts.
240 OnFailed(limited_retries_remaining_ ? &retry_later_entry_ : NULL);
241 if (limited_retries_remaining_)
242 --limited_retries_remaining_;
243 return;
244 case ExternalPolicyDataFetcher::HTTP_ERROR:
245 // Any other type of HTTP failure. Try again later.
246 OnFailed(&retry_later_entry_);
247 return;
248 case ExternalPolicyDataFetcher::MAX_SIZE_EXCEEDED:
249 // Received |data| exceeds maximum allowed size. This may be because the
250 // data being served is stale. Try again much later.
251 OnFailed(&retry_much_later_entry_);
252 return;
253 case ExternalPolicyDataFetcher::SUCCESS:
254 break;
255 }
256
257 if (base::SHA1HashString(*data) != request_.hash) {
258 // Received |data| does not match expected hash. This may be because the
259 // data being served is stale. Try again much later.
260 OnFailed(&retry_much_later_entry_);
261 return;
262 }
263
264 // If the callback rejects the data, try again much later.
265 if (!callback_.Run(*data)) {
266 OnFailed(&retry_much_later_entry_);
267 return;
268 }
269
270 // Signal success.
271 updater_->OnJobSucceeded(this);
272 }
273
274 void ExternalPolicyDataUpdater::FetchJob::OnFailed(net::BackoffEntry* entry) {
275 if (entry) {
276 entry->InformOfRequest(false);
277
278 // This function may have been invoked because the job was obsoleted and is
279 // in the process of being deleted. If this is the case, the WeakPtr will
280 // become invalid and the delayed task will never run.
281 updater_->task_runner_->PostDelayedTask(
282 FROM_HERE,
283 base::Bind(&FetchJob::Reschedule, AsWeakPtr()),
284 entry->GetTimeUntilRelease());
285 }
286
287 updater_->OnJobFailed(this);
288 }
289
290 void ExternalPolicyDataUpdater::FetchJob::Reschedule() {
291 updater_->ScheduleJob(this);
292 }
293
294 ExternalPolicyDataUpdater::ExternalPolicyDataUpdater(
295 scoped_refptr<base::SequencedTaskRunner> task_runner,
296 scoped_ptr<ExternalPolicyDataFetcher> external_policy_data_fetcher,
297 size_t max_parallel_fetches)
298 : task_runner_(task_runner),
299 external_policy_data_fetcher_(external_policy_data_fetcher.release()),
300 max_parallel_jobs_(max_parallel_fetches),
301 running_jobs_(0),
302 shutting_down_(false) {
303 DCHECK(task_runner_->RunsTasksOnCurrentThread());
304 }
305
306 ExternalPolicyDataUpdater::~ExternalPolicyDataUpdater() {
307 DCHECK(task_runner_->RunsTasksOnCurrentThread());
308 shutting_down_ = true;
309 STLDeleteValues(&job_map_);
310 }
311
312 void ExternalPolicyDataUpdater::FetchExternalData(
313 const std::string key,
314 const Request& request,
315 const FetchSuccessCallback& callback) {
316 DCHECK(task_runner_->RunsTasksOnCurrentThread());
317
318 // Check whether a job exists for this |key| already.
319 FetchJob* job = job_map_[key];
320 if (job) {
321 // If the current |job| is handling the given |request| already, nothing
322 // needs to be done.
323 if (job->request() == request)
324 return;
325
326 // Otherwise, the current |job| is obsolete. If the |job| is on the queue,
327 // its WeakPtr will be invalidated and skipped by StartNextJobs(). If |job|
328 // is currently running, it will call OnJobFailed() immediately.
329 delete job;
330 job_map_.erase(key);
331 }
332
333 // Start a new job to handle |request|.
334 job = new FetchJob(this, key, request, callback);
335 job_map_[key] = job;
336 ScheduleJob(job);
337 }
338
339 void ExternalPolicyDataUpdater::CancelExternalDataFetch(
340 const std::string& key) {
341 DCHECK(task_runner_->RunsTasksOnCurrentThread());
342
343 // If a |job| exists for this |key|, delete it. If the |job| is on the queue,
344 // its WeakPtr will be invalidated and skipped by StartNextJobs(). If |job| is
345 // currently running, it will call OnJobFailed() immediately.
346 std::map<std::string, FetchJob*>::iterator job = job_map_.find(key);
347 if (job != job_map_.end()) {
348 delete job->second;
349 job_map_.erase(job);
350 }
351 }
352
353 void ExternalPolicyDataUpdater::StartNextJobs() {
354 if (shutting_down_)
355 return;
356
357 while (running_jobs_ < max_parallel_jobs_ && !job_queue_.empty()) {
358 FetchJob* job = job_queue_.front().get();
359 job_queue_.pop();
360
361 // Some of the jobs may have been invalidated, and have to be skipped.
362 if (job) {
363 ++running_jobs_;
364 // A started job will always call OnJobSucceeded() or OnJobFailed().
365 job->Start();
366 }
367 }
368 }
369
370 void ExternalPolicyDataUpdater::ScheduleJob(FetchJob* job) {
371 DCHECK_EQ(job_map_[job->key()], job);
372
373 job_queue_.push(job->AsWeakPtr());
374
375 StartNextJobs();
376 }
377
378 void ExternalPolicyDataUpdater::OnJobSucceeded(FetchJob* job) {
379 DCHECK(running_jobs_);
380 DCHECK_EQ(job_map_[job->key()], job);
381
382 --running_jobs_;
383 job_map_.erase(job->key());
384 delete job;
385
386 StartNextJobs();
387 }
388
389 void ExternalPolicyDataUpdater::OnJobFailed(FetchJob* job) {
390 DCHECK(running_jobs_);
391 DCHECK_EQ(job_map_[job->key()], job);
392
393 --running_jobs_;
394
395 // The job is not deleted when it fails because a retry attempt may have been
396 // scheduled.
397 StartNextJobs();
398 }
399
400 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698