parselink-old/source/client/client.cpp
Kurt Sassenrath a65bff1697 Lolwut
2024-01-17 14:30:08 -08:00

148 lines
4.5 KiB
C++

//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Client
//
// Client implementation. Most of this should be agnostic to the targeted
// platform (e.g. Linux vs Windows).
//
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
#include "client.h"
#include "parselink/logging.h"
#include "parselink/utility/ctstring.h"
#include "parselink/msgpack/core/packer.h"
#include <boost/asio/as_tuple.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/beast.hpp>
using namespace parselink;
using namespace parselink::client;
namespace net = boost::asio;
using net::awaitable;
using net::co_spawn;
using net::detached;
using net::use_awaitable;
using udp = net::ip::udp;
namespace {
logging::logger logger{"client"};
constexpr auto no_ex_coro = net::as_tuple(use_awaitable);
class simple_client {
public:
simple_client(config const& cfg) noexcept;
std::error_code run() noexcept;
private:
awaitable<void> connect_to_server() noexcept;
awaitable<void> connect_to_websocket() noexcept;
net::io_context io_context_;
std::string server_address_;
std::uint16_t server_port_;
std::string websocket_address_;
std::uint16_t websocket_port_;
};
static_assert(interface<simple_client>);
simple_client::simple_client(config const& cfg) noexcept
: io_context_{1} {
server_address_ =
cfg.server_address.empty() ? "localhost" : cfg.server_address;
server_port_ = !cfg.server_port ? 9001 : cfg.server_port;
websocket_address_ =
cfg.websocket_address.empty() ? "localhost" : cfg.websocket_address;
websocket_port_ = !cfg.websocket_port ? 10501 : cfg.websocket_port;
logger.debug("Creating parselink client. Configured server: {}:{}, "
"websocket: {}:{}.",
server_address_, server_port_, websocket_address_, websocket_port_);
};
awaitable<void> simple_client::connect_to_server() noexcept {
logger.debug("Connecting to parselink server...");
udp::resolver resolver(io_context_);
auto [ec, results] = co_await resolver.async_resolve(
{server_address_, std::to_string(server_port_)}, no_ex_coro);
if (ec) {
logger.error("Unable to resolve {}:{}: {}", server_address_,
server_port_, ec);
co_return;
} else if (results.empty()) {
logger.error("Unable to resolve {}:{} to an endpoint.", server_address_,
server_port_);
co_return;
}
for (auto const& r : results) {
udp::socket socket(io_context_);
socket.open(r.endpoint().protocol());
logger.debug("Connecting to {}", r.endpoint());
std::array<std::byte, 4096> buff;
msgpack::packer packer(buff);
packer.pack("hello");
auto span = packer.subspan();
auto [ec, bw] = co_await socket.async_send_to(
net::buffer(span.data(), span.size()), r.endpoint(),
no_ex_coro);
if (ec) {
logger.error("connection to {} failed: {}",
results.begin()->endpoint(), ec);
continue;
}
}
logger.error("Unable to connect to any resolved endpoints.");
co_return;
}
awaitable<void> simple_client::connect_to_websocket() noexcept {
logger.debug("Connecting to websocket server...");
co_return;
}
std::error_code simple_client::run() noexcept {
logger.debug("Starting client.");
net::signal_set signals(io_context_, SIGINT, SIGTERM);
signals.async_wait([&](auto sig, auto g) {
logger.info("Received signal: {} {}. Shutting down.", sig, g);
});
co_spawn(io_context_, connect_to_websocket(), detached);
co_spawn(io_context_, connect_to_server(), detached);
io_context_.run();
return std::make_error_code(std::errc::no_link);
};
} // anonymous namespace
std::error_code parselink::client::create_and_run(config const& cfg) noexcept {
simple_client client(cfg);
return client.run();
}