diff --git a/CMakeLists.txt b/CMakeLists.txt index b2d46ee..943143c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ set(ADC_NETWORK_HEADERS # net/adc_netmessage.h net/adc_netproto.h net/adc_netservice.h - net/adc_url.h + net/adc_endpoint.h ) option(BUILD_TESTS "Build tests" ON) diff --git a/net/adc_endpoint.h b/net/adc_endpoint.h new file mode 100644 index 0000000..2b43e59 --- /dev/null +++ b/net/adc_endpoint.h @@ -0,0 +1,359 @@ +#pragma once + + +#include "../common/adc_traits.h" + +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; + * + * for "local" kind protocol the endpoint string must consists of + * only "proto_mark" and "host_name" fields + * (e.g.: local://APP_UNIX_SOCKET) + * + */ + +class AdcEndpoint +{ +protected: + static constexpr std::string_view protoHostDelim = "://"; + static constexpr std::string_view hostPortDelim = ":"; + static constexpr std::string_view portPathDelim = "/"; + +public: + enum proto_id_t : uint8_t { + PROTO_ID_LOCAL, + PROTO_ID_TCP, + PROTO_ID_TLS, + PROTO_ID_UDP, + PROTO_ID_WS, + PROTO_ID_WSS, + 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::string_view protoMarkUDP{"udp"}; // UDP + static constexpr std::string_view protoMarkWS{"ws"}; // Websocket + static constexpr std::string_view protoMarkWSS{"wss"}; // Secure Websocket + + static constexpr std::array validProtoMarks = {protoMarkLocal, protoMarkTCP, protoMarkTLS, + protoMarkUDP, protoMarkWS, protoMarkWSS}; + + /* Constructors and destructor */ + + AdcEndpoint() = default; + + // full-feature constructor + template + AdcEndpoint(proto_id_t proto_id, HT&& host, int port = -1, PTT&& path = PTT()) : AdcEndpoint() + { + setEndpoint(proto_id, std::forward(host), port, std::forward(path)); + } + + + bool valid() const + { + // + return _isValid; + } + + + template + R endpoint() const + { + R r; + if (!_isValid) { + return r; + } + + if (_protoID == PROTO_ID_LOCAL) { // only proto mark, host and its delimiter + std::ranges::copy(endpointView | std::views::take(3) | std::views::join, std::back_inserter(r)); + } else { + std::ranges::copy(endpointView | std::views::join, std::back_inserter(r)); + } + + return r; + } + + std::string endpoint() const + { + // + return endpoint(); + } + + + template + R proto() const + { + if (!_isValid) { + return R(); + } + + const auto& proto = validProtoMarks[_protoID]; + return R{proto.begin(), proto.end()}; + } + + + std::string_view proto() const + { + if (!_isValid) { + return std::string_view(); + } + + return validProtoMarks[_protoID]; + } + + + template + R host() const + { + R r; + if (!_isValid) { + return r; + } + + std::ranges::copy(_host, std::back_inserter(r)); + + return r; + } + + template + R hostView() const + { + if (!_isValid) { + return R(); + } + + return R{_host.begin(), _host.end()}; + } + + std::string host() const + { + // + return _host; + } + + std::string_view hostView() const + { + // + return std::string_view{_host.begin(), _host.end()}; + } + + + int port() const + { + // + return _isValid ? _port : -1; + } + + template + R path() const + { + R r; + if (!_isValid) { + return r; + } + + std::ranges::copy(_path, std::back_inserter(r)); + + return r; + } + + template + R pathView() const + { + if (!_isValid) { + return R(); + } + + return R{_path.begin(), _path.end()}; + } + + std::string path() const + { + // + return _isValid ? _path : ""; + } + + std::string_view pathView() const + { + // + return _isValid ? std::string_view{_path.begin(), _path.end()} : std::string_view(); + } + + template + bool setEndpoint(proto_id_t proto_id, const HT& host, int port, const PTT& path) + { + if (!std::distance(std::begin(host), std::end(host))) { + return false; + } + + switch (proto_id) { + case PROTO_ID_LOCAL: + // ignore path (always "") and port (just skip) + _port = -1; + _path.clear(); + + break; + case PROTO_ID_TCP: + case PROTO_ID_TLS: + case PROTO_ID_UDP: + case PROTO_ID_WS: + case PROTO_ID_WSS: + if (port <= 0) { + return false; + } + + _port = port; + + _path.clear(); + std::ranges::copy(path, std::back_inserter(_path)); + + break; + default: + break; + } + + _isValid = true; + + _protoID = proto_id; + + _host.clear(); + std::ranges::copy(host, std::back_inserter(_host)); + + updateEndpointView(); + + return true; + } + + template + bool setEndpoint(char (&r)[N]) + { + return setEndpoint(std::string_view(r)); + } + + template + bool setEndpoint(const R& r) + { + auto found = std::ranges::search(r, protoHostDelim); + + if (found.empty()) { + return false; + } + + if (found.end() == r.end()) { // no host, only delimiter!!! + return false; + } + + auto sz = std::distance(r.begin(), found.begin()); + + std::string proto; + std::ranges::copy(r | std::views::take(sz), std::back_inserter(proto)); + + std::underlying_type_t i = 0; + proto_id_t id = PROTO_ID_UNKNOWN; + + for (const auto& el : validProtoMarks) { + if (std::ranges::equal(el, proto)) { + id = static_cast(i); + break; + } + ++i; + } + + if (id == PROTO_ID_UNKNOWN) { + return false; + } + + if (id == PROTO_ID_LOCAL) { // remaining part is host + _protoID = id; + _host.clear(); + std::ranges::copy(r | std::views::drop(sz), std::back_inserter(_host)); + _path.clear(); + _port = -1; + _isValid = true; + + return true; + } + + sz += proto.size(); + + found = std::ranges::search(r | std::views::drop(sz), hostPortDelim); + + if (found.empty()) { // no host-to-port delimiter + return false; + } + + if (found.end() == r.end()) { // the delimiter is at the end!!! + return false; + } + + auto pos = std::distance(r.begin(), found.begin()); + if (pos == sz) { // empty host field + return false; + } + + std::string host; + std::ranges::copy(r | std::views::drop(sz) | std::views::take(pos - sz), std::back_inserter(host)); + sz = pos + hostPortDelim.size(); + + found = std::ranges::search(r | std::views::drop(sz), portPathDelim); + pos = std::distance(r.begin(), found.begin()); + + std::string port_str; + std::ranges::copy(r | std::views::drop(sz) | std::views::take(pos - sz), std::back_inserter(port_str)); + + int port; + auto end_ptr = port_str.data() + port_str.size(); + + auto [ptr, ec] = std::from_chars(port_str.data(), end_ptr, port); + if (ec != std::errc() || ptr != end_ptr) { + return false; + } + + sz = pos + portPathDelim.size(); + + _path.clear(); + std::ranges::copy(r | std::views::drop(sz), std::back_inserter(_path)); + + _protoID = id; + _host = host; + _port = port; + + _isValid = true; + + updateEndpointView(); + + return true; + } + +protected: + bool _isValid{false}; + proto_id_t _protoID; + std::string _host, _path, _portStr; + int _port; + + std::array endpointView{protoMarkLocal, protoHostDelim, "localhost", hostPortDelim, + "7777", portPathDelim, ""}; + + void updateEndpointView() + { + endpointView[0] = validProtoMarks[_protoID]; + endpointView[2] = _host.c_str(); + + _portStr = std::to_string(_port); + endpointView[4] = _port == -1 ? "" : _portStr.c_str(); + + endpointView[6] = _path.c_str(); + } +}; + +} // namespace adc diff --git a/net/adc_url.h b/net/adc_url.h deleted file mode 100644 index ddd1083..0000000 --- a/net/adc_url.h +++ /dev/null @@ -1,256 +0,0 @@ -#pragma once - - -#include "../common/adc_traits.h" - -namespace adc -{ - - -/* - * Very simple URL parser and holder class - * - * URL: proto_mark://host_name:port_num/path - */ - -class AdcURL -{ -protected: - static constexpr std::string_view protoHostDelim = "://"; - static constexpr std::string_view hostPortDelim = ":"; - static constexpr std::string_view portPathDelim = "/"; - -public: - enum proto_id_t { PROTO_ID_LOCAL, PROTO_ID_TCP, PROTO_ID_TLS, PROTO_ID_UDP, PROTO_ID_WS, PROTO_ID_WSS }; - - 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::string_view protoMarkUDP{"udp"}; // UDP - static constexpr std::string_view protoMarkWS{"ws"}; // Websocket - static constexpr std::string_view protoMarkWSS{"wss"}; // Secure Websocket - - static constexpr std::array validProtoMarks = {protoMarkLocal, protoMarkTCP, protoMarkTLS, - protoMarkUDP, protoMarkWS, protoMarkWSS}; - - /* Constructors and destructor */ - - AdcURL() = default; - - // full-feature constructor - template - AdcURL(proto_id_t proto_id, HT&& host, int port, PTT&& path) : AdcURL() - { - setURL(proto_id, std::forward(host), port, std::forward(path)); - } - - - bool valid() const - { - // - return _isValid; - } - - - template - R url() const - { - R r; - if (!_isValid) { - return r; - } - - if (_protoID == PROTO_ID_LOCAL) { // only proto mark, host and its delimiter - std::ranges::copy(_urlView | std::views::take(3) | std::views::join, std::back_inserter(r)); - } else { - std::ranges::copy(_urlView | std::views::join, std::back_inserter(r)); - } - - return r; - } - - std::string url() const - { - // - return url(); - } - - - template - R proto() const - { - if (!_isValid) { - return R(); - } - - const auto& proto = validProtoMarks[_protoID]; - return R{proto.begin(), proto.end()}; - } - - - std::string_view proto() const - { - if (!_isValid) { - return std::string_view(); - } - - return validProtoMarks[_protoID]; - } - - - template - R host() const - { - R r; - if (!_isValid) { - return r; - } - - std::ranges::copy(_host, std::back_inserter(r)); - - return r; - } - - template - R hostView() const - { - if (!_isValid) { - return R(); - } - - return R{_host.begin(), _host.end()}; - } - - std::string host() const - { - // - return _host; - } - - std::string_view hostView() const - { - // - return std::string_view{_host.begin(), _host.end()}; - } - - - int port() const - { - // - return _isValid ? _port : -1; - } - - template - R path() const - { - R r; - if (!_isValid) { - return r; - } - - std::ranges::copy(_path, std::back_inserter(r)); - - return r; - } - - template - R pathView() const - { - if (!_isValid) { - return R(); - } - - return R{_path.begin(), _path.end()}; - } - - std::string path() const - { - // - return _isValid ? _path : ""; - } - - std::string_view pathView() const - { - // - return _isValid ? std::string_view{_path.begin(), _path.end()} : std::string_view(); - } - - template - bool setURL(proto_id_t proto_id, HT&& host, int port, PTT&& path) - { - if (!std::distance(host.begin(), host.end())) { - return false; - } - - switch (proto_id) { - case PROTO_ID_LOCAL: - // ignore path (always "") and port (just skip) - _port = -1; - _path.clear(); - - break; - case PROTO_ID_TCP: - case PROTO_ID_TLS: - case PROTO_ID_UDP: - case PROTO_ID_WS: - case PROTO_ID_WSS: - if (port <= 0) { - return false; - } - - _port = port; - - _path.clear(); - std::ranges::copy(std::forward(path), std::back_inserter(_path)); - - break; - default: - break; - } - - _isValid = true; - - _protoID = proto_id; - - _host.clear(); - std::ranges::copy(std::forward(host), std::back_inserter(_host)); - - updateURLView(); - - return true; - } - - template - bool setURL(R&& r) - { - auto found = std::ranges::search(std::forward(r), protoHostDelim); - - if (found.empty()) { - return false; - } - - - - return true; - } - -protected: - bool _isValid{false}; - proto_id_t _protoID; - std::string _host, _path, _portStr; - int _port; - - std::array _urlView{protoMarkLocal, protoHostDelim, "localhost", hostPortDelim, - "7777", portPathDelim, ""}; - - void updateURLView() - { - _urlView[0] = validProtoMarks[_protoID]; - _urlView[2] = _host.c_str(); - _portStr = std::to_string(_port); - _urlView[4] = _port == -1 ? "" : _portStr.c_str(); - _urlView[6] = _path.c_str(); - } -}; - -} // namespace adc diff --git a/tests/adc_netmsg_test.cpp b/tests/adc_netmsg_test.cpp index ba67e97..ceb65c3 100644 --- a/tests/adc_netmsg_test.cpp +++ b/tests/adc_netmsg_test.cpp @@ -6,6 +6,7 @@ // #include "../common/adc_utils.h" // #include "../net/adc_netmessage.h" +#include "../net/adc_endpoint.h" #include "../net/adc_netmsg.h" using namespace adc; @@ -66,6 +67,19 @@ TEST_CASE("[ADC NET MESSAGE]") std::cout << "[" << el << "]"; } std::cout << "\n"; + + + std::cout << "\n\n\n"; + + AdcEndpoint ept(AdcEndpoint::PROTO_ID_LOCAL, "SOCK"); + + 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); + + std::cout << "EPT: [" << ept.endpoint() << "]\n"; } /*