diff options
-rw-r--r-- | content/child/resource_dispatcher_unittest.cc | 1 | ||||
-rw-r--r-- | net/base/upload_data.cc | 37 | ||||
-rw-r--r-- | net/base/upload_data.h | 83 | ||||
-rw-r--r-- | net/base/upload_file_element_reader.cc | 48 | ||||
-rw-r--r-- | net/base/upload_file_element_reader.h | 31 | ||||
-rw-r--r-- | net/base/upload_file_element_reader_unittest.cc | 133 | ||||
-rw-r--r-- | net/net.gyp | 2 |
7 files changed, 335 insertions, 0 deletions
diff --git a/content/child/resource_dispatcher_unittest.cc b/content/child/resource_dispatcher_unittest.cc index c0a2ba1..349ce8c 100644 --- a/content/child/resource_dispatcher_unittest.cc +++ b/content/child/resource_dispatcher_unittest.cc @@ -14,6 +14,7 @@ #include "content/common/resource_messages.h" #include "content/public/common/resource_response.h" #include "net/base/net_errors.h" +#include "net/base/upload_data.h" #include "net/http/http_response_headers.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/common/appcache/appcache_interfaces.h" diff --git a/net/base/upload_data.cc b/net/base/upload_data.cc new file mode 100644 index 0000000..48cb202 --- /dev/null +++ b/net/base/upload_data.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/upload_data.h" + +#include "base/logging.h" + +namespace net { + +UploadData::UploadData() + : identifier_(0), + is_chunked_(false), + last_chunk_appended_(false) { +} + +void UploadData::AppendBytes(const char* bytes, int bytes_len) { + DCHECK(!is_chunked_); + if (bytes_len > 0) { + elements_.push_back(new UploadElement()); + elements_.back()->SetToBytes(bytes, bytes_len); + } +} + +void UploadData::AppendFileRange(const base::FilePath& file_path, + uint64 offset, uint64 length, + const base::Time& expected_modification_time) { + DCHECK(!is_chunked_); + elements_.push_back(new UploadElement()); + elements_.back()->SetToFilePathRange(file_path, offset, length, + expected_modification_time); +} + +UploadData::~UploadData() { +} + +} // namespace net diff --git a/net/base/upload_data.h b/net/base/upload_data.h new file mode 100644 index 0000000..b782ab4 --- /dev/null +++ b/net/base/upload_data.h @@ -0,0 +1,83 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_BASE_UPLOAD_DATA_H_ +#define NET_BASE_UPLOAD_DATA_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_vector.h" +#include "base/supports_user_data.h" +#include "net/base/net_export.h" +#include "net/base/upload_element.h" + +namespace base { +class FilePath; +class Time; +} // namespace base + +namespace net { + +//----------------------------------------------------------------------------- +// A very concrete class representing the data to be uploaded as part of a +// URLRequest. +// +// Until there is a more abstract class for this, this one derives from +// SupportsUserData to allow users to stash random data by +// key and ensure its destruction when UploadData is finally deleted. +class NET_EXPORT UploadData + : public base::RefCounted<UploadData>, + public base::SupportsUserData { + public: + UploadData(); + + void AppendBytes(const char* bytes, int bytes_len); + + void AppendFileRange(const base::FilePath& file_path, + uint64 offset, uint64 length, + const base::Time& expected_modification_time); + + // Initializes the object to send chunks of upload data over time rather + // than all at once. Chunked data may only contain bytes, not files. + void set_is_chunked(bool set) { is_chunked_ = set; } + bool is_chunked() const { return is_chunked_; } + + // set_last_chunk_appended() is only used for serialization. + void set_last_chunk_appended(bool set) { last_chunk_appended_ = set; } + bool last_chunk_appended() const { return last_chunk_appended_; } + + const ScopedVector<UploadElement>& elements() const { + return elements_; + } + + ScopedVector<UploadElement>* elements_mutable() { + return &elements_; + } + + void swap_elements(ScopedVector<UploadElement>* elements) { + elements_.swap(*elements); + } + + // Identifies a particular upload instance, which is used by the cache to + // formulate a cache key. This value should be unique across browser + // sessions. A value of 0 is used to indicate an unspecified identifier. + void set_identifier(int64 id) { identifier_ = id; } + int64 identifier() const { return identifier_; } + + private: + friend class base::RefCounted<UploadData>; + + virtual ~UploadData(); + + ScopedVector<UploadElement> elements_; + int64 identifier_; + bool is_chunked_; + bool last_chunk_appended_; + + DISALLOW_COPY_AND_ASSIGN(UploadData); +}; + +} // namespace net + +#endif // NET_BASE_UPLOAD_DATA_H_ diff --git a/net/base/upload_file_element_reader.cc b/net/base/upload_file_element_reader.cc index 82159e4..f320157 100644 --- a/net/base/upload_file_element_reader.cc +++ b/net/base/upload_file_element_reader.cc @@ -247,4 +247,52 @@ UploadFileElementReader::ScopedOverridingContentLengthForTests:: overriding_content_length = 0; } +UploadFileElementReaderSync::UploadFileElementReaderSync( + const base::FilePath& path, + uint64 range_offset, + uint64 range_length, + const base::Time& expected_modification_time) + : path_(path), + range_offset_(range_offset), + range_length_(range_length), + expected_modification_time_(expected_modification_time), + content_length_(0), + bytes_remaining_(0) { +} + +UploadFileElementReaderSync::~UploadFileElementReaderSync() { +} + +int UploadFileElementReaderSync::Init(const CompletionCallback& callback) { + bytes_remaining_ = 0; + content_length_ = 0; + file_stream_.reset(); + + const int result = InitInternal(path_, range_offset_, range_length_, + expected_modification_time_, + &file_stream_, &content_length_); + bytes_remaining_ = GetContentLength(); + return result; +} + +uint64 UploadFileElementReaderSync::GetContentLength() const { + return content_length_; +} + +uint64 UploadFileElementReaderSync::BytesRemaining() const { + return bytes_remaining_; +} + +int UploadFileElementReaderSync::Read(IOBuffer* buf, + int buf_length, + const CompletionCallback& callback) { + const int result = ReadInternal(buf, buf_length, BytesRemaining(), + file_stream_.get()); + if (result > 0) { + DCHECK_GE(bytes_remaining_, static_cast<uint64>(result)); + bytes_remaining_ -= result; + } + return result; +} + } // namespace net diff --git a/net/base/upload_file_element_reader.h b/net/base/upload_file_element_reader.h index 2c8ef10..a805c7a 100644 --- a/net/base/upload_file_element_reader.h +++ b/net/base/upload_file_element_reader.h @@ -106,6 +106,37 @@ class NET_EXPORT UploadFileElementReader : public UploadElementReader { DISALLOW_COPY_AND_ASSIGN(UploadFileElementReader); }; +// An UploadElementReader implementation for file which performs file operation +// synchronously. +// Use this class only if the thread is IO allowed. +class NET_EXPORT UploadFileElementReaderSync : public UploadElementReader { + public: + UploadFileElementReaderSync(const base::FilePath& path, + uint64 range_offset, + uint64 range_length, + const base::Time& expected_modification_time); + virtual ~UploadFileElementReaderSync(); + + // UploadElementReader overrides: + virtual int Init(const CompletionCallback& callback) OVERRIDE; + virtual uint64 GetContentLength() const OVERRIDE; + virtual uint64 BytesRemaining() const OVERRIDE; + virtual int Read(IOBuffer* buf, + int buf_length, + const CompletionCallback& callback) OVERRIDE; + + private: + const base::FilePath path_; + const uint64 range_offset_; + const uint64 range_length_; + const base::Time expected_modification_time_; + scoped_ptr<FileStream> file_stream_; + uint64 content_length_; + uint64 bytes_remaining_; + + DISALLOW_COPY_AND_ASSIGN(UploadFileElementReaderSync); +}; + } // namespace net #endif // NET_BASE_UPLOAD_FILE_ELEMENT_READER_H_ diff --git a/net/base/upload_file_element_reader_unittest.cc b/net/base/upload_file_element_reader_unittest.cc index d98262b..52f0f27 100644 --- a/net/base/upload_file_element_reader_unittest.cc +++ b/net/base/upload_file_element_reader_unittest.cc @@ -234,4 +234,137 @@ TEST_F(UploadFileElementReaderTest, WrongPath) { EXPECT_EQ(ERR_FILE_NOT_FOUND, init_callback.WaitForResult()); } + +class UploadFileElementReaderSyncTest : public PlatformTest { + protected: + virtual void SetUp() OVERRIDE { + // Some tests (*.ReadPartially) rely on bytes_.size() being even. + const char kData[] = "123456789abcdefghi"; + bytes_.assign(kData, kData + arraysize(kData) - 1); + + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + + ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), + &temp_file_path_)); + ASSERT_EQ( + static_cast<int>(bytes_.size()), + file_util::WriteFile(temp_file_path_, &bytes_[0], bytes_.size())); + + reader_.reset(new UploadFileElementReaderSync( + temp_file_path_, 0, kuint64max, base::Time())); + ASSERT_EQ(OK, reader_->Init(CompletionCallback())); + EXPECT_EQ(bytes_.size(), reader_->GetContentLength()); + EXPECT_EQ(bytes_.size(), reader_->BytesRemaining()); + EXPECT_FALSE(reader_->IsInMemory()); + } + + std::vector<char> bytes_; + scoped_ptr<UploadElementReader> reader_; + base::ScopedTempDir temp_dir_; + base::FilePath temp_file_path_; +}; + +TEST_F(UploadFileElementReaderSyncTest, ReadPartially) { + const size_t kHalfSize = bytes_.size() / 2; + ASSERT_EQ(bytes_.size(), kHalfSize * 2); + std::vector<char> buf(kHalfSize); + scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]); + EXPECT_EQ( + static_cast<int>(buf.size()), + reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback())); + EXPECT_EQ(bytes_.size() - buf.size(), reader_->BytesRemaining()); + EXPECT_EQ(std::vector<char>(bytes_.begin(), bytes_.begin() + kHalfSize), buf); + + EXPECT_EQ( + static_cast<int>(buf.size()), + reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(std::vector<char>(bytes_.begin() + kHalfSize, bytes_.end()), buf); +} + +TEST_F(UploadFileElementReaderSyncTest, ReadAll) { + std::vector<char> buf(bytes_.size()); + scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]); + EXPECT_EQ( + static_cast<int>(buf.size()), + reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(bytes_, buf); + // Try to read again. + EXPECT_EQ( + 0, reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback())); +} + +TEST_F(UploadFileElementReaderSyncTest, ReadTooMuch) { + const size_t kTooLargeSize = bytes_.size() * 2; + std::vector<char> buf(kTooLargeSize); + scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]); + EXPECT_EQ( + static_cast<int>(bytes_.size()), + reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + buf.resize(bytes_.size()); // Resize to compare. + EXPECT_EQ(bytes_, buf); +} + +TEST_F(UploadFileElementReaderSyncTest, 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_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(bytes_, buf); + + // Call Init() again to reset the state. + ASSERT_EQ(OK, reader_->Init(CompletionCallback())); + EXPECT_EQ(bytes_.size(), reader_->GetContentLength()); + EXPECT_EQ(bytes_.size(), reader_->BytesRemaining()); + + // Read again. + EXPECT_EQ( + static_cast<int>(buf.size()), + reader_->Read(wrapped_buffer.get(), buf.size(), CompletionCallback())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(bytes_, buf); +} + +TEST_F(UploadFileElementReaderSyncTest, Range) { + const uint64 kOffset = 2; + const uint64 kLength = bytes_.size() - kOffset * 3; + reader_.reset(new UploadFileElementReaderSync( + temp_file_path_, kOffset, kLength, base::Time())); + ASSERT_EQ(OK, reader_->Init(CompletionCallback())); + EXPECT_EQ(kLength, reader_->GetContentLength()); + EXPECT_EQ(kLength, reader_->BytesRemaining()); + std::vector<char> buf(kLength); + scoped_refptr<IOBuffer> wrapped_buffer = new WrappedIOBuffer(&buf[0]); + EXPECT_EQ(static_cast<int>(kLength), + reader_->Read(wrapped_buffer.get(), kLength, CompletionCallback())); + const std::vector<char> expected(bytes_.begin() + kOffset, + bytes_.begin() + kOffset + kLength); + EXPECT_EQ(expected, buf); +} + +TEST_F(UploadFileElementReaderSyncTest, FileChanged) { + base::File::Info info; + ASSERT_TRUE(base::GetFileInfo(temp_file_path_, &info)); + + // Expect one second before the actual modification time to simulate change. + const base::Time expected_modification_time = + info.last_modified - base::TimeDelta::FromSeconds(1); + reader_.reset(new UploadFileElementReaderSync( + temp_file_path_, 0, kuint64max, expected_modification_time)); + EXPECT_EQ(ERR_UPLOAD_FILE_CHANGED, reader_->Init(CompletionCallback())); +} + +TEST_F(UploadFileElementReaderSyncTest, WrongPath) { + const base::FilePath wrong_path(FILE_PATH_LITERAL("wrong_path")); + reader_.reset(new UploadFileElementReaderSync( + wrong_path, 0, kuint64max, base::Time())); + ASSERT_EQ(ERR_FILE_NOT_FOUND, reader_->Init(CompletionCallback())); +} + } // namespace net diff --git a/net/net.gyp b/net/net.gyp index 5a884e2..d3952ef 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -220,6 +220,8 @@ 'base/test_data_stream.h', 'base/upload_bytes_element_reader.cc', 'base/upload_bytes_element_reader.h', + 'base/upload_data.cc', + 'base/upload_data.h', 'base/upload_data_stream.cc', 'base/upload_data_stream.h', 'base/upload_element.cc', |