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

Side by Side Diff: chrome/browser/google_apis/drive_uploader.cc

Issue 12218011: WIP: Implement 5xx Error handling. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 7 years, 10 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 "chrome/browser/google_apis/drive_uploader.h" 5 #include "chrome/browser/google_apis/drive_uploader.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback.h" 10 #include "base/callback.h"
11 #include "base/message_loop.h" 11 #include "base/message_loop.h"
12 #include "base/rand_util.h"
12 #include "base/string_number_conversions.h" 13 #include "base/string_number_conversions.h"
14 #include "base/time.h"
13 #include "chrome/browser/google_apis/drive_service_interface.h" 15 #include "chrome/browser/google_apis/drive_service_interface.h"
14 #include "chrome/browser/google_apis/drive_upload_mode.h" 16 #include "chrome/browser/google_apis/drive_upload_mode.h"
15 #include "chrome/browser/google_apis/gdata_wapi_parser.h" 17 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
16 #include "content/public/browser/browser_thread.h" 18 #include "content/public/browser/browser_thread.h"
17 #include "net/base/file_stream.h" 19 #include "net/base/file_stream.h"
18 #include "net/base/net_errors.h" 20 #include "net/base/net_errors.h"
19 21
20 using content::BrowserThread; 22 using content::BrowserThread;
21 23
22 namespace { 24 namespace {
23 25
24 // Google Documents List API requires uploading in chunks of 512kB. 26 // Google Documents List API requires uploading in chunks of 512kB.
25 const int64 kUploadChunkSize = 512 * 1024; 27 const int64 kUploadChunkSize = 512 * 1024;
26 28
27 // Opens |path| with |file_stream| and returns the file size. 29 // Opens |path| with |file_stream| and returns the file size.
28 // If failed, returns an error code in a negative value. 30 // If failed, returns an error code in a negative value.
29 int64 OpenFileStreamAndGetSizeOnBlockingPool(net::FileStream* file_stream, 31 int64 OpenFileStreamAndGetSizeOnBlockingPool(net::FileStream* file_stream,
30 const FilePath& path) { 32 const FilePath& path) {
31 int result = file_stream->OpenSync( 33 int result = file_stream->OpenSync(
32 path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ); 34 path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
33 if (result != net::OK) 35 if (result != net::OK)
34 return result; 36 return result;
35 return file_stream->Available(); 37 return file_stream->Available();
36 } 38 }
37 39
40 // Returns true if the status code is 5xx (SERVER ERROR).
41 bool IsHttp5xxStatusCode(int status_code) {
42 return status_code >= 500 && status_code < 600;
43 }
44
38 } // namespace 45 } // namespace
39 46
40 namespace google_apis { 47 namespace google_apis {
41 48
42 // Structure containing current upload information of file, passed between 49 // Structure containing current upload information of file, passed between
43 // DriveServiceInterface methods and callbacks. 50 // DriveServiceInterface methods and callbacks.
44 struct DriveUploader::UploadFileInfo { 51 struct DriveUploader::UploadFileInfo {
45 UploadFileInfo(scoped_refptr<base::SequencedTaskRunner> task_runner, 52 UploadFileInfo(scoped_refptr<base::SequencedTaskRunner> task_runner,
46 UploadMode upload_mode, 53 UploadMode upload_mode,
47 const GURL& initial_upload_location, 54 const GURL& initial_upload_location,
48 const FilePath& drive_path, 55 const FilePath& drive_path,
49 const FilePath& local_path, 56 const FilePath& local_path,
50 const std::string& title, 57 const std::string& title,
51 const std::string& content_type, 58 const std::string& content_type,
52 const std::string& etag, 59 const std::string& etag,
53 const UploadCompletionCallback& callback) 60 const UploadCompletionCallback& callback)
54 : upload_mode(upload_mode), 61 : upload_mode(upload_mode),
55 initial_upload_location(initial_upload_location), 62 initial_upload_location(initial_upload_location),
56 drive_path(drive_path), 63 drive_path(drive_path),
57 file_path(local_path), 64 file_path(local_path),
58 title(title), 65 title(title),
59 content_type(content_type), 66 content_type(content_type),
60 etag(etag), 67 etag(etag),
61 completion_callback(callback), 68 completion_callback(callback),
62 content_length(0), 69 content_length(0),
63 next_send_position(0),
64 file_stream(new net::FileStream(NULL)), 70 file_stream(new net::FileStream(NULL)),
65 buf(new net::IOBuffer(kUploadChunkSize)), 71 buf(new net::IOBuffer(kUploadChunkSize)),
72 buffer_position(0),
73 buffer_size(0),
74 trial_count(0),
66 blocking_task_runner(task_runner) { 75 blocking_task_runner(task_runner) {
67 } 76 }
68 77
69 ~UploadFileInfo() { 78 ~UploadFileInfo() {
70 blocking_task_runner->DeleteSoon(FROM_HERE, file_stream.release()); 79 blocking_task_runner->DeleteSoon(FROM_HERE, file_stream.release());
71 } 80 }
72 81
73 // Bytes left to upload. 82 // Bytes left to upload.
74 int64 SizeRemaining() const { 83 int64 SizeRemaining() const {
75 DCHECK(content_length >= next_send_position); 84 DCHECK(content_length >= buffer_position + buffer_size);
76 return content_length - next_send_position; 85 return content_length - (buffer_position + buffer_size);
77 } 86 }
78 87
79 // Useful for printf debugging. 88 // Useful for printf debugging.
80 std::string DebugString() const { 89 std::string DebugString() const {
81 return "title=[" + title + 90 return "title=[" + title +
82 "], file_path=[" + file_path.AsUTF8Unsafe() + 91 "], file_path=[" + file_path.AsUTF8Unsafe() +
83 "], content_type=[" + content_type + 92 "], content_type=[" + content_type +
84 "], content_length=[" + base::UintToString(content_length) + 93 "], content_length=[" + base::UintToString(content_length) +
85 "], drive_path=[" + drive_path.AsUTF8Unsafe() + 94 "], drive_path=[" + drive_path.AsUTF8Unsafe() +
86 "]"; 95 "]";
(...skipping 22 matching lines...) Expand all
109 // Callback to be invoked once the upload has finished. 118 // Callback to be invoked once the upload has finished.
110 const UploadCompletionCallback completion_callback; 119 const UploadCompletionCallback completion_callback;
111 120
112 // Location URL where file is to be uploaded to, returned from 121 // Location URL where file is to be uploaded to, returned from
113 // InitiateUpload. Used for the subsequent ResumeUpload requests. 122 // InitiateUpload. Used for the subsequent ResumeUpload requests.
114 GURL upload_location; 123 GURL upload_location;
115 124
116 // Header content-Length. 125 // Header content-Length.
117 int64 content_length; 126 int64 content_length;
118 127
119 // The start position of the contents to be sent as the next upload chunk.
120 int64 next_send_position;
121
122 // For opening and reading from physical file. 128 // For opening and reading from physical file.
123 // 129 //
124 // File operations are posted to |blocking_task_runner|, while the ownership 130 // File operations are posted to |blocking_task_runner|, while the ownership
125 // of the stream is held in UI thread. At the point when this UploadFileInfo 131 // of the stream is held in UI thread. At the point when this UploadFileInfo
126 // is destroyed, the ownership of the stream is passed to the worker pool. 132 // is destroyed, the ownership of the stream is passed to the worker pool.
127 // TODO(kinaba): We should switch to async API of FileStream once 133 // TODO(kinaba): We should switch to async API of FileStream once
128 // crbug.com/164312 is fixed. 134 // crbug.com/164312 is fixed.
129 scoped_ptr<net::FileStream> file_stream; 135 scoped_ptr<net::FileStream> file_stream;
130 136
131 // Holds current content to be uploaded. 137 // Holds current content to be uploaded.
132 const scoped_refptr<net::IOBuffer> buf; 138 const scoped_refptr<net::IOBuffer> buf;
133 139
140 // The current |buf|'s head position in the file.
141 int64 buffer_position;
142
143 // The size of available data in the |buf|.
144 int64 buffer_size;
145
146 // The number of current trial to send the current |buf| content.
147 // This is used to calculate waiting duration. Please see also
148 // ResumeInterruptedUpload.
149 int trial_count;
150
134 // Runner for net::FileStream tasks. 151 // Runner for net::FileStream tasks.
135 const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner; 152 const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
136 }; 153 };
137 154
138 DriveUploader::DriveUploader(DriveServiceInterface* drive_service) 155 DriveUploader::DriveUploader(DriveServiceInterface* drive_service)
139 : drive_service_(drive_service), 156 : drive_service_(drive_service),
140 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { 157 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
141 base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool(); 158 base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool();
142 blocking_task_runner_ = blocking_pool->GetSequencedTaskRunner( 159 blocking_task_runner_ = blocking_pool->GetSequencedTaskRunner(
143 blocking_pool->GetSequenceToken()); 160 blocking_pool->GetSequenceToken());
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
263 UploadFailed(upload_file_info.Pass(), DRIVE_UPLOAD_ERROR_ABORT); 280 UploadFailed(upload_file_info.Pass(), DRIVE_UPLOAD_ERROR_ABORT);
264 return; 281 return;
265 } 282 }
266 283
267 upload_file_info->upload_location = upload_location; 284 upload_file_info->upload_location = upload_location;
268 285
269 // Start the upload from the beginning of the file. 286 // Start the upload from the beginning of the file.
270 UploadNextChunk(upload_file_info.Pass()); 287 UploadNextChunk(upload_file_info.Pass());
271 } 288 }
272 289
290 void DriveUploader::UploadCurrentChunk(
291 scoped_ptr<UploadFileInfo> upload_file_info,
292 int64 restart_position) {
293 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
294
295 int64 buffer_offset =
296 restart_position - upload_file_info->buffer_position;
297 DCHECK_GE(buffer_offset, 0);
298
299 UploadFileInfo* info_ptr = upload_file_info.get();
300 drive_service_->ResumeUpload(
301 ResumeUploadParams(info_ptr->upload_mode,
302 restart_position,
303 info_ptr->buffer_position + info_ptr->buffer_size,
304 info_ptr->content_length,
305 info_ptr->content_type,
306 info_ptr->buf,
307 buffer_offset,
308 info_ptr->upload_location,
309 info_ptr->drive_path),
310 base::Bind(&DriveUploader::OnResumeUploadResponseReceived,
311 weak_ptr_factory_.GetWeakPtr(),
312 base::Passed(&upload_file_info)));
313 }
314
273 void DriveUploader::UploadNextChunk( 315 void DriveUploader::UploadNextChunk(
274 scoped_ptr<UploadFileInfo> upload_file_info) { 316 scoped_ptr<UploadFileInfo> upload_file_info) {
275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
276 318
277 // Determine number of bytes to read for this upload iteration. 319 // Determine number of bytes to read for this upload iteration.
278 const int bytes_to_read = std::min(upload_file_info->SizeRemaining(), 320 const int bytes_to_read = std::min(upload_file_info->SizeRemaining(),
279 kUploadChunkSize); 321 kUploadChunkSize);
280 322
281 if (bytes_to_read == 0) { 323 if (bytes_to_read == 0) {
282 // net::FileStream doesn't allow to read 0 bytes, so directly proceed to the 324 // net::FileStream doesn't allow to read 0 bytes, so directly proceed to the
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 DCHECK_EQ(bytes_to_read, bytes_read); 357 DCHECK_EQ(bytes_to_read, bytes_read);
316 DVLOG(1) << "ReadCompletionCallback bytes read=" << bytes_read; 358 DVLOG(1) << "ReadCompletionCallback bytes read=" << bytes_read;
317 359
318 if (bytes_read < 0) { 360 if (bytes_read < 0) {
319 LOG(ERROR) << "Error reading from file " 361 LOG(ERROR) << "Error reading from file "
320 << upload_file_info->file_path.value(); 362 << upload_file_info->file_path.value();
321 UploadFailed(upload_file_info.Pass(), DRIVE_UPLOAD_ERROR_ABORT); 363 UploadFailed(upload_file_info.Pass(), DRIVE_UPLOAD_ERROR_ABORT);
322 return; 364 return;
323 } 365 }
324 366
325 int64 start_position = upload_file_info->next_send_position; 367 // Update the buffer's position.
326 upload_file_info->next_send_position += bytes_read; 368 upload_file_info->buffer_position += upload_file_info->buffer_size;
327 int64 end_position = upload_file_info->next_send_position; 369 upload_file_info->buffer_size = bytes_read;
370
371 // Reset the trial count.
372 upload_file_info->trial_count = 0;
328 373
329 UploadFileInfo* info_ptr = upload_file_info.get(); 374 UploadFileInfo* info_ptr = upload_file_info.get();
330 drive_service_->ResumeUpload( 375 drive_service_->ResumeUpload(
331 ResumeUploadParams(info_ptr->upload_mode, 376 ResumeUploadParams(info_ptr->upload_mode,
332 start_position, 377 info_ptr->buffer_position,
333 end_position, 378 info_ptr->buffer_position + info_ptr->buffer_size,
334 info_ptr->content_length, 379 info_ptr->content_length,
335 info_ptr->content_type, 380 info_ptr->content_type,
336 info_ptr->buf, 381 info_ptr->buf,
382 0, // buffer's offset.
337 info_ptr->upload_location, 383 info_ptr->upload_location,
338 info_ptr->drive_path), 384 info_ptr->drive_path),
339 base::Bind(&DriveUploader::OnResumeUploadResponseReceived, 385 base::Bind(&DriveUploader::OnResumeUploadResponseReceived,
340 weak_ptr_factory_.GetWeakPtr(), 386 weak_ptr_factory_.GetWeakPtr(),
341 base::Passed(&upload_file_info))); 387 base::Passed(&upload_file_info)));
342 } 388 }
343 389
344 void DriveUploader::OnResumeUploadResponseReceived( 390 void DriveUploader::OnResumeUploadResponseReceived(
345 scoped_ptr<UploadFileInfo> upload_file_info, 391 scoped_ptr<UploadFileInfo> upload_file_info,
346 const ResumeUploadResponse& response, 392 const ResumeUploadResponse& response,
(...skipping 13 matching lines...) Expand all
360 entry.Pass()); 406 entry.Pass());
361 return; 407 return;
362 } 408 }
363 409
364 // ETag mismatch. 410 // ETag mismatch.
365 if (response.code == HTTP_PRECONDITION) { 411 if (response.code == HTTP_PRECONDITION) {
366 UploadFailed(upload_file_info.Pass(), DRIVE_UPLOAD_ERROR_CONFLICT); 412 UploadFailed(upload_file_info.Pass(), DRIVE_UPLOAD_ERROR_CONFLICT);
367 return; 413 return;
368 } 414 }
369 415
416 // The uploading is interrupted. Move to resuming flow.
417 if (IsHttp5xxStatusCode(response.code)) {
418 ResumeInterruptedUpload(upload_file_info.Pass());
419 return;
420 }
421
370 // If code is 308 (RESUME_INCOMPLETE) and range_received is what has been 422 // If code is 308 (RESUME_INCOMPLETE) and range_received is what has been
371 // previously uploaded (i.e. = upload_file_info->end_position), proceed to 423 // previously uploaded (i.e. = upload_file_info->end_position), proceed to
372 // upload the next chunk. 424 // upload the next chunk.
373 if (response.code != HTTP_RESUME_INCOMPLETE || 425 if (response.code != HTTP_RESUME_INCOMPLETE ||
374 response.start_position_received != 0 || 426 response.start_position_received != 0 ||
375 response.end_position_received != upload_file_info->next_send_position) { 427 response.end_position_received < upload_file_info->buffer_position ||
428 response.end_position_received >
429 upload_file_info->buffer_position + upload_file_info->buffer_size) {
376 // TODO(achuith): Handle error cases, e.g. 430 // TODO(achuith): Handle error cases, e.g.
377 // - when previously uploaded data wasn't received by Google Docs server, 431 // - when previously uploaded data wasn't received by Google Docs server,
378 // i.e. when end_position_received < upload_file_info->end_position 432 // i.e. when end_position_received < upload_file_info->end_position
379 LOG(ERROR) << "UploadNextChunk http code=" << response.code 433 LOG(ERROR) << "UploadNextChunk http code=" << response.code
380 << ", start_position_received=" << response.start_position_received 434 << ", start_position_received=" << response.start_position_received
381 << ", end_position_received=" << response.end_position_received 435 << ", end_position_received=" << response.end_position_received;
382 << ", expected end range=" << upload_file_info->next_send_position; 436 // TODO
383 UploadFailed(upload_file_info.Pass(), 437 UploadFailed(upload_file_info.Pass(),
384 response.code == HTTP_FORBIDDEN ? 438 response.code == HTTP_FORBIDDEN ?
385 DRIVE_UPLOAD_ERROR_NO_SPACE : DRIVE_UPLOAD_ERROR_ABORT); 439 DRIVE_UPLOAD_ERROR_NO_SPACE : DRIVE_UPLOAD_ERROR_ABORT);
386 return; 440 return;
387 } 441 }
388 442
389 DVLOG(1) << "Received range " << response.start_position_received 443 DVLOG(1) << "Received range " << response.start_position_received
390 << "-" << response.end_position_received 444 << "-" << response.end_position_received
391 << " for [" << upload_file_info->title << "]"; 445 << " for [" << upload_file_info->title << "]";
392 446
393 // Continue uploading. 447 // Continue uploading.
394 UploadNextChunk(upload_file_info.Pass()); 448 if (upload_file_info->buffer_position + upload_file_info->buffer_size ==
449 response.end_position_received) {
450 // The current chunk has been sent successuflly, so send the next chunk.
451 UploadNextChunk(upload_file_info.Pass());
452 } else {
453 // There is remaining data in the current chunk. Re-send it.
454 //
455 // Note that PostTask is necessary here because we have to finish an
456 // uploading callback before calling ResumeUpload again due to the
457 // implementation of OperationRegistry (http://crbug.com/134814).
458 MessageLoop::current()->PostTask(
459 FROM_HERE,
460 base::Bind(&DriveUploader::UploadCurrentChunk,
461 weak_ptr_factory_.GetWeakPtr(),
462 base::Passed(&upload_file_info),
463 response.end_position_received));
464 }
465 }
466
467 void DriveUploader::ResumeInterruptedUpload(
468 scoped_ptr<UploadFileInfo> upload_file_info) {
469 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
470
471 // When the upload is interrupted, we'll wait some period and restart
472 // the upload, following the document:
473 // https://developers.google.com/drive/manage-uploads#exp-backoff
474 // The waiting duration is calculated as follows:
475 // First trial: 1sec + random millisecs.
476 // Second trial: 2secs + random millisecs.
477 // Third trial: 4secs + random millisecs.
478 // Forth trial: 8secs + random millisecs.
479 // :
480 // n-th trial: 2^(n-1)secs + random millisecs.
481 base::TimeDelta wait_duration =
482 base::TimeDelta::FromSeconds(1 << upload_file_info->trial_count) +
483 base::TimeDelta::FromMilliseconds(base::RandInt(0, 9));
484 ++upload_file_info->trial_count;
485
486 MessageLoop::current()->PostDelayedTask(
487 FROM_HERE,
488 base::Bind(&DriveUploader::ResumeInterruptedUploadAfterWaiting,
489 weak_ptr_factory_.GetWeakPtr(),
490 base::Passed(&upload_file_info)),
491 wait_duration);
492 }
493
494 void DriveUploader::ResumeInterruptedUploadAfterWaiting(
495 scoped_ptr<UploadFileInfo> upload_file_info) {
496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
497
498 UploadFileInfo* info_ptr = upload_file_info.get();
499 drive_service_->GetUploadState(
500 info_ptr->upload_mode,
501 info_ptr->drive_path,
502 info_ptr->upload_location,
503 info_ptr->content_length,
504 base::Bind(&DriveUploader::OnResumeUploadResponseReceived,
505 weak_ptr_factory_.GetWeakPtr(),
506 base::Passed(&upload_file_info)));
395 } 507 }
396 508
397 void DriveUploader::UploadFailed(scoped_ptr<UploadFileInfo> upload_file_info, 509 void DriveUploader::UploadFailed(scoped_ptr<UploadFileInfo> upload_file_info,
398 DriveUploadError error) { 510 DriveUploadError error) {
399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 511 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
400 512
401 LOG(ERROR) << "Upload failed " << upload_file_info->DebugString(); 513 LOG(ERROR) << "Upload failed " << upload_file_info->DebugString();
402 514
403 upload_file_info->completion_callback.Run(error, 515 upload_file_info->completion_callback.Run(error,
404 upload_file_info->drive_path, 516 upload_file_info->drive_path,
405 upload_file_info->file_path, 517 upload_file_info->file_path,
406 scoped_ptr<ResourceEntry>()); 518 scoped_ptr<ResourceEntry>());
407 } 519 }
408 520
409 } // namespace google_apis 521 } // namespace google_apis
OLDNEW
« no previous file with comments | « chrome/browser/google_apis/drive_uploader.h ('k') | chrome/browser/google_apis/dummy_drive_service.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698