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

Side by Side Diff: components/policy/core/common/cloud/device_management_service.cc

Issue 1928013004: Add delayed retry to DeviceManagementService requests. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix failing tests Created 4 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/policy/core/common/cloud/device_management_service.h" 5 #include "components/policy/core/common/cloud/device_management_service.h"
6 6
7 #include <cmath>
7 #include <utility> 8 #include <utility>
8 9
9 #include "base/bind.h" 10 #include "base/bind.h"
10 #include "base/compiler_specific.h" 11 #include "base/compiler_specific.h"
11 #include "base/location.h" 12 #include "base/location.h"
12 #include "base/macros.h" 13 #include "base/macros.h"
13 #include "base/single_thread_task_runner.h" 14 #include "base/single_thread_task_runner.h"
14 #include "base/thread_task_runner_handle.h" 15 #include "base/thread_task_runner_handle.h"
15 #include "components/data_use_measurement/core/data_use_user_data.h" 16 #include "components/data_use_measurement/core/data_use_user_data.h"
16 #include "net/base/escape.h" 17 #include "net/base/escape.h"
(...skipping 10 matching lines...) Expand all
27 namespace policy { 28 namespace policy {
28 29
29 namespace { 30 namespace {
30 31
31 const char kPostContentType[] = "application/protobuf"; 32 const char kPostContentType[] = "application/protobuf";
32 33
33 const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth="; 34 const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth=";
34 const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token="; 35 const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token=";
35 36
36 // Number of times to retry on ERR_NETWORK_CHANGED errors. 37 // Number of times to retry on ERR_NETWORK_CHANGED errors.
37 const int kMaxNetworkChangedRetries = 3; 38 const int kMaxRetries = 3;
38 39
39 // HTTP Error Codes of the DM Server with their concrete meanings in the context 40 // HTTP Error Codes of the DM Server with their concrete meanings in the context
40 // of the DM Server communication. 41 // of the DM Server communication.
41 const int kSuccess = 200; 42 const int kSuccess = 200;
42 const int kInvalidArgument = 400; 43 const int kInvalidArgument = 400;
43 const int kInvalidAuthCookieOrDMToken = 401; 44 const int kInvalidAuthCookieOrDMToken = 401;
44 const int kMissingLicenses = 402; 45 const int kMissingLicenses = 402;
45 const int kDeviceManagementNotAllowed = 403; 46 const int kDeviceManagementNotAllowed = 403;
46 const int kInvalidURL = 404; // This error is not coming from the GFE. 47 const int kInvalidURL = 404; // This error is not coming from the GFE.
47 const int kInvalidSerialNumber = 405; 48 const int kInvalidSerialNumber = 405;
48 const int kDomainMismatch = 406; 49 const int kDomainMismatch = 406;
49 const int kDeviceIdConflict = 409; 50 const int kDeviceIdConflict = 409;
50 const int kDeviceNotFound = 410; 51 const int kDeviceNotFound = 410;
51 const int kPendingApproval = 412; 52 const int kPendingApproval = 412;
52 const int kInternalServerError = 500; 53 const int kInternalServerError = 500;
53 const int kServiceUnavailable = 503; 54 const int kServiceUnavailable = 503;
54 const int kPolicyNotFound = 902; 55 const int kPolicyNotFound = 902;
55 const int kDeprovisioned = 903; 56 const int kDeprovisioned = 903;
56 57
58 // Delay after first unsuccessful upload attempt. After each additional failure,
59 // the delay increases exponentially. Can be changed for testing to prevent
60 // timeouts.
61 long g_retry_delay_ms = 10000;
62
57 bool IsProxyError(const net::URLRequestStatus status) { 63 bool IsProxyError(const net::URLRequestStatus status) {
58 switch (status.error()) { 64 switch (status.error()) {
59 case net::ERR_PROXY_CONNECTION_FAILED: 65 case net::ERR_PROXY_CONNECTION_FAILED:
60 case net::ERR_TUNNEL_CONNECTION_FAILED: 66 case net::ERR_TUNNEL_CONNECTION_FAILED:
61 case net::ERR_PROXY_AUTH_UNSUPPORTED: 67 case net::ERR_PROXY_AUTH_UNSUPPORTED:
62 case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE: 68 case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE:
63 case net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED: 69 case net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED:
64 case net::ERR_PROXY_CERTIFICATE_INVALID: 70 case net::ERR_PROXY_CERTIFICATE_INVALID:
65 case net::ERR_SOCKS_CONNECTION_FAILED: 71 case net::ERR_SOCKS_CONNECTION_FAILED:
66 case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE: 72 case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
67 return true; 73 return true;
68 } 74 }
69 return false; 75 return false;
70 } 76 }
71 77
78 bool IsConnectionError(const net::URLRequestStatus status) {
79 switch (status.error()) {
80 case net::ERR_NETWORK_CHANGED:
81 case net::ERR_NAME_NOT_RESOLVED:
82 case net::ERR_INTERNET_DISCONNECTED:
83 case net::ERR_ADDRESS_UNREACHABLE:
84 case net::ERR_CONNECTION_TIMED_OUT:
85 case net::ERR_NAME_RESOLUTION_FAILED:
86 return true;
87 }
88 return false;
89 }
90
72 bool IsProtobufMimeType(const net::URLFetcher* fetcher) { 91 bool IsProtobufMimeType(const net::URLFetcher* fetcher) {
73 return fetcher->GetResponseHeaders()->HasHeaderValue( 92 return fetcher->GetResponseHeaders()->HasHeaderValue(
74 "content-type", "application/x-protobuffer"); 93 "content-type", "application/x-protobuffer");
75 } 94 }
76 95
77 bool FailedWithProxy(const net::URLFetcher* fetcher) { 96 bool FailedWithProxy(const net::URLFetcher* fetcher) {
78 if ((fetcher->GetLoadFlags() & net::LOAD_BYPASS_PROXY) != 0) { 97 if ((fetcher->GetLoadFlags() & net::LOAD_BYPASS_PROXY) != 0) {
79 // The request didn't use a proxy. 98 // The request didn't use a proxy.
80 return false; 99 return false;
81 } 100 }
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
160 void ConfigureRequest(net::URLFetcher* fetcher); 179 void ConfigureRequest(net::URLFetcher* fetcher);
161 180
162 // Returns true if this job should be retried. |fetcher| has just completed, 181 // Returns true if this job should be retried. |fetcher| has just completed,
163 // and can be inspected to determine if the request failed and should be 182 // and can be inspected to determine if the request failed and should be
164 // retried. 183 // retried.
165 bool ShouldRetry(const net::URLFetcher* fetcher); 184 bool ShouldRetry(const net::URLFetcher* fetcher);
166 185
167 // Invoked right before retrying this job. 186 // Invoked right before retrying this job.
168 void PrepareRetry(); 187 void PrepareRetry();
169 188
189 // Number of times that this job has been retried due to connection errors.
190 int retries_count() { return retries_count_; }
191
170 protected: 192 protected:
171 // DeviceManagementRequestJob: 193 // DeviceManagementRequestJob:
172 void Run() override; 194 void Run() override;
173 195
174 private: 196 private:
175 // Invokes the callback with the given error code. 197 // Invokes the callback with the given error code.
176 void ReportError(DeviceManagementStatus code); 198 void ReportError(DeviceManagementStatus code);
177 199
178 // Pointer to the service this job is associated with. 200 // Pointer to the service this job is associated with.
179 DeviceManagementService* service_; 201 DeviceManagementService* service_;
180 202
181 // Whether the BYPASS_PROXY flag should be set by ConfigureRequest(). 203 // Whether the BYPASS_PROXY flag should be set by ConfigureRequest().
182 bool bypass_proxy_; 204 bool bypass_proxy_;
183 205
184 // Number of times that this job has been retried due to ERR_NETWORK_CHANGED. 206 // Number of times that this job has been retried due to connection errors.
185 int retries_count_; 207 int retries_count_;
186 208
187 // The request context to use for this job. 209 // The request context to use for this job.
188 scoped_refptr<net::URLRequestContextGetter> request_context_; 210 scoped_refptr<net::URLRequestContextGetter> request_context_;
189 211
190 DISALLOW_COPY_AND_ASSIGN(DeviceManagementRequestJobImpl); 212 DISALLOW_COPY_AND_ASSIGN(DeviceManagementRequestJobImpl);
191 }; 213 };
192 214
193 DeviceManagementRequestJobImpl::DeviceManagementRequestJobImpl( 215 DeviceManagementRequestJobImpl::DeviceManagementRequestJobImpl(
194 JobType type, 216 JobType type,
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 bool DeviceManagementRequestJobImpl::ShouldRetry( 347 bool DeviceManagementRequestJobImpl::ShouldRetry(
326 const net::URLFetcher* fetcher) { 348 const net::URLFetcher* fetcher) {
327 if (FailedWithProxy(fetcher) && !bypass_proxy_) { 349 if (FailedWithProxy(fetcher) && !bypass_proxy_) {
328 // Retry the job if it failed due to a broken proxy, by bypassing the 350 // Retry the job if it failed due to a broken proxy, by bypassing the
329 // proxy on the next try. 351 // proxy on the next try.
330 bypass_proxy_ = true; 352 bypass_proxy_ = true;
331 return true; 353 return true;
332 } 354 }
333 355
334 // Early device policy fetches on ChromeOS and Auto-Enrollment checks are 356 // Early device policy fetches on ChromeOS and Auto-Enrollment checks are
335 // often interrupted during ChromeOS startup when network change notifications 357 // often interrupted during ChromeOS startup when network is not yet ready.
336 // are sent. Allowing the fetcher to retry once after that is enough to 358 // Allowing the fetcher to retry once after that is enough to recover; allow
337 // recover; allow it to retry up to 3 times just in case. 359 // it to retry up to 3 times just in case.
338 if (fetcher->GetStatus().error() == net::ERR_NETWORK_CHANGED && 360 if (IsConnectionError(fetcher->GetStatus()) && retries_count_ < kMaxRetries) {
339 retries_count_ < kMaxNetworkChangedRetries) {
340 ++retries_count_; 361 ++retries_count_;
341 return true; 362 return true;
342 } 363 }
343 364
344 // The request didn't fail, or the limit of retry attempts has been reached; 365 // The request didn't fail, or the limit of retry attempts has been reached;
345 // forward the result to the job owner. 366 // forward the result to the job owner.
346 return false; 367 return false;
347 } 368 }
348 369
349 void DeviceManagementRequestJobImpl::PrepareRetry() { 370 void DeviceManagementRequestJobImpl::PrepareRetry() {
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
409 430
410 DeviceManagementService::~DeviceManagementService() { 431 DeviceManagementService::~DeviceManagementService() {
411 // All running jobs should have been cancelled by now. 432 // All running jobs should have been cancelled by now.
412 DCHECK(pending_jobs_.empty()); 433 DCHECK(pending_jobs_.empty());
413 DCHECK(queued_jobs_.empty()); 434 DCHECK(queued_jobs_.empty());
414 } 435 }
415 436
416 DeviceManagementRequestJob* DeviceManagementService::CreateJob( 437 DeviceManagementRequestJob* DeviceManagementService::CreateJob(
417 DeviceManagementRequestJob::JobType type, 438 DeviceManagementRequestJob::JobType type,
418 const scoped_refptr<net::URLRequestContextGetter>& request_context) { 439 const scoped_refptr<net::URLRequestContextGetter>& request_context) {
440 DCHECK(thread_checker_.CalledOnValidThread());
441
419 return new DeviceManagementRequestJobImpl( 442 return new DeviceManagementRequestJobImpl(
420 type, 443 type,
421 configuration_->GetAgentParameter(), 444 configuration_->GetAgentParameter(),
422 configuration_->GetPlatformParameter(), 445 configuration_->GetPlatformParameter(),
423 this, 446 this,
424 request_context); 447 request_context);
425 } 448 }
426 449
427 void DeviceManagementService::ScheduleInitialization( 450 void DeviceManagementService::ScheduleInitialization(
428 int64_t delay_milliseconds) { 451 int64_t delay_milliseconds) {
452 DCHECK(thread_checker_.CalledOnValidThread());
453
429 if (initialized_) 454 if (initialized_)
430 return; 455 return;
431 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( 456 task_runner_->PostDelayedTask(
432 FROM_HERE, base::Bind(&DeviceManagementService::Initialize, 457 FROM_HERE, base::Bind(&DeviceManagementService::Initialize,
433 weak_ptr_factory_.GetWeakPtr()), 458 weak_ptr_factory_.GetWeakPtr()),
434 base::TimeDelta::FromMilliseconds(delay_milliseconds)); 459 base::TimeDelta::FromMilliseconds(delay_milliseconds));
435 } 460 }
436 461
437 void DeviceManagementService::Initialize() { 462 void DeviceManagementService::Initialize() {
438 if (initialized_) 463 if (initialized_)
Andrew T Wilson (Slow) 2016/05/10 17:58:32 Add a thread_checker_.CalledOnValidThread() to thi
Marton Hunyady 2016/05/11 09:40:08 Done.
439 return; 464 return;
440 initialized_ = true; 465 initialized_ = true;
441 466
442 while (!queued_jobs_.empty()) { 467 while (!queued_jobs_.empty()) {
443 StartJob(queued_jobs_.front()); 468 StartJob(queued_jobs_.front());
444 queued_jobs_.pop_front(); 469 queued_jobs_.pop_front();
445 } 470 }
446 } 471 }
447 472
448 void DeviceManagementService::Shutdown() { 473 void DeviceManagementService::Shutdown() {
474 DCHECK(thread_checker_.CalledOnValidThread());
Andrew T Wilson (Slow) 2016/05/10 17:58:33 This is fine as-is, but I do wonder if perhaps we
Marton Hunyady 2016/05/11 09:40:08 Done, I invalidated the pointers. (However, this m
449 for (JobFetcherMap::iterator job(pending_jobs_.begin()); 475 for (JobFetcherMap::iterator job(pending_jobs_.begin());
450 job != pending_jobs_.end(); 476 job != pending_jobs_.end();
451 ++job) { 477 ++job) {
452 delete job->first; 478 delete job->first;
453 queued_jobs_.push_back(job->second); 479 queued_jobs_.push_back(job->second);
454 } 480 }
455 pending_jobs_.clear(); 481 pending_jobs_.clear();
456 } 482 }
457 483
458 DeviceManagementService::DeviceManagementService( 484 DeviceManagementService::DeviceManagementService(
459 std::unique_ptr<Configuration> configuration) 485 std::unique_ptr<Configuration> configuration)
460 : configuration_(std::move(configuration)), 486 : configuration_(std::move(configuration)),
461 initialized_(false), 487 initialized_(false),
488 task_runner_(base::ThreadTaskRunnerHandle::Get()),
462 weak_ptr_factory_(this) { 489 weak_ptr_factory_(this) {
463 DCHECK(configuration_); 490 DCHECK(configuration_);
464 } 491 }
465 492
466 void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job) { 493 void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job) {
494 DCHECK(thread_checker_.CalledOnValidThread());
495
467 std::string server_url = GetServerUrl(); 496 std::string server_url = GetServerUrl();
468 net::URLFetcher* fetcher = 497 net::URLFetcher* fetcher =
469 net::URLFetcher::Create(kURLFetcherID, job->GetURL(server_url), 498 net::URLFetcher::Create(kURLFetcherID, job->GetURL(server_url),
470 net::URLFetcher::POST, this).release(); 499 net::URLFetcher::POST, this).release();
471 data_use_measurement::DataUseUserData::AttachToFetcher( 500 data_use_measurement::DataUseUserData::AttachToFetcher(
472 fetcher, data_use_measurement::DataUseUserData::POLICY); 501 fetcher, data_use_measurement::DataUseUserData::POLICY);
473 job->ConfigureRequest(fetcher); 502 job->ConfigureRequest(fetcher);
474 pending_jobs_[fetcher] = job; 503 pending_jobs_[fetcher] = job;
475 fetcher->Start(); 504 fetcher->Start();
476 } 505 }
477 506
478 std::string DeviceManagementService::GetServerUrl() { 507 std::string DeviceManagementService::GetServerUrl() {
508 DCHECK(thread_checker_.CalledOnValidThread());
479 return configuration_->GetServerUrl(); 509 return configuration_->GetServerUrl();
480 } 510 }
481 511
512 // static
513 void DeviceManagementService::SetRetryDelayForTesting(long retryDelayMs) {
Andrew T Wilson (Slow) 2016/05/10 17:58:33 nit: retry_delay_ms
Marton Hunyady 2016/05/11 09:40:08 Done.
514 CHECK_GE(retryDelayMs, 0);
515 g_retry_delay_ms = retryDelayMs;
516 }
517
482 void DeviceManagementService::OnURLFetchComplete( 518 void DeviceManagementService::OnURLFetchComplete(
483 const net::URLFetcher* source) { 519 const net::URLFetcher* source) {
484 JobFetcherMap::iterator entry(pending_jobs_.find(source)); 520 JobFetcherMap::iterator entry(pending_jobs_.find(source));
485 if (entry == pending_jobs_.end()) { 521 if (entry == pending_jobs_.end()) {
486 NOTREACHED() << "Callback from foreign URL fetcher"; 522 NOTREACHED() << "Callback from foreign URL fetcher";
487 return; 523 return;
488 } 524 }
489 525
490 DeviceManagementRequestJobImpl* job = entry->second; 526 DeviceManagementRequestJobImpl* job = entry->second;
491 pending_jobs_.erase(entry); 527 pending_jobs_.erase(entry);
492 528
493 if (job->ShouldRetry(source)) { 529 if (job->ShouldRetry(source)) {
494 VLOG(1) << "Retrying dmserver request.";
495 job->PrepareRetry(); 530 job->PrepareRetry();
496 StartJob(job); 531 int delay = g_retry_delay_ms * (int)exp2(job->retries_count() - 1);
Andrew T Wilson (Slow) 2016/05/10 17:58:33 direct cast is bad, use static_cast. Also, just c
Marton Hunyady 2016/05/11 09:40:08 Done.
532 LOG(WARNING) << "Dmserver request failed, retrying in " << delay / 1000
533 << "s.";
534 task_runner_->PostDelayedTask(
535 FROM_HERE, base::Bind(&DeviceManagementService::StartJob,
536 weak_ptr_factory_.GetWeakPtr(), job),
537 base::TimeDelta::FromMilliseconds(delay));
497 } else { 538 } else {
498 std::string data; 539 std::string data;
499 source->GetResponseAsString(&data); 540 source->GetResponseAsString(&data);
500 job->HandleResponse(source->GetStatus(), source->GetResponseCode(), 541 job->HandleResponse(source->GetStatus(), source->GetResponseCode(),
501 source->GetCookies(), data); 542 source->GetCookies(), data);
502 } 543 }
503 delete source; 544 delete source;
504 } 545 }
505 546
506 void DeviceManagementService::AddJob(DeviceManagementRequestJobImpl* job) { 547 void DeviceManagementService::AddJob(DeviceManagementRequestJobImpl* job) {
(...skipping 14 matching lines...) Expand all
521 } 562 }
522 } 563 }
523 564
524 const JobQueue::iterator elem = 565 const JobQueue::iterator elem =
525 std::find(queued_jobs_.begin(), queued_jobs_.end(), job); 566 std::find(queued_jobs_.begin(), queued_jobs_.end(), job);
526 if (elem != queued_jobs_.end()) 567 if (elem != queued_jobs_.end())
527 queued_jobs_.erase(elem); 568 queued_jobs_.erase(elem);
528 } 569 }
529 570
530 } // namespace policy 571 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698