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 |