blob: 38feb4f005db468b50d1f7c48208e5ea8522c053 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/api/sockets_udp/test_udp_echo_server.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/host_port_pair.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/log/net_log_source.h"
#include "net/socket/udp_server_socket.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/udp_socket.mojom.h"
#include "services/network/test/test_udp_socket.h"
#include "services/network/test/udp_socket_test_util.h"
namespace extensions {
class TestUdpSocketListenerImpl : public network::test::UDPSocketListenerImpl {
public:
TestUdpSocketListenerImpl() = default;
~TestUdpSocketListenerImpl() override = default;
TestUdpSocketListenerImpl(const TestUdpSocketListenerImpl&) = delete;
TestUdpSocketListenerImpl& operator=(const TestUdpSocketListenerImpl&) =
delete;
using OnReceivedCallback = base::OnceCallback<
void(int32_t, net::IPEndPoint, base::span<const uint8_t>)>;
void SetOnReceivedCallback(OnReceivedCallback on_received_callback) {
on_received_callback_ = std::move(on_received_callback);
}
private:
void OnReceived(int32_t result,
const std::optional<net::IPEndPoint>& src_addr,
std::optional<base::span<const uint8_t>> data) override {
if (on_received_callback_) {
std::move(on_received_callback_)
.Run(result, src_addr.value(), data.value());
}
}
OnReceivedCallback on_received_callback_;
};
class TestUdpEchoServer::Core {
public:
Core() : udp_listener_receiver_(&listener_impl_) {}
~Core() = default;
Core(const Core&) = delete;
Core& operator=(const Core&) = delete;
void Start(network::mojom::NetworkContext* network_context,
net::HostPortPair* host_port_pair,
int* result) {
network_context->CreateUDPSocket(
server_socket_.BindNewPipeAndPassReceiver(),
udp_listener_receiver_.BindNewPipeAndPassRemote());
server_socket_.set_disconnect_handler(
base::BindLambdaForTesting([]() { NOTREACHED(); }));
net::IPEndPoint server_addr(net::IPAddress::IPv4Localhost(), 0);
auto server_helper =
std::make_unique<network::test::UDPSocketTestHelper>(&server_socket_);
*result = server_helper->BindSync(server_addr, nullptr, &server_addr);
*host_port_pair = net::HostPortPair::FromIPEndPoint(server_addr);
SetUpRead();
}
void SetUpRead() {
// Set up server socket and listener to expect another read.
server_socket_->ReceiveMore(1);
listener_impl_.SetOnReceivedCallback(base::BindOnce(
&TestUdpEchoServer::Core::OnRead, core_weak_ptr_factory_.GetWeakPtr()));
}
void OnRead(int32_t result,
net::IPEndPoint src_addr,
base::span<const uint8_t> data) {
// Consider read errors fatal, to avoid getting into a read error loop.
if (result < 0) {
LOG(ERROR) << "Error reading from socket: " << net::ErrorToString(result);
return;
}
server_socket_->SendTo(
src_addr, data,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
base::BindOnce(&TestUdpEchoServer::Core::OnSend,
core_weak_ptr_factory_.GetWeakPtr(), result));
}
void OnSend(int32_t read_result, int32_t send_result) {
if (send_result < 0) {
LOG(ERROR) << "Error writing to socket: "
<< net::ErrorToString(send_result);
} else if (send_result != read_result) {
LOG(ERROR) << "Failed to write entire message. Expected " << read_result
<< ", but wrote " << send_result;
}
SetUpRead();
}
private:
mojo::Remote<network::mojom::UDPSocket> server_socket_;
std::unique_ptr<network::test::UDPSocketTestHelper> server_socket_helper_;
mojo::Receiver<network::mojom::UDPSocketListener> udp_listener_receiver_;
TestUdpSocketListenerImpl listener_impl_;
base::WeakPtrFactory<TestUdpEchoServer::Core> core_weak_ptr_factory_{this};
};
TestUdpEchoServer::TestUdpEchoServer() = default;
TestUdpEchoServer::~TestUdpEchoServer() {
base::SequencedTaskRunner::GetCurrentDefault()->DeleteSoon(FROM_HERE,
std::move(core_));
base::RunLoop run_loop;
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
}
bool TestUdpEchoServer::Start(network::mojom::NetworkContext* network_context,
net::HostPortPair* host_port_pair) {
core_ = std::make_unique<Core>();
int result = net::ERR_UNEXPECTED;
core_->Start(network_context, host_port_pair, &result);
return result == net::OK;
}
} // namespace extensions