#ifndef _CONNECTION_HPP #define _CONNECTION_HPP #include "generic.pb.h" #include #include #include #include #include #include #include #include #include #include namespace Connection { using namespace boost; struct Packet { Generic::Operation op; std::unique_ptr data = nullptr; }; struct Error : public std::runtime_error { Packet packet; Error(const std::string& description, Generic::Error::Code code) : std::runtime_error(description) { packet.op.set_code(Generic::Operation::Error); auto error = std::make_unique(); error->set_description(description); error->set_code(code); packet.data = std::move(error); } }; struct FatalError : public Error { using Error::Error; }; class Client : public std::enable_shared_from_this { std::vector>& clients; asio::ip::tcp::socket socket; std::string username; bool good = true; public: Client(asio::io_context& ioc, std::vector>& clients) : socket(ioc), clients(clients) {} asio::awaitable receivePacket(); asio::awaitable sendProtobuf(const google::protobuf::Message& buffer); asio::awaitable sendPacket(const Packet& packet); asio::awaitable handlePacket(const Packet& packet); asio::awaitable connect(); template asio::awaitable receiveProtobuf(uint16_t maxSize = 1024) { // Receive size uint16_t size; co_await asio::async_read(socket, asio::buffer(&size, sizeof(size)), asio::use_awaitable); // Check size if (size > maxSize) { throw FatalError("Buffer too large ("+std::to_string(size)+" > "+std::to_string(maxSize)+')', Generic::Error::TooLarge); } // Receive protobuf Buffer protobuf; if (size) { std::vector rawProtobuf(size); co_await asio::async_read(socket, asio::buffer(rawProtobuf.data(), rawProtobuf.size()), asio::use_awaitable); // Parse protobuf protobuf.ParseFromArray(rawProtobuf.data(), static_cast(size)); } // Return it co_return protobuf; } asio::awaitable sendOK() { Packet packet; packet.op.set_code(Generic::Operation::OK); co_await sendPacket(packet); } auto& getSocket() { return socket; } const auto& getUsername() const { return username; } bool isGood() const { return good; } bool isAuthed() const { return good && !username.empty(); } }; class Server { asio::io_context& ioc; std::vector> clients; public: Server(asio::io_context& ioc) : ioc(ioc) {} asio::awaitable listen(int port); }; } #endif