#pragma once #include "error.hpp" #include #include #include #include #include namespace math::client { namespace transport { class Error : public client::Error { public: explicit Error(const std::string& msg) : client::Error{"transport error: " + msg} { } }; } class Transport { public: virtual ~Transport() = default; using ProcessResult = std::function; virtual void send_query(const std::string&, const ProcessResult&) = 0; }; using TransportPtr = std::unique_ptr; class NetworkTransport : public Transport { public: static constexpr auto DEFAULT_PORT = "18000"; NetworkTransport(const std::string& host, const std::string& port) : m_host{host}, m_port{port} { } protected: const std::string m_host; const std::string m_port; }; class BlockingNetworkTransport : public NetworkTransport { public: BlockingNetworkTransport(const std::string &host, const std::string& port) : NetworkTransport{host, port}, m_socket{m_io_context} { try { connect(); } catch (const boost::system::system_error& e) { throw transport::Error{e.what()}; } } void send_query(const std::string& query, const ProcessResult& on_reply) override { std::string reply; try { reply = send_query(query); } catch (const boost::system::system_error& e) { throw transport::Error{e.what()}; } on_reply(reply); } private: void connect() { boost::asio::ip::tcp::resolver resolver{m_io_context}; boost::asio::connect(m_socket, resolver.resolve(m_host, m_port)); } std::string send_query(const std::string& query) { write(query); return read_line(); } void write(std::string input) { input += '\n'; boost::asio::write(m_socket, boost::asio::const_buffer{input.c_str(), input.size()}); } std::string read_line() { const auto bytes = boost::asio::read_until(m_socket, m_buffer, '\n'); const auto data = boost::asio::buffer_cast(m_buffer.data()); const std::string result{data, bytes - 1}; m_buffer.consume(bytes); return result; } boost::asio::io_context m_io_context; boost::asio::ip::tcp::socket m_socket; boost::asio::streambuf m_buffer; }; inline TransportPtr make_blocking_network_transport( const std::string& host, const std::string& port) { return std::make_unique(host, port); } }