blob: f0bd724d4c6383d7bf17dd49193edd7018ec3250 [file] [log] [blame]
// Copyright 2018 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 <stdint.h>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "base/threading/thread.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "mojo/public/cpp/system/simple_watcher.h"
#include "net/base/completion_once_callback.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/socket/server_socket.h"
#include "net/socket/socket_test_util.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 {
namespace {
// A mock ServerSocket that completes Accept() using a specified result.
class MockServerSocket : public net::ServerSocket {
public:
explicit MockServerSocket(
std::vector<std::unique_ptr<net::StaticSocketDataProvider>>
data_providers)
: data_providers_(std::move(data_providers)) {}
~MockServerSocket() override {}
// net::ServerSocket implementation.
int Listen(const net::IPEndPoint& address, int backlog) override {
return net::OK;
}
int GetLocalAddress(net::IPEndPoint* address) const override {
return net::OK;
}
int Accept(std::unique_ptr<net::StreamSocket>* socket,
net::CompletionOnceCallback callback) override {
DCHECK(accept_callback_.is_null());
if (accept_result_ == net::OK && mode_ == net::SYNCHRONOUS)
*socket = CreateMockAcceptSocket();
if (mode_ == net::ASYNC || accept_result_ == net::ERR_IO_PENDING) {
accept_socket_ = socket;
accept_callback_ = std::move(callback);
}
run_loop_.Quit();
if (mode_ == net::ASYNC) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&MockServerSocket::CompleteAccept,
base::Unretained(this), accept_result_));
return net::ERR_IO_PENDING;
}
return accept_result_;
}
void SetAcceptResult(net::IoMode mode, int result) {
// It doesn't make sense to return net::ERR_IO_PENDING asynchronously.
DCHECK(!(mode == net::ASYNC && result == net::ERR_IO_PENDING));
mode_ = mode;
accept_result_ = result;
}
void WaitForFirstAccept() { run_loop_.Run(); }
void CompleteAccept(int result) {
DCHECK(!accept_callback_.is_null());
DCHECK_NE(net::ERR_IO_PENDING, result);
*accept_socket_ = CreateMockAcceptSocket();
accept_socket_ = nullptr;
std::move(accept_callback_).Run(result);
}
private:
std::unique_ptr<net::StreamSocket> CreateMockAcceptSocket() {
DCHECK_GT(data_providers_.size(), next_data_provider_index_);
auto mock_socket = std::make_unique<net::MockTCPClientSocket>(
net::AddressList(), nullptr /*netlog*/,
data_providers_[next_data_provider_index_++].get());
mock_socket->set_enable_read_if_ready(true);
EXPECT_EQ(net::OK, mock_socket->Connect(base::DoNothing()));
return std::move(mock_socket);
}
net::IoMode mode_ = net::SYNCHRONOUS;
int accept_result_ = net::OK;
net::CompletionOnceCallback accept_callback_;
std::unique_ptr<net::StreamSocket>* accept_socket_;
base::RunLoop run_loop_;
std::vector<std::unique_ptr<net::StaticSocketDataProvider>> data_providers_;
size_t next_data_provider_index_ = 0;
};
// A MockServerSocket that fails at GetLocalAddress().
class FailingServerSocket : public MockServerSocket {
public:
FailingServerSocket()
: MockServerSocket(
std::vector<std::unique_ptr<net::StaticSocketDataProvider>>()) {}
~FailingServerSocket() override {}
int GetLocalAddress(net::IPEndPoint* address) const override {
return net::ERR_FAILED;
}
};
// A server implemented using mojom::TCPServerSocket. It owns the server socket
// pointer and as well as client connections. SendData() and StartReading()
// operate on the newest client connection.
class TestServer {
public:
TestServer()
: TestServer(net::IPEndPoint(net::IPAddress::IPv6Localhost(), 0)) {}
explicit TestServer(const net::IPEndPoint& server_addr)
: factory_(nullptr, &url_request_context_),
readable_handle_watcher_(FROM_HERE,
mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC),
server_addr_(server_addr) {}
~TestServer() {}
void Start(uint32_t backlog) {
int net_error = net::ERR_FAILED;
base::RunLoop run_loop;
factory_.CreateTCPServerSocket(
server_addr_, backlog, TRAFFIC_ANNOTATION_FOR_TESTS,
mojo::MakeRequest(&server_socket_),
base::BindLambdaForTesting(
[&](int result, const base::Optional<net::IPEndPoint>& local_addr) {
net_error = result;
if (local_addr)
server_addr_ = local_addr.value();
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(net::OK, net_error);
}
// Accepts one connection. Upon successful completion, |callback| will be
// invoked.
void AcceptOneConnection(net::CompletionOnceCallback callback) {
server_socket_->Accept(
nullptr, base::BindOnce(&TestServer::OnAccept, base::Unretained(this),
std::move(callback)));
}
// Sends data over the most recent connection that is established.
void SendData(const std::string& msg) {
EXPECT_TRUE(mojo::BlockingCopyFromString(msg, server_socket_send_handle_));
}
// Starts reading. Can be called multiple times. It cancels any previous
// StartReading(). Once |expected_contents| is read, |callback| will be
// invoked. If an error occurs or the pipe is broken before read can
// complete, |callback| will be run, but ADD_FAILURE() will be called.
void StartReading(const std::string& expected_contents,
base::OnceClosure callback) {
readable_handle_watcher_.Cancel();
received_contents_.clear();
expected_contents_ = expected_contents;
read_callback_ = std::move(callback);
readable_handle_watcher_.Watch(
server_socket_receive_handle_.get(),
MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
base::BindRepeating(&TestServer::OnReadable, base::Unretained(this)));
}
void DestroyServerSocket() { server_socket_.reset(); }
const net::IPEndPoint& server_addr() { return server_addr_; }
mojom::TCPConnectedSocket* most_recent_connected_socket() {
return connected_sockets_.back().get();
}
private:
void OnAccept(net::CompletionOnceCallback callback,
int result,
const base::Optional<net::IPEndPoint>& remote_addr,
mojom::TCPConnectedSocketPtr connected_socket,
mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
connected_sockets_.push_back(std::move(connected_socket));
server_socket_receive_handle_ = std::move(receive_pipe_handle);
server_socket_send_handle_ = std::move(send_pipe_handle);
std::move(callback).Run(result);
}
void OnReadable(MojoResult result) {
if (result != MOJO_RESULT_OK) {
ADD_FAILURE() << "Unexpected broken pipe with error: " << result;
EXPECT_EQ(expected_contents_, received_contents_);
std::move(read_callback_).Run();
return;
}
char buffer[16];
uint32_t read_size = sizeof(buffer);
result = server_socket_receive_handle_->ReadData(buffer, &read_size,
MOJO_READ_DATA_FLAG_NONE);
if (result == MOJO_RESULT_SHOULD_WAIT)
return;
if (result != MOJO_RESULT_OK) {
ADD_FAILURE() << "Unexpected read error: " << result;
EXPECT_EQ(expected_contents_, received_contents_);
std::move(read_callback_).Run();
return;
}
received_contents_.append(buffer, read_size);
if (received_contents_.size() == expected_contents_.size()) {
EXPECT_EQ(expected_contents_, received_contents_);
std::move(read_callback_).Run();
}
}
net::TestURLRequestContext url_request_context_;
SocketFactory factory_;
mojom::TCPServerSocketPtr server_socket_;
std::vector<mojom::TCPConnectedSocketPtr> connected_sockets_;
mojo::ScopedDataPipeConsumerHandle server_socket_receive_handle_;
mojo::ScopedDataPipeProducerHandle server_socket_send_handle_;
mojo::SimpleWatcher readable_handle_watcher_;
net::IPEndPoint server_addr_;
std::string expected_contents_;
base::OnceClosure read_callback_;
std::string received_contents_;
};
} // namespace
class TCPSocketTest : public testing::Test {
public:
TCPSocketTest()
: task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
url_request_context_(true) {}
~TCPSocketTest() override {}
void Init(net::ClientSocketFactory* mock_client_socket_factory) {
url_request_context_.set_client_socket_factory(mock_client_socket_factory);
url_request_context_.Init();
factory_ = std::make_unique<SocketFactory>(nullptr /*net_log*/,
&url_request_context_);
}
void SetUp() override { Init(nullptr); }
// 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::vector<char> buffer(num_bytes);
uint32_t read_size = static_cast<uint32_t>(num_bytes);
MojoResult result = handle->get().ReadData(buffer.data(), &read_size,
MOJO_READ_DATA_FLAG_NONE);
if (result == MOJO_RESULT_SHOULD_WAIT)
continue;
if (result != MOJO_RESULT_OK)
return received_contents;
received_contents.append(buffer.data(), read_size);
}
return received_contents;
}
// Creates a TCPServerSocket with the mock server socket, |socket|.
void CreateServerSocketWithMockSocket(
uint32_t backlog,
mojom::TCPServerSocketRequest request,
std::unique_ptr<net::ServerSocket> socket) {
auto server_socket_impl = std::make_unique<TCPServerSocket>(
factory_.get(), nullptr /*netlog*/, TRAFFIC_ANNOTATION_FOR_TESTS);
server_socket_impl->SetSocketForTest(std::move(socket));
net::IPEndPoint local_addr;
EXPECT_EQ(net::OK,
server_socket_impl->Listen(local_addr, backlog, &local_addr));
tcp_server_socket_bindings_.AddBinding(std::move(server_socket_impl),
std::move(request));
}
int CreateTCPConnectedSocketSync(
mojom::TCPConnectedSocketRequest request,
mojom::SocketObserverPtr observer,
const base::Optional<net::IPEndPoint>& local_addr,
const net::IPEndPoint& remote_addr,
mojo::ScopedDataPipeConsumerHandle* receive_pipe_handle_out,
mojo::ScopedDataPipeProducerHandle* send_pipe_handle_out,
mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options =
nullptr) {
net::AddressList remote_addr_list(remote_addr);
base::RunLoop run_loop;
int net_error = net::ERR_FAILED;
factory_->CreateTCPConnectedSocket(
local_addr, remote_addr_list, std::move(tcp_connected_socket_options),
TRAFFIC_ANNOTATION_FOR_TESTS, std::move(request), std::move(observer),
base::BindLambdaForTesting(
[&](int result,
const base::Optional<net::IPEndPoint>& actual_local_addr,
const base::Optional<net::IPEndPoint>& peer_addr,
mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
net_error = result;
if (result == net::OK) {
EXPECT_NE(0, actual_local_addr.value().port());
EXPECT_EQ(remote_addr, peer_addr.value());
}
*receive_pipe_handle_out = std::move(receive_pipe_handle);
*send_pipe_handle_out = std::move(send_pipe_handle);
run_loop.Quit();
}));
run_loop.Run();
return net_error;
}
TestSocketObserver* observer() { return &test_observer_; }
SocketFactory* factory() { return factory_.get(); }
private:
base::test::TaskEnvironment task_environment_;
net::TestURLRequestContext url_request_context_;
std::unique_ptr<SocketFactory> factory_;
TestSocketObserver test_observer_;
mojo::StrongBindingSet<mojom::TCPServerSocket> tcp_server_socket_bindings_;
mojo::StrongBindingSet<mojom::TCPConnectedSocket>
tcp_connected_socket_bindings_;
DISALLOW_COPY_AND_ASSIGN(TCPSocketTest);
};
TEST_F(TCPSocketTest, ReadAndWrite) {
const struct TestData {
base::Optional<net::IPEndPoint> client_addr;
net::IPEndPoint server_addr;
} kTestCases[] = {
{base::nullopt, net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0)},
{base::nullopt, net::IPEndPoint(net::IPAddress::IPv6Localhost(), 0)},
{net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0),
net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0)},
{net::IPEndPoint(net::IPAddress::IPv6Localhost(), 0),
net::IPEndPoint(net::IPAddress::IPv6Localhost(), 0)},
};
for (auto test : kTestCases) {
TestServer server(test.server_addr);
server.Start(1 /*backlog*/);
net::TestCompletionCallback accept_callback;
server.AcceptOneConnection(accept_callback.callback());
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
mojom::TCPConnectedSocketPtr client_socket;
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
test.client_addr, server.server_addr(),
&client_socket_receive_handle, &client_socket_send_handle));
ASSERT_EQ(net::OK, accept_callback.WaitForResult());
// Test sending data from server to client.
const char kTestMsg[] = "hello";
server.SendData(kTestMsg);
EXPECT_EQ(kTestMsg, Read(&client_socket_receive_handle, strlen(kTestMsg)));
// Test sending data from client to server.
base::RunLoop read_run_loop;
server.StartReading(kTestMsg, read_run_loop.QuitClosure());
EXPECT_TRUE(
mojo::BlockingCopyFromString(kTestMsg, client_socket_send_handle));
read_run_loop.Run();
}
}
TEST_F(TCPSocketTest, CannotConnectToWrongInterface) {
const struct TestData {
net::IPEndPoint client_addr;
net::IPEndPoint server_addr;
} kTestCases[] = {
{net::IPEndPoint(net::IPAddress::IPv6Localhost(), 0),
net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0)},
{net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0),
net::IPEndPoint(net::IPAddress::IPv6Localhost(), 0)},
};
for (auto test : kTestCases) {
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
TestServer server(test.server_addr);
server.Start(1 /*backlog*/);
net::TestCompletionCallback accept_callback;
server.AcceptOneConnection(accept_callback.callback());
mojom::TCPConnectedSocketPtr client_socket;
int result = CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
test.client_addr, server.server_addr(), &client_socket_receive_handle,
&client_socket_send_handle);
// Both net::ERR_INVALID_ARGUMENT and net::ERR_ADDRESS_UNREACHABLE can be
// returned. On Linux, for eample, the former is returned when talking ipv4
// to a ipv6 remote, and the latter is returned when talking ipv6 to a ipv4
// remote. net::ERR_CONNECTION_FAILED is returned on Windows.
EXPECT_TRUE(result == net::ERR_CONNECTION_FAILED ||
result == net::ERR_INVALID_ARGUMENT ||
result == net::ERR_ADDRESS_UNREACHABLE)
<< "actual result: " << result;
}
}
TEST_F(TCPSocketTest, ServerReceivesMultipleAccept) {
uint32_t backlog = 10;
TestServer server;
server.Start(backlog);
std::vector<std::unique_ptr<net::TestCompletionCallback>> accept_callbacks;
// Issue |backlog| Accepts(), so the queue is filled up.
for (size_t i = 0; i < backlog; ++i) {
auto callback = std::make_unique<net::TestCompletionCallback>();
server.AcceptOneConnection(callback->callback());
accept_callbacks.push_back(std::move(callback));
}
// Accept() beyond the queue size should fail immediately.
net::TestCompletionCallback callback;
server.AcceptOneConnection(callback.callback());
EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES, callback.WaitForResult());
// After handling incoming connections, all callbacks should now complete.
std::vector<mojom::TCPConnectedSocketPtr> client_sockets;
for (size_t i = 0; i < backlog; ++i) {
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
mojom::TCPConnectedSocketPtr client_socket;
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt /*local_addr*/, server.server_addr(),
&client_socket_receive_handle, &client_socket_send_handle));
client_sockets.push_back(std::move(client_socket));
}
for (const auto& callback : accept_callbacks) {
EXPECT_EQ(net::OK, callback->WaitForResult());
}
}
// Check that accepted sockets can't be upgraded to TLS, since UpgradeToTLS only
// supports the client side of a TLS handshake.
TEST_F(TCPSocketTest, AcceptedSocketCantUpgradeToTLS) {
TestServer server;
server.Start(1 /* backlog */);
net::TestCompletionCallback callback;
server.AcceptOneConnection(callback.callback());
mojom::TCPConnectedSocketPtr client_socket;
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt /*local_addr*/, server.server_addr(),
&client_socket_receive_handle, &client_socket_send_handle));
EXPECT_EQ(net::OK, callback.WaitForResult());
// Consumers generally close these before attempting to upgrade the socket,
// since TCPConnectedSocket waits for the pipes to close before upgrading the
// connection.
client_socket_receive_handle.reset();
client_socket_send_handle.reset();
base::RunLoop run_loop;
mojom::TLSClientSocketPtr tls_client_socket;
server.most_recent_connected_socket()->UpgradeToTLS(
net::HostPortPair("foopy", 443), nullptr /* options */,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
mojo::MakeRequest(&tls_client_socket), nullptr /* observer */,
base::BindLambdaForTesting(
[&](int net_error,
mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
mojo::ScopedDataPipeProducerHandle send_pipe_handle,
const base::Optional<net::SSLInfo>& ssl_info) {
EXPECT_EQ(net::ERR_NOT_IMPLEMENTED, net_error);
run_loop.Quit();
}));
}
// Tests that if a socket is closed, the other side can observe that the pipes
// are broken.
TEST_F(TCPSocketTest, SocketClosed) {
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
mojom::TCPConnectedSocketPtr client_socket;
const char kTestMsg[] = "hello";
auto server = std::make_unique<TestServer>();
server->Start(1 /*backlog*/);
net::TestCompletionCallback accept_callback;
server->AcceptOneConnection(accept_callback.callback());
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), observer()->GetObserverPtr(),
base::nullopt /*local_addr*/, server->server_addr(),
&client_socket_receive_handle, &client_socket_send_handle));
ASSERT_EQ(net::OK, accept_callback.WaitForResult());
// Send some data from server to client.
server->SendData(kTestMsg);
EXPECT_EQ(kTestMsg, Read(&client_socket_receive_handle, strlen(kTestMsg)));
// Resetting the |server| destroys the TCPConnectedSocket ptr owned by the
// server.
server = nullptr;
// Read should return EOF.
EXPECT_EQ("", Read(&client_socket_receive_handle, 1));
// Read from |client_socket_receive_handle| again should return that the pipe
// is broken.
char buffer[16];
uint32_t read_size = sizeof(buffer);
MojoResult mojo_result = client_socket_receive_handle->ReadData(
buffer, &read_size, MOJO_READ_DATA_FLAG_NONE);
ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, mojo_result);
EXPECT_TRUE(client_socket_receive_handle->QuerySignalsState().peer_closed());
// Send pipe should be closed.
while (true) {
base::RunLoop().RunUntilIdle();
uint32_t size = strlen(kTestMsg);
MojoResult r = client_socket_send_handle->WriteData(
kTestMsg, &size, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
if (r == MOJO_RESULT_SHOULD_WAIT)
continue;
if (r == MOJO_RESULT_FAILED_PRECONDITION)
break;
}
EXPECT_TRUE(client_socket_send_handle->QuerySignalsState().peer_closed());
int result = observer()->WaitForWriteError();
EXPECT_TRUE(result == net::ERR_CONNECTION_RESET ||
result == net::ERR_CONNECTION_ABORTED)
<< "actual result: " << result;
}
TEST_F(TCPSocketTest, ReadPipeClosed) {
const char kTestMsg[] = "hello";
TestServer server;
server.Start(1 /*backlog*/);
net::TestCompletionCallback accept_callback;
server.AcceptOneConnection(accept_callback.callback());
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
mojom::TCPConnectedSocketPtr client_socket;
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt /*local_addr*/, server.server_addr(),
&client_socket_receive_handle, &client_socket_send_handle));
ASSERT_EQ(net::OK, accept_callback.WaitForResult());
// Close |client_socket_receive_handle|. The socket should remain open.
client_socket_receive_handle.reset();
// Send should proceed as normal.
base::RunLoop read_run_loop;
server.StartReading(kTestMsg, read_run_loop.QuitClosure());
EXPECT_TRUE(
mojo::BlockingCopyFromString(kTestMsg, client_socket_send_handle));
read_run_loop.Run();
}
TEST_F(TCPSocketTest, WritePipeClosed) {
const char kTestMsg[] = "hello";
TestServer server;
server.Start(1 /*backlog*/);
net::TestCompletionCallback accept_callback;
server.AcceptOneConnection(accept_callback.callback());
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
mojom::TCPConnectedSocketPtr client_socket;
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt /*local_addr*/, server.server_addr(),
&client_socket_receive_handle, &client_socket_send_handle));
ASSERT_EQ(net::OK, accept_callback.WaitForResult());
// Close |client_socket_send_handle|. The socket should remain open.
client_socket_send_handle.reset();
// Receive should proceed as normal.
server.SendData(kTestMsg);
EXPECT_EQ(kTestMsg, Read(&client_socket_receive_handle, strlen(kTestMsg)));
}
// Tests that if the server socket is destroyed, any connected sockets that it
// handed out remain alive.
TEST_F(TCPSocketTest, ServerSocketClosedAcceptedSocketAlive) {
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
const char kTestMsg[] = "hello";
TestServer server;
server.Start(1 /*backlog*/);
net::TestCompletionCallback accept_callback;
server.AcceptOneConnection(accept_callback.callback());
mojom::TCPConnectedSocketPtr client_socket;
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt /*local_addr*/, server.server_addr(),
&client_socket_receive_handle, &client_socket_send_handle));
ASSERT_EQ(net::OK, accept_callback.WaitForResult());
// Now destroys the server socket.
server.DestroyServerSocket();
base::RunLoop().RunUntilIdle();
// Sending and receiving should still work.
server.SendData(kTestMsg);
EXPECT_EQ(kTestMsg, Read(&client_socket_receive_handle, strlen(kTestMsg)));
base::RunLoop read_run_loop;
server.StartReading(kTestMsg, read_run_loop.QuitClosure());
EXPECT_TRUE(
mojo::BlockingCopyFromString(kTestMsg, client_socket_send_handle));
read_run_loop.Run();
}
// Tests both async and sync cases.
class TCPSocketWithMockSocketTest
: public TCPSocketTest,
public ::testing::WithParamInterface<net::IoMode> {
public:
void SetUp() override {
mock_client_socket_factory_.set_enable_read_if_ready(true);
Init(&mock_client_socket_factory_);
}
net::MockClientSocketFactory mock_client_socket_factory_;
};
INSTANTIATE_TEST_SUITE_P(/* no prefix */,
TCPSocketWithMockSocketTest,
testing::Values(net::SYNCHRONOUS, net::ASYNC));
// Tests that a server socket handles Accept() correctly when the underlying
// implementation completes Accept() in sync and async mode.
TEST_P(TCPSocketWithMockSocketTest,
ServerAcceptClientConnectionWithMockSocket) {
net::IoMode accept_mode = GetParam();
const uint32_t kBacklog = 10;
const net::MockRead kReads[] = {
net::MockRead(net::ASYNC, net::ERR_IO_PENDING)};
std::vector<std::unique_ptr<net::StaticSocketDataProvider>> data_providers;
for (size_t i = 0; i < kBacklog + 1; ++i) {
auto provider = std::make_unique<net::StaticSocketDataProvider>(
kReads, base::span<net::MockWrite>());
provider->set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
data_providers.push_back(std::move(provider));
}
auto mock_server_socket =
std::make_unique<MockServerSocket>(std::move(data_providers));
MockServerSocket* mock_server_socket_raw = mock_server_socket.get();
mojom::TCPServerSocketPtr server_socket;
// Use a mock socket to control net::ServerSocket::Accept() behavior.
CreateServerSocketWithMockSocket(kBacklog, mojo::MakeRequest(&server_socket),
std::move(mock_server_socket));
// Complete first Accept() using manual completion via CompleteAccept().
mock_server_socket_raw->SetAcceptResult(net::SYNCHRONOUS,
net::ERR_IO_PENDING);
std::vector<std::unique_ptr<net::TestCompletionCallback>> accept_callbacks;
for (size_t i = 0; i < kBacklog; ++i) {
auto callback = std::make_unique<net::TestCompletionCallback>();
server_socket->Accept(
nullptr, base::BindOnce(
[](net::CompletionOnceCallback callback, int result,
const base::Optional<net::IPEndPoint>& remote_addr,
mojom::TCPConnectedSocketPtr connected_socket,
mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
std::move(callback).Run(result);
},
callback->callback()));
accept_callbacks.push_back(std::move(callback));
}
mock_server_socket_raw->WaitForFirstAccept();
mock_server_socket_raw->SetAcceptResult(accept_mode, net::OK);
mock_server_socket_raw->CompleteAccept(net::OK);
// First net::ServerSocket::Accept() will complete asynchronously
// internally. Other queued Accept() will complete
// synchronously/asynchronously depending on |mode| internally.
for (const auto& callback : accept_callbacks) {
EXPECT_EQ(net::OK, callback->WaitForResult());
}
// New Accept() should complete synchronously internally. Make sure this is
// okay.
auto callback = std::make_unique<net::TestCompletionCallback>();
server_socket->Accept(
nullptr, base::BindOnce(
[](net::CompletionOnceCallback callback, int result,
const base::Optional<net::IPEndPoint>& remote_addr,
mojom::TCPConnectedSocketPtr connected_socket,
mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
std::move(callback).Run(result);
},
callback->callback()));
EXPECT_EQ(net::OK, callback->WaitForResult());
}
// Tests that TCPServerSocket::Accept() is used with a non-null
// SocketObserver and that the observer is invoked when a read error
// occurs.
TEST_P(TCPSocketWithMockSocketTest, ServerAcceptWithObserverReadError) {
net::IoMode mode = GetParam();
const net::MockRead kReadError[] = {net::MockRead(mode, net::ERR_TIMED_OUT)};
std::vector<std::unique_ptr<net::StaticSocketDataProvider>> data_providers;
std::unique_ptr<net::StaticSocketDataProvider> provider;
provider = std::make_unique<net::StaticSocketDataProvider>(
kReadError, base::span<net::MockWrite>());
provider->set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
data_providers.push_back(std::move(provider));
auto mock_server_socket =
std::make_unique<MockServerSocket>(std::move(data_providers));
mojom::TCPServerSocketPtr server_socket;
CreateServerSocketWithMockSocket(1 /*backlog*/,
mojo::MakeRequest(&server_socket),
std::move(mock_server_socket));
auto callback = std::make_unique<net::TestCompletionCallback>();
mojom::TCPConnectedSocketPtr connected_socket_result;
mojo::ScopedDataPipeConsumerHandle receive_handle;
mojo::ScopedDataPipeProducerHandle send_handle;
server_socket->Accept(
observer()->GetObserverPtr(),
base::BindLambdaForTesting(
[&](int result, const base::Optional<net::IPEndPoint>& remote_addr,
mojom::TCPConnectedSocketPtr connected_socket,
mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
callback->callback().Run(result);
connected_socket_result = std::move(connected_socket);
receive_handle = std::move(receive_pipe_handle);
send_handle = std::move(send_pipe_handle);
}));
EXPECT_EQ(net::OK, callback->WaitForResult());
base::RunLoop().RunUntilIdle();
uint32_t read_size = 16;
std::vector<char> buffer(read_size);
MojoResult result = receive_handle->ReadData(buffer.data(), &read_size,
MOJO_READ_DATA_FLAG_NONE);
ASSERT_NE(MOJO_RESULT_OK, result);
EXPECT_EQ(net::ERR_TIMED_OUT, observer()->WaitForReadError());
}
// Tests that TCPServerSocket::Accept() is used with a non-null SocketObserver
// and that the observer is invoked when a write error occurs.
TEST_P(TCPSocketWithMockSocketTest, ServerAcceptWithObserverWriteError) {
net::IoMode mode = GetParam();
const net::MockRead kReads[] = {net::MockRead(net::SYNCHRONOUS, net::OK)};
const net::MockWrite kWriteError[] = {
net::MockWrite(mode, net::ERR_TIMED_OUT)};
std::vector<std::unique_ptr<net::StaticSocketDataProvider>> data_providers;
std::unique_ptr<net::StaticSocketDataProvider> provider;
provider =
std::make_unique<net::StaticSocketDataProvider>(kReads, kWriteError);
provider->set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
data_providers.push_back(std::move(provider));
auto mock_server_socket =
std::make_unique<MockServerSocket>(std::move(data_providers));
mojom::TCPServerSocketPtr server_socket;
CreateServerSocketWithMockSocket(1 /*backlog*/,
mojo::MakeRequest(&server_socket),
std::move(mock_server_socket));
auto callback = std::make_unique<net::TestCompletionCallback>();
mojom::TCPConnectedSocketPtr connected_socket_result;
mojo::ScopedDataPipeConsumerHandle receive_handle;
mojo::ScopedDataPipeProducerHandle send_handle;
server_socket->Accept(
observer()->GetObserverPtr(),
base::BindLambdaForTesting(
[&](int result, const base::Optional<net::IPEndPoint>& remote_addr,
mojom::TCPConnectedSocketPtr connected_socket,
mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
callback->callback().Run(result);
connected_socket_result = std::move(connected_socket);
receive_handle = std::move(receive_pipe_handle);
send_handle = std::move(send_pipe_handle);
}));
EXPECT_EQ(net::OK, callback->WaitForResult());
const char kTestMsg[] = "abcdefghij";
// Repeatedly write data to the |send_handle| until write fails.
while (true) {
base::RunLoop().RunUntilIdle();
uint32_t num_bytes = strlen(kTestMsg);
MojoResult result = send_handle->WriteData(&kTestMsg, &num_bytes,
MOJO_WRITE_DATA_FLAG_NONE);
if (result == MOJO_RESULT_SHOULD_WAIT)
continue;
if (result != MOJO_RESULT_OK)
break;
}
EXPECT_EQ(net::ERR_TIMED_OUT, observer()->WaitForWriteError());
}
TEST_P(TCPSocketWithMockSocketTest, ReadAndWriteMultiple) {
mojom::TCPConnectedSocketPtr client_socket;
const char kTestMsg[] = "abcdefghij";
const size_t kMsgSize = strlen(kTestMsg);
const 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 (size_t i = 0; i < kMsgSize; ++i) {
reads.push_back(net::MockRead(mode, &kTestMsg[i], 1, sequence_number++));
}
if (j == kNumIterations - 1) {
reads.push_back(net::MockRead(mode, net::OK, sequence_number++));
}
for (size_t i = 0; i < kMsgSize; ++i) {
writes.push_back(
net::MockWrite(mode, &kTestMsg[i], 1, sequence_number++));
}
}
net::StaticSocketDataProvider data_provider(reads, writes);
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
data_provider.set_connect_data(
net::MockConnect(net::SYNCHRONOUS, net::OK, server_addr));
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt /*local_addr*/, server_addr, &client_socket_receive_handle,
&client_socket_send_handle);
// Loop kNumIterations times to test that writes can follow reads, and reads
// can follow writes.
for (int j = 0; j < kNumIterations; ++j) {
// Reading kMsgSize should coalesce the 1-byte mock reads.
EXPECT_EQ(kTestMsg, Read(&client_socket_receive_handle, kMsgSize));
// Write multiple times.
for (size_t i = 0; i < kMsgSize; ++i) {
uint32_t num_bytes = 1;
EXPECT_EQ(MOJO_RESULT_OK,
client_socket_send_handle->WriteData(
&kTestMsg[i], &num_bytes, MOJO_WRITE_DATA_FLAG_NONE));
// Flush the 1 byte write.
base::RunLoop().RunUntilIdle();
}
}
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
TEST_P(TCPSocketWithMockSocketTest, PartialStreamSocketWrite) {
mojom::TCPConnectedSocketPtr client_socket;
const char kTestMsg[] = "abcdefghij";
const size_t kMsgSize = strlen(kTestMsg);
const 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 (size_t i = 0; i < kMsgSize; ++i) {
reads.push_back(net::MockRead(mode, &kTestMsg[i], 1, sequence_number++));
}
if (j == kNumIterations - 1) {
reads.push_back(net::MockRead(mode, net::OK, sequence_number++));
}
for (size_t i = 0; i < kMsgSize; ++i) {
writes.push_back(
net::MockWrite(mode, &kTestMsg[i], 1, sequence_number++));
}
}
net::StaticSocketDataProvider data_provider(reads, writes);
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
data_provider.set_connect_data(
net::MockConnect(net::SYNCHRONOUS, net::OK, server_addr));
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt /*local_addr*/, server_addr, &client_socket_receive_handle,
&client_socket_send_handle);
// Loop kNumIterations times to test that writes can follow reads, and reads
// can follow writes.
for (int j = 0; j < kNumIterations; ++j) {
// Reading kMsgSize should coalesce the 1-byte mock reads.
EXPECT_EQ(kTestMsg, Read(&client_socket_receive_handle, kMsgSize));
// 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.
uint32_t first_write_size = kMsgSize / 2;
EXPECT_EQ(MOJO_RESULT_OK,
client_socket_send_handle->WriteData(
&kTestMsg[0], &first_write_size, MOJO_WRITE_DATA_FLAG_NONE));
// Flush the kMsgSize/2 byte write.
base::RunLoop().RunUntilIdle();
uint32_t second_write_size = kMsgSize - first_write_size;
EXPECT_EQ(MOJO_RESULT_OK,
client_socket_send_handle->WriteData(&kTestMsg[first_write_size],
&second_write_size,
MOJO_WRITE_DATA_FLAG_NONE));
// Flush the kMsgSize/2 byte write.
base::RunLoop().RunUntilIdle();
}
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
TEST_P(TCPSocketWithMockSocketTest, ReadError) {
mojom::TCPConnectedSocketPtr client_socket;
net::IoMode mode = GetParam();
net::MockRead reads[] = {net::MockRead(mode, net::ERR_FAILED)};
const char kTestMsg[] = "hello!";
net::MockWrite writes[] = {
net::MockWrite(mode, kTestMsg, strlen(kTestMsg), 0)};
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
net::StaticSocketDataProvider data_provider(reads, writes);
data_provider.set_connect_data(
net::MockConnect(net::SYNCHRONOUS, net::OK, server_addr));
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), observer()->GetObserverPtr(),
base::nullopt /*local_addr*/, server_addr, &client_socket_receive_handle,
&client_socket_send_handle);
EXPECT_EQ("", Read(&client_socket_receive_handle, 1));
EXPECT_EQ(net::ERR_FAILED, observer()->WaitForReadError());
// Writes can proceed even though there is a read error.
uint32_t num_bytes = strlen(kTestMsg);
EXPECT_EQ(MOJO_RESULT_OK,
client_socket_send_handle->WriteData(&kTestMsg, &num_bytes,
MOJO_WRITE_DATA_FLAG_NONE));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
TEST_P(TCPSocketWithMockSocketTest, WriteError) {
mojom::TCPConnectedSocketPtr client_socket;
net::IoMode mode = GetParam();
const char kTestMsg[] = "hello!";
// The first MockRead needs to complete asynchronously because otherwise it
// can't be paused to happen after the MockWrite.
net::MockRead reads[] = {
net::MockRead(net::ASYNC, kTestMsg, strlen(kTestMsg), 1),
net::MockRead(mode, net::OK, 2)};
net::MockWrite writes[] = {net::MockWrite(mode, net::ERR_FAILED, 0)};
net::SequencedSocketData data_provider(reads, writes);
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
data_provider.set_connect_data(
net::MockConnect(net::SYNCHRONOUS, net::OK, server_addr));
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), observer()->GetObserverPtr(),
base::nullopt /*local_addr*/, server_addr, &client_socket_receive_handle,
&client_socket_send_handle);
uint32_t num_bytes = strlen(kTestMsg);
EXPECT_EQ(MOJO_RESULT_OK,
client_socket_send_handle->WriteData(&kTestMsg, &num_bytes,
MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(net::ERR_FAILED, observer()->WaitForWriteError());
// Reads can proceed even though there is a read error.
EXPECT_EQ(kTestMsg, Read(&client_socket_receive_handle, strlen(kTestMsg)));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(data_provider.AllReadDataConsumed());
EXPECT_TRUE(data_provider.AllWriteDataConsumed());
}
TEST_P(TCPSocketWithMockSocketTest, InitialTCPConnectedSocketOptions) {
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
for (int receive_buffer_size :
{-1, 0, 1024, TCPConnectedSocket::kMaxBufferSize,
TCPConnectedSocket::kMaxBufferSize + 1}) {
for (int send_buffer_size :
{-1, 0, 2048, TCPConnectedSocket::kMaxBufferSize,
TCPConnectedSocket::kMaxBufferSize + 1}) {
for (int no_delay : {false, true}) {
mojom::TCPConnectedSocketPtr client_socket;
net::StaticSocketDataProvider data_provider;
data_provider.set_connect_data(
net::MockConnect(GetParam(), net::OK, server_addr));
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options =
mojom::TCPConnectedSocketOptions::New();
tcp_connected_socket_options->receive_buffer_size = receive_buffer_size;
tcp_connected_socket_options->send_buffer_size = send_buffer_size;
tcp_connected_socket_options->no_delay = no_delay;
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt /*local_addr*/, server_addr,
&client_socket_receive_handle, &client_socket_send_handle,
std::move(tcp_connected_socket_options)));
if (receive_buffer_size <= 0) {
EXPECT_EQ(-1, data_provider.receive_buffer_size());
} else if (receive_buffer_size <= TCPConnectedSocket::kMaxBufferSize) {
EXPECT_EQ(receive_buffer_size, data_provider.receive_buffer_size());
} else {
EXPECT_EQ(TCPConnectedSocket::kMaxBufferSize,
data_provider.receive_buffer_size());
}
if (send_buffer_size <= 0) {
EXPECT_EQ(-1, data_provider.send_buffer_size());
} else if (send_buffer_size <= TCPConnectedSocket::kMaxBufferSize) {
EXPECT_EQ(send_buffer_size, data_provider.send_buffer_size());
} else {
EXPECT_EQ(TCPConnectedSocket::kMaxBufferSize,
data_provider.send_buffer_size());
}
EXPECT_EQ(no_delay, data_provider.no_delay());
}
}
}
}
TEST_P(TCPSocketWithMockSocketTest, InitialTCPConnectedSocketOptionsFails) {
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
enum class FailedCall {
SET_RECEIVE_BUFFER_SIZE,
SET_SEND_BUFFER_SIZE,
SET_NO_DELAY,
};
for (const auto& failed_call :
{FailedCall::SET_RECEIVE_BUFFER_SIZE, FailedCall::SET_SEND_BUFFER_SIZE,
FailedCall::SET_NO_DELAY}) {
mojom::TCPConnectedSocketPtr client_socket;
net::StaticSocketDataProvider data_provider;
data_provider.set_connect_data(
net::MockConnect(GetParam(), net::OK, server_addr));
switch (failed_call) {
case FailedCall::SET_RECEIVE_BUFFER_SIZE:
data_provider.set_set_receive_buffer_size_result(net::ERR_FAILED);
break;
case FailedCall::SET_SEND_BUFFER_SIZE:
data_provider.set_set_send_buffer_size_result(net::ERR_FAILED);
break;
case FailedCall::SET_NO_DELAY:
data_provider.set_set_no_delay_result(false);
break;
}
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options =
mojom::TCPConnectedSocketOptions::New();
tcp_connected_socket_options->receive_buffer_size = 1;
tcp_connected_socket_options->send_buffer_size = 2;
tcp_connected_socket_options->no_delay = false;
EXPECT_EQ(net::ERR_FAILED,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt /*local_addr*/, server_addr,
&client_socket_receive_handle, &client_socket_send_handle,
std::move(tcp_connected_socket_options)));
}
}
TEST_P(TCPSocketWithMockSocketTest, SetBufferSizes) {
typedef struct {
int passed_buffer_size;
int expected_buffer_size;
} BufferSizeTestData;
static const BufferSizeTestData kBufferSizeDataTestCases[] = {
// Setting a buffer size < 0 is replaced by setting a buffer size of 0.
{-1, 0},
{1024, 1024},
{TCPConnectedSocket::kMaxBufferSize + 1,
TCPConnectedSocket::kMaxBufferSize},
{0, 0}};
mojom::TCPConnectedSocketPtr client_socket;
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
net::StaticSocketDataProvider data_provider;
data_provider.set_connect_data(
net::MockConnect(GetParam(), net::OK, server_addr));
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt /*local_addr*/, server_addr,
&client_socket_receive_handle, &client_socket_send_handle));
EXPECT_EQ(-1, data_provider.receive_buffer_size());
for (const BufferSizeTestData& test_case : kBufferSizeDataTestCases) {
net::TestCompletionCallback callback;
// Setting a buffer size < 0 is replaced by setting a buffer size of 0.
client_socket->SetReceiveBufferSize(test_case.passed_buffer_size,
callback.callback());
EXPECT_EQ(net::OK, callback.WaitForResult());
EXPECT_EQ(test_case.expected_buffer_size,
data_provider.receive_buffer_size());
}
EXPECT_EQ(-1, data_provider.send_buffer_size());
for (const BufferSizeTestData& test_case : kBufferSizeDataTestCases) {
net::TestCompletionCallback callback;
// Setting a buffer size < 0 is replaced by setting a buffer size of 0.
client_socket->SetSendBufferSize(test_case.passed_buffer_size,
callback.callback());
EXPECT_EQ(net::OK, callback.WaitForResult());
EXPECT_EQ(test_case.expected_buffer_size, data_provider.send_buffer_size());
}
}
TEST_P(TCPSocketWithMockSocketTest, SetBufferSizesFails) {
mojom::TCPConnectedSocketPtr client_socket;
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
net::StaticSocketDataProvider data_provider;
data_provider.set_connect_data(
net::MockConnect(GetParam(), net::OK, server_addr));
data_provider.set_set_receive_buffer_size_result(net::ERR_FAILED);
data_provider.set_set_send_buffer_size_result(net::ERR_UNEXPECTED);
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt /*local_addr*/, server_addr,
&client_socket_receive_handle, &client_socket_send_handle));
net::TestCompletionCallback receive_buffer_callback;
client_socket->SetReceiveBufferSize(1024, receive_buffer_callback.callback());
EXPECT_EQ(net::ERR_FAILED, receive_buffer_callback.WaitForResult());
net::TestCompletionCallback send_buffer_callback;
client_socket->SetSendBufferSize(1024, send_buffer_callback.callback());
EXPECT_EQ(net::ERR_UNEXPECTED, send_buffer_callback.WaitForResult());
}
TEST_F(TCPSocketWithMockSocketTest, SetNoDelayAndKeepAlive) {
net::StaticSocketDataProvider data_provider;
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
mojom::TCPConnectedSocketPtr client_socket;
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt, server_addr, &client_socket_receive_handle,
&client_socket_send_handle));
EXPECT_TRUE(data_provider.no_delay());
{
base::RunLoop run_loop;
client_socket->SetNoDelay(false /* no_delay */,
base::BindLambdaForTesting([&](bool success) {
EXPECT_TRUE(success);
run_loop.Quit();
}));
run_loop.Run();
EXPECT_FALSE(data_provider.no_delay());
}
{
base::RunLoop run_loop;
client_socket->SetNoDelay(true /* no_delay */,
base::BindLambdaForTesting([&](bool success) {
EXPECT_TRUE(success);
run_loop.Quit();
}));
run_loop.Run();
EXPECT_TRUE(data_provider.no_delay());
}
{
const int kKeepAliveDelay = 123;
base::RunLoop run_loop;
client_socket->SetKeepAlive(true /* enable */, kKeepAliveDelay,
base::BindLambdaForTesting([&](bool success) {
EXPECT_TRUE(success);
run_loop.Quit();
}));
run_loop.Run();
EXPECT_TRUE(data_provider.keep_alive_enabled());
EXPECT_EQ(kKeepAliveDelay, data_provider.keep_alive_delay());
}
{
base::RunLoop run_loop;
client_socket->SetKeepAlive(false /* enable */, 0 /* delay */,
base::BindLambdaForTesting([&](bool success) {
EXPECT_TRUE(success);
run_loop.Quit();
}));
run_loop.Run();
EXPECT_FALSE(data_provider.keep_alive_enabled());
}
{
const int kKeepAliveDelay = 1234;
base::RunLoop run_loop;
client_socket->SetKeepAlive(true /* enable */, kKeepAliveDelay,
base::BindLambdaForTesting([&](bool success) {
EXPECT_TRUE(success);
run_loop.Quit();
}));
run_loop.Run();
EXPECT_TRUE(data_provider.keep_alive_enabled());
EXPECT_EQ(kKeepAliveDelay, data_provider.keep_alive_delay());
}
}
TEST_F(TCPSocketWithMockSocketTest, SetNoDelayFails) {
net::StaticSocketDataProvider data_provider;
data_provider.set_set_no_delay_result(false);
data_provider.set_set_keep_alive_result(false);
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
mojom::TCPConnectedSocketPtr client_socket;
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt, server_addr, &client_socket_receive_handle,
&client_socket_send_handle));
{
base::RunLoop run_loop;
client_socket->SetNoDelay(false /* no_delay */,
base::BindLambdaForTesting([&](bool success) {
EXPECT_FALSE(success);
run_loop.Quit();
}));
run_loop.Run();
}
{
base::RunLoop run_loop;
client_socket->SetKeepAlive(true /* enable */, 123 /* delay */,
base::BindLambdaForTesting([&](bool success) {
EXPECT_FALSE(success);
run_loop.Quit();
}));
run_loop.Run();
}
}
TEST_F(TCPSocketWithMockSocketTest, SetOptionsAfterTLSUpgrade) {
// Populate with some mock reads, so UpgradeToTLS() won't error out because of
// a closed receive pipe.
const net::MockRead kReads[] = {
net::MockRead(net::ASYNC, "hello", 5 /* length */),
net::MockRead(net::ASYNC, net::OK)};
net::StaticSocketDataProvider data_provider(kReads,
base::span<net::MockWrite>());
net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::ERR_FAILED);
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_socket);
mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
mojom::TCPConnectedSocketPtr client_socket;
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
EXPECT_EQ(net::OK,
CreateTCPConnectedSocketSync(
mojo::MakeRequest(&client_socket), nullptr /*observer*/,
base::nullopt, server_addr, &client_socket_receive_handle,
&client_socket_send_handle));
// UpgradeToTLS will destroy network::TCPConnectedSocket::|socket_|. Calling
// SetNoDelay and SetKeepAlive should error out.
mojom::TLSClientSocketPtr tls_socket;
client_socket_receive_handle.reset();
client_socket_send_handle.reset();
{
base::RunLoop run_loop;
net::HostPortPair host_port_pair("example.org", 443);
client_socket->UpgradeToTLS(
host_port_pair, nullptr /* ssl_config_ptr */,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
mojo::MakeRequest(&tls_socket), nullptr /*observer */,
base::BindLambdaForTesting(
[&](int result,
mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
mojo::ScopedDataPipeProducerHandle send_pipe_handle,
const base::Optional<net::SSLInfo>& ssl_info) {
EXPECT_EQ(net::ERR_FAILED, result);
run_loop.Quit();
}));
run_loop.Run();
}
net::TestCompletionCallback receive_buffer_callback;
client_socket->SetReceiveBufferSize(1024, receive_buffer_callback.callback());
EXPECT_EQ(net::ERR_UNEXPECTED, receive_buffer_callback.WaitForResult());
EXPECT_EQ(-1, data_provider.receive_buffer_size());
net::TestCompletionCallback send_buffer_callback;
client_socket->SetSendBufferSize(1024, send_buffer_callback.callback());
EXPECT_EQ(net::ERR_UNEXPECTED, send_buffer_callback.WaitForResult());
EXPECT_EQ(-1, data_provider.send_buffer_size());
{
base::RunLoop run_loop;
client_socket->SetNoDelay(false /* no_delay */,
base::BindLambdaForTesting([&](bool success) {
EXPECT_FALSE(success);
run_loop.Quit();
}));
run_loop.Run();
EXPECT_TRUE(data_provider.no_delay());
}
{
base::RunLoop run_loop;
client_socket->SetKeepAlive(true /* enable */, 123 /* delay */,
base::BindLambdaForTesting([&](bool success) {
EXPECT_FALSE(success);
run_loop.Quit();
}));
run_loop.Run();
}
}
TEST_F(TCPSocketWithMockSocketTest, SocketDestroyedBeforeConnectCompletes) {
std::vector<net::MockRead> reads;
std::vector<net::MockWrite> writes;
net::StaticSocketDataProvider data_provider(reads, writes);
data_provider.set_connect_data(
net::MockConnect(net::ASYNC, net::ERR_IO_PENDING));
mock_client_socket_factory_.AddSocketDataProvider(&data_provider);
mojom::TCPConnectedSocketPtr client_socket;
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 1234);
net::AddressList remote_addr_list(server_addr);
int net_error = net::OK;
base::RunLoop run_loop;
factory()->CreateTCPConnectedSocket(
base::nullopt, remote_addr_list,
nullptr /* tcp_connected_socket_options */, TRAFFIC_ANNOTATION_FOR_TESTS,
mojo::MakeRequest(&client_socket), nullptr,
base::BindLambdaForTesting(
[&](int result,
const base::Optional<net::IPEndPoint>& actual_local_addr,
const base::Optional<net::IPEndPoint>& peer_addr,
mojo::ScopedDataPipeConsumerHandle receive_pipe_handle,
mojo::ScopedDataPipeProducerHandle send_pipe_handle) {
net_error = result;
run_loop.Quit();
}));
client_socket.reset();
run_loop.Run();
EXPECT_EQ(net::ERR_ABORTED, net_error);
}
// Tests the case where net::ServerSocket::Listen() succeeds but
// net::ServerSocket::GetLocalAddress() fails. This should still be considered
// as a failure.
TEST(TCPServerSocketTest, GetLocalAddressFailedInListen) {
base::test::TaskEnvironment task_environment(
base::test::TaskEnvironment::MainThreadType::IO);
TCPServerSocket socket(nullptr /* delegate */, nullptr /* net_log */,
TRAFFIC_ANNOTATION_FOR_TESTS);
socket.SetSocketForTest(std::make_unique<FailingServerSocket>());
net::IPEndPoint local_addr;
EXPECT_EQ(net::ERR_FAILED, socket.Listen(local_addr, 1, &local_addr));
}
} // namespace network