//----------------------------------------------------------------------------- // ___ __ _ _ // / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __ // / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ / // / ___/ (_| | | \__ \ __/ /__| | | | | < // \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ . // //----------------------------------------------------------------------------- // 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 #include #include #include #include #include #include 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 connect_to_server() noexcept; awaitable 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(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 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_); logger.debug("Attempting connection with {}", results.begin()->endpoint()); auto [ec2] = co_await socket.async_connect(*results.begin(), no_ex_coro); if (ec2) { logger.error("connection to {} failed: {}", results.begin()->endpoint(), ec2); co_return; } co_return; } awaitable 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(); }