Files
ligo/include/ligo/detail/transport.hpp
T
khodowany 67429eacd1
publish / publish (push) Successful in 31m41s
Initial public khodo snapshot
2026-09-04 19:30:11 -05:00

118 lines
7.1 KiB
C++

#pragma once
#include <expected>
#include <atomic>
#include <chrono>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/as_tuple.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/bind_cancellation_slot.hpp>
#include <boost/asio/cancellation_signal.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <boost/beast/core.hpp>
#include <boost/json/value.hpp>
#include <boost/system/error_code.hpp>
#include <modulus.hpp>
#include <ligo/detail/errors.hpp>
#include <ligo/service.hpp>
namespace ligo::detail {
struct endpoint_parts {
std::string host{"127.0.0.1"};
std::uint16_t port{};
};
auto status_name(std::int32_t code) -> std::string;
auto parse_status_code(std::string_view value) -> std::int32_t;
auto network_failure(boost::system::error_code const& error) -> modulus::error;
auto operation_failure(boost::system::error_code const& error, std::string timeout_message) -> modulus::error;
auto invalid_argument(std::string message) -> modulus::error;
auto unavailable(std::string message) -> modulus::error;
auto validate_positive_timeout(std::chrono::milliseconds timeout, std::string_view name) -> std::expected<void, modulus::error>;
auto report_error(std::function<void(modulus::error)> const& on_error, modulus::error error) -> void;
auto report_error(std::function<void(modulus::error)> const& on_error, std::expected<void, modulus::error> status) -> void;
auto response_envelope(std::expected<boost::json::value, modulus::error> result) -> boost::json::value;
auto serialize_response_envelope(std::expected<boost::json::value, modulus::error> result) -> std::string;
auto unwrap_response_envelope(boost::json::value value, std::string_view protocol_name) -> std::expected<boost::json::value, modulus::error>;
auto parse_port(std::string_view text) -> std::expected<std::uint16_t, modulus::error>;
auto parse_endpoint_text(std::string_view text, std::uint16_t fallback_port) -> std::expected<ligo::detail::endpoint_parts, modulus::error>;
auto parse_client_endpoint_text(std::string_view text) -> std::expected<ligo::detail::endpoint_parts, modulus::error>;
auto parse_server_endpoint_text(std::string_view text) -> std::expected<ligo::detail::endpoint_parts, modulus::error>;
auto start_tcp_acceptor(boost::asio::ip::tcp::acceptor& acceptor, std::string const& host, std::uint16_t port) -> std::expected<std::uint16_t, modulus::error>;
auto resolve_tcp_endpoints(boost::asio::any_io_executor executor, std::string const& host, std::uint16_t port, std::chrono::milliseconds timeout, std::string timeout_message) -> boost::asio::awaitable<std::expected<boost::asio::ip::tcp::resolver::results_type, modulus::error>>;
auto call_handler(ligo::service_router const& router, ligo::request_context context, boost::json::value payload) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>>;
auto call_response_handler(ligo::service_router const& router, ligo::request_context context, boost::json::value payload) -> boost::asio::awaitable<std::expected<ligo::service_response, modulus::error>>;
struct stream_deadline {
std::shared_ptr<boost::asio::steady_timer> timer{};
std::shared_ptr<std::atomic<std::bool_t>> timed_out{};
std::shared_ptr<std::atomic<std::bool_t>> active{};
std::shared_ptr<boost::asio::cancellation_signal> cancellation{};
};
auto start_stream_deadline(boost::beast::tcp_stream& stream, std::chrono::milliseconds timeout) -> ligo::detail::stream_deadline;
auto start_stream_deadline(boost::asio::ip::tcp::socket& socket, std::chrono::milliseconds timeout) -> ligo::detail::stream_deadline;
template <typename NEXT_LAYER>
auto start_stream_deadline(boost::asio::ssl::stream<NEXT_LAYER>& stream, std::chrono::milliseconds timeout) -> ligo::detail::stream_deadline;
auto cancel_stream_deadline(ligo::detail::stream_deadline& deadline) -> void;
auto deadline_token(ligo::detail::stream_deadline& deadline) -> decltype(boost::asio::bind_cancellation_slot(std::declval<boost::asio::cancellation_slot>(), boost::asio::as_tuple(boost::asio::use_awaitable)));
auto operation_failure(boost::system::error_code const& error, ligo::detail::stream_deadline const& deadline, std::string timeout_message) -> modulus::error;
template <typename NEXT_LAYER>
auto async_tls_handshake(boost::asio::ssl::stream<NEXT_LAYER>& stream, boost::asio::ssl::stream_base::handshake_type type, std::chrono::milliseconds timeout, std::string timeout_message) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
template <typename NEXT_LAYER>
auto async_tls_shutdown(boost::asio::ssl::stream<NEXT_LAYER>& stream, std::chrono::milliseconds timeout, std::string timeout_message) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
}
template <typename NEXT_LAYER>
auto ligo::detail::start_stream_deadline(boost::asio::ssl::stream<NEXT_LAYER>& stream, std::chrono::milliseconds timeout) -> ligo::detail::stream_deadline {
auto deadline = ligo::detail::stream_deadline{.timer = std::make_shared<boost::asio::steady_timer>(stream.get_executor()), .timed_out = std::make_shared<std::atomic<std::bool_t>>(false), .active = std::make_shared<std::atomic<std::bool_t>>(true), .cancellation = std::make_shared<boost::asio::cancellation_signal>()};
deadline.timer->expires_after(timeout);
deadline.timer->async_wait([timer = deadline.timer, timed_out = deadline.timed_out, active = deadline.active, cancellation = deadline.cancellation](boost::system::error_code const& error) {
if (!error && active->exchange(false, std::memory_order_acq_rel)) {
timed_out->store(true, std::memory_order_release);
cancellation->emit(boost::asio::cancellation_type::terminal);
}
});
return deadline;
}
template <typename NEXT_LAYER>
auto ligo::detail::async_tls_handshake(boost::asio::ssl::stream<NEXT_LAYER>& stream, boost::asio::ssl::stream_base::handshake_type type, std::chrono::milliseconds timeout, std::string timeout_message) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
auto deadline = ligo::detail::start_stream_deadline(stream, timeout);
auto [error] = co_await stream.async_handshake(type, ligo::detail::deadline_token(deadline));
ligo::detail::cancel_stream_deadline(deadline);
if (error) {
co_return ligo::detail::fail_void(ligo::detail::operation_failure(error, deadline, std::move(timeout_message)));
}
co_return std::expected<void, modulus::error>{};
}
template <typename NEXT_LAYER>
auto ligo::detail::async_tls_shutdown(boost::asio::ssl::stream<NEXT_LAYER>& stream, std::chrono::milliseconds timeout, std::string timeout_message) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
auto deadline = ligo::detail::start_stream_deadline(stream, timeout);
auto [error] = co_await stream.async_shutdown(ligo::detail::deadline_token(deadline));
ligo::detail::cancel_stream_deadline(deadline);
if (error) {
co_return ligo::detail::fail_void(ligo::detail::operation_failure(error, deadline, std::move(timeout_message)));
}
co_return std::expected<void, modulus::error>{};
}