#pragma once /* ABSTRACT DEVICE COMPONENTS LIBRARY */ #include #include #include #include #include #if __has_include() // POSIX #define FORK_EXISTS 1 #include #include #include #endif #include "adc_net_concepts.h" namespace adc { /* SOME USEFULL PRIVITIVES */ // Ageneric implementation pf POSIX OS daemon class AdcPosixGenericDaemon { public: virtual ~AdcPosixGenericDaemon() = default; // run server as daemon (still only on POSIX OSes) void daemonize() { daemonizePrepare(); // reference implementation of forking for POSIX OSes #ifdef FORK_EXISTS // get TEMP directory in OS auto tmp_path = std::filesystem::temp_directory_path(); if (tmp_path.empty()) { tmp_path = std::filesystem::current_path().root_path(); } // _ioContext.notify_fork(asio::execution_context::fork_prepare); if (pid_t pid = fork()) { if (pid > 0) { exit(0); } else { throw std::system_error(errno, std::generic_category(), "CANNOT FORK 1-STAGE"); } } if (setsid() == -1) { throw std::system_error(errno, std::generic_category(), "CANNOT FORK SETSID"); } std::filesystem::current_path(tmp_path); umask(0); if (pid_t pid = fork()) { if (pid > 0) { exit(0); } else { throw std::system_error(errno, std::generic_category(), "CANNOT FORK 2-STAGE"); } } close(0); close(1); close(2); // _ioContext.notify_fork(asio::io_context::fork_child); #endif daemonizeFinalize(); } protected: virtual void daemonizePrepare() = 0; virtual void daemonizeFinalize() = 0; }; // a basic network session manager (basic start and stop functionality) class AdcNetSessionManager { public: virtual ~AdcNetSessionManager() = default; protected: AdcNetSessionManager() = default; AdcNetSessionManager(const AdcNetSessionManager&) = delete; AdcNetSessionManager(AdcNetSessionManager&& other) { if (this == &other) { return; } for (auto& func : _moveCtorFunc) { func(this); } _stopSessionFunc = std::move(other._stopSessionFunc); _moveCtorFunc = std::move(other._moveCtorFunc); } AdcNetSessionManager& operator=(const AdcNetSessionManager&) = delete; AdcNetSessionManager& operator=(AdcNetSessionManager&& other) { if (this != &other) { for (auto& func : _moveCtorFunc) { func(this); } _stopSessionFunc = std::move(other._stopSessionFunc); _moveCtorFunc = std::move(other._moveCtorFunc); } return *this; }; template constexpr static bool anySessionPredicate(const typename SessionT::netsession_ident_t&) { return true; } // started sessions weak pointers template static std::unordered_map>> _serverSessions; std::vector> _stopSessionFunc; std::vector> _moveCtorFunc; template void startSession(std::shared_ptr& sess_ptr) { auto res = _serverSessions[this].emplace(sess_ptr); if (res.second) { sess_ptr.start(); _stopSessionFunc.emplace_back([res, this]() { if (!res.first.expired()) { // session is still existing auto sess = res.first.lock(); sess->stop(); _serverSessions[this].erase(res.first); return true; } else { return false; } }); } // define move-function only once per SessionT! if (_serverSessions[this].size() == 1) { _moveCtorFunc.emplace_back([this](const AdcNetSessionManager* new_instance) { _serverSessions[new_instance] = std::move(_serverSessions[this]); }); } } template PredT = decltype(anySessionPredicate)> size_t stopSessions(PredT&& comp_func = anySessionPredicate()) { size_t N = 0; std::set> remove_wptr; for (auto& wptr : _serverSessions[this]) { if (std::shared_ptr sptr = wptr.lock()) { if constexpr (std::same_as)>) { sptr->stop(); remove_wptr.emplace(wptr); ++N; } else { if (std::forward(comp_func)(sptr->ident())) { sptr->stop(); remove_wptr.emplace(wptr); ++N; } } } else { // remove already stopped sessions?!! remove_wptr.emplace(wptr); } } for (auto& wptr : remove_wptr) { _serverSessions[this].erase(wptr); } return N; } size_t stopAllSessions() { size_t N = 0; for (auto& func : _stopSessionFunc) { func() ? ++N : 0; } _stopSessionFunc.clear(); _moveCtorFunc.clear(); // there are nothing to move after stopping of all sessions return N; } }; /* very generic network server */ class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager { public: typedef std::string server_ident_t; template AdcGenericNetServer(R&& id) : _serverIdent() { if constexpr (std::is_array_v>) { _serverIdent = id; } else { _serverIdent = server_ident_t{id.begin(), id.end()}; } } AdcGenericNetServer(const AdcGenericNetServer&) = delete; AdcGenericNetServer(AdcGenericNetServer&& other) : AdcPosixGenericDaemon(std::move(other)), AdcNetSessionManager(std::move(other)) { if (this == &other) { return; } _serverIdent = std::move(other._serverIdent); _stopListenFunc = std::move(other._stopListenFunc); for (auto& func : other._moveCtorFunc) { func(this); } _moveCtorFunc = std::move(other._moveCtorFunc); } virtual ~AdcGenericNetServer() = default; AdcGenericNetServer& operator=(const AdcGenericNetServer&) = delete; AdcGenericNetServer& operator=(AdcGenericNetServer&& other) { if (this != &other) { AdcPosixGenericDaemon::operator=(std::move(other)); AdcNetSessionManager::operator=(std::move(other)); _serverIdent = std::move(other._serverIdent); _stopListenFunc = std::move(other._stopListenFunc); for (auto& func : other._moveCtorFunc) { func(this); } _moveCtorFunc = std::move(other._moveCtorFunc); } return *this; } virtual server_ident_t ident() const { return _serverIdent; } // start accepting remote connections, create and start given network session // It must be assumed that this is asynchronous operation!!! template void start(const typename SessionT::netsession_ident_t& id, const typename SessionT::netsession_ctx_t& sess_ctx, AcceptorCtorArgTs&&... ctor_args) { if (!_isListening[this][id]) { auto acceptor = std::make_shared( std::forward(ctor_args)...); _stopListenFunc.emplace_back([acceptor, id, this]() { std::error_code ec; acceptor->close(ec); _isListening[this][id] = false; }); doAccept(acceptor, id, sess_ctx); } // only once per SessionT if (_isListening[this].size() == 1) { _moveCtorFunc = [this](const AdcGenericNetServer* new_instance) { _isListening[new_instance] = std::move(_isListening[this]); }; } }; template bool isListening(const typename SessionT::netsession_ident_t& id) const { return _isListening[this][id]; } virtual void start() = 0; virtual void stop() { for (auto& func : _stopListenFunc) { func(); } _stopListenFunc.clear(); stopAllSessions(); }; protected: // template // inline static std::unordered_map _isListening{}; template inline static std::unordered_map> _isListening{}; std::vector> _stopListenFunc; std::vector> _moveCtorFunc; server_ident_t _serverIdent; template void doAccept(std::shared_ptr acceptor, const IDT& id, const CTXT& sess_ctx) { acceptor->asyncAccept([acceptor, &id, &sess_ctx, this](auto ec, typename SessionT::netservice_t srv) mutable { if (!ec) { auto sess = std::make_shared(id, std::move(srv), sess_ctx); startSession(sess); _isListening[this][id] = true; doAccept(acceptor, id, sess_ctx); } else { _isListening[this][id] = false; } }); } }; template class AdcAbstractNetServer { public: struct ServerEvents { std::function onOpen; std::function onClose; std::function)> onData; std::function onError; }; AdcAbstractNetServer(ServerEvents&& events) : _serverEvents(std::move(events)) {} template void listen(const typename NetServiceT::endpoint_t& endpoint, AcceptorCtorArgTs&&... ctor_args) { auto acceptor = std::make_shared(endpoint, std::forward(ctor_args)...); } void stop() {} protected: ServerEvents _serverEvents; typename NetServiceT::acceptor_t _acceptor; }; } // namespace adc