|  | // Copyright 2014 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/chunked_upload_data_stream.h" | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include "base/memory/scoped_ptr.h" | 
|  | #include "net/base/io_buffer.h" | 
|  | #include "net/base/net_errors.h" | 
|  | #include "net/base/test_completion_callback.h" | 
|  | #include "net/base/upload_data_stream.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kTestData[] = "0123456789"; | 
|  | const size_t kTestDataSize = arraysize(kTestData) - 1; | 
|  | const size_t kTestBufferSize = 1 << 14;  // 16KB. | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Reads data once from the upload data stream, and returns the data as string. | 
|  | // Expects the read to succeed synchronously. | 
|  | std::string ReadSync(UploadDataStream* stream, int buffer_size) { | 
|  | scoped_refptr<IOBuffer> buf = new IOBuffer(buffer_size); | 
|  | int result = stream->Read(buf.get(), | 
|  | buffer_size, | 
|  | TestCompletionCallback().callback()); | 
|  | EXPECT_GE(result, 0); | 
|  | return std::string(buf->data(), result); | 
|  | } | 
|  |  | 
|  | // Check the case data is added after the first read attempt. | 
|  | TEST(ChunkedUploadDataStreamTest, AppendOnce) { | 
|  | ChunkedUploadDataStream stream(0); | 
|  |  | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | TestCompletionCallback callback; | 
|  | scoped_refptr<IOBuffer> buf = new IOBuffer(kTestBufferSize); | 
|  | int result = stream.Read(buf.get(), kTestBufferSize, callback.callback()); | 
|  | ASSERT_EQ(ERR_IO_PENDING, result); | 
|  |  | 
|  | stream.AppendData(kTestData, kTestDataSize, true); | 
|  | int read = callback.WaitForResult(); | 
|  | ASSERT_GE(read, 0); | 
|  | EXPECT_EQ(kTestData, std::string(buf->data(), read)); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(kTestDataSize, stream.position()); | 
|  | EXPECT_TRUE(stream.IsEOF()); | 
|  | } | 
|  |  | 
|  | TEST(ChunkedUploadDataStreamTest, AppendOnceBeforeRead) { | 
|  | ChunkedUploadDataStream stream(0); | 
|  |  | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | stream.AppendData(kTestData, kTestDataSize, true); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | std::string data = ReadSync(&stream, kTestBufferSize); | 
|  | EXPECT_EQ(kTestData, data); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(kTestDataSize, stream.position()); | 
|  | EXPECT_TRUE(stream.IsEOF()); | 
|  | } | 
|  |  | 
|  | TEST(ChunkedUploadDataStreamTest, AppendOnceBeforeInit) { | 
|  | ChunkedUploadDataStream stream(0); | 
|  |  | 
|  | stream.AppendData(kTestData, kTestDataSize, true); | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | std::string data = ReadSync(&stream, kTestBufferSize); | 
|  | EXPECT_EQ(kTestData, data); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(kTestDataSize, stream.position()); | 
|  | EXPECT_TRUE(stream.IsEOF()); | 
|  | } | 
|  |  | 
|  | TEST(ChunkedUploadDataStreamTest, MultipleAppends) { | 
|  | ChunkedUploadDataStream stream(0); | 
|  |  | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size()); | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | TestCompletionCallback callback; | 
|  | scoped_refptr<IOBuffer> buf = new IOBuffer(kTestBufferSize); | 
|  | for (size_t i = 0; i < kTestDataSize; ++i) { | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(i, stream.position()); | 
|  | ASSERT_FALSE(stream.IsEOF()); | 
|  | int bytes_read = stream.Read(buf.get(), | 
|  | kTestBufferSize, | 
|  | callback.callback()); | 
|  | ASSERT_EQ(ERR_IO_PENDING, bytes_read); | 
|  | stream.AppendData(&kTestData[i], 1, i == kTestDataSize - 1); | 
|  | ASSERT_EQ(1, callback.WaitForResult()); | 
|  | EXPECT_EQ(kTestData[i], buf->data()[0]); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(kTestDataSize, stream.position()); | 
|  | ASSERT_TRUE(stream.IsEOF()); | 
|  | } | 
|  |  | 
|  | TEST(ChunkedUploadDataStreamTest, MultipleAppendsBetweenReads) { | 
|  | ChunkedUploadDataStream stream(0); | 
|  |  | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | scoped_refptr<IOBuffer> buf = new IOBuffer(kTestBufferSize); | 
|  | for (size_t i = 0; i < kTestDataSize; ++i) { | 
|  | EXPECT_EQ(i, stream.position()); | 
|  | ASSERT_FALSE(stream.IsEOF()); | 
|  | stream.AppendData(&kTestData[i], 1, i == kTestDataSize - 1); | 
|  | int bytes_read = stream.Read(buf.get(), | 
|  | kTestBufferSize, | 
|  | TestCompletionCallback().callback()); | 
|  | ASSERT_EQ(1, bytes_read); | 
|  | EXPECT_EQ(kTestData[i], buf->data()[0]); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(kTestDataSize, stream.position()); | 
|  | ASSERT_TRUE(stream.IsEOF()); | 
|  | } | 
|  |  | 
|  | // Checks that multiple reads can be merged. | 
|  | TEST(ChunkedUploadDataStreamTest, MultipleAppendsBeforeInit) { | 
|  | ChunkedUploadDataStream stream(0); | 
|  | stream.AppendData(kTestData, 1, false); | 
|  | stream.AppendData(kTestData + 1, 1, false); | 
|  | stream.AppendData(kTestData + 2, kTestDataSize - 2, true); | 
|  |  | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | std::string data = ReadSync(&stream, kTestBufferSize); | 
|  | EXPECT_EQ(kTestData, data); | 
|  | EXPECT_EQ(kTestDataSize, stream.position()); | 
|  | ASSERT_TRUE(stream.IsEOF()); | 
|  | } | 
|  |  | 
|  | TEST(ChunkedUploadDataStreamTest, MultipleReads) { | 
|  | // Use a read size different from the write size to test bounds checking. | 
|  | const size_t kReadSize = kTestDataSize + 3; | 
|  |  | 
|  | ChunkedUploadDataStream stream(0); | 
|  | stream.AppendData(kTestData, kTestDataSize, false); | 
|  | stream.AppendData(kTestData, kTestDataSize, false); | 
|  | stream.AppendData(kTestData, kTestDataSize, false); | 
|  | stream.AppendData(kTestData, kTestDataSize, true); | 
|  |  | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | std::string data = ReadSync(&stream, kReadSize); | 
|  | EXPECT_EQ("0123456789012", data); | 
|  | EXPECT_EQ(kReadSize, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | data = ReadSync(&stream, kReadSize); | 
|  | EXPECT_EQ("3456789012345", data); | 
|  | EXPECT_EQ(2 * kReadSize, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | data = ReadSync(&stream, kReadSize); | 
|  | EXPECT_EQ("6789012345678", data); | 
|  | EXPECT_EQ(3 * kReadSize, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | data = ReadSync(&stream, kReadSize); | 
|  | EXPECT_EQ("9", data); | 
|  | EXPECT_EQ(4 * kTestDataSize, stream.position()); | 
|  | EXPECT_TRUE(stream.IsEOF()); | 
|  | } | 
|  |  | 
|  | TEST(ChunkedUploadDataStreamTest, EmptyUpload) { | 
|  | ChunkedUploadDataStream stream(0); | 
|  |  | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | TestCompletionCallback callback; | 
|  | scoped_refptr<IOBuffer> buf = new IOBuffer(kTestBufferSize); | 
|  | int result = stream.Read(buf.get(), kTestBufferSize, callback.callback()); | 
|  | ASSERT_EQ(ERR_IO_PENDING, result); | 
|  |  | 
|  | stream.AppendData(NULL, 0, true); | 
|  | int read = callback.WaitForResult(); | 
|  | EXPECT_EQ(0, read); | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_TRUE(stream.IsEOF()); | 
|  | } | 
|  |  | 
|  | TEST(ChunkedUploadDataStreamTest, EmptyUploadEndedBeforeInit) { | 
|  | ChunkedUploadDataStream stream(0); | 
|  | stream.AppendData(NULL, 0, true); | 
|  |  | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | std::string data = ReadSync(&stream, kTestBufferSize); | 
|  | ASSERT_EQ("", data); | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_TRUE(stream.IsEOF()); | 
|  | } | 
|  |  | 
|  | TEST(ChunkedUploadDataStreamTest, RewindAfterComplete) { | 
|  | ChunkedUploadDataStream stream(0); | 
|  | stream.AppendData(kTestData, 1, false); | 
|  | stream.AppendData(kTestData + 1, kTestDataSize - 1, true); | 
|  |  | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | std::string data = ReadSync(&stream, kTestBufferSize); | 
|  | EXPECT_EQ(kTestData, data); | 
|  | EXPECT_EQ(kTestDataSize, stream.position()); | 
|  | ASSERT_TRUE(stream.IsEOF()); | 
|  |  | 
|  | // Rewind stream and repeat. | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | data = ReadSync(&stream, kTestBufferSize); | 
|  | EXPECT_EQ(kTestData, data); | 
|  | EXPECT_EQ(kTestDataSize, stream.position()); | 
|  | ASSERT_TRUE(stream.IsEOF()); | 
|  | } | 
|  |  | 
|  | TEST(ChunkedUploadDataStreamTest, RewindWhileReading) { | 
|  | ChunkedUploadDataStream stream(0); | 
|  |  | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | TestCompletionCallback callback; | 
|  | scoped_refptr<IOBuffer> buf = new IOBuffer(kTestBufferSize); | 
|  | int result = stream.Read(buf.get(), kTestBufferSize, callback.callback()); | 
|  | ASSERT_EQ(ERR_IO_PENDING, result); | 
|  |  | 
|  | ASSERT_EQ(OK, stream.Init(TestCompletionCallback().callback())); | 
|  | EXPECT_FALSE(stream.IsInMemory()); | 
|  | EXPECT_EQ(0u, stream.size());  // Content-Length is 0 for chunked data. | 
|  | EXPECT_EQ(0u, stream.position()); | 
|  | EXPECT_FALSE(stream.IsEOF()); | 
|  |  | 
|  | // Adding data now should not result in calling the original read callback, | 
|  | // since the stream was re-initialized for reuse, which cancels all pending | 
|  | // reads. | 
|  | stream.AppendData(kTestData, kTestDataSize, true); | 
|  | EXPECT_FALSE(callback.have_result()); | 
|  |  | 
|  | std::string data = ReadSync(&stream, kTestBufferSize); | 
|  | EXPECT_EQ(kTestData, data); | 
|  | EXPECT_EQ(kTestDataSize, stream.position()); | 
|  | ASSERT_TRUE(stream.IsEOF()); | 
|  | EXPECT_FALSE(callback.have_result()); | 
|  | } | 
|  |  | 
|  | }  // namespace net |