diff options
author | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-11 20:27:21 +0000 |
---|---|---|
committer | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-11 20:27:21 +0000 |
commit | d989616505f0195fdd1d646160fe080a6dfad492 (patch) | |
tree | 07ed24b733d004155f5d457f8943c43b8afb6a0f | |
parent | 12194a0bd01d99873ea1abdcc9d8b9c838c08bcf (diff) | |
download | chromium_src-d989616505f0195fdd1d646160fe080a6dfad492.zip chromium_src-d989616505f0195fdd1d646160fe080a6dfad492.tar.gz chromium_src-d989616505f0195fdd1d646160fe080a6dfad492.tar.bz2 |
net: Move data reading functionalities from UploadElement to UploadElementReader
Add a new interface net::UploadElementReader and its implementations for bytes and file.
Rewrite UploadDataStream using UploadElementReader.
UploadElement::SetContentLength is replaced with UploadFileElementReader::ScopedOverridingContentLengthForTests.
HttpNetworkTransactionSpdy(2|3)Test.UnreadableUploadFileAfterAuthRestart is bit modified since a new FileStream is created every time when UploadDataStream is initialized.
BUG=145329
TEST=net_unittests
Review URL: https://chromiumcodereview.appspot.com/10868064
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@156113 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/base/upload_bytes_element_reader.cc | 54 | ||||
-rw-r--r-- | net/base/upload_bytes_element_reader.h | 36 | ||||
-rw-r--r-- | net/base/upload_bytes_element_reader_unittest.cc | 61 | ||||
-rw-r--r-- | net/base/upload_data_stream.cc | 83 | ||||
-rw-r--r-- | net/base/upload_data_stream.h | 4 | ||||
-rw-r--r-- | net/base/upload_data_stream_unittest.cc | 5 | ||||
-rw-r--r-- | net/base/upload_element.cc | 157 | ||||
-rw-r--r-- | net/base/upload_element.h | 57 | ||||
-rw-r--r-- | net/base/upload_element_reader.cc | 38 | ||||
-rw-r--r-- | net/base/upload_element_reader.h | 50 | ||||
-rw-r--r-- | net/base/upload_file_element_reader.cc | 142 | ||||
-rw-r--r-- | net/base/upload_file_element_reader.h | 63 | ||||
-rw-r--r-- | net/base/upload_file_element_reader_unittest.cc | 110 | ||||
-rw-r--r-- | net/http/http_network_transaction_spdy2_unittest.cc | 7 | ||||
-rw-r--r-- | net/http/http_network_transaction_spdy3_unittest.cc | 7 | ||||
-rw-r--r-- | net/net.gyp | 8 |
16 files changed, 615 insertions, 267 deletions
diff --git a/net/base/upload_bytes_element_reader.cc b/net/base/upload_bytes_element_reader.cc new file mode 100644 index 0000000..c99ce74 --- /dev/null +++ b/net/base/upload_bytes_element_reader.cc @@ -0,0 +1,54 @@ +// 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_bytes_element_reader.h" + +#include "base/logging.h" +#include "net/base/net_errors.h" + +namespace net { + +UploadBytesElementReader::UploadBytesElementReader(const char* bytes, + int bytes_length) + : bytes_(bytes), + bytes_length_(bytes_length), + offset_(0) { +} + +UploadBytesElementReader::~UploadBytesElementReader() { +} + +int UploadBytesElementReader::InitSync() { + return OK; +} + +uint64 UploadBytesElementReader::GetContentLength() const { + return bytes_length_; +} + +uint64 UploadBytesElementReader::BytesRemaining() const { + return bytes_length_ - offset_; +} + +int UploadBytesElementReader::ReadSync(char* buf, int buf_length) { + DCHECK_LT(0, buf_length); + + const size_t num_bytes_to_read = + std::min(BytesRemaining(), static_cast<uint64>(buf_length)); + + // Check if we have anything to copy first, because we are getting + // the address of an element in |bytes_| and that will throw an + // exception if |bytes_| is an empty vector. + if (num_bytes_to_read > 0) + memcpy(buf, bytes_ + offset_, num_bytes_to_read); + + offset_ += num_bytes_to_read; + return num_bytes_to_read; +} + +bool UploadBytesElementReader::IsInMemory() const { + return true; +} + +} // namespace net diff --git a/net/base/upload_bytes_element_reader.h b/net/base/upload_bytes_element_reader.h new file mode 100644 index 0000000..cb9d3e8 --- /dev/null +++ b/net/base/upload_bytes_element_reader.h @@ -0,0 +1,36 @@ +// 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_BYTES_ELEMENT_READER_H_ +#define NET_BASE_UPLOAD_BYTES_ELEMENT_READER_H_ + +#include "base/compiler_specific.h" +#include "net/base/upload_element_reader.h" + +namespace net { + +// An UploadElementReader implementation for bytes. +class NET_EXPORT_PRIVATE UploadBytesElementReader : public UploadElementReader { + public: + UploadBytesElementReader(const char* bytes, int bytes_length); + virtual ~UploadBytesElementReader(); + + // UploadElementReader overrides: + virtual int InitSync() OVERRIDE; + virtual uint64 GetContentLength() const OVERRIDE; + virtual uint64 BytesRemaining() const OVERRIDE; + virtual int ReadSync(char* buf, int buf_length) OVERRIDE; + virtual bool IsInMemory() const OVERRIDE; + + private: + const char* bytes_; + int bytes_length_; + int offset_; + + DISALLOW_COPY_AND_ASSIGN(UploadBytesElementReader); +}; + +} // namespace net + +#endif // NET_BASE_UPLOAD_BYTES_ELEMENT_READER_H_ diff --git a/net/base/upload_bytes_element_reader_unittest.cc b/net/base/upload_bytes_element_reader_unittest.cc new file mode 100644 index 0000000..9e68feb --- /dev/null +++ b/net/base/upload_bytes_element_reader_unittest.cc @@ -0,0 +1,61 @@ +// 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_bytes_element_reader.h" + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/net_errors.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace net { + +class UploadBytesElementReaderTest : public PlatformTest { + protected: + virtual void SetUp() OVERRIDE { + const char kData[] = "123abc"; + bytes_.assign(kData, kData + arraysize(kData)); + reader_.reset(new UploadBytesElementReader(&bytes_[0], bytes_.size())); + ASSERT_EQ(OK, reader_->InitSync()); + EXPECT_EQ(bytes_.size(), reader_->GetContentLength()); + EXPECT_EQ(bytes_.size(), reader_->BytesRemaining()); + EXPECT_TRUE(reader_->IsInMemory()); + } + + std::vector<char> bytes_; + scoped_ptr<UploadElementReader> reader_; +}; + +TEST_F(UploadBytesElementReaderTest, ReadPartially) { + const size_t kHalfSize = bytes_.size() / 2; + std::vector<char> buf(kHalfSize); + EXPECT_EQ(static_cast<int>(buf.size()), + reader_->ReadSync(&buf[0], buf.size())); + EXPECT_EQ(bytes_.size() - buf.size(), reader_->BytesRemaining()); + bytes_.resize(kHalfSize); // Resize to compare. + EXPECT_EQ(bytes_, buf); +} + +TEST_F(UploadBytesElementReaderTest, ReadAll) { + std::vector<char> buf(bytes_.size()); + EXPECT_EQ(static_cast<int>(buf.size()), + reader_->ReadSync(&buf[0], buf.size())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(bytes_, buf); + // Try to read again. + EXPECT_EQ(0, reader_->ReadSync(&buf[0], buf.size())); +} + +TEST_F(UploadBytesElementReaderTest, ReadTooMuch) { + const size_t kTooLargeSize = bytes_.size() * 2; + std::vector<char> buf(kTooLargeSize); + EXPECT_EQ(static_cast<int>(bytes_.size()), + reader_->ReadSync(&buf[0], buf.size())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + buf.resize(bytes_.size()); // Resize to compare. + EXPECT_EQ(bytes_, buf); +} + +} // namespace net diff --git a/net/base/upload_data_stream.cc b/net/base/upload_data_stream.cc index b96680e..e46f2e6 100644 --- a/net/base/upload_data_stream.cc +++ b/net/base/upload_data_stream.cc @@ -4,12 +4,10 @@ #include "net/base/upload_data_stream.h" -#include "base/file_util.h" #include "base/logging.h" -#include "base/threading/thread_restrictions.h" -#include "net/base/file_stream.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" +#include "net/base/upload_element_reader.h" namespace net { @@ -27,6 +25,9 @@ UploadDataStream::UploadDataStream(UploadData* upload_data) total_size_(0), current_position_(0), initialized_successfully_(false) { + 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])); } UploadDataStream::~UploadDataStream() { @@ -34,57 +35,47 @@ UploadDataStream::~UploadDataStream() { int UploadDataStream::Init() { DCHECK(!initialized_successfully_); - std::vector<UploadElement>* elements = upload_data_->elements_mutable(); - { - base::ThreadRestrictions::ScopedAllowIO allow_io; - total_size_ = 0; - if (!is_chunked()) { - for (size_t i = 0; i < elements->size(); ++i) - total_size_ += (*elements)[i].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. - for (size_t i = 0; i < elements->size(); ++i) { - const UploadElement& element = (*elements)[i]; - if (element.type() == UploadElement::TYPE_FILE && - !element.expected_file_modification_time().is_null()) { - // Temporarily allow until fix: http://crbug.com/72001. - base::ThreadRestrictions::ScopedAllowIO allow_io; - base::PlatformFileInfo info; - if (file_util::GetFileInfo(element.file_path(), &info) && - element.expected_file_modification_time().ToTimeT() != - info.last_modified.ToTimeT()) { - return ERR_UPLOAD_FILE_CHANGED; - } - } + uint64 total_size = 0; + for (size_t i = 0; i < element_readers_.size(); ++i) { + UploadElementReader* reader = element_readers_[i]; + const int result = reader->InitSync(); + if (result != OK) + return result; + if (!is_chunked()) + total_size += reader->GetContentLength(); } - - // Reset the offset, as upload_data_ may already be read (i.e. UploadData - // can be reused for a new UploadDataStream). - for (size_t i = 0; i < elements->size(); ++i) - (*elements)[i].ResetOffset(); + total_size_ = total_size; initialized_successfully_ = true; return OK; } int UploadDataStream::Read(IOBuffer* buf, int buf_len) { - std::vector<UploadElement>& elements = - *upload_data_->elements_mutable(); + DCHECK(initialized_successfully_); - int bytes_copied = 0; - while (bytes_copied < buf_len && element_index_ < elements.size()) { - UploadElement& element = elements[element_index_]; + // Initialize readers for newly appended chunks. + if (is_chunked()) { + const std::vector<UploadElement>& elements = *upload_data_->elements(); + DCHECK_LE(element_readers_.size(), elements.size()); - bytes_copied += element.ReadSync(buf->data() + bytes_copied, - buf_len - bytes_copied); + for (size_t i = element_readers_.size(); i < elements.size(); ++i) { + const UploadElement& element = elements[i]; + DCHECK_EQ(UploadElement::TYPE_BYTES, element.type()); + UploadElementReader* reader = UploadElementReader::Create(element); - if (element.BytesRemaining() == 0) + const int rv = reader->InitSync(); + DCHECK_EQ(rv, OK); + element_readers_.push_back(reader); + } + } + + int bytes_copied = 0; + while (bytes_copied < buf_len && element_index_ < element_readers_.size()) { + UploadElementReader* reader = element_readers_[element_index_]; + bytes_copied += reader->ReadSync(buf->data() + bytes_copied, + buf_len - bytes_copied); + if (reader->BytesRemaining() == 0) ++element_index_; if (is_chunked() && !merge_chunks_) @@ -99,6 +90,7 @@ int UploadDataStream::Read(IOBuffer* buf, int buf_len) { } bool UploadDataStream::IsEOF() const { + DCHECK(initialized_successfully_); const std::vector<UploadElement>& elements = *upload_data_->elements(); // Check if all elements are consumed. @@ -119,9 +111,8 @@ bool UploadDataStream::IsInMemory() const { if (is_chunked()) return false; - const std::vector<UploadElement>& elements = *upload_data_->elements(); - for (size_t i = 0; i < elements.size(); ++i) { - if (elements[i].type() != UploadElement::TYPE_BYTES) + for (size_t i = 0; i < element_readers_.size(); ++i) { + if (!element_readers_[i]->IsInMemory()) return false; } return true; diff --git a/net/base/upload_data_stream.h b/net/base/upload_data_stream.h index 3d060f9..73c9359 100644 --- a/net/base/upload_data_stream.h +++ b/net/base/upload_data_stream.h @@ -6,13 +6,14 @@ #define NET_BASE_UPLOAD_DATA_STREAM_H_ #include "base/memory/ref_counted.h" +#include "base/memory/scoped_vector.h" #include "net/base/net_export.h" #include "net/base/upload_data.h" namespace net { -class FileStream; class IOBuffer; +class UploadElementReader; class NET_EXPORT UploadDataStream { public: @@ -75,6 +76,7 @@ class NET_EXPORT UploadDataStream { static void set_merge_chunks(bool merge) { merge_chunks_ = merge; } scoped_refptr<UploadData> upload_data_; + ScopedVector<UploadElementReader> element_readers_; // Index of the current upload element (i.e. the element currently being // read). The index is used as a cursor to iterate over elements in diff --git a/net/base/upload_data_stream_unittest.cc b/net/base/upload_data_stream_unittest.cc index dc415f84..8a8ec39 100644 --- a/net/base/upload_data_stream_unittest.cc +++ b/net/base/upload_data_stream_unittest.cc @@ -16,6 +16,7 @@ #include "net/base/io_buffer.h" #include "net/base/net_errors.h" #include "net/base/upload_data.h" +#include "net/base/upload_file_element_reader.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -116,10 +117,12 @@ TEST_F(UploadDataStreamTest, FileSmallerThanLength) { file_util::WriteFile(temp_file_path, kTestData, kTestDataSize)); const uint64 kFakeSize = kTestDataSize*2; + UploadFileElementReader::ScopedOverridingContentLengthForTests + overriding_content_length(kFakeSize); + std::vector<UploadElement> elements; UploadElement element; element.SetToFilePath(temp_file_path); - element.SetContentLength(kFakeSize); elements.push_back(element); upload_data_->SetElements(elements); diff --git a/net/base/upload_element.cc b/net/base/upload_element.cc index 8f55c6a..fd7df78 100644 --- a/net/base/upload_element.cc +++ b/net/base/upload_element.cc @@ -18,165 +18,10 @@ UploadElement::UploadElement() bytes_start_(NULL), bytes_length_(0), file_range_offset_(0), - file_range_length_(kuint64max), - override_content_length_(false), - content_length_computed_(false), - content_length_(-1), - offset_(0), - file_stream_(NULL) { + file_range_length_(kuint64max) { } UploadElement::~UploadElement() { - // In the common case |file__stream_| will be null. - if (file_stream_) { - // Temporarily allow until fix: http://crbug.com/72001. - base::ThreadRestrictions::ScopedAllowIO allow_io; - file_stream_->CloseSync(); - delete file_stream_; - } -} - -uint64 UploadElement::GetContentLength() { - if (override_content_length_ || content_length_computed_) - return content_length_; - - if (type_ == TYPE_BYTES) - return bytes_length(); - - DCHECK_EQ(TYPE_FILE, type_); - DCHECK(!file_stream_); - - // TODO(darin): This size calculation could be out of sync with the state of - // the file when we get around to reading it. We should probably find a way - // to lock the file or somehow protect against this error condition. - - content_length_computed_ = true; - content_length_ = 0; - - // We need to open the file here to decide if we should report the file's - // size or zero. We cache the open file, so that we can still read it when - // it comes time to. - file_stream_ = OpenFileStream(); - if (!file_stream_) - return 0; - - int64 length = 0; - if (!file_util::GetFileSize(file_path_, &length)) - return 0; - - if (file_range_offset_ >= static_cast<uint64>(length)) - return 0; // range is beyond eof - - // compensate for the offset and clip file_range_length_ to eof - content_length_ = std::min(length - file_range_offset_, file_range_length_); - return content_length_; -} - -int UploadElement::ReadSync(char* buf, int buf_len) { - if (type_ == TYPE_BYTES) { - return ReadFromMemorySync(buf, buf_len); - } else if (type_ == TYPE_FILE) { - return ReadFromFileSync(buf, buf_len); - } - - NOTREACHED(); - return 0; -} - -uint64 UploadElement::BytesRemaining() { - return GetContentLength() - offset_; -} - -void UploadElement::ResetOffset() { - offset_ = 0; - - // Delete the file stream if already opened, so we can reread the file from - // the beginning. - if (file_stream_) { - // Temporarily allow until fix: http://crbug.com/72001. - base::ThreadRestrictions::ScopedAllowIO allow_io; - file_stream_->CloseSync(); - delete file_stream_; - file_stream_ = NULL; - } -} - -FileStream* UploadElement::OpenFileStream() { - scoped_ptr<FileStream> file(new FileStream(NULL)); - int64 rv = file->OpenSync( - file_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 \"" << file_path_.value() - << "\" for reading: " << rv; - return NULL; - } - if (file_range_offset_) { - rv = file->SeekSync(FROM_BEGIN, file_range_offset_); - if (rv < 0) { - DLOG(WARNING) << "Failed to seek \"" << file_path_.value() - << "\" to offset: " << file_range_offset_ << " (" << rv - << ")"; - return NULL; - } - } - - return file.release(); -} - -int UploadElement::ReadFromMemorySync(char* buf, int buf_len) { - DCHECK_LT(0, buf_len); - DCHECK(type_ == TYPE_BYTES); - - const size_t num_bytes_to_read = std::min(BytesRemaining(), - static_cast<uint64>(buf_len)); - - // Check if we have anything to copy first, because we are getting - // the address of an element in |bytes_| and that will throw an - // exception if |bytes_| is an empty vector. - if (num_bytes_to_read > 0) - memcpy(buf, bytes() + offset_, num_bytes_to_read); - - offset_ += num_bytes_to_read; - return num_bytes_to_read; -} - -int UploadElement::ReadFromFileSync(char* buf, int buf_len) { - DCHECK_LT(0, buf_len); - DCHECK_EQ(TYPE_FILE, type_); - - // Open the file of the current element if not yet opened. - // In common usage, GetContentLength() opened it already. - if (!file_stream_) { - // Temporarily allow until fix: http://crbug.com/72001. - base::ThreadRestrictions::ScopedAllowIO allow_io; - file_stream_ = OpenFileStream(); - } - - const int num_bytes_to_read = - static_cast<int>(std::min(BytesRemaining(), - static_cast<uint64>(buf_len))); - if (num_bytes_to_read > 0) { - int num_bytes_consumed = 0; - // Temporarily allow until fix: http://crbug.com/72001. - base::ThreadRestrictions::ScopedAllowIO allow_io; - // file_stream_ is NULL if the target file is - // missing or not readable. - if (file_stream_) { - num_bytes_consumed = - file_stream_->ReadSync(buf, num_bytes_to_read); - } - if (num_bytes_consumed <= 0) { - // If there's less data to read than we initially observed, then - // pad with zero. Otherwise the server will hang waiting for the - // rest of the data. - memset(buf, 0, num_bytes_to_read); - } - } - - offset_ += num_bytes_to_read; - return num_bytes_to_read; } } // namespace net diff --git a/net/base/upload_element.h b/net/base/upload_element.h index ea58bfd..96e065e 100644 --- a/net/base/upload_element.h +++ b/net/base/upload_element.h @@ -9,15 +9,11 @@ #include "base/basictypes.h" #include "base/file_path.h" -#include "base/gtest_prod_util.h" #include "base/time.h" -#include "googleurl/src/gurl.h" #include "net/base/net_export.h" namespace net { -class FileStream; - // A class representing an element contained by UploadData. class NET_EXPORT UploadElement { public: @@ -72,42 +68,7 @@ class NET_EXPORT UploadElement { expected_file_modification_time_ = expected_modification_time; } - // Returns the byte-length of the element. For files that do not exist, 0 - // is returned. This is done for consistency with Mozilla. - uint64 GetContentLength(); - - // Reads up to |buf_len| bytes synchronously. Returns the number of bytes - // read. This function never fails. If there's less data to read than we - // initially observed, then pad with zero (this can happen with files). - // |buf_len| must be greater than 0. - int ReadSync(char* buf, int buf_len); - - // Returns the number of bytes remaining to read. - uint64 BytesRemaining(); - - // Resets the offset to zero and closes the file stream if opened, so - // that the element can be reread. - void ResetOffset(); - private: - // Returns a FileStream opened for reading for this element, positioned - // at |file_range_offset_|. Returns NULL if the file is not openable. - FileStream* OpenFileStream(); - - // Reads up to |buf_len| bytes synchronously from memory (i.e. type_ is - // TYPE_BYTES). - int ReadFromMemorySync(char* buf, int buf_len); - - // Reads up to |buf_len| bytes synchronously from a file (i.e. type_ is - // TYPE_FILE). - int ReadFromFileSync(char* buf, int buf_len); - - // Allows tests to override the result of GetContentLength. - void SetContentLength(uint64 content_length) { - override_content_length_ = true; - content_length_ = content_length; - } - Type type_; std::vector<char> buf_; const char* bytes_start_; @@ -116,24 +77,6 @@ class NET_EXPORT UploadElement { uint64 file_range_offset_; uint64 file_range_length_; base::Time expected_file_modification_time_; - bool override_content_length_; - bool content_length_computed_; - uint64 content_length_; - - // The byte offset from the beginning of the element data. Used to track - // the current position when reading data. - uint64 offset_; - - // The stream of the element data, if this element is of TYPE_FILE. - FileStream* file_stream_; - - FRIEND_TEST_ALL_PREFIXES(UploadDataStreamTest, FileSmallerThanLength); - FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionTest, - UploadFileSmallerThanLength); - FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionSpdy2Test, - UploadFileSmallerThanLength); - FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionSpdy3Test, - UploadFileSmallerThanLength); }; #if defined(UNIT_TEST) diff --git a/net/base/upload_element_reader.cc b/net/base/upload_element_reader.cc new file mode 100644 index 0000000..bb5cd85 --- /dev/null +++ b/net/base/upload_element_reader.cc @@ -0,0 +1,38 @@ +// 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_element_reader.h" + +#include "base/logging.h" +#include "net/base/upload_bytes_element_reader.h" +#include "net/base/upload_element.h" +#include "net/base/upload_file_element_reader.h" + +namespace net { + +// static +UploadElementReader* UploadElementReader::Create(const UploadElement& element) { + UploadElementReader* reader = NULL; + switch (element.type()) { + case UploadElement::TYPE_BYTES: + reader = new UploadBytesElementReader(element.bytes(), + element.bytes_length()); + break; + case UploadElement::TYPE_FILE: + reader = new UploadFileElementReader( + element.file_path(), + element.file_range_offset(), + element.file_range_length(), + element.expected_file_modification_time()); + break; + } + DCHECK(reader); + return reader; +} + +bool UploadElementReader::IsInMemory() const { + return false; +} + +} // namespace net diff --git a/net/base/upload_element_reader.h b/net/base/upload_element_reader.h new file mode 100644 index 0000000..3e8bcea --- /dev/null +++ b/net/base/upload_element_reader.h @@ -0,0 +1,50 @@ +// 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_ELEMENT_READER_H_ +#define NET_BASE_UPLOAD_ELEMENT_READER_H_ + +#include "base/basictypes.h" +#include "net/base/net_export.h" + +namespace net { + +class UploadElement; + +// An interface to read an upload data element. +class NET_EXPORT UploadElementReader { + public: + UploadElementReader() {} + virtual ~UploadElementReader() {} + + // Creates an appropriate UploadElementReader instance for the given element. + static UploadElementReader* Create(const UploadElement& element); + + // Initializes the instance synchronously. + virtual int InitSync() = 0; + + // Returns the byte-length of the element. For files that do not exist, 0 + // is returned. This is done for consistency with Mozilla. + virtual uint64 GetContentLength() const = 0; + + // Returns the number of bytes remaining to read. + virtual uint64 BytesRemaining() const = 0; + + // Returns true if the upload element is entirely in memory. + // The default implementation returns false. + virtual bool IsInMemory() const; + + // Reads up to |buf_length| bytes synchronously. Returns the number of bytes + // read. This function never fails. If there's less data to read than we + // initially observed, then pad with zero (this can happen with files). + // |buf_length| must be greater than 0. + virtual int ReadSync(char* buf, int buf_length) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(UploadElementReader); +}; + +} // namespace net + +#endif // NET_BASE_UPLOAD_ELEMENT_READER_H_ diff --git a/net/base/upload_file_element_reader.cc b/net/base/upload_file_element_reader.cc new file mode 100644 index 0000000..b71c022 --- /dev/null +++ b/net/base/upload_file_element_reader.cc @@ -0,0 +1,142 @@ +// 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_file_element_reader.h" + +#include "base/file_util.h" +#include "base/threading/thread_restrictions.h" +#include "net/base/file_stream.h" +#include "net/base/net_errors.h" + +namespace net { + +namespace { + +// In tests, this value is used to override the return value of +// UploadFileElementReader::GetContentLength() when set to non-zero. +uint64 overriding_content_length = 0; + +} // namespace + +UploadFileElementReader::UploadFileElementReader( + const 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) { +} + +UploadFileElementReader::~UploadFileElementReader() { + // Temporarily allow until fix: http://crbug.com/72001. + base::ThreadRestrictions::ScopedAllowIO allow_io; + if (file_stream_.get()) + file_stream_->CloseSync(); +} + +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; +} + +uint64 UploadFileElementReader::GetContentLength() const { + if (overriding_content_length) + return overriding_content_length; + return content_length_; +} + +uint64 UploadFileElementReader::BytesRemaining() const { + return bytes_remaining_; +} + +int UploadFileElementReader::ReadSync(char* buf, int buf_length) { + // Temporarily allow until fix: http://crbug.com/72001. + base::ThreadRestrictions::ScopedAllowIO allow_io; + DCHECK_LT(0, buf_length); + + const uint64 num_bytes_to_read = + static_cast<int>(std::min(BytesRemaining(), + static_cast<uint64>(buf_length))); + if (num_bytes_to_read > 0) { + int num_bytes_consumed = 0; + // file_stream_ is NULL if the target file is + // missing or not readable. + if (file_stream_.get()) { + num_bytes_consumed = + file_stream_->ReadSync(buf, num_bytes_to_read); + } + if (num_bytes_consumed <= 0) { + // If there's less data to read than we initially observed, then + // pad with zero. Otherwise the server will hang waiting for the + // rest of the data. + memset(buf, 0, num_bytes_to_read); + } + } + DCHECK_GE(bytes_remaining_, num_bytes_to_read); + bytes_remaining_ -= num_bytes_to_read; + return num_bytes_to_read; +} + +UploadFileElementReader::ScopedOverridingContentLengthForTests:: +ScopedOverridingContentLengthForTests(uint64 value) { + overriding_content_length = value; +} + +UploadFileElementReader::ScopedOverridingContentLengthForTests:: +~ScopedOverridingContentLengthForTests() { + overriding_content_length = 0; +} + +} // namespace net diff --git a/net/base/upload_file_element_reader.h b/net/base/upload_file_element_reader.h new file mode 100644 index 0000000..34d8194 --- /dev/null +++ b/net/base/upload_file_element_reader.h @@ -0,0 +1,63 @@ +// 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_FILE_ELEMENT_READER_H_ +#define NET_BASE_UPLOAD_FILE_ELEMENT_READER_H_ + +#include "base/compiler_specific.h" +#include "base/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/time.h" +#include "net/base/upload_element_reader.h" + +namespace net { + +class FileStream; + +// An UploadElementReader implementation for file. +class NET_EXPORT_PRIVATE UploadFileElementReader : public UploadElementReader { + public: + UploadFileElementReader(const FilePath& path, + uint64 range_offset, + uint64 range_length, + const base::Time& expected_modification_time); + virtual ~UploadFileElementReader(); + + // UploadElementReader overrides: + virtual int InitSync() OVERRIDE; + virtual uint64 GetContentLength() const OVERRIDE; + virtual uint64 BytesRemaining() const OVERRIDE; + virtual int ReadSync(char* buf, int buf_length) OVERRIDE; + + private: + // Sets an value to override the result for GetContentLength(). + // Used for tests. + struct NET_EXPORT_PRIVATE ScopedOverridingContentLengthForTests { + ScopedOverridingContentLengthForTests(uint64 value); + ~ScopedOverridingContentLengthForTests(); + }; + + FilePath path_; + uint64 range_offset_; + uint64 range_length_; + base::Time expected_modification_time_; + scoped_ptr<FileStream> file_stream_; + uint64 content_length_; + uint64 bytes_remaining_; + + FRIEND_TEST_ALL_PREFIXES(UploadDataStreamTest, FileSmallerThanLength); + FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionTest, + UploadFileSmallerThanLength); + FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionSpdy2Test, + UploadFileSmallerThanLength); + FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionSpdy3Test, + UploadFileSmallerThanLength); + + DISALLOW_COPY_AND_ASSIGN(UploadFileElementReader); +}; + +} // 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 new file mode 100644 index 0000000..6a80716 --- /dev/null +++ b/net/base/upload_file_element_reader_unittest.cc @@ -0,0 +1,110 @@ +// 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_file_element_reader.h" + +#include "base/file_util.h" +#include "base/scoped_temp_dir.h" +#include "net/base/net_errors.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace net { + +class UploadFileElementReaderTest : public PlatformTest { + protected: + virtual void SetUp() OVERRIDE { + const char kData[] = "123456789abcdefghi"; + bytes_.assign(kData, kData + arraysize(kData)); + + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + + ASSERT_TRUE(file_util::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 UploadFileElementReader( + temp_file_path_, 0, kuint64max, base::Time())); + ASSERT_EQ(OK, reader_->InitSync()); + EXPECT_EQ(bytes_.size(), reader_->GetContentLength()); + EXPECT_EQ(bytes_.size(), reader_->BytesRemaining()); + EXPECT_FALSE(reader_->IsInMemory()); + } + + std::vector<char> bytes_; + scoped_ptr<UploadElementReader> reader_; + ScopedTempDir temp_dir_; + FilePath temp_file_path_; +}; + +TEST_F(UploadFileElementReaderTest, ReadPartially) { + const size_t kHalfSize = bytes_.size() / 2; + std::vector<char> buf(kHalfSize); + EXPECT_EQ(static_cast<int>(buf.size()), + reader_->ReadSync(&buf[0], buf.size())); + EXPECT_EQ(bytes_.size() - buf.size(), reader_->BytesRemaining()); + bytes_.resize(kHalfSize); // Resize to compare. + EXPECT_EQ(bytes_, buf); +} + +TEST_F(UploadFileElementReaderTest, ReadAll) { + std::vector<char> buf(bytes_.size()); + EXPECT_EQ(static_cast<int>(buf.size()), + reader_->ReadSync(&buf[0], buf.size())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + EXPECT_EQ(bytes_, buf); + // Try to read again. + EXPECT_EQ(0, reader_->ReadSync(&buf[0], buf.size())); +} + +TEST_F(UploadFileElementReaderTest, ReadTooMuch) { + const size_t kTooLargeSize = bytes_.size() * 2; + std::vector<char> buf(kTooLargeSize); + EXPECT_EQ(static_cast<int>(bytes_.size()), + reader_->ReadSync(&buf[0], buf.size())); + EXPECT_EQ(0U, reader_->BytesRemaining()); + buf.resize(bytes_.size()); // Resize to compare. + EXPECT_EQ(bytes_, buf); +} + +TEST_F(UploadFileElementReaderTest, Range) { + const uint64 kOffset = 2; + const uint64 kLength = bytes_.size() - kOffset * 3; + reader_.reset(new UploadFileElementReader( + temp_file_path_, kOffset, kLength, base::Time())); + ASSERT_EQ(OK, reader_->InitSync()); + EXPECT_EQ(kLength, reader_->GetContentLength()); + EXPECT_EQ(kLength, reader_->BytesRemaining()); + std::vector<char> buf(kLength); + EXPECT_EQ(static_cast<int>(kLength), + reader_->ReadSync(&buf[0], kLength)); + const std::vector<char> expected(bytes_.begin() + kOffset, + bytes_.begin() + kOffset + kLength); + EXPECT_EQ(expected, buf); +} + +TEST_F(UploadFileElementReaderTest, FileChanged) { + base::PlatformFileInfo info; + ASSERT_TRUE(file_util::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 UploadFileElementReader( + temp_file_path_, 0, kuint64max, expected_modification_time)); + EXPECT_EQ(ERR_UPLOAD_FILE_CHANGED, reader_->InitSync()); +} + +TEST_F(UploadFileElementReaderTest, WrongPath) { + const FilePath wrong_path(FILE_PATH_LITERAL("wrong_path")); + reader_.reset(new UploadFileElementReader( + wrong_path, 0, kuint64max, base::Time())); + ASSERT_EQ(OK, reader_->InitSync()); + EXPECT_EQ(0U, reader_->GetContentLength()); + EXPECT_EQ(0U, reader_->BytesRemaining()); +} + +} // namespace net diff --git a/net/http/http_network_transaction_spdy2_unittest.cc b/net/http/http_network_transaction_spdy2_unittest.cc index 9bc9c2c..282a29aa 100644 --- a/net/http/http_network_transaction_spdy2_unittest.cc +++ b/net/http/http_network_transaction_spdy2_unittest.cc @@ -33,7 +33,7 @@ #include "net/base/ssl_config_service_defaults.h" #include "net/base/ssl_info.h" #include "net/base/test_completion_callback.h" -#include "net/base/upload_data.h" +#include "net/base/upload_file_element_reader.h" #include "net/http/http_auth_handler_digest.h" #include "net/http/http_auth_handler_mock.h" #include "net/http/http_auth_handler_ntlm.h" @@ -6496,11 +6496,12 @@ TEST_F(HttpNetworkTransactionSpdy2Test, UploadFileSmallerThanLength) { FilePath temp_file_path; ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path)); const uint64 kFakeSize = 100000; // file is actually blank + UploadFileElementReader::ScopedOverridingContentLengthForTests + overriding_content_length(kFakeSize); std::vector<UploadElement> elements; UploadElement element; element.SetToFilePath(temp_file_path); - element.SetContentLength(kFakeSize); elements.push_back(element); request.upload_data->SetElements(elements); @@ -6634,7 +6635,7 @@ TEST_F(HttpNetworkTransactionSpdy2Test, UnreadableUploadFileAfterAuthRestart) { MockWrite("POST /upload HTTP/1.1\r\n" "Host: www.google.com\r\n" "Connection: keep-alive\r\n" - "Content-Length: 16\r\n" + "Content-Length: 0\r\n" "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), MockWrite(SYNCHRONOUS, unreadable_contents.c_str(), temp_file_contents.length()), diff --git a/net/http/http_network_transaction_spdy3_unittest.cc b/net/http/http_network_transaction_spdy3_unittest.cc index 2ed5b28..21befa6 100644 --- a/net/http/http_network_transaction_spdy3_unittest.cc +++ b/net/http/http_network_transaction_spdy3_unittest.cc @@ -33,7 +33,7 @@ #include "net/base/ssl_config_service_defaults.h" #include "net/base/ssl_info.h" #include "net/base/test_completion_callback.h" -#include "net/base/upload_data.h" +#include "net/base/upload_file_element_reader.h" #include "net/http/http_auth_handler_digest.h" #include "net/http/http_auth_handler_mock.h" #include "net/http/http_auth_handler_ntlm.h" @@ -6496,11 +6496,12 @@ TEST_F(HttpNetworkTransactionSpdy3Test, UploadFileSmallerThanLength) { FilePath temp_file_path; ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path)); const uint64 kFakeSize = 100000; // file is actually blank + UploadFileElementReader::ScopedOverridingContentLengthForTests + overriding_content_length(kFakeSize); std::vector<UploadElement> elements; UploadElement element; element.SetToFilePath(temp_file_path); - element.SetContentLength(kFakeSize); elements.push_back(element); request.upload_data->SetElements(elements); @@ -6634,7 +6635,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, UnreadableUploadFileAfterAuthRestart) { MockWrite("POST /upload HTTP/1.1\r\n" "Host: www.google.com\r\n" "Connection: keep-alive\r\n" - "Content-Length: 16\r\n" + "Content-Length: 0\r\n" "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), MockWrite(SYNCHRONOUS, unreadable_contents.c_str(), temp_file_contents.length()), diff --git a/net/net.gyp b/net/net.gyp index 02ef211..c12d62f 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -276,12 +276,18 @@ 'base/transport_security_state_static.h', 'base/unix_domain_socket_posix.cc', 'base/unix_domain_socket_posix.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', 'base/upload_element.h', + 'base/upload_element_reader.cc', + 'base/upload_element_reader.h', + 'base/upload_file_element_reader.cc', + 'base/upload_file_element_reader.h', 'base/upload_progress.h', 'base/winsock_init.cc', 'base/winsock_init.h', @@ -1232,7 +1238,9 @@ 'base/test_completion_callback_unittest.cc', 'base/transport_security_state_unittest.cc', 'base/unix_domain_socket_posix_unittest.cc', + 'base/upload_bytes_element_reader_unittest.cc', 'base/upload_data_stream_unittest.cc', + 'base/upload_file_element_reader_unittest.cc', 'base/x509_certificate_unittest.cc', 'base/x509_cert_types_unittest.cc', 'base/x509_util_nss_unittest.cc', |