| // Copyright 2017 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 "services/network/chunked_data_pipe_upload_data_stream.h" |
| |
| #include <stdint.h> |
| |
| #include <limits> |
| #include <memory> |
| |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "mojo/public/cpp/system/data_pipe.h" |
| #include "mojo/public/cpp/system/data_pipe_utils.h" |
| #include "net/base/completion_once_callback.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/test_completion_callback.h" |
| #include "net/log/net_log_with_source.h" |
| #include "services/network/public/cpp/resource_request_body.h" |
| #include "services/network/public/mojom/chunked_data_pipe_getter.mojom.h" |
| #include "services/network/test_chunked_data_pipe_getter.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| // Most tests of this class are at the URLLoader layer. These tests focus on |
| // things too difficult to cover with integration tests. |
| |
| namespace network { |
| |
| namespace { |
| |
| class ChunkedDataPipeUploadDataStreamTest : public testing::Test { |
| public: |
| ChunkedDataPipeUploadDataStreamTest() { CreateAndInitChunkedUploadStream(); } |
| |
| void CreateAndInitChunkedUploadStream() { |
| chunked_data_pipe_getter_ = std::make_unique<TestChunkedDataPipeGetter>(); |
| chunked_upload_stream_ = std::make_unique<ChunkedDataPipeUploadDataStream>( |
| base::MakeRefCounted<network::ResourceRequestBody>(), |
| chunked_data_pipe_getter_->GetDataPipeGetterPtr()); |
| // Nothing interesting happens before Init, so always wait for it in the |
| // test fixture. |
| net::TestCompletionCallback callback; |
| EXPECT_EQ(net::OK, chunked_upload_stream_->Init(callback.callback(), |
| net::NetLogWithSource())); |
| get_size_callback_ = chunked_data_pipe_getter_->WaitForGetSize(); |
| write_pipe_ = chunked_data_pipe_getter_->WaitForStartReading(); |
| |
| EXPECT_TRUE(chunked_upload_stream_->is_chunked()); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| EXPECT_FALSE(chunked_upload_stream_->IsInMemory()); |
| } |
| |
| protected: |
| base::MessageLoopForIO message_loop_; |
| std::unique_ptr<TestChunkedDataPipeGetter> chunked_data_pipe_getter_; |
| std::unique_ptr<ChunkedDataPipeUploadDataStream> chunked_upload_stream_; |
| |
| mojom::ChunkedDataPipeGetter::GetSizeCallback get_size_callback_; |
| mojo::ScopedDataPipeProducerHandle write_pipe_; |
| }; |
| |
| // Test just reading through the response body once, where reads are initiated |
| // before data is received. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, ReadBeforeDataReady) { |
| const std::string kData = "1234567890"; |
| |
| for (int consumer_read_size : {5, 10, 20}) { |
| for (int num_writes : {0, 1, 2}) { |
| CreateAndInitChunkedUploadStream(); |
| |
| for (int i = 0; i < num_writes; ++i) { |
| std::string read_data; |
| while (read_data.size() < kData.size()) { |
| net::TestCompletionCallback callback; |
| auto io_buffer = |
| base::MakeRefCounted<net::IOBufferWithSize>(consumer_read_size); |
| int result = chunked_upload_stream_->Read( |
| io_buffer.get(), io_buffer->size(), callback.callback()); |
| if (read_data.size() == 0) |
| mojo::BlockingCopyFromString(kData, write_pipe_); |
| result = callback.GetResult(result); |
| ASSERT_LT(0, result); |
| EXPECT_LE(result, consumer_read_size); |
| read_data.append(std::string(io_buffer->data(), result)); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| } |
| EXPECT_EQ(read_data, kData); |
| } |
| |
| auto io_buffer = |
| base::MakeRefCounted<net::IOBufferWithSize>(consumer_read_size); |
| net::TestCompletionCallback callback; |
| int result = chunked_upload_stream_->Read( |
| io_buffer.get(), io_buffer->size(), callback.callback()); |
| EXPECT_EQ(net::ERR_IO_PENDING, result); |
| |
| std::move(get_size_callback_).Run(net::OK, num_writes * kData.size()); |
| EXPECT_EQ(net::OK, callback.GetResult(result)); |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| } |
| } |
| } |
| |
| // Test just reading through the response body once, where reads are initiated |
| // after data has been received. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, ReadAfterDataReady) { |
| const std::string kData = "1234567890"; |
| |
| for (int consumer_read_size : {5, 10, 20}) { |
| for (int num_writes : {0, 1, 2}) { |
| CreateAndInitChunkedUploadStream(); |
| |
| for (int i = 0; i < num_writes; ++i) { |
| mojo::BlockingCopyFromString(kData, write_pipe_); |
| base::RunLoop().RunUntilIdle(); |
| |
| std::string read_data; |
| while (read_data.size() < kData.size()) { |
| net::TestCompletionCallback callback; |
| auto io_buffer = |
| base::MakeRefCounted<net::IOBufferWithSize>(consumer_read_size); |
| int result = chunked_upload_stream_->Read( |
| io_buffer.get(), io_buffer->size(), callback.callback()); |
| ASSERT_LT(0, result); |
| EXPECT_LE(result, consumer_read_size); |
| read_data.append(std::string(io_buffer->data(), result)); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| } |
| EXPECT_EQ(read_data, kData); |
| } |
| |
| std::move(get_size_callback_).Run(net::OK, num_writes * kData.size()); |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = |
| base::MakeRefCounted<net::IOBufferWithSize>(consumer_read_size); |
| EXPECT_EQ(net::OK, |
| chunked_upload_stream_->Read(io_buffer.get(), io_buffer->size(), |
| callback.callback())); |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| } |
| } |
| } |
| |
| // Test the case where the URLRequest reads through the request body multiple |
| // times, as can happen in the case of redirects or retries. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, MultipleReadThrough) { |
| const std::string kData = "1234567890"; |
| |
| // Send size up front - cases where the size isn't initially known are checked |
| // elsewhere, and have to have size before the first read through can complete |
| // successfully. |
| std::move(get_size_callback_).Run(net::OK, kData.size()); |
| base::RunLoop().RunUntilIdle(); |
| |
| // 3 is an arbitrary number greater than 1. |
| for (int i = 0; i < 3; i++) { |
| // Already initialized before loop runs for the first time. |
| if (i != 0) { |
| net::TestCompletionCallback callback; |
| EXPECT_EQ(net::OK, chunked_upload_stream_->Init(callback.callback(), |
| net::NetLogWithSource())); |
| |
| write_pipe_ = chunked_data_pipe_getter_->WaitForStartReading(); |
| } |
| |
| mojo::BlockingCopyFromString(kData, write_pipe_); |
| |
| std::string read_data; |
| while (read_data.size() < kData.size()) { |
| net::TestCompletionCallback callback; |
| auto io_buffer = |
| base::MakeRefCounted<net::IOBufferWithSize>(kData.size()); |
| int result = chunked_upload_stream_->Read( |
| io_buffer.get(), io_buffer->size(), callback.callback()); |
| result = callback.GetResult(result); |
| ASSERT_LT(0, result); |
| EXPECT_LE(static_cast<size_t>(result), kData.size()); |
| read_data.append(std::string(io_buffer->data(), result)); |
| if (read_data.size() == kData.size()) { |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| } else { |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| } |
| } |
| EXPECT_EQ(kData, read_data); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(net::OK, callback.GetResult(result)); |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| |
| chunked_upload_stream_->Reset(); |
| } |
| } |
| |
| // Test the case where the URLRequest partially reads through the request body |
| // multiple times, as can happen in the case of retries. The size is known from |
| // the start. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, |
| MultiplePartialReadThroughWithKnownSize) { |
| const std::string kData = "1234567890"; |
| |
| std::move(get_size_callback_).Run(net::OK, kData.size()); |
| // Wait for the stream to learn the size, to avoid any races. |
| base::RunLoop().RunUntilIdle(); |
| |
| // In each iteration, read through more of the body. Reset the stream after |
| // each loop iteration but the last, which reads the entire body. |
| for (size_t num_bytes_to_read = 0; num_bytes_to_read <= kData.size(); |
| num_bytes_to_read++) { |
| // Already initialized before loop runs for the first time. |
| if (num_bytes_to_read != 0) { |
| net::TestCompletionCallback callback; |
| EXPECT_EQ(net::OK, chunked_upload_stream_->Init(callback.callback(), |
| net::NetLogWithSource())); |
| |
| write_pipe_ = chunked_data_pipe_getter_->WaitForStartReading(); |
| } |
| |
| mojo::BlockingCopyFromString(kData.substr(0, num_bytes_to_read), |
| write_pipe_); |
| |
| std::string read_data; |
| while (read_data.size() < num_bytes_to_read) { |
| net::TestCompletionCallback callback; |
| auto io_buffer = |
| base::MakeRefCounted<net::IOBufferWithSize>(kData.size()); |
| int result = chunked_upload_stream_->Read( |
| io_buffer.get(), io_buffer->size(), callback.callback()); |
| result = callback.GetResult(result); |
| ASSERT_LT(0, result); |
| EXPECT_LE(static_cast<size_t>(result), kData.size()); |
| read_data.append(std::string(io_buffer->data(), result)); |
| |
| if (read_data.size() == kData.size()) { |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| } else { |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| } |
| } |
| EXPECT_EQ(kData.substr(0, num_bytes_to_read), read_data); |
| if (num_bytes_to_read != kData.size()) |
| chunked_upload_stream_->Reset(); |
| } |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(net::OK, callback.GetResult(result)); |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| } |
| |
| // Test the case where the URLRequest partially reads through the request body |
| // multiple times, as can happen in the case of retries. The size isn't known |
| // until the end. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, |
| MultiplePartialReadThroughSizeNotKnown) { |
| const std::string kData = "1234567890"; |
| |
| // In each iteration, read through more of the body. Reset the stream after |
| // each loop iteration but the last, which reads the entire body. |
| for (size_t num_bytes_to_read = 0; num_bytes_to_read <= kData.size(); |
| num_bytes_to_read++) { |
| // Already initialized before loop runs for the first time. |
| if (num_bytes_to_read != 0) { |
| net::TestCompletionCallback callback; |
| EXPECT_EQ(net::OK, chunked_upload_stream_->Init(callback.callback(), |
| net::NetLogWithSource())); |
| |
| write_pipe_ = chunked_data_pipe_getter_->WaitForStartReading(); |
| } |
| |
| mojo::BlockingCopyFromString(kData.substr(0, num_bytes_to_read), |
| write_pipe_); |
| |
| std::string read_data; |
| while (read_data.size() < num_bytes_to_read) { |
| net::TestCompletionCallback callback; |
| auto io_buffer = |
| base::MakeRefCounted<net::IOBufferWithSize>(kData.size()); |
| int result = chunked_upload_stream_->Read( |
| io_buffer.get(), io_buffer->size(), callback.callback()); |
| result = callback.GetResult(result); |
| ASSERT_LT(0, result); |
| EXPECT_LE(static_cast<size_t>(result), kData.size()); |
| read_data.append(std::string(io_buffer->data(), result)); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| } |
| EXPECT_EQ(kData.substr(0, num_bytes_to_read), read_data); |
| if (num_bytes_to_read != kData.size()) |
| chunked_upload_stream_->Reset(); |
| } |
| |
| std::move(get_size_callback_).Run(net::OK, kData.size()); |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(net::OK, callback.GetResult(result)); |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| } |
| |
| // Test where GetSize() is invoked before the upload is initialized. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, GetSizeSucceedsBeforeInit) { |
| const std::string kData = "1234567890"; |
| |
| chunked_data_pipe_getter_ = std::make_unique<TestChunkedDataPipeGetter>(); |
| chunked_upload_stream_ = std::make_unique<ChunkedDataPipeUploadDataStream>( |
| base::MakeRefCounted<network::ResourceRequestBody>(), |
| chunked_data_pipe_getter_->GetDataPipeGetterPtr()); |
| get_size_callback_ = chunked_data_pipe_getter_->WaitForGetSize(); |
| std::move(get_size_callback_).Run(net::OK, kData.size()); |
| // Wait for the ChunkedUploadStream to receive the size. |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestCompletionCallback callback; |
| ASSERT_EQ(net::OK, chunked_upload_stream_->Init(callback.callback(), |
| net::NetLogWithSource())); |
| write_pipe_ = chunked_data_pipe_getter_->WaitForStartReading(); |
| |
| EXPECT_TRUE(chunked_upload_stream_->is_chunked()); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| |
| mojo::BlockingCopyFromString(kData, write_pipe_); |
| |
| std::string read_data; |
| while (read_data.size() < kData.size()) { |
| net::TestCompletionCallback callback; |
| int read_size = kData.size() - read_data.size(); |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(read_size); |
| int result = chunked_upload_stream_->Read( |
| io_buffer.get(), io_buffer->size(), callback.callback()); |
| result = callback.GetResult(result); |
| ASSERT_LT(0, result); |
| EXPECT_LE(result, read_size); |
| read_data.append(std::string(io_buffer->data(), result)); |
| EXPECT_EQ(read_data.size() == kData.size(), |
| chunked_upload_stream_->IsEOF()); |
| } |
| } |
| |
| // Test where GetSize() is only invoked after the upload is reset. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, GetSizeSucceedsAfterReset) { |
| const std::string kData = "1234567890"; |
| |
| // Read through the body once, without a size |
| std::string read_data; |
| mojo::BlockingCopyFromString(kData, write_pipe_); |
| while (read_data.size() < kData.size()) { |
| net::TestCompletionCallback callback; |
| int read_size = kData.size() - read_data.size(); |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(read_size); |
| int result = chunked_upload_stream_->Read( |
| io_buffer.get(), io_buffer->size(), callback.callback()); |
| result = callback.GetResult(result); |
| ASSERT_LT(0, result); |
| EXPECT_LE(result, read_size); |
| read_data.append(std::string(io_buffer->data(), result)); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| } |
| |
| // Reset, get the size, and read through the body again. |
| chunked_upload_stream_->Reset(); |
| |
| std::move(get_size_callback_).Run(net::OK, kData.size()); |
| // Wait for the ChunkedUploadStream to receive the size. |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestCompletionCallback callback; |
| ASSERT_EQ(net::OK, chunked_upload_stream_->Init(callback.callback(), |
| net::NetLogWithSource())); |
| write_pipe_ = chunked_data_pipe_getter_->WaitForStartReading(); |
| |
| read_data.erase(); |
| mojo::BlockingCopyFromString(kData, write_pipe_); |
| while (read_data.size() < kData.size()) { |
| net::TestCompletionCallback callback; |
| int read_size = kData.size() - read_data.size(); |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(read_size); |
| int result = chunked_upload_stream_->Read( |
| io_buffer.get(), io_buffer->size(), callback.callback()); |
| result = callback.GetResult(result); |
| ASSERT_LT(0, result); |
| EXPECT_LE(result, read_size); |
| read_data.append(std::string(io_buffer->data(), result)); |
| EXPECT_EQ(read_data.size() == kData.size(), |
| chunked_upload_stream_->IsEOF()); |
| } |
| } |
| |
| // Test where GetSize() is invoked with an error before the upload is |
| // initialized. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, GetSizeFailsBeforeInit) { |
| const std::string kData = "1234567890"; |
| |
| chunked_data_pipe_getter_ = std::make_unique<TestChunkedDataPipeGetter>(); |
| chunked_upload_stream_ = std::make_unique<ChunkedDataPipeUploadDataStream>( |
| base::MakeRefCounted<network::ResourceRequestBody>(), |
| chunked_data_pipe_getter_->GetDataPipeGetterPtr()); |
| get_size_callback_ = chunked_data_pipe_getter_->WaitForGetSize(); |
| std::move(get_size_callback_).Run(net::ERR_ACCESS_DENIED, 0); |
| // Wait for the ChunkedUploadStream to receive the size. |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestCompletionCallback callback; |
| EXPECT_EQ(net::ERR_ACCESS_DENIED, |
| chunked_upload_stream_->Init(callback.callback(), |
| net::NetLogWithSource())); |
| } |
| |
| // Test where GetSize() is only invoked with an error after the upload is reset. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, GetSizeFailsAfterReset) { |
| const std::string kData = "1234567890"; |
| |
| // Read through the body once, without a size |
| std::string read_data; |
| mojo::BlockingCopyFromString(kData, write_pipe_); |
| while (read_data.size() < kData.size()) { |
| net::TestCompletionCallback callback; |
| int read_size = kData.size() - read_data.size(); |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(read_size); |
| int result = chunked_upload_stream_->Read( |
| io_buffer.get(), io_buffer->size(), callback.callback()); |
| result = callback.GetResult(result); |
| ASSERT_LT(0, result); |
| EXPECT_LE(result, read_size); |
| read_data.append(std::string(io_buffer->data(), result)); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| } |
| |
| // Reset, get the size, and read through the body again. |
| chunked_upload_stream_->Reset(); |
| |
| std::move(get_size_callback_).Run(net::ERR_ACCESS_DENIED, kData.size()); |
| // Wait for the ChunkedUploadStream to receive the size. |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestCompletionCallback callback; |
| ASSERT_EQ(net::ERR_ACCESS_DENIED, |
| chunked_upload_stream_->Init(callback.callback(), |
| net::NetLogWithSource())); |
| } |
| |
| // Three variations on when the stream can be closed before a request succeeds. |
| |
| // Stream is closed, then a read attempted, then the GetSizeCallback is invoked. |
| // The read should notice the close body pipe, but not report anything until the |
| // GetSizeCallback is invoked. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeSuccess1) { |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(result, net::ERR_IO_PENDING); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| |
| std::move(get_size_callback_).Run(net::OK, 0); |
| EXPECT_EQ(net::OK, callback.GetResult(result)); |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| } |
| |
| // A read attempt, stream is closed, then the GetSizeCallback is invoked. The |
| // watcher should see the close, but not report anything until the |
| // GetSizeCallback is invoked. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeSuccess2) { |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(result, net::ERR_IO_PENDING); |
| |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(callback.have_result()); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| |
| std::move(get_size_callback_).Run(net::OK, 0); |
| EXPECT_EQ(net::OK, callback.GetResult(result)); |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| } |
| |
| // The stream is closed, the GetSizeCallback is invoked, and then a read |
| // attempt. The read attempt should notice the request already successfully |
| // completed. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeSuccess3) { |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| std::move(get_size_callback_).Run(net::OK, 0); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(net::OK, callback.GetResult(result)); |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| } |
| |
| // Same cases as above, but the GetSizeCallback indicates the response was |
| // truncated. |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeTruncation1) { |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(result, net::ERR_IO_PENDING); |
| |
| std::move(get_size_callback_).Run(net::OK, 1); |
| EXPECT_EQ(net::ERR_FAILED, callback.GetResult(result)); |
| } |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeTruncation2) { |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(result, net::ERR_IO_PENDING); |
| |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(callback.have_result()); |
| |
| std::move(get_size_callback_).Run(net::OK, 1); |
| EXPECT_EQ(net::ERR_FAILED, callback.GetResult(result)); |
| } |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeTruncation3) { |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| std::move(get_size_callback_).Run(net::OK, 1); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(net::ERR_FAILED, callback.GetResult(result)); |
| } |
| |
| // Same cases as above, but the GetSizeCallback indicates the read failed. |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeFailure1) { |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(result, net::ERR_IO_PENDING); |
| |
| std::move(get_size_callback_).Run(net::ERR_ACCESS_DENIED, 0); |
| EXPECT_EQ(net::ERR_ACCESS_DENIED, callback.GetResult(result)); |
| } |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeFailure2) { |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(result, net::ERR_IO_PENDING); |
| |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(callback.have_result()); |
| |
| std::move(get_size_callback_).Run(net::ERR_ACCESS_DENIED, 0); |
| EXPECT_EQ(net::ERR_ACCESS_DENIED, callback.GetResult(result)); |
| } |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeFailure3) { |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| std::move(get_size_callback_).Run(net::ERR_ACCESS_DENIED, 0); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(net::ERR_ACCESS_DENIED, callback.GetResult(result)); |
| } |
| |
| // Same cases as above, but ChunkedBodyGetter pipe is destroyed without invoking |
| // the GetSizeCallback. |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeCloseGetter1) { |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(result, net::ERR_IO_PENDING); |
| |
| chunked_data_pipe_getter_.reset(); |
| EXPECT_EQ(net::ERR_FAILED, callback.GetResult(result)); |
| } |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeCloseGetter2) { |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(result, net::ERR_IO_PENDING); |
| |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(callback.have_result()); |
| |
| chunked_data_pipe_getter_.reset(); |
| EXPECT_EQ(net::ERR_FAILED, callback.GetResult(result)); |
| } |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, CloseBodyPipeBeforeCloseGetter3) { |
| write_pipe_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| chunked_data_pipe_getter_.reset(); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(net::ERR_FAILED, callback.GetResult(result)); |
| } |
| |
| // Test uses same order as CloseBodyPipeBeforeTruncation1, but in this test the |
| // GetSizeCallback indicates too many bytes were received. |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, ExtraBytes1) { |
| const std::string kData = "123"; |
| mojo::BlockingCopyFromString(kData, write_pipe_); |
| |
| std::string read_data; |
| while (read_data.size() < kData.size()) { |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(kData.size()); |
| int result = chunked_upload_stream_->Read( |
| io_buffer.get(), io_buffer->size(), callback.callback()); |
| result = callback.GetResult(result); |
| ASSERT_LT(0, result); |
| read_data.append(std::string(io_buffer->data(), result)); |
| EXPECT_LE(read_data.size(), kData.size()); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| } |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(result, net::ERR_IO_PENDING); |
| |
| std::move(get_size_callback_).Run(net::OK, kData.size() - 1); |
| EXPECT_EQ(net::ERR_FAILED, callback.GetResult(result)); |
| } |
| |
| // Extra bytes are received after getting the size notification. No error should |
| // be reported, |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, ExtraBytes2) { |
| const std::string kData = "123"; |
| |
| // Read first byte. |
| mojo::BlockingCopyFromString(kData.substr(0, 1), write_pipe_); |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = chunked_upload_stream_->Read(io_buffer.get(), io_buffer->size(), |
| callback.callback()); |
| result = callback.GetResult(result); |
| ASSERT_EQ(1, result); |
| EXPECT_FALSE(chunked_upload_stream_->IsEOF()); |
| |
| // Start another read. |
| ASSERT_EQ(net::ERR_IO_PENDING, chunked_upload_stream_->Read( |
| io_buffer.get(), 1, callback.callback())); |
| |
| // Learn the size was only one byte. Read should complete, indicating the end |
| // of the stream was reached. |
| std::move(get_size_callback_).Run(net::OK, 1); |
| EXPECT_EQ(net::OK, callback.WaitForResult()); |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| |
| // More data is copied to the stream unexpectedly. It should be ignored. |
| mojo::BlockingCopyFromString(kData.substr(1), write_pipe_); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| } |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, ClosePipeGetterBeforeInit) { |
| chunked_data_pipe_getter_ = std::make_unique<TestChunkedDataPipeGetter>(); |
| chunked_upload_stream_ = std::make_unique<ChunkedDataPipeUploadDataStream>( |
| base::MakeRefCounted<network::ResourceRequestBody>(), |
| chunked_data_pipe_getter_->GetDataPipeGetterPtr()); |
| |
| // Destroy the DataPipeGetter pipe, which is the pipe used for |
| // GetSizeCallback. |
| chunked_data_pipe_getter_->ClosePipe(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Init should fail in this case. |
| net::TestCompletionCallback callback; |
| EXPECT_EQ(net::ERR_FAILED, chunked_upload_stream_->Init( |
| callback.callback(), net::NetLogWithSource())); |
| } |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, |
| ClosePipeGetterWithoutCallingGetSizeCallbackNoPendingRead) { |
| // Destroy the DataPipeGetter pipe, which is the pipe used for |
| // GetSizeCallback. |
| chunked_data_pipe_getter_->ClosePipe(); |
| base::RunLoop().RunUntilIdle(); |
| |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(net::ERR_FAILED, callback.GetResult(result)); |
| } |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, |
| ClosePipeGetterWithoutCallingGetSizeCallbackPendingRead) { |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(1); |
| int result = |
| chunked_upload_stream_->Read(io_buffer.get(), 1, callback.callback()); |
| EXPECT_EQ(net::ERR_IO_PENDING, result); |
| |
| // Destroy the DataPipeGetter pipe, which is the pipe used for |
| // GetSizeCallback. |
| chunked_data_pipe_getter_->ClosePipe(); |
| EXPECT_EQ(net::ERR_FAILED, callback.GetResult(result)); |
| } |
| |
| TEST_F(ChunkedDataPipeUploadDataStreamTest, |
| ClosePipeGetterAfterCallingGetSizeCallback) { |
| const char kData[] = "1"; |
| const int kDataLen = strlen(kData); |
| net::TestCompletionCallback callback; |
| auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(kDataLen); |
| std::move(get_size_callback_).Run(net::OK, kDataLen); |
| // Destroy the DataPipeGetter pipe, which is the pipe used for |
| // GetSizeCallback. |
| chunked_data_pipe_getter_->ClosePipe(); |
| base::RunLoop().RunUntilIdle(); |
| |
| int result = chunked_upload_stream_->Read(io_buffer.get(), kDataLen, |
| callback.callback()); |
| EXPECT_EQ(net::ERR_IO_PENDING, result); |
| mojo::BlockingCopyFromString(kData, write_pipe_); |
| |
| // Since the pipe was closed the GetSizeCallback was invoked, reading the |
| // upload body should succeed once. |
| EXPECT_EQ(kDataLen, callback.GetResult(result)); |
| EXPECT_TRUE(chunked_upload_stream_->IsEOF()); |
| EXPECT_EQ(0, chunked_upload_stream_->Read(io_buffer.get(), kDataLen, |
| callback.callback())); |
| |
| // But trying again will result in failure. |
| chunked_upload_stream_->Reset(); |
| EXPECT_EQ(net::ERR_FAILED, chunked_upload_stream_->Init( |
| callback.callback(), net::NetLogWithSource())); |
| } |
| |
| } // namespace |
| |
| } // namespace network |