blob: d484bc6a65bb8032100f85cae67a13c41c644c27 [file]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/socket_data_pump.h"
#include <stdint.h>
#include <utility>
#include <vector>
#include "base/containers/span.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/test/task_environment.h"
#include "mojo/public/cpp/system/simple_watcher.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/log/net_log_source.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/tcp_client_socket.h"
#include "net/socket/tcp_server_socket.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/mojo_socket_test_util.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/udp_socket.mojom.h"
#include "services/network/socket_factory.h"
#include "services/network/tcp_connected_socket.h"
#include "services/network/tcp_server_socket.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
// Test delegate to wait on network read/write errors.
class TestSocketDataPumpDelegate : public SocketDataPump::Delegate {
public:
TestSocketDataPumpDelegate() {}
TestSocketDataPumpDelegate(const TestSocketDataPumpDelegate&) = delete;
TestSocketDataPumpDelegate& operator=(const TestSocketDataPumpDelegate&) =
delete;
~TestSocketDataPumpDelegate() {}
// Waits for read error. Returns the error observed.
int WaitForReadError() {
read_loop_.Run();
int error = read_error_;
read_error_ = net::OK;
return error;
}
// Waits for write error. Returns the error observed.
int WaitForWriteError() {
write_loop_.Run();
int error = write_error_;
write_error_ = net::OK;
return error;
}
// Waits for shutdown.
void WaitForShutdown() { shutdown_loop_.Run(); }
private:
void OnNetworkReadError(int error) override {
read_error_ = error;
read_loop_.Quit();
}
void OnNetworkWriteError(int error) override {
write_error_ = error;
write_loop_.Quit();
}
void OnShutdown() override { shutdown_loop_.Quit(); }
int read_error_ = net::OK;
int write_error_ = net::OK;
base::RunLoop read_loop_;
base::RunLoop write_loop_;
base::RunLoop shutdown_loop_;
};
class SocketDataPumpTest : public testing::Test,
public ::testing::WithParamInterface<net::IoMode> {
public:
SocketDataPumpTest()
: task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {}
SocketDataPumpTest(const SocketDataPumpTest&) = delete;
SocketDataPumpTest& operator=(const SocketDataPumpTest&) = delete;
~SocketDataPumpTest() override {}
// Initializes the test case with a socket data provider, which will be used
// to populate the read/write data of the mock socket.
void Init(net::StaticSocketDataProvider* data_provider) {
mock_client_socket_factory_.AddSocketDataProvider(data_provider);
mock_client_socket_factory_.set_enable_read_if_ready(true);
mojo::ScopedDataPipeConsumerHandle send_consumer_handle;
ASSERT_EQ(mojo::CreateDataPipe(nullptr, send_handle_, send_consumer_handle),
MOJO_RESULT_OK);
mojo::ScopedDataPipeProducerHandle receive_producer_handle;
ASSERT_EQ(
mojo::CreateDataPipe(nullptr, receive_producer_handle, receive_handle_),
MOJO_RESULT_OK);
socket_ = mock_client_socket_factory_.CreateTransportClientSocket(
net::AddressList(),
// No need to use a target network here. This is used only for testing
// in non-multi-network scenarios.
net::handles::kInvalidNetworkHandle,
nullptr /*socket_performance_watcher*/,
nullptr /*network_quality_estimator*/, nullptr /*netlog*/,
net::NetLogSource());
net::TestCompletionCallback callback;
int result = socket_->Connect(callback.callback());
if (result == net::ERR_IO_PENDING)
result = callback.WaitForResult();
EXPECT_EQ(net::OK, result);
data_pump_ = std::make_unique<SocketDataPump>(
socket_.get(), delegate(), std::move(receive_producer_handle),
std::move(send_consumer_handle), TRAFFIC_ANNOTATION_FOR_TESTS);
}
// Reads |num_bytes| from |handle| or reads until an error occurs. Returns the
// bytes read as a string.
std::string Read(mojo::ScopedDataPipeConsumerHandle* handle,
size_t num_bytes) {
std::string received_contents;
while (received_contents.size() < num_bytes) {
base::RunLoop().RunUntilIdle();
std::string buffer(num_bytes, '\0');
MojoResult result = handle->get().ReadData(
MOJO_READ_DATA_FLAG_NONE, base::as_writable_byte_span(buffer),
num_bytes);
if (result == MOJO_RESULT_SHOULD_WAIT)
continue;
if (result != MOJO_RESULT_OK)
return received_contents;
received_contents.append(std::string_view(buffer).substr(0, num_bytes));
}
return received_contents;
}
TestSocketDataPumpDelegate* delegate() { return &test_delegate_; }
mojo::ScopedDataPipeConsumerHandle receive_handle_;
mojo::ScopedDataPipeProducerHandle send_handle_;
private:
base::test::TaskEnvironment task_environment_;
net::MockClientSocketFactory mock_client_socket_factory_;
TestSocketDataPumpDelegate test_delegate_;
std::unique_ptr<net::StreamSocket> socket_;
std::unique_ptr<SocketDataPump> data_pump_;
};
INSTANTIATE_TEST_SUITE_P(All,
SocketDataPumpTest,
testing::Values(net::SYNCHRONOUS, net::ASYNC));
TEST_P(SocketDataPumpTest, ReadAndWriteMultiple) {
constexpr std::string_view kTestMsg = "abcdefghij";
constexpr int kNumIterations = 3;
std::vector<net::MockRead> reads;
std::vector<net::MockWrite> writes;
int sequence_number = 0;
net::IoMode mode = GetParam();
for (int j = 0; j < kNumIterations; ++j) {
for (const char& c : kTestMsg) {
reads.emplace_back(mode, sequence_number++, base::byte_span_from_ref(c));
}
if (j == kNumIterations - 1) {
reads.emplace_back(mode, net::OK, sequence_number++);
}
for (const char& c : kTestMsg) {
writes.emplace_back(mode, sequence_number++, base::byte_span_from_ref(c));
}
}
net::StaticSocketDataProvider data_provider(reads, writes);
Init(&data_provider);
// Loop kNumIterations times to test that writes can follow reads, and reads
// can follow writes.
for (int j = 0; j < kNumIterations; ++j) {
// Reading `kTestMsg.size()` should coalesce the 1-byte mock reads.
EXPECT_EQ(kTestMsg, Read(&receive_handle_, kTestMsg.size()));
// Write multiple times.
for (const char& c : kTestMsg) {
size_t actually_written_bytes = 0;
EXPECT_EQ(MOJO_RESULT_OK,
send_handle_->WriteData(base::byte_span_from_ref(c),
MOJO_WRITE_DATA_FLAG_NONE,
actually_written_bytes));
// Flush the 1 byte write.
base::RunLoop().RunUntilIdle();
}
}
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
TEST_P(SocketDataPumpTest, PartialStreamSocketWrite) {
constexpr std::string_view kTestMsg = "abcdefghij";
constexpr int kNumIterations = 3;
std::vector<net::MockRead> reads;
std::vector<net::MockWrite> writes;
int sequence_number = 0;
net::IoMode mode = GetParam();
for (int j = 0; j < kNumIterations; ++j) {
for (const char& c : kTestMsg) {
reads.emplace_back(mode, sequence_number++, base::byte_span_from_ref(c));
}
if (j == kNumIterations - 1) {
reads.emplace_back(mode, net::OK, sequence_number++);
}
for (const char& c : kTestMsg) {
writes.emplace_back(mode, sequence_number++, base::byte_span_from_ref(c));
}
}
net::StaticSocketDataProvider data_provider(reads, writes);
Init(&data_provider);
// Loop kNumIterations times to test that writes can follow reads, and reads
// can follow writes.
for (int j = 0; j < kNumIterations; ++j) {
// Reading `kTestMsg.size()` should coalesce the 1-byte mock reads.
EXPECT_EQ(kTestMsg, Read(&receive_handle_, kTestMsg.size()));
// Write twice, each with kMsgSize/2 bytes which is bigger than the 1-byte
// MockWrite(). This is to exercise that StreamSocket::Write() can do
// partial write.
auto [first_write, second_write] =
base::as_byte_span(kTestMsg).split_at(kTestMsg.size() / 2);
size_t actually_written_bytes = 0;
EXPECT_EQ(MOJO_RESULT_OK,
send_handle_->WriteData(first_write, MOJO_WRITE_DATA_FLAG_NONE,
actually_written_bytes));
EXPECT_EQ(kTestMsg.size() / 2, actually_written_bytes);
// Flush the kMsgSize/2 byte write.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(MOJO_RESULT_OK,
send_handle_->WriteData(second_write, MOJO_WRITE_DATA_FLAG_NONE,
actually_written_bytes));
EXPECT_EQ(kTestMsg.size() - first_write.size(), actually_written_bytes);
// Flush the kMsgSize/2 byte write.
base::RunLoop().RunUntilIdle();
}
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
TEST_P(SocketDataPumpTest, ReadEof) {
net::IoMode mode = GetParam();
net::MockRead reads[] = {net::MockRead(mode, net::OK)};
constexpr std::string_view kTestMsg = "hello!";
net::MockWrite writes[] = {net::MockWrite(mode, 0, kTestMsg)};
net::StaticSocketDataProvider data_provider(reads, writes);
Init(&data_provider);
EXPECT_EQ("", Read(&receive_handle_, 1));
EXPECT_EQ(net::OK, delegate()->WaitForReadError());
// Writes can proceed even though there is a read error.
size_t actually_written_bytes = 0;
EXPECT_EQ(MOJO_RESULT_OK,
send_handle_->WriteData(base::as_byte_span(kTestMsg),
MOJO_WRITE_DATA_FLAG_NONE,
actually_written_bytes));
EXPECT_EQ(kTestMsg.size(), actually_written_bytes);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
TEST_P(SocketDataPumpTest, ReadError) {
net::IoMode mode = GetParam();
net::MockRead reads[] = {net::MockRead(mode, net::ERR_FAILED)};
constexpr std::string_view kTestMsg = "hello!";
net::MockWrite writes[] = {net::MockWrite(mode, 0, kTestMsg)};
net::StaticSocketDataProvider data_provider(reads, writes);
Init(&data_provider);
EXPECT_EQ("", Read(&receive_handle_, 1));
EXPECT_EQ(net::ERR_FAILED, delegate()->WaitForReadError());
// Writes can proceed even though there is a read error.
size_t actually_written_bytes = 0;
EXPECT_EQ(MOJO_RESULT_OK,
send_handle_->WriteData(base::as_byte_span(kTestMsg),
MOJO_WRITE_DATA_FLAG_NONE,
actually_written_bytes));
EXPECT_EQ(kTestMsg.size(), actually_written_bytes);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
TEST_P(SocketDataPumpTest, WriteEof) {
net::IoMode mode = GetParam();
constexpr std::string_view kTestMsg = "hello!";
net::MockRead reads[] = {net::MockRead(mode, 0, kTestMsg),
net::MockRead(mode, net::OK)};
net::MockWrite writes[] = {net::MockWrite(mode, net::OK)};
net::StaticSocketDataProvider data_provider(reads, writes);
Init(&data_provider);
size_t actually_written_bytes = 0;
EXPECT_EQ(MOJO_RESULT_OK,
send_handle_->WriteData(base::as_byte_span(kTestMsg),
MOJO_WRITE_DATA_FLAG_NONE,
actually_written_bytes));
EXPECT_EQ(kTestMsg.size(), actually_written_bytes);
EXPECT_EQ(net::OK, delegate()->WaitForWriteError());
// Reads can proceed even though there is a read error.
EXPECT_EQ(kTestMsg, Read(&receive_handle_, kTestMsg.size()));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
TEST_P(SocketDataPumpTest, WriteError) {
net::IoMode mode = GetParam();
constexpr std::string_view kTestMsg = "hello!";
net::MockRead reads[] = {net::MockRead(mode, 0, kTestMsg),
net::MockRead(mode, net::OK)};
net::MockWrite writes[] = {net::MockWrite(mode, net::ERR_FAILED)};
net::StaticSocketDataProvider data_provider(reads, writes);
Init(&data_provider);
size_t actually_written_bytes = 0;
EXPECT_EQ(MOJO_RESULT_OK,
send_handle_->WriteData(base::as_byte_span(kTestMsg),
MOJO_WRITE_DATA_FLAG_NONE,
actually_written_bytes));
EXPECT_EQ(kTestMsg.size(), actually_written_bytes);
EXPECT_EQ(net::ERR_FAILED, delegate()->WaitForWriteError());
// Reads can proceed even though there is a read error.
EXPECT_EQ(kTestMsg, Read(&receive_handle_, kTestMsg.size()));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
TEST_P(SocketDataPumpTest, PipesShutdown) {
net::IoMode mode = GetParam();
net::MockRead reads[] = {net::MockRead(mode, net::OK)};
net::StaticSocketDataProvider data_provider(reads,
base::span<net::MockWrite>());
Init(&data_provider);
send_handle_.reset();
receive_handle_.reset();
delegate()->WaitForShutdown();
}
namespace {
class PumpDestroyingDelegate : public SocketDataPump::Delegate {
public:
PumpDestroyingDelegate() = default;
~PumpDestroyingDelegate() = default;
PumpDestroyingDelegate(const PumpDestroyingDelegate&) = delete;
PumpDestroyingDelegate& operator=(const PumpDestroyingDelegate&) = delete;
void set_pump(std::unique_ptr<SocketDataPump> pump) {
pump_ = std::move(pump);
}
void set_run_on_shutdown(base::OnceClosure closure) {
run_on_shutdown_ = std::move(closure);
}
// SocketDataPump::Delegate implementation:
void OnNetworkReadError(int net_error) override {}
void OnNetworkWriteError(int net_error) override {}
void OnShutdown() override {
pump_ = nullptr;
if (run_on_shutdown_) {
std::move(run_on_shutdown_).Run();
}
}
private:
std::unique_ptr<SocketDataPump> pump_;
base::OnceClosure run_on_shutdown_;
};
// A dummy socket that behaves as if writes are blocked until the TakeWrite()
// method is explicitly called.
class BlockedStreamSocket : public net::StreamSocket {
public:
BlockedStreamSocket() = default;
~BlockedStreamSocket() override = default;
void set_run_on_write(base::OnceClosure closure) {
run_on_write_ = std::move(closure);
}
std::vector<uint8_t> TakeWrite() {
size_t buf_len = pending_write_buf_len_;
std::vector data(std::from_range, pending_write_buf_->first(buf_len));
pending_write_buf_ = nullptr;
pending_write_buf_len_ = 0;
std::move(pending_write_callback_).Run(base::checked_cast<int>(buf_len));
return data;
}
// net::StreamSocket implementation:
int Read(net::IOBuffer* buf,
int buf_len,
net::CompletionOnceCallback callback) override {
return net::ERR_IO_PENDING;
}
int ReadIfReady(net::IOBuffer* buf,
int buf_len,
net::CompletionOnceCallback callback) override {
return net::ERR_IO_PENDING;
}
int CancelReadIfReady() override { return net::OK; }
int Write(
net::IOBuffer* buf,
int buf_len,
net::CompletionOnceCallback callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) override {
pending_write_buf_ = buf;
pending_write_buf_len_ = base::checked_cast<size_t>(buf_len);
pending_write_callback_ = std::move(callback);
if (run_on_write_) {
std::move(run_on_write_).Run();
}
return net::ERR_IO_PENDING;
}
int SetReceiveBufferSize(int32_t size) override { return net::OK; }
int SetSendBufferSize(int32_t size) override { return net::OK; }
int Connect(net::CompletionOnceCallback callback) override { return net::OK; }
void Disconnect() override {}
bool IsConnected() const override { return true; }
bool IsConnectedAndIdle() const override { return false; }
int GetPeerAddress(net::IPEndPoint* address) const override {
return net::OK;
}
int GetLocalAddress(net::IPEndPoint* address) const override {
return net::OK;
}
const net::NetLogWithSource& NetLog() const override { return net_log_; }
bool WasEverUsed() const override { return true; }
net::NextProto GetNegotiatedProtocol() const override {
return net::NextProto::kProtoUnknown;
}
bool GetSSLInfo(net::SSLInfo* ssl_info) override { return false; }
int64_t GetTotalReceivedBytes() const override { return 0; }
void ApplySocketTag(const net::SocketTag& tag) override {}
private:
scoped_refptr<net::IOBuffer> pending_write_buf_;
size_t pending_write_buf_len_ = 0;
net::CompletionOnceCallback pending_write_callback_;
base::OnceClosure run_on_write_;
net::NetLogWithSource net_log_;
};
} // namespace
TEST(SocketDataPumpTest, ShutdownWhileBlockedOnWrite) {
base::test::TaskEnvironment task_environment(
base::test::TaskEnvironment::MainThreadType::IO);
BlockedStreamSocket socket;
base::RunLoop wait_for_write;
socket.set_run_on_write(wait_for_write.QuitClosure());
mojo::ScopedDataPipeProducerHandle send_producer;
mojo::ScopedDataPipeConsumerHandle send_consumer;
ASSERT_EQ(MOJO_RESULT_OK,
mojo::CreateDataPipe(nullptr, send_producer, send_consumer));
mojo::ScopedDataPipeProducerHandle receive_producer;
mojo::ScopedDataPipeConsumerHandle receive_consumer;
ASSERT_EQ(MOJO_RESULT_OK,
mojo::CreateDataPipe(nullptr, receive_producer, receive_consumer));
PumpDestroyingDelegate delegate;
auto pump = std::make_unique<SocketDataPump>(
&socket, &delegate, std::move(receive_producer), std::move(send_consumer),
TRAFFIC_ANNOTATION_FOR_TESTS);
delegate.set_pump(std::move(pump));
base::RunLoop wait_for_shutdown;
delegate.set_run_on_shutdown(wait_for_shutdown.QuitClosure());
// Write data to the send pipe.
const std::string data = "secret";
size_t actually_written = 0;
ASSERT_EQ(MOJO_RESULT_OK, send_producer->WriteData(base::as_byte_span(data),
MOJO_WRITE_DATA_FLAG_NONE,
actually_written));
EXPECT_EQ(actually_written, data.size());
// Run until SocketDataPump reads from the pipe and calls socket.Write().
wait_for_write.Run();
// Trigger OnShutdown() by closing the receive consumer.
// This will cause PumpDestroyingDelegate to destroy the SocketDataPump.
receive_consumer.reset();
wait_for_shutdown.Run();
// Copy the data that was passed to Write().
std::vector<uint8_t> written_data = socket.TakeWrite();
EXPECT_EQ(data, base::as_string_view(written_data));
}
} // namespace network