start network/serial communications developing
This commit is contained in:
parent
aefeb50aab
commit
b09cb001d6
211
cxx/comm_server.h
Normal file
211
cxx/comm_server.h
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <asio/awaitable.hpp>
|
||||||
|
#include <asio/ip/tcp.hpp>
|
||||||
|
#include <asio/local/seq_packet_protocol.hpp>
|
||||||
|
#include <asio/local/stream_protocol.hpp>
|
||||||
|
#include <asio/serial_port.hpp>
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include "comm_server_endpoint.h"
|
||||||
|
#include "mount.h"
|
||||||
|
|
||||||
|
namespace mcc
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
namespace traits
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept mcc_endpoint_c = std::derived_from<T, asio::serial_port> || std::derived_from<T, asio::ip::tcp::endpoint> ||
|
||||||
|
std::derived_from<T, asio::local::stream_protocol::endpoint> ||
|
||||||
|
std::derived_from<T, asio::local::seq_packet_protocol::endpoint>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static constexpr bool is_serial_proto = std::derived_from<T, asio::serial_port>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static constexpr bool is_tcp_proto = std::derived_from<T, asio::ip::tcp::endpoint>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static constexpr bool is_local_stream_proto = std::derived_from<T, asio::local::stream_protocol::endpoint>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static constexpr bool is_local_seqpack_proto = std::derived_from<T, asio::local::seq_packet_protocol::endpoint>;
|
||||||
|
|
||||||
|
} // namespace traits
|
||||||
|
|
||||||
|
|
||||||
|
class MccMountServer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MccMountServer(asio::io_context& ctx, std::shared_ptr<spdlog::logger> logger)
|
||||||
|
: _asioContext(ctx), _serverLogger(std::move(logger))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~MccMountServer() {}
|
||||||
|
|
||||||
|
void listen(traits::mcc_endpoint_c auto endpoint)
|
||||||
|
{
|
||||||
|
using epn_t = std::decay_t<decltype(endpoint)>;
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
|
||||||
|
if constexpr (traits::is_serial_proto<epn_t>) {
|
||||||
|
// first, check if port is openned
|
||||||
|
if (!endpoint.is_open()) {
|
||||||
|
if (ec) {
|
||||||
|
// ??????????
|
||||||
|
_serverLogger->error("Serial port was not open!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_serialPorts.emplace_back(std::move(endpoint));
|
||||||
|
|
||||||
|
} else if constexpr (traits::is_tcp_proto<epn_t>) {
|
||||||
|
} else if constexpr (traits::is_local_stream_proto<epn_t>) {
|
||||||
|
} else if constexpr (traits::is_local_seqpack_proto<epn_t>) {
|
||||||
|
} else {
|
||||||
|
static_assert(false, "INVALID ENDPOINT!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// close listening on all endpoints
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
std::error_code ec;
|
||||||
|
size_t N = 0, M = 0;
|
||||||
|
|
||||||
|
_serverLogger->info("Close all listening endpoints ...");
|
||||||
|
|
||||||
|
auto num =
|
||||||
|
_serialPorts.size() + _tcpAcceptors.size() + _localStreamAcceptors.size() + _localSeqpackAcceptors.size();
|
||||||
|
if (!num) {
|
||||||
|
_serverLogger->info("There are no listening ports/sockets!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_serialPorts.size()) {
|
||||||
|
_serverLogger->debug("Close serial ports ...");
|
||||||
|
|
||||||
|
for (auto& s_port : _serialPorts) {
|
||||||
|
s_port.close(ec);
|
||||||
|
if (ec) {
|
||||||
|
_serverLogger->error("Cannot close serial port! ec = '{}'", ec.message());
|
||||||
|
++M;
|
||||||
|
}
|
||||||
|
++N;
|
||||||
|
}
|
||||||
|
|
||||||
|
_serverLogger->debug("{} from {} serial ports were closed!", M, N);
|
||||||
|
|
||||||
|
_serialPorts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_tcpAcceptors.size()) {
|
||||||
|
_serverLogger->debug("Close TCP listening sockets ...");
|
||||||
|
N = 0;
|
||||||
|
M = 0;
|
||||||
|
|
||||||
|
for (auto& acc : _tcpAcceptors) {
|
||||||
|
acc.close(ec);
|
||||||
|
if (ec) {
|
||||||
|
_serverLogger->error("Cannot close TCP socket! ec = '{}'", ec.message());
|
||||||
|
++M;
|
||||||
|
}
|
||||||
|
++N;
|
||||||
|
}
|
||||||
|
|
||||||
|
_serverLogger->debug("{} from {} TCP sockets were closed!", M, N);
|
||||||
|
|
||||||
|
_tcpAcceptors.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_localStreamAcceptors.size()) {
|
||||||
|
_serverLogger->debug("Close local stream listening sockets ...");
|
||||||
|
N = 0;
|
||||||
|
M = 0;
|
||||||
|
|
||||||
|
for (auto& acc : _localStreamAcceptors) {
|
||||||
|
acc.close(ec);
|
||||||
|
if (ec) {
|
||||||
|
_serverLogger->error("Cannot close local stream socket! ec = '{}'", ec.message());
|
||||||
|
++M;
|
||||||
|
}
|
||||||
|
++N;
|
||||||
|
}
|
||||||
|
|
||||||
|
_serverLogger->debug("{} from {} local stream sockets were closed!", M, N);
|
||||||
|
|
||||||
|
_localStreamAcceptors.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_localSeqpackAcceptors.size()) {
|
||||||
|
_serverLogger->debug("Close local seqpack listening sockets ...");
|
||||||
|
N = 0;
|
||||||
|
M = 0;
|
||||||
|
|
||||||
|
for (auto& acc : _localSeqpackAcceptors) {
|
||||||
|
acc.close(ec);
|
||||||
|
if (ec) {
|
||||||
|
_serverLogger->error("Cannot close local seqpack socket! ec = '{}'", ec.message());
|
||||||
|
++M;
|
||||||
|
}
|
||||||
|
++N;
|
||||||
|
}
|
||||||
|
|
||||||
|
_serverLogger->debug("{} from {} local seqpack sockets were closed!", M, N);
|
||||||
|
|
||||||
|
_localSeqpackAcceptors.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
_serverLogger->info("The all server listening endpoints were closed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
asio::io_context& _asioContext;
|
||||||
|
std::shared_ptr<spdlog::logger> _serverLogger;
|
||||||
|
|
||||||
|
std::vector<asio::serial_port> _serialPorts;
|
||||||
|
std::vector<asio::ip::tcp::acceptor> _tcpAcceptors;
|
||||||
|
std::vector<asio::local::stream_protocol::acceptor> _localStreamAcceptors;
|
||||||
|
std::vector<asio::local::seq_packet_protocol::acceptor> _localSeqpackAcceptors;
|
||||||
|
|
||||||
|
void startAccept(traits::mcc_endpoint_c auto endpoint)
|
||||||
|
{
|
||||||
|
using epn_t = std::decay_t<decltype(endpoint)>;
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::stringstream st;
|
||||||
|
auto acc = epn_t::protocol_type::endpoint(_asioContext, endpoint);
|
||||||
|
st << acc.local_endpoint();
|
||||||
|
_serverLogger->info("Try to start listening at <{}> endpoint ...", st.str());
|
||||||
|
|
||||||
|
if constexpr (traits::is_tcp_proto<epn_t>) {
|
||||||
|
_tcpAcceptors.emplace_back(std::move(acc));
|
||||||
|
} else if constexpr (traits::is_local_stream_proto<epn_t>) {
|
||||||
|
_localStreamAcceptors.emplace_back(std::move(acc));
|
||||||
|
} else if constexpr (traits::is_local_seqpack_proto<epn_t>) {
|
||||||
|
_localSeqpackAcceptors.emplace_back(std::move(acc));
|
||||||
|
} else {
|
||||||
|
static_assert(false, "INVALID ENDPOINT!!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (const std::system_error& err) {
|
||||||
|
_serverLogger->error("An error occured while creating of connection acceptor! ec = '{}'", err.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AccT>
|
||||||
|
void doAccept()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mcc
|
||||||
476
cxx/comm_server_endpoint.h
Normal file
476
cxx/comm_server_endpoint.h
Normal file
@ -0,0 +1,476 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <charconv>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <ranges>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace mcc
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace traits
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
concept mcc_char_view = std::ranges::view<R> && std::same_as<std::ranges::range_value_t<R>, char>;
|
||||||
|
|
||||||
|
|
||||||
|
// input range of char/const char
|
||||||
|
template <typename R, typename CharT = char>
|
||||||
|
concept mcc_input_char_range =
|
||||||
|
std::ranges::input_range<R> && std::is_same_v<std::remove_cv_t<std::ranges::range_value_t<R>>, CharT>;
|
||||||
|
|
||||||
|
|
||||||
|
// output range of char/const char
|
||||||
|
template <typename R, typename CharT = char>
|
||||||
|
concept mcc_output_char_range =
|
||||||
|
std::ranges::output_range<R, CharT> && std::same_as<std::remove_cv_t<std::ranges::range_value_t<R>>, CharT>;
|
||||||
|
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
concept mcc_view_or_output_char_range = mcc_char_view<R> || mcc_output_char_range<R>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace traits
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
|
||||||
|
static constexpr bool MccCharRangeCompare(const traits::mcc_char_view auto& what,
|
||||||
|
const traits::mcc_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Very simple various protocols endpoint parser and holder class
|
||||||
|
*
|
||||||
|
* endpoint: proto_mark://host_name:port_num/path
|
||||||
|
* where "part" is optional for all non-local protocol kinds;
|
||||||
|
*
|
||||||
|
* for local kind of protocols the endpoint must be given as:
|
||||||
|
* local://stream/PATH
|
||||||
|
* local://seqpacket/PATH
|
||||||
|
* local://serial/PATH
|
||||||
|
* where 'stream' and 'seqpacket' "host_name"-field marks the
|
||||||
|
* stream-type and seqpacket-type UNIX domain sockets protocols;
|
||||||
|
* 'serial' marks a serial (RS232/485) protocol.
|
||||||
|
* 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!
|
||||||
|
*
|
||||||
|
* EXAMPLES: tcp://192.168.70.130:3131
|
||||||
|
* local://serial/dev/ttyS1
|
||||||
|
* local://seqpacket/tmp/BM70_SERVER_SOCK
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class MccServerEndpoint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr std::string_view protoHostDelim = "://";
|
||||||
|
static constexpr std::string_view hostPortDelim = ":";
|
||||||
|
static constexpr std::string_view portPathDelim = "/";
|
||||||
|
|
||||||
|
enum proto_id_t : uint8_t {
|
||||||
|
PROTO_ID_LOCAL,
|
||||||
|
PROTO_ID_SEQLOCAL,
|
||||||
|
PROTO_ID_SERLOCAL,
|
||||||
|
PROTO_ID_TCP,
|
||||||
|
PROTO_ID_TLS,
|
||||||
|
PROTO_ID_UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::string_view protoMarkLocal{"local"}; // UNIX domain
|
||||||
|
static constexpr std::string_view protoMarkTCP{"tcp"}; // TCP
|
||||||
|
static constexpr std::string_view protoMarkTLS{"tls"}; // TLS
|
||||||
|
|
||||||
|
static constexpr std::array validProtoMarks{protoMarkLocal, protoMarkTCP, protoMarkTLS};
|
||||||
|
|
||||||
|
|
||||||
|
static constexpr std::string_view localProtoTypeStream{"stream"}; // UNIX domain stream
|
||||||
|
static constexpr std::string_view localProtoTypeSeqpacket{"seqpacket"}; // UNIX domain seqpacket
|
||||||
|
static constexpr std::string_view localProtoTypeSerial{"serial"}; // serial (RS232/485)
|
||||||
|
|
||||||
|
static constexpr std::array validLocalProtoTypes{localProtoTypeStream, localProtoTypeSeqpacket,
|
||||||
|
localProtoTypeSerial};
|
||||||
|
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range R>
|
||||||
|
MccServerEndpoint(const R& ept)
|
||||||
|
{
|
||||||
|
fromRange(ept);
|
||||||
|
}
|
||||||
|
|
||||||
|
MccServerEndpoint(const MccServerEndpoint& other)
|
||||||
|
{
|
||||||
|
copyInst(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
MccServerEndpoint(MccServerEndpoint&& other)
|
||||||
|
{
|
||||||
|
moveInst(std::move(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~MccServerEndpoint() = default;
|
||||||
|
|
||||||
|
|
||||||
|
MccServerEndpoint& operator=(const MccServerEndpoint& other)
|
||||||
|
{
|
||||||
|
copyInst(other);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MccServerEndpoint& operator=(MccServerEndpoint&& other)
|
||||||
|
{
|
||||||
|
moveInst(std::move(other));
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range R>
|
||||||
|
requires std::ranges::contiguous_range<R>
|
||||||
|
bool fromRange(const R& ept)
|
||||||
|
{
|
||||||
|
_isValid = false;
|
||||||
|
|
||||||
|
// at least 'ws://a' (proto, proto-host delimiter and at least a single character of hostname)
|
||||||
|
if (std::ranges::size(ept) < 6) {
|
||||||
|
return _isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_array_v<std::remove_cvref_t<R>>) {
|
||||||
|
_endpoint = ept;
|
||||||
|
} else {
|
||||||
|
_endpoint.clear();
|
||||||
|
std::ranges::copy(ept, std::back_inserter(_endpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto found = std::ranges::search(_endpoint, protoHostDelim);
|
||||||
|
if (found.empty()) {
|
||||||
|
return _isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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()};
|
||||||
|
|
||||||
|
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 {
|
||||||
|
_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
_portView = std::string_view(f1.end(), _host.end());
|
||||||
|
if (_portView.size()) {
|
||||||
|
_host = std::string_view(_host.begin(), f1.begin());
|
||||||
|
|
||||||
|
if (!isLocal()) {
|
||||||
|
// convert port string to int
|
||||||
|
auto end_ptr = _portView.data() + _portView.size();
|
||||||
|
|
||||||
|
auto [ptr, ec] = std::from_chars(_portView.data(), end_ptr, _port);
|
||||||
|
if (ec != std::errc() || ptr != end_ptr) {
|
||||||
|
return _isValid;
|
||||||
|
}
|
||||||
|
} else { // ignore for local
|
||||||
|
_port = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_port = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLocal()) { // check for special values
|
||||||
|
idx = 0;
|
||||||
|
if (std::ranges::any_of(validLocalProtoTypes, [&idx, this](const auto& el) {
|
||||||
|
bool ok = utils::MccCharRangeCompare(_host, el, true);
|
||||||
|
if (!ok) {
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
})) {
|
||||||
|
_host = validLocalProtoTypes[idx];
|
||||||
|
} else {
|
||||||
|
return _isValid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_isValid = true;
|
||||||
|
|
||||||
|
return _isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
return _isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto endpoint() const
|
||||||
|
{
|
||||||
|
return _endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_view_or_output_char_range R>
|
||||||
|
R proto() const
|
||||||
|
{
|
||||||
|
return part<R>(PROTO_PART);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view proto() const
|
||||||
|
{
|
||||||
|
return proto<std::string_view>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_view_or_output_char_range R>
|
||||||
|
R host() const
|
||||||
|
{
|
||||||
|
return part<R>(HOST_PART);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view host() const
|
||||||
|
{
|
||||||
|
return host<std::string_view>();
|
||||||
|
}
|
||||||
|
|
||||||
|
int port() const
|
||||||
|
{
|
||||||
|
return _port;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_view_or_output_char_range R>
|
||||||
|
R portView() const
|
||||||
|
{
|
||||||
|
return part<R>(PORT_PART);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view portView() const
|
||||||
|
{
|
||||||
|
return portView<std::string_view>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_view_or_output_char_range R>
|
||||||
|
R path() const
|
||||||
|
{
|
||||||
|
return part<R>(PATH_PART);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view path() const
|
||||||
|
{
|
||||||
|
return path<std::string_view>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isLocal() const
|
||||||
|
{
|
||||||
|
return proto() == protoMarkLocal;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLocalStream() const
|
||||||
|
{
|
||||||
|
return host() == localProtoTypeStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLocalSerial() const
|
||||||
|
{
|
||||||
|
return host() == localProtoTypeSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLocalSeqpacket() const
|
||||||
|
{
|
||||||
|
return host() == localProtoTypeSeqpacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isTCP() const
|
||||||
|
{
|
||||||
|
return proto() == protoMarkTCP;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTLS() const
|
||||||
|
{
|
||||||
|
return proto() == protoMarkTLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// add '\0' char (or replace special-meaning char/char-sequence) to construct UNIX abstract namespace
|
||||||
|
// endpoint path
|
||||||
|
template <typename T = std::nullptr_t>
|
||||||
|
MccServerEndpoint& makeAbstract(const T& mark = nullptr)
|
||||||
|
requires(traits::mcc_input_char_range<T> || std::same_as<std::remove_cv_t<T>, char> ||
|
||||||
|
std::is_null_pointer_v<std::remove_cv_t<T>>)
|
||||||
|
{
|
||||||
|
if (!(isLocalStream() || isLocalSeqpacket())) { // only local proto is valid!
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_null_pointer_v<T>) { // just insert '\0'
|
||||||
|
auto it = _endpoint.insert(std::string::const_iterator(_path.begin()), '\0');
|
||||||
|
_path = std::string_view(it, _endpoint.end());
|
||||||
|
} else if constexpr (std::same_as<std::remove_cv_t<T>, char>) { // replace a character (mark)
|
||||||
|
auto pos = std::distance(_endpoint.cbegin(), std::string::const_iterator(_path.begin()));
|
||||||
|
if (_endpoint[pos] == mark) {
|
||||||
|
_endpoint[pos] = '\0';
|
||||||
|
}
|
||||||
|
} else { // replace a character range (mark)
|
||||||
|
if (std::ranges::equal(_path | std::views::take(std::ranges::size(mark), mark))) {
|
||||||
|
auto pos = std::distance(_endpoint.cbegin(), std::string::const_iterator(_path.begin()));
|
||||||
|
_endpoint.replace(pos, std::ranges::size(mark), 1, '\0');
|
||||||
|
_path = std::string_view(_endpoint.begin() + pos, _endpoint.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string _endpoint;
|
||||||
|
std::string_view _proto, _host, _path, _portView;
|
||||||
|
int _port;
|
||||||
|
bool _isValid;
|
||||||
|
|
||||||
|
|
||||||
|
virtual ssize_t checkProtoMark(std::string_view proto_mark)
|
||||||
|
{
|
||||||
|
ssize_t idx = 0;
|
||||||
|
|
||||||
|
// case-insensitive look-up
|
||||||
|
bool found = std::ranges::any_of(MccServerEndpoint::validProtoMarks, [&idx, &proto_mark](const auto& el) {
|
||||||
|
bool ok = utils::MccCharRangeCompare(proto_mark, el, true);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
});
|
||||||
|
|
||||||
|
return found ? idx : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EndpointPart { PROTO_PART, HOST_PART, PATH_PART, PORT_PART };
|
||||||
|
|
||||||
|
template <traits::mcc_view_or_output_char_range R>
|
||||||
|
R part(EndpointPart what) const
|
||||||
|
{
|
||||||
|
R res;
|
||||||
|
|
||||||
|
// if (!_isValid) {
|
||||||
|
// return res;
|
||||||
|
// }
|
||||||
|
|
||||||
|
auto part = _proto;
|
||||||
|
|
||||||
|
switch (what) {
|
||||||
|
case PROTO_PART:
|
||||||
|
part = _proto;
|
||||||
|
break;
|
||||||
|
case HOST_PART:
|
||||||
|
part = _host;
|
||||||
|
break;
|
||||||
|
case PATH_PART:
|
||||||
|
part = _path;
|
||||||
|
break;
|
||||||
|
case PORT_PART:
|
||||||
|
part = _portView;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::ranges::view<R>) {
|
||||||
|
return {part.begin(), part.end()};
|
||||||
|
} else {
|
||||||
|
std::ranges::copy(part, std::back_inserter(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copyInst(const MccServerEndpoint& other)
|
||||||
|
{
|
||||||
|
if (&other != this) {
|
||||||
|
if (other._isValid) {
|
||||||
|
_isValid = other._isValid;
|
||||||
|
_endpoint = other._endpoint;
|
||||||
|
_proto = other._proto;
|
||||||
|
|
||||||
|
auto idx = std::distance(other._endpoint.c_str(), other._host.data());
|
||||||
|
_host = std::string_view(_endpoint.c_str() + idx, other._host.size());
|
||||||
|
|
||||||
|
idx = std::distance(other._endpoint.c_str(), other._path.data());
|
||||||
|
_path = std::string_view(_endpoint.c_str() + idx, other._path.size());
|
||||||
|
|
||||||
|
_port = other._port;
|
||||||
|
} else {
|
||||||
|
_isValid = false;
|
||||||
|
_endpoint = std::string();
|
||||||
|
_proto = std::string_view();
|
||||||
|
_host = std::string_view();
|
||||||
|
_path = std::string_view();
|
||||||
|
_port = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void moveInst(MccServerEndpoint&& other)
|
||||||
|
{
|
||||||
|
if (&other != this) {
|
||||||
|
if (other._isValid) {
|
||||||
|
_isValid = std::move(other._isValid);
|
||||||
|
_endpoint = std::move(other._endpoint);
|
||||||
|
_proto = other._proto;
|
||||||
|
_host = std::move(other._host);
|
||||||
|
_path = std::move(other._path);
|
||||||
|
_port = std::move(other._port);
|
||||||
|
} else {
|
||||||
|
_isValid = false;
|
||||||
|
_endpoint = std::string();
|
||||||
|
_proto = std::string_view();
|
||||||
|
_host = std::string_view();
|
||||||
|
_path = std::string_view();
|
||||||
|
_port = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mcc
|
||||||
Loading…
x
Reference in New Issue
Block a user