diff --git a/net/adc_endpoint.h b/net/adc_endpoint.h index 835a6a4..42d451e 100644 --- a/net/adc_endpoint.h +++ b/net/adc_endpoint.h @@ -60,42 +60,200 @@ public: template AdcEndpointParser(const R& ept) { + fromRange(ept); } -protected: - std::string _proto, _host, _path; - int port; - template - bool parse(const R& ept) + requires std::ranges::contiguous_range + 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 false; + return _isValid; } - auto found = std::ranges::search(ept, protoHostDelim); + if constexpr (std::is_array_v>) { + _endpoint = ept; + } else { + std::ranges::copy(ept, _endpoint); + } + + auto found = std::ranges::search(_endpoint, protoHostDelim); if (found.empty()) { - return false; + return _isValid; } - bool ok = false; - std::string_view proto{ept.begin(), found.begin()}; + _proto = std::string_view{_endpoint.begin(), found.begin()}; for (auto& valid_proto : validProtoMarks) { - ok = proto == valid_proto; - if (ok) { + _isValid = _proto == valid_proto; + if (_isValid) { break; } } - if (!ok) { - return ok; + if (!_isValid) { + return _isValid; } - _proto.clear(); - std::ranges::copy(proto, std::back_inserter(_proto)); + _isValid = false; + + _host = std::string_view{found.end(), _endpoint.end()}; + if (_proto == protoMarkLocal) { // local proto allows only hostname + _path = std::string_view(); + _port = -1; + } else { + auto f1 = std::ranges::search(_host, hostPortDelim); + std::string_view port_sv; + if (f1.empty()) { // no port, but is must be! + return _isValid; + } else { + _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()); + + _path = std::string_view(f1.end(), &*_endpoint.end()); + } + + // 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) { + return _isValid; + } + } + } + + _isValid = true; + + return _isValid; + } + + + bool isValid() const + { + return _isValid; + } + + template + R proto() const + { + return part(PROTO_PART); + } + + std::string_view proto() const + { + return proto(); + } + + template + R host() const + { + return part(HOST_PART); + } + + std::string_view host() const + { + return host(); + } + + int port() const + { + return _port; + } + + template + R path() const + { + return part(PATH_PART); + } + + std::string_view path() const + { + return path(); + } + + + bool isLocal() const + { + return proto() == protoMarkLocal; + } + + bool isTCP() const + { + return proto() == protoMarkTCP; + } + + bool isTLS() const + { + return proto() == protoMarkTLS; + } + + bool isUDP() const + { + return proto() == protoMarkUDP; + } + + bool isWS() const + { + return proto() == protoMarkWS; + } + + bool isWSS() const + { + return proto() == protoMarkWSS; + } + +protected: + std::string _endpoint; + std::string_view _proto, _host, _path; + int _port; + bool _isValid; + + enum EndpointPart { PROTO_PART, HOST_PART, PATH_PART }; + + template + 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; + default: + break; + } + + if constexpr (std::ranges::view) { + return {part.begin(), part.end()}; + } else { + std::ranges::copy(part, res); + } + + return res; } }; diff --git a/net/adc_netserver.h b/net/adc_netserver.h index 48232f4..a138463 100644 --- a/net/adc_netserver.h +++ b/net/adc_netserver.h @@ -184,15 +184,17 @@ public: // start accepting remote connections, create and start given network session // It must be assumed that this is asynchronous operation!!! - template + template void start(const typename SessionT::netsession_ident_t& id, const typename SessionT::netsession_ctx_t& sess_ctx, - AccCtorArgTs&&... ctor_args) + AcceptorCtorArgTs&&... ctor_args) { - auto acceptor = - std::make_shared(std::forward(ctor_args)...); + if (!_isListening[this]) { + auto acceptor = std::make_shared( + std::forward(ctor_args)...); - doAccept(acceptor, id, sess_ctx); + doAccept(acceptor, id, sess_ctx); + } }; @@ -218,7 +220,7 @@ protected: server_ident_t _serverIdent; template - void doAccept(std::shared_ptr acceptor, const IDT& id, CTXT& sess_ctx) + 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) { diff --git a/tests/adc_netmsg_test.cpp b/tests/adc_netmsg_test.cpp index cb54fcd..3512a6e 100644 --- a/tests/adc_netmsg_test.cpp +++ b/tests/adc_netmsg_test.cpp @@ -83,6 +83,13 @@ TEST_CASE("[ADC NET MESSAGE]") std::cout << "EPT: [" << ept.endpoint() << "]\n"; + AdcEndpointParser prs(ee); + 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 TLS: " << prs.isTLS() << "\n"; + std::cout << std::boolalpha << "IS UDP: " << prs.isUDP() << "\n"; std::cout << "\n\n"; std::string bs;