| 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/chromeos/gdata/drive_uploader.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/callback.h" | |
| 11 #include "base/string_number_conversions.h" | |
| 12 #include "chrome/browser/chromeos/gdata/drive_service_interface.h" | |
| 13 #include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h" | |
| 14 #include "content/public/browser/browser_thread.h" | |
| 15 #include "content/public/browser/download_item.h" | |
| 16 #include "net/base/file_stream.h" | |
| 17 #include "net/base/net_errors.h" | |
| 18 | |
| 19 using content::BrowserThread; | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 // Google Documents List API requires uploading in chunks of 512kB. | |
| 24 const int64 kUploadChunkSize = 512 * 1024; | |
| 25 | |
| 26 // Maximum number of times we try to open a file before giving up. | |
| 27 const int kMaxFileOpenTries = 5; | |
| 28 | |
| 29 } // namespace | |
| 30 | |
| 31 namespace gdata { | |
| 32 | |
| 33 DriveUploader::DriveUploader(DriveServiceInterface* drive_service) | |
| 34 : drive_service_(drive_service), | |
| 35 next_upload_id_(0), | |
| 36 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
| 37 } | |
| 38 | |
| 39 DriveUploader::~DriveUploader() { | |
| 40 } | |
| 41 | |
| 42 int DriveUploader::UploadNewFile( | |
| 43 const GURL& upload_location, | |
| 44 const FilePath& drive_file_path, | |
| 45 const FilePath& local_file_path, | |
| 46 const std::string& title, | |
| 47 const std::string& content_type, | |
| 48 int64 content_length, | |
| 49 int64 file_size, | |
| 50 const UploadCompletionCallback& callback) { | |
| 51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 52 DCHECK(!upload_location.is_empty()); | |
| 53 DCHECK(!drive_file_path.empty()); | |
| 54 DCHECK(!local_file_path.empty()); | |
| 55 DCHECK(!title.empty()); | |
| 56 DCHECK(!content_type.empty()); | |
| 57 | |
| 58 scoped_ptr<UploadFileInfo> upload_file_info(new UploadFileInfo); | |
| 59 upload_file_info->upload_mode = UPLOAD_NEW_FILE; | |
| 60 upload_file_info->initial_upload_location = upload_location; | |
| 61 upload_file_info->drive_path = drive_file_path; | |
| 62 upload_file_info->file_path = local_file_path; | |
| 63 upload_file_info->title = title; | |
| 64 upload_file_info->content_type = content_type; | |
| 65 upload_file_info->content_length = content_length; | |
| 66 upload_file_info->file_size = file_size; | |
| 67 upload_file_info->all_bytes_present = content_length == file_size; | |
| 68 upload_file_info->completion_callback = callback; | |
| 69 | |
| 70 // When uploading a new file, we should retry file open as the file may | |
| 71 // not yet be ready. See comments in OpenCompletionCallback. | |
| 72 // TODO(satorux): The retry should be done only when we are uploading | |
| 73 // while downloading files from web sites (i.e. saving files to Drive). | |
| 74 upload_file_info->should_retry_file_open = true; | |
| 75 return StartUploadFile(upload_file_info.Pass()); | |
| 76 } | |
| 77 | |
| 78 int DriveUploader::StreamExistingFile( | |
| 79 const GURL& upload_location, | |
| 80 const FilePath& drive_file_path, | |
| 81 const FilePath& local_file_path, | |
| 82 const std::string& content_type, | |
| 83 int64 content_length, | |
| 84 int64 file_size, | |
| 85 const UploadCompletionCallback& callback) { | |
| 86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 87 DCHECK(!upload_location.is_empty()); | |
| 88 DCHECK(!drive_file_path.empty()); | |
| 89 DCHECK(!local_file_path.empty()); | |
| 90 DCHECK(!content_type.empty()); | |
| 91 | |
| 92 scoped_ptr<UploadFileInfo> upload_file_info(new UploadFileInfo); | |
| 93 upload_file_info->upload_mode = UPLOAD_EXISTING_FILE; | |
| 94 upload_file_info->initial_upload_location = upload_location; | |
| 95 upload_file_info->drive_path = drive_file_path; | |
| 96 upload_file_info->file_path = local_file_path; | |
| 97 upload_file_info->content_type = content_type; | |
| 98 upload_file_info->content_length = content_length; | |
| 99 upload_file_info->file_size = file_size; | |
| 100 upload_file_info->all_bytes_present = content_length == file_size; | |
| 101 upload_file_info->completion_callback = callback; | |
| 102 | |
| 103 // When uploading a new file, we should retry file open as the file may | |
| 104 // not yet be ready. See comments in OpenCompletionCallback. | |
| 105 // TODO(satorux): The retry should be done only when we are uploading | |
| 106 // while downloading files from web sites (i.e. saving files to Drive). | |
| 107 upload_file_info->should_retry_file_open = true; | |
| 108 return StartUploadFile(upload_file_info.Pass()); | |
| 109 } | |
| 110 | |
| 111 int DriveUploader::StartUploadFile( | |
| 112 scoped_ptr<UploadFileInfo> upload_file_info) { | |
| 113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 114 DCHECK(upload_file_info.get()); | |
| 115 DCHECK_EQ(upload_file_info->upload_id, -1); | |
| 116 DCHECK_NE(UPLOAD_INVALID, upload_file_info->upload_mode); | |
| 117 | |
| 118 const int upload_id = next_upload_id_++; | |
| 119 upload_file_info->upload_id = upload_id; | |
| 120 | |
| 121 // Add upload_file_info to our internal map and take ownership. | |
| 122 pending_uploads_[upload_id] = upload_file_info.release(); | |
| 123 UploadFileInfo* info = GetUploadFileInfo(upload_id); | |
| 124 DVLOG(1) << "Uploading file: " << info->DebugString(); | |
| 125 | |
| 126 // Create a FileStream to make sure the file can be opened successfully. | |
| 127 info->file_stream = new net::FileStream(NULL); | |
| 128 | |
| 129 // Create buffer to hold upload data. The full file size may not be known at | |
| 130 // this point, so it may not be appropriate to use info->file_size. | |
| 131 info->buf_len = kUploadChunkSize; | |
| 132 info->buf = new net::IOBuffer(info->buf_len); | |
| 133 | |
| 134 OpenFile(info); | |
| 135 return upload_id; | |
| 136 } | |
| 137 | |
| 138 int DriveUploader::UploadExistingFile( | |
| 139 const GURL& upload_location, | |
| 140 const FilePath& drive_file_path, | |
| 141 const FilePath& local_file_path, | |
| 142 const std::string& content_type, | |
| 143 int64 file_size, | |
| 144 const UploadCompletionCallback& callback) { | |
| 145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 146 DCHECK(!upload_location.is_empty()); | |
| 147 DCHECK(!drive_file_path.empty()); | |
| 148 DCHECK(!local_file_path.empty()); | |
| 149 DCHECK(!content_type.empty()); | |
| 150 | |
| 151 scoped_ptr<UploadFileInfo> upload_file_info(new UploadFileInfo); | |
| 152 upload_file_info->upload_mode = UPLOAD_EXISTING_FILE; | |
| 153 upload_file_info->initial_upload_location = upload_location; | |
| 154 upload_file_info->drive_path = drive_file_path; | |
| 155 upload_file_info->file_path = local_file_path; | |
| 156 upload_file_info->content_type = content_type; | |
| 157 upload_file_info->content_length = file_size; | |
| 158 upload_file_info->file_size = file_size; | |
| 159 upload_file_info->all_bytes_present = true; | |
| 160 upload_file_info->completion_callback = callback; | |
| 161 | |
| 162 // When uploading an updated file, we should not retry file open as the | |
| 163 // file should already be present by definition. | |
| 164 upload_file_info->should_retry_file_open = false; | |
| 165 return StartUploadFile(upload_file_info.Pass()); | |
| 166 } | |
| 167 | |
| 168 void DriveUploader::UpdateUpload(int upload_id, | |
| 169 content::DownloadItem* download) { | |
| 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 171 | |
| 172 UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id); | |
| 173 if (!upload_file_info) | |
| 174 return; | |
| 175 | |
| 176 const int64 file_size = download->GetReceivedBytes(); | |
| 177 | |
| 178 // Update file_size and all_bytes_present. | |
| 179 DVLOG(1) << "Updating file size from " << upload_file_info->file_size | |
| 180 << " to " << file_size | |
| 181 << (download->AllDataSaved() ? " (AllDataSaved)" : " (In-progress)"); | |
| 182 upload_file_info->file_size = file_size; | |
| 183 upload_file_info->all_bytes_present = download->AllDataSaved(); | |
| 184 if (upload_file_info->file_path != download->GetFullPath()) { | |
| 185 // We shouldn't see a rename if should_retry_file_open is true. The only | |
| 186 // rename we expect (for now) is the final rename that happens after the | |
| 187 // download transition from IN_PROGRESS -> COMPLETE. This, in turn, only | |
| 188 // happens after the upload completes. However, since this isn't enforced by | |
| 189 // the API contract, we reset the retry count so we can retry all over again | |
| 190 // with the new path. | |
| 191 // TODO(asanka): Introduce a synchronization point after the initial rename | |
| 192 // of the download and get rid of the retry logic. | |
| 193 upload_file_info->num_file_open_tries = 0; | |
| 194 upload_file_info->file_path = download->GetFullPath(); | |
| 195 } | |
| 196 | |
| 197 // Resume upload if necessary and possible. | |
| 198 if (upload_file_info->upload_paused && | |
| 199 (upload_file_info->all_bytes_present || | |
| 200 upload_file_info->SizeRemaining() > kUploadChunkSize)) { | |
| 201 DVLOG(1) << "Resuming upload " << upload_file_info->title; | |
| 202 upload_file_info->upload_paused = false; | |
| 203 UploadNextChunk(upload_file_info); | |
| 204 } | |
| 205 | |
| 206 // Retry opening this file if we failed before. File open can fail because | |
| 207 // the downloads system sets the full path on the UI thread and schedules a | |
| 208 // rename on the FILE thread. Thus the new path is visible on the UI thread | |
| 209 // before the renamed file is available on the file system. | |
| 210 if (upload_file_info->should_retry_file_open) { | |
| 211 DCHECK(!download->IsComplete()); | |
| 212 // Disallow further retries. | |
| 213 upload_file_info->should_retry_file_open = false; | |
| 214 OpenFile(upload_file_info); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 int64 DriveUploader::GetUploadedBytes(int upload_id) const { | |
| 219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 220 UploadFileInfo* upload_info = GetUploadFileInfo(upload_id); | |
| 221 // We return the start_range as the count of uploaded bytes since that is the | |
| 222 // start of the next or currently uploading chunk. | |
| 223 // TODO(asanka): Use a finer grained progress value than this. We end up | |
| 224 // reporting progress in kUploadChunkSize increments. | |
| 225 return upload_info ? upload_info->start_range : 0; | |
| 226 } | |
| 227 | |
| 228 DriveUploader::UploadFileInfo* DriveUploader::GetUploadFileInfo( | |
| 229 int upload_id) const { | |
| 230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 231 | |
| 232 UploadFileInfoMap::const_iterator it = pending_uploads_.find(upload_id); | |
| 233 DVLOG_IF(1, it == pending_uploads_.end()) << "No upload found for id " | |
| 234 << upload_id; | |
| 235 return it != pending_uploads_.end() ? it->second : NULL; | |
| 236 } | |
| 237 | |
| 238 void DriveUploader::OpenFile(UploadFileInfo* upload_file_info) { | |
| 239 // Open the file asynchronously. | |
| 240 const int rv = upload_file_info->file_stream->Open( | |
| 241 upload_file_info->file_path, | |
| 242 base::PLATFORM_FILE_OPEN | | |
| 243 base::PLATFORM_FILE_READ | | |
| 244 base::PLATFORM_FILE_ASYNC, | |
| 245 base::Bind(&DriveUploader::OpenCompletionCallback, | |
| 246 weak_ptr_factory_.GetWeakPtr(), | |
| 247 upload_file_info->upload_id)); | |
| 248 DCHECK_EQ(net::ERR_IO_PENDING, rv); | |
| 249 } | |
| 250 | |
| 251 void DriveUploader::OpenCompletionCallback(int upload_id, int result) { | |
| 252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 253 | |
| 254 UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id); | |
| 255 if (!upload_file_info) | |
| 256 return; | |
| 257 | |
| 258 // The file may actually not exist yet, as the downloads system downloads | |
| 259 // to a temp location and then renames the file. If this is the case, we | |
| 260 // just retry opening the file later. | |
| 261 if (result != net::OK) { | |
| 262 DCHECK_EQ(result, net::ERR_FILE_NOT_FOUND); | |
| 263 | |
| 264 if (upload_file_info->should_retry_file_open) { | |
| 265 // File open failed. Try again later. | |
| 266 upload_file_info->num_file_open_tries++; | |
| 267 | |
| 268 DVLOG(1) << "Error opening \"" << upload_file_info->file_path.value() | |
| 269 << "\" for reading: " << net::ErrorToString(result) | |
| 270 << ", tries=" << upload_file_info->num_file_open_tries; | |
| 271 | |
| 272 // Stop trying to open this file if we exceed kMaxFileOpenTries. | |
| 273 const bool exceeded_max_attempts = | |
| 274 upload_file_info->num_file_open_tries >= kMaxFileOpenTries; | |
| 275 upload_file_info->should_retry_file_open = !exceeded_max_attempts; | |
| 276 } | |
| 277 if (!upload_file_info->should_retry_file_open) { | |
| 278 UploadFailed(scoped_ptr<UploadFileInfo>(upload_file_info), | |
| 279 DRIVE_FILE_ERROR_NOT_FOUND); | |
| 280 } | |
| 281 return; | |
| 282 } | |
| 283 | |
| 284 // Open succeeded, initiate the upload. | |
| 285 upload_file_info->should_retry_file_open = false; | |
| 286 if (upload_file_info->initial_upload_location.is_empty()) { | |
| 287 UploadFailed(scoped_ptr<UploadFileInfo>(upload_file_info), | |
| 288 DRIVE_FILE_ERROR_ABORT); | |
| 289 return; | |
| 290 } | |
| 291 drive_service_->InitiateUpload( | |
| 292 InitiateUploadParams(upload_file_info->upload_mode, | |
| 293 upload_file_info->title, | |
| 294 upload_file_info->content_type, | |
| 295 upload_file_info->content_length, | |
| 296 upload_file_info->initial_upload_location, | |
| 297 upload_file_info->drive_path), | |
| 298 base::Bind(&DriveUploader::OnUploadLocationReceived, | |
| 299 weak_ptr_factory_.GetWeakPtr(), | |
| 300 upload_file_info->upload_id)); | |
| 301 } | |
| 302 | |
| 303 void DriveUploader::OnUploadLocationReceived( | |
| 304 int upload_id, | |
| 305 GDataErrorCode code, | |
| 306 const GURL& upload_location) { | |
| 307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 308 | |
| 309 UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id); | |
| 310 if (!upload_file_info) | |
| 311 return; | |
| 312 | |
| 313 DVLOG(1) << "Got upload location [" << upload_location.spec() | |
| 314 << "] for [" << upload_file_info->title << "]"; | |
| 315 | |
| 316 if (code != HTTP_SUCCESS) { | |
| 317 // TODO(achuith): Handle error codes from Google Docs server. | |
| 318 UploadFailed(scoped_ptr<UploadFileInfo>(upload_file_info), | |
| 319 DRIVE_FILE_ERROR_ABORT); | |
| 320 return; | |
| 321 } | |
| 322 | |
| 323 upload_file_info->upload_location = upload_location; | |
| 324 | |
| 325 // Start the upload from the beginning of the file. | |
| 326 UploadNextChunk(upload_file_info); | |
| 327 } | |
| 328 | |
| 329 void DriveUploader::UploadNextChunk(UploadFileInfo* upload_file_info) { | |
| 330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 331 // Check that |upload_file_info| is in pending_uploads_. | |
| 332 DCHECK(upload_file_info == GetUploadFileInfo(upload_file_info->upload_id)); | |
| 333 DVLOG(1) << "Number of pending uploads=" << pending_uploads_.size(); | |
| 334 | |
| 335 // Determine number of bytes to read for this upload iteration, which cannot | |
| 336 // exceed size of buf i.e. buf_len. | |
| 337 const int64 bytes_remaining = upload_file_info->SizeRemaining(); | |
| 338 const int bytes_to_read = std::min(upload_file_info->SizeRemaining(), | |
| 339 upload_file_info->buf_len); | |
| 340 | |
| 341 // Update the content length if the file_size is known. | |
| 342 if (upload_file_info->all_bytes_present) | |
| 343 upload_file_info->content_length = upload_file_info->file_size; | |
| 344 else if (bytes_remaining == bytes_to_read) { | |
| 345 // Wait for more data if this is the last chunk we have and we don't know | |
| 346 // whether we've reached the end of the file. We won't know how much data to | |
| 347 // expect until the transfer is complete (the Content-Length might be | |
| 348 // incorrect or absent). If we've sent the last chunk out already when we | |
| 349 // find out there's no more data, we won't be able to complete the upload. | |
| 350 DVLOG(1) << "Paused upload " << upload_file_info->title; | |
| 351 upload_file_info->upload_paused = true; | |
| 352 return; | |
| 353 } | |
| 354 | |
| 355 if (bytes_to_read == 0) { | |
| 356 // This should only happen when the actual file size is 0. | |
| 357 DCHECK(upload_file_info->all_bytes_present && | |
| 358 upload_file_info->content_length == 0); | |
| 359 | |
| 360 upload_file_info->start_range = 0; | |
| 361 upload_file_info->end_range = -1; | |
| 362 // Skips file_stream->Read and error checks for 0-byte case. Immediately | |
| 363 // proceeds to ResumeUpload. | |
| 364 // TODO(kinaba): http://crbug.com/134814 | |
| 365 // Replace the following PostTask() to an direct method call. This is needed | |
| 366 // because we have to ResumeUpload after the previous InitiateUpload or | |
| 367 // ResumeUpload is completely finished; at this point, we are inside the | |
| 368 // callback function from the previous operation, which is not treated as | |
| 369 // finished yet. | |
| 370 base::MessageLoopProxy::current()->PostTask( | |
| 371 FROM_HERE, | |
| 372 base::Bind(&DriveUploader::ResumeUpload, | |
| 373 weak_ptr_factory_.GetWeakPtr(), | |
| 374 upload_file_info->upload_id)); | |
| 375 return; | |
| 376 } | |
| 377 | |
| 378 upload_file_info->file_stream->Read( | |
| 379 upload_file_info->buf, | |
| 380 bytes_to_read, | |
| 381 base::Bind(&DriveUploader::ReadCompletionCallback, | |
| 382 weak_ptr_factory_.GetWeakPtr(), | |
| 383 upload_file_info->upload_id, | |
| 384 bytes_to_read)); | |
| 385 } | |
| 386 | |
| 387 void DriveUploader::ReadCompletionCallback( | |
| 388 int upload_id, | |
| 389 int bytes_to_read, | |
| 390 int bytes_read) { | |
| 391 // The Read is asynchronously executed on BrowserThread::UI, where | |
| 392 // Read() was called. | |
| 393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 394 DVLOG(1) << "ReadCompletionCallback bytes read=" << bytes_read; | |
| 395 | |
| 396 UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id); | |
| 397 if (!upload_file_info) | |
| 398 return; | |
| 399 | |
| 400 // TODO(achuith): Handle this error. | |
| 401 DCHECK_EQ(bytes_to_read, bytes_read); | |
| 402 DCHECK_GT(bytes_read, 0) << "Error reading from file " | |
| 403 << upload_file_info->file_path.value(); | |
| 404 | |
| 405 upload_file_info->start_range = upload_file_info->end_range + 1; | |
| 406 upload_file_info->end_range = upload_file_info->start_range + | |
| 407 bytes_read - 1; | |
| 408 | |
| 409 ResumeUpload(upload_id); | |
| 410 } | |
| 411 | |
| 412 void DriveUploader::ResumeUpload(int upload_id) { | |
| 413 UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id); | |
| 414 if (!upload_file_info) | |
| 415 return; | |
| 416 | |
| 417 drive_service_->ResumeUpload( | |
| 418 ResumeUploadParams(upload_file_info->upload_mode, | |
| 419 upload_file_info->start_range, | |
| 420 upload_file_info->end_range, | |
| 421 upload_file_info->content_length, | |
| 422 upload_file_info->content_type, | |
| 423 upload_file_info->buf, | |
| 424 upload_file_info->upload_location, | |
| 425 upload_file_info->drive_path), | |
| 426 base::Bind(&DriveUploader::OnResumeUploadResponseReceived, | |
| 427 weak_ptr_factory_.GetWeakPtr(), | |
| 428 upload_file_info->upload_id)); | |
| 429 } | |
| 430 | |
| 431 void DriveUploader::OnResumeUploadResponseReceived( | |
| 432 int upload_id, | |
| 433 const ResumeUploadResponse& response, | |
| 434 scoped_ptr<DocumentEntry> entry) { | |
| 435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 436 | |
| 437 UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id); | |
| 438 if (!upload_file_info) | |
| 439 return; | |
| 440 | |
| 441 const UploadMode upload_mode = upload_file_info->upload_mode; | |
| 442 if ((upload_mode == UPLOAD_NEW_FILE && response.code == HTTP_CREATED) || | |
| 443 (upload_mode == UPLOAD_EXISTING_FILE && response.code == HTTP_SUCCESS)) { | |
| 444 DVLOG(1) << "Successfully created uploaded file=[" | |
| 445 << upload_file_info->title; | |
| 446 | |
| 447 // Remove |upload_id| from the UploadFileInfoMap. The UploadFileInfo object | |
| 448 // will be deleted upon completion of completion_callback. | |
| 449 RemoveUpload(upload_id); | |
| 450 | |
| 451 // Done uploading. | |
| 452 upload_file_info->entry = entry.Pass(); | |
| 453 if (!upload_file_info->completion_callback.is_null()) { | |
| 454 upload_file_info->completion_callback.Run( | |
| 455 DRIVE_FILE_OK, | |
| 456 upload_file_info->drive_path, | |
| 457 upload_file_info->file_path, | |
| 458 upload_file_info->entry.Pass()); | |
| 459 } | |
| 460 return; | |
| 461 } | |
| 462 | |
| 463 // If code is 308 (RESUME_INCOMPLETE) and range_received is what has been | |
| 464 // previously uploaded (i.e. = upload_file_info->end_range), proceed to | |
| 465 // upload the next chunk. | |
| 466 if (response.code != HTTP_RESUME_INCOMPLETE || | |
| 467 response.start_range_received != 0 || | |
| 468 response.end_range_received != upload_file_info->end_range) { | |
| 469 // TODO(achuith): Handle error cases, e.g. | |
| 470 // - when previously uploaded data wasn't received by Google Docs server, | |
| 471 // i.e. when end_range_received < upload_file_info->end_range | |
| 472 LOG(ERROR) << "UploadNextChunk http code=" << response.code | |
| 473 << ", start_range_received=" << response.start_range_received | |
| 474 << ", end_range_received=" << response.end_range_received | |
| 475 << ", expected end range=" << upload_file_info->end_range; | |
| 476 UploadFailed( | |
| 477 scoped_ptr<UploadFileInfo>(upload_file_info), | |
| 478 response.code == HTTP_FORBIDDEN ? | |
| 479 DRIVE_FILE_ERROR_NO_SPACE : DRIVE_FILE_ERROR_ABORT); | |
| 480 return; | |
| 481 } | |
| 482 | |
| 483 DVLOG(1) << "Received range " << response.start_range_received | |
| 484 << "-" << response.end_range_received | |
| 485 << " for [" << upload_file_info->title << "]"; | |
| 486 | |
| 487 // Continue uploading. | |
| 488 UploadNextChunk(upload_file_info); | |
| 489 } | |
| 490 | |
| 491 void DriveUploader::UploadFailed(scoped_ptr<UploadFileInfo> upload_file_info, | |
| 492 DriveFileError error) { | |
| 493 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 494 | |
| 495 RemoveUpload(upload_file_info->upload_id); | |
| 496 | |
| 497 LOG(ERROR) << "Upload failed " << upload_file_info->DebugString(); | |
| 498 // This is subtle but we should take the callback reference before | |
| 499 // calling upload_file_info.Pass(). Otherwise, it'll crash. | |
| 500 const UploadCompletionCallback& callback = | |
| 501 upload_file_info->completion_callback; | |
| 502 if (!callback.is_null()) | |
| 503 callback.Run(error, | |
| 504 upload_file_info->drive_path, | |
| 505 upload_file_info->file_path, | |
| 506 upload_file_info->entry.Pass()); | |
| 507 } | |
| 508 | |
| 509 void DriveUploader::RemoveUpload(int upload_id) { | |
| 510 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 511 pending_uploads_.erase(upload_id); | |
| 512 } | |
| 513 | |
| 514 DriveUploader::UploadFileInfo::UploadFileInfo() | |
| 515 : upload_id(-1), | |
| 516 file_size(0), | |
| 517 content_length(0), | |
| 518 upload_mode(UPLOAD_INVALID), | |
| 519 file_stream(NULL), | |
| 520 buf_len(0), | |
| 521 start_range(0), | |
| 522 end_range(-1), | |
| 523 all_bytes_present(false), | |
| 524 upload_paused(false), | |
| 525 should_retry_file_open(false), | |
| 526 num_file_open_tries(0) { | |
| 527 } | |
| 528 | |
| 529 DriveUploader::UploadFileInfo::~UploadFileInfo() { | |
| 530 // The file stream is closed by the destructor asynchronously. | |
| 531 if (file_stream) { | |
| 532 delete file_stream; | |
| 533 file_stream = NULL; | |
| 534 } | |
| 535 } | |
| 536 | |
| 537 int64 DriveUploader::UploadFileInfo::SizeRemaining() const { | |
| 538 DCHECK(file_size > end_range); | |
| 539 // Note that uploaded_bytes = end_range + 1; | |
| 540 return file_size - end_range - 1; | |
| 541 } | |
| 542 | |
| 543 std::string DriveUploader::UploadFileInfo::DebugString() const { | |
| 544 return "title=[" + title + | |
| 545 "], file_path=[" + file_path.value() + | |
| 546 "], content_type=[" + content_type + | |
| 547 "], content_length=[" + base::UintToString(content_length) + | |
| 548 "], file_size=[" + base::UintToString(file_size) + | |
| 549 "], drive_path=[" + drive_path.value() + | |
| 550 "]"; | |
| 551 } | |
| 552 | |
| 553 } // namespace gdata | |
| OLD | NEW |