| Index: chrome/browser/policy/device_management_service.cc
|
| diff --git a/chrome/browser/policy/device_management_service.cc b/chrome/browser/policy/device_management_service.cc
|
| index b218e31d0e00fcd7eda7f0c4e0d7597b0476159e..3fc7301d4f6249c882e51120d81760b7bc9e3a3e 100644
|
| --- a/chrome/browser/policy/device_management_service.cc
|
| +++ b/chrome/browser/policy/device_management_service.cc
|
| @@ -53,6 +53,9 @@ const char kPostContentType[] = "application/protobuf";
|
| const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth=";
|
| const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token=";
|
|
|
| +// Number of times to retry on ERR_NETWORK_CHANGED errors.
|
| +const int kMaxNetworkChangedRetries = 3;
|
| +
|
| // HTTP Error Codes of the DM Server with their concrete meanings in the context
|
| // of the DM Server communication.
|
| const int kSuccess = 200;
|
| @@ -90,11 +93,38 @@ bool IsProxyError(const net::URLRequestStatus status) {
|
| return false;
|
| }
|
|
|
| -bool IsProtobufMimeType(const net::URLFetcher* source) {
|
| - return source->GetResponseHeaders()->HasHeaderValue(
|
| +bool IsProtobufMimeType(const net::URLFetcher* fetcher) {
|
| + return fetcher->GetResponseHeaders()->HasHeaderValue(
|
| "content-type", "application/x-protobuffer");
|
| }
|
|
|
| +bool FailedWithProxy(const net::URLFetcher* fetcher) {
|
| + if ((fetcher->GetLoadFlags() & net::LOAD_BYPASS_PROXY) != 0) {
|
| + // The request didn't use a proxy.
|
| + return false;
|
| + }
|
| +
|
| + if (!fetcher->GetStatus().is_success() &&
|
| + IsProxyError(fetcher->GetStatus())) {
|
| + LOG(WARNING) << "Proxy failed while contacting dmserver.";
|
| + return true;
|
| + }
|
| +
|
| + if (fetcher->GetStatus().is_success() &&
|
| + fetcher->GetResponseCode() == kSuccess &&
|
| + fetcher->WasFetchedViaProxy() &&
|
| + !IsProtobufMimeType(fetcher)) {
|
| + // The proxy server can be misconfigured but pointing to an existing
|
| + // server that replies to requests. Try to recover if a successful
|
| + // request that went through a proxy returns an unexpected mime type.
|
| + LOG(WARNING) << "Got bad mime-type in response from dmserver that was "
|
| + << "fetched via a proxy.";
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| const char* UserAffiliationToString(UserAffiliation affiliation) {
|
| switch (affiliation) {
|
| case USER_AFFILIATION_MANAGED:
|
| @@ -275,6 +305,14 @@ class DeviceManagementRequestJobImpl : public DeviceManagementRequestJob {
|
| // Configures the fetcher, setting up payload and headers.
|
| void ConfigureRequest(net::URLFetcher* fetcher);
|
|
|
| + // Returns true if this job should be retried. |fetcher| has just completed,
|
| + // and can be inspected to determine if the request failed and should be
|
| + // retried.
|
| + bool ShouldRetry(const net::URLFetcher* fetcher);
|
| +
|
| + // Invoked right before retrying this job.
|
| + void PrepareRetry();
|
| +
|
| protected:
|
| // DeviceManagementRequestJob:
|
| virtual void Run() OVERRIDE;
|
| @@ -286,6 +324,12 @@ class DeviceManagementRequestJobImpl : public DeviceManagementRequestJob {
|
| // Pointer to the service this job is associated with.
|
| DeviceManagementService* service_;
|
|
|
| + // Whether the BYPASS_PROXY flag should be set by ConfigureRequest().
|
| + bool bypass_proxy_;
|
| +
|
| + // Number of times that this job has been retried due to ERR_NETWORK_CHANGED.
|
| + int retries_count_;
|
| +
|
| DISALLOW_COPY_AND_ASSIGN(DeviceManagementRequestJobImpl);
|
| };
|
|
|
| @@ -293,7 +337,9 @@ DeviceManagementRequestJobImpl::DeviceManagementRequestJobImpl(
|
| JobType type,
|
| DeviceManagementService* service)
|
| : DeviceManagementRequestJob(type),
|
| - service_(service) {}
|
| + service_(service),
|
| + bypass_proxy_(false),
|
| + retries_count_(0) {}
|
|
|
| DeviceManagementRequestJobImpl::~DeviceManagementRequestJobImpl() {
|
| service_->RemoveJob(this);
|
| @@ -389,6 +435,10 @@ GURL DeviceManagementRequestJobImpl::GetURL(
|
|
|
| void DeviceManagementRequestJobImpl::ConfigureRequest(
|
| net::URLFetcher* fetcher) {
|
| + fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
|
| + net::LOAD_DO_NOT_SAVE_COOKIES |
|
| + net::LOAD_DISABLE_CACHE |
|
| + (bypass_proxy_ ? net::LOAD_BYPASS_PROXY : 0));
|
| std::string payload;
|
| CHECK(request_.SerializeToString(&payload));
|
| fetcher->SetUploadData(kPostContentType, payload);
|
| @@ -400,6 +450,35 @@ void DeviceManagementRequestJobImpl::ConfigureRequest(
|
| fetcher->SetExtraRequestHeaders(extra_headers);
|
| }
|
|
|
| +bool DeviceManagementRequestJobImpl::ShouldRetry(
|
| + const net::URLFetcher* fetcher) {
|
| + if (FailedWithProxy(fetcher) && !bypass_proxy_) {
|
| + // Retry the job if it failed due to a broken proxy, by bypassing the
|
| + // proxy on the next try.
|
| + bypass_proxy_ = true;
|
| + return true;
|
| + }
|
| +
|
| + // Early device policy fetches on ChromeOS and Auto-Enrollment checks are
|
| + // often interrupted during ChromeOS startup when network change notifications
|
| + // are sent. Allowing the fetcher to retry once after that is enough to
|
| + // recover; allow it to retry up to 3 times just in case.
|
| + if (fetcher->GetStatus().error() == net::ERR_NETWORK_CHANGED &&
|
| + retries_count_ < kMaxNetworkChangedRetries) {
|
| + ++retries_count_;
|
| + return true;
|
| + }
|
| +
|
| + // The request didn't fail, or the limit of retry attempts has been reached;
|
| + // forward the result to the job owner.
|
| + return false;
|
| +}
|
| +
|
| +void DeviceManagementRequestJobImpl::PrepareRetry() {
|
| + if (!retry_callback_.is_null())
|
| + retry_callback_.Run(this);
|
| +}
|
| +
|
| void DeviceManagementRequestJobImpl::ReportError(DeviceManagementStatus code) {
|
| em::DeviceManagementResponse dummy_response;
|
| callback_.Run(code, dummy_response);
|
| @@ -441,6 +520,11 @@ DeviceManagementRequestJob::DeviceManagementRequestJob(JobType type) {
|
| AddParameter(dm_protocol::kParamPlatform, GetPlatformString());
|
| }
|
|
|
| +void DeviceManagementRequestJob::SetRetryCallback(
|
| + const RetryCallback& retry_callback) {
|
| + retry_callback_ = retry_callback;
|
| +}
|
| +
|
| void DeviceManagementRequestJob::Start(const Callback& callback) {
|
| callback_ = callback;
|
| Run();
|
| @@ -481,7 +565,7 @@ void DeviceManagementService::Initialize() {
|
| initialized_ = true;
|
|
|
| while (!queued_jobs_.empty()) {
|
| - StartJob(queued_jobs_.front(), false);
|
| + StartJob(queued_jobs_.front());
|
| queued_jobs_.pop_front();
|
| }
|
| }
|
| @@ -503,21 +587,10 @@ DeviceManagementService::DeviceManagementService(
|
| ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
|
| }
|
|
|
| -void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job,
|
| - bool bypass_proxy) {
|
| +void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job) {
|
| net::URLFetcher* fetcher = net::URLFetcher::Create(
|
| 0, job->GetURL(server_url_), net::URLFetcher::POST, this);
|
| - fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
|
| - net::LOAD_DO_NOT_SAVE_COOKIES |
|
| - net::LOAD_DISABLE_CACHE |
|
| - (bypass_proxy ? net::LOAD_BYPASS_PROXY : 0));
|
| fetcher->SetRequestContext(request_context_getter_.get());
|
| - // Early device policy fetches on ChromeOS and Auto-Enrollment checks are
|
| - // often interrupted during ChromeOS startup when network change notifications
|
| - // are sent. Allowing the fetcher to retry once after that is enough to
|
| - // recover; allow it to retry up to 3 times just in case.
|
| - // http://crosbug.com/16114
|
| - fetcher->SetAutomaticallyRetryOnNetworkChanges(3);
|
| job->ConfigureRequest(fetcher);
|
| pending_jobs_[fetcher] = job;
|
| fetcher->Start();
|
| @@ -534,31 +607,10 @@ void DeviceManagementService::OnURLFetchComplete(
|
| DeviceManagementRequestJobImpl* job = entry->second;
|
| pending_jobs_.erase(entry);
|
|
|
| - // Retry the job if it failed due to a broken proxy, by bypassing the
|
| - // proxy on the next try. Don't retry if this URLFetcher already bypassed
|
| - // the proxy.
|
| - bool retry = false;
|
| - if ((source->GetLoadFlags() & net::LOAD_BYPASS_PROXY) == 0) {
|
| - if (!source->GetStatus().is_success() &&
|
| - IsProxyError(source->GetStatus())) {
|
| - LOG(WARNING) << "Proxy failed while contacting dmserver.";
|
| - retry = true;
|
| - } else if (source->GetStatus().is_success() &&
|
| - source->GetResponseCode() == kSuccess &&
|
| - source->WasFetchedViaProxy() &&
|
| - !IsProtobufMimeType(source)) {
|
| - // The proxy server can be misconfigured but pointing to an existing
|
| - // server that replies to requests. Try to recover if a successful
|
| - // request that went through a proxy returns an unexpected mime type.
|
| - LOG(WARNING) << "Got bad mime-type in response from dmserver that was "
|
| - << "fetched via a proxy.";
|
| - retry = true;
|
| - }
|
| - }
|
| -
|
| - if (retry) {
|
| - LOG(WARNING) << "Retrying dmserver request without using a proxy.";
|
| - StartJob(job, true);
|
| + if (job->ShouldRetry(source)) {
|
| + VLOG(1) << "Retrying dmserver request.";
|
| + job->PrepareRetry();
|
| + StartJob(job);
|
| } else {
|
| std::string data;
|
| source->GetResponseAsString(&data);
|
| @@ -570,7 +622,7 @@ void DeviceManagementService::OnURLFetchComplete(
|
|
|
| void DeviceManagementService::AddJob(DeviceManagementRequestJobImpl* job) {
|
| if (initialized_)
|
| - StartJob(job, false);
|
| + StartJob(job);
|
| else
|
| queued_jobs_.push_back(job);
|
| }
|
|
|