118 lines
7.1 KiB
C++
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>{};
|
|
}
|