Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |