Fix build break
[chromium-blink-merge.git] / chrome / browser / google_apis / drive_uploader.cc
blobb05e8534c792b6301e8818231e4193440511be91
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.
5 #include "chrome/browser/google_apis/drive_uploader.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/message_loop.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "chrome/browser/google_apis/drive_service_interface.h"
14 #include "chrome/browser/google_apis/drive_upload_mode.h"
15 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/power_save_blocker.h"
18 #include "net/base/file_stream.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/net_errors.h"
22 using content::BrowserThread;
24 namespace {
26 // Google Documents List API requires uploading in chunks of 512kB.
27 const int64 kUploadChunkSize = 512 * 1024;
29 // Opens |path| with |file_stream| and returns the file size.
30 // If failed, returns an error code in a negative value.
31 int64 OpenFileStreamAndGetSizeOnBlockingPool(net::FileStream* file_stream,
32 const base::FilePath& path) {
33 int result = file_stream->OpenSync(
34 path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
35 if (result != net::OK)
36 return result;
37 return file_stream->Available();
40 } // namespace
42 namespace google_apis {
44 // Structure containing current upload information of file, passed between
45 // DriveServiceInterface methods and callbacks.
46 struct DriveUploader::UploadFileInfo {
47 UploadFileInfo(scoped_refptr<base::SequencedTaskRunner> task_runner,
48 UploadMode upload_mode,
49 const base::FilePath& drive_path,
50 const base::FilePath& local_path,
51 const std::string& content_type,
52 const UploadCompletionCallback& callback,
53 const ProgressCallback& progress_callback)
54 : upload_mode(upload_mode),
55 drive_path(drive_path),
56 file_path(local_path),
57 content_type(content_type),
58 completion_callback(callback),
59 progress_callback(progress_callback),
60 content_length(0),
61 next_send_position(0),
62 file_stream(new net::FileStream(NULL)),
63 buf(new net::IOBuffer(kUploadChunkSize)),
64 blocking_task_runner(task_runner),
65 power_save_blocker(content::PowerSaveBlocker::Create(
66 content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
67 "Upload in progress")) {
70 ~UploadFileInfo() {
71 blocking_task_runner->DeleteSoon(FROM_HERE, file_stream.release());
74 // Bytes left to upload.
75 int64 SizeRemaining() const {
76 DCHECK(content_length >= next_send_position);
77 return content_length - next_send_position;
80 // Useful for printf debugging.
81 std::string DebugString() const {
82 return "file_path=[" + file_path.AsUTF8Unsafe() +
83 "], content_type=[" + content_type +
84 "], content_length=[" + base::UintToString(content_length) +
85 "], drive_path=[" + drive_path.AsUTF8Unsafe() +
86 "]";
89 // Whether this is uploading a new file or updating an existing file.
90 const UploadMode upload_mode;
92 // Final path in gdata. Looks like /special/drive/MyFolder/MyFile.
93 const base::FilePath drive_path;
95 // The local file path of the file to be uploaded.
96 const base::FilePath file_path;
98 // Content-Type of file.
99 const std::string content_type;
101 // Callback to be invoked once the upload has finished.
102 const UploadCompletionCallback completion_callback;
104 // Callback to periodically notify the upload progress.
105 const ProgressCallback progress_callback;
107 // Location URL where file is to be uploaded to, returned from
108 // InitiateUpload. Used for the subsequent ResumeUpload requests.
109 GURL upload_location;
111 // Header content-Length.
112 int64 content_length;
114 // The start position of the contents to be sent as the next upload chunk.
115 int64 next_send_position;
117 // For opening and reading from physical file.
119 // File operations are posted to |blocking_task_runner|, while the ownership
120 // of the stream is held in UI thread. At the point when this UploadFileInfo
121 // is destroyed, the ownership of the stream is passed to the worker pool.
122 // TODO(kinaba): We should switch to async API of FileStream once
123 // crbug.com/164312 is fixed.
124 scoped_ptr<net::FileStream> file_stream;
126 // Holds current content to be uploaded.
127 const scoped_refptr<net::IOBuffer> buf;
129 // Runner for net::FileStream tasks.
130 const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
132 // Blocks system suspend while upload is in progress.
133 scoped_ptr<content::PowerSaveBlocker> power_save_blocker;
136 DriveUploader::DriveUploader(DriveServiceInterface* drive_service)
137 : drive_service_(drive_service),
138 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
139 base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool();
140 blocking_task_runner_ = blocking_pool->GetSequencedTaskRunner(
141 blocking_pool->GetSequenceToken());
144 DriveUploader::~DriveUploader() {}
146 void DriveUploader::UploadNewFile(const std::string& parent_resource_id,
147 const base::FilePath& drive_file_path,
148 const base::FilePath& local_file_path,
149 const std::string& title,
150 const std::string& content_type,
151 const UploadCompletionCallback& callback,
152 const ProgressCallback& progress_callback) {
153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
154 DCHECK(!parent_resource_id.empty());
155 DCHECK(!drive_file_path.empty());
156 DCHECK(!local_file_path.empty());
157 DCHECK(!title.empty());
158 DCHECK(!content_type.empty());
159 DCHECK(!callback.is_null());
161 StartUploadFile(
162 scoped_ptr<UploadFileInfo>(new UploadFileInfo(blocking_task_runner_,
163 UPLOAD_NEW_FILE,
164 drive_file_path,
165 local_file_path,
166 content_type,
167 callback,
168 progress_callback)),
169 base::Bind(&DriveUploader::StartInitiateUploadNewFile,
170 weak_ptr_factory_.GetWeakPtr(),
171 parent_resource_id,
172 title));
175 void DriveUploader::UploadExistingFile(
176 const std::string& resource_id,
177 const base::FilePath& drive_file_path,
178 const base::FilePath& local_file_path,
179 const std::string& content_type,
180 const std::string& etag,
181 const UploadCompletionCallback& callback,
182 const ProgressCallback& progress_callback) {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
184 DCHECK(!resource_id.empty());
185 DCHECK(!drive_file_path.empty());
186 DCHECK(!local_file_path.empty());
187 DCHECK(!content_type.empty());
188 DCHECK(!callback.is_null());
190 StartUploadFile(
191 scoped_ptr<UploadFileInfo>(new UploadFileInfo(blocking_task_runner_,
192 UPLOAD_EXISTING_FILE,
193 drive_file_path,
194 local_file_path,
195 content_type,
196 callback,
197 progress_callback)),
198 base::Bind(&DriveUploader::StartInitiateUploadExistingFile,
199 weak_ptr_factory_.GetWeakPtr(),
200 resource_id,
201 etag));
204 void DriveUploader::StartUploadFile(
205 scoped_ptr<UploadFileInfo> upload_file_info,
206 const StartInitiateUploadCallback& start_initiate_upload_callback) {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
208 DVLOG(1) << "Uploading file: " << upload_file_info->DebugString();
210 // Passing a raw net::FileStream* to the blocking pool is safe, because it is
211 // owned by |upload_file_info| in the reply callback.
212 UploadFileInfo* info_ptr = upload_file_info.get();
213 base::PostTaskAndReplyWithResult(
214 blocking_task_runner_.get(),
215 FROM_HERE,
216 base::Bind(&OpenFileStreamAndGetSizeOnBlockingPool,
217 info_ptr->file_stream.get(),
218 info_ptr->file_path),
219 base::Bind(&DriveUploader::OpenCompletionCallback,
220 weak_ptr_factory_.GetWeakPtr(),
221 base::Passed(&upload_file_info),
222 start_initiate_upload_callback));
225 void DriveUploader::OpenCompletionCallback(
226 scoped_ptr<UploadFileInfo> upload_file_info,
227 const StartInitiateUploadCallback& start_initiate_upload_callback,
228 int64 file_size) {
229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
231 if (file_size < 0) {
232 UploadFailed(upload_file_info.Pass(), DRIVE_UPLOAD_ERROR_NOT_FOUND);
233 return;
236 upload_file_info->content_length = file_size;
238 // Open succeeded, initiate the upload.
239 start_initiate_upload_callback.Run(upload_file_info.Pass());
242 void DriveUploader::StartInitiateUploadNewFile(
243 const std::string& parent_resource_id,
244 const std::string& title,
245 scoped_ptr<UploadFileInfo> upload_file_info) {
246 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
248 UploadFileInfo* info_ptr = upload_file_info.get();
249 drive_service_->InitiateUploadNewFile(
250 info_ptr->drive_path,
251 info_ptr->content_type,
252 info_ptr->content_length,
253 parent_resource_id,
254 title,
255 base::Bind(&DriveUploader::OnUploadLocationReceived,
256 weak_ptr_factory_.GetWeakPtr(),
257 base::Passed(&upload_file_info)));
260 void DriveUploader::StartInitiateUploadExistingFile(
261 const std::string& resource_id,
262 const std::string& etag,
263 scoped_ptr<UploadFileInfo> upload_file_info) {
264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
266 UploadFileInfo* info_ptr = upload_file_info.get();
267 drive_service_->InitiateUploadExistingFile(
268 info_ptr->drive_path,
269 info_ptr->content_type,
270 info_ptr->content_length,
271 resource_id,
272 etag,
273 base::Bind(&DriveUploader::OnUploadLocationReceived,
274 weak_ptr_factory_.GetWeakPtr(),
275 base::Passed(&upload_file_info)));
278 void DriveUploader::OnUploadLocationReceived(
279 scoped_ptr<UploadFileInfo> upload_file_info,
280 GDataErrorCode code,
281 const GURL& upload_location) {
282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
284 DVLOG(1) << "Got upload location [" << upload_location.spec()
285 << "] for [" << upload_file_info->drive_path.value() << "]";
287 if (code != HTTP_SUCCESS) {
288 // TODO(achuith): Handle error codes from Google Docs server.
289 if (code == HTTP_PRECONDITION) {
290 // ETag mismatch.
291 UploadFailed(upload_file_info.Pass(), DRIVE_UPLOAD_ERROR_CONFLICT);
292 return;
294 UploadFailed(upload_file_info.Pass(), DRIVE_UPLOAD_ERROR_ABORT);
295 return;
298 upload_file_info->upload_location = upload_location;
300 // Start the upload from the beginning of the file.
301 UploadNextChunk(upload_file_info.Pass());
304 void DriveUploader::UploadNextChunk(
305 scoped_ptr<UploadFileInfo> upload_file_info) {
306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
308 // Determine number of bytes to read for this upload iteration.
309 const int bytes_to_read = std::min(upload_file_info->SizeRemaining(),
310 kUploadChunkSize);
312 if (bytes_to_read == 0) {
313 // net::FileStream doesn't allow to read 0 bytes, so directly proceed to the
314 // completion callback. PostTask is necessary because we have to finish
315 // InitiateUpload's callback before calling ResumeUpload, due to the
316 // implementation of OperationRegistry. (http://crbug.com/134814)
317 MessageLoop::current()->PostTask(
318 FROM_HERE,
319 base::Bind(&DriveUploader::ReadCompletionCallback,
320 weak_ptr_factory_.GetWeakPtr(),
321 base::Passed(&upload_file_info), 0, 0));
322 return;
325 // Passing a raw |file_stream| and |buf| to the blocking pool is safe, because
326 // they are owned by |upload_file_info| in the reply callback.
327 UploadFileInfo* info_ptr = upload_file_info.get();
328 base::PostTaskAndReplyWithResult(
329 blocking_task_runner_.get(),
330 FROM_HERE,
331 base::Bind(&net::FileStream::ReadUntilComplete,
332 base::Unretained(info_ptr->file_stream.get()),
333 info_ptr->buf->data(),
334 bytes_to_read),
335 base::Bind(&DriveUploader::ReadCompletionCallback,
336 weak_ptr_factory_.GetWeakPtr(),
337 base::Passed(&upload_file_info),
338 bytes_to_read));
341 void DriveUploader::ReadCompletionCallback(
342 scoped_ptr<UploadFileInfo> upload_file_info,
343 int bytes_to_read,
344 int bytes_read) {
345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
346 DCHECK_EQ(bytes_to_read, bytes_read);
347 DVLOG(1) << "ReadCompletionCallback bytes read=" << bytes_read;
349 if (bytes_read < 0) {
350 LOG(ERROR) << "Error reading from file "
351 << upload_file_info->file_path.value();
352 UploadFailed(upload_file_info.Pass(), DRIVE_UPLOAD_ERROR_ABORT);
353 return;
356 int64 start_position = upload_file_info->next_send_position;
357 upload_file_info->next_send_position += bytes_read;
358 int64 end_position = upload_file_info->next_send_position;
360 UploadFileInfo* info_ptr = upload_file_info.get();
361 drive_service_->ResumeUpload(
362 info_ptr->upload_mode,
363 info_ptr->drive_path,
364 info_ptr->upload_location,
365 start_position,
366 end_position,
367 info_ptr->content_length,
368 info_ptr->content_type,
369 info_ptr->buf,
370 base::Bind(&DriveUploader::OnUploadRangeResponseReceived,
371 weak_ptr_factory_.GetWeakPtr(),
372 base::Passed(&upload_file_info)),
373 base::Bind(&DriveUploader::OnUploadProgress,
374 weak_ptr_factory_.GetWeakPtr(),
375 info_ptr->progress_callback,
376 start_position,
377 info_ptr->content_length));
380 void DriveUploader::OnUploadRangeResponseReceived(
381 scoped_ptr<UploadFileInfo> upload_file_info,
382 const UploadRangeResponse& response,
383 scoped_ptr<ResourceEntry> entry) {
384 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
386 if (response.code == HTTP_CREATED || response.code == HTTP_SUCCESS) {
387 // When upload_mode is UPLOAD_NEW_FILE, we expect HTTP_CREATED, and
388 // when upload_mode is UPLOAD_EXISTING_FILE, we expect HTTP_SUCCESS.
389 // There is an exception: if we uploading an empty file, UPLOAD_NEW_FILE
390 // also returns HTTP_SUCCESS on Drive API v2. The correct way of the fix
391 // should be uploading the metadata only. However, to keep the
392 // compatibility with GData WAPI during the migration period, we just
393 // relax the condition here.
394 // TODO(hidehiko): Upload metadata only for empty files, after GData WAPI
395 // code is gone.
396 DVLOG(1) << "Successfully created uploaded file=["
397 << upload_file_info->drive_path.value() << "]";
399 // Done uploading.
400 upload_file_info->completion_callback.Run(DRIVE_UPLOAD_OK,
401 upload_file_info->drive_path,
402 upload_file_info->file_path,
403 entry.Pass());
404 return;
407 // ETag mismatch.
408 if (response.code == HTTP_PRECONDITION) {
409 UploadFailed(upload_file_info.Pass(), DRIVE_UPLOAD_ERROR_CONFLICT);
410 return;
413 // If code is 308 (RESUME_INCOMPLETE) and range_received is what has been
414 // previously uploaded (i.e. = upload_file_info->end_position), proceed to
415 // upload the next chunk.
416 if (response.code != HTTP_RESUME_INCOMPLETE ||
417 response.start_position_received != 0 ||
418 response.end_position_received != upload_file_info->next_send_position) {
419 // TODO(achuith): Handle error cases, e.g.
420 // - when previously uploaded data wasn't received by Google Docs server,
421 // i.e. when end_position_received < upload_file_info->end_position
422 LOG(ERROR) << "UploadNextChunk http code=" << response.code
423 << ", start_position_received=" << response.start_position_received
424 << ", end_position_received=" << response.end_position_received
425 << ", expected end range=" << upload_file_info->next_send_position;
426 UploadFailed(upload_file_info.Pass(),
427 response.code == HTTP_FORBIDDEN ?
428 DRIVE_UPLOAD_ERROR_NO_SPACE : DRIVE_UPLOAD_ERROR_ABORT);
429 return;
432 DVLOG(1) << "Received range " << response.start_position_received
433 << "-" << response.end_position_received
434 << " for [" << upload_file_info->drive_path.value() << "]";
436 // Continue uploading.
437 UploadNextChunk(upload_file_info.Pass());
440 void DriveUploader::OnUploadProgress(const ProgressCallback& callback,
441 int64 start_position,
442 int64 total_size,
443 int64 progress_of_chunk,
444 int64 total_of_chunk) {
445 if (!callback.is_null())
446 callback.Run(start_position + progress_of_chunk, total_size);
449 void DriveUploader::UploadFailed(scoped_ptr<UploadFileInfo> upload_file_info,
450 DriveUploadError error) {
451 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
453 LOG(ERROR) << "Upload failed " << upload_file_info->DebugString();
455 upload_file_info->completion_callback.Run(error,
456 upload_file_info->drive_path,
457 upload_file_info->file_path,
458 scoped_ptr<ResourceEntry>());
461 } // namespace google_apis