diff options
author | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-02 15:18:46 +0000 |
---|---|---|
committer | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-02 15:18:46 +0000 |
commit | e5d477340a27793e23258c2a2ed08e3682d37205 (patch) | |
tree | 061f5ab49076b2de32560cd76aeec96348764283 /net/base | |
parent | f355ed2c91dc2ff549ab45a4af038b3fbce3d7ec (diff) | |
download | chromium_src-e5d477340a27793e23258c2a2ed08e3682d37205.zip chromium_src-e5d477340a27793e23258c2a2ed08e3682d37205.tar.gz chromium_src-e5d477340a27793e23258c2a2ed08e3682d37205.tar.bz2 |
net: Allow calling UploadDataStream::Init() multiple times for reset purpose
There should be a way to reset UploadDataStream for reusing the stream again and again.
BUG=156574
TEST=net_unittests
Review URL: https://chromiumcodereview.appspot.com/11304003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@165680 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/upload_bytes_element_reader.cc | 3 | ||||
-rw-r--r-- | net/base/upload_bytes_element_reader_unittest.cc | 22 | ||||
-rw-r--r-- | net/base/upload_data_stream.cc | 73 | ||||
-rw-r--r-- | net/base/upload_data_stream.h | 22 | ||||
-rw-r--r-- | net/base/upload_data_stream_unittest.cc | 242 | ||||
-rw-r--r-- | net/base/upload_element_reader.h | 4 | ||||
-rw-r--r-- | net/base/upload_file_element_reader.cc | 41 | ||||
-rw-r--r-- | net/base/upload_file_element_reader.h | 18 | ||||
-rw-r--r-- | net/base/upload_file_element_reader_unittest.cc | 85 |
9 files changed, 441 insertions, 69 deletions
diff --git a/net/base/upload_bytes_element_reader.cc b/net/base/upload_bytes_element_reader.cc index 22d44f4..dab63f4 100644 --- a/net/base/upload_bytes_element_reader.cc +++ b/net/base/upload_bytes_element_reader.cc @@ -21,10 +21,11 @@ UploadBytesElementReader::~UploadBytesElementReader() { } int UploadBytesElementReader::Init(const CompletionCallback& callback) { - return OK; + return InitSync(); } int UploadBytesElementReader::InitSync() { + offset_ = 0; return OK; } diff --git a/net/base/upload_bytes_element_reader_unittest.cc b/net/base/upload_bytes_element_reader_unittest.cc index 1a3c5d4..eca720f 100644 --- a/net/base/upload_bytes_element_reader_unittest.cc +++ b/net/base/upload_bytes_element_reader_unittest.cc @@ -62,4 +62,26 @@ TEST_F(UploadBytesElementReaderTest, ReadTooMuch) { EXPECT_EQ(bytes_, buf); } +TEST_F(UploadBytesElementReaderTest, MultipleInit) { + std::vector<char> buf(bytes_.size()); + scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]); + + // Read all. + EXPECT_EQ(static_cast<int>(buf.size()), + reader_->ReadSync(wrapped_buffer, buf.size())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(bytes_, buf); + + // Call Init() again to reset the state. + ASSERT_EQ(OK, reader_->InitSync()); + EXPECT_EQ(bytes_.size(), reader_->GetContentLength()); + EXPECT_EQ(bytes_.size(), reader_->BytesRemaining()); + + // Read again. + EXPECT_EQ(static_cast<int>(buf.size()), + reader_->ReadSync(wrapped_buffer, buf.size())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(bytes_, buf); +} + } // namespace net diff --git a/net/base/upload_data_stream.cc b/net/base/upload_data_stream.cc index 405a638..3cc85f1 100644 --- a/net/base/upload_data_stream.cc +++ b/net/base/upload_data_stream.cc @@ -25,41 +25,37 @@ UploadDataStream::UploadDataStream(UploadData* upload_data) total_size_(0), current_position_(0), initialized_successfully_(false), - weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { + weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + weak_ptr_factory_for_chunks_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { const std::vector<UploadElement>& elements = *upload_data_->elements(); for (size_t i = 0; i < elements.size(); ++i) element_readers_.push_back(UploadElementReader::Create(elements[i])); upload_data_->set_chunk_callback( base::Bind(&UploadDataStream::OnChunkAvailable, - weak_ptr_factory_.GetWeakPtr())); + weak_ptr_factory_for_chunks_.GetWeakPtr())); } UploadDataStream::~UploadDataStream() { } int UploadDataStream::Init(const CompletionCallback& callback) { - DCHECK(!initialized_successfully_); - + Reset(); // Use fast path when initialization can be done synchronously. if (IsInMemory()) return InitSync(); - InitInternal(0, callback, OK); - return ERR_IO_PENDING; + return InitInternal(0, callback); } int UploadDataStream::InitSync() { - DCHECK(!initialized_successfully_); - + Reset(); // Initialize all readers synchronously. for (size_t i = 0; i < element_readers_.size(); ++i) { UploadElementReader* reader = element_readers_[i]; const int result = reader->InitSync(); - if (result != OK) { - element_readers_.clear(); + if (result != OK) return result; - } } FinalizeInitialization(); @@ -109,41 +105,54 @@ bool UploadDataStream::IsInMemory() const { return true; } -void UploadDataStream::InitInternal(int start_index, - const CompletionCallback& callback, - int previous_result) { - DCHECK(!initialized_successfully_); - DCHECK_NE(ERR_IO_PENDING, previous_result); +void UploadDataStream::Reset() { + weak_ptr_factory_.InvalidateWeakPtrs(); + pending_chunked_read_callback_.Reset(); + initialized_successfully_ = false; + current_position_ = 0; + total_size_ = 0; + element_index_ = 0; +} - // Check the last result. - if (previous_result != OK) { - element_readers_.clear(); - callback.Run(previous_result); - return; - } +int UploadDataStream::InitInternal(int start_index, + const CompletionCallback& callback) { + DCHECK(!initialized_successfully_); // Call Init() for all elements. for (size_t i = start_index; i < element_readers_.size(); ++i) { UploadElementReader* reader = element_readers_[i]; // When new_result is ERR_IO_PENDING, InitInternal() will be called // with start_index == i + 1 when reader->Init() finishes. - const int new_result = reader->Init( - base::Bind(&UploadDataStream::InitInternal, + const int result = reader->Init( + base::Bind(&UploadDataStream::ResumePendingInit, weak_ptr_factory_.GetWeakPtr(), i + 1, callback)); - if (new_result != OK) { - if (new_result != ERR_IO_PENDING) { - element_readers_.clear(); - callback.Run(new_result); - } - return; - } + if (result != OK) + return result; } // Finalize initialization. FinalizeInitialization(); - callback.Run(OK); + return OK; +} + +void UploadDataStream::ResumePendingInit(int start_index, + const CompletionCallback& callback, + int previous_result) { + DCHECK(!initialized_successfully_); + DCHECK(!callback.is_null()); + DCHECK_NE(ERR_IO_PENDING, previous_result); + + // Check the last result. + if (previous_result != OK) { + callback.Run(previous_result); + return; + } + + const int result = InitInternal(start_index, callback); + if (result != ERR_IO_PENDING) + callback.Run(result); } void UploadDataStream::FinalizeInitialization() { diff --git a/net/base/upload_data_stream.h b/net/base/upload_data_stream.h index 0e001f9..263ca97 100644 --- a/net/base/upload_data_stream.h +++ b/net/base/upload_data_stream.h @@ -25,9 +25,11 @@ class NET_EXPORT UploadDataStream { explicit UploadDataStream(UploadData* upload_data); ~UploadDataStream(); - // Initializes the stream. This function must be called exactly once, - // before calling any other method. It is not valid to call any method - // (other than the destructor) if Init() returns a failure. + // Initializes the stream. This function must be called before calling any + // other method. It is not valid to call any method (other than the + // destructor) if Init() returns a failure. This method can be called multiple + // times. Calling this method after a Init() success results in resetting the + // state. // // Does the initialization synchronously and returns the result if possible, // otherwise returns ERR_IO_PENDING and runs the callback with the result. @@ -87,11 +89,17 @@ class NET_EXPORT UploadDataStream { FRIEND_TEST_ALL_PREFIXES(UploadDataStreamTest, InitAsyncFailureSync); FRIEND_TEST_ALL_PREFIXES(UploadDataStreamTest, ReadAsync); + // Resets this instance to the uninitialized state. + void Reset(); + // Runs Init() for all element readers. // This method is used to implement Init(). - void InitInternal(int start_index, - const CompletionCallback& callback, - int previous_result); + int InitInternal(int start_index, const CompletionCallback& callback); + + // Resumes initialization and runs callback with the result when necessary. + void ResumePendingInit(int start_index, + const CompletionCallback& callback, + int previous_result); // Finalizes the initialization process. // This method is used to implement Init(). @@ -135,6 +143,8 @@ class NET_EXPORT UploadDataStream { base::WeakPtrFactory<UploadDataStream> weak_ptr_factory_; + base::WeakPtrFactory<UploadDataStream> weak_ptr_factory_for_chunks_; + // TODO(satish): Remove this once we have a better way to unit test POST // requests with chunked uploads. static bool merge_chunks_; diff --git a/net/base/upload_data_stream_unittest.cc b/net/base/upload_data_stream_unittest.cc index 57a34ca..3c77804 100644 --- a/net/base/upload_data_stream_unittest.cc +++ b/net/base/upload_data_stream_unittest.cc @@ -13,9 +13,11 @@ #include "base/file_util.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" +#include "base/scoped_temp_dir.h" #include "base/time.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" +#include "net/base/test_completion_callback.h" #include "net/base/upload_data.h" #include "net/base/upload_file_element_reader.h" #include "testing/gmock/include/gmock/gmock.h" @@ -128,10 +130,16 @@ class UploadDataStreamTest : public PlatformTest { public: UploadDataStreamTest() : upload_data_(new UploadData) { } + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + } + void FileChangedHelper(const FilePath& file_path, const base::Time& time, bool error_expected); + ScopedTempDir temp_dir_; + scoped_refptr<UploadData> upload_data_; }; @@ -164,7 +172,8 @@ TEST_F(UploadDataStreamTest, ConsumeAllBytes) { TEST_F(UploadDataStreamTest, File) { FilePath temp_file_path; - ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path)); + ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), + &temp_file_path)); ASSERT_EQ(static_cast<int>(kTestDataSize), file_util::WriteFile(temp_file_path, kTestData, kTestDataSize)); @@ -187,12 +196,12 @@ TEST_F(UploadDataStreamTest, File) { } EXPECT_EQ(kTestDataSize, stream.position()); ASSERT_TRUE(stream.IsEOF()); - file_util::Delete(temp_file_path, false); } TEST_F(UploadDataStreamTest, FileSmallerThanLength) { FilePath temp_file_path; - ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path)); + ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), + &temp_file_path)); ASSERT_EQ(static_cast<int>(kTestDataSize), file_util::WriteFile(temp_file_path, kTestData, kTestDataSize)); const uint64 kFakeSize = kTestDataSize*2; @@ -224,13 +233,12 @@ TEST_F(UploadDataStreamTest, FileSmallerThanLength) { // transaction doesn't hang. Therefore we expected the full size. EXPECT_EQ(kFakeSize, read_counter); EXPECT_EQ(read_counter, stream.position()); - - file_util::Delete(temp_file_path, false); } TEST_F(UploadDataStreamTest, FileAndBytes) { FilePath temp_file_path; - ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path)); + ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), + &temp_file_path)); ASSERT_EQ(static_cast<int>(kTestDataSize), file_util::WriteFile(temp_file_path, kTestData, kTestDataSize)); @@ -255,8 +263,6 @@ TEST_F(UploadDataStreamTest, FileAndBytes) { } EXPECT_EQ(kStreamSize, stream.position()); ASSERT_TRUE(stream.IsEOF()); - - file_util::Delete(temp_file_path, false); } TEST_F(UploadDataStreamTest, Chunk) { @@ -455,7 +461,8 @@ void UploadDataStreamTest::FileChangedHelper(const FilePath& file_path, TEST_F(UploadDataStreamTest, FileChanged) { FilePath temp_file_path; - ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path)); + ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), + &temp_file_path)); ASSERT_EQ(static_cast<int>(kTestDataSize), file_util::WriteFile(temp_file_path, kTestData, kTestDataSize)); @@ -469,13 +476,12 @@ TEST_F(UploadDataStreamTest, FileChanged) { FileChangedHelper(temp_file_path, file_info.last_modified - base::TimeDelta::FromSeconds(1), true); - - file_util::Delete(temp_file_path, false); } TEST_F(UploadDataStreamTest, UploadDataReused) { FilePath temp_file_path; - ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path)); + ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), + &temp_file_path)); ASSERT_EQ(static_cast<int>(kTestDataSize), file_util::WriteFile(temp_file_path, kTestData, kTestDataSize)); @@ -502,8 +508,218 @@ TEST_F(UploadDataStreamTest, UploadDataReused) { EXPECT_EQ(kTestDataSize, stream.size()); EXPECT_EQ(kTestData, ReadFromUploadDataStream(&stream)); } +} + +TEST_F(UploadDataStreamTest, MultipleInit) { + FilePath temp_file_path; + ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), + &temp_file_path)); + ASSERT_EQ(static_cast<int>(kTestDataSize), + file_util::WriteFile(temp_file_path, kTestData, kTestDataSize)); + + // Prepare data. + upload_data_->AppendBytes(kTestData, kTestDataSize); + upload_data_->AppendFileRange(temp_file_path, 0, kuint64max, base::Time()); + UploadDataStream stream(upload_data_); + + std::string expected_data(kTestData, kTestData + kTestDataSize); + expected_data += expected_data; + + // Call InitSync(). + ASSERT_EQ(OK, stream.InitSync()); + EXPECT_FALSE(stream.IsEOF()); + EXPECT_EQ(kTestDataSize*2, stream.size()); + + // Read. + EXPECT_EQ(expected_data, ReadFromUploadDataStream(&stream)); + EXPECT_TRUE(stream.IsEOF()); + + // Call InitSync() again to reset. + ASSERT_EQ(OK, stream.InitSync()); + EXPECT_FALSE(stream.IsEOF()); + EXPECT_EQ(kTestDataSize*2, stream.size()); + + // Read again. + EXPECT_EQ(expected_data, ReadFromUploadDataStream(&stream)); + EXPECT_TRUE(stream.IsEOF()); +} + +TEST_F(UploadDataStreamTest, MultipleInitAsync) { + FilePath temp_file_path; + ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), + &temp_file_path)); + ASSERT_EQ(static_cast<int>(kTestDataSize), + file_util::WriteFile(temp_file_path, kTestData, kTestDataSize)); + TestCompletionCallback test_callback; + + // Prepare data. + upload_data_->AppendBytes(kTestData, kTestDataSize); + upload_data_->AppendFileRange(temp_file_path, 0, kuint64max, base::Time()); + UploadDataStream stream(upload_data_); + + std::string expected_data(kTestData, kTestData + kTestDataSize); + expected_data += expected_data; + + // Call Init(). + ASSERT_EQ(ERR_IO_PENDING, stream.Init(test_callback.callback())); + EXPECT_EQ(OK, test_callback.WaitForResult()); + EXPECT_FALSE(stream.IsEOF()); + EXPECT_EQ(kTestDataSize*2, stream.size()); + + // Read. + EXPECT_EQ(expected_data, ReadFromUploadDataStream(&stream)); + EXPECT_TRUE(stream.IsEOF()); + + // Call Init() again to reset. + ASSERT_EQ(ERR_IO_PENDING, stream.Init(test_callback.callback())); + EXPECT_EQ(OK, test_callback.WaitForResult()); + EXPECT_FALSE(stream.IsEOF()); + EXPECT_EQ(kTestDataSize*2, stream.size()); + + // Read again. + EXPECT_EQ(expected_data, ReadFromUploadDataStream(&stream)); + EXPECT_TRUE(stream.IsEOF()); +} + +TEST_F(UploadDataStreamTest, InitToReset) { + FilePath temp_file_path; + ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), + &temp_file_path)); + ASSERT_EQ(static_cast<int>(kTestDataSize), + file_util::WriteFile(temp_file_path, kTestData, kTestDataSize)); + + // Prepare data. + upload_data_->AppendBytes(kTestData, kTestDataSize); + upload_data_->AppendFileRange(temp_file_path, 0, kuint64max, base::Time()); + UploadDataStream stream(upload_data_); + + std::vector<char> expected_data(kTestData, kTestData + kTestDataSize); + expected_data.insert(expected_data.end(), expected_data.begin(), + expected_data.begin() + kTestDataSize); + + // Call Init(). + TestCompletionCallback init_callback1; + ASSERT_EQ(ERR_IO_PENDING, stream.Init(init_callback1.callback())); + EXPECT_EQ(OK, init_callback1.WaitForResult()); + EXPECT_FALSE(stream.IsEOF()); + EXPECT_EQ(kTestDataSize*2, stream.size()); + + // Read some. + TestCompletionCallback read_callback1; + std::vector<char> buf(kTestDataSize + kTestDataSize/2); + scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]); + EXPECT_EQ(ERR_IO_PENDING, stream.Read(wrapped_buffer, buf.size(), + read_callback1.callback())); + EXPECT_EQ(static_cast<int>(buf.size()), read_callback1.WaitForResult()); + EXPECT_EQ(buf.size(), stream.position()); + + // Call Init to reset the state. + TestCompletionCallback init_callback2; + ASSERT_EQ(ERR_IO_PENDING, stream.Init(init_callback2.callback())); + EXPECT_EQ(OK, init_callback2.WaitForResult()); + EXPECT_FALSE(stream.IsEOF()); + EXPECT_EQ(kTestDataSize*2, stream.size()); + + // Read. + TestCompletionCallback read_callback2; + std::vector<char> buf2(kTestDataSize*2); + scoped_refptr<IOBuffer> wrapped_buffer2 = new WrappedIOBuffer(&buf2[0]); + EXPECT_EQ(ERR_IO_PENDING, stream.Read(wrapped_buffer2, buf2.size(), + read_callback2.callback())); + EXPECT_EQ(static_cast<int>(buf2.size()), read_callback2.WaitForResult()); + EXPECT_EQ(expected_data, buf2); +} + +TEST_F(UploadDataStreamTest, InitDuringAsyncInit) { + FilePath temp_file_path; + ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), + &temp_file_path)); + ASSERT_EQ(static_cast<int>(kTestDataSize), + file_util::WriteFile(temp_file_path, kTestData, kTestDataSize)); + + // Prepare data. + upload_data_->AppendBytes(kTestData, kTestDataSize); + upload_data_->AppendFileRange(temp_file_path, 0, kuint64max, base::Time()); + UploadDataStream stream(upload_data_); + + std::vector<char> expected_data(kTestData, kTestData + kTestDataSize); + expected_data.insert(expected_data.end(), expected_data.begin(), + expected_data.begin() + kTestDataSize); + + // Start Init. + TestCompletionCallback init_callback1; + EXPECT_EQ(ERR_IO_PENDING, stream.Init(init_callback1.callback())); + + // Call Init again to cancel the previous init. + TestCompletionCallback init_callback2; + EXPECT_EQ(ERR_IO_PENDING, stream.Init(init_callback2.callback())); + EXPECT_EQ(OK, init_callback2.WaitForResult()); + EXPECT_FALSE(stream.IsEOF()); + EXPECT_EQ(kTestDataSize*2, stream.size()); + + // Read. + TestCompletionCallback read_callback2; + std::vector<char> buf2(kTestDataSize*2); + scoped_refptr<IOBuffer> wrapped_buffer2 = new WrappedIOBuffer(&buf2[0]); + EXPECT_EQ(ERR_IO_PENDING, stream.Read(wrapped_buffer2, buf2.size(), + read_callback2.callback())); + EXPECT_EQ(static_cast<int>(buf2.size()), read_callback2.WaitForResult()); + EXPECT_EQ(expected_data, buf2); + EXPECT_TRUE(stream.IsEOF()); + + // Make sure callbacks are not called for cancelled operations. + EXPECT_FALSE(init_callback1.have_result()); +} + +TEST_F(UploadDataStreamTest, InitDuringAsyncRead) { + FilePath temp_file_path; + ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), + &temp_file_path)); + ASSERT_EQ(static_cast<int>(kTestDataSize), + file_util::WriteFile(temp_file_path, kTestData, kTestDataSize)); + + // Prepare data. + upload_data_->AppendBytes(kTestData, kTestDataSize); + upload_data_->AppendFileRange(temp_file_path, 0, kuint64max, base::Time()); + UploadDataStream stream(upload_data_); + + std::vector<char> expected_data(kTestData, kTestData + kTestDataSize); + expected_data.insert(expected_data.end(), expected_data.begin(), + expected_data.begin() + kTestDataSize); + + // Call Init(). + TestCompletionCallback init_callback1; + ASSERT_EQ(ERR_IO_PENDING, stream.Init(init_callback1.callback())); + EXPECT_EQ(OK, init_callback1.WaitForResult()); + EXPECT_FALSE(stream.IsEOF()); + EXPECT_EQ(kTestDataSize*2, stream.size()); + + // Start reading. + TestCompletionCallback read_callback1; + std::vector<char> buf(kTestDataSize*2); + scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]); + EXPECT_EQ(ERR_IO_PENDING, stream.Read(wrapped_buffer, buf.size(), + read_callback1.callback())); + + // Call Init to cancel the previous read. + TestCompletionCallback init_callback2; + EXPECT_EQ(ERR_IO_PENDING, stream.Init(init_callback2.callback())); + EXPECT_EQ(OK, init_callback2.WaitForResult()); + EXPECT_FALSE(stream.IsEOF()); + EXPECT_EQ(kTestDataSize*2, stream.size()); + + // Read. + TestCompletionCallback read_callback2; + std::vector<char> buf2(kTestDataSize*2); + scoped_refptr<IOBuffer> wrapped_buffer2 = new WrappedIOBuffer(&buf2[0]); + EXPECT_EQ(ERR_IO_PENDING, stream.Read(wrapped_buffer2, buf2.size(), + read_callback2.callback())); + EXPECT_EQ(static_cast<int>(buf2.size()), read_callback2.WaitForResult()); + EXPECT_EQ(expected_data, buf2); + EXPECT_TRUE(stream.IsEOF()); - file_util::Delete(temp_file_path, false); + // Make sure callbacks are not called for cancelled operations. + EXPECT_FALSE(read_callback1.have_result()); } } // namespace net diff --git a/net/base/upload_element_reader.h b/net/base/upload_element_reader.h index 4ba973d..1511f74 100644 --- a/net/base/upload_element_reader.h +++ b/net/base/upload_element_reader.h @@ -25,10 +25,12 @@ class NET_EXPORT UploadElementReader { // Initializes the instance synchronously when possible, otherwise does // initialization aynschronously, returns ERR_IO_PENDING and runs callback. + // Calling this method again after a Init() success results in resetting the + // state. virtual int Init(const CompletionCallback& callback) = 0; // Initializes the instance always synchronously. - // Use this method only in tests and Chrome Frame. + // Use this method only if the thread is IO allowed or the data is in-memory. virtual int InitSync(); // Returns the byte-length of the element. For files that do not exist, 0 diff --git a/net/base/upload_file_element_reader.cc b/net/base/upload_file_element_reader.cc index 97fab5e..11144e6 100644 --- a/net/base/upload_file_element_reader.cc +++ b/net/base/upload_file_element_reader.cc @@ -26,7 +26,7 @@ int InitInternal(const FilePath& path, uint64 range_offset, uint64 range_length, const base::Time& expected_modification_time, - scoped_ptr<FileStream>* out_file_stream, + UploadFileElementReader::ScopedFileStreamPtr* out_file_stream, uint64* out_content_length) { scoped_ptr<FileStream> file_stream(new FileStream(NULL)); int64 rv = file_stream->OpenSync( @@ -54,7 +54,7 @@ int InitInternal(const FilePath& path, length = std::min(length - range_offset, range_length); } *out_content_length = length; - out_file_stream->swap(file_stream); + 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 @@ -100,6 +100,16 @@ int ReadInternal(scoped_refptr<IOBuffer> buf, } // namespace +void UploadFileElementReader::FileStreamDeleter::operator() ( + FileStream* file_stream) const { + if (file_stream) { + base::WorkerPool::PostTask(FROM_HERE, + base::Bind(&base::DeletePointer<FileStream>, + file_stream), + true /* task_is_slow */); + } +} + UploadFileElementReader::UploadFileElementReader( const FilePath& path, uint64 range_offset, @@ -111,20 +121,16 @@ UploadFileElementReader::UploadFileElementReader( expected_modification_time_(expected_modification_time), content_length_(0), bytes_remaining_(0), - weak_ptr_factory_(this) { + ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { } UploadFileElementReader::~UploadFileElementReader() { - if (file_stream_.get()) { - base::WorkerPool::PostTask(FROM_HERE, - base::Bind(&base::DeletePointer<FileStream>, - file_stream_.release()), - true /* task_is_slow */); - } } int UploadFileElementReader::Init(const CompletionCallback& callback) { - scoped_ptr<FileStream>* file_stream = new scoped_ptr<FileStream>; + Reset(); + + ScopedFileStreamPtr* file_stream = new ScopedFileStreamPtr; uint64* content_length = new uint64; const bool posted = base::PostTaskAndReplyWithResult( base::WorkerPool::GetTaskRunner(true /* task_is_slow */), @@ -146,7 +152,9 @@ int UploadFileElementReader::Init(const CompletionCallback& callback) { } int UploadFileElementReader::InitSync() { - scoped_ptr<FileStream> file_stream; + Reset(); + + ScopedFileStreamPtr file_stream; uint64 content_length = 0; const int result = InitInternal(path_, range_offset_, range_length_, expected_modification_time_, @@ -200,8 +208,15 @@ int UploadFileElementReader::ReadSync(IOBuffer* buf, int buf_length) { return result; } +void UploadFileElementReader::Reset() { + weak_ptr_factory_.InvalidateWeakPtrs(); + bytes_remaining_ = 0; + content_length_ = 0; + file_stream_.reset(); +} + void UploadFileElementReader::OnInitCompleted( - scoped_ptr<FileStream>* file_stream, + ScopedFileStreamPtr* file_stream, uint64* content_length, const CompletionCallback& callback, int result) { @@ -213,7 +228,7 @@ void UploadFileElementReader::OnInitCompleted( } void UploadFileElementReader::OnReadCompleted( - scoped_ptr<FileStream> file_stream, + ScopedFileStreamPtr file_stream, const CompletionCallback& callback, int result) { file_stream_.swap(file_stream); diff --git a/net/base/upload_file_element_reader.h b/net/base/upload_file_element_reader.h index 57e8c97..1a9b9bf 100644 --- a/net/base/upload_file_element_reader.h +++ b/net/base/upload_file_element_reader.h @@ -20,6 +20,15 @@ class FileStream; // An UploadElementReader implementation for file. class NET_EXPORT_PRIVATE UploadFileElementReader : public UploadElementReader { public: + // Deletes FileStream on the worker pool to avoid blocking the IO thread. + // This class is used as a template argument of scoped_ptr_malloc. + class FileStreamDeleter { + public: + void operator() (FileStream* file_stream) const; + }; + + typedef scoped_ptr_malloc<FileStream, FileStreamDeleter> ScopedFileStreamPtr; + UploadFileElementReader(const FilePath& path, uint64 range_offset, uint64 range_length, @@ -37,14 +46,17 @@ class NET_EXPORT_PRIVATE UploadFileElementReader : public UploadElementReader { virtual int ReadSync(IOBuffer* buf, int buf_length) OVERRIDE; private: + // Resets this instance to the uninitialized state. + void Reset(); + // This method is used to implement Init(). - void OnInitCompleted(scoped_ptr<FileStream>* file_stream, + void OnInitCompleted(ScopedFileStreamPtr* file_stream, uint64* content_length, const CompletionCallback& callback, int result); // This method is used to implement Read(). - void OnReadCompleted(scoped_ptr<FileStream> file_stream, + void OnReadCompleted(ScopedFileStreamPtr file_stream, const CompletionCallback& callback, int result); @@ -59,7 +71,7 @@ class NET_EXPORT_PRIVATE UploadFileElementReader : public UploadElementReader { uint64 range_offset_; uint64 range_length_; base::Time expected_modification_time_; - scoped_ptr<FileStream> file_stream_; + ScopedFileStreamPtr file_stream_; uint64 content_length_; uint64 bytes_remaining_; base::WeakPtrFactory<UploadFileElementReader> weak_ptr_factory_; diff --git a/net/base/upload_file_element_reader_unittest.cc b/net/base/upload_file_element_reader_unittest.cc index c0dc943..f8a9c34 100644 --- a/net/base/upload_file_element_reader_unittest.cc +++ b/net/base/upload_file_element_reader_unittest.cc @@ -81,6 +81,28 @@ TEST_F(UploadFileElementReaderTest, ReadTooMuch) { EXPECT_EQ(bytes_, buf); } +TEST_F(UploadFileElementReaderTest, MultipleInit) { + std::vector<char> buf(bytes_.size()); + scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]); + + // Read all. + EXPECT_EQ(static_cast<int>(buf.size()), + reader_->ReadSync(wrapped_buffer, buf.size())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(bytes_, buf); + + // Call InitSync() again to reset the state. + ASSERT_EQ(OK, reader_->InitSync()); + EXPECT_EQ(bytes_.size(), reader_->GetContentLength()); + EXPECT_EQ(bytes_.size(), reader_->BytesRemaining()); + + // Read again. + EXPECT_EQ(static_cast<int>(buf.size()), + reader_->ReadSync(wrapped_buffer, buf.size())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(bytes_, buf); +} + TEST_F(UploadFileElementReaderTest, ReadPartiallyAsync) { const size_t kHalfSize = bytes_.size() / 2; ASSERT_EQ(bytes_.size(), kHalfSize * 2); @@ -134,6 +156,69 @@ TEST_F(UploadFileElementReaderTest, ReadTooMuchAsync) { EXPECT_EQ(bytes_, buf); } +TEST_F(UploadFileElementReaderTest, MultipleInitAsync) { + std::vector<char> buf(bytes_.size()); + scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]); + TestCompletionCallback test_callback; + + // Read all. + EXPECT_EQ(ERR_IO_PENDING, reader_->Read(wrapped_buffer, buf.size(), + test_callback.callback())); + EXPECT_EQ(static_cast<int>(buf.size()), test_callback.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(bytes_, buf); + + // Call Init() again to reset the state. + EXPECT_EQ(ERR_IO_PENDING, reader_->Init(test_callback.callback())); + EXPECT_EQ(OK, test_callback.WaitForResult()); + EXPECT_EQ(bytes_.size(), reader_->GetContentLength()); + EXPECT_EQ(bytes_.size(), reader_->BytesRemaining()); + + // Read again. + EXPECT_EQ(ERR_IO_PENDING, reader_->Read(wrapped_buffer, buf.size(), + test_callback.callback())); + + EXPECT_EQ(static_cast<int>(buf.size()), test_callback.WaitForResult()); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(bytes_, buf); +} + +TEST_F(UploadFileElementReaderTest, InitDuringAsyncOperation) { + std::vector<char> buf(bytes_.size()); + scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]); + + // Start reading all. + TestCompletionCallback read_callback1; + EXPECT_EQ(ERR_IO_PENDING, reader_->Read(wrapped_buffer, buf.size(), + read_callback1.callback())); + + // Call Init to cancel the previous read. + TestCompletionCallback init_callback1; + EXPECT_EQ(ERR_IO_PENDING, reader_->Init(init_callback1.callback())); + + // Call Init again to cancel the previous init. + TestCompletionCallback init_callback2; + EXPECT_EQ(ERR_IO_PENDING, reader_->Init(init_callback2.callback())); + EXPECT_EQ(OK, init_callback2.WaitForResult()); + EXPECT_EQ(bytes_.size(), reader_->GetContentLength()); + EXPECT_EQ(bytes_.size(), reader_->BytesRemaining()); + + // Read half. + std::vector<char> buf2(bytes_.size() / 2); + scoped_refptr<IOBuffer> wrapped_buffer2 = new WrappedIOBuffer(&buf2[0]); + TestCompletionCallback read_callback2; + EXPECT_EQ(ERR_IO_PENDING, reader_->Read(wrapped_buffer2, buf2.size(), + read_callback2.callback())); + EXPECT_EQ(static_cast<int>(buf2.size()), read_callback2.WaitForResult()); + EXPECT_EQ(bytes_.size() - buf2.size(), reader_->BytesRemaining()); + EXPECT_EQ(std::vector<char>(bytes_.begin(), bytes_.begin() + buf2.size()), + buf2); + + // Make sure callbacks are not called for cancelled operations. + EXPECT_FALSE(read_callback1.have_result()); + EXPECT_FALSE(init_callback1.have_result()); +} + TEST_F(UploadFileElementReaderTest, Range) { const uint64 kOffset = 2; const uint64 kLength = bytes_.size() - kOffset * 3; |