#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_OPENSSL_WITH_ASIO #include #include #endif #include "../../common/adc_traits.h" #include "../adc_net_concepts.h" namespace adc::traits { // special ASIO-related template specializations template <> struct adc_func_traits> { using ret_t = std::nullptr_t; using args_t = std::tuple; using arg1_t = std::nullptr_t; static constexpr size_t arity = 0; }; template <> struct adc_func_traits> { using ret_t = std::nullptr_t; using args_t = std::tuple; using arg1_t = std::nullptr_t; static constexpr size_t arity = 0; }; template <> struct adc_func_traits { using ret_t = std::nullptr_t; using args_t = std::tuple; using arg1_t = std::nullptr_t; static constexpr size_t arity = 0; }; } // namespace adc::traits namespace adc::impl { template concept adc_asio_transport_proto_c = std::derived_from || std::derived_from || std::derived_from || std::derived_from || std::derived_from; template concept adc_asio_tls_transport_proto_c = std::derived_from || std::derived_from || std::derived_from; template concept adc_asio_stream_transport_proto_c = std::derived_from || std::derived_from; template concept adc_asio_is_future = requires { // [](std::type_identity>) {}(std::type_identity>()); [](std::type_identity>) { }(std::type_identity>{}); }; template concept adc_asio_is_awaitable = requires { [](std::type_identity>) { }(std::type_identity>{}); }; template concept adc_asio_special_comp_token_c = adc_asio_is_future || adc_asio_is_awaitable || std::same_as, asio::deferred_t>; template < bool USE_TLS, // true - use of asio::ssl::stream adc_asio_transport_proto_c TRANSPORT_PROTOT, // transport-level proto (e.g. asio::ip::tcp) interfaces::adc_netsession_proto_c SESSION_PROTOT, // session-level proto (see ../adc_netproto.h) traits::adc_output_char_range RMSGT = std::vector> // used only for inner storing of message byte sequence class AdcBaseNetServiceASIO : public SESSION_PROTOT { public: #ifdef USE_OPENSSL_WITH_ASIO // static_assert(!(USE_TLS && adc_asio_tls_transport_proto_c), // "INVALID 'TRANSPORT_PROTOT' TEMPLATE ARGUMENT!"); static constexpr bool isTLS = USE_TLS; #else // ignore USE_TLS static constexpr bool isTLS = false; #endif // typedefs to satisfy 'adc_netservice_c' concept typedef std::string_view netservice_ident_t; typedef std::vector send_msg_t; // in general, only one of several possible typedef RMSGT recv_msg_t; // in general, only one of several possible (see class template arguments declaration) typedef traits::adc_common_duration_t timeout_t; using endpoint_t = typename TRANSPORT_PROTOT::endpoint; // typedefs for completion tokens (callbacks, required by 'adc_netservice_c' concept) typedef std::error_code async_callback_err_t; typedef std::function async_connect_callback_t; typedef std::function async_send_callback_t; typedef std::function async_receive_callback_t; // typedefs from transport protocol using socket_t = typename TRANSPORT_PROTOT::socket; // low-level socket type #ifdef USE_OPENSSL_WITH_ASIO using session_level_socket_t = std::conditional_t, std::nullptr_t>; // TLS certificate attributes comparison function: // 'serial' - as returned by OpenSSL BN_bn2hex // 'fingerprint' - as returned by OpenSSL X509_digest // 'depth' - depth in chain // the function must return 0 - if comparison failed; otherwise - something != 0 typedef std::function& fingerprint, int depth)> cert_comp_func_t; #else using session_level_socket_t = socket_t; #endif struct acceptor_t { using srv_acceptor_t = std::conditional_t< std::derived_from>, std::nullptr_t, // there is no acceptor typename TRANSPORT_PROTOT::acceptor>; static constexpr std::chrono::duration DEFAULT_ACCEPT_TIMEOUT = std::chrono::seconds::max(); typedef AdcBaseNetServiceASIO netservice_t; typedef std::function async_accept_callback_t; acceptor_t(asio::io_context& io_context) requires(!netservice_t::isTLS) : _ioContext(io_context), _acceptor(io_context) { } acceptor_t(asio::io_context& io_context, const netservice_t::endpoint_t& endpoint) requires(!netservice_t::isTLS) : _ioContext(io_context), _acceptor(io_context, endpoint) { } #ifdef USE_OPENSSL_WITH_ASIO acceptor_t(asio::io_context& io_context, asio::ssl::context tls_ctx, asio::ssl::verify_mode tls_verify_mode = asio::ssl::context_base::verify_peer) requires netservice_t::isTLS : _ioContext(io_context), _acceptor(io_context), _tlsCtx(std::move(tls_ctx)), _tlsVerMode(tls_verify_mode) { } acceptor_t(asio::io_context& io_context, const netservice_t::endpoint_t& endpoint, asio::ssl::context tls_ctx, asio::ssl::verify_mode tls_verify_mode = asio::ssl::context_base::verify_peer) requires netservice_t::isTLS : _ioContext(io_context), _acceptor(io_context, endpoint), _tlsCtx(std::move(tls_ctx)), _tlsVerMode(tls_verify_mode) { } #endif template TokenT, traits::adc_time_duration_c DT = decltype(DEFAULT_ACCEPT_TIMEOUT)> auto asyncAccept(TokenT&& token, const DT& timeout = DEFAULT_ACCEPT_TIMEOUT) { enum { sock_accept, handshake, done }; auto timer = netservice_t::getDeadlineTimer(_acceptor, timeout); #ifdef USE_OPENSSL_WITH_ASIO auto srv = [&, this]() { if constexpr (netservice_t::isTLS) { return std::make_unique(_ioContext, std::move(_tlsCtx), _tlsVerMode); } else { return std::make_unique(_ioContext); } }(); #else auto srv = std::make_unique(_ioContext); #endif return asio::async_compose( [timer = std::move(timer), srv = std::move(srv), state = sock_accept, this]( auto& self, std::error_code ec = {}) mutable { if (!ec) { switch (state) { case sock_accept: if constexpr (netservice_t::isTLS) { state = handshake; } else { state = done; } return _acceptor.async_accept(srv->_socket, std::move(self)); break; case handshake: state = done; #ifdef USE_OPENSSL_WITH_ASIO if constexpr (netservice_t::isTLS) { srv->_sessSocket = netservice_t::session_level_socket_t(std::move(srv->_socket), srv->_tlsContext); return srv->_sessSocket.async_handshake(asio::ssl::stream_base::server, std::move(self)); } #endif break; case done: break; default: break; } } if (netservice_t::isTimeout(timer, ec)) { ec = std::make_error_code(std::errc::timed_out); } else { // an error occured in async_accept od async_handshake timer->cancel(); } self.complete(ec, std::move(*srv)); srv.reset(); }, token, _ioContext); } template TokenT, traits::adc_time_duration_c DT = decltype(DEFAULT_ACCEPT_TIMEOUT)> auto asyncAccept(const netservice_t::endpoint_t& endpoint, TokenT&& token, const DT& timeout = DEFAULT_ACCEPT_TIMEOUT) { _acceptor = srv_acceptor_t(_ioContext, endpoint); return asyncAccept(std::forward(token), timeout); } template auto accept(const DT& timeout = DEFAULT_ACCEPT_TIMEOUT) { auto f = asyncAccept(asio::use_future, timeout); return f.get(); } template auto accept(const endpoint_t& endpoint, const DT& timeout = DEFAULT_ACCEPT_TIMEOUT) { _acceptor = srv_acceptor_t(_ioContext, endpoint); return accept(timeout); } void close(std::error_code& ec) { _acceptor.close(ec); } private: asio::io_context& _ioContext; srv_acceptor_t _acceptor; #ifdef USE_OPENSSL_WITH_ASIO asio::ssl::context _tlsCtx{asio::ssl::context_base::tlsv13}; asio::ssl::verify_mode _tlsVerMode; #endif }; static constexpr std::chrono::duration DEFAULT_CONNECT_TIMEOUT = std::chrono::seconds(10); static constexpr std::chrono::duration DEFAULT_SEND_TIMEOUT = std::chrono::seconds(5); static constexpr std::chrono::duration DEFAULT_RECEIVE_TIMEOUT = std::chrono::seconds(5); /* CONSTRUCTORS AND DESTRUCTOR */ AdcBaseNetServiceASIO(asio::io_context& ctx) requires(!isTLS) : SESSION_PROTOT(), _ioContext(ctx), _receiveStrand(_ioContext), _socket(_ioContext), _receiveQueue() { } // AdcBaseNetServiceASIO(socket_t socket) // : SESSION_PROTOT(), // _ioContext(static_cast(socket.get_executor().context())), // _socket(std::move(socket)), // _receiveStrand(_ioContext), // _receiveQueue() // { // } #ifdef USE_OPENSSL_WITH_ASIO AdcBaseNetServiceASIO(asio::io_context& ctx, asio::ssl::context tls_context, const asio::ssl::verify_mode& tls_peer_verify_mode = asio::ssl::verify_peer) requires isTLS : SESSION_PROTOT(), _ioContext(ctx), _receiveStrand(_ioContext), _socket(_ioContext), _receiveQueue(), _tlsContext(std::move(tls_context)), _tlsPeerVerifyMode(tls_peer_verify_mode), _sessSocket(_ioContext, _tlsContext) { } #endif AdcBaseNetServiceASIO(AdcBaseNetServiceASIO&& other) requires(!isTLS) : _ioContext(other._ioContext), _receiveStrand(std::move(other._receiveStrand)), _socket(std::move(other._socket)), _sessSocket(std::move(other._sessSocket)), _streamBuffer(), _receiveQueue(std::move(other._receiveQueue)) { auto bytes = asio::buffer_copy(_streamBuffer.prepare(other._streamBuffer.size()), other._streamBuffer.data()); _streamBuffer.commit(bytes); } #ifdef USE_OPENSSL_WITH_ASIO AdcBaseNetServiceASIO(AdcBaseNetServiceASIO&& other) requires isTLS : _ioContext(other._ioContext), _receiveStrand(std::move(other._receiveStrand)), _socket(std::move(other._socket)), _sessSocket(std::move(other._sessSocket)), _streamBuffer(), _receiveQueue(std::move(other._receiveQueue)), _tlsContext(std::move(other._tlsContext)), _tlsPeerVerifyMode(std::move(other._tlsPeerVerifyMode)) { auto bytes = asio::buffer_copy(_streamBuffer.prepare(other._streamBuffer.size()), other._streamBuffer.data()); _streamBuffer.commit(bytes); } #endif AdcBaseNetServiceASIO(const AdcBaseNetServiceASIO&) = delete; // no copy constructor! virtual ~AdcBaseNetServiceASIO() {} AdcBaseNetServiceASIO& operator=(const AdcBaseNetServiceASIO&) = delete; AdcBaseNetServiceASIO& operator=(AdcBaseNetServiceASIO&& other) requires(!isTLS) { _ioContext = other._ioContext; _receiveStrand = std::move(other._receiveStrand); _receiveQueue = std::move(other._receiveQueue); _socket = std::move(other._socket); _streamBuffer.consume(_streamBuffer.size()); auto bytes = asio::buffer_copy(_streamBuffer.prepare(other._streamBuffer.size()), other._streamBuffer.data()); _streamBuffer.commit(bytes); return *this; }; #ifdef USE_OPENSSL_WITH_ASIO AdcBaseNetServiceASIO& operator=(AdcBaseNetServiceASIO&& other) requires isTLS { _ioContext = other._ioContext; _receiveStrand = std::move(other._receiveStrand); _receiveQueue = std::move(other._receiveQueue); _socket = std::move(other._socket); _sessSocket = std::move(other._sessSocket); _tlsContext = std::move(other._tlsContext); _tlsPeerVerifyMode = std::move(other._tlsPeerVerifyMode); _streamBuffer.consume(_streamBuffer.size()); auto bytes = asio::buffer_copy(_streamBuffer.prepare(other._streamBuffer.size()), other._streamBuffer.data()); _streamBuffer.commit(bytes); return *this; }; #endif constexpr netservice_ident_t ident() const { return _ident; } /* asynchronuos methods */ template TokenT, traits::adc_time_duration_c TimeoutT = decltype(DEFAULT_CONNECT_TIMEOUT)> auto asyncConnect(const endpoint_t& endpoint, TokenT&& token, const TimeoutT& timeout = DEFAULT_CONNECT_TIMEOUT) { enum { sock_connect, handshake, done }; auto timer = getDeadlineTimer(_socket, timeout); return asio::async_compose( [state = sock_connect, endpoint, timer = std::move(timer), this](auto& self, std::error_code ec = {}) mutable { if (!ec) { switch (state) { case sock_connect: if constexpr (isTLS) { state = handshake; } else { state = done; } return _socket.async_connect(endpoint, std::move(self)); break; case handshake: state = done; #ifdef USE_OPENSSL_WITH_ASIO if constexpr (isTLS) { _sessSocket = session_level_socket_t(std::move(_socket), _tlsContext); return _sessSocket.async_handshake(session_level_socket_t::client, std::move(self)); } #endif break; case done: break; default: break; } } if (isTimeout(timer, ec)) { ec = std::make_error_code(std::errc::timed_out); } else { // an error occured in async_connect or async_handshake timer->cancel(); } self.complete(ec); }, token, _socket); } template TokenT, traits::adc_time_duration_c TimeoutT = decltype(DEFAULT_SEND_TIMEOUT)> auto asyncSend(const MessageT& msg, TokenT&& token, const TimeoutT& timeout = DEFAULT_SEND_TIMEOUT) { // create buffer sequence of sending session protocol representation of the input message std::vector buff_seq; // std::ranges::for_each(this->toProto(msg), [&buff_seq](const auto& el) { buff_seq.emplace_back(el); }); std::ranges::for_each(this->toProto(msg), [&buff_seq](const auto& el) { buff_seq.emplace_back(el.data(), el.size()); }); auto timer = getDeadlineTimer(_socket, timeout); return asio::async_compose( [start = true, buff_seq = std::move(buff_seq), timer = std::move(timer), this]( auto& self, std::error_code ec = {}, size_t = 0) mutable { if (!ec) { if (start) { start = false; if constexpr (isTLS) { return asio::async_write(_sessSocket, buff_seq, std::move(self)); } else { if constexpr (std::derived_from< socket_t, asio::basic_stream_socket>) { return asio::async_write(_socket, buff_seq, std::move(self)); } else if constexpr (std::derived_from>) { return _socket.async_send(buff_seq, std::move(self)); } else if constexpr (std::derived_from>) { return _socket.async_send(buff_seq, 0, std::move(self)); } else { static_assert(false, "UNKNOWN ASIO-LIBRARY SOCKET TYPE!!!"); } } } } if (isTimeout(timer, ec)) { ec = std::make_error_code(std::errc::timed_out); } else { // an error occured in async_write/async_send timer->cancel(); } self.complete(ec); }, token, _socket); } template auto asyncReceive(TokenT&& token, const TimeoutT& timeout = DEFAULT_RECEIVE_TIMEOUT) { // static asio::streambuf _streamBuffer; // check completion token signature and deduce message type // if constexpr (!adc_asio_special_comp_token_c && !is_async_ctx_t) { if constexpr (!adc_asio_special_comp_token_c) { static_assert(traits::adc_func_traits::arity == 2, "INVALID COMPLETION TOKEN SIGNATURE!"); static_assert(std::is_same_v>, std::error_code>, "INVALID COMPLETION TOKEN SIGNATURE!"); static_assert(traits::adc_output_char_range< std::tuple_element_t<1, typename traits::adc_func_traits::args_t>>, "INVALID COMPLETION TOKEN SIGNATURE!"); } using msg_t = std::conditional_t< // adc_asio_special_comp_token_c || is_async_ctx_t, RMSGT, adc_asio_special_comp_token_c, RMSGT, std::remove_cvref_t::args_t>>>; auto out_flags = std::make_shared(); auto timer = getDeadlineTimer(_socket, timeout); return asio::async_compose( [out_flags, do_read = true, timer = std::move(timer), this](auto& self, std::error_code ec = {}, size_t nbytes = 0) mutable { msg_t msg; if (!ec) { if (do_read) { do_read = false; if (_receiveQueue.size()) { // return message from queue timer->cancel(); auto imsg = _receiveQueue.front(); _receiveQueue.pop(); if constexpr (std::is_same_v) { self.complete(std::error_code(), std::move(imsg)); } else { self.complete(std::error_code(), {imsg.begin(), imsg.end()}); } return; } auto n_avail = _socket.available(); if (!n_avail) { return _socket.async_wait(asio::ip::tcp::socket::wait_read, std::move(self)); } auto buff = _streamBuffer.prepare(n_avail ? n_avail : 1); if constexpr (isTLS) { return asio::async_read(_sessSocket, std::move(buff), asio::transfer_at_least(1), std::move(self)); } else { if constexpr (std::derived_from< socket_t, asio::basic_stream_socket>) { return asio::async_read(_socket, std::move(buff), asio::transfer_at_least(1), std::move(self)); } else if constexpr (std::derived_from>) { // datagram, so it should be received at once return _socket.async_receive(std::move(buff), std::move(self)); } else if constexpr (std::derived_from>) { // datagram, so it should be received at once return _socket.async_receive(std::move(buff), *out_flags, std::move(self)); } else { static_assert(false, "UNKNOWN ASIO-LIBRARY SOCKET TYPE!!!"); } } } // zero-length message for SEQ_PACK sockets is EOF if constexpr (std::derived_from>) { if (!nbytes) { timer->cancel(); self.complete(std::error_code(asio::error::misc_errors::eof), std::move(msg)); return; } } _streamBuffer.commit(nbytes); // if (!nbytes) { // do_read = true; // asio::post(std::move(self)); // initiate consequence socket's read operation // return; // } auto start_ptr = static_cast(_streamBuffer.data().data()); auto net_pack = this->search(std::span(start_ptr, _streamBuffer.size())); if (net_pack.empty()) { do_read = true; asio::post(std::move(self)); // initiate consequence socket's read operation return; } timer->cancel(); // there were no errors in the asynchronous read-operation, so stop timer // here one has at least a single message std::ranges::copy(this->fromProto(net_pack), std::back_inserter(msg)); _streamBuffer.consume(net_pack.size()); while (_streamBuffer.size()) { // search for possible additional session protocol packets start_ptr = static_cast(_streamBuffer.data().data()); net_pack = this->search(std::span(start_ptr, _streamBuffer.size())); if (!net_pack.empty()) { _receiveQueue.emplace(); std::ranges::copy(this->fromProto(net_pack), std::back_inserter(_receiveQueue.back())); _streamBuffer.consume(net_pack.size()); } else { break; // exit and hold remaining bytes in stream buffer } } } if (isTimeout(timer, ec)) { ec = std::make_error_code(std::errc::timed_out); } else { // an error occured in async_* timer->cancel(); } if constexpr (std::is_same_v) { self.complete(ec, std::move(msg)); } else { self.complete(ec, {msg.begin(), msg.end()}); } // if constexpr (adc_asio_special_comp_token_c) { // self.complete(ec, std::move(msg)); // } else { // may be of non-RMSGT type // self.complete(ec, {msg.begin(), msg.end()}); // } }, token, _receiveStrand); } /* blocking methods */ template auto connect(const endpoint_t& endpoint, const TimeoutT& timeout = DEFAULT_CONNECT_TIMEOUT) { std::future ftr = asyncConnect(endpoint, asio::use_future, timeout); ftr.get(); } template auto send(const R& msg, const TimeoutT& timeout = DEFAULT_SEND_TIMEOUT) { std::future ftr = asyncSend(msg, asio::use_future, timeout); ftr.get(); } template auto receive(const TimeoutT& timeout = DEFAULT_RECEIVE_TIMEOUT) { std::future ftr = asyncReceive(asio::use_future, timeout); return ftr.get(); } // one still may receive messages from queue! std::error_code close() { std::error_code ec; #ifdef USE_OPENSSL_WITH_ASIO if constexpr (isTLS) { _sessSocket.shutdown(ec); } #endif _socket.shutdown(_shutdownType, ec); if (!ec) { _socket.close(ec); } return ec; } /* additional ASIO-related methods */ void clearRcvQueue() { // clear receiving messages queue // NOTE: there is no racing condition here since using asio::strand! asio::post(_receiveStrand, [this]() { // _receiveQueue = {}; }); } void clearRcvBuff() { asio::post(_receiveStrand, [this]() { // _streamBuffer.consume(_streamBuffer.size()); }); } void clearRcvData() { asio::post(_receiveStrand, [this]() { _receiveQueue = {}; _streamBuffer.consume(_streamBuffer.size()); }); } void setShutdownType(asio::socket_base::shutdown_type shutdown_type) { _shutdownType = shutdown_type; } asio::socket_base::shutdown_type getShutdownType() const { return _shutdownType; } template static void formatError(std::error_code err, OutputItT out_iter, FormatT fmt = "{} (Err category: {}) (Err msg: {})") { std::format_to(out_iter, fmt, err.value(), err.category().name(), err.message()); } protected: static constexpr netservice_ident_t _ident = std::derived_from> ? "STREAM-SOCKET NETWORK SERVICE" : std::derived_from> ? "DATAGRAM-SOCKET NETWORK SERVICE" : std::derived_from> ? "SEQPACKET-SOCKET NETWORK SERVICE" : "UNKNOWN"; asio::io_context& _ioContext; asio::io_context::strand _receiveStrand; socket_t _socket; asio::streambuf _streamBuffer; std::queue> _receiveQueue; asio::socket_base::shutdown_type _shutdownType = asio::socket_base::shutdown_both; #ifdef USE_OPENSSL_WITH_ASIO session_level_socket_t _sessSocket; std::conditional_t _tlsContext; asio::ssl::verify_mode _tlsPeerVerifyMode; cert_comp_func_t _tlsCertCompFunc; std::string _tlsCertFingerprintDigest; // reference implementation fo certificate verification function virtual bool verifyCertificate(int preverified_ok, X509_STORE_CTX* store) { if (preverified_ok == 0) { int err = X509_STORE_CTX_get_error(store); auto err_str = X509_verify_cert_error_string(err); // log_error("TLS certificate verification error: {}", err_str); return preverified_ok; } char subject_name[256]; int depth; ASN1_INTEGER* serial; BIGNUM* bnser; X509* cert = X509_STORE_CTX_get_current_cert(store); if (cert != NULL) { depth = X509_STORE_CTX_get_error_depth(store); X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); serial = X509_get_serialNumber(cert); // IT IS INTERNAL POINTER SO IT MUST NOT BE FREED UP!!! bnser = ASN1_INTEGER_to_BN(serial, NULL); auto serial_hex = BN_bn2hex(bnser); // log_debug("Received TLS certificate: SUBJECT = {}, SERIAL = {}, DEPTH = {}", subject_name, serial_hex, // depth); // if no compare function then do not compute fingerprint if (_tlsCertCompFunc) { // compute certificate fingerprint unsigned char digest_buff[EVP_MAX_MD_SIZE]; const EVP_MD* digest = EVP_get_digestbyname(_tlsCertFingerprintDigest.c_str()); unsigned int N; if (X509_digest(cert, digest, digest_buff, &N)) { preverified_ok = _tlsCertCompFunc(std::string(serial_hex), std::vector(digest_buff, digest_buff + N), depth); } else { // log_error("Cannot compute client certificate fingerprint! Cannot verify the certificate!"); preverified_ok = 0; } } BN_free(bnser); OPENSSL_free(serial_hex); } else { // log_error("OpenSSL error: cannot get current certificate"); preverified_ok = 0; } return preverified_ok; } #endif template static std::unique_ptr getDeadlineTimer(CancelableT& obj, const TimeoutT& timeout, bool arm = true) { auto timer = std::make_unique(obj.get_executor()); // if (timeout == std::chrono::duration::max()) { // return timer; // do not arm the timer if MAX duration are given // } if (arm) { std::chrono::seconds max_d = std::chrono::duration_cast( std::chrono::steady_clock::time_point::max() - std::chrono::steady_clock::now() - std::chrono::seconds(1)); if (timeout < max_d) { timer->expires_after(timeout); } else { timer->expires_after(max_d); // to avoid overflow! } timer->async_wait([&obj](const std::error_code& ec) mutable { if (!ec) { obj.cancel(); } }); } return timer; } template static bool isTimeout(const std::unique_ptr& timer, const std::error_code& ec) { auto exp_time = timer->expiry(); return (exp_time < std::chrono::steady_clock::now()) && (ec == asio::error::operation_aborted); } template static bool isTimeout(const std::shared_ptr& timer, const std::error_code& ec) { auto exp_time = timer->expiry(); return (exp_time < std::chrono::steady_clock::now()) && (ec == asio::error::operation_aborted); } }; /* */ template < adc_asio_transport_proto_c TRANSPORT_PROTOT, // transport-level proto (e.g. asio::ip::tcp) interfaces::adc_netsession_proto_c SESSION_PROTOT, // session-level proto (see ../adc_netproto.h) traits::adc_output_char_range RMSGT = std::vector> // used only for inner storing of message byte sequence using AdcNetServiceASIO = AdcBaseNetServiceASIO; #ifdef USE_OPENSSL_WITH_ASIO template < adc_asio_transport_proto_c TRANSPORT_PROTOT, // transport-level proto (e.g. asio::ip::tcp) interfaces::adc_netsession_proto_c SESSION_PROTOT, // session-level proto (see ../adc_netproto.h) traits::adc_output_char_range RMSGT = std::vector> // used only for inner storing of message byte sequence using AdcNetServiceASIOTLS = AdcBaseNetServiceASIO; #endif } // namespace adc::impl