| // 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 |