From 9a2baa702de38e5a9995c6c620fbfc3d67ae9555 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Sat, 15 Jun 2024 21:23:57 +0300 Subject: [PATCH] ... --- CMakeLists.txt | 6 ++ net/adc_endpoint.h | 64 ++++++++++++++++-- net/adc_netmsg.h | 139 ++++++++++++++------------------------ net/adc_netservice.h | 21 +++--- net/adc_netservice_asio.h | 68 ++++++++++++++++--- 5 files changed, 182 insertions(+), 116 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f39c2f..bf3ce9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,12 @@ if (ASIO_LIBRARY) add_compile_options(ASIO::ASIO) add_compile_definitions(PUBLIC USE_ASIO_LIBRARY) + + option(OPENSSL_LIBRARY "Use openssl library for related ASIO-based implementation" ON) + if (OPENSSL_LIBRARY) + find_package(OpenSSL REQUIRED) + add_compile_definitions(PUBLIC USE_OPENSSL_WITH_ASIO) + endif() endif() diff --git a/net/adc_endpoint.h b/net/adc_endpoint.h index 2b43e59..9ae6a75 100644 --- a/net/adc_endpoint.h +++ b/net/adc_endpoint.h @@ -1,6 +1,13 @@ #pragma once +/* + +ABSTRACT DEVICE COMPONENTS LIBRARY + + */ + + #include "../common/adc_traits.h" namespace adc @@ -13,20 +20,21 @@ namespace adc * 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 + * for the "local" kind protocol the endpoint string must consists of * only "proto_mark" and "host_name" fields * (e.g.: local://APP_UNIX_SOCKET) * + * NOTE: "proto_mark" field is parsed as case-insensitive string! + * */ class AdcEndpoint { -protected: +public: 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, @@ -47,6 +55,50 @@ public: static constexpr std::array validProtoMarks = {protoMarkLocal, protoMarkTCP, protoMarkTLS, protoMarkUDP, protoMarkWS, protoMarkWSS}; + + // factory methods + + template + AdcEndpoint createLocal(R&& path) + { + return AdcEndpoint(PROTO_ID_LOCAL, std::string_view(""), -1, std::forward(path)); + } + + template + AdcEndpoint createTCP(HT&& host, int port) + { + return AdcEndpoint(PROTO_ID_TCP, std::forward(host), port); + } + +#ifdef USE_OPENSSL_WITH_ASIO + template + AdcEndpoint createTLS(HT&& host, int port) + { + return AdcEndpoint(PROTO_ID_TLS, std::forward(host), port); + } +#endif + + template + AdcEndpoint createUDP(HT&& host, int port) + { + return AdcEndpoint(PROTO_ID_UDP, std::forward(host), port); + } + + + template + AdcEndpoint createWS(HT&& host, int port, PTT&& path = PTT()) + { + return AdcEndpoint(PROTO_ID_WS, std::forward(host), port, std::forward(path)); + } + + + template + AdcEndpoint createWSS(HT&& host, int port, PTT&& path = PTT()) + { + return AdcEndpoint(PROTO_ID_WSS, std::forward(host), port, std::forward(path)); + } + + /* Constructors and destructor */ AdcEndpoint() = default; @@ -255,8 +307,10 @@ public: auto sz = std::distance(r.begin(), found.begin()); - std::string proto; - std::ranges::copy(r | std::views::take(sz), std::back_inserter(proto)); + std::string proto; // case-insensitive! + std::ranges::copy( + r | std::views::take(sz) | std::views::transform([](auto ch) -> char { return std::tolower(ch); }), + std::back_inserter(proto)); std::underlying_type_t i = 0; proto_id_t id = PROTO_ID_UNKNOWN; diff --git a/net/adc_netmsg.h b/net/adc_netmsg.h index 9cf1cc8..1e661ae 100644 --- a/net/adc_netmsg.h +++ b/net/adc_netmsg.h @@ -43,91 +43,17 @@ void convertToBytes(ByteStorageT& res, const T& v, const Ts&... vs) } // namespace utils -namespace traits -{ - -template -concept adc_netmessage_c = requires(const T t) { // const methods - requires std::same_as, char>; - { t.empty() } -> std::convertible_to; - { t.byteSize() } -> std::convertible_to; - { t.bytes() } -> adc_output_char_range; - { t.byteView() } -> adc_range_of_view_char_range; - { t.setFromBytes(std::input_iterator) } -> std::same_as; -}; - -} // namespace traits - /* - Trivial message interface: - byte storage: just a char-range -*/ + */ template -class AdcNetMessageTrivialInterface +class AdcNetMessageInterface { public: - virtual ~AdcNetMessageTrivialInterface() = default; + typedef ByteStorageT byte_storage_t; + typedef ByteViewT byte_view_t; - bool empty() const { return std::ranges::distance(_bytes.begin(), _bytes.end()) == 0; } - - size_t byteSize() const - { - // - return std::ranges::distance(_bytes.begin(), _bytes.end()); - } - - // get a copy of message bytes - template - R bytes() const - { - R r; - std::ranges::copy(_bytes, std::back_inserter(r)); - - return r; - } - - - virtual ByteStorageT bytes() const - { - // - return bytes(); - } - - // get a view of message bytes - template - R bytesView() const - { - R r; - - r.emplace_back(_bytes.begin(), _bytes.end()); - - return r; - } - - std::vector bytesView() const - { - // - return bytesView>(); - } - -protected: - ByteStorageT _bytes; - - AdcNetMessageTrivialInterface() = default; -}; - - - -/* - interface for more complex messages: - byte storage: std::vector of char-range (sequence of buffers) -*/ -template -class AdcNetMessageSeqInterface -{ -public: - virtual ~AdcNetMessageSeqInterface() = default; + virtual ~AdcNetMessageInterface() = default; bool empty() const { @@ -201,11 +127,11 @@ public: } protected: - std::vector _bytes; + std::vector _bytes; // sequence of byte buffers size_t _reservedNum; - AdcNetMessageSeqInterface(size_t reserved = 0) : _reservedNum(reserved), _bytes() + AdcNetMessageInterface(size_t reserved = 0) : _reservedNum(reserved), _bytes() { // reserve the "_reservedNum" first elements _bytes.resize(_reservedNum); @@ -220,16 +146,24 @@ protected: // Generic message class template -class AdcGenericNetMessage : public AdcNetMessageTrivialInterface +class AdcGenericNetMessage : public AdcNetMessageInterface { - using base_t = AdcNetMessageTrivialInterface; + using base_t = AdcNetMessageInterface; public: + using typename base_t::byte_storage_t; + using typename base_t::byte_view_t; + using base_t::base_t; using base_t::bytes; using base_t::bytesView; using base_t::empty; + AdcGenericNetMessage() : base_t() + { + // just the single buffer + this->_bytes.resize(1); + } template AdcGenericNetMessage(const T& v, const Ts&... vs) : AdcGenericNetMessage() @@ -241,13 +175,13 @@ public: template void appendBytes(const T& v, const Ts&... vs) { - utils::convertToBytes(this->_bytes, v, vs...); + utils::convertToBytes(this->_bytes[0], v, vs...); } template void setBytes(const T& v, const Ts&... vs) { - this->_bytes = ByteStorageT(); + this->_bytes[0] = ByteStorageT(); appendBytes(v, vs...); } @@ -257,7 +191,7 @@ public: void appendFromBytes(IT begin, IT end) requires std::same_as, char> { - std::copy(begin, end, std::back_inserter(this->_bytes)); + std::copy(begin, end, std::back_inserter(this->_bytes[0])); } @@ -265,7 +199,7 @@ public: void setFromBytes(IT begin, IT end) requires std::same_as, char> { - this->_bytes = ByteStorageT(); + this->_bytes[0] = ByteStorageT(); appendFromBytes(begin, end); } @@ -283,10 +217,10 @@ static constexpr char ADC_DEFAULT_KEY_TOKEN_DELIMITER[] = " "; template -class AdcTokenNetMessage : public AdcNetMessageSeqInterface +class AdcTokenNetMessage : public AdcNetMessageInterface { // AdcNetMessageSeqInterface::_bytes - tokens - using base_t = AdcNetMessageSeqInterface; + using base_t = AdcNetMessageInterface; public: static constexpr std::string_view tokenDelimiter{TOKEN_DELIM}; @@ -457,7 +391,7 @@ public: using base_t::tokens; - AdcKeyTokenNetMessage() + AdcKeyTokenNetMessage() : base_t() { this->_reservedNum = 1; // reserve the first element for keyword this->_bytes.resize(this->_reservedNum); @@ -599,4 +533,29 @@ public: } }; + + +namespace traits +{ + +template +concept adc_netmessage_c = requires { + typename T::byte_storage_t; + typename T::byte_view_t; + std::derived_from>; +}; +// template +// concept adc_netmessage_c = requires(const T t) { // const methods +// requires std::same_as, char>; +// { t.empty() } -> std::convertible_to; +// { t.byteSize() } -> std::convertible_to; +// { t.bytes() } -> adc_output_char_range; +// { t.byteView() } -> adc_range_of_view_char_range; +// { t.setFromBytes(std::input_iterator) } -> std::same_as; +// }; + +} // namespace traits + + + } // namespace adc diff --git a/net/adc_netservice.h b/net/adc_netservice.h index c794137..534291a 100644 --- a/net/adc_netservice.h +++ b/net/adc_netservice.h @@ -11,12 +11,13 @@ ABSTRACT DEVICE COMPONENTS LIBRARY #include +#include "adc_netmsg.h" namespace adc { -template +template class AdcNetService { protected: @@ -56,13 +57,13 @@ public: } - template + template auto asyncSend(const NetMessageT& msg, const timeout_drtn_t& timeout = defaultSendTimeout, ArgTs&&... args) { return _impl.asyncSend(msg, timeout, std::forward(args)...); } - template + template auto asyncReceive(const timeout_drtn_t& timeout = defaultRecvTimeout, ArgTs&&... args) { return _impl.asyncReceive(timeout, std::forward(args)...); @@ -78,14 +79,14 @@ public: return _impl.connect(endpoint, timeout, std::forward(args)...); } - template + template auto send(const NetMessageT& msg, const timeout_drtn_t& timeout = defaultSendTimeout, ArgTs&&... args) { return _impl.send(msg, timeout, std::forward(args)...); } - template + template NetMessageT receive(const timeout_drtn_t& timeout = defaultRecvTimeout, ArgTs&&... args) { return _impl.receive(timeout, std::forward(args)...); @@ -102,11 +103,11 @@ public: namespace traits { -// template -// concept adc_netservice_c = requires { -// typename T::impl_t; -// std::derived_from>; -// }; +template +concept adc_netservice_c = requires { + typename T::impl_t; + std::derived_from>; +}; } // namespace traits diff --git a/net/adc_netservice_asio.h b/net/adc_netservice_asio.h index 6314e97..461d662 100644 --- a/net/adc_netservice_asio.h +++ b/net/adc_netservice_asio.h @@ -10,15 +10,19 @@ #include +#include "adc_netservice.h" #ifdef USE_ASIO_LIBRARY -#include +// #include #include #include #include #include -#include +// #include +#include #include +#include +#include #include #include #include @@ -26,11 +30,14 @@ #include + +#include "adc_netmsg.h" + namespace adc::impl { -template +template class AdcNetServiceASIO : public InetProtoT { public: @@ -63,7 +70,12 @@ public: switch (state) { case starting: state = cancel_timer; - return _socket.async_connect(endpoint, std::move(self)); + if constexpr (std::derived_from>) { + return _socket.lowest_layer().async_connect(endpoint, std::move(self)); + } else { + return _socket.async_connect(endpoint, std::move(self)); + } break; case cancel_timer: timer->cancel(); @@ -78,7 +90,9 @@ public: token, _socket); } - template CompletionTokenT> + template CompletionTokenT> auto asynSend(const NetMessageT& msg, const TimeoutT& timeout, CompletionTokenT&& token) { enum { starting, cancel_timer }; @@ -93,10 +107,11 @@ public: // wrapper return asio::async_compose( [buff = std::move(buff), timer = std::move(timer), state = starting, this]( - auto& self, const std::error_code& ec = {}, size_t sz = 0) mutable { + auto& self, const std::error_code& ec = {}, size_t = 0) mutable { if (!ec) { switch (state) { case starting: + state = cancel_timer; if constexpr (std::derived_from< socket_t, asio::basic_stream_socket>) { return asio::async_write(_socket, buff, std::move(self)); @@ -124,7 +139,7 @@ public: } - template + template auto asyncReceive(const TimeoutT& timeout, CompletionTokenT&& token) { enum { starting, cancel_timer }; @@ -135,15 +150,16 @@ public: return asio::async_compose( [timer = std::move(timer), out_flags = std::move(out_flags), state = starting, this]( - auto& self, const std::error_code& ec = {}, size_t sz = 0) mutable { + auto& self, const std::error_code& ec = {}, size_t = 0) mutable { if (!ec) { switch (state) { case starting: + state = cancel_timer; if constexpr (std::derived_from< socket_t, asio::basic_stream_socket>) { return asio::async_read_until( _socket, _streamBuffer, - [this](auto begin, auto end) { this->matchCondition(begin, end); }, + [this](auto begin, auto end) { return this->matchCondition(begin, end); }, std::move(self)); } else if constexpr (std::derived_from>) { @@ -200,20 +216,43 @@ public: ftr.get(); } - template + template auto send(const NetMessageT& msg, const TimeoutT& timeout) { std::future ftr = asyncSend(msg, timeout, asio::use_future); ftr.get(); } - template + template auto receive(const TimeoutT& timeout) { std::future ftr = asyncReceive(timeout, asio::use_future); return ftr.get(); } + + std::error_code close(asio::socket_base::shutdown_type stype = asio::socket_base::shutdown_both) + { + std::error_code ec; + + if constexpr (std::derived_from>) { + _socket.shutdown(ec); // shutdown OpenSSL stream + if (!ec) { + _socket.lowest_layer().shutdown(stype, ec); + if (!ec) { + _socket.lowest_layer().close(ec); + } + } + } else { + _socket.shutdown(stype, ec); + if (!ec) { + _socket.close(ec); + } + } + + return ec; + } + protected: socket_t& _socket; @@ -242,4 +281,11 @@ protected: } // namespace adc::impl +namespace adc +{ + +typedef AdcNetService> AdcNetServiceAsioTcp; + +} // namespace adc + #endif