| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/base/upload_file_element_reader.h" |
| |
| #include "base/bind.h" |
| #include "base/files/file_util.h" |
| #include "base/location.h" |
| #include "base/profiler/scoped_tracker.h" |
| #include "base/task_runner_util.h" |
| #include "net/base/file_stream.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/net_errors.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| // In tests, this value is used to override the return value of |
| // UploadFileElementReader::GetContentLength() when set to non-zero. |
| uint64 overriding_content_length = 0; |
| |
| } // namespace |
| |
| UploadFileElementReader::UploadFileElementReader( |
| base::TaskRunner* task_runner, |
| const base::FilePath& path, |
| uint64 range_offset, |
| uint64 range_length, |
| const base::Time& expected_modification_time) |
| : task_runner_(task_runner), |
| path_(path), |
| range_offset_(range_offset), |
| range_length_(range_length), |
| expected_modification_time_(expected_modification_time), |
| content_length_(0), |
| bytes_remaining_(0), |
| weak_ptr_factory_(this) { |
| DCHECK(task_runner_.get()); |
| } |
| |
| UploadFileElementReader::~UploadFileElementReader() { |
| } |
| |
| const UploadFileElementReader* UploadFileElementReader::AsFileReader() const { |
| return this; |
| } |
| |
| int UploadFileElementReader::Init(const CompletionCallback& callback) { |
| DCHECK(!callback.is_null()); |
| Reset(); |
| |
| file_stream_.reset(new FileStream(task_runner_.get())); |
| int result = file_stream_->Open( |
| path_, |
| base::File::FLAG_OPEN | base::File::FLAG_READ | |
| base::File::FLAG_ASYNC, |
| base::Bind(&UploadFileElementReader::OnOpenCompleted, |
| weak_ptr_factory_.GetWeakPtr(), |
| callback)); |
| DCHECK_GT(0, result); |
| return result; |
| } |
| |
| uint64 UploadFileElementReader::GetContentLength() const { |
| if (overriding_content_length) |
| return overriding_content_length; |
| return content_length_; |
| } |
| |
| uint64 UploadFileElementReader::BytesRemaining() const { |
| return bytes_remaining_; |
| } |
| |
| int UploadFileElementReader::Read(IOBuffer* buf, |
| int buf_length, |
| const CompletionCallback& callback) { |
| DCHECK(!callback.is_null()); |
| |
| int num_bytes_to_read = static_cast<int>( |
| std::min(BytesRemaining(), static_cast<uint64>(buf_length))); |
| if (num_bytes_to_read == 0) |
| return 0; |
| |
| int result = file_stream_->Read( |
| buf, num_bytes_to_read, |
| base::Bind(base::IgnoreResult(&UploadFileElementReader::OnReadCompleted), |
| weak_ptr_factory_.GetWeakPtr(), |
| callback)); |
| // Even in async mode, FileStream::Read() may return the result synchronously. |
| if (result != ERR_IO_PENDING) |
| return OnReadCompleted(CompletionCallback(), result); |
| return ERR_IO_PENDING; |
| } |
| |
| void UploadFileElementReader::Reset() { |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| bytes_remaining_ = 0; |
| content_length_ = 0; |
| file_stream_.reset(); |
| } |
| |
| void UploadFileElementReader::OnOpenCompleted( |
| const CompletionCallback& callback, |
| int result) { |
| // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. |
| tracked_objects::ScopedTracker tracking_profile( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "423948 UploadFileElementReader::OnOpenCompleted")); |
| |
| DCHECK(!callback.is_null()); |
| |
| if (result < 0) { |
| DLOG(WARNING) << "Failed to open \"" << path_.value() |
| << "\" for reading: " << result; |
| callback.Run(result); |
| return; |
| } |
| |
| if (range_offset_) { |
| int result = file_stream_->Seek( |
| base::File::FROM_BEGIN, range_offset_, |
| base::Bind(&UploadFileElementReader::OnSeekCompleted, |
| weak_ptr_factory_.GetWeakPtr(), |
| callback)); |
| DCHECK_GT(0, result); |
| if (result != ERR_IO_PENDING) |
| callback.Run(result); |
| } else { |
| OnSeekCompleted(callback, OK); |
| } |
| } |
| |
| void UploadFileElementReader::OnSeekCompleted( |
| const CompletionCallback& callback, |
| int64 result) { |
| DCHECK(!callback.is_null()); |
| |
| if (result < 0) { |
| DLOG(WARNING) << "Failed to seek \"" << path_.value() |
| << "\" to offset: " << range_offset_ << " (" << result << ")"; |
| callback.Run(static_cast<int>(result)); |
| return; |
| } |
| |
| base::File::Info* file_info = new base::File::Info; |
| bool posted = base::PostTaskAndReplyWithResult( |
| task_runner_.get(), |
| FROM_HERE, |
| base::Bind(&base::GetFileInfo, path_, file_info), |
| base::Bind(&UploadFileElementReader::OnGetFileInfoCompleted, |
| weak_ptr_factory_.GetWeakPtr(), |
| callback, |
| base::Owned(file_info))); |
| DCHECK(posted); |
| } |
| |
| void UploadFileElementReader::OnGetFileInfoCompleted( |
| const CompletionCallback& callback, |
| base::File::Info* file_info, |
| bool result) { |
| DCHECK(!callback.is_null()); |
| if (!result) { |
| DLOG(WARNING) << "Failed to get file info of \"" << path_.value() << "\""; |
| callback.Run(ERR_FILE_NOT_FOUND); |
| return; |
| } |
| |
| int64 length = file_info->size; |
| if (range_offset_ < static_cast<uint64>(length)) { |
| // Compensate for the offset. |
| length = std::min(length - range_offset_, range_length_); |
| } |
| |
| // If the underlying file has been changed and the expected file modification |
| // time is set, treat it as error. Note that the expected modification time |
| // from WebKit is based on time_t precision. So we have to convert both to |
| // time_t to compare. This check is used for sliced files. |
| if (!expected_modification_time_.is_null() && |
| expected_modification_time_.ToTimeT() != |
| file_info->last_modified.ToTimeT()) { |
| callback.Run(ERR_UPLOAD_FILE_CHANGED); |
| return; |
| } |
| |
| content_length_ = length; |
| bytes_remaining_ = GetContentLength(); |
| callback.Run(OK); |
| } |
| |
| int UploadFileElementReader::OnReadCompleted( |
| const CompletionCallback& callback, |
| int result) { |
| if (result == 0) // Reached end-of-file earlier than expected. |
| result = ERR_UPLOAD_FILE_CHANGED; |
| |
| if (result > 0) { |
| DCHECK_GE(bytes_remaining_, static_cast<uint64>(result)); |
| bytes_remaining_ -= result; |
| } |
| |
| if (!callback.is_null()) |
| callback.Run(result); |
| return result; |
| } |
| |
| UploadFileElementReader::ScopedOverridingContentLengthForTests:: |
| ScopedOverridingContentLengthForTests(uint64 value) { |
| overriding_content_length = value; |
| } |
| |
| UploadFileElementReader::ScopedOverridingContentLengthForTests:: |
| ~ScopedOverridingContentLengthForTests() { |
| overriding_content_length = 0; |
| } |
| |
| } // namespace net |