| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/policy/device_management_service.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/compiler_specific.h" | |
| 11 #include "base/message_loop.h" | |
| 12 #include "base/message_loop_proxy.h" | |
| 13 #include "base/stringprintf.h" | |
| 14 #include "base/sys_info.h" | |
| 15 #include "chrome/browser/browser_process.h" | |
| 16 #include "chrome/browser/net/basic_http_user_agent_settings.h" | |
| 17 #include "chrome/browser/net/chrome_net_log.h" | |
| 18 #include "chrome/common/chrome_version_info.h" | |
| 19 #include "content/public/browser/browser_thread.h" | |
| 20 #include "content/public/common/content_client.h" | |
| 21 #include "googleurl/src/gurl.h" | |
| 22 #include "net/base/escape.h" | |
| 23 #include "net/base/host_resolver.h" | |
| 24 #include "net/base/load_flags.h" | |
| 25 #include "net/base/net_errors.h" | |
| 26 #include "net/base/ssl_config_service_defaults.h" | |
| 27 #include "net/cookies/cookie_monster.h" | |
| 28 #include "net/http/http_network_layer.h" | |
| 29 #include "net/http/http_response_headers.h" | |
| 30 #include "net/proxy/proxy_service.h" | |
| 31 #include "net/url_request/url_fetcher.h" | |
| 32 #include "net/url_request/url_request_context.h" | |
| 33 #include "net/url_request/url_request_context_getter.h" | |
| 34 #include "net/url_request/url_request_status.h" | |
| 35 | |
| 36 #if defined(OS_CHROMEOS) | |
| 37 #include "chrome/browser/chromeos/system/statistics_provider.h" | |
| 38 #endif | |
| 39 | |
| 40 using content::BrowserThread; | |
| 41 | |
| 42 namespace em = enterprise_management; | |
| 43 | |
| 44 namespace policy { | |
| 45 | |
| 46 namespace { | |
| 47 | |
| 48 const char kValueAgent[] = "%s %s(%s)"; | |
| 49 const char kValuePlatform[] = "%s|%s|%s"; | |
| 50 | |
| 51 const char kPostContentType[] = "application/protobuf"; | |
| 52 | |
| 53 const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth="; | |
| 54 const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token="; | |
| 55 | |
| 56 // HTTP Error Codes of the DM Server with their concrete meanings in the context | |
| 57 // of the DM Server communication. | |
| 58 const int kSuccess = 200; | |
| 59 const int kInvalidArgument = 400; | |
| 60 const int kInvalidAuthCookieOrDMToken = 401; | |
| 61 const int kMissingLicenses = 402; | |
| 62 const int kDeviceManagementNotAllowed = 403; | |
| 63 const int kInvalidURL = 404; // This error is not coming from the GFE. | |
| 64 const int kInvalidSerialNumber = 405; | |
| 65 const int kDeviceIdConflict = 409; | |
| 66 const int kDeviceNotFound = 410; | |
| 67 const int kPendingApproval = 412; | |
| 68 const int kInternalServerError = 500; | |
| 69 const int kServiceUnavailable = 503; | |
| 70 const int kPolicyNotFound = 902; // This error is not sent as HTTP status code. | |
| 71 | |
| 72 #if defined(OS_CHROMEOS) | |
| 73 // Machine info keys. | |
| 74 const char kMachineInfoHWClass[] = "hardware_class"; | |
| 75 const char kMachineInfoBoard[] = "CHROMEOS_RELEASE_BOARD"; | |
| 76 #endif | |
| 77 | |
| 78 bool IsProxyError(const net::URLRequestStatus status) { | |
| 79 switch (status.error()) { | |
| 80 case net::ERR_PROXY_CONNECTION_FAILED: | |
| 81 case net::ERR_TUNNEL_CONNECTION_FAILED: | |
| 82 case net::ERR_PROXY_AUTH_UNSUPPORTED: | |
| 83 case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE: | |
| 84 case net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED: | |
| 85 case net::ERR_PROXY_CERTIFICATE_INVALID: | |
| 86 case net::ERR_SOCKS_CONNECTION_FAILED: | |
| 87 case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE: | |
| 88 return true; | |
| 89 } | |
| 90 return false; | |
| 91 } | |
| 92 | |
| 93 bool IsProtobufMimeType(const net::URLFetcher* source) { | |
| 94 return source->GetResponseHeaders()->HasHeaderValue( | |
| 95 "content-type", "application/x-protobuffer"); | |
| 96 } | |
| 97 | |
| 98 const char* UserAffiliationToString(UserAffiliation affiliation) { | |
| 99 switch (affiliation) { | |
| 100 case USER_AFFILIATION_MANAGED: | |
| 101 return dm_protocol::kValueUserAffiliationManaged; | |
| 102 case USER_AFFILIATION_NONE: | |
| 103 return dm_protocol::kValueUserAffiliationNone; | |
| 104 } | |
| 105 NOTREACHED() << "Invalid user affiliation " << affiliation; | |
| 106 return dm_protocol::kValueUserAffiliationNone; | |
| 107 } | |
| 108 | |
| 109 const char* JobTypeToRequestType(DeviceManagementRequestJob::JobType type) { | |
| 110 switch (type) { | |
| 111 case DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT: | |
| 112 return dm_protocol::kValueRequestAutoEnrollment; | |
| 113 case DeviceManagementRequestJob::TYPE_REGISTRATION: | |
| 114 return dm_protocol::kValueRequestRegister; | |
| 115 case DeviceManagementRequestJob::TYPE_POLICY_FETCH: | |
| 116 return dm_protocol::kValueRequestPolicy; | |
| 117 case DeviceManagementRequestJob::TYPE_UNREGISTRATION: | |
| 118 return dm_protocol::kValueRequestUnregister; | |
| 119 } | |
| 120 NOTREACHED() << "Invalid job type " << type; | |
| 121 return ""; | |
| 122 } | |
| 123 | |
| 124 const std::string& GetAgentString() { | |
| 125 CR_DEFINE_STATIC_LOCAL(std::string, agent, ()); | |
| 126 if (!agent.empty()) | |
| 127 return agent; | |
| 128 | |
| 129 chrome::VersionInfo version_info; | |
| 130 agent = base::StringPrintf(kValueAgent, | |
| 131 version_info.Name().c_str(), | |
| 132 version_info.Version().c_str(), | |
| 133 version_info.LastChange().c_str()); | |
| 134 return agent; | |
| 135 } | |
| 136 | |
| 137 const std::string& GetPlatformString() { | |
| 138 CR_DEFINE_STATIC_LOCAL(std::string, platform, ()); | |
| 139 if (!platform.empty()) | |
| 140 return platform; | |
| 141 | |
| 142 std::string os_name(base::SysInfo::OperatingSystemName()); | |
| 143 std::string os_hardware(base::SysInfo::OperatingSystemArchitecture()); | |
| 144 | |
| 145 #if defined(OS_CHROMEOS) | |
| 146 chromeos::system::StatisticsProvider* provider = | |
| 147 chromeos::system::StatisticsProvider::GetInstance(); | |
| 148 | |
| 149 std::string hwclass; | |
| 150 std::string board; | |
| 151 if (!provider->GetMachineStatistic(kMachineInfoHWClass, &hwclass) || | |
| 152 !provider->GetMachineStatistic(kMachineInfoBoard, &board)) { | |
| 153 LOG(ERROR) << "Failed to get machine information"; | |
| 154 } | |
| 155 os_name += ",CrOS," + board; | |
| 156 os_hardware += "," + hwclass; | |
| 157 #endif | |
| 158 | |
| 159 std::string os_version("-"); | |
| 160 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) | |
| 161 int32 os_major_version = 0; | |
| 162 int32 os_minor_version = 0; | |
| 163 int32 os_bugfix_version = 0; | |
| 164 base::SysInfo::OperatingSystemVersionNumbers(&os_major_version, | |
| 165 &os_minor_version, | |
| 166 &os_bugfix_version); | |
| 167 os_version = base::StringPrintf("%d.%d.%d", | |
| 168 os_major_version, | |
| 169 os_minor_version, | |
| 170 os_bugfix_version); | |
| 171 #endif | |
| 172 | |
| 173 platform = base::StringPrintf(kValuePlatform, | |
| 174 os_name.c_str(), | |
| 175 os_hardware.c_str(), | |
| 176 os_version.c_str()); | |
| 177 return platform; | |
| 178 } | |
| 179 | |
| 180 // Custom request context implementation that allows to override the user agent, | |
| 181 // amongst others. Wraps a baseline request context from which we reuse the | |
| 182 // networking components. | |
| 183 class DeviceManagementRequestContext : public net::URLRequestContext { | |
| 184 public: | |
| 185 explicit DeviceManagementRequestContext(net::URLRequestContext* base_context); | |
| 186 virtual ~DeviceManagementRequestContext(); | |
| 187 | |
| 188 private: | |
| 189 BasicHttpUserAgentSettings basic_http_user_agent_settings_; | |
| 190 }; | |
| 191 | |
| 192 DeviceManagementRequestContext::DeviceManagementRequestContext( | |
| 193 net::URLRequestContext* base_context) | |
| 194 // Use sane Accept-Language and Accept-Charset values for our purposes. | |
| 195 : basic_http_user_agent_settings_("*", "*") { | |
| 196 // Share resolver, proxy service and ssl bits with the baseline context. This | |
| 197 // is important so we don't make redundant requests (e.g. when resolving proxy | |
| 198 // auto configuration). | |
| 199 set_net_log(base_context->net_log()); | |
| 200 set_host_resolver(base_context->host_resolver()); | |
| 201 set_proxy_service(base_context->proxy_service()); | |
| 202 set_ssl_config_service(base_context->ssl_config_service()); | |
| 203 | |
| 204 // Share the http session. | |
| 205 set_http_transaction_factory( | |
| 206 new net::HttpNetworkLayer( | |
| 207 base_context->http_transaction_factory()->GetSession())); | |
| 208 | |
| 209 // No cookies, please. | |
| 210 set_cookie_store(new net::CookieMonster(NULL, NULL)); | |
| 211 | |
| 212 set_http_user_agent_settings(&basic_http_user_agent_settings_); | |
| 213 } | |
| 214 | |
| 215 DeviceManagementRequestContext::~DeviceManagementRequestContext() { | |
| 216 delete http_transaction_factory(); | |
| 217 } | |
| 218 | |
| 219 // Request context holder. | |
| 220 class DeviceManagementRequestContextGetter | |
| 221 : public net::URLRequestContextGetter { | |
| 222 public: | |
| 223 explicit DeviceManagementRequestContextGetter( | |
| 224 net::URLRequestContextGetter* base_context_getter) | |
| 225 : base_context_getter_(base_context_getter) {} | |
| 226 | |
| 227 // Overridden from net::URLRequestContextGetter: | |
| 228 virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE; | |
| 229 virtual scoped_refptr<base::SingleThreadTaskRunner> | |
| 230 GetNetworkTaskRunner() const OVERRIDE; | |
| 231 | |
| 232 protected: | |
| 233 virtual ~DeviceManagementRequestContextGetter() {} | |
| 234 | |
| 235 private: | |
| 236 scoped_ptr<net::URLRequestContext> context_; | |
| 237 scoped_refptr<net::URLRequestContextGetter> base_context_getter_; | |
| 238 }; | |
| 239 | |
| 240 | |
| 241 net::URLRequestContext* | |
| 242 DeviceManagementRequestContextGetter::GetURLRequestContext() { | |
| 243 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 244 if (!context_.get()) { | |
| 245 context_.reset(new DeviceManagementRequestContext( | |
| 246 base_context_getter_->GetURLRequestContext())); | |
| 247 } | |
| 248 | |
| 249 return context_.get(); | |
| 250 } | |
| 251 | |
| 252 scoped_refptr<base::SingleThreadTaskRunner> | |
| 253 DeviceManagementRequestContextGetter::GetNetworkTaskRunner() const { | |
| 254 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); | |
| 255 } | |
| 256 | |
| 257 } // namespace | |
| 258 | |
| 259 // Request job implementation used with DeviceManagementService. | |
| 260 class DeviceManagementRequestJobImpl : public DeviceManagementRequestJob { | |
| 261 public: | |
| 262 DeviceManagementRequestJobImpl(JobType type, | |
| 263 DeviceManagementService* service); | |
| 264 virtual ~DeviceManagementRequestJobImpl(); | |
| 265 | |
| 266 // Handles the URL request response. | |
| 267 void HandleResponse(const net::URLRequestStatus& status, | |
| 268 int response_code, | |
| 269 const net::ResponseCookies& cookies, | |
| 270 const std::string& data); | |
| 271 | |
| 272 // Gets the URL to contact. | |
| 273 GURL GetURL(const std::string& server_url); | |
| 274 | |
| 275 // Configures the fetcher, setting up payload and headers. | |
| 276 void ConfigureRequest(net::URLFetcher* fetcher); | |
| 277 | |
| 278 protected: | |
| 279 // DeviceManagementRequestJob: | |
| 280 virtual void Run() OVERRIDE; | |
| 281 | |
| 282 private: | |
| 283 // Invokes the callback with the given error code. | |
| 284 void ReportError(DeviceManagementStatus code); | |
| 285 | |
| 286 // Pointer to the service this job is associated with. | |
| 287 DeviceManagementService* service_; | |
| 288 | |
| 289 DISALLOW_COPY_AND_ASSIGN(DeviceManagementRequestJobImpl); | |
| 290 }; | |
| 291 | |
| 292 DeviceManagementRequestJobImpl::DeviceManagementRequestJobImpl( | |
| 293 JobType type, | |
| 294 DeviceManagementService* service) | |
| 295 : DeviceManagementRequestJob(type), | |
| 296 service_(service) {} | |
| 297 | |
| 298 DeviceManagementRequestJobImpl::~DeviceManagementRequestJobImpl() { | |
| 299 service_->RemoveJob(this); | |
| 300 } | |
| 301 | |
| 302 void DeviceManagementRequestJobImpl::Run() { | |
| 303 service_->AddJob(this); | |
| 304 } | |
| 305 | |
| 306 void DeviceManagementRequestJobImpl::HandleResponse( | |
| 307 const net::URLRequestStatus& status, | |
| 308 int response_code, | |
| 309 const net::ResponseCookies& cookies, | |
| 310 const std::string& data) { | |
| 311 if (status.status() != net::URLRequestStatus::SUCCESS) { | |
| 312 LOG(WARNING) << "DMServer request failed, status: " << status.status() | |
| 313 << ", error: " << status.error(); | |
| 314 ReportError(DM_STATUS_REQUEST_FAILED); | |
| 315 return; | |
| 316 } | |
| 317 | |
| 318 if (response_code != kSuccess) | |
| 319 LOG(WARNING) << "DMServer sent an error response: " << response_code; | |
| 320 | |
| 321 switch (response_code) { | |
| 322 case kSuccess: { | |
| 323 em::DeviceManagementResponse response; | |
| 324 if (!response.ParseFromString(data)) { | |
| 325 ReportError(DM_STATUS_RESPONSE_DECODING_ERROR); | |
| 326 return; | |
| 327 } | |
| 328 callback_.Run(DM_STATUS_SUCCESS, response); | |
| 329 return; | |
| 330 } | |
| 331 case kInvalidArgument: | |
| 332 ReportError(DM_STATUS_REQUEST_INVALID); | |
| 333 return; | |
| 334 case kInvalidAuthCookieOrDMToken: | |
| 335 ReportError(DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID); | |
| 336 return; | |
| 337 case kMissingLicenses: | |
| 338 ReportError(DM_STATUS_SERVICE_MISSING_LICENSES); | |
| 339 return; | |
| 340 case kDeviceManagementNotAllowed: | |
| 341 ReportError(DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED); | |
| 342 return; | |
| 343 case kPendingApproval: | |
| 344 ReportError(DM_STATUS_SERVICE_ACTIVATION_PENDING); | |
| 345 return; | |
| 346 case kInvalidURL: | |
| 347 case kInternalServerError: | |
| 348 case kServiceUnavailable: | |
| 349 ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE); | |
| 350 return; | |
| 351 case kDeviceNotFound: | |
| 352 ReportError(DM_STATUS_SERVICE_DEVICE_NOT_FOUND); | |
| 353 return; | |
| 354 case kPolicyNotFound: | |
| 355 ReportError(DM_STATUS_SERVICE_POLICY_NOT_FOUND); | |
| 356 return; | |
| 357 case kInvalidSerialNumber: | |
| 358 ReportError(DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER); | |
| 359 return; | |
| 360 case kDeviceIdConflict: | |
| 361 ReportError(DM_STATUS_SERVICE_DEVICE_ID_CONFLICT); | |
| 362 return; | |
| 363 default: | |
| 364 // Handle all unknown 5xx HTTP error codes as temporary and any other | |
| 365 // unknown error as one that needs more time to recover. | |
| 366 if (response_code >= 500 && response_code <= 599) | |
| 367 ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE); | |
| 368 else | |
| 369 ReportError(DM_STATUS_HTTP_STATUS_ERROR); | |
| 370 return; | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 GURL DeviceManagementRequestJobImpl::GetURL( | |
| 375 const std::string& server_url) { | |
| 376 std::string result(server_url); | |
| 377 result += '?'; | |
| 378 for (ParameterMap::const_iterator entry(query_params_.begin()); | |
| 379 entry != query_params_.end(); | |
| 380 ++entry) { | |
| 381 if (entry != query_params_.begin()) | |
| 382 result += '&'; | |
| 383 result += net::EscapeQueryParamValue(entry->first, true); | |
| 384 result += '='; | |
| 385 result += net::EscapeQueryParamValue(entry->second, true); | |
| 386 } | |
| 387 return GURL(result); | |
| 388 } | |
| 389 | |
| 390 void DeviceManagementRequestJobImpl::ConfigureRequest( | |
| 391 net::URLFetcher* fetcher) { | |
| 392 std::string payload; | |
| 393 CHECK(request_.SerializeToString(&payload)); | |
| 394 fetcher->SetUploadData(kPostContentType, payload); | |
| 395 std::string extra_headers; | |
| 396 if (!gaia_token_.empty()) | |
| 397 extra_headers += kServiceTokenAuthHeader + gaia_token_ + "\n"; | |
| 398 if (!dm_token_.empty()) | |
| 399 extra_headers += kDMTokenAuthHeader + dm_token_ + "\n"; | |
| 400 fetcher->SetExtraRequestHeaders(extra_headers); | |
| 401 } | |
| 402 | |
| 403 void DeviceManagementRequestJobImpl::ReportError(DeviceManagementStatus code) { | |
| 404 em::DeviceManagementResponse dummy_response; | |
| 405 callback_.Run(code, dummy_response); | |
| 406 } | |
| 407 | |
| 408 DeviceManagementRequestJob::~DeviceManagementRequestJob() {} | |
| 409 | |
| 410 void DeviceManagementRequestJob::SetGaiaToken(const std::string& gaia_token) { | |
| 411 gaia_token_ = gaia_token; | |
| 412 } | |
| 413 | |
| 414 void DeviceManagementRequestJob::SetOAuthToken(const std::string& oauth_token) { | |
| 415 AddParameter(dm_protocol::kParamOAuthToken, oauth_token); | |
| 416 } | |
| 417 | |
| 418 void DeviceManagementRequestJob::SetUserAffiliation( | |
| 419 UserAffiliation user_affiliation) { | |
| 420 AddParameter(dm_protocol::kParamUserAffiliation, | |
| 421 UserAffiliationToString(user_affiliation)); | |
| 422 } | |
| 423 | |
| 424 void DeviceManagementRequestJob::SetDMToken(const std::string& dm_token) { | |
| 425 dm_token_ = dm_token; | |
| 426 } | |
| 427 | |
| 428 void DeviceManagementRequestJob::SetClientID(const std::string& client_id) { | |
| 429 AddParameter(dm_protocol::kParamDeviceID, client_id); | |
| 430 } | |
| 431 | |
| 432 em::DeviceManagementRequest* DeviceManagementRequestJob::GetRequest() { | |
| 433 return &request_; | |
| 434 } | |
| 435 | |
| 436 DeviceManagementRequestJob::DeviceManagementRequestJob(JobType type) { | |
| 437 AddParameter(dm_protocol::kParamRequest, JobTypeToRequestType(type)); | |
| 438 AddParameter(dm_protocol::kParamDeviceType, dm_protocol::kValueDeviceType); | |
| 439 AddParameter(dm_protocol::kParamAppType, dm_protocol::kValueAppType); | |
| 440 AddParameter(dm_protocol::kParamAgent, GetAgentString()); | |
| 441 AddParameter(dm_protocol::kParamPlatform, GetPlatformString()); | |
| 442 } | |
| 443 | |
| 444 void DeviceManagementRequestJob::Start(const Callback& callback) { | |
| 445 callback_ = callback; | |
| 446 Run(); | |
| 447 } | |
| 448 | |
| 449 void DeviceManagementRequestJob::AddParameter(const std::string& name, | |
| 450 const std::string& value) { | |
| 451 query_params_.push_back(std::make_pair(name, value)); | |
| 452 } | |
| 453 | |
| 454 DeviceManagementService::~DeviceManagementService() { | |
| 455 // All running jobs should have been cancelled by now. | |
| 456 DCHECK(pending_jobs_.empty()); | |
| 457 DCHECK(queued_jobs_.empty()); | |
| 458 } | |
| 459 | |
| 460 DeviceManagementRequestJob* DeviceManagementService::CreateJob( | |
| 461 DeviceManagementRequestJob::JobType type) { | |
| 462 return new DeviceManagementRequestJobImpl(type, this); | |
| 463 } | |
| 464 | |
| 465 void DeviceManagementService::ScheduleInitialization(int64 delay_milliseconds) { | |
| 466 if (initialized_) | |
| 467 return; | |
| 468 MessageLoop::current()->PostDelayedTask( | |
| 469 FROM_HERE, | |
| 470 base::Bind(&DeviceManagementService::Initialize, | |
| 471 weak_ptr_factory_.GetWeakPtr()), | |
| 472 base::TimeDelta::FromMilliseconds(delay_milliseconds)); | |
| 473 } | |
| 474 | |
| 475 void DeviceManagementService::Initialize() { | |
| 476 if (initialized_) | |
| 477 return; | |
| 478 DCHECK(!request_context_getter_); | |
| 479 request_context_getter_ = new DeviceManagementRequestContextGetter( | |
| 480 g_browser_process->system_request_context()); | |
| 481 initialized_ = true; | |
| 482 | |
| 483 while (!queued_jobs_.empty()) { | |
| 484 StartJob(queued_jobs_.front(), false); | |
| 485 queued_jobs_.pop_front(); | |
| 486 } | |
| 487 } | |
| 488 | |
| 489 void DeviceManagementService::Shutdown() { | |
| 490 for (JobFetcherMap::iterator job(pending_jobs_.begin()); | |
| 491 job != pending_jobs_.end(); | |
| 492 ++job) { | |
| 493 delete job->first; | |
| 494 queued_jobs_.push_back(job->second); | |
| 495 } | |
| 496 pending_jobs_.clear(); | |
| 497 } | |
| 498 | |
| 499 DeviceManagementService::DeviceManagementService( | |
| 500 const std::string& server_url) | |
| 501 : server_url_(server_url), | |
| 502 initialized_(false), | |
| 503 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
| 504 } | |
| 505 | |
| 506 void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job, | |
| 507 bool bypass_proxy) { | |
| 508 net::URLFetcher* fetcher = net::URLFetcher::Create( | |
| 509 0, job->GetURL(server_url_), net::URLFetcher::POST, this); | |
| 510 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | |
| 511 net::LOAD_DO_NOT_SAVE_COOKIES | | |
| 512 net::LOAD_DISABLE_CACHE | | |
| 513 (bypass_proxy ? net::LOAD_BYPASS_PROXY : 0)); | |
| 514 fetcher->SetRequestContext(request_context_getter_.get()); | |
| 515 // Early device policy fetches on ChromeOS and Auto-Enrollment checks are | |
| 516 // often interrupted during ChromeOS startup when network change notifications | |
| 517 // are sent. Allowing the fetcher to retry once after that is enough to | |
| 518 // recover; allow it to retry up to 3 times just in case. | |
| 519 // http://crosbug.com/16114 | |
| 520 fetcher->SetAutomaticallyRetryOnNetworkChanges(3); | |
| 521 job->ConfigureRequest(fetcher); | |
| 522 pending_jobs_[fetcher] = job; | |
| 523 fetcher->Start(); | |
| 524 } | |
| 525 | |
| 526 void DeviceManagementService::OnURLFetchComplete( | |
| 527 const net::URLFetcher* source) { | |
| 528 JobFetcherMap::iterator entry(pending_jobs_.find(source)); | |
| 529 if (entry == pending_jobs_.end()) { | |
| 530 NOTREACHED() << "Callback from foreign URL fetcher"; | |
| 531 return; | |
| 532 } | |
| 533 | |
| 534 DeviceManagementRequestJobImpl* job = entry->second; | |
| 535 pending_jobs_.erase(entry); | |
| 536 | |
| 537 // Retry the job if it failed due to a broken proxy, by bypassing the | |
| 538 // proxy on the next try. Don't retry if this URLFetcher already bypassed | |
| 539 // the proxy. | |
| 540 bool retry = false; | |
| 541 if ((source->GetLoadFlags() & net::LOAD_BYPASS_PROXY) == 0) { | |
| 542 if (!source->GetStatus().is_success() && | |
| 543 IsProxyError(source->GetStatus())) { | |
| 544 LOG(WARNING) << "Proxy failed while contacting dmserver."; | |
| 545 retry = true; | |
| 546 } else if (source->GetStatus().is_success() && | |
| 547 source->GetResponseCode() == kSuccess && | |
| 548 source->WasFetchedViaProxy() && | |
| 549 !IsProtobufMimeType(source)) { | |
| 550 // The proxy server can be misconfigured but pointing to an existing | |
| 551 // server that replies to requests. Try to recover if a successful | |
| 552 // request that went through a proxy returns an unexpected mime type. | |
| 553 LOG(WARNING) << "Got bad mime-type in response from dmserver that was " | |
| 554 << "fetched via a proxy."; | |
| 555 retry = true; | |
| 556 } | |
| 557 } | |
| 558 | |
| 559 if (retry) { | |
| 560 LOG(WARNING) << "Retrying dmserver request without using a proxy."; | |
| 561 StartJob(job, true); | |
| 562 } else { | |
| 563 std::string data; | |
| 564 source->GetResponseAsString(&data); | |
| 565 job->HandleResponse(source->GetStatus(), source->GetResponseCode(), | |
| 566 source->GetCookies(), data); | |
| 567 } | |
| 568 delete source; | |
| 569 } | |
| 570 | |
| 571 void DeviceManagementService::AddJob(DeviceManagementRequestJobImpl* job) { | |
| 572 if (initialized_) | |
| 573 StartJob(job, false); | |
| 574 else | |
| 575 queued_jobs_.push_back(job); | |
| 576 } | |
| 577 | |
| 578 void DeviceManagementService::RemoveJob(DeviceManagementRequestJobImpl* job) { | |
| 579 for (JobFetcherMap::iterator entry(pending_jobs_.begin()); | |
| 580 entry != pending_jobs_.end(); | |
| 581 ++entry) { | |
| 582 if (entry->second == job) { | |
| 583 delete entry->first; | |
| 584 pending_jobs_.erase(entry); | |
| 585 return; | |
| 586 } | |
| 587 } | |
| 588 | |
| 589 const JobQueue::iterator elem = | |
| 590 std::find(queued_jobs_.begin(), queued_jobs_.end(), job); | |
| 591 if (elem != queued_jobs_.end()) | |
| 592 queued_jobs_.erase(elem); | |
| 593 } | |
| 594 | |
| 595 } // namespace policy | |
| OLD | NEW |