402 lines
11 KiB
C++
402 lines
11 KiB
C++
#pragma once
|
|
|
|
/*
|
|
|
|
ABSTRACT DEVICE COMPONENTS LIBRARY
|
|
|
|
*/
|
|
|
|
#include <filesystem>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <set>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
|
|
#if __has_include(<unistd.h>) // POSIX
|
|
#define FORK_EXISTS 1
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <cerrno>
|
|
#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 <interfaces::adc_netsession_c SessionT>
|
|
constexpr static bool anySessionPredicate(const typename SessionT::netsession_ident_t&)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// started sessions weak pointers
|
|
template <interfaces::adc_netsession_c SessionT>
|
|
static std::unordered_map<const AdcNetSessionManager*, std::unordered_set<std::weak_ptr<SessionT>>> _serverSessions;
|
|
std::vector<std::function<bool()>> _stopSessionFunc;
|
|
std::vector<std::function<void(const AdcNetSessionManager*)>> _moveCtorFunc;
|
|
|
|
template <interfaces::adc_netsession_c SessionT>
|
|
void startSession(std::shared_ptr<SessionT>& sess_ptr)
|
|
{
|
|
auto res = _serverSessions<SessionT>[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<SessionT>[this].erase(res.first);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
// define move-function only once per SessionT!
|
|
if (_serverSessions<SessionT>[this].size() == 1) {
|
|
_moveCtorFunc.emplace_back([this](const AdcNetSessionManager* new_instance) {
|
|
_serverSessions<SessionT>[new_instance] = std::move(_serverSessions<SessionT>[this]);
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
template <interfaces::adc_netsession_c SessionT,
|
|
std::predicate<typename SessionT::netsession_ident_t> PredT = decltype(anySessionPredicate<SessionT>)>
|
|
size_t stopSessions(PredT&& comp_func = anySessionPredicate<SessionT>())
|
|
{
|
|
size_t N = 0;
|
|
|
|
std::set<std::weak_ptr<SessionT>> remove_wptr;
|
|
|
|
for (auto& wptr : _serverSessions<SessionT>[this]) {
|
|
if (std::shared_ptr<SessionT> sptr = wptr.lock()) {
|
|
if constexpr (std::same_as<PredT, decltype(anySessionPredicate<SessionT>)>) {
|
|
sptr->stop();
|
|
remove_wptr.emplace(wptr);
|
|
++N;
|
|
} else {
|
|
if (std::forward<PredT>(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<SessionT>[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 <traits::adc_input_char_range R>
|
|
AdcGenericNetServer(const R& id) : _serverIdent()
|
|
{
|
|
if constexpr (std::is_array_v<std::remove_cvref_t<R>>) {
|
|
_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 <interfaces::adc_netsession_c SessionT, typename... AcceptorCtorArgTs>
|
|
void start(const typename SessionT::netsession_ident_t& id,
|
|
const typename SessionT::netsession_ctx_t& sess_ctx,
|
|
AcceptorCtorArgTs&&... ctor_args)
|
|
{
|
|
if (!_isListening<SessionT>[this][id]) {
|
|
auto acceptor = std::make_shared<typename SessionT::netservice_t::acceptor_t>(
|
|
std::forward<AcceptorCtorArgTs>(ctor_args)...);
|
|
|
|
_stopListenFunc.emplace_back([acceptor, id, this]() {
|
|
std::error_code ec;
|
|
acceptor->close(ec);
|
|
|
|
_isListening<SessionT>[this][id] = false;
|
|
});
|
|
|
|
doAccept<SessionT>(acceptor, id, sess_ctx);
|
|
}
|
|
|
|
// only once per SessionT
|
|
if (_isListening<SessionT>[this].size() == 1) {
|
|
_moveCtorFunc.emplace_back([this](const AdcGenericNetServer* new_instance) {
|
|
_isListening<SessionT>[new_instance] = std::move(_isListening<SessionT>[this]);
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
template <interfaces::adc_netsession_c SessionT>
|
|
bool isListening(const typename SessionT::netsession_ident_t& id) const
|
|
{
|
|
return _isListening<SessionT>[this][id];
|
|
}
|
|
|
|
virtual void start() = 0;
|
|
|
|
virtual void stop()
|
|
{
|
|
for (auto& func : _stopListenFunc) {
|
|
func();
|
|
}
|
|
|
|
_stopListenFunc.clear();
|
|
|
|
stopAllSessions();
|
|
};
|
|
|
|
|
|
|
|
protected:
|
|
// template <interfaces::adc_netsession_c SessionT>
|
|
// inline static std::unordered_map<const AdcGenericNetServer*, bool> _isListening{};
|
|
template <interfaces::adc_netsession_c SessionT>
|
|
inline static std::unordered_map<const AdcGenericNetServer*,
|
|
std::map<const typename SessionT::netsession_ident_t, bool>>
|
|
_isListening{};
|
|
|
|
std::vector<std::function<void()>> _stopListenFunc;
|
|
std::vector<std::function<void(const AdcGenericNetServer*)>> _moveCtorFunc;
|
|
|
|
server_ident_t _serverIdent;
|
|
|
|
template <typename SessionT, typename AT, typename IDT, typename CTXT>
|
|
void doAccept(std::shared_ptr<AT> 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<SessionT>(id, std::move(srv), sess_ctx);
|
|
startSession(sess);
|
|
|
|
_isListening<SessionT>[this][id] = true;
|
|
doAccept<SessionT>(acceptor, id, sess_ctx);
|
|
} else {
|
|
_isListening<SessionT>[this][id] = false;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
template <interfaces::adc_netservice_c NetServiceT>
|
|
class AdcAbstractNetServer
|
|
{
|
|
public:
|
|
struct ServerEvents {
|
|
std::function<void(NetServiceT*)> onOpen;
|
|
std::function<void(NetServiceT*, std::error_code)> onClose;
|
|
std::function<void(NetServiceT*, std::span<const char>)> onData;
|
|
std::function<void(NetServiceT*, std::error_code)> onError;
|
|
};
|
|
|
|
|
|
AdcAbstractNetServer(ServerEvents&& events) : _serverEvents(std::move(events)) {}
|
|
|
|
template <typename... AcceptorCtorArgTs>
|
|
void listen(const typename NetServiceT::endpoint_t& endpoint, AcceptorCtorArgTs&&... ctor_args)
|
|
{
|
|
auto acceptor =
|
|
std::make_shared<typename NetServiceT::acceptor_t>(endpoint, std::forward<AcceptorCtorArgTs>(ctor_args)...);
|
|
}
|
|
|
|
void stop() {}
|
|
|
|
protected:
|
|
ServerEvents _serverEvents;
|
|
|
|
typename NetServiceT::acceptor_t _acceptor;
|
|
};
|
|
|
|
|
|
} // namespace adc
|