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

Side by Side Diff: chrome/browser/policy/component_cloud_policy_updater.cc

Issue 12189011: Split up chrome/browser/policy subdirectory (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase, add chrome/browser/chromeos/policy/OWNERS Created 7 years, 9 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 | 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/component_cloud_policy_updater.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/stl_util.h"
14 #include "chrome/browser/policy/component_cloud_policy_store.h"
15 #include "chrome/browser/policy/proto/chrome_extension_policy.pb.h"
16 #include "chrome/browser/policy/proto/device_management_backend.pb.h"
17 #include "googleurl/src/gurl.h"
18 #include "net/base/backoff_entry.h"
19 #include "net/base/load_flags.h"
20 #include "net/base/net_errors.h"
21 #include "net/url_request/url_fetcher.h"
22 #include "net/url_request/url_fetcher_delegate.h"
23 #include "net/url_request/url_request_context_getter.h"
24 #include "net/url_request/url_request_status.h"
25
26 namespace em = enterprise_management;
27
28 namespace policy {
29
30 namespace {
31
32 // The maximum size of the serialized policy protobuf.
33 const size_t kPolicyProtoMaxSize = 16 * 1024;
34
35 // The maximum size of the downloaded policy data.
36 const int64 kPolicyDataMaxSize = 5 * 1024 * 1024;
37
38 // Policies for exponential backoff of failed requests. There are 3 policies,
39 // corresponding to the 3 RetrySchedule enum values below.
40
41 // For temporary errors (HTTP 500, RST, etc).
42 const net::BackoffEntry::Policy kRetrySoonPolicy = {
43 // Number of initial errors to ignore before starting to back off.
44 0,
45
46 // Initial delay in ms: 60 seconds.
47 1000 * 60,
48
49 // Factor by which the waiting time is multiplied.
50 2,
51
52 // Fuzzing percentage; this spreads delays randomly between 80% and 100%
53 // of the calculated time.
54 0.20,
55
56 // Maximum delay in ms: 12 hours.
57 1000 * 60 * 60 * 12,
58
59 // When to discard an entry: never.
60 -1,
61
62 // |always_use_initial_delay|; false means that the initial delay is
63 // applied after the first error, and starts backing off from there.
64 false,
65 };
66
67 // For other errors (request failed, server errors).
68 const net::BackoffEntry::Policy kRetryLaterPolicy = {
69 // Number of initial errors to ignore before starting to back off.
70 0,
71
72 // Initial delay in ms: 1 hour.
73 1000 * 60 * 60,
74
75 // Factor by which the waiting time is multiplied.
76 2,
77
78 // Fuzzing percentage; this spreads delays randomly between 80% and 100%
79 // of the calculated time.
80 0.20,
81
82 // Maximum delay in ms: 12 hours.
83 1000 * 60 * 60 * 12,
84
85 // When to discard an entry: never.
86 -1,
87
88 // |always_use_initial_delay|; false means that the initial delay is
89 // applied after the first error, and starts backing off from there.
90 false,
91 };
92
93 // When the data fails validation (maybe because the policy URL and the data
94 // served at that URL are out of sync). This essentially retries every 12 hours,
95 // with some random jitter.
96 const net::BackoffEntry::Policy kRetryMuchLaterPolicy = {
97 // Number of initial errors to ignore before starting to back off.
98 0,
99
100 // Initial delay in ms: 12 hours.
101 1000 * 60 * 60 * 12,
102
103 // Factor by which the waiting time is multiplied.
104 2,
105
106 // Fuzzing percentage; this spreads delays randomly between 80% and 100%
107 // of the calculated time.
108 0.20,
109
110 // Maximum delay in ms: 12 hours.
111 1000 * 60 * 60 * 12,
112
113 // When to discard an entry: never.
114 -1,
115
116 // |always_use_initial_delay|; false means that the initial delay is
117 // applied after the first error, and starts backing off from there.
118 false,
119 };
120
121 // Maximum number of retries for requests that aren't likely to get a
122 // different response (e.g. HTTP 4xx replies).
123 const int kMaxLimitedRetries = 3;
124
125 } // namespace
126
127 // Each FetchJob contains the data about a particular component, and handles
128 // the downloading of its corresponding data. These objects are owned by the
129 // updater, and the updater always outlives FetchJobs.
130 // A FetchJob can be scheduled for a retry later, but its data never changes.
131 // If the ExternalPolicyData for a particular component changes then a new
132 // FetchJob is created, and the previous one is discarded.
133 class ComponentCloudPolicyUpdater::FetchJob
134 : public base::SupportsWeakPtr<FetchJob>,
135 public net::URLFetcherDelegate {
136 public:
137 FetchJob(ComponentCloudPolicyUpdater* updater,
138 const PolicyNamespace& ns,
139 const std::string& serialized_response,
140 const em::ExternalPolicyData& data);
141 virtual ~FetchJob();
142
143 const PolicyNamespace& policy_namespace() const { return ns_; }
144
145 // Returns true if |other| equals |data_|.
146 bool ParamsEquals(const em::ExternalPolicyData& other);
147
148 void StartJob();
149
150 // URLFetcherDelegate implementation:
151 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
152 virtual void OnURLFetchDownloadProgress(const net::URLFetcher* source,
153 int64 current,
154 int64 total) OVERRIDE;
155
156 private:
157 void OnSucceeded();
158 void OnFailed(net::BackoffEntry* backoff_entry);
159 void Schedule();
160
161 // Always valid as long as |this| is alive.
162 ComponentCloudPolicyUpdater* updater_;
163
164 const PolicyNamespace ns_;
165 const std::string serialized_response_;
166 const em::ExternalPolicyData data_;
167
168 // If |fetcher_| exists then |this| is the current job, and must call either
169 // OnSucceeded or OnFailed.
170 scoped_ptr<net::URLFetcher> fetcher_;
171
172 // Some errors should trigger a limited number of retries, even with backoff.
173 // This counts the number of such retries, to stop retrying once the limit
174 // is reached.
175 int limited_retries_count_;
176
177 // Various delays to retry a failed download, depending on the failure reason.
178 net::BackoffEntry retry_soon_entry_;
179 net::BackoffEntry retry_later_entry_;
180 net::BackoffEntry retry_much_later_entry_;
181
182 DISALLOW_COPY_AND_ASSIGN(FetchJob);
183 };
184
185 ComponentCloudPolicyUpdater::FetchJob::FetchJob(
186 ComponentCloudPolicyUpdater* updater,
187 const PolicyNamespace& ns,
188 const std::string& serialized_response,
189 const em::ExternalPolicyData& data)
190 : updater_(updater),
191 ns_(ns),
192 serialized_response_(serialized_response),
193 data_(data),
194 limited_retries_count_(0),
195 retry_soon_entry_(&kRetrySoonPolicy),
196 retry_later_entry_(&kRetryLaterPolicy),
197 retry_much_later_entry_(&kRetryMuchLaterPolicy) {}
198
199 ComponentCloudPolicyUpdater::FetchJob::~FetchJob() {
200 if (fetcher_) {
201 fetcher_.reset();
202 // This is the current job; inform the updater that it was cancelled.
203 updater_->OnJobFailed(this);
204 }
205 }
206
207 bool ComponentCloudPolicyUpdater::FetchJob::ParamsEquals(
208 const em::ExternalPolicyData& other) {
209 return data_.download_url() == other.download_url() &&
210 data_.secure_hash() == other.secure_hash() &&
211 data_.download_auth_method() == other.download_auth_method();
212 }
213
214 void ComponentCloudPolicyUpdater::FetchJob::StartJob() {
215 fetcher_.reset(net::URLFetcher::Create(
216 0, GURL(data_.download_url()), net::URLFetcher::GET, this));
217 fetcher_->SetRequestContext(updater_->request_context_);
218 fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE |
219 net::LOAD_DISABLE_CACHE |
220 net::LOAD_DO_NOT_SAVE_COOKIES |
221 net::LOAD_IS_DOWNLOAD |
222 net::LOAD_DO_NOT_SEND_COOKIES |
223 net::LOAD_DO_NOT_SEND_AUTH_DATA);
224 fetcher_->SetAutomaticallyRetryOnNetworkChanges(3);
225 fetcher_->Start();
226 }
227
228 void ComponentCloudPolicyUpdater::FetchJob::OnURLFetchComplete(
229 const net::URLFetcher* source) {
230 DCHECK(source == fetcher_.get());
231
232 const net::URLRequestStatus status = source->GetStatus();
233 if (status.status() != net::URLRequestStatus::SUCCESS) {
234 if (status.error() == net::ERR_CONNECTION_RESET ||
235 status.error() == net::ERR_TEMPORARILY_THROTTLED) {
236 // The connection was interrupted; try again soon.
237 OnFailed(&retry_soon_entry_);
238 return;
239 } else {
240 // Other network error; try again later.
241 OnFailed(&retry_later_entry_);
242 return;
243 }
244 } else {
245 // Status is success; inspect the HTTP response code.
246 if (source->GetResponseCode() >= 500) {
247 // Problem at the server; try again soon.
248 OnFailed(&retry_soon_entry_);
249 return;
250 } else if (source->GetResponseCode() >= 400) {
251 // Client error; this is unlikely to go away. Retry later, and give up
252 // retrying after 3 attempts.
253 OnFailed(limited_retries_count_ < kMaxLimitedRetries ? &retry_later_entry_
254 : NULL);
255 limited_retries_count_++;
256 return;
257 } else if (source->GetResponseCode() != 200) {
258 // Other HTTP failure; try again later.
259 OnFailed(&retry_later_entry_);
260 return;
261 }
262 }
263
264 std::string data;
265 if (!source->GetResponseAsString(&data) ||
266 static_cast<int64>(data.size()) > kPolicyDataMaxSize ||
267 !updater_->store_->Store(
268 ns_, serialized_response_, data_.secure_hash(), data)) {
269 // Failed to retrieve |data|, or it exceeds the size limit, or it failed
270 // validation. This may be a temporary error at the download URL.
271 OnFailed(&retry_much_later_entry_);
272 return;
273 }
274
275 OnSucceeded();
276 }
277
278 void ComponentCloudPolicyUpdater::FetchJob::OnURLFetchDownloadProgress(
279 const net::URLFetcher* source,
280 int64 current,
281 int64 total) {
282 DCHECK(source == fetcher_.get());
283 // Reject the data if it exceeds the size limit. The content length is in
284 // |total|, and it may be -1 when not known.
285 if (current > kPolicyDataMaxSize || total > kPolicyDataMaxSize)
286 OnFailed(&retry_much_later_entry_);
287 }
288
289 void ComponentCloudPolicyUpdater::FetchJob::OnSucceeded() {
290 fetcher_.reset();
291 updater_->OnJobSucceeded(this);
292 }
293
294 void ComponentCloudPolicyUpdater::FetchJob::OnFailed(net::BackoffEntry* entry) {
295 fetcher_.reset();
296
297 if (entry) {
298 entry->InformOfRequest(false);
299
300 // If new ExternalPolicyData for this component is fetched then this job
301 // will be deleted, and the retry task is invalidated. A new job using the
302 // new data will be scheduled immediately in that case.
303 updater_->task_runner_->PostDelayedTask(
304 FROM_HERE,
305 base::Bind(&FetchJob::Schedule, AsWeakPtr()),
306 entry->GetTimeUntilRelease());
307 }
308
309 updater_->OnJobFailed(this);
310 }
311
312 void ComponentCloudPolicyUpdater::FetchJob::Schedule() {
313 updater_->ScheduleJob(this);
314 }
315
316 ComponentCloudPolicyUpdater::ComponentCloudPolicyUpdater(
317 scoped_refptr<base::SequencedTaskRunner> task_runner,
318 scoped_refptr<net::URLRequestContextGetter> request_context,
319 ComponentCloudPolicyStore* store)
320 : task_runner_(task_runner),
321 request_context_(request_context),
322 store_(store),
323 shutting_down_(false) {
324 DCHECK(task_runner_->RunsTasksOnCurrentThread());
325 }
326
327 ComponentCloudPolicyUpdater::~ComponentCloudPolicyUpdater() {
328 DCHECK(CalledOnValidThread());
329 shutting_down_ = true;
330 STLDeleteValues(&fetch_jobs_);
331 }
332
333 void ComponentCloudPolicyUpdater::UpdateExternalPolicy(
334 scoped_ptr<em::PolicyFetchResponse> response) {
335 DCHECK(CalledOnValidThread());
336
337 // Keep a serialized copy of |response|, to cache it later.
338 // The policy is also rejected if it exceeds the maximum size.
339 std::string serialized_response;
340 if (!response->SerializeToString(&serialized_response) ||
341 serialized_response.size() > kPolicyProtoMaxSize) {
342 return;
343 }
344
345 // Validate the policy before doing anything else.
346 PolicyNamespace ns;
347 em::ExternalPolicyData data;
348 if (!store_->ValidatePolicy(response.Pass(), &ns, &data)) {
349 LOG(ERROR) << "Failed to validate component policy fetched from DMServer";
350 return;
351 }
352
353 // Maybe the data for this hash has already been downloaded and cached.
354 if (data.has_secure_hash() &&
355 data.secure_hash() == store_->GetCachedHash(ns)) {
356 return;
357 }
358
359 // TODO(joaodasilva): implement the other two auth methods.
360 if (data.download_auth_method() != em::ExternalPolicyData::NONE)
361 return;
362
363 // Check for an existing job for this component.
364 FetchJob* job = fetch_jobs_[ns];
365 if (job) {
366 // Check if this data has already been seen.
367 if (job->ParamsEquals(data))
368 return;
369
370 // The existing job is obsolete, cancel it. If |job| is in the job queue
371 // then its WeakPtr will be invalided and skipped in the next StartNextJob.
372 // If |job| is the current job then it will immediately call OnJobFailed.
373 delete job;
374 fetch_jobs_.erase(ns);
375 }
376
377 if (data.download_url().empty()) {
378 // There is no policy for this component, or the policy has been removed.
379 store_->Delete(ns);
380 } else {
381 // Start a new job with the new or updated data.
382 job = new FetchJob(this, ns, serialized_response, data);
383 fetch_jobs_[ns] = job;
384 ScheduleJob(job);
385 }
386 }
387
388 void ComponentCloudPolicyUpdater::ScheduleJob(FetchJob* job) {
389 job_queue_.push(job->AsWeakPtr());
390 // The job at the front of the queue is always the current job. If |job| is
391 // at the front then start it immediately. An invalid job is never at the
392 // front; as soon as it becomes invalidated it will call OnJobFailed() and
393 // flush the queue.
394 if (job == job_queue_.front().get())
395 StartNextJob();
396 }
397
398 void ComponentCloudPolicyUpdater::StartNextJob() {
399 // Some of the jobs may have been invalidated, and have to be skipped.
400 while (!job_queue_.empty() && !job_queue_.front())
401 job_queue_.pop();
402
403 // A started job will always call OnJobSucceeded or OnJobFailed.
404 if (!job_queue_.empty() && !shutting_down_)
405 job_queue_.front()->StartJob();
406 }
407
408 void ComponentCloudPolicyUpdater::OnJobSucceeded(FetchJob* job) {
409 DCHECK(fetch_jobs_[job->policy_namespace()] == job);
410 DCHECK(!job_queue_.empty() && job_queue_.front() == job);
411 fetch_jobs_.erase(job->policy_namespace());
412 delete job;
413 job_queue_.pop();
414 StartNextJob();
415 }
416
417 void ComponentCloudPolicyUpdater::OnJobFailed(FetchJob* job) {
418 DCHECK(fetch_jobs_[job->policy_namespace()] == job);
419 DCHECK(!job_queue_.empty() && job_queue_.front() == job);
420 // The job isn't deleted when it fails because a retry attempt may have been
421 // scheduled. It's also kept so that UpdateExternalPolicy() can see the
422 // current data, and avoid a new fetch if the data hasn't changed.
423 job_queue_.pop();
424 StartNextJob();
425 }
426
427 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698