parselink-old/source/client/client.cpp

139 lines
4.1 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 <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/tcp.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;
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...");
net::ip::tcp::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;
}
net::ip::tcp::socket socket(io_context_);
for (auto const& r : results) {
logger.debug("Connecting to {}", r.endpoint());
auto [ec] = co_await socket.async_connect(r, 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) {
logger.info("Received signal: {}. Shutting down.", sig);
});
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();
}