This commit is contained in:
Timur A. Fatkhullin 2024-10-21 18:11:35 +03:00
parent b8fdae9d16
commit 0d62c9defc
9 changed files with 247 additions and 68 deletions

View File

@ -626,6 +626,26 @@ static auto AdcSplitCharRange(R&& r, DR&& delim, size_t start = 0, size_t num =
}
static constexpr bool AdcCharRangeCompare(const traits::adc_char_view auto& what,
const traits::adc_char_view auto& where,
bool case_insensitive = false)
{
if (std::ranges::size(what) == std::ranges::size(where)) {
if (case_insensitive) {
auto f = std::ranges::search(where,
std::views::transform(what, [](const char& ch) { return std::tolower(ch); }));
return !f.empty();
} else {
auto f = std::ranges::search(where, what);
return !f.empty();
}
}
return false;
}
// FVN-1a hash function
template <traits::adc_input_char_range R>
static constexpr size_t AdcFNV1aHash(const R& r)

View File

@ -205,7 +205,7 @@ protected:
public:
template <interfaces::adc_netservice_c NetServiceT>
class Session : std::enable_shared_from_this<Session<NetServiceT>>
class Session : public std::enable_shared_from_this<Session<NetServiceT>>
{
public:
typedef std::string netsession_ident_t;

View File

@ -2,6 +2,9 @@
#include <algorithm>
#include "../common/adc_utils.h"
/*
ABSTRACT DEVICE COMPONENTS LIBRARY
@ -19,13 +22,18 @@ namespace adc
* Very simple various protocols endpoint parser and holder class
*
* endpoint: proto_mark://host_name:port_num/path
* where "part" is optional for all protocol kinds;
* where "part" is optional for all non-local protocol kinds;
*
* for the "local" kind protocol the endpoint string must consists of
* only "proto_mark" and "host_name" fields
* (e.g.: local://APP_UNIX_SOCKET)
* for local kind of protocols the endpoint must be given as:
* local://stream/PATH
* local://datagram/PATH
* local://seqpacket/PATH
* where 'stream', 'datagram' and 'seqpacket' "host_name"-field marks the
* stream-type, datagram-type and seqpacket-type UNIX domain sockets protocols.
* here, possible "port_num" field is allowed but ignored.
*
* NOTE: "proto_mark" and "host_name" (for local kind) fields are parsed in case-insensitive manner!
*
* NOTE: "proto_mark" field is parsed as case-insensitive string!
*
*/
@ -39,6 +47,7 @@ public:
enum proto_id_t : uint8_t {
PROTO_ID_LOCAL,
PROTO_ID_SEQLOCAL,
PROTO_ID_TCP,
PROTO_ID_TLS,
PROTO_ID_UDP,
@ -58,6 +67,14 @@ public:
protoMarkUDP, protoMarkWS, protoMarkWSS};
static constexpr std::string_view localProtoTypeStream{"stream"}; // UNIX domain stream
static constexpr std::string_view localProtoTypeDatagram{"datagram"}; // UNIX domain datagram
static constexpr std::string_view localProtoTypeSeqpacket{"seqpacket"}; // UNIX domain seqpacket
static constexpr std::array validLocalProtoTypes{localProtoTypeStream, localProtoTypeDatagram,
localProtoTypeSeqpacket};
template <traits::adc_input_char_range R>
AdcEndpointParser(const R& ept)
{
@ -81,7 +98,8 @@ public:
if constexpr (std::is_array_v<std::remove_cvref_t<R>>) {
_endpoint = ept;
} else {
std::ranges::copy(ept, _endpoint);
_endpoint.clear();
std::ranges::copy(ept, std::back_inserter(_endpoint));
}
auto found = std::ranges::search(_endpoint, protoHostDelim);
@ -90,53 +108,59 @@ public:
}
_proto = std::string_view{_endpoint.begin(), found.begin()};
// for (auto& valid_proto : validProtoMarks) {
// _isValid = _proto == valid_proto;
// if (_isValid) {
// break;
// }
// }
// if (!_isValid) {
// return _isValid;
// }
// _isValid = false;
if (!checkProtoMark(_proto)) {
ssize_t idx;
if ((idx = checkProtoMark(std::string_view{_endpoint.begin(), found.begin()})) < 0) {
return _isValid;
}
_proto = validProtoMarks[idx];
_host = std::string_view{found.end(), _endpoint.end()};
if (_proto == protoMarkLocal) { // local proto allows only hostname
_path = std::string_view();
_port = -1;
auto f1 = std::ranges::search(_host, portPathDelim);
std::string_view port_sv;
if (f1.empty() && isLocal()) { // no path, but it is mandatory for 'local'!
return _isValid;
} else {
auto f1 = std::ranges::search(_host, hostPortDelim);
std::string_view port_sv;
if (f1.empty()) { // no port, but is must be!
_host = std::string_view(_host.begin(), f1.begin());
_path = std::string_view(f1.end(), &*_endpoint.end());
f1 = std::ranges::search(_host, hostPortDelim);
if (f1.empty() && !isLocal()) { // no port, but it is mandatory for non-local!
return _isValid;
} else {
}
port_sv = std::string_view(f1.end(), _host.end());
if (port_sv.size()) {
_host = std::string_view(_host.begin(), f1.begin());
// port_sv = std::string_view(f1.end(), _endpoint.end());
port_sv = std::string_view(f1.end(), &*_endpoint.end());
f1 = std::ranges::search(port_sv, portPathDelim);
if (f1.empty()) {
_path = std::string_view();
} else {
port_sv = std::string_view(port_sv.begin(), f1.begin());
if (!isLocal()) {
// convert port string to int
auto end_ptr = port_sv.data() + port_sv.size();
_path = std::string_view(f1.end(), &*_endpoint.end());
auto [ptr, ec] = std::from_chars(port_sv.data(), end_ptr, _port);
if (ec != std::errc() || ptr != end_ptr) {
return _isValid;
}
} else { // ignore for local
_port = -1;
}
} else {
_port = -1;
}
// convert port string to int
auto end_ptr = port_sv.data() + port_sv.size();
auto [ptr, ec] = std::from_chars(port_sv.data(), end_ptr, _port);
if (ec != std::errc() || ptr != end_ptr) {
if (isLocal()) { // check for special values
idx = 0;
if (std::ranges::any_of(validLocalProtoTypes, [&idx, this](const auto& el) {
bool ok = utils::AdcCharRangeCompare(_host, el, true);
if (!ok) {
++idx;
}
return ok;
})) {
_host = validLocalProtoTypes[idx];
} else {
return _isValid;
}
}
@ -197,6 +221,22 @@ public:
return proto() == protoMarkLocal;
}
bool isLocalStream() const
{
return host() == localProtoTypeStream;
}
bool isLocalDatagram() const
{
return host() == localProtoTypeDatagram;
}
bool isLocalSeqpacket() const
{
return host() == localProtoTypeSeqpacket;
}
bool isTCP() const
{
return proto() == protoMarkTCP;
@ -229,10 +269,22 @@ protected:
bool _isValid;
virtual bool checkProtoMark(std::string_view proto_mark)
virtual ssize_t checkProtoMark(std::string_view proto_mark)
{
return std::ranges::any_of(AdcEndpointParser::validProtoMarks,
[proto_mark](const auto& el) { return el == proto_mark; });
ssize_t idx = 0;
// case-insensitive look-up
bool found = std::ranges::any_of(AdcEndpointParser::validProtoMarks, [&idx, &proto_mark, this](const auto& el) {
bool ok = utils::AdcCharRangeCompare(proto_mark, el, true);
if (!ok) {
++idx;
}
return ok;
});
return found ? idx : -1;
}
enum EndpointPart { PROTO_PART, HOST_PART, PATH_PART };
@ -242,9 +294,9 @@ protected:
{
R res;
if (!_isValid) {
return res;
}
// if (!_isValid) {
// return res;
// }
auto part = _proto;

View File

@ -169,8 +169,8 @@ concept adc_netsession_c =
typename SESST::netsession_ctx_t;
requires std::constructible_from<SESST, const typename SESST::netsession_ident_t,
std::shared_ptr<typename SESST::netservice_t>,
typename SESST::netsession_ctx_t>;
// std::shared_ptr<typename SESST::netservice_t>,
typename SESST::netservice_t, typename SESST::netsession_ctx_t>;
// netsession_ident_t ident() const
{ sess_const.ident() } -> std::same_as<typename SESST::netsession_ident_t>;

View File

@ -230,7 +230,7 @@ public:
typedef std::string server_ident_t;
template <traits::adc_input_char_range R>
AdcGenericNetServer(R&& id) : _serverIdent()
AdcGenericNetServer(const R& id) : _serverIdent()
{
if constexpr (std::is_array_v<std::remove_cvref_t<R>>) {
_serverIdent = id;

View File

@ -0,0 +1,85 @@
#pragma once
#include <asio/io_context.hpp>
#include <asio/ip/tcp.hpp>
#include <asio/ip/udp.hpp>
#include <asio/local/seq_packet_protocol.hpp>
#include <asio/local/stream_protocol.hpp>
#include "../adc_device_netserver.h"
#include "../adc_endpoint.h"
#include "adc_netservice_asio.h"
namespace adc::impl
{
class AdcDeviceNetServerASIO : public AdcDeviceNetServer
{
public:
template <traits::adc_input_char_range R>
AdcDeviceNetServerASIO(const R& id, asio::io_context& io_context) : AdcDeviceNetServer(id), _ioContext(io_context)
{
}
template <interfaces::adc_netsession_proto_c SessProtoT, std::derived_from<AdcEndpointParser> EptT>
void start(const EptT& endpoint)
{
if (!endpoint.isValid()) {
return;
}
// may thow here!
#ifdef USE_OPENSSL_WITH_ASIO
if (endpoint.isTCP() || endpoint.isTLS()) {
asio::ip::tcp::endpoint ept(asio::ip::make_address(endpoint.host()), endpoint.port());
if (endpoint.isTCP()) {
using srv_t = AdcNetServiceASIOBase<asio::ip::tcp, SessProtoT>;
AdcDeviceNetServer::start<Session<srv_t>>("TCP", this, _ioContext, ept);
} else {
}
#else
if (endpoint.isTCP()) {
asio::ip::tcp::endpoint ept(asio::ip::make_address(endpoint.host()), endpoint.port());
using srv_t = AdcNetServiceASIOBase<asio::ip::tcp, AdcStopSeqSessionProto<>>;
AdcDeviceNetServer::start<Session<srv_t>>("TCP", this, _ioContext, ept);
#endif
} else if (endpoint.isLocal()) {
if (endpoint.isLocalStream()) {
asio::local::stream_protocol::endpoint ept(endpoint.template path<std::string>());
using srv_t = AdcNetServiceASIOBase<asio::local::stream_protocol, SessProtoT>;
AdcDeviceNetServer::start<Session<srv_t>>("LOCAL STREAM", this, _ioContext, ept);
} else if (endpoint.isLocalDatagram()) {
asio::local::datagram_protocol::endpoint ept(endpoint.template path<std::string>());
using srv_t = AdcNetServiceASIOBase<asio::local::datagram_protocol, SessProtoT>;
AdcDeviceNetServer::start<Session<srv_t>>("LOCAL DGRAM", this, _ioContext, ept);
} else if (endpoint.isLocalSeqpacket()) {
asio::local::seq_packet_protocol::endpoint ept(endpoint.template path<std::string>());
using srv_t = AdcNetServiceASIOBase<asio::local::seq_packet_protocol, SessProtoT>;
AdcDeviceNetServer::start<Session<srv_t>>("LOCAL SEQPACK", this, _ioContext, ept);
}
} else {
throw std::system_error(std::make_error_code(std::errc::protocol_not_supported));
}
}
// some default endpoint?!!
void start() {}
protected:
asio::io_context& _ioContext;
// demonizing ASIO-related methods
virtual void daemonizePrepare()
{
_ioContext.notify_fork(asio::execution_context::fork_prepare);
}
virtual void daemonizeFinalize()
{
_ioContext.notify_fork(asio::io_context::fork_child);
}
};
} // namespace adc::impl

View File

@ -161,9 +161,11 @@ public:
typedef AdcNetServiceASIOBase netservice_t;
typedef std::shared_ptr<netservice_t> sptr_netservice_t;
typedef std::function<void(std::error_code, sptr_netservice_t)> async_accept_callback_t;
typedef std::function<void(std::error_code, netservice_t)> async_accept_callback_t;
// typedef std::function<void(std::error_code, sptr_netservice_t)> async_accept_callback_t;
template <asio::completion_token_for<void(std::error_code, sptr_netservice_t)> TokenT,
// template <asio::completion_token_for<void(std::error_code, sptr_netservice_t)> TokenT,
template <asio::completion_token_for<void(std::error_code, netservice_t)> TokenT,
traits::adc_time_duration_c DT = decltype(DEFAULT_ACCEPT_TIMEOUT)>
auto asyncAccept(TokenT&& token, const DT& timeout = DEFAULT_ACCEPT_TIMEOUT)
{
@ -175,7 +177,8 @@ public:
_socket = AdcNetServiceASIOBase::socket_t{_ioContext};
auto timer = getDeadlineTimer(_acceptor, timeout);
return asio::async_compose<TokenT, void(std::error_code, sptr_netservice_t)>(
// return asio::async_compose<TokenT, void(std::error_code, sptr_netservice_t)>(
return asio::async_compose<TokenT, void(std::error_code, netservice_t)>(
[timer = std::move(timer), start = true, this](auto& self, std::error_code ec = {}) mutable {
if (!ec) {
if (start) {
@ -186,7 +189,8 @@ public:
}
} catch (std::system_error err) {
timer->cancel();
self.complete(err.code(), std::make_shared<netservice_t>(_ioContext));
self.complete(err.code(), netservice_t{_ioContext});
// self.complete(err.code(), std::make_shared<netservice_t>(_ioContext));
return;
}
@ -200,13 +204,13 @@ public:
timer->cancel();
}
// self.complete(ec, AdcNetServiceASIOBase(std::move(_socket)));
self.complete(ec, std::make_shared<netservice_t>(std::move(_socket)));
self.complete(ec, netservice_t(std::move(_socket)));
// self.complete(ec, std::make_shared<netservice_t>(std::move(_socket)));
},
token, _ioContext);
}
template <asio::completion_token_for<void(std::error_code, AdcNetServiceASIOBase)> TokenT,
template <asio::completion_token_for<void(std::error_code, netservice_t)> TokenT,
traits::adc_time_duration_c DT = decltype(DEFAULT_ACCEPT_TIMEOUT)>
auto asyncAccept(const AdcNetServiceASIOBase::endpoint_t& endpoint,
TokenT&& token,

View File

@ -73,17 +73,21 @@ TEST_CASE("[ADC NET MESSAGE]")
// std::cout << "\n\n\n";
AdcEndpoint ept(AdcEndpoint::PROTO_ID_LOCAL, "SOCK");
// AdcEndpoint ept(AdcEndpoint::PROTO_ID_LOCAL, "SOCK");
std::cout << "EPT: [" << ept.endpoint() << "]\n";
std::cout << "EPT SZ: " << ept.endpoint().size() << "\n";
// std::cout << "EPT: [" << ept.endpoint() << "]\n";
// std::cout << "EPT SZ: " << ept.endpoint().size() << "\n";
char ee[] = "tls://dewlkj.dwed.dwed:8012/dwelk";
ept.setEndpoint(ee);
// char ee[] = "tLs://dewlkK.dwed.dwed:8012/dwelk";
// ept.setEndpoint(ee);
std::cout << "EPT: [" << ept.endpoint() << "]\n";
// std::cout << "EPT: [" << ept.endpoint() << "]\n";
char ee[] = "tLs://dewlkK.dwed.dwed:8012/dwelk";
std::cout << "EPT CHAR RANGE: " << ee << "\n";
AdcEndpointParser prs(ee);
std::cout << std::boolalpha << "IS VALID: " << prs.isValid() << "\n";
std::cout << "PROTO: [" << prs.proto() << "]\n";
std::cout << "HOST: [" << prs.host() << "]\n";
std::cout << "PORT: [" << prs.port() << "]\n";
@ -91,9 +95,21 @@ TEST_CASE("[ADC NET MESSAGE]")
std::cout << std::boolalpha << "IS TLS: " << prs.isTLS() << "\n";
std::cout << std::boolalpha << "IS UDP: " << prs.isUDP() << "\n";
std::cout << "\n\n";
std::string bs;
std::cout << "\n";
std::string bs = "local://seqpaCKet:dwe/tmp.dev/213";
std::cout << "EPT CHAR RANGE: " << bs << "\n";
prs = AdcEndpointParser(bs);
std::cout << std::boolalpha << "IS VALID: " << prs.isValid() << "\n";
std::cout << "PROTO: [" << prs.proto() << "]\n";
std::cout << "HOST: [" << prs.host() << "]\n";
std::cout << "PORT: [" << prs.port() << "]\n";
std::cout << "PATH: [" << prs.path() << "]\n";
std::cout << std::boolalpha << "IS LOCAL STREAM: " << prs.isLocalStream() << "\n";
std::cout << std::boolalpha << "IS LOCAL SEQPACKET: " << prs.isLocalSeqpacket() << "\n";
std::cout << std::boolalpha << "IS UDP: " << prs.isUDP() << "\n";
std::cout << "\n\n";
bs.clear();
AdcDeviceProtoMessage dpm(bs);
std::cout << std::boolalpha << "IS VALID: " << dpm.isValid() << "\n";

View File

@ -40,7 +40,9 @@ int main()
acc.asyncAccept([](std::error_code ec, auto srv) {
if (!ec) {
receive(std::move(srv));
auto sptr = std::make_shared<srv_t>(std::move(srv));
receive(sptr);
// receive(std::move(srv));
} else {
std::cout << "ACCEPT ERR: " << ec.message() << "\n";
}