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

Side by Side Diff: chrome/browser/chromeos/policy/upload_job_impl.cc

Issue 1875443003: Retry uploading in UploadJobImpl when error occurs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix include 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) 2015 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2015 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 "chrome/browser/chromeos/policy/upload_job_impl.h" 5 #include "chrome/browser/chromeos/policy/upload_job_impl.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <set> 8 #include <set>
9 #include <utility> 9 #include <utility>
10 10
11 #include "base/location.h"
11 #include "base/logging.h" 12 #include "base/logging.h"
12 #include "base/macros.h" 13 #include "base/macros.h"
13 #include "base/strings/stringprintf.h" 14 #include "base/strings/stringprintf.h"
14 #include "google_apis/gaia/gaia_constants.h" 15 #include "google_apis/gaia/gaia_constants.h"
15 #include "google_apis/gaia/google_service_auth_error.h" 16 #include "google_apis/gaia/google_service_auth_error.h"
16 #include "net/base/mime_util.h" 17 #include "net/base/mime_util.h"
17 #include "net/http/http_status_code.h" 18 #include "net/http/http_status_code.h"
18 #include "net/url_request/url_request_status.h" 19 #include "net/url_request/url_request_status.h"
19 20
20 namespace policy { 21 namespace policy {
21 22
22 namespace { 23 namespace {
23 24
24 // Format for bearer tokens in HTTP requests to access OAuth 2.0 protected 25 // Format for bearer tokens in HTTP requests to access OAuth 2.0 protected
25 // resources. 26 // resources.
26 const char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s"; 27 const char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s";
27 28
28 // Value the "Content-Type" field will be set to in the POST request. 29 // Value the "Content-Type" field will be set to in the POST request.
29 const char kUploadContentType[] = "multipart/form-data"; 30 const char kUploadContentType[] = "multipart/form-data";
30 31
31 // Number of upload retries. 32 // Number of upload attempts.
32 const int kMaxRetries = 1; 33 const int kMaxAttempts = 4;
33 34
34 // Max size of MIME boundary according to RFC 1341, section 7.2.1. 35 // Max size of MIME boundary according to RFC 1341, section 7.2.1.
35 const size_t kMaxMimeBoundarySize = 70; 36 const size_t kMaxMimeBoundarySize = 70;
36 37
38 // Delay after each unsuccessful upload attempt.
39 long g_retry_delay_ms = 25000;
40
37 } // namespace 41 } // namespace
38 42
39 UploadJobImpl::Delegate::~Delegate() { 43 UploadJobImpl::Delegate::~Delegate() {
40 } 44 }
41 45
42 UploadJobImpl::MimeBoundaryGenerator::~MimeBoundaryGenerator() { 46 UploadJobImpl::MimeBoundaryGenerator::~MimeBoundaryGenerator() {
43 } 47 }
44 48
45 UploadJobImpl::RandomMimeBoundaryGenerator::~RandomMimeBoundaryGenerator() { 49 UploadJobImpl::RandomMimeBoundaryGenerator::~RandomMimeBoundaryGenerator() {
46 } 50 }
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 const { 124 const {
121 return net::GenerateMimeMultipartBoundary(); 125 return net::GenerateMimeMultipartBoundary();
122 } 126 }
123 127
124 UploadJobImpl::UploadJobImpl( 128 UploadJobImpl::UploadJobImpl(
125 const GURL& upload_url, 129 const GURL& upload_url,
126 const std::string& account_id, 130 const std::string& account_id,
127 OAuth2TokenService* token_service, 131 OAuth2TokenService* token_service,
128 scoped_refptr<net::URLRequestContextGetter> url_context_getter, 132 scoped_refptr<net::URLRequestContextGetter> url_context_getter,
129 Delegate* delegate, 133 Delegate* delegate,
130 std::unique_ptr<MimeBoundaryGenerator> boundary_generator) 134 std::unique_ptr<MimeBoundaryGenerator> boundary_generator,
135 scoped_refptr<base::SequencedTaskRunner> task_runner)
131 : OAuth2TokenService::Consumer("cros_upload_job"), 136 : OAuth2TokenService::Consumer("cros_upload_job"),
132 upload_url_(upload_url), 137 upload_url_(upload_url),
133 account_id_(account_id), 138 account_id_(account_id),
134 token_service_(token_service), 139 token_service_(token_service),
135 url_context_getter_(url_context_getter), 140 url_context_getter_(url_context_getter),
136 delegate_(delegate), 141 delegate_(delegate),
137 boundary_generator_(std::move(boundary_generator)), 142 boundary_generator_(std::move(boundary_generator)),
138 state_(IDLE), 143 state_(IDLE),
139 retry_(0) { 144 retry_(0),
145 task_runner_(task_runner),
146 weak_factory_(this) {
140 DCHECK(token_service_); 147 DCHECK(token_service_);
141 DCHECK(url_context_getter_); 148 DCHECK(url_context_getter_);
142 DCHECK(delegate_); 149 DCHECK(delegate_);
143 if (!upload_url_.is_valid()) { 150 if (!upload_url_.is_valid()) {
144 state_ = ERROR; 151 state_ = ERROR;
145 NOTREACHED() << upload_url_ << " is not a valid URL."; 152 NOTREACHED() << upload_url_ << " is not a valid URL.";
146 } 153 }
147 } 154 }
148 155
149 UploadJobImpl::~UploadJobImpl() { 156 UploadJobImpl::~UploadJobImpl() {
150 } 157 }
151 158
152 void UploadJobImpl::AddDataSegment( 159 void UploadJobImpl::AddDataSegment(
153 const std::string& name, 160 const std::string& name,
154 const std::string& filename, 161 const std::string& filename,
155 const std::map<std::string, std::string>& header_entries, 162 const std::map<std::string, std::string>& header_entries,
156 std::unique_ptr<std::string> data) { 163 std::unique_ptr<std::string> data) {
164 DCHECK(thread_checker_.CalledOnValidThread());
157 // Cannot add data to busy or failed instance. 165 // Cannot add data to busy or failed instance.
158 DCHECK_EQ(IDLE, state_); 166 DCHECK_EQ(IDLE, state_);
159 if (state_ != IDLE) 167 if (state_ != IDLE)
160 return; 168 return;
161 169
162 std::unique_ptr<DataSegment> data_segment( 170 std::unique_ptr<DataSegment> data_segment(
163 new DataSegment(name, filename, std::move(data), header_entries)); 171 new DataSegment(name, filename, std::move(data), header_entries));
164 data_segments_.push_back(std::move(data_segment)); 172 data_segments_.push_back(std::move(data_segment));
165 } 173 }
166 174
167 void UploadJobImpl::Start() { 175 void UploadJobImpl::Start() {
176 DCHECK(thread_checker_.CalledOnValidThread());
168 // Cannot start an upload on a busy or failed instance. 177 // Cannot start an upload on a busy or failed instance.
169 DCHECK_EQ(IDLE, state_); 178 DCHECK_EQ(IDLE, state_);
170 if (state_ != IDLE) 179 if (state_ != IDLE)
171 return; 180 return;
181 DCHECK_EQ(0, retry_);
182
172 RequestAccessToken(); 183 RequestAccessToken();
173 } 184 }
174 185
186 // static
187 void UploadJobImpl::SetRetryDelayForTesting(long retry_delay_ms) {
188 CHECK_GE(retry_delay_ms, 0);
189 g_retry_delay_ms = retry_delay_ms;
190 }
191
175 void UploadJobImpl::RequestAccessToken() { 192 void UploadJobImpl::RequestAccessToken() {
193 DCHECK(thread_checker_.CalledOnValidThread());
194 DCHECK(!access_token_request_);
195
176 state_ = ACQUIRING_TOKEN; 196 state_ = ACQUIRING_TOKEN;
177 197
178 OAuth2TokenService::ScopeSet scope_set; 198 OAuth2TokenService::ScopeSet scope_set;
179 scope_set.insert(GaiaConstants::kDeviceManagementServiceOAuth); 199 scope_set.insert(GaiaConstants::kDeviceManagementServiceOAuth);
180 access_token_request_ = 200 access_token_request_ =
181 token_service_->StartRequest(account_id_, scope_set, this); 201 token_service_->StartRequest(account_id_, scope_set, this);
182 } 202 }
183 203
184 bool UploadJobImpl::SetUpMultipart() { 204 bool UploadJobImpl::SetUpMultipart() {
185 DCHECK_EQ(ACQUIRING_TOKEN, state_); 205 DCHECK_EQ(ACQUIRING_TOKEN, state_);
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
257 277
258 upload_fetcher_ = 278 upload_fetcher_ =
259 net::URLFetcher::Create(upload_url_, net::URLFetcher::POST, this); 279 net::URLFetcher::Create(upload_url_, net::URLFetcher::POST, this);
260 upload_fetcher_->SetRequestContext(url_context_getter_.get()); 280 upload_fetcher_->SetRequestContext(url_context_getter_.get());
261 upload_fetcher_->SetUploadData(content_type, *post_data_); 281 upload_fetcher_->SetUploadData(content_type, *post_data_);
262 upload_fetcher_->AddExtraRequestHeader( 282 upload_fetcher_->AddExtraRequestHeader(
263 base::StringPrintf(kAuthorizationHeaderFormat, access_token.c_str())); 283 base::StringPrintf(kAuthorizationHeaderFormat, access_token.c_str()));
264 upload_fetcher_->Start(); 284 upload_fetcher_->Start();
265 } 285 }
266 286
267 void UploadJobImpl::StartUpload(const std::string& access_token) { 287 void UploadJobImpl::StartUpload() {
288 DCHECK(thread_checker_.CalledOnValidThread());
289
268 if (!SetUpMultipart()) { 290 if (!SetUpMultipart()) {
269 LOG(ERROR) << "Multipart message assembly failed."; 291 LOG(ERROR) << "Multipart message assembly failed.";
270 state_ = ERROR; 292 state_ = ERROR;
271 return; 293 return;
272 } 294 }
273 CreateAndStartURLFetcher(access_token); 295 CreateAndStartURLFetcher(access_token_);
274 state_ = UPLOADING; 296 state_ = UPLOADING;
275 } 297 }
276 298
277 void UploadJobImpl::OnGetTokenSuccess( 299 void UploadJobImpl::OnGetTokenSuccess(
278 const OAuth2TokenService::Request* request, 300 const OAuth2TokenService::Request* request,
279 const std::string& access_token, 301 const std::string& access_token,
280 const base::Time& expiration_time) { 302 const base::Time& expiration_time) {
281 DCHECK_EQ(ACQUIRING_TOKEN, state_); 303 DCHECK_EQ(ACQUIRING_TOKEN, state_);
282 DCHECK_EQ(access_token_request_.get(), request); 304 DCHECK_EQ(access_token_request_.get(), request);
283 access_token_request_.reset(); 305 access_token_request_.reset();
284 306
285 // Also cache the token locally, so that we can revoke it later if necessary. 307 // Also cache the token locally, so that we can revoke it later if necessary.
286 access_token_ = access_token; 308 access_token_ = access_token;
287 StartUpload(access_token); 309 StartUpload();
288 } 310 }
289 311
290 void UploadJobImpl::OnGetTokenFailure( 312 void UploadJobImpl::OnGetTokenFailure(
291 const OAuth2TokenService::Request* request, 313 const OAuth2TokenService::Request* request,
292 const GoogleServiceAuthError& error) { 314 const GoogleServiceAuthError& error) {
293 DCHECK_EQ(ACQUIRING_TOKEN, state_); 315 DCHECK_EQ(ACQUIRING_TOKEN, state_);
294 DCHECK_EQ(access_token_request_.get(), request); 316 DCHECK_EQ(access_token_request_.get(), request);
295 access_token_request_.reset(); 317 access_token_request_.reset();
296 LOG(ERROR) << "Token request failed: " << error.ToString(); 318 LOG(ERROR) << "Token request failed: " << error.ToString();
297 state_ = ERROR; 319 HandleError(AUTHENTICATION_ERROR);
298 delegate_->OnFailure(AUTHENTICATION_ERROR); 320 }
321
322 void UploadJobImpl::HandleError(ErrorCode error_code) {
323 retry_++;
324 upload_fetcher_.reset();
325
326 LOG(ERROR) << "Upload failed, error code: " << error_code;
327
328 if (retry_ >= kMaxAttempts) {
329 // Maximum number of attempts reached, failure.
330 LOG(ERROR) << "Maximum number of attempts reached.";
331 access_token_.clear();
332 post_data_.reset();
333 state_ = ERROR;
334 delegate_->OnFailure(error_code);
335 } else {
336 if (error_code == AUTHENTICATION_ERROR) {
337 LOG(ERROR) << "Retrying upload with a new token.";
338 // Request new token and retry.
339 OAuth2TokenService::ScopeSet scope_set;
340 scope_set.insert(GaiaConstants::kDeviceManagementServiceOAuth);
341 token_service_->InvalidateAccessToken(account_id_, scope_set,
342 access_token_);
343 access_token_.clear();
344 task_runner_->PostDelayedTask(
345 FROM_HERE, base::Bind(&UploadJobImpl::RequestAccessToken,
346 weak_factory_.GetWeakPtr()),
347 base::TimeDelta::FromMilliseconds(g_retry_delay_ms));
348 } else {
349 // Retry without a new token.
350 state_ = ACQUIRING_TOKEN;
351 LOG(WARNING) << "Retrying upload with the same token.";
352 task_runner_->PostDelayedTask(
353 FROM_HERE,
354 base::Bind(&UploadJobImpl::StartUpload, weak_factory_.GetWeakPtr()),
355 base::TimeDelta::FromMilliseconds(g_retry_delay_ms));
356 }
357 }
299 } 358 }
300 359
301 void UploadJobImpl::OnURLFetchComplete(const net::URLFetcher* source) { 360 void UploadJobImpl::OnURLFetchComplete(const net::URLFetcher* source) {
302 DCHECK_EQ(upload_fetcher_.get(), source); 361 DCHECK_EQ(upload_fetcher_.get(), source);
362 DCHECK_EQ(UPLOADING, state_);
303 const net::URLRequestStatus& status = source->GetStatus(); 363 const net::URLRequestStatus& status = source->GetStatus();
304 if (!status.is_success()) { 364 if (!status.is_success()) {
305 LOG(ERROR) << "URLRequestStatus error " << status.error(); 365 LOG(ERROR) << "URLRequestStatus error " << status.error();
306 upload_fetcher_.reset(); 366 HandleError(NETWORK_ERROR);
307 state_ = ERROR; 367 } else {
308 post_data_.reset(); 368 const int response_code = source->GetResponseCode();
309 delegate_->OnFailure(NETWORK_ERROR); 369 if (response_code == net::HTTP_OK) {
310 return; 370 // Successful upload
311 }
312
313 const int response_code = source->GetResponseCode();
314 const bool success = response_code == net::HTTP_OK;
315 if (!success)
316 LOG(ERROR) << "POST request failed with HTTP status code " << response_code;
317
318 if (response_code == net::HTTP_UNAUTHORIZED) {
319 if (retry_ >= kMaxRetries) {
320 upload_fetcher_.reset(); 371 upload_fetcher_.reset();
372 access_token_.clear();
373 post_data_.reset();
374 state_ = SUCCESS;
375 delegate_->OnSuccess();
376 } else if (response_code == net::HTTP_UNAUTHORIZED) {
321 LOG(ERROR) << "Unauthorized request."; 377 LOG(ERROR) << "Unauthorized request.";
322 state_ = ERROR; 378 HandleError(AUTHENTICATION_ERROR);
323 post_data_.reset(); 379 } else {
324 delegate_->OnFailure(AUTHENTICATION_ERROR); 380 LOG(ERROR) << "POST request failed with HTTP status code "
325 return; 381 << response_code << ".";
382 HandleError(SERVER_ERROR);
326 } 383 }
327 retry_++;
328 upload_fetcher_.reset();
329 OAuth2TokenService::ScopeSet scope_set;
330 scope_set.insert(GaiaConstants::kDeviceManagementServiceOAuth);
331 token_service_->InvalidateAccessToken(account_id_, scope_set,
332 access_token_);
333 access_token_.clear();
334 RequestAccessToken();
335 return;
336 }
337
338 upload_fetcher_.reset();
339 access_token_.clear();
340 upload_fetcher_.reset();
341 post_data_.reset();
342 if (success) {
343 state_ = SUCCESS;
344 delegate_->OnSuccess();
345 } else {
346 state_ = ERROR;
347 delegate_->OnFailure(SERVER_ERROR);
348 } 384 }
349 } 385 }
350 386
351 } // namespace policy 387 } // namespace policy
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/policy/upload_job_impl.h ('k') | chrome/browser/chromeos/policy/upload_job_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698