summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorsatorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-10 07:39:11 +0000
committersatorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-10 07:39:11 +0000
commit1dce708bbe7fdef15436e20f36f805f53bfdcb73 (patch)
tree3b7852c44e541e6d755fb1044c857d37bc622252 /net
parent9caded3f4f06300155f117134f59b5d85b98119f (diff)
downloadchromium_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.cc60
-rw-r--r--net/base/upload_data.h19
-rw-r--r--net/base/upload_data_stream.cc7
-rw-r--r--net/base/upload_data_stream_unittest.cc4
-rw-r--r--net/base/upload_data_unittest.cc173
-rw-r--r--net/http/http_network_transaction_unittest.cc2
-rw-r--r--net/spdy/spdy_network_transaction_unittest.cc13
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[] = {