| 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/drive/drive_uploader.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/callback.h" | |
| 11 #include "base/files/file_util.h" | |
| 12 #include "base/metrics/histogram_macros.h" | |
| 13 #include "base/strings/string_number_conversions.h" | |
| 14 #include "base/task_runner_util.h" | |
| 15 #include "chrome/browser/drive/drive_service_interface.h" | |
| 16 #include "content/public/browser/power_save_blocker.h" | |
| 17 #include "google_apis/drive/drive_api_parser.h" | |
| 18 | |
| 19 using google_apis::CancelCallback; | |
| 20 using google_apis::FileResource; | |
| 21 using google_apis::DRIVE_CANCELLED; | |
| 22 using google_apis::DriveApiErrorCode; | |
| 23 using google_apis::DRIVE_NO_SPACE; | |
| 24 using google_apis::HTTP_CONFLICT; | |
| 25 using google_apis::HTTP_CREATED; | |
| 26 using google_apis::HTTP_FORBIDDEN; | |
| 27 using google_apis::HTTP_NOT_FOUND; | |
| 28 using google_apis::HTTP_PRECONDITION; | |
| 29 using google_apis::HTTP_RESUME_INCOMPLETE; | |
| 30 using google_apis::HTTP_SUCCESS; | |
| 31 using google_apis::ProgressCallback; | |
| 32 using google_apis::UploadRangeResponse; | |
| 33 | |
| 34 namespace drive { | |
| 35 | |
| 36 namespace { | |
| 37 // Upload data is split to multiple HTTP request each conveying kUploadChunkSize | |
| 38 // bytes (except the request for uploading the last chunk of data). | |
| 39 // The value must be a multiple of 512KB according to the spec of GData WAPI and | |
| 40 // Drive API v2. It is set to a smaller value than 2^31 for working around | |
| 41 // server side error (crbug.com/264089). | |
| 42 const int64 kUploadChunkSize = (1LL << 30); // 1GB | |
| 43 // Maximum file size to be uploaded by multipart requests. The file that is | |
| 44 // larger than the size is processed by resumable upload. | |
| 45 const int64 kMaxMultipartUploadSize = (1LL << 20); // 1MB | |
| 46 | |
| 47 // Drive upload protocol. This is used to back a histogram. Sync this with UMA | |
| 48 // enum "DriveUploadProtocol" and treat this as append-only. | |
| 49 enum DriveUploadProtocol { | |
| 50 UPLOAD_METHOD_RESUMABLE, | |
| 51 UPLOAD_METHOD_MULTIPART, | |
| 52 UPLOAD_METHOD_BATCH, | |
| 53 UPLOAD_METHOD_MAX_VALUE | |
| 54 }; | |
| 55 | |
| 56 void RecordDriveUploadProtocol(DriveUploadProtocol protocol) { | |
| 57 UMA_HISTOGRAM_ENUMERATION( | |
| 58 "Drive.UploadProtocol", protocol, UPLOAD_METHOD_MAX_VALUE); | |
| 59 } | |
| 60 } // namespace | |
| 61 | |
| 62 // Refcounted helper class to manage batch request. DriveUploader uses the class | |
| 63 // for keeping the BatchRequestConfigurator instance while it prepares upload | |
| 64 // file information asynchronously. DriveUploader discard the reference after | |
| 65 // getting file information and the instance will be destroyed after all | |
| 66 // preparations complete. At that time, the helper instance commits owned batch | |
| 67 // request at the destrutor. | |
| 68 class DriveUploader::RefCountedBatchRequest | |
| 69 : public base::RefCounted<RefCountedBatchRequest> { | |
| 70 public: | |
| 71 RefCountedBatchRequest( | |
| 72 scoped_ptr<BatchRequestConfiguratorInterface> configurator) | |
| 73 : configurator_(configurator.Pass()) {} | |
| 74 | |
| 75 // Gets pointer of BatchRequestConfiguratorInterface owned by the instance. | |
| 76 BatchRequestConfiguratorInterface* configurator() const { | |
| 77 return configurator_.get(); | |
| 78 } | |
| 79 | |
| 80 private: | |
| 81 friend class base::RefCounted<RefCountedBatchRequest>; | |
| 82 ~RefCountedBatchRequest() { configurator_->Commit(); } | |
| 83 scoped_ptr<BatchRequestConfiguratorInterface> configurator_; | |
| 84 }; | |
| 85 | |
| 86 // Structure containing current upload information of file, passed between | |
| 87 // DriveServiceInterface methods and callbacks. | |
| 88 struct DriveUploader::UploadFileInfo { | |
| 89 UploadFileInfo(const base::FilePath& local_path, | |
| 90 const std::string& content_type, | |
| 91 const UploadCompletionCallback& callback, | |
| 92 const ProgressCallback& progress_callback) | |
| 93 : file_path(local_path), | |
| 94 content_type(content_type), | |
| 95 completion_callback(callback), | |
| 96 progress_callback(progress_callback), | |
| 97 content_length(0), | |
| 98 next_start_position(-1), | |
| 99 power_save_blocker(content::PowerSaveBlocker::Create( | |
| 100 content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, | |
| 101 content::PowerSaveBlocker::kReasonOther, | |
| 102 "Upload in progress")), | |
| 103 cancelled(false), | |
| 104 weak_ptr_factory_(this) {} | |
| 105 | |
| 106 ~UploadFileInfo() { | |
| 107 } | |
| 108 | |
| 109 // Useful for printf debugging. | |
| 110 std::string DebugString() const { | |
| 111 return "file_path=[" + file_path.AsUTF8Unsafe() + | |
| 112 "], content_type=[" + content_type + | |
| 113 "], content_length=[" + base::UintToString(content_length) + | |
| 114 "]"; | |
| 115 } | |
| 116 | |
| 117 // Returns the callback to cancel the upload represented by this struct. | |
| 118 CancelCallback GetCancelCallback() { | |
| 119 return base::Bind(&UploadFileInfo::Cancel, weak_ptr_factory_.GetWeakPtr()); | |
| 120 } | |
| 121 | |
| 122 // The local file path of the file to be uploaded. | |
| 123 const base::FilePath file_path; | |
| 124 | |
| 125 // Content-Type of file. | |
| 126 const std::string content_type; | |
| 127 | |
| 128 // Callback to be invoked once the upload has finished. | |
| 129 const UploadCompletionCallback completion_callback; | |
| 130 | |
| 131 // Callback to periodically notify the upload progress. | |
| 132 const ProgressCallback progress_callback; | |
| 133 | |
| 134 // Location URL where file is to be uploaded to, returned from | |
| 135 // InitiateUpload. Used for the subsequent ResumeUpload requests. | |
| 136 GURL upload_location; | |
| 137 | |
| 138 // Header content-Length. | |
| 139 int64 content_length; | |
| 140 | |
| 141 int64 next_start_position; | |
| 142 | |
| 143 // Blocks system suspend while upload is in progress. | |
| 144 scoped_ptr<content::PowerSaveBlocker> power_save_blocker; | |
| 145 | |
| 146 // Fields for implementing cancellation. |cancel_callback| is non-null if | |
| 147 // there is an in-flight HTTP request. In that case, |cancell_callback| will | |
| 148 // cancel the operation. |cancelled| is initially false and turns to true | |
| 149 // once Cancel() is called. DriveUploader will check this field before after | |
| 150 // an async task other than HTTP requests and cancels the subsequent requests | |
| 151 // if this is flagged to true. | |
| 152 CancelCallback cancel_callback; | |
| 153 bool cancelled; | |
| 154 | |
| 155 private: | |
| 156 // Cancels the upload represented by this struct. | |
| 157 void Cancel() { | |
| 158 cancelled = true; | |
| 159 if (!cancel_callback.is_null()) | |
| 160 cancel_callback.Run(); | |
| 161 } | |
| 162 | |
| 163 base::WeakPtrFactory<UploadFileInfo> weak_ptr_factory_; | |
| 164 DISALLOW_COPY_AND_ASSIGN(UploadFileInfo); | |
| 165 }; | |
| 166 | |
| 167 DriveUploader::DriveUploader( | |
| 168 DriveServiceInterface* drive_service, | |
| 169 const scoped_refptr<base::TaskRunner>& blocking_task_runner) | |
| 170 : drive_service_(drive_service), | |
| 171 blocking_task_runner_(blocking_task_runner), | |
| 172 weak_ptr_factory_(this) { | |
| 173 } | |
| 174 | |
| 175 DriveUploader::~DriveUploader() {} | |
| 176 | |
| 177 CancelCallback DriveUploader::UploadNewFile( | |
| 178 const std::string& parent_resource_id, | |
| 179 const base::FilePath& local_file_path, | |
| 180 const std::string& title, | |
| 181 const std::string& content_type, | |
| 182 const UploadNewFileOptions& options, | |
| 183 const UploadCompletionCallback& callback, | |
| 184 const ProgressCallback& progress_callback) { | |
| 185 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 186 DCHECK(!parent_resource_id.empty()); | |
| 187 DCHECK(!local_file_path.empty()); | |
| 188 DCHECK(!title.empty()); | |
| 189 DCHECK(!content_type.empty()); | |
| 190 DCHECK(!callback.is_null()); | |
| 191 | |
| 192 return StartUploadFile( | |
| 193 scoped_ptr<UploadFileInfo>(new UploadFileInfo( | |
| 194 local_file_path, content_type, callback, progress_callback)), | |
| 195 base::Bind(&DriveUploader::CallUploadServiceAPINewFile, | |
| 196 weak_ptr_factory_.GetWeakPtr(), parent_resource_id, title, | |
| 197 options, current_batch_request_)); | |
| 198 } | |
| 199 | |
| 200 void DriveUploader::StartBatchProcessing() { | |
| 201 DCHECK(current_batch_request_ == nullptr); | |
| 202 current_batch_request_ = | |
| 203 new RefCountedBatchRequest(drive_service_->StartBatchRequest().Pass()); | |
| 204 } | |
| 205 | |
| 206 void DriveUploader::StopBatchProcessing() { | |
| 207 current_batch_request_ = nullptr; | |
| 208 } | |
| 209 | |
| 210 CancelCallback DriveUploader::UploadExistingFile( | |
| 211 const std::string& resource_id, | |
| 212 const base::FilePath& local_file_path, | |
| 213 const std::string& content_type, | |
| 214 const UploadExistingFileOptions& options, | |
| 215 const UploadCompletionCallback& callback, | |
| 216 const ProgressCallback& progress_callback) { | |
| 217 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 218 DCHECK(!resource_id.empty()); | |
| 219 DCHECK(!local_file_path.empty()); | |
| 220 DCHECK(!content_type.empty()); | |
| 221 DCHECK(!callback.is_null()); | |
| 222 | |
| 223 return StartUploadFile( | |
| 224 scoped_ptr<UploadFileInfo>(new UploadFileInfo( | |
| 225 local_file_path, content_type, callback, progress_callback)), | |
| 226 base::Bind(&DriveUploader::CallUploadServiceAPIExistingFile, | |
| 227 weak_ptr_factory_.GetWeakPtr(), resource_id, options, | |
| 228 current_batch_request_)); | |
| 229 } | |
| 230 | |
| 231 CancelCallback DriveUploader::ResumeUploadFile( | |
| 232 const GURL& upload_location, | |
| 233 const base::FilePath& local_file_path, | |
| 234 const std::string& content_type, | |
| 235 const UploadCompletionCallback& callback, | |
| 236 const ProgressCallback& progress_callback) { | |
| 237 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 238 DCHECK(!local_file_path.empty()); | |
| 239 DCHECK(!content_type.empty()); | |
| 240 DCHECK(!callback.is_null()); | |
| 241 | |
| 242 scoped_ptr<UploadFileInfo> upload_file_info(new UploadFileInfo( | |
| 243 local_file_path, content_type, callback, progress_callback)); | |
| 244 upload_file_info->upload_location = upload_location; | |
| 245 | |
| 246 return StartUploadFile( | |
| 247 upload_file_info.Pass(), | |
| 248 base::Bind(&DriveUploader::StartGetUploadStatus, | |
| 249 weak_ptr_factory_.GetWeakPtr())); | |
| 250 } | |
| 251 | |
| 252 CancelCallback DriveUploader::StartUploadFile( | |
| 253 scoped_ptr<UploadFileInfo> upload_file_info, | |
| 254 const StartInitiateUploadCallback& start_initiate_upload_callback) { | |
| 255 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 256 DVLOG(1) << "Uploading file: " << upload_file_info->DebugString(); | |
| 257 | |
| 258 UploadFileInfo* info_ptr = upload_file_info.get(); | |
| 259 base::PostTaskAndReplyWithResult( | |
| 260 blocking_task_runner_.get(), | |
| 261 FROM_HERE, | |
| 262 base::Bind(&base::GetFileSize, | |
| 263 info_ptr->file_path, | |
| 264 &info_ptr->content_length), | |
| 265 base::Bind(&DriveUploader::StartUploadFileAfterGetFileSize, | |
| 266 weak_ptr_factory_.GetWeakPtr(), | |
| 267 base::Passed(&upload_file_info), | |
| 268 start_initiate_upload_callback)); | |
| 269 return info_ptr->GetCancelCallback(); | |
| 270 } | |
| 271 | |
| 272 void DriveUploader::StartUploadFileAfterGetFileSize( | |
| 273 scoped_ptr<UploadFileInfo> upload_file_info, | |
| 274 const StartInitiateUploadCallback& start_initiate_upload_callback, | |
| 275 bool get_file_size_result) { | |
| 276 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 277 | |
| 278 if (!get_file_size_result) { | |
| 279 UploadFailed(upload_file_info.Pass(), HTTP_NOT_FOUND); | |
| 280 return; | |
| 281 } | |
| 282 DCHECK_GE(upload_file_info->content_length, 0); | |
| 283 | |
| 284 if (upload_file_info->cancelled) { | |
| 285 UploadFailed(upload_file_info.Pass(), DRIVE_CANCELLED); | |
| 286 return; | |
| 287 } | |
| 288 start_initiate_upload_callback.Run(upload_file_info.Pass()); | |
| 289 } | |
| 290 | |
| 291 void DriveUploader::CallUploadServiceAPINewFile( | |
| 292 const std::string& parent_resource_id, | |
| 293 const std::string& title, | |
| 294 const UploadNewFileOptions& options, | |
| 295 const scoped_refptr<RefCountedBatchRequest>& batch_request, | |
| 296 scoped_ptr<UploadFileInfo> upload_file_info) { | |
| 297 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 298 | |
| 299 UploadFileInfo* const info_ptr = upload_file_info.get(); | |
| 300 if (info_ptr->content_length <= kMaxMultipartUploadSize) { | |
| 301 DriveServiceBatchOperationsInterface* service; | |
| 302 // If this is a batched request, calls the API on the request instead. | |
| 303 if (batch_request.get()) { | |
| 304 service = batch_request->configurator(); | |
| 305 RecordDriveUploadProtocol(UPLOAD_METHOD_BATCH); | |
| 306 } else { | |
| 307 service = drive_service_; | |
| 308 RecordDriveUploadProtocol(UPLOAD_METHOD_MULTIPART); | |
| 309 } | |
| 310 info_ptr->cancel_callback = service->MultipartUploadNewFile( | |
| 311 info_ptr->content_type, info_ptr->content_length, parent_resource_id, | |
| 312 title, info_ptr->file_path, options, | |
| 313 base::Bind(&DriveUploader::OnMultipartUploadComplete, | |
| 314 weak_ptr_factory_.GetWeakPtr(), | |
| 315 base::Passed(&upload_file_info)), | |
| 316 info_ptr->progress_callback); | |
| 317 } else { | |
| 318 RecordDriveUploadProtocol(UPLOAD_METHOD_RESUMABLE); | |
| 319 info_ptr->cancel_callback = drive_service_->InitiateUploadNewFile( | |
| 320 info_ptr->content_type, info_ptr->content_length, parent_resource_id, | |
| 321 title, options, base::Bind(&DriveUploader::OnUploadLocationReceived, | |
| 322 weak_ptr_factory_.GetWeakPtr(), | |
| 323 base::Passed(&upload_file_info))); | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 void DriveUploader::CallUploadServiceAPIExistingFile( | |
| 328 const std::string& resource_id, | |
| 329 const UploadExistingFileOptions& options, | |
| 330 const scoped_refptr<RefCountedBatchRequest>& batch_request, | |
| 331 scoped_ptr<UploadFileInfo> upload_file_info) { | |
| 332 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 333 | |
| 334 UploadFileInfo* const info_ptr = upload_file_info.get(); | |
| 335 if (info_ptr->content_length <= kMaxMultipartUploadSize) { | |
| 336 DriveServiceBatchOperationsInterface* service; | |
| 337 // If this is a batched request, calls the API on the request instead. | |
| 338 if (batch_request.get()) { | |
| 339 service = batch_request->configurator(); | |
| 340 RecordDriveUploadProtocol(UPLOAD_METHOD_BATCH); | |
| 341 } else { | |
| 342 service = drive_service_; | |
| 343 RecordDriveUploadProtocol(UPLOAD_METHOD_MULTIPART); | |
| 344 } | |
| 345 info_ptr->cancel_callback = service->MultipartUploadExistingFile( | |
| 346 info_ptr->content_type, info_ptr->content_length, resource_id, | |
| 347 info_ptr->file_path, options, | |
| 348 base::Bind(&DriveUploader::OnMultipartUploadComplete, | |
| 349 weak_ptr_factory_.GetWeakPtr(), | |
| 350 base::Passed(&upload_file_info)), | |
| 351 info_ptr->progress_callback); | |
| 352 } else { | |
| 353 RecordDriveUploadProtocol(UPLOAD_METHOD_RESUMABLE); | |
| 354 info_ptr->cancel_callback = drive_service_->InitiateUploadExistingFile( | |
| 355 info_ptr->content_type, info_ptr->content_length, resource_id, options, | |
| 356 base::Bind(&DriveUploader::OnUploadLocationReceived, | |
| 357 weak_ptr_factory_.GetWeakPtr(), | |
| 358 base::Passed(&upload_file_info))); | |
| 359 } | |
| 360 } | |
| 361 | |
| 362 void DriveUploader::OnUploadLocationReceived( | |
| 363 scoped_ptr<UploadFileInfo> upload_file_info, | |
| 364 DriveApiErrorCode code, | |
| 365 const GURL& upload_location) { | |
| 366 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 367 | |
| 368 DVLOG(1) << "Got upload location [" << upload_location.spec() | |
| 369 << "] for [" << upload_file_info->file_path.value() << "]"; | |
| 370 | |
| 371 if (code != HTTP_SUCCESS) { | |
| 372 if (code == HTTP_PRECONDITION) | |
| 373 code = HTTP_CONFLICT; // ETag mismatch. | |
| 374 UploadFailed(upload_file_info.Pass(), code); | |
| 375 return; | |
| 376 } | |
| 377 | |
| 378 upload_file_info->upload_location = upload_location; | |
| 379 upload_file_info->next_start_position = 0; | |
| 380 UploadNextChunk(upload_file_info.Pass()); | |
| 381 } | |
| 382 | |
| 383 void DriveUploader::StartGetUploadStatus( | |
| 384 scoped_ptr<UploadFileInfo> upload_file_info) { | |
| 385 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 386 DCHECK(upload_file_info); | |
| 387 | |
| 388 UploadFileInfo* info_ptr = upload_file_info.get(); | |
| 389 info_ptr->cancel_callback = drive_service_->GetUploadStatus( | |
| 390 info_ptr->upload_location, | |
| 391 info_ptr->content_length, | |
| 392 base::Bind(&DriveUploader::OnUploadRangeResponseReceived, | |
| 393 weak_ptr_factory_.GetWeakPtr(), | |
| 394 base::Passed(&upload_file_info))); | |
| 395 } | |
| 396 | |
| 397 void DriveUploader::UploadNextChunk( | |
| 398 scoped_ptr<UploadFileInfo> upload_file_info) { | |
| 399 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 400 DCHECK(upload_file_info); | |
| 401 DCHECK_GE(upload_file_info->next_start_position, 0); | |
| 402 DCHECK_LE(upload_file_info->next_start_position, | |
| 403 upload_file_info->content_length); | |
| 404 | |
| 405 if (upload_file_info->cancelled) { | |
| 406 UploadFailed(upload_file_info.Pass(), DRIVE_CANCELLED); | |
| 407 return; | |
| 408 } | |
| 409 | |
| 410 // Limit the size of data uploaded per each request by kUploadChunkSize. | |
| 411 const int64 end_position = std::min( | |
| 412 upload_file_info->content_length, | |
| 413 upload_file_info->next_start_position + kUploadChunkSize); | |
| 414 | |
| 415 UploadFileInfo* info_ptr = upload_file_info.get(); | |
| 416 info_ptr->cancel_callback = drive_service_->ResumeUpload( | |
| 417 info_ptr->upload_location, | |
| 418 info_ptr->next_start_position, | |
| 419 end_position, | |
| 420 info_ptr->content_length, | |
| 421 info_ptr->content_type, | |
| 422 info_ptr->file_path, | |
| 423 base::Bind(&DriveUploader::OnUploadRangeResponseReceived, | |
| 424 weak_ptr_factory_.GetWeakPtr(), | |
| 425 base::Passed(&upload_file_info)), | |
| 426 base::Bind(&DriveUploader::OnUploadProgress, | |
| 427 weak_ptr_factory_.GetWeakPtr(), | |
| 428 info_ptr->progress_callback, | |
| 429 info_ptr->next_start_position, | |
| 430 info_ptr->content_length)); | |
| 431 } | |
| 432 | |
| 433 void DriveUploader::OnUploadRangeResponseReceived( | |
| 434 scoped_ptr<UploadFileInfo> upload_file_info, | |
| 435 const UploadRangeResponse& response, | |
| 436 scoped_ptr<FileResource> entry) { | |
| 437 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 438 | |
| 439 if (response.code == HTTP_CREATED || response.code == HTTP_SUCCESS) { | |
| 440 // When uploading a new file, we expect HTTP_CREATED, and when uploading | |
| 441 // an existing file (to overwrite), we expect HTTP_SUCCESS. | |
| 442 // There is an exception: if we uploading an empty file, uploading a new | |
| 443 // file also returns HTTP_SUCCESS on Drive API v2. The correct way of the | |
| 444 // fix should be uploading the metadata only. However, to keep the | |
| 445 // compatibility with GData WAPI during the migration period, we just | |
| 446 // relax the condition here. | |
| 447 // TODO(hidehiko): Upload metadata only for empty files, after GData WAPI | |
| 448 // code is gone. | |
| 449 DVLOG(1) << "Successfully created uploaded file=[" | |
| 450 << upload_file_info->file_path.value() << "]"; | |
| 451 | |
| 452 // Done uploading. | |
| 453 upload_file_info->completion_callback.Run( | |
| 454 HTTP_SUCCESS, GURL(), entry.Pass()); | |
| 455 return; | |
| 456 } | |
| 457 | |
| 458 // ETag mismatch. | |
| 459 if (response.code == HTTP_PRECONDITION) { | |
| 460 UploadFailed(upload_file_info.Pass(), HTTP_CONFLICT); | |
| 461 return; | |
| 462 } | |
| 463 | |
| 464 // If code is 308 (RESUME_INCOMPLETE) and |range_received| starts with 0 | |
| 465 // (meaning that the data is uploaded from the beginning of the file), | |
| 466 // proceed to upload the next chunk. | |
| 467 if (response.code != HTTP_RESUME_INCOMPLETE || | |
| 468 response.start_position_received != 0) { | |
| 469 DVLOG(1) | |
| 470 << "UploadNextChunk http code=" << response.code | |
| 471 << ", start_position_received=" << response.start_position_received | |
| 472 << ", end_position_received=" << response.end_position_received; | |
| 473 UploadFailed( | |
| 474 upload_file_info.Pass(), | |
| 475 response.code == HTTP_FORBIDDEN ? DRIVE_NO_SPACE : response.code); | |
| 476 return; | |
| 477 } | |
| 478 | |
| 479 DVLOG(1) << "Received range " << response.start_position_received | |
| 480 << "-" << response.end_position_received | |
| 481 << " for [" << upload_file_info->file_path.value() << "]"; | |
| 482 | |
| 483 upload_file_info->next_start_position = response.end_position_received; | |
| 484 UploadNextChunk(upload_file_info.Pass()); | |
| 485 } | |
| 486 | |
| 487 void DriveUploader::OnUploadProgress(const ProgressCallback& callback, | |
| 488 int64 start_position, | |
| 489 int64 total_size, | |
| 490 int64 progress_of_chunk, | |
| 491 int64 total_of_chunk) { | |
| 492 if (!callback.is_null()) | |
| 493 callback.Run(start_position + progress_of_chunk, total_size); | |
| 494 } | |
| 495 | |
| 496 void DriveUploader::UploadFailed(scoped_ptr<UploadFileInfo> upload_file_info, | |
| 497 DriveApiErrorCode error) { | |
| 498 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 499 | |
| 500 DVLOG(1) << "Upload failed " << upload_file_info->DebugString(); | |
| 501 | |
| 502 if (upload_file_info->next_start_position < 0) { | |
| 503 // Discard the upload location because no request could succeed with it. | |
| 504 // Maybe it's obsolete. | |
| 505 upload_file_info->upload_location = GURL(); | |
| 506 } | |
| 507 | |
| 508 upload_file_info->completion_callback.Run( | |
| 509 error, upload_file_info->upload_location, scoped_ptr<FileResource>()); | |
| 510 } | |
| 511 | |
| 512 void DriveUploader::OnMultipartUploadComplete( | |
| 513 scoped_ptr<UploadFileInfo> upload_file_info, | |
| 514 google_apis::DriveApiErrorCode error, | |
| 515 scoped_ptr<FileResource> entry) { | |
| 516 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 517 | |
| 518 if (error == HTTP_CREATED || error == HTTP_SUCCESS) { | |
| 519 DVLOG(1) << "Successfully created uploaded file=[" | |
| 520 << upload_file_info->file_path.value() << "]"; | |
| 521 // Done uploading. | |
| 522 upload_file_info->completion_callback.Run( | |
| 523 HTTP_SUCCESS, upload_file_info->upload_location, entry.Pass()); | |
| 524 } else { | |
| 525 DVLOG(1) << "Upload failed " << upload_file_info->DebugString(); | |
| 526 if (error == HTTP_PRECONDITION) | |
| 527 error = HTTP_CONFLICT; // ETag mismatch. | |
| 528 upload_file_info->completion_callback.Run( | |
| 529 error, upload_file_info->upload_location, scoped_ptr<FileResource>()); | |
| 530 } | |
| 531 } | |
| 532 | |
| 533 } // namespace drive | |
| OLD | NEW |