diff options
author | satorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-10 07:39:11 +0000 |
---|---|---|
committer | satorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-10 07:39:11 +0000 |
commit | 1dce708bbe7fdef15436e20f36f805f53bfdcb73 (patch) | |
tree | 3b7852c44e541e6d755fb1044c857d37bc622252 /net | |
parent | 9caded3f4f06300155f117134f59b5d85b98119f (diff) | |
download | chromium_src-1dce708bbe7fdef15436e20f36f805f53bfdcb73.zip chromium_src-1dce708bbe7fdef15436e20f36f805f53bfdcb73.tar.gz chromium_src-1dce708bbe7fdef15436e20f36f805f53bfdcb73.tar.bz2 |
net: Make UploadData::GetContentLength() asynchronous.
However, the asynchronous version is not used yet.
The synchronous version is kept as GetContentLengthSync().
The existing code is changed to use the synchronous version.
TEST=net_unittests
BUG=72001,112607
Review URL: https://chromiumcodereview.appspot.com/9321003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@121411 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/upload_data.cc | 60 | ||||
-rw-r--r-- | net/base/upload_data.h | 19 | ||||
-rw-r--r-- | net/base/upload_data_stream.cc | 7 | ||||
-rw-r--r-- | net/base/upload_data_stream_unittest.cc | 4 | ||||
-rw-r--r-- | net/base/upload_data_unittest.cc | 173 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 2 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 13 |
7 files changed, 219 insertions, 59 deletions
diff --git a/net/base/upload_data.cc b/net/base/upload_data.cc index bffe13b..d445307 100644 --- a/net/base/upload_data.cc +++ b/net/base/upload_data.cc @@ -6,15 +6,29 @@ #include <algorithm> +#include "base/bind.h" #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" #include "base/threading/thread_restrictions.h" +#include "base/threading/worker_pool.h" +#include "base/tracked_objects.h" #include "net/base/file_stream.h" #include "net/base/net_errors.h" namespace net { +namespace { + +// Helper function for GetContentLength(). +void OnGetContentLengthComplete( + const UploadData::ContentLengthCallback& callback, + uint64* content_length) { + callback.Run(*content_length); +} + +} // namespace + UploadData::Element::Element() : type_(TYPE_BYTES), file_range_offset_(0), @@ -73,15 +87,8 @@ uint64 UploadData::Element::GetContentLength() { return 0; int64 length = 0; - - { - // TODO(tzik): - // file_util::GetFileSize may cause blocking IO. - // Temporary allow until fix: http://crbug.com/72001. - base::ThreadRestrictions::ScopedAllowIO allow_io; - if (!file_util::GetFileSize(file_path_, &length)) + if (!file_util::GetFileSize(file_path_, &length)) return 0; - } if (file_range_offset_ >= static_cast<uint64>(length)) return 0; // range is beyond eof @@ -101,11 +108,6 @@ FileStream* UploadData::Element::NewFileStreamForReading() { return file; } - // TODO(tzik): - // FileStream::Open and FileStream::Seek may cause blocking IO. - // Temporary allow until fix: http://crbug.com/72001. - base::ThreadRestrictions::ScopedAllowIO allow_io; - scoped_ptr<FileStream> file(new FileStream(NULL)); int64 rv = file->OpenSync( file_path_, @@ -172,15 +174,21 @@ void UploadData::set_chunk_callback(ChunkCallback* callback) { chunk_callback_ = callback; } -uint64 UploadData::GetContentLength() { - if (is_chunked_) - return 0; +void UploadData::GetContentLength(const ContentLengthCallback& callback) { + uint64* result = new uint64(0); + const bool task_is_slow = true; + const bool posted = base::WorkerPool::PostTaskAndReply( + FROM_HERE, + base::Bind(&UploadData::DoGetContentLength, this, result), + base::Bind(&OnGetContentLengthComplete, callback, base::Owned(result)), + task_is_slow); + DCHECK(posted); +} - uint64 len = 0; - std::vector<Element>::iterator it = elements_.begin(); - for (; it != elements_.end(); ++it) - len += (*it).GetContentLength(); - return len; +uint64 UploadData::GetContentLengthSync() { + uint64 content_length = 0; + DoGetContentLength(&content_length); + return content_length; } bool UploadData::IsInMemory() const { @@ -203,6 +211,16 @@ void UploadData::SetElements(const std::vector<Element>& elements) { elements_ = elements; } +void UploadData::DoGetContentLength(uint64* content_length) { + *content_length = 0; + + if (is_chunked_) + return; + + for (size_t i = 0; i < elements_.size(); ++i) + *content_length += elements_[i].GetContentLength(); +} + UploadData::~UploadData() { } diff --git a/net/base/upload_data.h b/net/base/upload_data.h index 31c1958..d42ffad 100644 --- a/net/base/upload_data.h +++ b/net/base/upload_data.h @@ -9,6 +9,7 @@ #include <vector> #include "base/basictypes.h" +#include "base/callback_forward.h" #include "base/file_path.h" #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" @@ -107,7 +108,6 @@ class NET_EXPORT UploadData : public base::RefCounted<UploadData> { // Returns the byte-length of the element. For files that do not exist, 0 // is returned. This is done for consistency with Mozilla. - // Once called, this function will always return the same value. uint64 GetContentLength(); // Returns a FileStream opened for reading for this element, positioned at @@ -163,8 +163,18 @@ class NET_EXPORT UploadData : public base::RefCounted<UploadData> { void set_is_chunked(bool set) { is_chunked_ = set; } bool is_chunked() const { return is_chunked_; } - // Returns the total size in bytes of the data to upload. - uint64 GetContentLength(); + // Gets the total size in bytes of the data to upload. Computing the + // content length can result in performing file IO hence the operation is + // done asynchronously. Runs the callback with the content length once the + // computation is done. + typedef base::Callback<void(uint64 content_length)> ContentLengthCallback; + void GetContentLength(const ContentLengthCallback& callback); + + // Returns the total size in bytes of the data to upload, for testing. + // This version may perform file IO on the current thread. This function + // will fail if called on a thread where file IO is prohibited. Usually + // used for testing, but Chrome Frame also uses this version. + uint64 GetContentLengthSync(); // Returns true if the upload data is entirely in memory (i.e. the // upload data is not chunked, and all elemnts are of TYPE_BYTES). @@ -187,6 +197,9 @@ class NET_EXPORT UploadData : public base::RefCounted<UploadData> { int64 identifier() const { return identifier_; } private: + // Helper function for GetContentLength(). + void DoGetContentLength(uint64* content_length); + friend class base::RefCounted<UploadData>; ~UploadData(); diff --git a/net/base/upload_data_stream.cc b/net/base/upload_data_stream.cc index cee54c7..5b2e5d3 100644 --- a/net/base/upload_data_stream.cc +++ b/net/base/upload_data_stream.cc @@ -31,7 +31,10 @@ UploadDataStream::~UploadDataStream() { int UploadDataStream::Init() { DCHECK(!initialized_successfully_); - total_size_ = upload_data_->GetContentLength(); + { + base::ThreadRestrictions::ScopedAllowIO allow_io; + total_size_ = upload_data_->GetContentLengthSync(); + } // If the underlying file has been changed and the expected file // modification time is set, treat it as error. Note that the expected @@ -98,6 +101,8 @@ int UploadDataStream::Read(IOBuffer* buf, int buf_len) { // Open the file of the current element if not yet opened. if (!element_file_stream_.get()) { element_file_bytes_remaining_ = element.GetContentLength(); + // Temporarily allow until fix: http://crbug.com/72001. + base::ThreadRestrictions::ScopedAllowIO allow_io; element_file_stream_.reset(element.NewFileStreamForReading()); } diff --git a/net/base/upload_data_stream_unittest.cc b/net/base/upload_data_stream_unittest.cc index 94ebea5..078f200 100644 --- a/net/base/upload_data_stream_unittest.cc +++ b/net/base/upload_data_stream_unittest.cc @@ -7,9 +7,11 @@ #include <vector> #include "base/basictypes.h" +#include "base/bind.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" #include "base/time.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" @@ -78,7 +80,7 @@ TEST_F(UploadDataStreamTest, FileSmallerThanLength) { element.SetContentLength(kFakeSize); elements.push_back(element); upload_data_->SetElements(elements); - EXPECT_EQ(kFakeSize, upload_data_->GetContentLength()); + EXPECT_EQ(kFakeSize, upload_data_->GetContentLengthSync()); scoped_ptr<UploadDataStream> stream(new UploadDataStream(upload_data_)); ASSERT_EQ(OK, stream->Init()); diff --git a/net/base/upload_data_unittest.cc b/net/base/upload_data_unittest.cc index 5d56523..63636a2 100644 --- a/net/base/upload_data_unittest.cc +++ b/net/base/upload_data_unittest.cc @@ -4,58 +4,177 @@ #include "net/base/upload_data.h" +#include "base/bind.h" #include "base/file_path.h" +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/scoped_temp_dir.h" #include "base/memory/ref_counted.h" +#include "base/threading/thread_restrictions.h" #include "base/time.h" #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" namespace net { -TEST(UploadDataTest, IsInMemory_Empty) { - scoped_refptr<UploadData> upload_data = new UploadData; - ASSERT_TRUE(upload_data->IsInMemory()); +// Simplified version of TestCompletionCallback for ContentLengthCallback, +// that handles uint64 rather than int. +class TestContentLengthCallback { + public: + TestContentLengthCallback() + : result_(0), + callback_(ALLOW_THIS_IN_INITIALIZER_LIST( + base::Bind(&TestContentLengthCallback::SetResult, + base::Unretained(this)))) { + } + + ~TestContentLengthCallback() {} + + const UploadData::ContentLengthCallback& callback() const { + return callback_; + } + + // Waits for the result and returns it. + uint64 WaitForResult() { + MessageLoop::current()->Run(); + return result_; + } + + private: + // Sets the result and stops the message loop. + void SetResult(uint64 result) { + result_ = result; + MessageLoop::current()->Quit(); + } + + uint64 result_; + const UploadData::ContentLengthCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(TestContentLengthCallback); +}; + +class UploadDataTest : public PlatformTest { + protected: + virtual void SetUp() { + upload_data_= new UploadData; + // To ensure that file IO is not performed on the main thread in the + // test (i.e. GetContentLengthSync() will fail if file IO is performed). + base::ThreadRestrictions::SetIOAllowed(false); + } + + virtual void TearDown() { + base::ThreadRestrictions::SetIOAllowed(true); + } + + // Creates a temporary file with the given data. The temporary file is + // deleted by temp_dir_. Returns true on success. + bool CreateTemporaryFile(const std::string& data, + FilePath* temp_file_path) { + base::ThreadRestrictions::ScopedAllowIO allow_io; + if (!temp_dir_.CreateUniqueTempDir()) + return false; + if (!file_util::CreateTemporaryFileInDir(temp_dir_.path(), temp_file_path)) + return false; + if (static_cast<int>(data.size()) != + file_util::WriteFile(*temp_file_path, data.data(), data.size())) + return false; + + return true; + } + + ScopedTempDir temp_dir_; + scoped_refptr<UploadData> upload_data_; +}; + +TEST_F(UploadDataTest, IsInMemory_Empty) { + ASSERT_TRUE(upload_data_->IsInMemory()); } -TEST(UploadDataTest, IsInMemory_Bytes) { - scoped_refptr<UploadData> upload_data = new UploadData; - upload_data->AppendBytes("123", 3); - ASSERT_TRUE(upload_data->IsInMemory()); +TEST_F(UploadDataTest, IsInMemory_Bytes) { + upload_data_->AppendBytes("123", 3); + ASSERT_TRUE(upload_data_->IsInMemory()); } -TEST(UploadDataTest, IsInMemory_File) { - scoped_refptr<UploadData> upload_data = new UploadData; - upload_data->AppendFileRange( +TEST_F(UploadDataTest, IsInMemory_File) { + upload_data_->AppendFileRange( FilePath(FILE_PATH_LITERAL("random_file_name.txt")), 0, 0, base::Time()); - ASSERT_FALSE(upload_data->IsInMemory()); + ASSERT_FALSE(upload_data_->IsInMemory()); } -TEST(UploadDataTest, IsInMemory_Blob) { - scoped_refptr<UploadData> upload_data = new UploadData; - upload_data->AppendBlob(GURL("blog:internal:12345")); +TEST_F(UploadDataTest, IsInMemory_Blob) { + upload_data_->AppendBlob(GURL("blog:internal:12345")); // Until it's resolved, we don't know what blob contains. - ASSERT_FALSE(upload_data->IsInMemory()); + ASSERT_FALSE(upload_data_->IsInMemory()); } -TEST(UploadDataTest, IsInMemory_Chunk) { - scoped_refptr<UploadData> upload_data = new UploadData; - upload_data->set_is_chunked(true); - ASSERT_FALSE(upload_data->IsInMemory()); +TEST_F(UploadDataTest, IsInMemory_Chunk) { + upload_data_->set_is_chunked(true); + ASSERT_FALSE(upload_data_->IsInMemory()); } -TEST(UploadDataTest, IsInMemory_Mixed) { - scoped_refptr<UploadData> upload_data = new UploadData; - ASSERT_TRUE(upload_data->IsInMemory()); +TEST_F(UploadDataTest, IsInMemory_Mixed) { + ASSERT_TRUE(upload_data_->IsInMemory()); - upload_data->AppendBytes("123", 3); - upload_data->AppendBytes("abc", 3); - ASSERT_TRUE(upload_data->IsInMemory()); + upload_data_->AppendBytes("123", 3); + upload_data_->AppendBytes("abc", 3); + ASSERT_TRUE(upload_data_->IsInMemory()); - upload_data->AppendFileRange( + upload_data_->AppendFileRange( FilePath(FILE_PATH_LITERAL("random_file_name.txt")), 0, 0, base::Time()); - ASSERT_FALSE(upload_data->IsInMemory()); + ASSERT_FALSE(upload_data_->IsInMemory()); +} + +TEST_F(UploadDataTest, GetContentLength_Empty) { + ASSERT_EQ(0U, upload_data_->GetContentLengthSync()); +} + +TEST_F(UploadDataTest, GetContentLength_Bytes) { + upload_data_->AppendBytes("123", 3); + ASSERT_EQ(3U, upload_data_->GetContentLengthSync()); +} + +TEST_F(UploadDataTest, GetContentLength_File) { + // Create a temporary file with some data. + const std::string kData = "hello"; + FilePath temp_file_path; + ASSERT_TRUE(CreateTemporaryFile(kData, &temp_file_path)); + upload_data_->AppendFileRange(temp_file_path, 0, kuint64max, base::Time()); + + // The length is returned asynchronously. + TestContentLengthCallback callback; + upload_data_->GetContentLength(callback.callback()); + ASSERT_EQ(kData.size(), callback.WaitForResult()); +} + +TEST_F(UploadDataTest, GetContentLength_Blob) { + upload_data_->AppendBlob(GURL("blog:internal:12345")); + ASSERT_EQ(0U, upload_data_->GetContentLengthSync()); +} + +TEST_F(UploadDataTest, GetContentLength_Chunk) { + upload_data_->set_is_chunked(true); + ASSERT_EQ(0U, upload_data_->GetContentLengthSync()); +} + +TEST_F(UploadDataTest, GetContentLength_Mixed) { + upload_data_->AppendBytes("123", 3); + upload_data_->AppendBytes("abc", 3); + + const uint64 content_length = upload_data_->GetContentLengthSync(); + ASSERT_EQ(6U, content_length); + + // Append a file. + const std::string kData = "hello"; + FilePath temp_file_path; + ASSERT_TRUE(CreateTemporaryFile(kData, &temp_file_path)); + upload_data_->AppendFileRange(temp_file_path, 0, kuint64max, base::Time()); + + TestContentLengthCallback callback; + upload_data_->GetContentLength(callback.callback()); + ASSERT_EQ(content_length + kData.size(), callback.WaitForResult()); } } // namespace net diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 75b6a98..8457a1d 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -6184,7 +6184,7 @@ TEST_F(HttpNetworkTransactionTest, UploadFileSmallerThanLength) { element.SetContentLength(kFakeSize); elements.push_back(element); request.upload_data->SetElements(elements); - EXPECT_EQ(kFakeSize, request.upload_data->GetContentLength()); + EXPECT_EQ(kFakeSize, request.upload_data->GetContentLengthSync()); MockRead data_reads[] = { MockRead("HTTP/1.0 200 OK\r\n\r\n"), diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index 6d4c496d..97e494d 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -1689,14 +1689,16 @@ TEST_P(SpdyNetworkTransactionTest, EmptyPost) { request.upload_data = new UploadData(); // Http POST Content-Length is using UploadDataStream::size(). - // It is the same as request.upload_data->GetContentLength(). + // It is the same as request.upload_data->GetContentLengthSync(). scoped_ptr<UploadDataStream> stream( new UploadDataStream(request.upload_data)); ASSERT_EQ(OK, stream->Init()); - ASSERT_EQ(request.upload_data->GetContentLength(), stream->size()); + ASSERT_EQ(request.upload_data->GetContentLengthSync(), + stream->size()); scoped_ptr<spdy::SpdyFrame> - req(ConstructSpdyPost(request.upload_data->GetContentLength(), NULL, 0)); + req(ConstructSpdyPost( + request.upload_data->GetContentLengthSync(), NULL, 0)); // Set the FIN bit since there will be no body. req->set_flags(spdy::CONTROL_FLAG_FIN); MockWrite writes[] = { @@ -1736,11 +1738,12 @@ TEST_P(SpdyNetworkTransactionTest, PostWithEarlySynReply) { request.upload_data->AppendBytes(upload, sizeof(upload)); // Http POST Content-Length is using UploadDataStream::size(). - // It is the same as request.upload_data->GetContentLength(). + // It is the same as request.upload_data->GetContentLengthSync(). scoped_ptr<UploadDataStream> stream( new UploadDataStream(request.upload_data)); ASSERT_EQ(OK, stream->Init()); - ASSERT_EQ(request.upload_data->GetContentLength(), stream->size()); + ASSERT_EQ(request.upload_data->GetContentLengthSync(), + stream->size()); scoped_ptr<spdy::SpdyFrame> stream_reply(ConstructSpdyPostSynReply(NULL, 0)); scoped_ptr<spdy::SpdyFrame> stream_body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { |