blob: a4d8063e6f334d4bf3d1e297fdb151d4cfdcfdec [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 "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"
namespace extensions {
// Size of UDP buffer. Max allowed read size.
static const size_t kIOBufferSize = 4096;
class TestUdpEchoServer::Core {
public:
Core() = default;
~Core() = default;
Core(const Core&) = delete;
Core& operator=(const Core&) = delete;
void Start(net::HostPortPair* host_port_pair, int* result) {
udp_server_socket_ = std::make_unique<net::UDPServerSocket>(
nullptr /* net_log */, net::NetLogSource());
io_buffer_ = base::MakeRefCounted<net::IOBufferWithSize>(kIOBufferSize);
*result = udp_server_socket_->Listen(
net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0 /* port */));
if (*result != net::OK) {
LOG(ERROR) << "UDP listen failed.";
udp_server_socket_.reset();
return;
}
net::IPEndPoint local_address;
*result = udp_server_socket_->GetLocalAddress(&local_address);
if (*result != net::OK) {
LOG(ERROR) << "Failed to get local address.";
udp_server_socket_.reset();
return;
}
*host_port_pair = net::HostPortPair::FromIPEndPoint(local_address);
ReadLoop();
}
void ReadLoop() {
int result = udp_server_socket_->RecvFrom(
io_buffer_.get(), io_buffer_->size(), &recv_from_address_,
base::BindOnce(&Core::OnReadComplete, base::Unretained(this)));
if (result != net::ERR_IO_PENDING)
OnReadComplete(result);
}
void OnReadComplete(int result) {
if (result < 0) {
// Consider read errors fatal, to avoid getting into a read error loop.
LOG(ERROR) << "Error reading from socket: " << net::ErrorToString(result);
return;
}
int send_result = udp_server_socket_->SendTo(
io_buffer_.get(), result, recv_from_address_,
base::BindOnce(&Core::OnSendComplete, base::Unretained(this), result));
if (send_result != net::ERR_IO_PENDING)
OnSendComplete(result, send_result);
}
void OnSendComplete(int expected_result, int result) {
// Don't consider write errors to be fatal.
if (result < 0) {
LOG(ERROR) << "Error writing to socket: " << net::ErrorToString(result);
} else if (result != expected_result) {
LOG(ERROR) << "Failed to write entire message. Expected "
<< expected_result << ", but wrote " << result;
}
ReadLoop();
}
private:
std::unique_ptr<net::UDPServerSocket> udp_server_socket_;
scoped_refptr<net::IOBufferWithSize> io_buffer_;
net::IPEndPoint recv_from_address_;
};
TestUdpEchoServer::TestUdpEchoServer() = default;
TestUdpEchoServer::~TestUdpEchoServer() {
if (io_thread_) {
io_thread_->task_runner()->DeleteSoon(FROM_HERE, std::move(core_));
base::RunLoop run_loop;
io_thread_->task_runner()->PostTask(FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
io_thread_.reset();
}
}
bool TestUdpEchoServer::Start(net::HostPortPair* host_port_pair) {
DCHECK(!io_thread_);
core_ = std::make_unique<Core>();
base::Thread::Options thread_options;
thread_options.message_pump_type = base::MessagePumpType::IO;
io_thread_ = std::make_unique<base::Thread>("EmbeddedTestServer IO Thread");
CHECK(io_thread_->StartWithOptions(std::move(thread_options)));
CHECK(io_thread_->WaitUntilThreadStarted());
base::RunLoop run_loop;
int result = net::ERR_UNEXPECTED;
io_thread_->task_runner()->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&TestUdpEchoServer::Core::Start,
base::Unretained(core_.get()), host_port_pair, &result),
run_loop.QuitClosure());
run_loop.Run();
return result == net::OK;
}
} // namespace extensions