139 lines
4.1 KiB
C++
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();
|
|
}
|