diff options
Diffstat (limited to 'chromium/net/base/upload_file_element_reader.cc')
-rw-r--r-- | chromium/net/base/upload_file_element_reader.cc | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/chromium/net/base/upload_file_element_reader.cc b/chromium/net/base/upload_file_element_reader.cc new file mode 100644 index 00000000000..d1f2a12ac8c --- /dev/null +++ b/chromium/net/base/upload_file_element_reader.cc @@ -0,0 +1,290 @@ +// 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/file_util.h" +#include "base/location.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; + +// This function is used to implement Init(). +template<typename FileStreamDeleter> +int InitInternal(const base::FilePath& path, + uint64 range_offset, + uint64 range_length, + const base::Time& expected_modification_time, + scoped_ptr<FileStream, FileStreamDeleter>* out_file_stream, + uint64* out_content_length) { + scoped_ptr<FileStream> file_stream(new FileStream(NULL)); + int64 rv = file_stream->OpenSync( + path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ); + if (rv != OK) { + // If the file can't be opened, we'll just upload an empty file. + DLOG(WARNING) << "Failed to open \"" << path.value() + << "\" for reading: " << rv; + file_stream.reset(); + } else if (range_offset) { + rv = file_stream->SeekSync(FROM_BEGIN, range_offset); + if (rv < 0) { + DLOG(WARNING) << "Failed to seek \"" << path.value() + << "\" to offset: " << range_offset << " (" << rv << ")"; + file_stream.reset(); + } + } + + int64 length = 0; + if (file_stream.get() && + file_util::GetFileSize(path, &length) && + range_offset < static_cast<uint64>(length)) { + // Compensate for the offset. + length = std::min(length - range_offset, range_length); + } + *out_content_length = length; + out_file_stream->reset(file_stream.release()); + + // 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()) { + base::PlatformFileInfo info; + if (file_util::GetFileInfo(path, &info) && + expected_modification_time.ToTimeT() != info.last_modified.ToTimeT()) { + return ERR_UPLOAD_FILE_CHANGED; + } + } + + return OK; +} + +// This function is used to implement Read(). +int ReadInternal(scoped_refptr<IOBuffer> buf, + int buf_length, + uint64 bytes_remaining, + FileStream* file_stream) { + DCHECK_LT(0, buf_length); + + const uint64 num_bytes_to_read = + std::min(bytes_remaining, static_cast<uint64>(buf_length)); + + int result = 0; + if (num_bytes_to_read > 0) { + DCHECK(file_stream); // file_stream is non-null if content_length_ > 0. + result = file_stream->ReadSync(buf->data(), num_bytes_to_read); + if (result == 0) // Reached end-of-file earlier than expected. + result = ERR_UPLOAD_FILE_CHANGED; + } + return result; +} + +} // namespace + +UploadFileElementReader::FileStreamDeleter::FileStreamDeleter( + base::TaskRunner* task_runner) : task_runner_(task_runner) { + DCHECK(task_runner_.get()); +} + +UploadFileElementReader::FileStreamDeleter::~FileStreamDeleter() {} + +void UploadFileElementReader::FileStreamDeleter::operator() ( + FileStream* file_stream) const { + if (file_stream) { + task_runner_->PostTask(FROM_HERE, + base::Bind(&base::DeletePointer<FileStream>, + file_stream)); + } +} + +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), + file_stream_(NULL, FileStreamDeleter(task_runner_.get())), + 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(); + + ScopedFileStreamPtr* file_stream = + new ScopedFileStreamPtr(NULL, FileStreamDeleter(task_runner_.get())); + uint64* content_length = new uint64; + const bool posted = base::PostTaskAndReplyWithResult( + task_runner_.get(), + FROM_HERE, + base::Bind(&InitInternal<FileStreamDeleter>, + path_, + range_offset_, + range_length_, + expected_modification_time_, + file_stream, + content_length), + base::Bind(&UploadFileElementReader::OnInitCompleted, + weak_ptr_factory_.GetWeakPtr(), + base::Owned(file_stream), + base::Owned(content_length), + callback)); + DCHECK(posted); + return ERR_IO_PENDING; +} + +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()); + + if (BytesRemaining() == 0) + return 0; + + // Save the value of file_stream_.get() before base::Passed() invalidates it. + FileStream* file_stream_ptr = file_stream_.get(); + // Pass the ownership of file_stream_ to the worker pool to safely perform + // operation even when |this| is destructed before the read completes. + const bool posted = base::PostTaskAndReplyWithResult( + task_runner_.get(), + FROM_HERE, + base::Bind(&ReadInternal, + scoped_refptr<IOBuffer>(buf), + buf_length, + BytesRemaining(), + file_stream_ptr), + base::Bind(&UploadFileElementReader::OnReadCompleted, + weak_ptr_factory_.GetWeakPtr(), + base::Passed(&file_stream_), + callback)); + DCHECK(posted); + return ERR_IO_PENDING; +} + +void UploadFileElementReader::Reset() { + weak_ptr_factory_.InvalidateWeakPtrs(); + bytes_remaining_ = 0; + content_length_ = 0; + file_stream_.reset(); +} + +void UploadFileElementReader::OnInitCompleted( + ScopedFileStreamPtr* file_stream, + uint64* content_length, + const CompletionCallback& callback, + int result) { + file_stream_.swap(*file_stream); + content_length_ = *content_length; + bytes_remaining_ = GetContentLength(); + if (!callback.is_null()) + callback.Run(result); +} + +void UploadFileElementReader::OnReadCompleted( + ScopedFileStreamPtr file_stream, + const CompletionCallback& callback, + int result) { + file_stream_.swap(file_stream); + if (result > 0) { + DCHECK_GE(bytes_remaining_, static_cast<uint64>(result)); + bytes_remaining_ -= result; + } + if (!callback.is_null()) + callback.Run(result); +} + +UploadFileElementReader::ScopedOverridingContentLengthForTests:: +ScopedOverridingContentLengthForTests(uint64 value) { + overriding_content_length = value; +} + +UploadFileElementReader::ScopedOverridingContentLengthForTests:: +~ScopedOverridingContentLengthForTests() { + overriding_content_length = 0; +} + +UploadFileElementReaderSync::UploadFileElementReaderSync( + const base::FilePath& path, + uint64 range_offset, + uint64 range_length, + const base::Time& expected_modification_time) + : path_(path), + range_offset_(range_offset), + range_length_(range_length), + expected_modification_time_(expected_modification_time), + content_length_(0), + bytes_remaining_(0) { +} + +UploadFileElementReaderSync::~UploadFileElementReaderSync() { +} + +int UploadFileElementReaderSync::Init(const CompletionCallback& callback) { + bytes_remaining_ = 0; + content_length_ = 0; + file_stream_.reset(); + + const int result = InitInternal(path_, range_offset_, range_length_, + expected_modification_time_, + &file_stream_, &content_length_); + bytes_remaining_ = GetContentLength(); + return result; +} + +uint64 UploadFileElementReaderSync::GetContentLength() const { + return content_length_; +} + +uint64 UploadFileElementReaderSync::BytesRemaining() const { + return bytes_remaining_; +} + +int UploadFileElementReaderSync::Read(IOBuffer* buf, + int buf_length, + const CompletionCallback& callback) { + const int result = ReadInternal(buf, buf_length, BytesRemaining(), + file_stream_.get()); + if (result > 0) { + DCHECK_GE(bytes_remaining_, static_cast<uint64>(result)); + bytes_remaining_ -= result; + } + return result; +} + +} // namespace net |