summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-11 20:27:21 +0000
committerhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-11 20:27:21 +0000
commitd989616505f0195fdd1d646160fe080a6dfad492 (patch)
tree07ed24b733d004155f5d457f8943c43b8afb6a0f
parent12194a0bd01d99873ea1abdcc9d8b9c838c08bcf (diff)
downloadchromium_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.cc54
-rw-r--r--net/base/upload_bytes_element_reader.h36
-rw-r--r--net/base/upload_bytes_element_reader_unittest.cc61
-rw-r--r--net/base/upload_data_stream.cc83
-rw-r--r--net/base/upload_data_stream.h4
-rw-r--r--net/base/upload_data_stream_unittest.cc5
-rw-r--r--net/base/upload_element.cc157
-rw-r--r--net/base/upload_element.h57
-rw-r--r--net/base/upload_element_reader.cc38
-rw-r--r--net/base/upload_element_reader.h50
-rw-r--r--net/base/upload_file_element_reader.cc142
-rw-r--r--net/base/upload_file_element_reader.h63
-rw-r--r--net/base/upload_file_element_reader_unittest.cc110
-rw-r--r--net/http/http_network_transaction_spdy2_unittest.cc7
-rw-r--r--net/http/http_network_transaction_spdy3_unittest.cc7
-rw-r--r--net/net.gyp8
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',