diff options
author | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-18 14:01:53 +0000 |
---|---|---|
committer | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-18 14:01:53 +0000 |
commit | df7adc6e3a6c82831052ebe799912e66ee01304d (patch) | |
tree | 3d05c14a37ab3b598a2b76e1c251b9b867a721a5 | |
parent | 014bc6fdc57911f95f9de6119b70bcbda774b1e8 (diff) | |
download | chromium_src-df7adc6e3a6c82831052ebe799912e66ee01304d.zip chromium_src-df7adc6e3a6c82831052ebe799912e66ee01304d.tar.gz chromium_src-df7adc6e3a6c82831052ebe799912e66ee01304d.tar.bz2 |
net: Make UploadDataStream::Init() asynchronous.
Rename the existing synchronous version to InitSync() and add a new asynchronous version, Init().
However, the new asynchronous version is not used yet.
The existing code is changed to use the synchronous version.
BUG=72001
TEST=net_unittests
Review URL: https://chromiumcodereview.appspot.com/10913179
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@157344 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome_frame/urlmon_upload_data_stream.cc | 4 | ||||
-rw-r--r-- | net/base/upload_bytes_element_reader.cc | 4 | ||||
-rw-r--r-- | net/base/upload_bytes_element_reader.h | 1 | ||||
-rw-r--r-- | net/base/upload_data_stream.cc | 77 | ||||
-rw-r--r-- | net/base/upload_data_stream.h | 30 | ||||
-rw-r--r-- | net/base/upload_data_stream_unittest.cc | 154 | ||||
-rw-r--r-- | net/base/upload_element_reader.cc | 6 | ||||
-rw-r--r-- | net/base/upload_element_reader.h | 10 | ||||
-rw-r--r-- | net/base/upload_file_element_reader.cc | 148 | ||||
-rw-r--r-- | net/base/upload_file_element_reader.h | 9 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 2 | ||||
-rw-r--r-- | net/http/http_stream_parser_unittest.cc | 12 | ||||
-rw-r--r-- | net/spdy/spdy_http_stream_spdy2_unittest.cc | 4 | ||||
-rw-r--r-- | net/spdy/spdy_http_stream_spdy3_unittest.cc | 6 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_spdy2_unittest.cc | 4 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_spdy3_unittest.cc | 4 |
16 files changed, 390 insertions, 85 deletions
diff --git a/chrome_frame/urlmon_upload_data_stream.cc b/chrome_frame/urlmon_upload_data_stream.cc index 2437554..8aae0df7 100644 --- a/chrome_frame/urlmon_upload_data_stream.cc +++ b/chrome_frame/urlmon_upload_data_stream.cc @@ -11,7 +11,7 @@ void UrlmonUploadDataStream::Initialize(net::UploadData* upload_data) { upload_data_ = upload_data; request_body_stream_.reset( new net::UploadDataStream(upload_data)); - const int result = request_body_stream_->Init(); + const int result = request_body_stream_->InitSync(); DCHECK_EQ(net::OK, result); } @@ -70,7 +70,7 @@ STDMETHODIMP UrlmonUploadDataStream::Seek(LARGE_INTEGER move, DWORD origin, if (origin == STREAM_SEEK_SET && move.QuadPart == 0) { if (request_body_stream_->position() != 0) { request_body_stream_.reset(new net::UploadDataStream(upload_data_)); - const int result = request_body_stream_->Init(); + const int result = request_body_stream_->InitSync(); DCHECK_EQ(net::OK, result); } if (new_pos) { diff --git a/net/base/upload_bytes_element_reader.cc b/net/base/upload_bytes_element_reader.cc index c99ce74..9beb1f4 100644 --- a/net/base/upload_bytes_element_reader.cc +++ b/net/base/upload_bytes_element_reader.cc @@ -19,6 +19,10 @@ UploadBytesElementReader::UploadBytesElementReader(const char* bytes, UploadBytesElementReader::~UploadBytesElementReader() { } +int UploadBytesElementReader::Init(const CompletionCallback& callback) { + return OK; +} + int UploadBytesElementReader::InitSync() { return OK; } diff --git a/net/base/upload_bytes_element_reader.h b/net/base/upload_bytes_element_reader.h index cb9d3e8..1467ed7 100644 --- a/net/base/upload_bytes_element_reader.h +++ b/net/base/upload_bytes_element_reader.h @@ -17,6 +17,7 @@ class NET_EXPORT_PRIVATE UploadBytesElementReader : public UploadElementReader { virtual ~UploadBytesElementReader(); // UploadElementReader overrides: + virtual int Init(const CompletionCallback& callback) OVERRIDE; virtual int InitSync() OVERRIDE; virtual uint64 GetContentLength() const OVERRIDE; virtual uint64 BytesRemaining() const OVERRIDE; diff --git a/net/base/upload_data_stream.cc b/net/base/upload_data_stream.cc index e46f2e6..a62528d 100644 --- a/net/base/upload_data_stream.cc +++ b/net/base/upload_data_stream.cc @@ -24,7 +24,8 @@ UploadDataStream::UploadDataStream(UploadData* upload_data) element_index_(0), total_size_(0), current_position_(0), - initialized_successfully_(false) { + initialized_successfully_(false), + weak_ptr_factory_(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])); @@ -33,21 +34,31 @@ UploadDataStream::UploadDataStream(UploadData* upload_data) UploadDataStream::~UploadDataStream() { } -int UploadDataStream::Init() { +int UploadDataStream::Init(const CompletionCallback& callback) { DCHECK(!initialized_successfully_); - uint64 total_size = 0; + // Use fast path when initialization can be done synchronously. + if (IsInMemory()) + return InitSync(); + + InitInternal(0, callback, OK); + return ERR_IO_PENDING; +} + +int UploadDataStream::InitSync() { + DCHECK(!initialized_successfully_); + + // 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) + if (result != OK) { + element_readers_.clear(); return result; - if (!is_chunked()) - total_size += reader->GetContentLength(); + } } - total_size_ = total_size; - initialized_successfully_ = true; + FinalizeInitialization(); return OK; } @@ -118,4 +129,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); + + // Check the last result. + if (previous_result != OK) { + element_readers_.clear(); + callback.Run(previous_result); + return; + } + + // 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, + 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; + } + } + + // Finalize initialization. + FinalizeInitialization(); + callback.Run(OK); +} + +void UploadDataStream::FinalizeInitialization() { + DCHECK(!initialized_successfully_); + if (!is_chunked()) { + uint64 total_size = 0; + for (size_t i = 0; i < element_readers_.size(); ++i) { + UploadElementReader* reader = element_readers_[i]; + total_size += reader->GetContentLength(); + } + total_size_ = total_size; + } + initialized_successfully_ = true; +} + } // namespace net diff --git a/net/base/upload_data_stream.h b/net/base/upload_data_stream.h index 73c9359..8679b2c 100644 --- a/net/base/upload_data_stream.h +++ b/net/base/upload_data_stream.h @@ -5,8 +5,11 @@ #ifndef NET_BASE_UPLOAD_DATA_STREAM_H_ #define NET_BASE_UPLOAD_DATA_STREAM_H_ +#include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_vector.h" +#include "base/memory/weak_ptr.h" +#include "net/base/completion_callback.h" #include "net/base/net_export.h" #include "net/base/upload_data.h" @@ -24,10 +27,17 @@ class NET_EXPORT UploadDataStream { // before calling any other method. It is not valid to call any method // (other than the destructor) if Init() returns a failure. // + // Does the initialization synchronously and returns the result if possible, + // otherwise returns ERR_IO_PENDING and runs the callback with the result. + // // Returns OK on success. Returns ERR_UPLOAD_FILE_CHANGED if the expected // file modification time is set (usually not set, but set for sliced // files) and the target file is changed. - int Init(); + int Init(const CompletionCallback& callback); + + // Initializes the stream synchronously. + // Use this method only in tests and Chrome Frame. + int InitSync(); // Reads up to |buf_len| bytes from the upload data stream to |buf|. The // number of bytes read is returned. Partial reads are allowed. Zero is @@ -71,6 +81,22 @@ class NET_EXPORT UploadDataStream { friend class SpdyNetworkTransactionSpdy2Test; friend class SpdyNetworkTransactionSpdy3Test; + // TODO(hashimoto): Stop directly accsssing element_readers_ from tests and + // remove these friend declarations. + FRIEND_TEST_ALL_PREFIXES(UploadDataStreamTest, InitAsync); + FRIEND_TEST_ALL_PREFIXES(UploadDataStreamTest, InitAsyncFailureAsync); + FRIEND_TEST_ALL_PREFIXES(UploadDataStreamTest, InitAsyncFailureSync); + + // Runs Init() for all element readers. + // This method is used to implement Init(). + void InitInternal(int start_index, + const CompletionCallback& callback, + int previous_result); + + // Finalizes the initialization process. + // This method is used to implement Init(). + void FinalizeInitialization(); + // These methods are provided only to be used by unit tests. static void ResetMergeChunks(); static void set_merge_chunks(bool merge) { merge_chunks_ = merge; } @@ -90,6 +116,8 @@ class NET_EXPORT UploadDataStream { // True if the initialization was successful. bool initialized_successfully_; + base::WeakPtrFactory<UploadDataStream> weak_ptr_factory_; + // 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 8a8ec39..33d035e4 100644 --- a/net/base/upload_data_stream_unittest.cc +++ b/net/base/upload_data_stream_unittest.cc @@ -17,9 +17,15 @@ #include "net/base/net_errors.h" #include "net/base/upload_data.h" #include "net/base/upload_file_element_reader.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::_; + namespace net { namespace { @@ -39,6 +45,46 @@ std::string ReadFromUploadDataStream(UploadDataStream* stream) { return data_read; } +// A mock class of UploadElementReader. +class MockUploadElementReader : public UploadElementReader { + public: + MockUploadElementReader() : init_result_(OK) {} + virtual ~MockUploadElementReader() {} + + MOCK_METHOD1(Init, int(const CompletionCallback& callback)); + MOCK_CONST_METHOD0(GetContentLength, uint64()); + MOCK_CONST_METHOD0(BytesRemaining, uint64()); + MOCK_CONST_METHOD0(IsInMemory, bool()); + MOCK_METHOD2(ReadSync, int(char* buf, int buf_length)); + + // Sets expectation to return the specified result from Init() asynchronously. + void SetAsyncInitExpectation(int result) { + init_result_ = result; + EXPECT_CALL(*this, Init(_)) + .WillOnce(DoAll(Invoke(this, &MockUploadElementReader::OnInit), + Return(ERR_IO_PENDING))); + } + + private: + void OnInit(const CompletionCallback& callback) { + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(callback, init_result_)); + } + + // Result value returned from Init(). + int init_result_; +}; + +// A mock CompletionCallback. +class MockCompletionCallback { + public: + MOCK_METHOD1(Run, void(int result)); + + CompletionCallback CreateCallback() { + return base::Bind(&MockCompletionCallback::Run, base::Unretained(this)); + } +}; + } // namespace class UploadDataStreamTest : public PlatformTest { @@ -55,7 +101,7 @@ class UploadDataStreamTest : public PlatformTest { TEST_F(UploadDataStreamTest, EmptyUploadData) { upload_data_->AppendBytes("", 0); scoped_ptr<UploadDataStream> stream(new UploadDataStream(upload_data_)); - ASSERT_EQ(OK, stream->Init()); + ASSERT_EQ(OK, stream->InitSync()); EXPECT_TRUE(stream->IsInMemory()); ASSERT_TRUE(stream.get()); EXPECT_EQ(0U, stream->size()); @@ -66,7 +112,7 @@ TEST_F(UploadDataStreamTest, EmptyUploadData) { TEST_F(UploadDataStreamTest, ConsumeAllBytes) { upload_data_->AppendBytes(kTestData, kTestDataSize); scoped_ptr<UploadDataStream> stream(new UploadDataStream(upload_data_)); - ASSERT_EQ(OK, stream->Init()); + ASSERT_EQ(OK, stream->InitSync()); EXPECT_TRUE(stream->IsInMemory()); ASSERT_TRUE(stream.get()); EXPECT_EQ(kTestDataSize, stream->size()); @@ -94,7 +140,7 @@ TEST_F(UploadDataStreamTest, File) { upload_data_->SetElements(elements); scoped_ptr<UploadDataStream> stream(new UploadDataStream(upload_data_)); - ASSERT_EQ(OK, stream->Init()); + ASSERT_EQ(OK, stream->InitSync()); EXPECT_FALSE(stream->IsInMemory()); ASSERT_TRUE(stream.get()); EXPECT_EQ(kTestDataSize, stream->size()); @@ -127,7 +173,7 @@ TEST_F(UploadDataStreamTest, FileSmallerThanLength) { upload_data_->SetElements(elements); scoped_ptr<UploadDataStream> stream(new UploadDataStream(upload_data_)); - ASSERT_EQ(OK, stream->Init()); + ASSERT_EQ(OK, stream->InitSync()); EXPECT_FALSE(stream->IsInMemory()); ASSERT_TRUE(stream.get()); EXPECT_EQ(kFakeSize, stream->size()); @@ -164,7 +210,7 @@ TEST_F(UploadDataStreamTest, FileAndBytes) { const uint64 kStreamSize = kTestDataSize + kFileRangeLength; scoped_ptr<UploadDataStream> stream(new UploadDataStream(upload_data_)); - ASSERT_EQ(OK, stream->Init()); + ASSERT_EQ(OK, stream->InitSync()); EXPECT_FALSE(stream->IsInMemory()); ASSERT_TRUE(stream.get()); EXPECT_EQ(kStreamSize, stream->size()); @@ -188,7 +234,7 @@ TEST_F(UploadDataStreamTest, Chunk) { const uint64 kStreamSize = kTestDataSize*2; scoped_ptr<UploadDataStream> stream(new UploadDataStream(upload_data_)); - ASSERT_EQ(OK, stream->Init()); + ASSERT_EQ(OK, stream->InitSync()); EXPECT_FALSE(stream->IsInMemory()); ASSERT_TRUE(stream.get()); EXPECT_EQ(0U, stream->size()); // Content-Length is 0 for chunked data. @@ -203,6 +249,96 @@ TEST_F(UploadDataStreamTest, Chunk) { ASSERT_TRUE(stream->IsEOF()); } +// Init() with on-memory and not-on-memory readers. +TEST_F(UploadDataStreamTest, InitAsync) { + // Create stream without element readers. + UploadDataStream stream(upload_data_); + + // Set mock readers to the stream. + MockUploadElementReader* reader = NULL; + + reader = new MockUploadElementReader; + EXPECT_CALL(*reader, Init(_)).WillOnce(Return(OK)); + EXPECT_CALL(*reader, GetContentLength()).WillRepeatedly(Return(0)); + EXPECT_CALL(*reader, IsInMemory()).WillRepeatedly(Return(true)); + stream.element_readers_.push_back(reader); + + reader = new MockUploadElementReader; + EXPECT_CALL(*reader, Init(_)).WillOnce(Return(OK)); + EXPECT_CALL(*reader, GetContentLength()).WillRepeatedly(Return(0)); + EXPECT_CALL(*reader, IsInMemory()).WillRepeatedly(Return(true)); + stream.element_readers_.push_back(reader); + + reader = new MockUploadElementReader; + reader->SetAsyncInitExpectation(OK); + EXPECT_CALL(*reader, GetContentLength()).WillRepeatedly(Return(0)); + EXPECT_CALL(*reader, IsInMemory()).WillRepeatedly(Return(false)); + stream.element_readers_.push_back(reader); + + reader = new MockUploadElementReader; + reader->SetAsyncInitExpectation(OK); + EXPECT_CALL(*reader, GetContentLength()).WillRepeatedly(Return(0)); + EXPECT_CALL(*reader, IsInMemory()).WillRepeatedly(Return(false)); + stream.element_readers_.push_back(reader); + + reader = new MockUploadElementReader; + EXPECT_CALL(*reader, Init(_)).WillOnce(Return(OK)); + EXPECT_CALL(*reader, GetContentLength()).WillRepeatedly(Return(0)); + EXPECT_CALL(*reader, IsInMemory()).WillRepeatedly(Return(true)); + stream.element_readers_.push_back(reader); + + // Run Init(). + MockCompletionCallback mock_callback; + EXPECT_CALL(mock_callback, Run(OK)).Times(1); + EXPECT_EQ(stream.Init(mock_callback.CreateCallback()), ERR_IO_PENDING); + MessageLoop::current()->RunAllPending(); +} + +// Init() of a reader fails asynchronously. +TEST_F(UploadDataStreamTest, InitAsyncFailureAsync) { + // Create stream without element readers. + UploadDataStream stream(upload_data_); + + // Set a mock reader to the stream. + MockUploadElementReader* reader = NULL; + + reader = new MockUploadElementReader; + reader->SetAsyncInitExpectation(ERR_FAILED); + EXPECT_CALL(*reader, IsInMemory()).WillRepeatedly(Return(false)); + stream.element_readers_.push_back(reader); + + // Run Init(). + MockCompletionCallback mock_callback; + EXPECT_CALL(mock_callback, Run(ERR_FAILED)).Times(1); + EXPECT_EQ(stream.Init(mock_callback.CreateCallback()), ERR_IO_PENDING); + MessageLoop::current()->RunAllPending(); +} + +// Init() of a reader fails synchronously. +TEST_F(UploadDataStreamTest, InitAsyncFailureSync) { + // Create stream without element readers. + UploadDataStream stream(upload_data_); + + // Set mock readers to the stream. + MockUploadElementReader* reader = NULL; + + reader = new MockUploadElementReader; + reader->SetAsyncInitExpectation(OK); + EXPECT_CALL(*reader, IsInMemory()).WillRepeatedly(Return(false)); + stream.element_readers_.push_back(reader); + + reader = new MockUploadElementReader; + EXPECT_CALL(*reader, Init(_)).WillOnce(Return(ERR_FAILED)); + EXPECT_CALL(*reader, IsInMemory()).WillRepeatedly(Return(true)); + stream.element_readers_.push_back(reader); + + // Run Init(). + MockCompletionCallback mock_callback; + EXPECT_CALL(mock_callback, Run(ERR_FAILED)).Times(1); + EXPECT_EQ(stream.Init(mock_callback.CreateCallback()), ERR_IO_PENDING); + MessageLoop::current()->RunAllPending(); +} + void UploadDataStreamTest::FileChangedHelper(const FilePath& file_path, const base::Time& time, bool error_expected) { @@ -216,7 +352,7 @@ void UploadDataStreamTest::FileChangedHelper(const FilePath& file_path, upload_data->SetElements(elements); scoped_ptr<UploadDataStream> stream(new UploadDataStream(upload_data)); - int error_code = stream->Init(); + int error_code = stream->InitSync(); if (error_expected) ASSERT_EQ(ERR_UPLOAD_FILE_CHANGED, error_code); else @@ -259,7 +395,7 @@ TEST_F(UploadDataStreamTest, UploadDataReused) { // Confirm that the file is read properly. { UploadDataStream stream(upload_data_); - ASSERT_EQ(OK, stream.Init()); + ASSERT_EQ(OK, stream.InitSync()); EXPECT_EQ(kTestDataSize, stream.size()); EXPECT_EQ(kTestData, ReadFromUploadDataStream(&stream)); } @@ -268,7 +404,7 @@ TEST_F(UploadDataStreamTest, UploadDataReused) { // file is read properly. { UploadDataStream stream(upload_data_); - ASSERT_EQ(OK, stream.Init()); + ASSERT_EQ(OK, stream.InitSync()); EXPECT_EQ(kTestDataSize, stream.size()); EXPECT_EQ(kTestData, ReadFromUploadDataStream(&stream)); } diff --git a/net/base/upload_element_reader.cc b/net/base/upload_element_reader.cc index bb5cd85..a7f25f6 100644 --- a/net/base/upload_element_reader.cc +++ b/net/base/upload_element_reader.cc @@ -5,6 +5,7 @@ #include "net/base/upload_element_reader.h" #include "base/logging.h" +#include "net/base/net_errors.h" #include "net/base/upload_bytes_element_reader.h" #include "net/base/upload_element.h" #include "net/base/upload_file_element_reader.h" @@ -31,6 +32,11 @@ UploadElementReader* UploadElementReader::Create(const UploadElement& element) { return reader; } +int UploadElementReader::InitSync() { + NOTREACHED() << "This instance does not support InitSync()."; + return ERR_NOT_IMPLEMENTED; +} + bool UploadElementReader::IsInMemory() const { return false; } diff --git a/net/base/upload_element_reader.h b/net/base/upload_element_reader.h index 3e8bcea..acb1eca 100644 --- a/net/base/upload_element_reader.h +++ b/net/base/upload_element_reader.h @@ -6,6 +6,7 @@ #define NET_BASE_UPLOAD_ELEMENT_READER_H_ #include "base/basictypes.h" +#include "net/base/completion_callback.h" #include "net/base/net_export.h" namespace net { @@ -21,8 +22,13 @@ class NET_EXPORT UploadElementReader { // Creates an appropriate UploadElementReader instance for the given element. static UploadElementReader* Create(const UploadElement& element); - // Initializes the instance synchronously. - virtual int InitSync() = 0; + // Initializes the instance synchronously when possible, otherwise does + // initialization aynschronously, returns ERR_IO_PENDING and runs callback. + virtual int Init(const CompletionCallback& callback) = 0; + + // Initializes the instance always synchronously. + // Use this method only in tests and Chrome Frame. + virtual int InitSync(); // Returns the byte-length of the element. For files that do not exist, 0 // is returned. This is done for consistency with Mozilla. diff --git a/net/base/upload_file_element_reader.cc b/net/base/upload_file_element_reader.cc index b71c022..86be79d 100644 --- a/net/base/upload_file_element_reader.cc +++ b/net/base/upload_file_element_reader.cc @@ -4,8 +4,11 @@ #include "net/base/upload_file_element_reader.h" +#include "base/bind.h" #include "base/file_util.h" +#include "base/location.h" #include "base/threading/thread_restrictions.h" +#include "base/threading/worker_pool.h" #include "net/base/file_stream.h" #include "net/base/net_errors.h" @@ -17,6 +20,58 @@ namespace { // UploadFileElementReader::GetContentLength() when set to non-zero. uint64 overriding_content_length = 0; +// This method is used to implement Init(). +void InitInternal(const FilePath& path, + uint64 range_offset, + uint64 range_length, + const base::Time& expected_modification_time, + scoped_ptr<FileStream>* out_file_stream, + uint64* out_content_length, + int* out_result) { + 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->CloseSync(); + 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->swap(file_stream); + + // 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()) { + *out_result = ERR_UPLOAD_FILE_CHANGED; + return; + } + } + + *out_result = OK; +} + } // namespace UploadFileElementReader::UploadFileElementReader( @@ -29,7 +84,8 @@ UploadFileElementReader::UploadFileElementReader( range_length_(range_length), expected_modification_time_(expected_modification_time), content_length_(0), - bytes_remaining_(0) { + bytes_remaining_(0), + weak_ptr_factory_(this) { } UploadFileElementReader::~UploadFileElementReader() { @@ -39,56 +95,42 @@ UploadFileElementReader::~UploadFileElementReader() { file_stream_->CloseSync(); } +int UploadFileElementReader::Init(const CompletionCallback& callback) { + scoped_ptr<FileStream>* file_stream = new scoped_ptr<FileStream>; + uint64* content_length = new uint64; + int* result = new int; + const bool posted = base::WorkerPool::PostTaskAndReply( + FROM_HERE, + base::Bind(&InitInternal, + path_, + range_offset_, + range_length_, + expected_modification_time_, + file_stream, + content_length, + result), + base::Bind(&UploadFileElementReader::OnInitCompleted, + weak_ptr_factory_.GetWeakPtr(), + base::Owned(file_stream), + base::Owned(content_length), + base::Owned(result), + callback), + true /* task_is_slow */); + DCHECK(posted); + return ERR_IO_PENDING; +} + int UploadFileElementReader::InitSync() { // Temporarily allow until fix: http://crbug.com/72001. base::ThreadRestrictions::ScopedAllowIO allow_io; - 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(); - } - if (file_stream.get() && 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->CloseSync(); - file_stream.reset(); - } - } - file_stream_.reset(file_stream.release()); - - 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_); - } - content_length_ = length; - bytes_remaining_ = GetContentLength(); - - // 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; + scoped_ptr<FileStream> file_stream; + uint64 content_length = 0; + int result = OK; + InitInternal(path_, range_offset_, range_length_, expected_modification_time_, + &file_stream, &content_length, &result); + OnInitCompleted(&file_stream, &content_length, &result, CompletionCallback()); + return result; } uint64 UploadFileElementReader::GetContentLength() const { @@ -129,6 +171,18 @@ int UploadFileElementReader::ReadSync(char* buf, int buf_length) { return num_bytes_to_read; } +void UploadFileElementReader::OnInitCompleted( + scoped_ptr<FileStream>* file_stream, + uint64* content_length, + int* result, + const CompletionCallback& callback) { + file_stream_.swap(*file_stream); + content_length_ = *content_length; + bytes_remaining_ = GetContentLength(); + if (!callback.is_null()) + callback.Run(*result); +} + UploadFileElementReader::ScopedOverridingContentLengthForTests:: ScopedOverridingContentLengthForTests(uint64 value) { overriding_content_length = value; diff --git a/net/base/upload_file_element_reader.h b/net/base/upload_file_element_reader.h index 34d8194..cc8e830 100644 --- a/net/base/upload_file_element_reader.h +++ b/net/base/upload_file_element_reader.h @@ -9,6 +9,7 @@ #include "base/file_path.h" #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/time.h" #include "net/base/upload_element_reader.h" @@ -26,12 +27,19 @@ class NET_EXPORT_PRIVATE UploadFileElementReader : public UploadElementReader { virtual ~UploadFileElementReader(); // UploadElementReader overrides: + virtual int Init(const CompletionCallback& callback) OVERRIDE; virtual int InitSync() OVERRIDE; virtual uint64 GetContentLength() const OVERRIDE; virtual uint64 BytesRemaining() const OVERRIDE; virtual int ReadSync(char* buf, int buf_length) OVERRIDE; private: + // This method is used to implement Init(). + void OnInitCompleted(scoped_ptr<FileStream>* file_stream, + uint64* content_length, + int* result, + const CompletionCallback& callback); + // Sets an value to override the result for GetContentLength(). // Used for tests. struct NET_EXPORT_PRIVATE ScopedOverridingContentLengthForTests { @@ -46,6 +54,7 @@ class NET_EXPORT_PRIVATE UploadFileElementReader : public UploadElementReader { scoped_ptr<FileStream> file_stream_; uint64 content_length_; uint64 bytes_remaining_; + base::WeakPtrFactory<UploadFileElementReader> weak_ptr_factory_; FRIEND_TEST_ALL_PREFIXES(UploadDataStreamTest, FileSmallerThanLength); FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionTest, diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 844fc34b..f987806 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -747,7 +747,7 @@ int HttpNetworkTransaction::DoBuildRequest() { request_body_.reset(NULL); if (request_->upload_data) { request_body_.reset(new UploadDataStream(request_->upload_data)); - const int error_code = request_body_->Init(); + const int error_code = request_body_->InitSync(); if (error_code != OK) { request_body_.reset(NULL); return error_code; diff --git a/net/http/http_stream_parser_unittest.cc b/net/http/http_stream_parser_unittest.cc index b6cb778..9ef3cc7 100644 --- a/net/http/http_stream_parser_unittest.cc +++ b/net/http/http_stream_parser_unittest.cc @@ -97,7 +97,7 @@ TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_NoBody) { TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_EmptyBody) { scoped_refptr<UploadData> upload_data = new UploadData; scoped_ptr<UploadDataStream> body(new UploadDataStream(upload_data)); - ASSERT_EQ(OK, body->Init()); + ASSERT_EQ(OK, body->InitSync()); // Shouldn't be merged if upload data is empty. ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( "some header", body.get())); @@ -110,7 +110,7 @@ TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_ChunkedBody) { upload_data->AppendChunk(payload.data(), payload.size(), true); scoped_ptr<UploadDataStream> body(new UploadDataStream(upload_data)); - ASSERT_EQ(OK, body->Init()); + ASSERT_EQ(OK, body->InitSync()); // Shouldn't be merged if upload data carries chunked data. ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( "some header", body.get())); @@ -129,7 +129,7 @@ TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_FileBody) { upload_data->AppendFileRange(temp_file_path, 0, 0, base::Time()); scoped_ptr<UploadDataStream> body(new UploadDataStream(upload_data)); - ASSERT_EQ(OK, body->Init()); + ASSERT_EQ(OK, body->InitSync()); // Shouldn't be merged if upload data carries a file, as it's not in-memory. ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( "some header", body.get())); @@ -141,7 +141,7 @@ TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_SmallBodyInMemory) { upload_data->AppendBytes(payload.data(), payload.size()); scoped_ptr<UploadDataStream> body(new UploadDataStream(upload_data)); - ASSERT_EQ(OK, body->Init()); + ASSERT_EQ(OK, body->InitSync()); // Yes, should be merged if the in-memory body is small here. ASSERT_TRUE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( "some header", body.get())); @@ -153,7 +153,7 @@ TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_LargeBodyInMemory) { upload_data->AppendBytes(payload.data(), payload.size()); scoped_ptr<UploadDataStream> body(new UploadDataStream(upload_data)); - ASSERT_EQ(OK, body->Init()); + ASSERT_EQ(OK, body->InitSync()); // Shouldn't be merged if the in-memory body is large here. ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( "some header", body.get())); @@ -226,7 +226,7 @@ TEST(HttpStreamParser, AsyncChunkAndAsyncSocket) { scoped_ptr<UploadDataStream> upload_stream( new UploadDataStream(upload_data)); - ASSERT_EQ(OK, upload_stream->Init()); + ASSERT_EQ(OK, upload_stream->InitSync()); HttpRequestHeaders request_headers; request_headers.SetHeader("Host", "localhost"); diff --git a/net/spdy/spdy_http_stream_spdy2_unittest.cc b/net/spdy/spdy_http_stream_spdy2_unittest.cc index 58461ab..aec278a 100644 --- a/net/spdy/spdy_http_stream_spdy2_unittest.cc +++ b/net/spdy/spdy_http_stream_spdy2_unittest.cc @@ -164,7 +164,7 @@ TEST_F(SpdyHttpStreamSpdy2Test, SendChunkedPost) { scoped_ptr<UploadDataStream> upload_stream( new UploadDataStream(request.upload_data)); - ASSERT_EQ(OK, upload_stream->Init()); + ASSERT_EQ(OK, upload_stream->InitSync()); EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest( headers, upload_stream.Pass(), &response, callback.callback())); EXPECT_TRUE(http_session_->spdy_session_pool()->HasSession(pair)); @@ -259,7 +259,7 @@ TEST_F(SpdyHttpStreamSpdy2Test, DelayedSendChunkedPost) { scoped_ptr<UploadDataStream> upload_stream( new UploadDataStream(request.upload_data)); - ASSERT_EQ(OK, upload_stream->Init()); + ASSERT_EQ(OK, upload_stream->InitSync()); request.upload_data->AppendChunk(kUploadData, kUploadDataSize, false); diff --git a/net/spdy/spdy_http_stream_spdy3_unittest.cc b/net/spdy/spdy_http_stream_spdy3_unittest.cc index 91acad0..bcd02d3 100644 --- a/net/spdy/spdy_http_stream_spdy3_unittest.cc +++ b/net/spdy/spdy_http_stream_spdy3_unittest.cc @@ -173,7 +173,7 @@ TEST_F(SpdyHttpStreamSpdy3Test, SendChunkedPost) { scoped_ptr<UploadDataStream> upload_stream( new UploadDataStream(request.upload_data)); - ASSERT_EQ(OK, upload_stream->Init()); + ASSERT_EQ(OK, upload_stream->InitSync()); EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest( headers, upload_stream.Pass(), &response, callback.callback())); EXPECT_TRUE(http_session_->spdy_session_pool()->HasSession(pair)); @@ -268,7 +268,7 @@ TEST_F(SpdyHttpStreamSpdy3Test, DelayedSendChunkedPost) { scoped_ptr<UploadDataStream> upload_stream( new UploadDataStream(request.upload_data)); - ASSERT_EQ(OK, upload_stream->Init()); + ASSERT_EQ(OK, upload_stream->InitSync()); request.upload_data->AppendChunk(kUploadData, kUploadDataSize, false); @@ -404,7 +404,7 @@ TEST_F(SpdyHttpStreamSpdy3Test, DelayedSendChunkedPostWithWindowUpdate) { scoped_ptr<UploadDataStream> upload_stream( new UploadDataStream(request.upload_data)); - ASSERT_EQ(OK, upload_stream->Init()); + ASSERT_EQ(OK, upload_stream->InitSync()); request.upload_data->AppendChunk(kUploadData, kUploadDataSize, true); diff --git a/net/spdy/spdy_network_transaction_spdy2_unittest.cc b/net/spdy/spdy_network_transaction_spdy2_unittest.cc index b3afbf9..8710a38 100644 --- a/net/spdy/spdy_network_transaction_spdy2_unittest.cc +++ b/net/spdy/spdy_network_transaction_spdy2_unittest.cc @@ -1655,7 +1655,7 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, EmptyPost) { const uint64 kContentLength = 0; scoped_ptr<UploadDataStream> stream( new UploadDataStream(request.upload_data)); - ASSERT_EQ(OK, stream->Init()); + ASSERT_EQ(OK, stream->InitSync()); ASSERT_EQ(kContentLength, stream->size()); scoped_ptr<SpdyFrame> req(ConstructSpdyPost(kContentLength, NULL, 0)); @@ -1700,7 +1700,7 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, PostWithEarlySynReply) { const uint64 kContentLength = sizeof(upload); scoped_ptr<UploadDataStream> stream( new UploadDataStream(request.upload_data)); - ASSERT_EQ(OK, stream->Init()); + ASSERT_EQ(OK, stream->InitSync()); ASSERT_EQ(kContentLength, stream->size()); scoped_ptr<SpdyFrame> stream_reply(ConstructSpdyPostSynReply(NULL, 0)); scoped_ptr<SpdyFrame> stream_body(ConstructSpdyBodyFrame(1, true)); diff --git a/net/spdy/spdy_network_transaction_spdy3_unittest.cc b/net/spdy/spdy_network_transaction_spdy3_unittest.cc index a348213..a43ceb4 100644 --- a/net/spdy/spdy_network_transaction_spdy3_unittest.cc +++ b/net/spdy/spdy_network_transaction_spdy3_unittest.cc @@ -1661,7 +1661,7 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, EmptyPost) { const uint64 kContentLength = 0; scoped_ptr<UploadDataStream> stream( new UploadDataStream(request.upload_data)); - ASSERT_EQ(OK, stream->Init()); + ASSERT_EQ(OK, stream->InitSync()); ASSERT_EQ(kContentLength, stream->size()); scoped_ptr<SpdyFrame> req(ConstructSpdyPost(kContentLength, NULL, 0)); @@ -1706,7 +1706,7 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, PostWithEarlySynReply) { const uint64 kContentLength = sizeof(upload); scoped_ptr<UploadDataStream> stream( new UploadDataStream(request.upload_data)); - ASSERT_EQ(OK, stream->Init()); + ASSERT_EQ(OK, stream->InitSync()); ASSERT_EQ(kContentLength, stream->size()); scoped_ptr<SpdyFrame> stream_reply(ConstructSpdyPostSynReply(NULL, 0)); scoped_ptr<SpdyFrame> stream_body(ConstructSpdyBodyFrame(1, true)); |