summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
authorhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-02 15:18:46 +0000
committerhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-02 15:18:46 +0000
commite5d477340a27793e23258c2a2ed08e3682d37205 (patch)
tree061f5ab49076b2de32560cd76aeec96348764283 /net/base
parentf355ed2c91dc2ff549ab45a4af038b3fbce3d7ec (diff)
downloadchromium_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.cc3
-rw-r--r--net/base/upload_bytes_element_reader_unittest.cc22
-rw-r--r--net/base/upload_data_stream.cc73
-rw-r--r--net/base/upload_data_stream.h22
-rw-r--r--net/base/upload_data_stream_unittest.cc242
-rw-r--r--net/base/upload_element_reader.h4
-rw-r--r--net/base/upload_file_element_reader.cc41
-rw-r--r--net/base/upload_file_element_reader.h18
-rw-r--r--net/base/upload_file_element_reader_unittest.cc85
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;