| OLD | NEW |
| 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/chromeos/gdata/gdata_uploader.h" | 5 #include "chrome/browser/chromeos/gdata/gdata_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" |
| (...skipping 21 matching lines...) Expand all Loading... |
| 32 | 32 |
| 33 GDataUploader::GDataUploader(GDataFileSystem* file_system) | 33 GDataUploader::GDataUploader(GDataFileSystem* file_system) |
| 34 : file_system_(file_system), | 34 : file_system_(file_system), |
| 35 next_upload_id_(0), | 35 next_upload_id_(0), |
| 36 ALLOW_THIS_IN_INITIALIZER_LIST(uploader_factory_(this)) { | 36 ALLOW_THIS_IN_INITIALIZER_LIST(uploader_factory_(this)) { |
| 37 } | 37 } |
| 38 | 38 |
| 39 GDataUploader::~GDataUploader() { | 39 GDataUploader::~GDataUploader() { |
| 40 } | 40 } |
| 41 | 41 |
| 42 void GDataUploader::UploadFile(UploadFileInfo* upload_file_info) { | 42 int GDataUploader::UploadFile(scoped_ptr<UploadFileInfo> upload_file_info) { |
| 43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 44 DCHECK(upload_file_info); | 44 DCHECK(upload_file_info.get()); |
| 45 DCHECK_EQ(upload_file_info->upload_id, -1); | 45 DCHECK_EQ(upload_file_info->upload_id, -1); |
| 46 DCHECK(!upload_file_info->file_path.empty()); |
| 47 DCHECK_NE(upload_file_info->file_size, 0); |
| 48 DCHECK(!upload_file_info->gdata_path.empty()); |
| 49 DCHECK(!upload_file_info->title.empty()); |
| 50 DCHECK(!upload_file_info->content_type.empty()); |
| 46 | 51 |
| 47 upload_file_info->upload_id = next_upload_id_++; | 52 const int upload_id = next_upload_id_++; |
| 53 upload_file_info->upload_id = upload_id; |
| 48 // Add upload_file_info to our internal map and take ownership. | 54 // Add upload_file_info to our internal map and take ownership. |
| 49 pending_uploads_[upload_file_info->upload_id] = upload_file_info; | 55 pending_uploads_[upload_id] = upload_file_info.release(); |
| 50 DVLOG(1) << "Uploading file: " << upload_file_info->DebugString(); | 56 |
| 57 UploadFileInfo* info = GetUploadFileInfo(upload_id); |
| 58 DVLOG(1) << "Uploading file: " << info->DebugString(); |
| 51 | 59 |
| 52 // Create a FileStream to make sure the file can be opened successfully. | 60 // Create a FileStream to make sure the file can be opened successfully. |
| 53 upload_file_info->file_stream = new net::FileStream(NULL); | 61 info->file_stream = new net::FileStream(NULL); |
| 54 | 62 |
| 55 // Create buffer to hold upload data. | 63 // Create buffer to hold upload data. |
| 56 upload_file_info->buf_len = std::min(upload_file_info->file_size, | 64 info->buf_len = std::min(info->file_size, kUploadChunkSize); |
| 57 kUploadChunkSize); | 65 info->buf = new net::IOBuffer(info->buf_len); |
| 58 upload_file_info->buf = new net::IOBuffer(upload_file_info->buf_len); | |
| 59 | 66 |
| 60 OpenFile(upload_file_info); | 67 OpenFile(info); |
| 68 return upload_id; |
| 61 } | 69 } |
| 62 | 70 |
| 63 void GDataUploader::UpdateUpload(int upload_id, | 71 void GDataUploader::UpdateUpload(int upload_id, |
| 64 content::DownloadItem* download) { | 72 content::DownloadItem* download) { |
| 65 UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id); | 73 UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id); |
| 66 if (!upload_file_info) | 74 if (!upload_file_info) |
| 67 return; | 75 return; |
| 68 | 76 |
| 69 const int64 file_size = download->GetReceivedBytes(); | 77 const int64 file_size = download->GetReceivedBytes(); |
| 70 | 78 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 101 // rename on the FILE thread. Thus the new path is visible on the UI thread | 109 // rename on the FILE thread. Thus the new path is visible on the UI thread |
| 102 // before the renamed file is available on the file system. | 110 // before the renamed file is available on the file system. |
| 103 if (upload_file_info->should_retry_file_open) { | 111 if (upload_file_info->should_retry_file_open) { |
| 104 DCHECK(!download->IsComplete()); | 112 DCHECK(!download->IsComplete()); |
| 105 // Disallow further retries. | 113 // Disallow further retries. |
| 106 upload_file_info->should_retry_file_open = false; | 114 upload_file_info->should_retry_file_open = false; |
| 107 OpenFile(upload_file_info); | 115 OpenFile(upload_file_info); |
| 108 } | 116 } |
| 109 | 117 |
| 110 if (download->IsComplete()) | 118 if (download->IsComplete()) |
| 111 UploadComplete(upload_file_info); | 119 MoveFileToCache(upload_file_info); |
| 112 } | 120 } |
| 113 | 121 |
| 114 int64 GDataUploader::GetUploadedBytes(int upload_id) const { | 122 int64 GDataUploader::GetUploadedBytes(int upload_id) const { |
| 115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 116 UploadFileInfo* upload_info = GetUploadFileInfo(upload_id); | 124 UploadFileInfo* upload_info = GetUploadFileInfo(upload_id); |
| 117 // We return the start_range as the count of uploaded bytes since that is the | 125 // We return the start_range as the count of uploaded bytes since that is the |
| 118 // start of the next or currently uploading chunk. | 126 // start of the next or currently uploading chunk. |
| 119 // TODO(asanka): Use a finer grained progress value than this. We end up | 127 // TODO(asanka): Use a finer grained progress value than this. We end up |
| 120 // reporting progress in kUploadChunkSize increments. | 128 // reporting progress in kUploadChunkSize increments. |
| 121 return upload_info ? upload_info->start_range : 0; | 129 return upload_info ? upload_info->start_range : 0; |
| 122 } | 130 } |
| 123 | 131 |
| 124 UploadFileInfo* GDataUploader::GetUploadFileInfo(int upload_id) const { | 132 UploadFileInfo* GDataUploader::GetUploadFileInfo(int upload_id) const { |
| 125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 126 | 134 |
| 127 UploadFileInfoMap::const_iterator it = pending_uploads_.find(upload_id); | 135 UploadFileInfoMap::const_iterator it = pending_uploads_.find(upload_id); |
| 128 DVLOG_IF(1, it == pending_uploads_.end()) << "No upload found for id " | 136 DVLOG_IF(1, it == pending_uploads_.end()) << "No upload found for id " |
| 129 << upload_id; | 137 << upload_id; |
| 130 return it != pending_uploads_.end() ? it->second : NULL; | 138 return it != pending_uploads_.end() ? it->second : NULL; |
| 131 } | 139 } |
| 132 | 140 |
| 133 void GDataUploader::RemovePendingUpload(UploadFileInfo* upload_file_info) { | |
| 134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 135 | |
| 136 pending_uploads_.erase(upload_file_info->upload_id); | |
| 137 if (!upload_file_info->completion_callback.is_null()) { | |
| 138 upload_file_info->completion_callback.Run( | |
| 139 base::PLATFORM_FILE_ERROR_ABORT, NULL); | |
| 140 } | |
| 141 | |
| 142 file_system_->CancelOperation(upload_file_info->gdata_path); | |
| 143 | |
| 144 // The file stream is closed by the destructor asynchronously. | |
| 145 delete upload_file_info->file_stream; | |
| 146 delete upload_file_info; | |
| 147 } | |
| 148 | |
| 149 void GDataUploader::OpenFile(UploadFileInfo* upload_file_info) { | 141 void GDataUploader::OpenFile(UploadFileInfo* upload_file_info) { |
| 150 // Open the file asynchronously. | 142 // Open the file asynchronously. |
| 151 const int rv = upload_file_info->file_stream->Open( | 143 const int rv = upload_file_info->file_stream->Open( |
| 152 upload_file_info->file_path, | 144 upload_file_info->file_path, |
| 153 base::PLATFORM_FILE_OPEN | | 145 base::PLATFORM_FILE_OPEN | |
| 154 base::PLATFORM_FILE_READ | | 146 base::PLATFORM_FILE_READ | |
| 155 base::PLATFORM_FILE_ASYNC, | 147 base::PLATFORM_FILE_ASYNC, |
| 156 base::Bind(&GDataUploader::OpenCompletionCallback, | 148 base::Bind(&GDataUploader::OpenCompletionCallback, |
| 157 uploader_factory_.GetWeakPtr(), | 149 uploader_factory_.GetWeakPtr(), |
| 158 upload_file_info->upload_id)); | 150 upload_file_info->upload_id)); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 176 | 168 |
| 177 DVLOG(1) << "Error opening \"" << upload_file_info->file_path.value() | 169 DVLOG(1) << "Error opening \"" << upload_file_info->file_path.value() |
| 178 << "\" for reading: " << net::ErrorToString(result) | 170 << "\" for reading: " << net::ErrorToString(result) |
| 179 << ", tries=" << upload_file_info->num_file_open_tries; | 171 << ", tries=" << upload_file_info->num_file_open_tries; |
| 180 | 172 |
| 181 // Stop trying to open this file if we exceed kMaxFileOpenTries. | 173 // Stop trying to open this file if we exceed kMaxFileOpenTries. |
| 182 const bool exceeded_max_attempts = | 174 const bool exceeded_max_attempts = |
| 183 upload_file_info->num_file_open_tries >= kMaxFileOpenTries; | 175 upload_file_info->num_file_open_tries >= kMaxFileOpenTries; |
| 184 upload_file_info->should_retry_file_open = !exceeded_max_attempts; | 176 upload_file_info->should_retry_file_open = !exceeded_max_attempts; |
| 185 if (exceeded_max_attempts) | 177 if (exceeded_max_attempts) |
| 186 RemovePendingUpload(upload_file_info); | 178 UploadFailed(upload_file_info); |
| 187 | 179 |
| 188 return; | 180 return; |
| 189 } | 181 } |
| 190 | 182 |
| 191 // Open succeeded, initiate the upload. | 183 // Open succeeded, initiate the upload. |
| 192 upload_file_info->should_retry_file_open = false; | 184 upload_file_info->should_retry_file_open = false; |
| 193 file_system_->InitiateUpload( | 185 file_system_->InitiateUpload( |
| 194 upload_file_info->title, | 186 upload_file_info->title, |
| 195 upload_file_info->content_type, | 187 upload_file_info->content_type, |
| 196 upload_file_info->content_length, | 188 upload_file_info->content_length, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 209 | 201 |
| 210 UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id); | 202 UploadFileInfo* upload_file_info = GetUploadFileInfo(upload_id); |
| 211 if (!upload_file_info) | 203 if (!upload_file_info) |
| 212 return; | 204 return; |
| 213 | 205 |
| 214 DVLOG(1) << "Got upload location [" << upload_location.spec() | 206 DVLOG(1) << "Got upload location [" << upload_location.spec() |
| 215 << "] for [" << upload_file_info->title << "]"; | 207 << "] for [" << upload_file_info->title << "]"; |
| 216 | 208 |
| 217 if (code != HTTP_SUCCESS) { | 209 if (code != HTTP_SUCCESS) { |
| 218 // TODO(achuith): Handle error codes from Google Docs server. | 210 // TODO(achuith): Handle error codes from Google Docs server. |
| 219 RemovePendingUpload(upload_file_info); | 211 UploadFailed(upload_file_info); |
| 220 NOTREACHED(); | |
| 221 return; | 212 return; |
| 222 } | 213 } |
| 223 | 214 |
| 224 upload_file_info->upload_location = upload_location; | 215 upload_file_info->upload_location = upload_location; |
| 225 | 216 |
| 226 // Start the upload from the beginning of the file. | 217 // Start the upload from the beginning of the file. |
| 227 UploadNextChunk(upload_file_info); | 218 UploadNextChunk(upload_file_info); |
| 228 } | 219 } |
| 229 | 220 |
| 230 void GDataUploader::UploadNextChunk(UploadFileInfo* upload_file_info) { | 221 void GDataUploader::UploadNextChunk(UploadFileInfo* upload_file_info) { |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 308 if (!upload_file_info) | 299 if (!upload_file_info) |
| 309 return; | 300 return; |
| 310 | 301 |
| 311 if (response.code == HTTP_CREATED) { | 302 if (response.code == HTTP_CREATED) { |
| 312 DVLOG(1) << "Successfully created uploaded file=[" | 303 DVLOG(1) << "Successfully created uploaded file=[" |
| 313 << upload_file_info->title; | 304 << upload_file_info->title; |
| 314 | 305 |
| 315 // Done uploading. | 306 // Done uploading. |
| 316 upload_file_info->entry = entry.Pass(); | 307 upload_file_info->entry = entry.Pass(); |
| 317 if (!upload_file_info->completion_callback.is_null()) { | 308 if (!upload_file_info->completion_callback.is_null()) { |
| 318 upload_file_info->completion_callback.Run( | 309 upload_file_info->completion_callback.Run(base::PLATFORM_FILE_OK, |
| 319 base::PLATFORM_FILE_OK, | 310 upload_file_info); |
| 320 upload_file_info->entry.get()); | |
| 321 upload_file_info->completion_callback.Reset(); | 311 upload_file_info->completion_callback.Reset(); |
| 322 } | 312 } |
| 313 // TODO(achuith): DeleteUpload() here and let clients call |
| 314 // GDataFileSystem::AddUploadedFile. |
| 323 return; | 315 return; |
| 324 } | 316 } |
| 325 | 317 |
| 326 // If code is 308 (RESUME_INCOMPLETE) and range_received is what has been | 318 // If code is 308 (RESUME_INCOMPLETE) and range_received is what has been |
| 327 // previously uploaded (i.e. = upload_file_info->end_range), proceed to | 319 // previously uploaded (i.e. = upload_file_info->end_range), proceed to |
| 328 // upload the next chunk. | 320 // upload the next chunk. |
| 329 if (response.code != HTTP_RESUME_INCOMPLETE || | 321 if (response.code != HTTP_RESUME_INCOMPLETE || |
| 330 response.start_range_received != 0 || | 322 response.start_range_received != 0 || |
| 331 response.end_range_received != upload_file_info->end_range) { | 323 response.end_range_received != upload_file_info->end_range) { |
| 332 // TODO(achuith): Handle error cases, e.g. | 324 // TODO(achuith): Handle error cases, e.g. |
| 333 // - when previously uploaded data wasn't received by Google Docs server, | 325 // - when previously uploaded data wasn't received by Google Docs server, |
| 334 // i.e. when end_range_received < upload_file_info->end_range | 326 // i.e. when end_range_received < upload_file_info->end_range |
| 335 // - when quota is exceeded, which is 1GB for files not converted to Google | 327 // - when quota is exceeded, which is 1GB for files not converted to Google |
| 336 // Docs format; even though the quota-exceeded content length | 328 // Docs format; even though the quota-exceeded content length |
| 337 // is specified in the header when posting request to get upload | 329 // is specified in the header when posting request to get upload |
| 338 // location, the server allows us to upload all chunks of entire file | 330 // location, the server allows us to upload all chunks of entire file |
| 339 // successfully, but instead of returning 201 (CREATED) status code after | 331 // successfully, but instead of returning 201 (CREATED) status code after |
| 340 // receiving the last chunk, it returns 403 (FORBIDDEN); response content | 332 // receiving the last chunk, it returns 403 (FORBIDDEN); response content |
| 341 // then will indicate quote exceeded exception. | 333 // then will indicate quote exceeded exception. |
| 342 NOTREACHED() << "UploadNextChunk http code=" << response.code | 334 NOTREACHED() << "UploadNextChunk http code=" << response.code |
| 343 << ", start_range_received=" << response.start_range_received | 335 << ", start_range_received=" << response.start_range_received |
| 344 << ", end_range_received=" << response.end_range_received | 336 << ", end_range_received=" << response.end_range_received |
| 345 << ", expected end range=" << upload_file_info->end_range; | 337 << ", expected end range=" << upload_file_info->end_range; |
| 346 | 338 |
| 347 RemovePendingUpload(upload_file_info); | 339 UploadFailed(upload_file_info); |
| 348 return; | 340 return; |
| 349 } | 341 } |
| 350 | 342 |
| 351 DVLOG(1) << "Received range " << response.start_range_received | 343 DVLOG(1) << "Received range " << response.start_range_received |
| 352 << "-" << response.end_range_received | 344 << "-" << response.end_range_received |
| 353 << " for [" << upload_file_info->title << "]"; | 345 << " for [" << upload_file_info->title << "]"; |
| 354 | 346 |
| 355 // Continue uploading. | 347 // Continue uploading. |
| 356 UploadNextChunk(upload_file_info); | 348 UploadNextChunk(upload_file_info); |
| 357 } | 349 } |
| 358 | 350 |
| 359 void GDataUploader::UploadComplete(UploadFileInfo* upload_file_info) { | 351 void GDataUploader::MoveFileToCache(UploadFileInfo* upload_file_info) { |
| 360 DVLOG(1) << "UploadComplete " << upload_file_info->file_path.value(); | 352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 361 file_system_->AddUploadedFile(upload_file_info->gdata_path.DirName(), | 353 if (upload_file_info->entry == NULL) |
| 362 upload_file_info->entry.get(), | 354 return; |
| 363 upload_file_info->file_path, | 355 |
| 364 GDataFileSystemInterface::FILE_OPERATION_MOVE); | 356 DVLOG(1) << "MoveFileToCache " << upload_file_info->file_path.value(); |
| 365 RemovePendingUpload(upload_file_info); | 357 file_system_->AddUploadedFile( |
| 358 upload_file_info->gdata_path.DirName(), |
| 359 upload_file_info->entry.get(), |
| 360 upload_file_info->file_path, |
| 361 GDataFileSystemInterface::FILE_OPERATION_MOVE); |
| 362 DeleteUpload(upload_file_info); |
| 363 } |
| 364 |
| 365 void GDataUploader::UploadFailed(UploadFileInfo* upload_file_info) { |
| 366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 367 LOG(ERROR) << "Upload failed " << upload_file_info->DebugString(); |
| 368 if (!upload_file_info->completion_callback.is_null()) { |
| 369 upload_file_info->completion_callback.Run(base::PLATFORM_FILE_ERROR_ABORT, |
| 370 upload_file_info); |
| 371 } |
| 372 file_system_->CancelOperation(upload_file_info->gdata_path); |
| 373 DeleteUpload(upload_file_info); |
| 374 } |
| 375 |
| 376 void GDataUploader::DeleteUpload(UploadFileInfo* upload_file_info) { |
| 377 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 378 |
| 379 DVLOG(1) << "Deleting upload " << upload_file_info->gdata_path.value(); |
| 380 pending_uploads_.erase(upload_file_info->upload_id); |
| 381 |
| 382 // The file stream is closed by the destructor asynchronously. |
| 383 delete upload_file_info->file_stream; |
| 384 delete upload_file_info; |
| 366 } | 385 } |
| 367 | 386 |
| 368 } // namespace gdata | 387 } // namespace gdata |
| OLD | NEW |