Formatting
publish / publish (push) Successful in 27m34s

This commit is contained in:
khodowany
2026-09-14 23:54:56 -05:00
parent 6914712d20
commit 4170d3a003
26 changed files with 668 additions and 621 deletions
+4
View File
@@ -48,6 +48,10 @@ jobs:
restore-keys: conan-${{ runner.os }}- restore-keys: conan-${{ runner.os }}-
- name: Configure native release - name: Configure native release
run: python3 modulus.py configure --target x86_64_gcc12_release run: python3 modulus.py configure --target x86_64_gcc12_release
- name: Check code style
run: |
python3 -m pip install --no-cache-dir -r build/build_tools/docker/format-requirements.txt
python3 modulus.py format --check
- name: Build native release - name: Build native release
run: python3 modulus.py build --target x86_64_gcc12_release run: python3 modulus.py build --target x86_64_gcc12_release
- name: Test native release - name: Test native release
+2
View File
@@ -6,6 +6,8 @@ Ligo uses Modulus for shared build tooling, Boost.JSON, and OpenSSL integration.
## Build ## Build
Code style is managed by Modulus: run `./modulus.py format` or `./modulus.py format --check`. The generated Neovim integration uses the same pinned container formatter for C++ and Python, including existing format-on-save toggles. Configure first to prepare tooling; customize `tooling.editor.clang_format` and `tooling.format.exclude` in `modulus.json`, not generated editor files. See the shared Modulus README for the compact layout and no-alias policy.
Configure, build, and test the default target: Configure, build, and test the default target:
```sh ```sh
-8
View File
@@ -41,15 +41,12 @@ class ligo_conan(conan.ConanFile):
"tests/*.cpp", "tests/*.cpp",
"tests/*.hpp", "tests/*.hpp",
) )
def config_options(self): def config_options(self):
if self.settings.os == "Windows": if self.settings.os == "Windows":
del self.options.fPIC del self.options.fPIC
def configure(self): def configure(self):
if self.options.shared: if self.options.shared:
del self.options.fPIC del self.options.fPIC
def requirements(self): def requirements(self):
self.requires( self.requires(
"modulus/latest", "modulus/latest",
@@ -59,7 +56,6 @@ class ligo_conan(conan.ConanFile):
"with_openssl": True, "with_openssl": True,
}, },
) )
def generate(self): def generate(self):
deps = conan.tools.cmake.CMakeDeps(self) deps = conan.tools.cmake.CMakeDeps(self)
deps.generate() deps.generate()
@@ -69,14 +65,12 @@ class ligo_conan(conan.ConanFile):
toolchain.variables["BUILD_SHARED_LIBS"] = bool(self.options.shared) toolchain.variables["BUILD_SHARED_LIBS"] = bool(self.options.shared)
toolchain.variables["BUILD_TESTING"] = bool(self.options.with_tests) toolchain.variables["BUILD_TESTING"] = bool(self.options.with_tests)
toolchain.generate() toolchain.generate()
def build(self): def build(self):
cmake = conan.tools.cmake.CMake(self) cmake = conan.tools.cmake.CMake(self)
cmake.configure() cmake.configure()
cmake.build() cmake.build()
if self.options.with_tests and conan.tools.build.can_run(self): if self.options.with_tests and conan.tools.build.can_run(self):
cmake.test(target="check") cmake.test(target="check")
def package_id(self): def package_id(self):
self.info.options.rm_safe("with_tests") self.info.options.rm_safe("with_tests")
for requirement in ( for requirement in (
@@ -88,11 +82,9 @@ class ligo_conan(conan.ConanFile):
): ):
if requirement in self.info.requires.pkg_names: if requirement in self.info.requires.pkg_names:
self.info.requires.remove(requirement) self.info.requires.remove(requirement)
def package(self): def package(self):
cmake = conan.tools.cmake.CMake(self) cmake = conan.tools.cmake.CMake(self)
cmake.install() cmake.install()
def package_info(self): def package_info(self):
self.cpp_info.set_property( self.cpp_info.set_property(
"cmake_file_name", "cmake_file_name",
+10 -9
View File
@@ -12,6 +12,7 @@
#include <ligo/security.hpp> #include <ligo/security.hpp>
namespace ligo { namespace ligo {
enum class protocol { enum class protocol {
@@ -41,14 +42,14 @@ struct endpoint_config {
ligo::udp_reliability_config udp{}; ligo::udp_reliability_config udp{};
}; };
auto validate(ligo::endpoint_config const& config) -> std::expected<void, modulus::error>; auto validate(ligo::endpoint_config const &config) -> std::expected<void, modulus::error>;
auto parse_endpoint_config(boost::json::value const& value) -> std::expected<ligo::endpoint_config, modulus::error>; auto parse_endpoint_config(boost::json::value const &value) -> std::expected<ligo::endpoint_config, modulus::error>;
auto parse_endpoint_configs(boost::json::value const& value) -> std::expected<std::vector<ligo::endpoint_config>, modulus::error>; auto parse_endpoint_configs(boost::json::value const &value) -> std::expected<std::vector<ligo::endpoint_config>, modulus::error>;
auto load_endpoint_configs(std::string const& path) -> std::expected<std::vector<ligo::endpoint_config>, modulus::error>; auto load_endpoint_configs(std::string const &path) -> std::expected<std::vector<ligo::endpoint_config>, modulus::error>;
auto to_json(ligo::udp_reliability_config const& config) -> boost::json::value; auto to_json(ligo::udp_reliability_config const &config) -> boost::json::value;
auto to_json(ligo::http_config const& config) -> boost::json::value; auto to_json(ligo::http_config const &config) -> boost::json::value;
auto to_json(ligo::security_config const& config) -> boost::json::value; auto to_json(ligo::security_config const &config) -> boost::json::value;
auto to_json(ligo::endpoint_config const& config) -> boost::json::value; auto to_json(ligo::endpoint_config const &config) -> boost::json::value;
auto to_json(std::vector<ligo::endpoint_config> const& configs) -> boost::json::value; auto to_json(std::vector<ligo::endpoint_config> const &configs) -> boost::json::value;
} }
+20 -19
View File
@@ -8,26 +8,27 @@
#include <modulus.hpp> #include <modulus.hpp>
namespace ligo::detail { namespace ligo::detail {
constexpr std::int32_t error_ok{0}; constexpr std::int32_t error_ok{0};
constexpr std::int32_t error_invalid_argument{1}; constexpr std::int32_t error_invalid_argument{1};
constexpr std::int32_t error_not_found{2}; constexpr std::int32_t error_not_found{2};
constexpr std::int32_t error_unavailable{3}; constexpr std::int32_t error_unavailable{3};
constexpr std::int32_t error_timeout{4}; constexpr std::int32_t error_timeout{4};
constexpr std::int32_t error_internal{5}; constexpr std::int32_t error_internal{5};
auto make_error(std::int32_t code, std::string message, modulus::source_location location = modulus::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION())) -> modulus::error; auto make_error(std::int32_t code, std::string message, modulus::source_location location = modulus::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION())) -> modulus::error;
auto fail_void(modulus::error error) -> std::expected<void, modulus::error>; auto fail_void(modulus::error error) -> std::expected<void, modulus::error>;
template <typename VALUE> template <typename VALUE>
auto fail(modulus::error error) -> std::expected<VALUE, modulus::error>; auto fail(modulus::error error) -> std::expected<VALUE, modulus::error>;
template <typename VALUE> template <typename VALUE>
auto fail(std::expected<void, modulus::error> const& result) -> std::expected<VALUE, modulus::error>; auto fail(std::expected<void, modulus::error> const &result) -> std::expected<VALUE, modulus::error>;
template <typename VALUE, typename MESSAGE> template <typename VALUE, typename MESSAGE>
auto fail(std::int32_t code, MESSAGE&& message, modulus::source_location location = modulus::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION())) -> std::expected<VALUE, modulus::error>; auto fail(std::int32_t code, MESSAGE &&message, modulus::source_location location = modulus::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION())) -> std::expected<VALUE, modulus::error>;
template <typename MESSAGE> template <typename MESSAGE>
auto fail_void(std::int32_t code, MESSAGE&& message, modulus::source_location location = modulus::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION())) -> std::expected<void, modulus::error>; auto fail_void(std::int32_t code, MESSAGE &&message, modulus::source_location location = modulus::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION())) -> std::expected<void, modulus::error>;
} }
@@ -37,17 +38,17 @@ auto ligo::detail::fail(modulus::error error) -> std::expected<VALUE, modulus::e
} }
template <typename VALUE> template <typename VALUE>
auto ligo::detail::fail(std::expected<void, modulus::error> const& result) -> std::expected<VALUE, modulus::error> { auto ligo::detail::fail(std::expected<void, modulus::error> const &result) -> std::expected<VALUE, modulus::error> {
assert(!result); assert(!result);
return ligo::detail::fail<VALUE>(result.error()); return ligo::detail::fail<VALUE>(result.error());
} }
template <typename VALUE, typename MESSAGE> template <typename VALUE, typename MESSAGE>
auto ligo::detail::fail(std::int32_t code, MESSAGE&& message, modulus::source_location location) -> std::expected<VALUE, modulus::error> { auto ligo::detail::fail(std::int32_t code, MESSAGE &&message, modulus::source_location location) -> std::expected<VALUE, modulus::error> {
return modulus::fail<VALUE>(ligo::detail::make_error(code, std::string{std::forward<MESSAGE>(message)}, location)); return modulus::fail<VALUE>(ligo::detail::make_error(code, std::string{std::forward<MESSAGE>(message)}, location));
} }
template <typename MESSAGE> template <typename MESSAGE>
auto ligo::detail::fail_void(std::int32_t code, MESSAGE&& message, modulus::source_location location) -> std::expected<void, modulus::error> { auto ligo::detail::fail_void(std::int32_t code, MESSAGE &&message, modulus::source_location location) -> std::expected<void, modulus::error> {
return modulus::fail<void>(ligo::detail::make_error(code, std::string{std::forward<MESSAGE>(message)}, location)); return modulus::fail<void>(ligo::detail::make_error(code, std::string{std::forward<MESSAGE>(message)}, location));
} }
+16 -15
View File
@@ -25,17 +25,18 @@
#include <ligo/detail/errors.hpp> #include <ligo/detail/errors.hpp>
#include <ligo/detail/transport.hpp> #include <ligo/detail/transport.hpp>
namespace ligo::detail { namespace ligo::detail {
auto cancel_session_stream(boost::beast::tcp_stream& stream) -> void; auto cancel_session_stream(boost::beast::tcp_stream &stream) -> void;
template <typename NEXT_LAYER> template <typename NEXT_LAYER>
auto cancel_session_stream(boost::asio::ssl::stream<NEXT_LAYER>& stream) -> void; auto cancel_session_stream(boost::asio::ssl::stream<NEXT_LAYER> &stream) -> void;
template <typename S> template <typename S>
struct session_registry { struct session_registry {
static auto cancel_waiters(std::vector<std::shared_ptr<boost::asio::steady_timer>> const& live) -> void; static auto cancel_waiters(std::vector<std::shared_ptr<boost::asio::steady_timer>> const &live) -> void;
auto add(std::shared_ptr<S> const& stream) -> void; auto add(std::shared_ptr<S> const &stream) -> void;
auto remove(std::shared_ptr<S> const& stream) -> void; auto remove(std::shared_ptr<S> const &stream) -> void;
auto cancel_all() -> void; auto cancel_all() -> void;
auto empty() const -> std::bool_t; auto empty() const -> std::bool_t;
auto wait_empty(std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>>; auto wait_empty(std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
@@ -57,34 +58,34 @@ struct session_guard {
template <typename S> template <typename S>
auto stop_accepting_server(std::shared_ptr<S> state) -> void; auto stop_accepting_server(std::shared_ptr<S> state) -> void;
template <typename S> template <typename S>
auto stop_accepting_server_now(std::shared_ptr<S> const& state) -> void; auto stop_accepting_server_now(std::shared_ptr<S> const &state) -> void;
template <typename S> template <typename S>
auto async_stop_accepting_server(std::shared_ptr<S> state, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>>; auto async_stop_accepting_server(std::shared_ptr<S> state, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
} }
template <typename NEXT_LAYER> template <typename NEXT_LAYER>
auto ligo::detail::cancel_session_stream(boost::asio::ssl::stream<NEXT_LAYER>& stream) -> void { auto ligo::detail::cancel_session_stream(boost::asio::ssl::stream<NEXT_LAYER> &stream) -> void {
auto ignored = boost::system::error_code{}; auto ignored = boost::system::error_code{};
stream.lowest_layer().cancel(ignored); stream.lowest_layer().cancel(ignored);
stream.lowest_layer().close(ignored); stream.lowest_layer().close(ignored);
} }
template <typename S> template <typename S>
auto ligo::detail::session_registry<S>::cancel_waiters(std::vector<std::shared_ptr<boost::asio::steady_timer>> const& live) -> void { auto ligo::detail::session_registry<S>::cancel_waiters(std::vector<std::shared_ptr<boost::asio::steady_timer>> const &live) -> void {
for (auto& waiter : live) { for (auto &waiter : live) {
waiter->cancel(); waiter->cancel();
} }
} }
template <typename S> template <typename S>
auto ligo::detail::session_registry<S>::add(std::shared_ptr<S> const& stream) -> void { auto ligo::detail::session_registry<S>::add(std::shared_ptr<S> const &stream) -> void {
auto lock = std::scoped_lock{mutex}; auto lock = std::scoped_lock{mutex};
sessions.insert(stream); sessions.insert(stream);
} }
template <typename S> template <typename S>
auto ligo::detail::session_registry<S>::remove(std::shared_ptr<S> const& stream) -> void { auto ligo::detail::session_registry<S>::remove(std::shared_ptr<S> const &stream) -> void {
auto live = std::vector<std::shared_ptr<boost::asio::steady_timer>>{}; auto live = std::vector<std::shared_ptr<boost::asio::steady_timer>>{};
{ {
auto lock = std::scoped_lock{mutex}; auto lock = std::scoped_lock{mutex};
@@ -101,7 +102,7 @@ auto ligo::detail::session_registry<S>::cancel_all() -> void {
auto lock = std::scoped_lock{mutex}; auto lock = std::scoped_lock{mutex};
copy.assign(sessions.begin(), sessions.end()); copy.assign(sessions.begin(), sessions.end());
} }
for (auto& stream : copy) { for (auto &stream : copy) {
boost::asio::post(stream->get_executor(), [stream] { boost::asio::post(stream->get_executor(), [stream] {
ligo::detail::cancel_session_stream(*stream); ligo::detail::cancel_session_stream(*stream);
}); });
@@ -132,7 +133,7 @@ auto ligo::detail::session_registry<S>::wait_empty(std::chrono::milliseconds tim
waiters.push_back(notify_timer); waiters.push_back(notify_timer);
} }
deadline_timer->expires_after(timeout); deadline_timer->expires_after(timeout);
deadline_timer->async_wait([notify_timer, timed_out](boost::system::error_code const& error) { deadline_timer->async_wait([notify_timer, timed_out](boost::system::error_code const &error) {
if (!error) { if (!error) {
timed_out->store(true, std::memory_order_relaxed); timed_out->store(true, std::memory_order_relaxed);
notify_timer->cancel(); notify_timer->cancel();
@@ -168,7 +169,7 @@ template <typename S>
auto ligo::detail::session_registry<S>::live_waiters_locked() -> std::vector<std::shared_ptr<boost::asio::steady_timer>> { auto ligo::detail::session_registry<S>::live_waiters_locked() -> std::vector<std::shared_ptr<boost::asio::steady_timer>> {
auto live = std::vector<std::shared_ptr<boost::asio::steady_timer>>{}; auto live = std::vector<std::shared_ptr<boost::asio::steady_timer>>{};
auto retained = std::vector<std::weak_ptr<boost::asio::steady_timer>>{}; auto retained = std::vector<std::weak_ptr<boost::asio::steady_timer>>{};
for (auto& waiter : waiters) { for (auto &waiter : waiters) {
if (auto locked = waiter.lock()) { if (auto locked = waiter.lock()) {
live.push_back(locked); live.push_back(locked);
retained.push_back(locked); retained.push_back(locked);
@@ -209,7 +210,7 @@ auto ligo::detail::stop_accepting_server(std::shared_ptr<S> state) -> void {
} }
template <typename S> template <typename S>
auto ligo::detail::stop_accepting_server_now(std::shared_ptr<S> const& state) -> void { auto ligo::detail::stop_accepting_server_now(std::shared_ptr<S> const &state) -> void {
auto lock = std::scoped_lock{state->lifecycle_mutex}; auto lock = std::scoped_lock{state->lifecycle_mutex};
if (state->stopped.exchange(true)) { if (state->stopped.exchange(true)) {
return; return;
+21 -18
View File
@@ -7,16 +7,17 @@
#include <modulus.hpp> #include <modulus.hpp>
namespace ligo::detail { namespace ligo::detail {
class state_handle { class state_handle {
public: public:
state_handle() = default; state_handle() = default;
state_handle(ligo::detail::state_handle const&) = delete; state_handle(ligo::detail::state_handle const &) = delete;
state_handle(ligo::detail::state_handle&& other) noexcept; state_handle(ligo::detail::state_handle &&other) noexcept;
~state_handle() = default; ~state_handle() = default;
auto operator=(ligo::detail::state_handle const&) -> ligo::detail::state_handle& = delete; auto operator=(ligo::detail::state_handle const &) -> ligo::detail::state_handle & = delete;
auto operator=(ligo::detail::state_handle&& other) noexcept -> ligo::detail::state_handle&; auto operator=(ligo::detail::state_handle &&other) noexcept -> ligo::detail::state_handle &;
explicit operator std::bool_t() const noexcept; explicit operator std::bool_t() const noexcept;
template <typename S> template <typename S>
explicit state_handle(std::shared_ptr<S> state); explicit state_handle(std::shared_ptr<S> state);
@@ -29,27 +30,28 @@ private:
class unique_state { class unique_state {
public: public:
unique_state() noexcept = default; unique_state() noexcept = default;
unique_state(ligo::detail::unique_state const&) = delete; unique_state(ligo::detail::unique_state const &) = delete;
unique_state(ligo::detail::unique_state&&) noexcept = default; unique_state(ligo::detail::unique_state &&) noexcept = default;
~unique_state() = default; ~unique_state() = default;
auto operator=(ligo::detail::unique_state const&) -> ligo::detail::unique_state& = delete; auto operator=(ligo::detail::unique_state const &) -> ligo::detail::unique_state & = delete;
auto operator=(ligo::detail::unique_state&&) noexcept -> ligo::detail::unique_state& = default; auto operator=(ligo::detail::unique_state &&) noexcept -> ligo::detail::unique_state & = default;
explicit operator std::bool_t() const noexcept; explicit operator std::bool_t() const noexcept;
template <typename STATE> template <typename STATE>
explicit unique_state(std::unique_ptr<STATE> state); explicit unique_state(std::unique_ptr<STATE> state);
template <typename STATE> template <typename STATE>
auto as() const noexcept -> STATE*; auto as() const noexcept -> STATE *;
private: private:
static auto ignore_state(void* state) -> void; static auto ignore_state(void *state) -> void;
template <typename STATE> template <typename STATE>
static auto delete_state(void* state) -> void; static auto delete_state(void *state) -> void;
std::unique_ptr<void, std::function<void(void*)>> state_{nullptr, ignore_state}; std::unique_ptr<void, std::function<void(void *)>> state_{nullptr, ignore_state};
}; };
} }
template <typename STATE> template <typename STATE>
ligo::detail::state_handle::state_handle(std::shared_ptr<STATE> state) : state_{std::shared_ptr<void>{std::move(state)}} {} ligo::detail::state_handle::state_handle(std::shared_ptr<STATE> state)
: state_{std::shared_ptr<void>{std::move(state)}} {}
template <typename STATE> template <typename STATE>
auto ligo::detail::state_handle::as() const -> std::shared_ptr<STATE> { auto ligo::detail::state_handle::as() const -> std::shared_ptr<STATE> {
@@ -57,14 +59,15 @@ auto ligo::detail::state_handle::as() const -> std::shared_ptr<STATE> {
} }
template <typename STATE> template <typename STATE>
ligo::detail::unique_state::unique_state(std::unique_ptr<STATE> state) : state_{state.release(), delete_state<STATE>} {} ligo::detail::unique_state::unique_state(std::unique_ptr<STATE> state)
: state_{state.release(), delete_state<STATE>} {}
template <typename STATE> template <typename STATE>
auto ligo::detail::unique_state::as() const noexcept -> STATE* { auto ligo::detail::unique_state::as() const noexcept -> STATE * {
return static_cast<STATE*>(state_.get()); return static_cast<STATE *>(state_.get());
} }
template <typename STATE> template <typename STATE>
auto ligo::detail::unique_state::delete_state(void* state) -> void { auto ligo::detail::unique_state::delete_state(void *state) -> void {
delete static_cast<STATE*>(state); delete static_cast<STATE *>(state);
} }
+21 -20
View File
@@ -28,6 +28,7 @@
#include <ligo/detail/errors.hpp> #include <ligo/detail/errors.hpp>
#include <ligo/service.hpp> #include <ligo/service.hpp>
namespace ligo::detail { namespace ligo::detail {
struct endpoint_parts { struct endpoint_parts {
@@ -37,13 +38,13 @@ struct endpoint_parts {
auto status_name(std::int32_t code) -> std::string; auto status_name(std::int32_t code) -> std::string;
auto parse_status_code(std::string_view value) -> std::int32_t; auto parse_status_code(std::string_view value) -> std::int32_t;
auto network_failure(boost::system::error_code const& error) -> modulus::error; 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 operation_failure(boost::system::error_code const &error, std::string timeout_message) -> modulus::error;
auto invalid_argument(std::string message) -> modulus::error; auto invalid_argument(std::string message) -> modulus::error;
auto unavailable(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 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, modulus::error error) -> void;
auto report_error(std::function<void(modulus::error)> const& on_error, std::expected<void, modulus::error> status) -> 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 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 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 unwrap_response_envelope(boost::json::value value, std::string_view protocol_name) -> std::expected<boost::json::value, modulus::error>;
@@ -51,10 +52,10 @@ auto parse_port(std::string_view text) -> std::expected<std::uint16_t, modulus::
auto parse_endpoint_text(std::string_view text, std::uint16_t fallback_port) -> std::expected<ligo::detail::endpoint_parts, 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_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 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 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 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_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>>; 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 { struct stream_deadline {
std::shared_ptr<boost::asio::steady_timer> timer{}; std::shared_ptr<boost::asio::steady_timer> timer{};
@@ -63,29 +64,29 @@ struct stream_deadline {
std::shared_ptr<boost::asio::cancellation_signal> cancellation{}; 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::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; auto start_stream_deadline(boost::asio::ip::tcp::socket &socket, std::chrono::milliseconds timeout) -> ligo::detail::stream_deadline;
template <typename NEXT_LAYER> template <typename NEXT_LAYER>
auto start_stream_deadline(boost::asio::ssl::stream<NEXT_LAYER>& stream, std::chrono::milliseconds timeout) -> ligo::detail::stream_deadline; 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 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 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; 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> 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>>; 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> 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>>; 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> 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 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>()}; 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->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) { 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)) { if (!error && active->exchange(false, std::memory_order_acq_rel)) {
timed_out->store(true, std::memory_order_release); timed_out->store(true, std::memory_order_release);
cancellation->emit(boost::asio::cancellation_type::terminal); cancellation->emit(boost::asio::cancellation_type::terminal);
@@ -95,7 +96,7 @@ auto ligo::detail::start_stream_deadline(boost::asio::ssl::stream<NEXT_LAYER>& s
} }
template <typename NEXT_LAYER> 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 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 deadline = ligo::detail::start_stream_deadline(stream, timeout);
auto [error] = co_await stream.async_handshake(type, ligo::detail::deadline_token(deadline)); auto [error] = co_await stream.async_handshake(type, ligo::detail::deadline_token(deadline));
ligo::detail::cancel_stream_deadline(deadline); ligo::detail::cancel_stream_deadline(deadline);
@@ -106,7 +107,7 @@ auto ligo::detail::async_tls_handshake(boost::asio::ssl::stream<NEXT_LAYER>& str
} }
template <typename NEXT_LAYER> 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 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 deadline = ligo::detail::start_stream_deadline(stream, timeout);
auto [error] = co_await stream.async_shutdown(ligo::detail::deadline_token(deadline)); auto [error] = co_await stream.async_shutdown(ligo::detail::deadline_token(deadline));
ligo::detail::cancel_stream_deadline(deadline); ligo::detail::cancel_stream_deadline(deadline);
+10 -10
View File
@@ -55,7 +55,7 @@ struct server_options {
class client { class client {
public: public:
explicit client(ligo::http::endpoint endpoint, ligo::http::client_options options = {}); explicit client(ligo::http::endpoint endpoint, ligo::http::client_options options = {});
auto call(ligo::http::request const& request) const -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>>; auto call(ligo::http::request const &request) const -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>>;
private: private:
ligo::http::endpoint endpoint_{}; ligo::http::endpoint endpoint_{};
ligo::http::client_options options_{}; ligo::http::client_options options_{};
@@ -63,13 +63,13 @@ private:
class server { class server {
public: public:
server(boost::asio::io_context& io, ligo::http::endpoint endpoint, ligo::service_router router, ligo::http::server_options options = {}); server(boost::asio::io_context &io, ligo::http::endpoint endpoint, ligo::service_router router, ligo::http::server_options options = {});
server(boost::asio::any_io_executor executor, ligo::http::endpoint endpoint, ligo::service_router router, ligo::http::server_options options = {}); server(boost::asio::any_io_executor executor, ligo::http::endpoint endpoint, ligo::service_router router, ligo::http::server_options options = {});
server(ligo::http::server const&) = delete; server(ligo::http::server const &) = delete;
server(ligo::http::server&&) noexcept = default; server(ligo::http::server &&) noexcept = default;
~server(); ~server();
auto operator=(ligo::http::server const&) -> ligo::http::server& = delete; auto operator=(ligo::http::server const &) -> ligo::http::server & = delete;
auto operator=(ligo::http::server&&) noexcept -> ligo::http::server&; auto operator=(ligo::http::server &&) noexcept -> ligo::http::server &;
auto start() -> std::expected<void, modulus::error>; auto start() -> std::expected<void, modulus::error>;
auto stop() -> void; auto stop() -> void;
auto async_stop(std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<void, modulus::error>>; auto async_stop(std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
@@ -78,9 +78,9 @@ private:
ligo::detail::state_handle state_{}; ligo::detail::state_handle state_{};
}; };
auto client_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::http::endpoint, modulus::error>; auto client_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::http::endpoint, modulus::error>;
auto server_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::http::endpoint, modulus::error>; auto server_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::http::endpoint, modulus::error>;
auto client_options_from_config(ligo::endpoint_config const& config) -> ligo::http::client_options; auto client_options_from_config(ligo::endpoint_config const &config) -> ligo::http::client_options;
auto server_options_from_config(ligo::endpoint_config const& config) -> ligo::http::server_options; auto server_options_from_config(ligo::endpoint_config const &config) -> ligo::http::server_options;
} }
+10 -10
View File
@@ -25,18 +25,18 @@ class byte_stream_state;
class byte_stream { class byte_stream {
public: public:
byte_stream() = default; byte_stream() = default;
byte_stream(ligo::net::byte_stream const&) = delete; byte_stream(ligo::net::byte_stream const &) = delete;
byte_stream(ligo::net::byte_stream&&) noexcept = default; byte_stream(ligo::net::byte_stream &&) noexcept = default;
~byte_stream(); ~byte_stream();
auto operator=(ligo::net::byte_stream const&) -> ligo::net::byte_stream& = delete; auto operator=(ligo::net::byte_stream const &) -> ligo::net::byte_stream & = delete;
auto operator=(ligo::net::byte_stream&&) noexcept -> ligo::net::byte_stream&; auto operator=(ligo::net::byte_stream &&) noexcept -> ligo::net::byte_stream &;
static auto from_socket(ligo::tcp::socket_handle socket) -> std::expected<ligo::net::byte_stream, modulus::error>; static auto from_socket(ligo::tcp::socket_handle socket) -> std::expected<ligo::net::byte_stream, modulus::error>;
static auto from_tls_socket(ligo::tcp::tls_socket socket) -> std::expected<ligo::net::byte_stream, modulus::error>; static auto from_tls_socket(ligo::tcp::tls_socket socket) -> std::expected<ligo::net::byte_stream, modulus::error>;
auto async_read_some(boost::asio::mutable_buffer buffer, std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>>; auto async_read_some(boost::asio::mutable_buffer buffer, std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>>;
auto async_write_some(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>>; auto async_write_some(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>>;
auto async_write_all(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<void, modulus::error>>; auto async_write_all(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
auto async_shutdown(std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<void, modulus::error>>; auto async_shutdown(std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
auto close(boost::system::error_code& error) -> void; auto close(boost::system::error_code &error) -> void;
auto is_open() const -> std::bool_t; auto is_open() const -> std::bool_t;
private: private:
explicit byte_stream(std::shared_ptr<ligo::net::byte_stream_state> state); explicit byte_stream(std::shared_ptr<ligo::net::byte_stream_state> state);
@@ -46,11 +46,11 @@ private:
class listener { class listener {
public: public:
listener() = default; listener() = default;
listener(ligo::net::listener const&) = delete; listener(ligo::net::listener const &) = delete;
listener(ligo::net::listener&&) noexcept = default; listener(ligo::net::listener &&) noexcept = default;
~listener(); ~listener();
auto operator=(ligo::net::listener const&) -> ligo::net::listener& = delete; auto operator=(ligo::net::listener const &) -> ligo::net::listener & = delete;
auto operator=(ligo::net::listener&&) noexcept -> ligo::net::listener&; auto operator=(ligo::net::listener &&) noexcept -> ligo::net::listener &;
static auto bind(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint) -> std::expected<ligo::net::listener, modulus::error>; static auto bind(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint) -> std::expected<ligo::net::listener, modulus::error>;
auto selected_port() const -> std::uint16_t; auto selected_port() const -> std::uint16_t;
auto async_accept() -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>; auto async_accept() -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>;
@@ -64,7 +64,7 @@ private:
auto connect_tcp(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>; auto connect_tcp(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>;
auto connect(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>; auto connect(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>;
auto connect(boost::asio::any_io_executor executor, ligo::endpoint_config const& config) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>; auto connect(boost::asio::any_io_executor executor, ligo::endpoint_config const &config) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>;
auto connect_tls_configured(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}, ligo::security_config security = {.mode = ligo::security_mode::TLS}) -> boost::asio::awaitable<std::expected<ligo::tcp::tls_socket, modulus::error>>; auto connect_tls_configured(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}, ligo::security_config security = {.mode = ligo::security_mode::TLS}) -> boost::asio::awaitable<std::expected<ligo::tcp::tls_socket, modulus::error>>;
auto connect_tls_handshaken(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}, ligo::security_config security = {.mode = ligo::security_mode::TLS}) -> boost::asio::awaitable<std::expected<ligo::tcp::tls_socket, modulus::error>>; auto connect_tls_handshaken(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}, ligo::security_config security = {.mode = ligo::security_mode::TLS}) -> boost::asio::awaitable<std::expected<ligo::tcp::tls_socket, modulus::error>>;
+7 -6
View File
@@ -11,6 +11,7 @@
#include <modulus.hpp> #include <modulus.hpp>
namespace ligo { namespace ligo {
enum class security_mode { enum class security_mode {
@@ -35,12 +36,12 @@ struct security_config {
std::shared_ptr<boost::asio::ssl::context> tls_context{}; std::shared_ptr<boost::asio::ssl::context> tls_context{};
}; };
auto make_tls_client_context(ligo::security_config const& config) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error>; auto make_tls_client_context(ligo::security_config const &config) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error>;
auto make_tls_server_context(ligo::security_config const& config) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error>; auto make_tls_server_context(ligo::security_config const &config) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error>;
auto apply_tls_client_verification(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& stream, std::string const& host, ligo::security_config const& config) -> std::expected<void, modulus::error>; auto apply_tls_client_verification(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &stream, std::string const &host, ligo::security_config const &config) -> std::expected<void, modulus::error>;
auto configure_tls_client_stream(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& stream, std::string const& host, ligo::security_config const& config) -> std::expected<void, modulus::error>; auto configure_tls_client_stream(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &stream, std::string const &host, ligo::security_config const &config) -> std::expected<void, modulus::error>;
auto validate_tls_server_config(std::string_view protocol, ligo::security_config const& config) -> std::expected<void, modulus::error>; auto validate_tls_server_config(std::string_view protocol, ligo::security_config const &config) -> std::expected<void, modulus::error>;
auto validate_tls_server_context(std::shared_ptr<boost::asio::ssl::context> const& context) -> std::expected<void, modulus::error>; auto validate_tls_server_context(std::shared_ptr<boost::asio::ssl::context> const &context) -> std::expected<void, modulus::error>;
auto unsupported_security(std::string_view protocol, ligo::security_mode mode) -> modulus::error; auto unsupported_security(std::string_view protocol, ligo::security_mode mode) -> modulus::error;
} }
+5 -4
View File
@@ -13,6 +13,7 @@
#include <modulus.hpp> #include <modulus.hpp>
namespace ligo { namespace ligo {
struct request_context { struct request_context {
@@ -29,11 +30,11 @@ struct service_response {
class service_router { class service_router {
public: public:
service_router() = default; service_router() = default;
service_router(ligo::service_router const& other); service_router(ligo::service_router const &other);
service_router(ligo::service_router&& other); service_router(ligo::service_router &&other);
~service_router() = default; ~service_router() = default;
auto operator=(ligo::service_router const& other) -> ligo::service_router&; auto operator=(ligo::service_router const &other) -> ligo::service_router &;
auto operator=(ligo::service_router&& other) -> ligo::service_router&; auto operator=(ligo::service_router &&other) -> ligo::service_router &;
auto add_route(std::string method, std::string route, std::function<boost::asio::awaitable<std::expected<boost::json::value, modulus::error>>(ligo::request_context, boost::json::value)> handler) -> void; auto add_route(std::string method, std::string route, std::function<boost::asio::awaitable<std::expected<boost::json::value, modulus::error>>(ligo::request_context, boost::json::value)> handler) -> void;
auto add_response_route(std::string method, std::string route, std::function<boost::asio::awaitable<std::expected<ligo::service_response, modulus::error>>(ligo::request_context, boost::json::value)> handler) -> void; auto add_response_route(std::string method, std::string route, std::function<boost::asio::awaitable<std::expected<ligo::service_response, modulus::error>>(ligo::request_context, boost::json::value)> handler) -> void;
auto call(ligo::request_context context, boost::json::value payload) const -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>>; auto call(ligo::request_context context, boost::json::value payload) const -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>>;
+23 -22
View File
@@ -33,16 +33,16 @@ struct endpoint {
struct socket_handle { struct socket_handle {
socket_handle() = default; socket_handle() = default;
socket_handle(ligo::tcp::socket_handle const&) = delete; socket_handle(ligo::tcp::socket_handle const &) = delete;
socket_handle(ligo::tcp::socket_handle&&) noexcept = default; socket_handle(ligo::tcp::socket_handle &&) noexcept = default;
explicit socket_handle(boost::asio::ip::tcp::socket socket); explicit socket_handle(boost::asio::ip::tcp::socket socket);
explicit socket_handle(std::shared_ptr<boost::asio::ip::tcp::socket> socket); explicit socket_handle(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
auto operator=(ligo::tcp::socket_handle const&) -> ligo::tcp::socket_handle& = delete; auto operator=(ligo::tcp::socket_handle const &) -> ligo::tcp::socket_handle & = delete;
auto operator=(ligo::tcp::socket_handle&&) noexcept -> ligo::tcp::socket_handle& = default; auto operator=(ligo::tcp::socket_handle &&) noexcept -> ligo::tcp::socket_handle & = default;
auto socket() -> std::expected<std::reference_wrapper<boost::asio::ip::tcp::socket>, modulus::error>; auto socket() -> std::expected<std::reference_wrapper<boost::asio::ip::tcp::socket>, modulus::error>;
auto socket() const -> std::expected<std::reference_wrapper<boost::asio::ip::tcp::socket const>, modulus::error>; auto socket() const -> std::expected<std::reference_wrapper<boost::asio::ip::tcp::socket const>, modulus::error>;
auto is_open() const -> std::bool_t; auto is_open() const -> std::bool_t;
auto close(boost::system::error_code& error) -> void; auto close(boost::system::error_code &error) -> void;
auto release_socket() -> std::expected<boost::asio::ip::tcp::socket, modulus::error>; auto release_socket() -> std::expected<boost::asio::ip::tcp::socket, modulus::error>;
private: private:
std::shared_ptr<boost::asio::ip::tcp::socket> socket_{}; std::shared_ptr<boost::asio::ip::tcp::socket> socket_{};
@@ -55,17 +55,17 @@ struct tls_socket {
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> stream; boost::asio::ssl::stream<boost::asio::ip::tcp::socket> stream;
}; };
tls_socket() = default; tls_socket() = default;
tls_socket(ligo::tcp::tls_socket const&) = delete; tls_socket(ligo::tcp::tls_socket const &) = delete;
tls_socket(ligo::tcp::tls_socket&&) noexcept = default; tls_socket(ligo::tcp::tls_socket &&) noexcept = default;
tls_socket(std::shared_ptr<boost::asio::ssl::context> context, boost::asio::ip::tcp::socket socket); tls_socket(std::shared_ptr<boost::asio::ssl::context> context, boost::asio::ip::tcp::socket socket);
auto operator=(ligo::tcp::tls_socket const&) -> ligo::tcp::tls_socket& = delete; auto operator=(ligo::tcp::tls_socket const &) -> ligo::tcp::tls_socket & = delete;
auto operator=(ligo::tcp::tls_socket&&) noexcept -> ligo::tcp::tls_socket& = default; auto operator=(ligo::tcp::tls_socket &&) noexcept -> ligo::tcp::tls_socket & = default;
auto stream() -> std::expected<std::reference_wrapper<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>, modulus::error>; auto stream() -> std::expected<std::reference_wrapper<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>, modulus::error>;
auto stream() const -> std::expected<std::reference_wrapper<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> const>, modulus::error>; auto stream() const -> std::expected<std::reference_wrapper<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> const>, modulus::error>;
auto context() -> std::expected<std::reference_wrapper<boost::asio::ssl::context>, modulus::error>; auto context() -> std::expected<std::reference_wrapper<boost::asio::ssl::context>, modulus::error>;
auto context() const -> std::expected<std::reference_wrapper<boost::asio::ssl::context const>, modulus::error>; auto context() const -> std::expected<std::reference_wrapper<boost::asio::ssl::context const>, modulus::error>;
auto is_open() const -> std::bool_t; auto is_open() const -> std::bool_t;
auto close(boost::system::error_code& error) -> void; auto close(boost::system::error_code &error) -> void;
auto release_stream() -> std::expected<ligo::tcp::tls_socket::released_stream, modulus::error>; auto release_stream() -> std::expected<ligo::tcp::tls_socket::released_stream, modulus::error>;
private: private:
std::shared_ptr<boost::asio::ssl::context> context_{}; std::shared_ptr<boost::asio::ssl::context> context_{};
@@ -91,7 +91,7 @@ struct server_options {
class client { class client {
public: public:
explicit client(ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}); explicit client(ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {});
auto call(ligo::tcp::request const& request) const -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>>; auto call(ligo::tcp::request const &request) const -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>>;
private: private:
ligo::tcp::endpoint endpoint_{}; ligo::tcp::endpoint endpoint_{};
ligo::tcp::client_options options_{}; ligo::tcp::client_options options_{};
@@ -99,13 +99,13 @@ private:
class server { class server {
public: public:
server(boost::asio::io_context& io, ligo::tcp::endpoint endpoint, ligo::service_router router, ligo::tcp::server_options options = {}); server(boost::asio::io_context &io, ligo::tcp::endpoint endpoint, ligo::service_router router, ligo::tcp::server_options options = {});
server(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::service_router router, ligo::tcp::server_options options = {}); server(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::service_router router, ligo::tcp::server_options options = {});
server(ligo::tcp::server const&) = delete; server(ligo::tcp::server const &) = delete;
server(ligo::tcp::server&&) noexcept = default; server(ligo::tcp::server &&) noexcept = default;
~server(); ~server();
auto operator=(ligo::tcp::server const&) -> ligo::tcp::server& = delete; auto operator=(ligo::tcp::server const &) -> ligo::tcp::server & = delete;
auto operator=(ligo::tcp::server&&) noexcept -> ligo::tcp::server&; auto operator=(ligo::tcp::server &&) noexcept -> ligo::tcp::server &;
auto start() -> std::expected<void, modulus::error>; auto start() -> std::expected<void, modulus::error>;
auto stop() -> void; auto stop() -> void;
auto async_stop(std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<void, modulus::error>>; auto async_stop(std::chrono::milliseconds timeout = std::chrono::milliseconds{5000}) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
@@ -114,15 +114,16 @@ private:
ligo::detail::state_handle state_{}; ligo::detail::state_handle state_{};
}; };
auto client_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::tcp::endpoint, modulus::error>; auto client_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::tcp::endpoint, modulus::error>;
auto server_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::tcp::endpoint, modulus::error>; auto server_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::tcp::endpoint, modulus::error>;
auto client_options_from_config(ligo::endpoint_config const& config) -> ligo::tcp::client_options; auto client_options_from_config(ligo::endpoint_config const &config) -> ligo::tcp::client_options;
auto server_options_from_config(ligo::endpoint_config const& config) -> ligo::tcp::server_options; auto server_options_from_config(ligo::endpoint_config const &config) -> ligo::tcp::server_options;
auto connect(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>; auto connect(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>;
auto connect(boost::asio::any_io_executor executor, ligo::endpoint_config const& config) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>; auto connect(boost::asio::any_io_executor executor, ligo::endpoint_config const &config) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>>;
auto connect_tls_configured(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}, ligo::security_config security = {.mode = ligo::security_mode::TLS}) -> boost::asio::awaitable<std::expected<ligo::tcp::tls_socket, modulus::error>>; auto connect_tls_configured(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}, ligo::security_config security = {.mode = ligo::security_mode::TLS}) -> boost::asio::awaitable<std::expected<ligo::tcp::tls_socket, modulus::error>>;
auto connect_tls_handshaken(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}, ligo::security_config security = {.mode = ligo::security_mode::TLS}) -> boost::asio::awaitable<std::expected<ligo::tcp::tls_socket, modulus::error>>; auto connect_tls_handshaken(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options = {}, ligo::security_config security = {.mode = ligo::security_mode::TLS}) -> boost::asio::awaitable<std::expected<ligo::tcp::tls_socket, modulus::error>>;
inline tls_socket::released_stream::released_stream(std::shared_ptr<boost::asio::ssl::context> context, boost::asio::ssl::stream<boost::asio::ip::tcp::socket> stream) : context{std::move(context)}, stream{std::move(stream)} {} inline tls_socket::released_stream::released_stream(std::shared_ptr<boost::asio::ssl::context> context, boost::asio::ssl::stream<boost::asio::ip::tcp::socket> stream)
: context{std::move(context)}, stream{std::move(stream)} {}
} }
+15 -15
View File
@@ -42,15 +42,15 @@ struct server_options {
class client { class client {
public: public:
client(ligo::udp::client const&) = delete; client(ligo::udp::client const &) = delete;
client(ligo::udp::client&&) noexcept = default; client(ligo::udp::client &&) noexcept = default;
~client(); ~client();
auto operator=(ligo::udp::client const&) -> ligo::udp::client& = delete; auto operator=(ligo::udp::client const &) -> ligo::udp::client & = delete;
auto operator=(ligo::udp::client&&) noexcept -> ligo::udp::client&; auto operator=(ligo::udp::client &&) noexcept -> ligo::udp::client &;
static auto open(boost::asio::any_io_executor executor, boost::asio::ip::udp::endpoint peer, ligo::udp::client_options options = {}) -> std::expected<ligo::udp::client, modulus::error>; static auto open(boost::asio::any_io_executor executor, boost::asio::ip::udp::endpoint peer, ligo::udp::client_options options = {}) -> std::expected<ligo::udp::client, modulus::error>;
static auto open(boost::asio::io_context& io, boost::asio::ip::udp::endpoint peer, ligo::udp::client_options options = {}) -> std::expected<ligo::udp::client, modulus::error>; static auto open(boost::asio::io_context &io, boost::asio::ip::udp::endpoint peer, ligo::udp::client_options options = {}) -> std::expected<ligo::udp::client, modulus::error>;
static auto bind(boost::asio::any_io_executor executor, ligo::udp::endpoint endpoint, ligo::udp::client_options options = {}) -> std::expected<ligo::udp::client, modulus::error>; static auto bind(boost::asio::any_io_executor executor, ligo::udp::endpoint endpoint, ligo::udp::client_options options = {}) -> std::expected<ligo::udp::client, modulus::error>;
static auto bind(boost::asio::io_context& io, ligo::udp::endpoint endpoint, ligo::udp::client_options options = {}) -> std::expected<ligo::udp::client, modulus::error>; static auto bind(boost::asio::io_context &io, ligo::udp::endpoint endpoint, ligo::udp::client_options options = {}) -> std::expected<ligo::udp::client, modulus::error>;
static auto adopt(std::unique_ptr<boost::asio::ip::udp::socket> socket, boost::asio::ip::udp::endpoint peer, ligo::udp::client_options options = {}) -> std::expected<ligo::udp::client, modulus::error>; static auto adopt(std::unique_ptr<boost::asio::ip::udp::socket> socket, boost::asio::ip::udp::endpoint peer, ligo::udp::client_options options = {}) -> std::expected<ligo::udp::client, modulus::error>;
auto send(boost::json::value payload) -> boost::asio::awaitable<std::expected<void, modulus::error>>; auto send(boost::json::value payload) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
auto send(ligo::udp::message message) -> boost::asio::awaitable<std::expected<void, modulus::error>>; auto send(ligo::udp::message message) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
@@ -65,13 +65,13 @@ private:
class server { class server {
public: public:
server(ligo::udp::server const&) = delete; server(ligo::udp::server const &) = delete;
server(ligo::udp::server&&) noexcept = default; server(ligo::udp::server &&) noexcept = default;
~server(); ~server();
auto operator=(ligo::udp::server const&) -> ligo::udp::server& = delete; auto operator=(ligo::udp::server const &) -> ligo::udp::server & = delete;
auto operator=(ligo::udp::server&&) noexcept -> ligo::udp::server&; auto operator=(ligo::udp::server &&) noexcept -> ligo::udp::server &;
static auto bind(boost::asio::any_io_executor executor, ligo::udp::endpoint endpoint, ligo::udp::server_options options = {}) -> std::expected<ligo::udp::server, modulus::error>; static auto bind(boost::asio::any_io_executor executor, ligo::udp::endpoint endpoint, ligo::udp::server_options options = {}) -> std::expected<ligo::udp::server, modulus::error>;
static auto bind(boost::asio::io_context& io, ligo::udp::endpoint endpoint, ligo::udp::server_options options = {}) -> std::expected<ligo::udp::server, modulus::error>; static auto bind(boost::asio::io_context &io, ligo::udp::endpoint endpoint, ligo::udp::server_options options = {}) -> std::expected<ligo::udp::server, modulus::error>;
static auto adopt(std::unique_ptr<boost::asio::ip::udp::socket> socket, boost::asio::ip::udp::endpoint peer, ligo::udp::server_options options = {}) -> std::expected<ligo::udp::server, modulus::error>; static auto adopt(std::unique_ptr<boost::asio::ip::udp::socket> socket, boost::asio::ip::udp::endpoint peer, ligo::udp::server_options options = {}) -> std::expected<ligo::udp::server, modulus::error>;
auto send(boost::json::value payload) -> boost::asio::awaitable<std::expected<void, modulus::error>>; auto send(boost::json::value payload) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
auto send(ligo::udp::message message) -> boost::asio::awaitable<std::expected<void, modulus::error>>; auto send(ligo::udp::message message) -> boost::asio::awaitable<std::expected<void, modulus::error>>;
@@ -84,9 +84,9 @@ private:
ligo::detail::state_handle state_{}; ligo::detail::state_handle state_{};
}; };
auto client_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::udp::endpoint, modulus::error>; auto client_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::udp::endpoint, modulus::error>;
auto server_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::udp::endpoint, modulus::error>; auto server_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::udp::endpoint, modulus::error>;
auto client_options_from_config(ligo::endpoint_config const& config) -> ligo::udp::client_options; auto client_options_from_config(ligo::endpoint_config const &config) -> ligo::udp::client_options;
auto server_options_from_config(ligo::endpoint_config const& config) -> ligo::udp::server_options; auto server_options_from_config(ligo::endpoint_config const &config) -> ligo::udp::server_options;
} }
+6 -2
View File
@@ -16,6 +16,7 @@ COMMANDS = (
"package", "package",
"lsp", "lsp",
"clangd", "clangd",
"format",
) )
COMMON_DIR = pathlib.Path("build") / "build_common" COMMON_DIR = pathlib.Path("build") / "build_common"
TOOLS_DIR = pathlib.Path("build") / "build_tools" TOOLS_DIR = pathlib.Path("build") / "build_tools"
@@ -60,6 +61,9 @@ def runtime_ready(root):
and (root / TOOLS_DIR / "clangd.py").is_file() and (root / TOOLS_DIR / "clangd.py").is_file()
and (root / RUNTIME_DIR / "modulus" / "cli.py").is_file() and (root / RUNTIME_DIR / "modulus" / "cli.py").is_file()
and (root / RUNTIME_DIR / "modulus" / "export.py").is_file() and (root / RUNTIME_DIR / "modulus" / "export.py").is_file()
and (root / TOOLS_DIR / "format.py").is_file()
and (root / RUNTIME_DIR / "modulus" / "formatting.py").is_file()
and (root / RUNTIME_DIR / "modulus" / "format_layout.py").is_file()
) )
def optional_credentials(): def optional_credentials():
@@ -340,9 +344,9 @@ def ensure_runtime(root):
def delegate(command, argv): def delegate(command, argv):
root = repo_root() root = repo_root()
if command == "clangd": if command in {"clangd", "format"}:
if not runtime_ready(root): if not runtime_ready(root):
print("Modulus clangd tooling is missing; run ./modulus.py configure first", file=sys.stderr) print(f"Modulus {command} tooling is missing; run ./modulus.py configure first", file=sys.stderr)
return 1 return 1
else: else:
ensure_runtime(root) ensure_runtime(root)
+79 -73
View File
@@ -17,13 +17,13 @@
#include <ligo/detail/errors.hpp> #include <ligo/detail/errors.hpp>
#include <ligo/security.hpp> #include <ligo/security.hpp>
namespace { namespace {
constexpr auto json_options = modulus::json_config::options{ constexpr auto json_options = modulus::json_config::options{
.category = "ligo", .category = "ligo",
.invalid_argument_code = ligo::detail::error_invalid_argument, .invalid_argument_code = ligo::detail::error_invalid_argument,
.not_found_code = ligo::detail::error_not_found .not_found_code = ligo::detail::error_not_found};
};
auto protocol_name(ligo::protocol value) -> std::string_view { auto protocol_name(ligo::protocol value) -> std::string_view {
switch (value) { switch (value) {
@@ -61,54 +61,57 @@ auto verify_mode_name(ligo::verify_mode value) -> std::string_view {
return "defaulted"; return "defaulted";
} }
auto parse_security_config(boost::json::value const& value) -> std::expected<ligo::security_config, modulus::error> { auto parse_security_config(boost::json::value const &value) -> std::expected<ligo::security_config, modulus::error> {
auto object = modulus::json_config::require_object(value, "endpoint security", json_options); auto object = modulus::json_config::require_object(value, "endpoint security", json_options);
if (!object) { if (!object) {
return ligo::detail::fail<ligo::security_config>(object.error()); return ligo::detail::fail<ligo::security_config>(object.error());
} }
auto unknown = modulus::json_config::reject_unknown_fields(**object, { auto unknown = modulus::json_config::reject_unknown_fields(**object, {
"mode", "mode",
"verify", "verify",
"peer_name", "peer_name",
"ca_file", "ca_file",
"certificate_chain_file", "certificate_chain_file",
"private_key_file", "private_key_file",
}, "endpoint security", json_options); },
"endpoint security", json_options);
if (!unknown) { if (!unknown) {
return ligo::detail::fail<ligo::security_config>(unknown.error()); return ligo::detail::fail<ligo::security_config>(unknown.error());
} }
auto config = ligo::security_config{}; auto config = ligo::security_config{};
auto mode = modulus::json_config::optional_enum_field(**object, "mode", config.mode, { auto mode = modulus::json_config::optional_enum_field(**object, "mode", config.mode, {
{ {
.name = "none", .name = "none",
.value = ligo::security_mode::NONE, .value = ligo::security_mode::NONE,
}, },
{ {
.name = "tls", .name = "tls",
.value = ligo::security_mode::TLS, .value = ligo::security_mode::TLS,
}, },
{ {
.name = "dtls", .name = "dtls",
.value = ligo::security_mode::DTLS, .value = ligo::security_mode::DTLS,
}, },
}, "endpoint security mode must be none, tls, or dtls", json_options); },
"endpoint security mode must be none, tls, or dtls", json_options);
if (!mode) { if (!mode) {
return ligo::detail::fail<ligo::security_config>(mode.error()); return ligo::detail::fail<ligo::security_config>(mode.error());
} }
auto verify = modulus::json_config::optional_enum_field(**object, "verify", config.verify, { auto verify = modulus::json_config::optional_enum_field(**object, "verify", config.verify, {
{ {
.name = "defaulted", .name = "defaulted",
.value = ligo::verify_mode::DEFAULTED, .value = ligo::verify_mode::DEFAULTED,
}, },
{ {
.name = "none", .name = "none",
.value = ligo::verify_mode::NONE, .value = ligo::verify_mode::NONE,
}, },
{ {
.name = "peer", .name = "peer",
.value = ligo::verify_mode::PEER, .value = ligo::verify_mode::PEER,
}, },
}, "endpoint security verify must be defaulted, none, or peer", json_options); },
"endpoint security verify must be defaulted, none, or peer", json_options);
if (!verify) { if (!verify) {
return ligo::detail::fail<ligo::security_config>(verify.error()); return ligo::detail::fail<ligo::security_config>(verify.error());
} }
@@ -139,7 +142,7 @@ auto parse_security_config(boost::json::value const& value) -> std::expected<lig
} }
auto ligo::validate(ligo::endpoint_config const& config) -> std::expected<void, modulus::error> { auto ligo::validate(ligo::endpoint_config const &config) -> std::expected<void, modulus::error> {
if (config.name.empty()) { if (config.name.empty()) {
return ligo::detail::fail_void(ligo::detail::error_invalid_argument, "endpoint name is required"); return ligo::detail::fail_void(ligo::detail::error_invalid_argument, "endpoint name is required");
} }
@@ -161,21 +164,22 @@ auto ligo::validate(ligo::endpoint_config const& config) -> std::expected<void,
return {}; return {};
} }
auto ligo::parse_endpoint_config(boost::json::value const& value) -> std::expected<ligo::endpoint_config, modulus::error> { auto ligo::parse_endpoint_config(boost::json::value const &value) -> std::expected<ligo::endpoint_config, modulus::error> {
auto object = modulus::json_config::require_object(value, "endpoint", json_options); auto object = modulus::json_config::require_object(value, "endpoint", json_options);
if (!object) { if (!object) {
return ligo::detail::fail<ligo::endpoint_config>(object.error()); return ligo::detail::fail<ligo::endpoint_config>(object.error());
} }
auto unknown = modulus::json_config::reject_unknown_fields(**object, { auto unknown = modulus::json_config::reject_unknown_fields(**object, {
"name", "name",
"protocol", "protocol",
"bind", "bind",
"connect", "connect",
"timeout_ms", "timeout_ms",
"security", "security",
"http", "http",
"udp", "udp",
}, "endpoint", json_options); },
"endpoint", json_options);
if (!unknown) { if (!unknown) {
return ligo::detail::fail<ligo::endpoint_config>(unknown.error()); return ligo::detail::fail<ligo::endpoint_config>(unknown.error());
} }
@@ -185,19 +189,20 @@ auto ligo::parse_endpoint_config(boost::json::value const& value) -> std::expect
return ligo::detail::fail<ligo::endpoint_config>(name.error()); return ligo::detail::fail<ligo::endpoint_config>(name.error());
} }
auto protocol = modulus::json_config::optional_enum_field(**object, "protocol", config.protocol, { auto protocol = modulus::json_config::optional_enum_field(**object, "protocol", config.protocol, {
{ {
.name = "http", .name = "http",
.value = ligo::protocol::HTTP, .value = ligo::protocol::HTTP,
}, },
{ {
.name = "tcp", .name = "tcp",
.value = ligo::protocol::TCP, .value = ligo::protocol::TCP,
}, },
{ {
.name = "udp", .name = "udp",
.value = ligo::protocol::UDP, .value = ligo::protocol::UDP,
}, },
}, "endpoint protocol must be http, tcp, or udp", json_options); },
"endpoint protocol must be http, tcp, or udp", json_options);
if (!protocol) { if (!protocol) {
return ligo::detail::fail<ligo::endpoint_config>(protocol.error()); return ligo::detail::fail<ligo::endpoint_config>(protocol.error());
} }
@@ -246,10 +251,11 @@ auto ligo::parse_endpoint_config(boost::json::value const& value) -> std::expect
return ligo::detail::fail<ligo::endpoint_config>(udp.error()); return ligo::detail::fail<ligo::endpoint_config>(udp.error());
} }
auto udp_unknown = modulus::json_config::reject_unknown_fields(**udp, { auto udp_unknown = modulus::json_config::reject_unknown_fields(**udp, {
"resend_interval_ms", "resend_interval_ms",
"max_in_flight", "max_in_flight",
"session_timeout_ms", "session_timeout_ms",
}, "endpoint udp", json_options); },
"endpoint udp", json_options);
if (!udp_unknown) { if (!udp_unknown) {
return ligo::detail::fail<ligo::endpoint_config>(udp_unknown.error()); return ligo::detail::fail<ligo::endpoint_config>(udp_unknown.error());
} }
@@ -276,7 +282,7 @@ auto ligo::parse_endpoint_config(boost::json::value const& value) -> std::expect
return modulus::success(std::move(config)); return modulus::success(std::move(config));
} }
auto ligo::parse_endpoint_configs(boost::json::value const& value) -> std::expected<std::vector<ligo::endpoint_config>, modulus::error> { auto ligo::parse_endpoint_configs(boost::json::value const &value) -> std::expected<std::vector<ligo::endpoint_config>, modulus::error> {
auto object = modulus::json_config::require_object(value, "config root", json_options); auto object = modulus::json_config::require_object(value, "config root", json_options);
if (!object) { if (!object) {
return ligo::detail::fail<std::vector<ligo::endpoint_config>>(object.error()); return ligo::detail::fail<std::vector<ligo::endpoint_config>>(object.error());
@@ -290,7 +296,7 @@ auto ligo::parse_endpoint_configs(boost::json::value const& value) -> std::expec
return ligo::detail::fail<std::vector<ligo::endpoint_config>>(ligo::detail::error_invalid_argument, "config endpoints array is required"); return ligo::detail::fail<std::vector<ligo::endpoint_config>>(ligo::detail::error_invalid_argument, "config endpoints array is required");
} }
auto configs = std::vector<ligo::endpoint_config>{}; auto configs = std::vector<ligo::endpoint_config>{};
for (auto const& endpoint_value : found->value().as_array()) { for (auto const &endpoint_value : found->value().as_array()) {
auto parsed = ligo::parse_endpoint_config(endpoint_value); auto parsed = ligo::parse_endpoint_config(endpoint_value);
if (!parsed) { if (!parsed) {
return ligo::detail::fail<std::vector<ligo::endpoint_config>>(parsed.error()); return ligo::detail::fail<std::vector<ligo::endpoint_config>>(parsed.error());
@@ -300,7 +306,7 @@ auto ligo::parse_endpoint_configs(boost::json::value const& value) -> std::expec
return modulus::success(std::move(configs)); return modulus::success(std::move(configs));
} }
auto ligo::load_endpoint_configs(std::string const& path) -> std::expected<std::vector<ligo::endpoint_config>, modulus::error> { auto ligo::load_endpoint_configs(std::string const &path) -> std::expected<std::vector<ligo::endpoint_config>, modulus::error> {
auto value = modulus::json_config::load_value(path, json_options); auto value = modulus::json_config::load_value(path, json_options);
if (!value) { if (!value) {
return ligo::detail::fail<std::vector<ligo::endpoint_config>>(value.error()); return ligo::detail::fail<std::vector<ligo::endpoint_config>>(value.error());
@@ -308,7 +314,7 @@ auto ligo::load_endpoint_configs(std::string const& path) -> std::expected<std::
return ligo::parse_endpoint_configs(*value); return ligo::parse_endpoint_configs(*value);
} }
auto ligo::to_json(ligo::udp_reliability_config const& config) -> boost::json::value { auto ligo::to_json(ligo::udp_reliability_config const &config) -> boost::json::value {
return boost::json::object{ return boost::json::object{
{ {
"resend_interval_ms", "resend_interval_ms",
@@ -325,7 +331,7 @@ auto ligo::to_json(ligo::udp_reliability_config const& config) -> boost::json::v
}; };
} }
auto ligo::to_json(ligo::http_config const& config) -> boost::json::value { auto ligo::to_json(ligo::http_config const &config) -> boost::json::value {
return boost::json::object{ return boost::json::object{
{ {
"max_body_size", "max_body_size",
@@ -334,7 +340,7 @@ auto ligo::to_json(ligo::http_config const& config) -> boost::json::value {
}; };
} }
auto ligo::to_json(ligo::security_config const& config) -> boost::json::value { auto ligo::to_json(ligo::security_config const &config) -> boost::json::value {
return boost::json::object{ return boost::json::object{
{ {
"mode", "mode",
@@ -363,7 +369,7 @@ auto ligo::to_json(ligo::security_config const& config) -> boost::json::value {
}; };
} }
auto ligo::to_json(ligo::endpoint_config const& config) -> boost::json::value { auto ligo::to_json(ligo::endpoint_config const &config) -> boost::json::value {
return boost::json::object{ return boost::json::object{
{ {
"name", "name",
@@ -400,9 +406,9 @@ auto ligo::to_json(ligo::endpoint_config const& config) -> boost::json::value {
}; };
} }
auto ligo::to_json(std::vector<ligo::endpoint_config> const& configs) -> boost::json::value { auto ligo::to_json(std::vector<ligo::endpoint_config> const &configs) -> boost::json::value {
auto endpoints = boost::json::array{}; auto endpoints = boost::json::array{};
for (auto const& config : configs) { for (auto const &config : configs) {
endpoints.push_back(ligo::to_json(config)); endpoints.push_back(ligo::to_json(config));
} }
return boost::json::object{ return boost::json::object{
+1
View File
@@ -5,6 +5,7 @@
#include <modulus.hpp> #include <modulus.hpp>
#include <cstdint> #include <cstdint>
auto ligo::detail::make_error(std::int32_t code, std::string message, modulus::source_location location) -> modulus::error { auto ligo::detail::make_error(std::int32_t code, std::string message, modulus::source_location location) -> modulus::error {
return modulus::make_error("ligo", code, std::move(message), location); return modulus::make_error("ligo", code, std::move(message), location);
} }
+2 -1
View File
@@ -3,7 +3,8 @@
#include <boost/beast/core.hpp> #include <boost/beast/core.hpp>
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
auto ligo::detail::cancel_session_stream(boost::beast::tcp_stream& stream) -> void {
auto ligo::detail::cancel_session_stream(boost::beast::tcp_stream &stream) -> void {
auto ignored = boost::system::error_code{}; auto ignored = boost::system::error_code{};
stream.socket().cancel(ignored); stream.socket().cancel(ignored);
stream.socket().close(ignored); stream.socket().close(ignored);
+25 -23
View File
@@ -30,9 +30,10 @@
#include <ligo/detail/state.hpp> #include <ligo/detail/state.hpp>
#include <ligo/service.hpp> #include <ligo/service.hpp>
namespace { namespace {
auto error_status_code(boost::system::error_code const& error) -> std::int32_t { auto error_status_code(boost::system::error_code const &error) -> std::int32_t {
if (!error) { if (!error) {
return ligo::detail::error_ok; return ligo::detail::error_ok;
} }
@@ -47,9 +48,10 @@ auto error_status_code(boost::system::error_code const& error) -> std::int32_t {
} }
ligo::detail::state_handle::state_handle(ligo::detail::state_handle&& other) noexcept : state_{other.state_.exchange(nullptr)} {} ligo::detail::state_handle::state_handle(ligo::detail::state_handle &&other) noexcept
: state_{other.state_.exchange(nullptr)} {}
auto ligo::detail::state_handle::operator=(ligo::detail::state_handle&& other) noexcept -> ligo::detail::state_handle& { auto ligo::detail::state_handle::operator=(ligo::detail::state_handle &&other) noexcept -> ligo::detail::state_handle & {
if (this != &other) { if (this != &other) {
state_.store(other.state_.exchange(nullptr)); state_.store(other.state_.exchange(nullptr));
} }
@@ -64,7 +66,7 @@ ligo::detail::unique_state::operator std::bool_t() const noexcept {
return static_cast<std::bool_t>(state_); return static_cast<std::bool_t>(state_);
} }
auto ligo::detail::unique_state::ignore_state(void* state) -> void { auto ligo::detail::unique_state::ignore_state(void *state) -> void {
static_cast<void>(state); static_cast<void>(state);
} }
@@ -105,25 +107,25 @@ auto ligo::detail::parse_status_code(std::string_view value) -> std::int32_t {
return ligo::detail::error_internal; return ligo::detail::error_internal;
} }
auto ligo::detail::network_failure(boost::system::error_code const& error) -> modulus::error { auto ligo::detail::network_failure(boost::system::error_code const &error) -> modulus::error {
return ligo::detail::make_error(error_status_code(error), error.message()); return ligo::detail::make_error(error_status_code(error), error.message());
} }
auto ligo::detail::operation_failure(boost::system::error_code const& error, std::string timeout_message) -> modulus::error { auto ligo::detail::operation_failure(boost::system::error_code const &error, std::string timeout_message) -> modulus::error {
if (error_status_code(error) == ligo::detail::error_timeout) { if (error_status_code(error) == ligo::detail::error_timeout) {
return ligo::detail::make_error(ligo::detail::error_timeout, std::move(timeout_message)); return ligo::detail::make_error(ligo::detail::error_timeout, std::move(timeout_message));
} }
return ligo::detail::network_failure(error); return ligo::detail::network_failure(error);
} }
auto ligo::detail::start_stream_deadline(boost::beast::tcp_stream& stream, std::chrono::milliseconds timeout) -> ligo::detail::stream_deadline { auto ligo::detail::start_stream_deadline(boost::beast::tcp_stream &stream, std::chrono::milliseconds timeout) -> ligo::detail::stream_deadline {
stream.expires_after(timeout); stream.expires_after(timeout);
return { return {
.cancellation = std::make_shared<boost::asio::cancellation_signal>(), .cancellation = std::make_shared<boost::asio::cancellation_signal>(),
}; };
} }
auto ligo::detail::start_stream_deadline(boost::asio::ip::tcp::socket& socket, std::chrono::milliseconds timeout) -> ligo::detail::stream_deadline { auto ligo::detail::start_stream_deadline(boost::asio::ip::tcp::socket &socket, std::chrono::milliseconds timeout) -> ligo::detail::stream_deadline {
auto deadline = ligo::detail::stream_deadline{ auto deadline = ligo::detail::stream_deadline{
.timer = std::make_shared<boost::asio::steady_timer>(socket.get_executor()), .timer = std::make_shared<boost::asio::steady_timer>(socket.get_executor()),
.timed_out = std::make_shared<std::atomic<std::bool_t>>(false), .timed_out = std::make_shared<std::atomic<std::bool_t>>(false),
@@ -131,7 +133,7 @@ auto ligo::detail::start_stream_deadline(boost::asio::ip::tcp::socket& socket, s
.cancellation = std::make_shared<boost::asio::cancellation_signal>(), .cancellation = std::make_shared<boost::asio::cancellation_signal>(),
}; };
deadline.timer->expires_after(timeout); 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) { 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)) { if (!error && active->exchange(false, std::memory_order_acq_rel)) {
timed_out->store(true, std::memory_order_release); timed_out->store(true, std::memory_order_release);
cancellation->emit(boost::asio::cancellation_type::terminal); cancellation->emit(boost::asio::cancellation_type::terminal);
@@ -140,7 +142,7 @@ auto ligo::detail::start_stream_deadline(boost::asio::ip::tcp::socket& socket, s
return deadline; return deadline;
} }
auto ligo::detail::cancel_stream_deadline(ligo::detail::stream_deadline& deadline) -> void { auto ligo::detail::cancel_stream_deadline(ligo::detail::stream_deadline &deadline) -> void {
if (deadline.active) { if (deadline.active) {
deadline.active->store(false, std::memory_order_release); deadline.active->store(false, std::memory_order_release);
} }
@@ -149,11 +151,11 @@ auto ligo::detail::cancel_stream_deadline(ligo::detail::stream_deadline& deadlin
} }
} }
auto ligo::detail::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 ligo::detail::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))) {
return boost::asio::bind_cancellation_slot(deadline.cancellation->slot(), boost::asio::as_tuple(boost::asio::use_awaitable)); return boost::asio::bind_cancellation_slot(deadline.cancellation->slot(), boost::asio::as_tuple(boost::asio::use_awaitable));
} }
auto ligo::detail::operation_failure(boost::system::error_code const& error, ligo::detail::stream_deadline const& deadline, std::string timeout_message) -> modulus::error { auto ligo::detail::operation_failure(boost::system::error_code const &error, ligo::detail::stream_deadline const &deadline, std::string timeout_message) -> modulus::error {
if (deadline.timed_out && deadline.timed_out->load(std::memory_order_acquire)) { if (deadline.timed_out && deadline.timed_out->load(std::memory_order_acquire)) {
return ligo::detail::make_error(ligo::detail::error_timeout, std::move(timeout_message)); return ligo::detail::make_error(ligo::detail::error_timeout, std::move(timeout_message));
} }
@@ -175,7 +177,7 @@ auto ligo::detail::validate_positive_timeout(std::chrono::milliseconds timeout,
return {}; return {};
} }
auto ligo::detail::report_error(std::function<void(modulus::error)> const& on_error, modulus::error error) -> void { auto ligo::detail::report_error(std::function<void(modulus::error)> const &on_error, modulus::error error) -> void {
if (!on_error) { if (!on_error) {
return; return;
} }
@@ -185,7 +187,7 @@ auto ligo::detail::report_error(std::function<void(modulus::error)> const& on_er
} }
} }
auto ligo::detail::report_error(std::function<void(modulus::error)> const& on_error, std::expected<void, modulus::error> status) -> void { auto ligo::detail::report_error(std::function<void(modulus::error)> const &on_error, std::expected<void, modulus::error> status) -> void {
if (!status) { if (!status) {
ligo::detail::report_error(on_error, status.error()); ligo::detail::report_error(on_error, status.error());
} }
@@ -212,7 +214,7 @@ auto ligo::detail::unwrap_response_envelope(boost::json::value value, std::strin
if (!value.is_object()) { if (!value.is_object()) {
return ligo::detail::fail<boost::json::value>(ligo::detail::error_invalid_argument, prefix + " response must be an object"); return ligo::detail::fail<boost::json::value>(ligo::detail::error_invalid_argument, prefix + " response must be an object");
} }
auto const& object = value.as_object(); auto const &object = value.as_object();
auto ok = object.find("ok"); auto ok = object.find("ok");
if (ok == object.end() || !ok->value().is_bool()) { if (ok == object.end() || !ok->value().is_bool()) {
return ligo::detail::fail<boost::json::value>(ligo::detail::error_invalid_argument, prefix + " response missing ok flag"); return ligo::detail::fail<boost::json::value>(ligo::detail::error_invalid_argument, prefix + " response missing ok flag");
@@ -234,8 +236,8 @@ auto ligo::detail::parse_port(std::string_view text) -> std::expected<std::uint1
return ligo::detail::fail<std::uint16_t>(ligo::detail::error_invalid_argument, "endpoint port is required"); return ligo::detail::fail<std::uint16_t>(ligo::detail::error_invalid_argument, "endpoint port is required");
} }
auto parsed = 0U; auto parsed = 0U;
auto const* begin = text.data(); auto const *begin = text.data();
auto const* end = text.data() + text.size(); auto const *end = text.data() + text.size();
auto [ptr, error] = std::from_chars(begin, end, parsed); auto [ptr, error] = std::from_chars(begin, end, parsed);
if (error != std::errc{} || ptr != end || parsed > std::numeric_limits<std::uint16_t>::max()) { if (error != std::errc{} || ptr != end || parsed > std::numeric_limits<std::uint16_t>::max()) {
return ligo::detail::fail<std::uint16_t>(ligo::detail::error_invalid_argument, "endpoint port is invalid"); return ligo::detail::fail<std::uint16_t>(ligo::detail::error_invalid_argument, "endpoint port is invalid");
@@ -313,7 +315,7 @@ auto ligo::detail::parse_server_endpoint_text(std::string_view text) -> std::exp
return ligo::detail::parse_endpoint_text(text, 0); return ligo::detail::parse_endpoint_text(text, 0);
} }
auto ligo::detail::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 ligo::detail::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 close_on_failure = [&acceptor] { auto close_on_failure = [&acceptor] {
auto ignored = boost::system::error_code{}; auto ignored = boost::system::error_code{};
acceptor.close(ignored); acceptor.close(ignored);
@@ -361,7 +363,7 @@ auto ligo::detail::start_tcp_acceptor(boost::asio::ip::tcp::acceptor& acceptor,
return ligo::detail::fail<std::uint16_t>(ligo::detail::error_unavailable, error.message()); return ligo::detail::fail<std::uint16_t>(ligo::detail::error_unavailable, error.message());
} }
auto last_error = ligo::detail::unavailable("no TCP bind endpoint resolved"); auto last_error = ligo::detail::unavailable("no TCP bind endpoint resolved");
for (auto const& endpoint : endpoints) { for (auto const &endpoint : endpoints) {
auto selected = bind_endpoint(endpoint.endpoint()); auto selected = bind_endpoint(endpoint.endpoint());
if (selected) { if (selected) {
return selected; return selected;
@@ -371,12 +373,12 @@ auto ligo::detail::start_tcp_acceptor(boost::asio::ip::tcp::acceptor& acceptor,
return ligo::detail::fail<std::uint16_t>(last_error); return ligo::detail::fail<std::uint16_t>(last_error);
} }
auto ligo::detail::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 ligo::detail::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 resolver = std::make_shared<boost::asio::ip::tcp::resolver>(executor); auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(executor);
auto timer = std::make_shared<boost::asio::steady_timer>(executor); auto timer = std::make_shared<boost::asio::steady_timer>(executor);
auto timed_out = std::make_shared<std::atomic<std::bool_t>>(false); auto timed_out = std::make_shared<std::atomic<std::bool_t>>(false);
timer->expires_after(timeout); timer->expires_after(timeout);
timer->async_wait([resolver, timer, timed_out](boost::system::error_code const& error) { timer->async_wait([resolver, timer, timed_out](boost::system::error_code const &error) {
if (!error) { if (!error) {
timed_out->store(true, std::memory_order_release); timed_out->store(true, std::memory_order_release);
resolver->cancel(); resolver->cancel();
@@ -394,10 +396,10 @@ auto ligo::detail::resolve_tcp_endpoints(boost::asio::any_io_executor executor,
co_return modulus::success(std::move(endpoints)); co_return modulus::success(std::move(endpoints));
} }
auto ligo::detail::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 ligo::detail::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>> {
co_return co_await router.call(std::move(context), std::move(payload)); co_return co_await router.call(std::move(context), std::move(payload));
} }
auto ligo::detail::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>> { auto ligo::detail::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>> {
co_return co_await router.call_response(std::move(context), std::move(payload)); co_return co_await router.call_response(std::move(context), std::move(payload));
} }
+31 -27
View File
@@ -43,6 +43,7 @@
#include <ligo/security.hpp> #include <ligo/security.hpp>
#include <ligo/service.hpp> #include <ligo/service.hpp>
namespace { namespace {
struct http_server_state { struct http_server_state {
@@ -80,7 +81,7 @@ auto http_status(std::int32_t code) -> boost::beast::http::status {
return boost::beast::http::status::internal_server_error; return boost::beast::http::status::internal_server_error;
} }
auto http_body_limit_exceeded(boost::system::error_code const& error) -> std::bool_t { auto http_body_limit_exceeded(boost::system::error_code const &error) -> std::bool_t {
return error == boost::beast::http::error::body_limit; return error == boost::beast::http::error::body_limit;
} }
@@ -104,7 +105,7 @@ auto http_verb(ligo::http::method method) -> std::expected<boost::beast::http::v
return ligo::detail::fail<boost::beast::http::verb>(ligo::detail::error_invalid_argument, "HTTP method is invalid"); return ligo::detail::fail<boost::beast::http::verb>(ligo::detail::error_invalid_argument, "HTTP method is invalid");
} }
auto should_send_json_body(ligo::http::request const& request) -> std::bool_t { auto should_send_json_body(ligo::http::request const &request) -> std::bool_t {
if (request.method != ligo::http::method::GET) { if (request.method != ligo::http::method::GET) {
return true; return true;
} }
@@ -114,7 +115,7 @@ auto should_send_json_body(ligo::http::request const& request) -> std::bool_t {
return !request.payload.is_object() || !request.payload.as_object().empty(); return !request.payload.is_object() || !request.payload.as_object().empty();
} }
auto host_header(std::string const& host, std::uint16_t port) -> std::string { auto host_header(std::string const &host, std::uint16_t port) -> std::string {
auto value = std::string{}; auto value = std::string{};
if (!host.empty() && host.front() == '[' && host.back() == ']') { if (!host.empty() && host.front() == '[' && host.back() == ']') {
value = host; value = host;
@@ -128,30 +129,30 @@ auto host_header(std::string const& host, std::uint16_t port) -> std::string {
return value; return value;
} }
auto remote_peer(boost::beast::tcp_stream& stream) -> std::string { auto remote_peer(boost::beast::tcp_stream &stream) -> std::string {
auto endpoint_error = boost::system::error_code{}; auto endpoint_error = boost::system::error_code{};
auto remote_endpoint = stream.socket().remote_endpoint(endpoint_error); auto remote_endpoint = stream.socket().remote_endpoint(endpoint_error);
return endpoint_error ? std::string{} : remote_endpoint.address().to_string(); return endpoint_error ? std::string{} : remote_endpoint.address().to_string();
} }
auto remote_peer(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& stream) -> std::string { auto remote_peer(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &stream) -> std::string {
auto endpoint_error = boost::system::error_code{}; auto endpoint_error = boost::system::error_code{};
auto remote_endpoint = stream.next_layer().remote_endpoint(endpoint_error); auto remote_endpoint = stream.next_layer().remote_endpoint(endpoint_error);
return endpoint_error ? std::string{} : remote_endpoint.address().to_string(); return endpoint_error ? std::string{} : remote_endpoint.address().to_string();
} }
auto close_http_session(boost::beast::tcp_stream& stream, std::chrono::milliseconds) -> boost::asio::awaitable<void> { auto close_http_session(boost::beast::tcp_stream &stream, std::chrono::milliseconds) -> boost::asio::awaitable<void> {
auto ignored = boost::beast::error_code{}; auto ignored = boost::beast::error_code{};
stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, ignored); stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, ignored);
co_return; co_return;
} }
auto close_http_session(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& stream, std::chrono::milliseconds timeout) -> boost::asio::awaitable<void> { auto close_http_session(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &stream, std::chrono::milliseconds timeout) -> boost::asio::awaitable<void> {
static_cast<void>(co_await ligo::detail::async_tls_shutdown(stream, timeout, "HTTP TLS shutdown timed out")); static_cast<void>(co_await ligo::detail::async_tls_shutdown(stream, timeout, "HTTP TLS shutdown timed out"));
} }
template <typename STREAM> template <typename STREAM>
auto write_http_failure_response(STREAM& stream, modulus::error status, std::chrono::milliseconds timeout, std::function<void(modulus::error)> const& on_error) -> boost::asio::awaitable<void> { auto write_http_failure_response(STREAM &stream, modulus::error status, std::chrono::milliseconds timeout, std::function<void(modulus::error)> const &on_error) -> boost::asio::awaitable<void> {
auto response = boost::beast::http::response<boost::beast::http::string_body>{}; auto response = boost::beast::http::response<boost::beast::http::string_body>{};
response.version(11); response.version(11);
response.set(boost::beast::http::field::content_type, "application/json"); response.set(boost::beast::http::field::content_type, "application/json");
@@ -170,7 +171,7 @@ auto write_http_failure_response(STREAM& stream, modulus::error status, std::chr
} }
template <typename STREAM> template <typename STREAM>
auto handle_session(STREAM& stream, std::shared_ptr<ligo::service_router const> router, ligo::http::server_options options) -> boost::asio::awaitable<void> { auto handle_session(STREAM &stream, std::shared_ptr<ligo::service_router const> router, ligo::http::server_options options) -> boost::asio::awaitable<void> {
assert(router != nullptr); assert(router != nullptr);
auto buffer = boost::beast::flat_buffer{}; auto buffer = boost::beast::flat_buffer{};
auto parser = boost::beast::http::request_parser<boost::beast::http::string_body>{}; auto parser = boost::beast::http::request_parser<boost::beast::http::string_body>{};
@@ -287,7 +288,7 @@ auto accept_tls_loop(std::shared_ptr<http_server_state> state) -> boost::asio::a
} }
} }
auto http_state(ligo::detail::state_handle const& state) -> std::shared_ptr<http_server_state> { auto http_state(ligo::detail::state_handle const &state) -> std::shared_ptr<http_server_state> {
return state.as<http_server_state>(); return state.as<http_server_state>();
} }
@@ -295,7 +296,7 @@ auto uninitialized_http_server() -> std::expected<void, modulus::error> {
return ligo::detail::fail_void(ligo::detail::unavailable("HTTP server is not initialized")); return ligo::detail::fail_void(ligo::detail::unavailable("HTTP server is not initialized"));
} }
auto start_http_server(std::shared_ptr<http_server_state> const& state) -> std::expected<void, modulus::error> { auto start_http_server(std::shared_ptr<http_server_state> const &state) -> std::expected<void, modulus::error> {
assert(!state || state->acceptor != nullptr); assert(!state || state->acceptor != nullptr);
assert(!state || state->router != nullptr); assert(!state || state->router != nullptr);
if (!state) { if (!state) {
@@ -363,7 +364,7 @@ auto stop_http_server(std::shared_ptr<http_server_state> state) -> void {
}); });
} }
auto stop_http_server_now(std::shared_ptr<http_server_state> const& state) -> void { auto stop_http_server_now(std::shared_ptr<http_server_state> const &state) -> void {
auto lock = std::scoped_lock{state->lifecycle_mutex}; auto lock = std::scoped_lock{state->lifecycle_mutex};
if (state->stopped.exchange(true)) { if (state->stopped.exchange(true)) {
return; return;
@@ -403,14 +404,14 @@ auto async_stop_http_server(std::shared_ptr<http_server_state> state, std::chron
co_return co_await state->tls_sessions->wait_empty(timeout); co_return co_await state->tls_sessions->wait_empty(timeout);
} }
auto selected_http_port(std::shared_ptr<http_server_state> const& state) -> std::uint16_t { auto selected_http_port(std::shared_ptr<http_server_state> const &state) -> std::uint16_t {
if (!state) { if (!state) {
return 0; return 0;
} }
return state->selected_port.load(); return state->selected_port.load();
} }
auto make_http_request_message(ligo::http::endpoint const& endpoint, ligo::http::request const& request, boost::beast::http::verb verb) -> boost::beast::http::request<boost::beast::http::string_body> { auto make_http_request_message(ligo::http::endpoint const &endpoint, ligo::http::request const &request, boost::beast::http::verb verb) -> boost::beast::http::request<boost::beast::http::string_body> {
auto http_request = boost::beast::http::request<boost::beast::http::string_body>{ auto http_request = boost::beast::http::request<boost::beast::http::string_body>{
verb, verb,
request.target, request.target,
@@ -426,7 +427,7 @@ auto make_http_request_message(ligo::http::endpoint const& endpoint, ligo::http:
} }
template <typename STREAM> template <typename STREAM>
auto read_http_response(STREAM& stream, std::chrono::milliseconds timeout, std::uint64_t max_body_size) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto read_http_response(STREAM &stream, std::chrono::milliseconds timeout, std::uint64_t max_body_size) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
auto buffer = boost::beast::flat_buffer{}; auto buffer = boost::beast::flat_buffer{};
auto parser = boost::beast::http::response_parser<boost::beast::http::string_body>{}; auto parser = boost::beast::http::response_parser<boost::beast::http::string_body>{};
parser.body_limit(max_body_size); parser.body_limit(max_body_size);
@@ -544,19 +545,23 @@ auto call_http_endpoint(ligo::http::endpoint endpoint, std::shared_ptr<ligo::htt
co_return co_await read_http_response(stream, options.timeout, options.max_body_size); co_return co_await read_http_response(stream, options.timeout, options.max_body_size);
} }
http_server_state::http_server_state(boost::asio::any_io_executor executor, ligo::http::endpoint endpoint, ligo::service_router router, ligo::http::server_options options) : executor{std::move(executor)}, strand{boost::asio::make_strand(this->executor)}, acceptor{std::make_shared<boost::asio::ip::tcp::acceptor>(strand)}, endpoint{std::move(endpoint)}, options{options}, router{std::make_shared<ligo::service_router>(std::move(router))} {} http_server_state::http_server_state(boost::asio::any_io_executor executor, ligo::http::endpoint endpoint, ligo::service_router router, ligo::http::server_options options)
: executor{std::move(executor)}, strand{boost::asio::make_strand(this->executor)}, acceptor{std::make_shared<boost::asio::ip::tcp::acceptor>(strand)}, endpoint{std::move(endpoint)}, options{options}, router{std::make_shared<ligo::service_router>(std::move(router))} {}
} }
ligo::http::client::client(ligo::http::endpoint endpoint, ligo::http::client_options options) : endpoint_{std::move(endpoint)}, options_{options} {} ligo::http::client::client(ligo::http::endpoint endpoint, ligo::http::client_options options)
: endpoint_{std::move(endpoint)}, options_{options} {}
auto ligo::http::client::call(ligo::http::request const& request) const -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto ligo::http::client::call(ligo::http::request const &request) const -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
return call_http_endpoint(endpoint_, std::make_shared<ligo::http::request const>(request), options_); return call_http_endpoint(endpoint_, std::make_shared<ligo::http::request const>(request), options_);
} }
ligo::http::server::server(boost::asio::io_context& io, ligo::http::endpoint endpoint, ligo::service_router router, ligo::http::server_options options) : ligo::http::server{io.get_executor(), std::move(endpoint), std::move(router), options} {} ligo::http::server::server(boost::asio::io_context &io, ligo::http::endpoint endpoint, ligo::service_router router, ligo::http::server_options options)
: ligo::http::server{io.get_executor(), std::move(endpoint), std::move(router), options} {}
ligo::http::server::server(boost::asio::any_io_executor executor, ligo::http::endpoint endpoint, ligo::service_router router, ligo::http::server_options options) : state_{ligo::detail::state_handle{std::make_shared<http_server_state>(std::move(executor), std::move(endpoint), std::move(router), options)}} {} ligo::http::server::server(boost::asio::any_io_executor executor, ligo::http::endpoint endpoint, ligo::service_router router, ligo::http::server_options options)
: state_{ligo::detail::state_handle{std::make_shared<http_server_state>(std::move(executor), std::move(endpoint), std::move(router), options)}} {}
ligo::http::server::~server() { ligo::http::server::~server() {
if (auto state = http_state(state_)) { if (auto state = http_state(state_)) {
@@ -564,7 +569,7 @@ ligo::http::server::~server() {
} }
} }
auto ligo::http::server::operator=(ligo::http::server&& other) noexcept -> ligo::http::server& { auto ligo::http::server::operator=(ligo::http::server &&other) noexcept -> ligo::http::server & {
if (this != &other) { if (this != &other) {
if (auto state = http_state(state_)) { if (auto state = http_state(state_)) {
stop_http_server_now(state); stop_http_server_now(state);
@@ -592,18 +597,17 @@ auto ligo::http::server::selected_port() const -> std::uint16_t {
return selected_http_port(http_state(state_)); return selected_http_port(http_state(state_));
} }
auto ligo::http::client_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::http::endpoint, modulus::error> { auto ligo::http::client_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::http::endpoint, modulus::error> {
auto parsed = ligo::detail::parse_client_endpoint_text(config.connect); auto parsed = ligo::detail::parse_client_endpoint_text(config.connect);
if (!parsed) { if (!parsed) {
return ligo::detail::fail<ligo::http::endpoint>(parsed.error()); return ligo::detail::fail<ligo::http::endpoint>(parsed.error());
} }
return modulus::success(ligo::http::endpoint{ return modulus::success(ligo::http::endpoint{
.host = std::move(parsed->host), .host = std::move(parsed->host),
.port = parsed->port .port = parsed->port});
});
} }
auto ligo::http::server_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::http::endpoint, modulus::error> { auto ligo::http::server_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::http::endpoint, modulus::error> {
auto parsed = ligo::detail::parse_server_endpoint_text(config.bind); auto parsed = ligo::detail::parse_server_endpoint_text(config.bind);
if (!parsed) { if (!parsed) {
return ligo::detail::fail<ligo::http::endpoint>(parsed.error()); return ligo::detail::fail<ligo::http::endpoint>(parsed.error());
@@ -614,7 +618,7 @@ auto ligo::http::server_endpoint_from_config(ligo::endpoint_config const& config
}); });
} }
auto ligo::http::client_options_from_config(ligo::endpoint_config const& config) -> ligo::http::client_options { auto ligo::http::client_options_from_config(ligo::endpoint_config const &config) -> ligo::http::client_options {
return { return {
.timeout = config.timeout, .timeout = config.timeout,
.max_body_size = config.http.max_body_size, .max_body_size = config.http.max_body_size,
@@ -622,7 +626,7 @@ auto ligo::http::client_options_from_config(ligo::endpoint_config const& config)
}; };
} }
auto ligo::http::server_options_from_config(ligo::endpoint_config const& config) -> ligo::http::server_options { auto ligo::http::server_options_from_config(ligo::endpoint_config const &config) -> ligo::http::server_options {
return { return {
.timeout = config.timeout, .timeout = config.timeout,
.max_body_size = config.http.max_body_size, .max_body_size = config.http.max_body_size,
+32 -27
View File
@@ -37,23 +37,23 @@
#include <ligo/tcp.hpp> #include <ligo/tcp.hpp>
#include <cstdint> #include <cstdint>
class ligo::net::byte_stream_state { class ligo::net::byte_stream_state {
public: public:
byte_stream_state() = default; byte_stream_state() = default;
byte_stream_state(ligo::net::byte_stream_state const&) = delete; byte_stream_state(ligo::net::byte_stream_state const &) = delete;
byte_stream_state(ligo::net::byte_stream_state&&) noexcept = delete; byte_stream_state(ligo::net::byte_stream_state &&) noexcept = delete;
virtual ~byte_stream_state() = default; virtual ~byte_stream_state() = default;
auto operator=(ligo::net::byte_stream_state const&) -> ligo::net::byte_stream_state& = delete; auto operator=(ligo::net::byte_stream_state const &) -> ligo::net::byte_stream_state & = delete;
auto operator=(ligo::net::byte_stream_state&&) noexcept -> ligo::net::byte_stream_state& = delete; auto operator=(ligo::net::byte_stream_state &&) noexcept -> ligo::net::byte_stream_state & = delete;
virtual auto async_read_some(boost::asio::mutable_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> = 0; virtual auto async_read_some(boost::asio::mutable_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> = 0;
virtual auto async_write_some(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> = 0; virtual auto async_write_some(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> = 0;
virtual auto async_write_all(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> = 0; virtual auto async_write_all(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> = 0;
virtual auto async_shutdown(std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> = 0; virtual auto async_shutdown(std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> = 0;
virtual auto close(boost::system::error_code& error) -> void = 0; virtual auto close(boost::system::error_code &error) -> void = 0;
virtual auto is_open() const -> std::bool_t = 0; virtual auto is_open() const -> std::bool_t = 0;
}; };
namespace { namespace {
struct net_listener_state { struct net_listener_state {
@@ -71,7 +71,7 @@ public:
auto async_write_some(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> override; auto async_write_some(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> override;
auto async_write_all(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> override; auto async_write_all(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> override;
auto async_shutdown(std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> override; auto async_shutdown(std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> override;
auto close(boost::system::error_code& error) -> void override; auto close(boost::system::error_code &error) -> void override;
auto is_open() const -> std::bool_t override; auto is_open() const -> std::bool_t override;
private: private:
std::shared_ptr<boost::asio::ip::tcp::socket> socket_{}; std::shared_ptr<boost::asio::ip::tcp::socket> socket_{};
@@ -84,18 +84,18 @@ public:
auto async_write_some(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> override; auto async_write_some(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> override;
auto async_write_all(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> override; auto async_write_all(boost::asio::const_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> override;
auto async_shutdown(std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> override; auto async_shutdown(std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> override;
auto close(boost::system::error_code& error) -> void override; auto close(boost::system::error_code &error) -> void override;
auto is_open() const -> std::bool_t override; auto is_open() const -> std::bool_t override;
private: private:
std::shared_ptr<boost::asio::ssl::context> context_{}; std::shared_ptr<boost::asio::ssl::context> context_{};
std::unique_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> stream_{}; std::unique_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> stream_{};
}; };
auto listener_state(ligo::detail::state_handle const& state) -> std::shared_ptr<net_listener_state> { auto listener_state(ligo::detail::state_handle const &state) -> std::shared_ptr<net_listener_state> {
return state.as<net_listener_state>(); return state.as<net_listener_state>();
} }
auto close_listener(std::shared_ptr<net_listener_state> const& state) -> void { auto close_listener(std::shared_ptr<net_listener_state> const &state) -> void {
if (!state || state->closed.exchange(true)) { if (!state || state->closed.exchange(true)) {
return; return;
} }
@@ -106,7 +106,7 @@ auto close_listener(std::shared_ptr<net_listener_state> const& state) -> void {
}); });
} }
auto validate_tls_accept_security(ligo::security_config const& security) -> std::expected<void, modulus::error> { auto validate_tls_accept_security(ligo::security_config const &security) -> std::expected<void, modulus::error> {
if (security.mode != ligo::security_mode::TLS) { if (security.mode != ligo::security_mode::TLS) {
return ligo::detail::fail_void(ligo::unsupported_security("TCP TLS accept", security.mode)); return ligo::detail::fail_void(ligo::unsupported_security("TCP TLS accept", security.mode));
} }
@@ -118,7 +118,7 @@ auto validate_stream_timeout(std::chrono::milliseconds timeout) -> std::expected
} }
template <typename STREAM> template <typename STREAM>
auto read_some_from(STREAM& stream, boost::asio::mutable_buffer buffer, std::chrono::milliseconds timeout, std::string timeout_message) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> { auto read_some_from(STREAM &stream, boost::asio::mutable_buffer buffer, std::chrono::milliseconds timeout, std::string timeout_message) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> {
auto valid = validate_stream_timeout(timeout); auto valid = validate_stream_timeout(timeout);
if (!valid) { if (!valid) {
co_return ligo::detail::fail<std::size_t>(valid.error()); co_return ligo::detail::fail<std::size_t>(valid.error());
@@ -134,7 +134,7 @@ auto read_some_from(STREAM& stream, boost::asio::mutable_buffer buffer, std::chr
} }
template <typename STREAM> template <typename STREAM>
auto write_some_to(STREAM& stream, boost::asio::const_buffer buffer, std::chrono::milliseconds timeout, std::string timeout_message) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> { auto write_some_to(STREAM &stream, boost::asio::const_buffer buffer, std::chrono::milliseconds timeout, std::string timeout_message) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> {
auto valid = validate_stream_timeout(timeout); auto valid = validate_stream_timeout(timeout);
if (!valid) { if (!valid) {
co_return ligo::detail::fail<std::size_t>(valid.error()); co_return ligo::detail::fail<std::size_t>(valid.error());
@@ -150,7 +150,7 @@ auto write_some_to(STREAM& stream, boost::asio::const_buffer buffer, std::chrono
} }
template <typename STREAM> template <typename STREAM>
auto write_all_to(STREAM& stream, boost::asio::const_buffer buffer, std::chrono::milliseconds timeout, std::string timeout_message) -> boost::asio::awaitable<std::expected<void, modulus::error>> { auto write_all_to(STREAM &stream, boost::asio::const_buffer buffer, std::chrono::milliseconds timeout, std::string timeout_message) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
auto valid = validate_stream_timeout(timeout); auto valid = validate_stream_timeout(timeout);
if (!valid) { if (!valid) {
co_return valid; co_return valid;
@@ -185,7 +185,7 @@ auto accept_socket(std::shared_ptr<net_listener_state> state, std::optional<std:
if (timeout.has_value()) { if (timeout.has_value()) {
timer = std::make_shared<boost::asio::steady_timer>(state->strand); timer = std::make_shared<boost::asio::steady_timer>(state->strand);
timer->expires_after(*timeout); timer->expires_after(*timeout);
timer->async_wait([state, timer, timed_out](boost::system::error_code const& error) { timer->async_wait([state, timer, timed_out](boost::system::error_code const &error) {
if (!error) { if (!error) {
timed_out->store(true, std::memory_order_release); timed_out->store(true, std::memory_order_release);
auto ignored = boost::system::error_code{}; auto ignored = boost::system::error_code{};
@@ -207,7 +207,7 @@ auto accept_socket(std::shared_ptr<net_listener_state> state, std::optional<std:
co_return modulus::success(ligo::tcp::socket_handle{std::move(socket)}); co_return modulus::success(ligo::tcp::socket_handle{std::move(socket)});
} }
auto make_server_tls_context(ligo::security_config const& security) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error> { auto make_server_tls_context(ligo::security_config const &security) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error> {
auto valid_security = validate_tls_accept_security(security); auto valid_security = validate_tls_accept_security(security);
if (!valid_security) { if (!valid_security) {
return ligo::detail::fail<std::shared_ptr<boost::asio::ssl::context>>(valid_security.error()); return ligo::detail::fail<std::shared_ptr<boost::asio::ssl::context>>(valid_security.error());
@@ -227,9 +227,11 @@ auto make_server_tls_context(ligo::security_config const& security) -> std::expe
return context; return context;
} }
net_listener_state::net_listener_state(boost::asio::any_io_executor executor) : strand{boost::asio::make_strand(std::move(executor))}, acceptor{strand} {} net_listener_state::net_listener_state(boost::asio::any_io_executor executor)
: strand{boost::asio::make_strand(std::move(executor))}, acceptor{strand} {}
tcp_byte_stream_state::tcp_byte_stream_state(boost::asio::ip::tcp::socket socket) : socket_{std::make_shared<boost::asio::ip::tcp::socket>(std::move(socket))} {} tcp_byte_stream_state::tcp_byte_stream_state(boost::asio::ip::tcp::socket socket)
: socket_{std::make_shared<boost::asio::ip::tcp::socket>(std::move(socket))} {}
auto tcp_byte_stream_state::async_read_some(boost::asio::mutable_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> { auto tcp_byte_stream_state::async_read_some(boost::asio::mutable_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> {
if (!is_open()) { if (!is_open()) {
@@ -272,7 +274,7 @@ auto tcp_byte_stream_state::async_shutdown(std::chrono::milliseconds timeout) ->
co_return std::expected<void, modulus::error>{}; co_return std::expected<void, modulus::error>{};
} }
auto tcp_byte_stream_state::close(boost::system::error_code& error) -> void { auto tcp_byte_stream_state::close(boost::system::error_code &error) -> void {
if (socket_) { if (socket_) {
socket_->close(error); socket_->close(error);
} }
@@ -282,7 +284,8 @@ auto tcp_byte_stream_state::is_open() const -> std::bool_t {
return socket_ && socket_->is_open(); return socket_ && socket_->is_open();
} }
tls_byte_stream_state::tls_byte_stream_state(ligo::tcp::tls_socket::released_stream released) : context_{std::move(released.context)}, stream_{std::make_unique<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>(std::move(released.stream))} {} tls_byte_stream_state::tls_byte_stream_state(ligo::tcp::tls_socket::released_stream released)
: context_{std::move(released.context)}, stream_{std::make_unique<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>(std::move(released.stream))} {}
auto tls_byte_stream_state::async_read_some(boost::asio::mutable_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> { auto tls_byte_stream_state::async_read_some(boost::asio::mutable_buffer buffer, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<std::size_t, modulus::error>> {
if (!is_open()) { if (!is_open()) {
@@ -315,7 +318,7 @@ auto tls_byte_stream_state::async_shutdown(std::chrono::milliseconds timeout) ->
co_return status; co_return status;
} }
auto tls_byte_stream_state::close(boost::system::error_code& error) -> void { auto tls_byte_stream_state::close(boost::system::error_code &error) -> void {
if (stream_) { if (stream_) {
stream_->lowest_layer().close(error); stream_->lowest_layer().close(error);
} }
@@ -332,7 +335,7 @@ ligo::net::byte_stream::~byte_stream() {
close(error); close(error);
} }
auto ligo::net::byte_stream::operator=(ligo::net::byte_stream&& other) noexcept -> ligo::net::byte_stream& { auto ligo::net::byte_stream::operator=(ligo::net::byte_stream &&other) noexcept -> ligo::net::byte_stream & {
if (this != &other) { if (this != &other) {
auto error = boost::system::error_code{}; auto error = boost::system::error_code{};
close(error); close(error);
@@ -389,7 +392,7 @@ auto ligo::net::byte_stream::async_shutdown(std::chrono::milliseconds timeout) -
co_return co_await state_->async_shutdown(timeout); co_return co_await state_->async_shutdown(timeout);
} }
auto ligo::net::byte_stream::close(boost::system::error_code& error) -> void { auto ligo::net::byte_stream::close(boost::system::error_code &error) -> void {
if (state_) { if (state_) {
state_->close(error); state_->close(error);
} }
@@ -402,13 +405,14 @@ auto ligo::net::byte_stream::is_open() const -> std::bool_t {
return false; return false;
} }
ligo::net::byte_stream::byte_stream(std::shared_ptr<ligo::net::byte_stream_state> state) : state_{std::move(state)} {} ligo::net::byte_stream::byte_stream(std::shared_ptr<ligo::net::byte_stream_state> state)
: state_{std::move(state)} {}
ligo::net::listener::~listener() { ligo::net::listener::~listener() {
close(); close();
} }
auto ligo::net::listener::operator=(ligo::net::listener&& other) noexcept -> ligo::net::listener& { auto ligo::net::listener::operator=(ligo::net::listener &&other) noexcept -> ligo::net::listener & {
if (this != &other) { if (this != &other) {
close(); close();
state_ = std::move(other.state_); state_ = std::move(other.state_);
@@ -487,7 +491,8 @@ auto ligo::net::listener::close() -> void {
close_listener(listener_state(state_)); close_listener(listener_state(state_));
} }
ligo::net::listener::listener(ligo::detail::state_handle state) : state_{std::move(state)} {} ligo::net::listener::listener(ligo::detail::state_handle state)
: state_{std::move(state)} {}
auto ligo::net::connect_tcp(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>> { auto ligo::net::connect_tcp(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::tcp::client_options options) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>> {
co_return co_await ligo::tcp::connect(std::move(executor), std::move(endpoint), options); co_return co_await ligo::tcp::connect(std::move(executor), std::move(endpoint), options);
@@ -497,7 +502,7 @@ auto ligo::net::connect(boost::asio::any_io_executor executor, ligo::tcp::endpoi
co_return co_await ligo::net::connect_tcp(std::move(executor), std::move(endpoint), options); co_return co_await ligo::net::connect_tcp(std::move(executor), std::move(endpoint), options);
} }
auto ligo::net::connect(boost::asio::any_io_executor executor, ligo::endpoint_config const& config) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>> { auto ligo::net::connect(boost::asio::any_io_executor executor, ligo::endpoint_config const &config) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>> {
co_return co_await ligo::tcp::connect(std::move(executor), config); co_return co_await ligo::tcp::connect(std::move(executor), config);
} }
+12 -11
View File
@@ -19,6 +19,7 @@
#include <ligo/detail/errors.hpp> #include <ligo/detail/errors.hpp>
#include <cstdint> #include <cstdint>
namespace { namespace {
auto ssl_failure(std::int32_t code, std::string message) -> modulus::error { auto ssl_failure(std::int32_t code, std::string message) -> modulus::error {
@@ -34,7 +35,7 @@ auto ssl_failure(std::string message) -> modulus::error {
return ssl_failure(ligo::detail::error_unavailable, std::move(message)); return ssl_failure(ligo::detail::error_unavailable, std::move(message));
} }
auto apply_verify_mode(boost::asio::ssl::context& context, ligo::verify_mode verify) -> std::expected<void, modulus::error> { auto apply_verify_mode(boost::asio::ssl::context &context, ligo::verify_mode verify) -> std::expected<void, modulus::error> {
auto const mode = verify == ligo::verify_mode::PEER || verify == ligo::verify_mode::DEFAULTED ? boost::asio::ssl::verify_peer : boost::asio::ssl::verify_none; auto const mode = verify == ligo::verify_mode::PEER || verify == ligo::verify_mode::DEFAULTED ? boost::asio::ssl::verify_peer : boost::asio::ssl::verify_none;
auto error = boost::system::error_code{}; auto error = boost::system::error_code{};
context.set_verify_mode(mode, error); context.set_verify_mode(mode, error);
@@ -44,7 +45,7 @@ auto apply_verify_mode(boost::asio::ssl::context& context, ligo::verify_mode ver
return {}; return {};
} }
auto apply_server_verify_mode(boost::asio::ssl::context& context, ligo::verify_mode verify) -> std::expected<void, modulus::error> { auto apply_server_verify_mode(boost::asio::ssl::context &context, ligo::verify_mode verify) -> std::expected<void, modulus::error> {
auto const mode = verify == ligo::verify_mode::PEER ? boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert : boost::asio::ssl::verify_none; auto const mode = verify == ligo::verify_mode::PEER ? boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert : boost::asio::ssl::verify_none;
auto error = boost::system::error_code{}; auto error = boost::system::error_code{};
context.set_verify_mode(mode, error); context.set_verify_mode(mode, error);
@@ -54,7 +55,7 @@ auto apply_server_verify_mode(boost::asio::ssl::context& context, ligo::verify_m
return {}; return {};
} }
auto load_verify_paths(boost::asio::ssl::context& context, ligo::security_config const& config) -> std::expected<void, modulus::error> { auto load_verify_paths(boost::asio::ssl::context &context, ligo::security_config const &config) -> std::expected<void, modulus::error> {
if (config.verify == ligo::verify_mode::NONE) { if (config.verify == ligo::verify_mode::NONE) {
return {}; return {};
} }
@@ -70,7 +71,7 @@ auto load_verify_paths(boost::asio::ssl::context& context, ligo::security_config
return {}; return {};
} }
auto load_server_identity(boost::asio::ssl::context& context, ligo::security_config const& config) -> std::expected<void, modulus::error> { auto load_server_identity(boost::asio::ssl::context &context, ligo::security_config const &config) -> std::expected<void, modulus::error> {
if (config.certificate_chain_file.empty() && config.private_key_file.empty()) { if (config.certificate_chain_file.empty() && config.private_key_file.empty()) {
return {}; return {};
} }
@@ -92,7 +93,7 @@ auto load_server_identity(boost::asio::ssl::context& context, ligo::security_con
return {}; return {};
} }
auto make_context(boost::asio::ssl::context::method method, ligo::security_config const& config, std::bool_t server) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error> { auto make_context(boost::asio::ssl::context::method method, ligo::security_config const &config, std::bool_t server) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error> {
if (config.mode == ligo::security_mode::DTLS) { if (config.mode == ligo::security_mode::DTLS) {
return ligo::detail::fail<std::shared_ptr<boost::asio::ssl::context>>(ligo::detail::error_invalid_argument, "DTLS config cannot create a TLS context"); return ligo::detail::fail<std::shared_ptr<boost::asio::ssl::context>>(ligo::detail::error_invalid_argument, "DTLS config cannot create a TLS context");
} }
@@ -154,19 +155,19 @@ auto make_context(boost::asio::ssl::context::method method, ligo::security_confi
} }
auto ligo::make_tls_client_context(ligo::security_config const& config) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error> { auto ligo::make_tls_client_context(ligo::security_config const &config) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error> {
return make_context(boost::asio::ssl::context::tls_client, config, false); return make_context(boost::asio::ssl::context::tls_client, config, false);
} }
auto ligo::make_tls_server_context(ligo::security_config const& config) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error> { auto ligo::make_tls_server_context(ligo::security_config const &config) -> std::expected<std::shared_ptr<boost::asio::ssl::context>, modulus::error> {
return make_context(boost::asio::ssl::context::tls_server, config, true); return make_context(boost::asio::ssl::context::tls_server, config, true);
} }
auto ligo::apply_tls_client_verification(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& stream, std::string const& host, ligo::security_config const& config) -> std::expected<void, modulus::error> { auto ligo::apply_tls_client_verification(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &stream, std::string const &host, ligo::security_config const &config) -> std::expected<void, modulus::error> {
return ligo::configure_tls_client_stream(stream, host, config); return ligo::configure_tls_client_stream(stream, host, config);
} }
auto ligo::configure_tls_client_stream(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& stream, std::string const& host, ligo::security_config const& config) -> std::expected<void, modulus::error> { auto ligo::configure_tls_client_stream(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &stream, std::string const &host, ligo::security_config const &config) -> std::expected<void, modulus::error> {
auto const identity = config.peer_name.empty() ? host : config.peer_name; auto const identity = config.peer_name.empty() ? host : config.peer_name;
if (!identity.empty()) { if (!identity.empty()) {
if (SSL_set_tlsext_host_name(stream.native_handle(), identity.c_str()) != 1) { if (SSL_set_tlsext_host_name(stream.native_handle(), identity.c_str()) != 1) {
@@ -195,7 +196,7 @@ auto ligo::configure_tls_client_stream(boost::asio::ssl::stream<boost::asio::ip:
return {}; return {};
} }
auto ligo::validate_tls_server_config(std::string_view protocol, ligo::security_config const& config) -> std::expected<void, modulus::error> { auto ligo::validate_tls_server_config(std::string_view protocol, ligo::security_config const &config) -> std::expected<void, modulus::error> {
if (config.mode != ligo::security_mode::TLS) { if (config.mode != ligo::security_mode::TLS) {
return {}; return {};
} }
@@ -211,7 +212,7 @@ auto ligo::validate_tls_server_config(std::string_view protocol, ligo::security_
return ligo::detail::fail_void(ligo::detail::error_invalid_argument, std::string{protocol} + " TLS server requires certificate chain/private key files or an SSL context"); return ligo::detail::fail_void(ligo::detail::error_invalid_argument, std::string{protocol} + " TLS server requires certificate chain/private key files or an SSL context");
} }
auto ligo::validate_tls_server_context(std::shared_ptr<boost::asio::ssl::context> const& context) -> std::expected<void, modulus::error> { auto ligo::validate_tls_server_context(std::shared_ptr<boost::asio::ssl::context> const &context) -> std::expected<void, modulus::error> {
if (!context) { if (!context) {
return ligo::detail::fail_void(ligo::detail::error_invalid_argument, "TLS server requires an SSL context"); return ligo::detail::fail_void(ligo::detail::error_invalid_argument, "TLS server requires an SSL context");
} }
+7 -6
View File
@@ -18,25 +18,26 @@
#include <ligo/detail/errors.hpp> #include <ligo/detail/errors.hpp>
#include <ligo/detail/transport.hpp> #include <ligo/detail/transport.hpp>
namespace { namespace {
auto json_content_type(std::string const& value) -> std::bool_t { auto json_content_type(std::string const &value) -> std::bool_t {
return value == "application/json" || value.starts_with("application/json;"); return value == "application/json" || value.starts_with("application/json;");
} }
} }
ligo::service_router::service_router(ligo::service_router const& other) { ligo::service_router::service_router(ligo::service_router const &other) {
auto lock = std::scoped_lock{other.mutex_}; auto lock = std::scoped_lock{other.mutex_};
handlers_ = other.handlers_; handlers_ = other.handlers_;
} }
ligo::service_router::service_router(ligo::service_router&& other) { ligo::service_router::service_router(ligo::service_router &&other) {
auto lock = std::scoped_lock{other.mutex_}; auto lock = std::scoped_lock{other.mutex_};
handlers_ = std::move(other.handlers_); handlers_ = std::move(other.handlers_);
} }
auto ligo::service_router::operator=(ligo::service_router const& other) -> ligo::service_router& { auto ligo::service_router::operator=(ligo::service_router const &other) -> ligo::service_router & {
if (this != &other) { if (this != &other) {
auto lock = std::scoped_lock{ auto lock = std::scoped_lock{
mutex_, mutex_,
@@ -47,7 +48,7 @@ auto ligo::service_router::operator=(ligo::service_router const& other) -> ligo:
return *this; return *this;
} }
auto ligo::service_router::operator=(ligo::service_router&& other) -> ligo::service_router& { auto ligo::service_router::operator=(ligo::service_router &&other) -> ligo::service_router & {
if (this != &other) { if (this != &other) {
auto lock = std::scoped_lock{ auto lock = std::scoped_lock{
mutex_, mutex_,
@@ -109,7 +110,7 @@ auto ligo::service_router::call_response(ligo::request_context context, boost::j
} }
try { try {
co_return co_await (*handler)(std::move(context), std::move(payload)); co_return co_await (*handler)(std::move(context), std::move(payload));
} catch (std::exception const& error) { } catch (std::exception const &error) {
co_return ligo::detail::fail<ligo::service_response>(ligo::detail::error_internal, std::string{"handler failed: "} + error.what()); co_return ligo::detail::fail<ligo::service_response>(ligo::detail::error_internal, std::string{"handler failed: "} + error.what());
} catch (...) { } catch (...) {
co_return ligo::detail::fail<ligo::service_response>(ligo::detail::error_internal, "handler failed"); co_return ligo::detail::fail<ligo::service_response>(ligo::detail::error_internal, "handler failed");
+38 -30
View File
@@ -46,6 +46,7 @@
#include <ligo/security.hpp> #include <ligo/security.hpp>
#include <ligo/service.hpp> #include <ligo/service.hpp>
namespace { namespace {
struct tcp_server_state { struct tcp_server_state {
@@ -81,12 +82,12 @@ auto encode_size(std::uint32_t size) -> std::array<std::uchar_t, 4> {
}; };
} }
auto decode_size(std::array<std::uchar_t, 4> const& bytes) -> std::uint32_t { auto decode_size(std::array<std::uchar_t, 4> const &bytes) -> std::uint32_t {
return (static_cast<std::uint32_t>(bytes[0]) << 24U) | (static_cast<std::uint32_t>(bytes[1]) << 16U) | (static_cast<std::uint32_t>(bytes[2]) << 8U) | static_cast<std::uint32_t>(bytes[3]); return (static_cast<std::uint32_t>(bytes[0]) << 24U) | (static_cast<std::uint32_t>(bytes[1]) << 16U) | (static_cast<std::uint32_t>(bytes[2]) << 8U) | static_cast<std::uint32_t>(bytes[3]);
} }
template <typename S> template <typename S>
auto read_frame_from(S& stream, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto read_frame_from(S &stream, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
auto header = std::array<std::uchar_t, 4>{}; auto header = std::array<std::uchar_t, 4>{};
auto header_deadline = ligo::detail::start_stream_deadline(stream, timeout); auto header_deadline = ligo::detail::start_stream_deadline(stream, timeout);
auto [header_error, header_size] = co_await boost::asio::async_read(stream, boost::asio::buffer(header), ligo::detail::deadline_token(header_deadline)); auto [header_error, header_size] = co_await boost::asio::async_read(stream, boost::asio::buffer(header), ligo::detail::deadline_token(header_deadline));
@@ -122,7 +123,7 @@ auto read_frame_from(S& stream, std::chrono::milliseconds timeout) -> boost::asi
} }
template <typename S> template <typename S>
auto write_frame_to(S& stream, std::string body, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> { auto write_frame_to(S &stream, std::string body, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
if (body.size() > max_frame_size) { if (body.size() > max_frame_size) {
co_return ligo::detail::fail_void(ligo::detail::error_invalid_argument, "frame too large"); co_return ligo::detail::fail_void(ligo::detail::error_invalid_argument, "frame too large");
} }
@@ -148,11 +149,11 @@ auto write_frame_to(S& stream, std::string body, std::chrono::milliseconds timeo
co_return std::expected<void, modulus::error>{}; co_return std::expected<void, modulus::error>{};
} }
auto parse_tcp_request(boost::json::value const& value) -> std::expected<parsed_tcp_request, modulus::error> { auto parse_tcp_request(boost::json::value const &value) -> std::expected<parsed_tcp_request, modulus::error> {
if (!value.is_object()) { if (!value.is_object()) {
return ligo::detail::fail<parsed_tcp_request>(ligo::detail::error_invalid_argument, "TCP request must be an object"); return ligo::detail::fail<parsed_tcp_request>(ligo::detail::error_invalid_argument, "TCP request must be an object");
} }
auto const& object = value.as_object(); auto const &object = value.as_object();
auto route = object.find("route"); auto route = object.find("route");
auto payload = object.find("payload"); auto payload = object.find("payload");
if (route == object.end() || !route->value().is_string() || payload == object.end()) { if (route == object.end() || !route->value().is_string() || payload == object.end()) {
@@ -164,7 +165,7 @@ auto parse_tcp_request(boost::json::value const& value) -> std::expected<parsed_
}); });
} }
auto serialize_tcp_request(ligo::tcp::request const& request) -> std::expected<std::string, modulus::error> { auto serialize_tcp_request(ligo::tcp::request const &request) -> std::expected<std::string, modulus::error> {
auto envelope = boost::json::object{}; auto envelope = boost::json::object{};
envelope["route"] = request.route; envelope["route"] = request.route;
envelope["payload"] = request.payload; envelope["payload"] = request.payload;
@@ -175,20 +176,20 @@ auto serialize_tcp_request(ligo::tcp::request const& request) -> std::expected<s
return modulus::success(std::move(body)); return modulus::success(std::move(body));
} }
auto remote_peer(boost::beast::tcp_stream& stream) -> std::string { auto remote_peer(boost::beast::tcp_stream &stream) -> std::string {
auto endpoint_error = boost::system::error_code{}; auto endpoint_error = boost::system::error_code{};
auto remote_endpoint = stream.socket().remote_endpoint(endpoint_error); auto remote_endpoint = stream.socket().remote_endpoint(endpoint_error);
return endpoint_error ? std::string{} : remote_endpoint.address().to_string(); return endpoint_error ? std::string{} : remote_endpoint.address().to_string();
} }
auto remote_peer(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& stream) -> std::string { auto remote_peer(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &stream) -> std::string {
auto endpoint_error = boost::system::error_code{}; auto endpoint_error = boost::system::error_code{};
auto remote_endpoint = stream.next_layer().remote_endpoint(endpoint_error); auto remote_endpoint = stream.next_layer().remote_endpoint(endpoint_error);
return endpoint_error ? std::string{} : remote_endpoint.address().to_string(); return endpoint_error ? std::string{} : remote_endpoint.address().to_string();
} }
template <typename STREAM> template <typename STREAM>
auto handle_session(STREAM& stream, std::shared_ptr<ligo::service_router const> router, ligo::tcp::server_options options) -> boost::asio::awaitable<void> { auto handle_session(STREAM &stream, std::shared_ptr<ligo::service_router const> router, ligo::tcp::server_options options) -> boost::asio::awaitable<void> {
assert(router != nullptr); assert(router != nullptr);
auto request_value = co_await read_frame_from(stream, options.timeout); auto request_value = co_await read_frame_from(stream, options.timeout);
if (!request_value) { if (!request_value) {
@@ -273,7 +274,7 @@ auto accept_tls_loop(std::shared_ptr<tcp_server_state> state) -> boost::asio::aw
} }
} }
auto tcp_state(ligo::detail::state_handle const& state) -> std::shared_ptr<tcp_server_state> { auto tcp_state(ligo::detail::state_handle const &state) -> std::shared_ptr<tcp_server_state> {
return state.as<tcp_server_state>(); return state.as<tcp_server_state>();
} }
@@ -281,7 +282,7 @@ auto uninitialized_tcp_server() -> std::expected<void, modulus::error> {
return ligo::detail::fail_void(ligo::detail::unavailable("TCP server is not initialized")); return ligo::detail::fail_void(ligo::detail::unavailable("TCP server is not initialized"));
} }
auto start_tcp_server(std::shared_ptr<tcp_server_state> const& state) -> std::expected<void, modulus::error> { auto start_tcp_server(std::shared_ptr<tcp_server_state> const &state) -> std::expected<void, modulus::error> {
assert(!state || state->acceptor != nullptr); assert(!state || state->acceptor != nullptr);
assert(!state || state->router != nullptr); assert(!state || state->router != nullptr);
if (!state) { if (!state) {
@@ -349,7 +350,7 @@ auto stop_tcp_server(std::shared_ptr<tcp_server_state> state) -> void {
}); });
} }
auto stop_tcp_server_now(std::shared_ptr<tcp_server_state> const& state) -> void { auto stop_tcp_server_now(std::shared_ptr<tcp_server_state> const &state) -> void {
auto lock = std::scoped_lock{state->lifecycle_mutex}; auto lock = std::scoped_lock{state->lifecycle_mutex};
if (state->stopped.exchange(true)) { if (state->stopped.exchange(true)) {
return; return;
@@ -389,7 +390,7 @@ auto async_stop_tcp_server(std::shared_ptr<tcp_server_state> state, std::chrono:
co_return co_await state->tls_sessions->wait_empty(timeout); co_return co_await state->tls_sessions->wait_empty(timeout);
} }
auto selected_tcp_port(std::shared_ptr<tcp_server_state> const& state) -> std::uint16_t { auto selected_tcp_port(std::shared_ptr<tcp_server_state> const &state) -> std::uint16_t {
if (!state) { if (!state) {
return 0; return 0;
} }
@@ -489,20 +490,23 @@ auto connect_tcp_socket(boost::asio::any_io_executor executor, ligo::tcp::endpoi
co_return modulus::success(ligo::tcp::socket_handle{stream.release_socket()}); co_return modulus::success(ligo::tcp::socket_handle{stream.release_socket()});
} }
auto validate_tls_connect_security(ligo::security_config const& security) -> std::expected<void, modulus::error> { auto validate_tls_connect_security(ligo::security_config const &security) -> std::expected<void, modulus::error> {
if (security.mode == ligo::security_mode::TLS) { if (security.mode == ligo::security_mode::TLS) {
return {}; return {};
} }
return ligo::detail::fail_void(ligo::detail::error_invalid_argument, "TLS connect requires TLS security mode"); return ligo::detail::fail_void(ligo::detail::error_invalid_argument, "TLS connect requires TLS security mode");
} }
tcp_server_state::tcp_server_state(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::service_router router, ligo::tcp::server_options options) : executor{std::move(executor)}, strand{boost::asio::make_strand(this->executor)}, acceptor{std::make_shared<boost::asio::ip::tcp::acceptor>(strand)}, endpoint{std::move(endpoint)}, options{options}, router{std::make_shared<ligo::service_router>(std::move(router))} {} tcp_server_state::tcp_server_state(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::service_router router, ligo::tcp::server_options options)
: executor{std::move(executor)}, strand{boost::asio::make_strand(this->executor)}, acceptor{std::make_shared<boost::asio::ip::tcp::acceptor>(strand)}, endpoint{std::move(endpoint)}, options{options}, router{std::make_shared<ligo::service_router>(std::move(router))} {}
} }
ligo::tcp::socket_handle::socket_handle(boost::asio::ip::tcp::socket socket) : socket_{std::make_shared<boost::asio::ip::tcp::socket>(std::move(socket))} {} ligo::tcp::socket_handle::socket_handle(boost::asio::ip::tcp::socket socket)
: socket_{std::make_shared<boost::asio::ip::tcp::socket>(std::move(socket))} {}
ligo::tcp::socket_handle::socket_handle(std::shared_ptr<boost::asio::ip::tcp::socket> socket) : socket_{std::move(socket)} {} ligo::tcp::socket_handle::socket_handle(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
: socket_{std::move(socket)} {}
auto ligo::tcp::socket_handle::socket() -> std::expected<std::reference_wrapper<boost::asio::ip::tcp::socket>, modulus::error> { auto ligo::tcp::socket_handle::socket() -> std::expected<std::reference_wrapper<boost::asio::ip::tcp::socket>, modulus::error> {
if (!socket_) { if (!socket_) {
@@ -522,7 +526,7 @@ auto ligo::tcp::socket_handle::is_open() const -> std::bool_t {
return socket_ && socket_->is_open(); return socket_ && socket_->is_open();
} }
auto ligo::tcp::socket_handle::close(boost::system::error_code& error) -> void { auto ligo::tcp::socket_handle::close(boost::system::error_code &error) -> void {
if (socket_) { if (socket_) {
socket_->close(error); socket_->close(error);
} }
@@ -540,7 +544,8 @@ auto ligo::tcp::socket_handle::release_socket() -> std::expected<boost::asio::ip
}; };
} }
ligo::tcp::tls_socket::tls_socket(std::shared_ptr<boost::asio::ssl::context> context, boost::asio::ip::tcp::socket socket) : context_{std::move(context)} { ligo::tcp::tls_socket::tls_socket(std::shared_ptr<boost::asio::ssl::context> context, boost::asio::ip::tcp::socket socket)
: context_{std::move(context)} {
if (context_) { if (context_) {
stream_ = std::make_unique<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>(std::move(socket), *context_); stream_ = std::make_unique<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>(std::move(socket), *context_);
} }
@@ -578,7 +583,7 @@ auto ligo::tcp::tls_socket::is_open() const -> std::bool_t {
return stream_ && stream_->lowest_layer().is_open(); return stream_ && stream_->lowest_layer().is_open();
} }
auto ligo::tcp::tls_socket::close(boost::system::error_code& error) -> void { auto ligo::tcp::tls_socket::close(boost::system::error_code &error) -> void {
if (stream_) { if (stream_) {
stream_->lowest_layer().close(error); stream_->lowest_layer().close(error);
} }
@@ -597,9 +602,10 @@ auto ligo::tcp::tls_socket::release_stream() -> std::expected<ligo::tcp::tls_soc
}; };
} }
ligo::tcp::client::client(ligo::tcp::endpoint endpoint, ligo::tcp::client_options options) : endpoint_{std::move(endpoint)}, options_{options} {} ligo::tcp::client::client(ligo::tcp::endpoint endpoint, ligo::tcp::client_options options)
: endpoint_{std::move(endpoint)}, options_{options} {}
auto ligo::tcp::client::call(ligo::tcp::request const& request) const -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto ligo::tcp::client::call(ligo::tcp::request const &request) const -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
auto body = serialize_tcp_request(request); auto body = serialize_tcp_request(request);
if (!body) { if (!body) {
return failed_tcp_call(body.error()); return failed_tcp_call(body.error());
@@ -607,9 +613,11 @@ auto ligo::tcp::client::call(ligo::tcp::request const& request) const -> boost::
return call_tcp_endpoint(endpoint_, std::make_shared<std::string const>(std::move(*body)), options_); return call_tcp_endpoint(endpoint_, std::make_shared<std::string const>(std::move(*body)), options_);
} }
ligo::tcp::server::server(boost::asio::io_context& io, ligo::tcp::endpoint endpoint, ligo::service_router router, ligo::tcp::server_options options) : ligo::tcp::server{io.get_executor(), std::move(endpoint), std::move(router), options} {} ligo::tcp::server::server(boost::asio::io_context &io, ligo::tcp::endpoint endpoint, ligo::service_router router, ligo::tcp::server_options options)
: ligo::tcp::server{io.get_executor(), std::move(endpoint), std::move(router), options} {}
ligo::tcp::server::server(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::service_router router, ligo::tcp::server_options options) : state_{ligo::detail::state_handle{std::make_shared<tcp_server_state>(std::move(executor), std::move(endpoint), std::move(router), options)}} {} ligo::tcp::server::server(boost::asio::any_io_executor executor, ligo::tcp::endpoint endpoint, ligo::service_router router, ligo::tcp::server_options options)
: state_{ligo::detail::state_handle{std::make_shared<tcp_server_state>(std::move(executor), std::move(endpoint), std::move(router), options)}} {}
ligo::tcp::server::~server() { ligo::tcp::server::~server() {
if (auto state = tcp_state(state_)) { if (auto state = tcp_state(state_)) {
@@ -617,7 +625,7 @@ ligo::tcp::server::~server() {
} }
} }
auto ligo::tcp::server::operator=(ligo::tcp::server&& other) noexcept -> ligo::tcp::server& { auto ligo::tcp::server::operator=(ligo::tcp::server &&other) noexcept -> ligo::tcp::server & {
if (this != &other) { if (this != &other) {
if (auto state = tcp_state(state_)) { if (auto state = tcp_state(state_)) {
stop_tcp_server_now(state); stop_tcp_server_now(state);
@@ -645,7 +653,7 @@ auto ligo::tcp::server::selected_port() const -> std::uint16_t {
return selected_tcp_port(tcp_state(state_)); return selected_tcp_port(tcp_state(state_));
} }
auto ligo::tcp::client_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::tcp::endpoint, modulus::error> { auto ligo::tcp::client_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::tcp::endpoint, modulus::error> {
auto parsed = ligo::detail::parse_client_endpoint_text(config.connect); auto parsed = ligo::detail::parse_client_endpoint_text(config.connect);
if (!parsed) { if (!parsed) {
return ligo::detail::fail<ligo::tcp::endpoint>(parsed.error()); return ligo::detail::fail<ligo::tcp::endpoint>(parsed.error());
@@ -656,7 +664,7 @@ auto ligo::tcp::client_endpoint_from_config(ligo::endpoint_config const& config)
}); });
} }
auto ligo::tcp::server_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::tcp::endpoint, modulus::error> { auto ligo::tcp::server_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::tcp::endpoint, modulus::error> {
auto parsed = ligo::detail::parse_server_endpoint_text(config.bind); auto parsed = ligo::detail::parse_server_endpoint_text(config.bind);
if (!parsed) { if (!parsed) {
return ligo::detail::fail<ligo::tcp::endpoint>(parsed.error()); return ligo::detail::fail<ligo::tcp::endpoint>(parsed.error());
@@ -667,14 +675,14 @@ auto ligo::tcp::server_endpoint_from_config(ligo::endpoint_config const& config)
}); });
} }
auto ligo::tcp::client_options_from_config(ligo::endpoint_config const& config) -> ligo::tcp::client_options { auto ligo::tcp::client_options_from_config(ligo::endpoint_config const &config) -> ligo::tcp::client_options {
return { return {
.timeout = config.timeout, .timeout = config.timeout,
.security = config.security, .security = config.security,
}; };
} }
auto ligo::tcp::server_options_from_config(ligo::endpoint_config const& config) -> ligo::tcp::server_options { auto ligo::tcp::server_options_from_config(ligo::endpoint_config const &config) -> ligo::tcp::server_options {
return { return {
.timeout = config.timeout, .timeout = config.timeout,
.on_error = {}, .on_error = {},
@@ -686,7 +694,7 @@ auto ligo::tcp::connect(boost::asio::any_io_executor executor, ligo::tcp::endpoi
co_return co_await connect_tcp_socket(std::move(executor), std::move(endpoint), options); co_return co_await connect_tcp_socket(std::move(executor), std::move(endpoint), options);
} }
auto ligo::tcp::connect(boost::asio::any_io_executor executor, ligo::endpoint_config const& config) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>> { auto ligo::tcp::connect(boost::asio::any_io_executor executor, ligo::endpoint_config const &config) -> boost::asio::awaitable<std::expected<ligo::tcp::socket_handle, modulus::error>> {
auto endpoint = ligo::tcp::client_endpoint_from_config(config); auto endpoint = ligo::tcp::client_endpoint_from_config(config);
if (!endpoint) { if (!endpoint) {
co_return ligo::detail::fail<ligo::tcp::socket_handle>(endpoint.error()); co_return ligo::detail::fail<ligo::tcp::socket_handle>(endpoint.error());
+69 -65
View File
@@ -48,18 +48,19 @@
#include <ligo/config.hpp> #include <ligo/config.hpp>
#include <ligo/security.hpp> #include <ligo/security.hpp>
namespace { namespace {
struct ssl_ctx_deleter { struct ssl_ctx_deleter {
auto operator()(SSL_CTX* context) const noexcept -> void; auto operator()(SSL_CTX *context) const noexcept -> void;
}; };
struct ssl_deleter { struct ssl_deleter {
auto operator()(SSL* ssl) const noexcept -> void; auto operator()(SSL *ssl) const noexcept -> void;
}; };
struct bio_deleter { struct bio_deleter {
auto operator()(BIO* bio) const noexcept -> void; auto operator()(BIO *bio) const noexcept -> void;
}; };
struct dtls_state { struct dtls_state {
@@ -108,11 +109,11 @@ struct reliable_channel_state {
constexpr auto max_datagram_size = std::size_t{65507}; constexpr auto max_datagram_size = std::size_t{65507};
constexpr auto max_dtls_plaintext_size = std::size_t{12000}; constexpr auto max_dtls_plaintext_size = std::size_t{12000};
auto same_peer(boost::asio::ip::udp::endpoint const& left, boost::asio::ip::udp::endpoint const& right) -> std::bool_t { auto same_peer(boost::asio::ip::udp::endpoint const &left, boost::asio::ip::udp::endpoint const &right) -> std::bool_t {
return left.address() == right.address() && left.port() == right.port(); return left.address() == right.address() && left.port() == right.port();
} }
auto uint_member(boost::json::object const& object, std::string_view name) -> std::optional<std::uint64_t> { auto uint_member(boost::json::object const &object, std::string_view name) -> std::optional<std::uint64_t> {
auto found = object.find(name); auto found = object.find(name);
if (found == object.end() || !found->value().is_number()) { if (found == object.end() || !found->value().is_number()) {
return std::nullopt; return std::nullopt;
@@ -126,7 +127,7 @@ auto uint_member(boost::json::object const& object, std::string_view name) -> st
return found->value().is_uint64() ? found->value().as_uint64() : static_cast<std::uint64_t>(found->value().as_int64()); return found->value().is_uint64() ? found->value().as_uint64() : static_cast<std::uint64_t>(found->value().as_int64());
} }
auto string_member(boost::json::object const& object, std::string_view name) -> std::string { auto string_member(boost::json::object const &object, std::string_view name) -> std::string {
auto found = object.find(name); auto found = object.find(name);
if (found == object.end() || !found->value().is_string()) { if (found == object.end() || !found->value().is_string()) {
return {}; return {};
@@ -151,7 +152,7 @@ auto make_ack(std::uint64_t session_id, std::uint64_t sequence) -> std::string {
return boost::json::serialize(envelope); return boost::json::serialize(envelope);
} }
auto validate_config(ligo::udp_reliability_config const& config) -> std::expected<void, modulus::error> { auto validate_config(ligo::udp_reliability_config const &config) -> std::expected<void, modulus::error> {
if (config.resend_interval <= std::chrono::milliseconds{0}) { if (config.resend_interval <= std::chrono::milliseconds{0}) {
return ligo::detail::fail_void(ligo::detail::invalid_argument("UDP resend_interval must be positive")); return ligo::detail::fail_void(ligo::detail::invalid_argument("UDP resend_interval must be positive"));
} }
@@ -177,7 +178,7 @@ auto dtls_failure(std::string message) -> modulus::error {
return dtls_failure(ligo::detail::error_unavailable, std::move(message)); return dtls_failure(ligo::detail::error_unavailable, std::move(message));
} }
auto validate_security(ligo::security_config const& config, std::bool_t server) -> std::expected<void, modulus::error> { auto validate_security(ligo::security_config const &config, std::bool_t server) -> std::expected<void, modulus::error> {
if (config.mode == ligo::security_mode::TLS) { if (config.mode == ligo::security_mode::TLS) {
return ligo::detail::fail_void(ligo::unsupported_security("UDP", config.mode)); return ligo::detail::fail_void(ligo::unsupported_security("UDP", config.mode));
} }
@@ -196,7 +197,7 @@ auto validate_security(ligo::security_config const& config, std::bool_t server)
return {}; return {};
} }
auto load_dtls_verify_paths(SSL_CTX* context, ligo::security_config const& config) -> std::expected<void, modulus::error> { auto load_dtls_verify_paths(SSL_CTX *context, ligo::security_config const &config) -> std::expected<void, modulus::error> {
if (config.verify == ligo::verify_mode::NONE) { if (config.verify == ligo::verify_mode::NONE) {
return {}; return {};
} }
@@ -212,7 +213,7 @@ auto load_dtls_verify_paths(SSL_CTX* context, ligo::security_config const& confi
return {}; return {};
} }
auto load_dtls_identity(SSL_CTX* context, ligo::security_config const& config) -> std::expected<void, modulus::error> { auto load_dtls_identity(SSL_CTX *context, ligo::security_config const &config) -> std::expected<void, modulus::error> {
if (config.certificate_chain_file.empty() && config.private_key_file.empty()) { if (config.certificate_chain_file.empty() && config.private_key_file.empty()) {
return {}; return {};
} }
@@ -228,7 +229,7 @@ auto load_dtls_identity(SSL_CTX* context, ligo::security_config const& config) -
return {}; return {};
} }
auto make_dtls_state(ligo::security_config const& config, std::bool_t server) -> std::expected<std::unique_ptr<dtls_state>, modulus::error> { auto make_dtls_state(ligo::security_config const &config, std::bool_t server) -> std::expected<std::unique_ptr<dtls_state>, modulus::error> {
if (config.mode != ligo::security_mode::DTLS) { if (config.mode != ligo::security_mode::DTLS) {
return modulus::success(std::unique_ptr<dtls_state>{}); return modulus::success(std::unique_ptr<dtls_state>{});
} }
@@ -265,8 +266,8 @@ auto make_dtls_state(ligo::security_config const& config, std::bool_t server) ->
} }
} }
SSL_set_mtu(ssl.get(), 1200); SSL_set_mtu(ssl.get(), 1200);
auto* ssl_bio = static_cast<BIO*>(nullptr); auto *ssl_bio = static_cast<BIO *>(nullptr);
auto* network_bio = static_cast<BIO*>(nullptr); auto *network_bio = static_cast<BIO *>(nullptr);
if (BIO_new_bio_dgram_pair(&ssl_bio, 0, &network_bio, 0) != 1 || ssl_bio == nullptr || network_bio == nullptr) { if (BIO_new_bio_dgram_pair(&ssl_bio, 0, &network_bio, 0) != 1 || ssl_bio == nullptr || network_bio == nullptr) {
BIO_free(ssl_bio); BIO_free(ssl_bio);
BIO_free(network_bio); BIO_free(network_bio);
@@ -287,9 +288,9 @@ auto make_dtls_state(ligo::security_config const& config, std::bool_t server) ->
})); }));
} }
auto drain_dtls_output(dtls_state& state) -> std::expected<std::vector<std::string>, modulus::error> { auto drain_dtls_output(dtls_state &state) -> std::expected<std::vector<std::string>, modulus::error> {
auto output = std::vector<std::string>{}; auto output = std::vector<std::string>{};
auto* bio = state.network_bio.get(); auto *bio = state.network_bio.get();
while (BIO_ctrl_pending(bio) > 0) { while (BIO_ctrl_pending(bio) > 0) {
auto const pending = static_cast<std::size_t>(BIO_ctrl_pending(bio)); auto const pending = static_cast<std::size_t>(BIO_ctrl_pending(bio));
auto datagram = std::string(std::min(pending, max_datagram_size), '\0'); auto datagram = std::string(std::min(pending, max_datagram_size), '\0');
@@ -303,18 +304,18 @@ auto drain_dtls_output(dtls_state& state) -> std::expected<std::vector<std::stri
return modulus::success(std::move(output)); return modulus::success(std::move(output));
} }
auto append_dtls_output(std::vector<std::string>& target, dtls_state& state) -> std::expected<void, modulus::error> { auto append_dtls_output(std::vector<std::string> &target, dtls_state &state) -> std::expected<void, modulus::error> {
auto drained = drain_dtls_output(state); auto drained = drain_dtls_output(state);
if (!drained) { if (!drained) {
return ligo::detail::fail_void(drained.error()); return ligo::detail::fail_void(drained.error());
} }
for (auto& datagram : *drained) { for (auto &datagram : *drained) {
target.push_back(std::move(datagram)); target.push_back(std::move(datagram));
} }
return {}; return {};
} }
auto run_dtls_handshake(dtls_state& state, std::vector<std::string>& output) -> std::expected<void, modulus::error> { auto run_dtls_handshake(dtls_state &state, std::vector<std::string> &output) -> std::expected<void, modulus::error> {
if (state.handshake_complete) { if (state.handshake_complete) {
return {}; return {};
} }
@@ -331,7 +332,7 @@ auto run_dtls_handshake(dtls_state& state, std::vector<std::string>& output) ->
return ligo::detail::fail_void(dtls_failure("UDP DTLS handshake failed")); return ligo::detail::fail_void(dtls_failure("UDP DTLS handshake failed"));
} }
auto protect_dtls_payload(reliable_channel_state& state, std::string_view plaintext) -> std::expected<std::vector<std::string>, modulus::error> { auto protect_dtls_payload(reliable_channel_state &state, std::string_view plaintext) -> std::expected<std::vector<std::string>, modulus::error> {
if (!state.dtls) { if (!state.dtls) {
return modulus::success(std::vector<std::string>{ return modulus::success(std::vector<std::string>{
std::string{plaintext}, std::string{plaintext},
@@ -362,7 +363,7 @@ auto protect_dtls_payload(reliable_channel_state& state, std::string_view plaint
return modulus::success(std::move(output)); return modulus::success(std::move(output));
} }
auto process_dtls_input(reliable_channel_state& state, std::string_view datagram) -> std::expected<dtls_processed, modulus::error> { auto process_dtls_input(reliable_channel_state &state, std::string_view datagram) -> std::expected<dtls_processed, modulus::error> {
auto processed = dtls_processed{}; auto processed = dtls_processed{};
if (!state.dtls) { if (!state.dtls) {
processed.plaintext.push_back(std::string{datagram}); processed.plaintext.push_back(std::string{datagram});
@@ -400,28 +401,28 @@ auto process_dtls_input(reliable_channel_state& state, std::string_view datagram
return modulus::success(std::move(processed)); return modulus::success(std::move(processed));
} }
auto channel_state(ligo::detail::state_handle const& handle) -> std::shared_ptr<reliable_channel_state> { auto channel_state(ligo::detail::state_handle const &handle) -> std::shared_ptr<reliable_channel_state> {
return handle.as<reliable_channel_state>(); return handle.as<reliable_channel_state>();
} }
auto notify_waiters(std::shared_ptr<reliable_channel_state> const& state) -> void { auto notify_waiters(std::shared_ptr<reliable_channel_state> const &state) -> void {
state->notify_timer.cancel(); state->notify_timer.cancel();
} }
auto notify_close_waiters(std::shared_ptr<reliable_channel_state> const& state) -> void { auto notify_close_waiters(std::shared_ptr<reliable_channel_state> const &state) -> void {
state->close_timer.cancel(); state->close_timer.cancel();
} }
auto notify_send_waiters(std::shared_ptr<reliable_channel_state> const& state) -> void { auto notify_send_waiters(std::shared_ptr<reliable_channel_state> const &state) -> void {
for (auto& [_, waiter] : state->ack_waiters) { for (auto &[_, waiter] : state->ack_waiters) {
if (auto timer = waiter.lock()) { if (auto timer = waiter.lock()) {
timer->cancel(); timer->cancel();
} }
} }
} }
auto send_wire_datagrams(std::shared_ptr<reliable_channel_state> const& state, std::vector<std::string> const& datagrams) -> boost::asio::awaitable<std::expected<void, modulus::error>> { auto send_wire_datagrams(std::shared_ptr<reliable_channel_state> const &state, std::vector<std::string> const &datagrams) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
for (auto const& datagram : datagrams) { for (auto const &datagram : datagrams) {
auto [send_error, _] = co_await state->socket->async_send_to(boost::asio::buffer(datagram), state->peer, boost::asio::bind_executor(state->strand, boost::asio::as_tuple(boost::asio::use_awaitable))); auto [send_error, _] = co_await state->socket->async_send_to(boost::asio::buffer(datagram), state->peer, boost::asio::bind_executor(state->strand, boost::asio::as_tuple(boost::asio::use_awaitable)));
if (send_error) { if (send_error) {
co_return ligo::detail::fail_void(ligo::detail::network_failure(send_error)); co_return ligo::detail::fail_void(ligo::detail::network_failure(send_error));
@@ -430,7 +431,7 @@ auto send_wire_datagrams(std::shared_ptr<reliable_channel_state> const& state, s
co_return std::expected<void, modulus::error>{}; co_return std::expected<void, modulus::error>{};
} }
auto send_ack(std::shared_ptr<reliable_channel_state> const& state, std::uint64_t sequence) -> boost::asio::awaitable<std::expected<void, modulus::error>> { auto send_ack(std::shared_ptr<reliable_channel_state> const &state, std::uint64_t sequence) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
auto ack_body = make_ack(state->session_id, sequence); auto ack_body = make_ack(state->session_id, sequence);
auto datagrams = protect_dtls_payload(*state, ack_body); auto datagrams = protect_dtls_payload(*state, ack_body);
if (!datagrams) { if (!datagrams) {
@@ -439,7 +440,7 @@ auto send_ack(std::shared_ptr<reliable_channel_state> const& state, std::uint64_
co_return co_await send_wire_datagrams(state, *datagrams); co_return co_await send_wire_datagrams(state, *datagrams);
} }
auto close_state(std::shared_ptr<reliable_channel_state> const& state) -> void { auto close_state(std::shared_ptr<reliable_channel_state> const &state) -> void {
if (!state || state->closed) { if (!state || state->closed) {
return; return;
} }
@@ -487,9 +488,9 @@ auto receive_loop(std::shared_ptr<reliable_channel_state> state) -> boost::asio:
continue; continue;
} }
auto processed = process_dtls_input(*state, std::string_view{ auto processed = process_dtls_input(*state, std::string_view{
state->receive_buffer.data(), state->receive_buffer.data(),
size, size,
}); });
if (!processed) { if (!processed) {
state->receive_error = processed.error(); state->receive_error = processed.error();
notify_send_waiters(state); notify_send_waiters(state);
@@ -503,13 +504,13 @@ auto receive_loop(std::shared_ptr<reliable_channel_state> state) -> boost::asio:
notify_waiters(state); notify_waiters(state);
continue; continue;
} }
for (auto& plaintext : processed->plaintext) { for (auto &plaintext : processed->plaintext) {
auto parse_error = boost::system::error_code{}; auto parse_error = boost::system::error_code{};
auto envelope = boost::json::parse(plaintext, parse_error); auto envelope = boost::json::parse(plaintext, parse_error);
if (parse_error || !envelope.is_object()) { if (parse_error || !envelope.is_object()) {
continue; continue;
} }
auto const& object = envelope.as_object(); auto const &object = envelope.as_object();
auto session = uint_member(object, "session"); auto session = uint_member(object, "session");
auto sequence = uint_member(object, "sequence"); auto sequence = uint_member(object, "sequence");
if (!session.has_value() || *session != state->session_id || !sequence.has_value()) { if (!session.has_value() || *session != state->session_id || !sequence.has_value()) {
@@ -563,7 +564,7 @@ auto receive_loop(std::shared_ptr<reliable_channel_state> state) -> boost::asio:
} }
} }
auto ensure_receive_loop(std::shared_ptr<reliable_channel_state> const& state) -> void { auto ensure_receive_loop(std::shared_ptr<reliable_channel_state> const &state) -> void {
if (state->receive_loop_started || state->closed) { if (state->receive_loop_started || state->closed) {
return; return;
} }
@@ -571,7 +572,7 @@ auto ensure_receive_loop(std::shared_ptr<reliable_channel_state> const& state) -
boost::asio::co_spawn(state->strand, receive_loop(state), boost::asio::detached); boost::asio::co_spawn(state->strand, receive_loop(state), boost::asio::detached);
} }
auto begin_operation(std::shared_ptr<reliable_channel_state> const& state) -> std::expected<void, modulus::error> { auto begin_operation(std::shared_ptr<reliable_channel_state> const &state) -> std::expected<void, modulus::error> {
if (state->closed || state->close_requested.load(std::memory_order_acquire)) { if (state->closed || state->close_requested.load(std::memory_order_acquire)) {
return ligo::detail::fail_void(ligo::detail::unavailable("UDP channel closed")); return ligo::detail::fail_void(ligo::detail::unavailable("UDP channel closed"));
} }
@@ -580,7 +581,7 @@ auto begin_operation(std::shared_ptr<reliable_channel_state> const& state) -> st
return {}; return {};
} }
auto finish_operation(std::shared_ptr<reliable_channel_state> const& state) -> void { auto finish_operation(std::shared_ptr<reliable_channel_state> const &state) -> void {
if (state->active_operations > 0) { if (state->active_operations > 0) {
--state->active_operations; --state->active_operations;
} }
@@ -593,7 +594,7 @@ auto finish_operation(std::shared_ptr<reliable_channel_state> const& state) -> v
} }
} }
auto finish_send(std::shared_ptr<reliable_channel_state> const& state, std::uint64_t sequence) -> void { auto finish_send(std::shared_ptr<reliable_channel_state> const &state, std::uint64_t sequence) -> void {
if (state->in_flight > 0) { if (state->in_flight > 0) {
--state->in_flight; --state->in_flight;
} }
@@ -603,7 +604,7 @@ auto finish_send(std::shared_ptr<reliable_channel_state> const& state, std::uint
finish_operation(state); finish_operation(state);
} }
auto udp_endpoint_from_parts(ligo::detail::endpoint_parts const& parts) -> std::expected<boost::asio::ip::udp::endpoint, modulus::error> { auto udp_endpoint_from_parts(ligo::detail::endpoint_parts const &parts) -> std::expected<boost::asio::ip::udp::endpoint, modulus::error> {
auto error = boost::system::error_code{}; auto error = boost::system::error_code{};
auto address = boost::asio::ip::make_address(parts.host, error); auto address = boost::asio::ip::make_address(parts.host, error);
if (error) { if (error) {
@@ -704,7 +705,6 @@ auto send_payload_on_strand(std::shared_ptr<reliable_channel_state> state, boost
finish_send(state, sequence); finish_send(state, sequence);
co_return ligo::detail::fail_void(sent.error()); co_return ligo::detail::fail_void(sent.error());
} }
if (state->acked.erase(sequence) > 0) { if (state->acked.erase(sequence) > 0) {
finish_send(state, sequence); finish_send(state, sequence);
co_return std::expected<void, modulus::error>{}; co_return std::expected<void, modulus::error>{};
@@ -813,7 +813,7 @@ auto receive_message(std::shared_ptr<reliable_channel_state> state) -> boost::as
}); });
} }
auto local_endpoint_for(std::shared_ptr<reliable_channel_state> const& state) -> std::expected<boost::asio::ip::udp::endpoint, modulus::error> { auto local_endpoint_for(std::shared_ptr<reliable_channel_state> const &state) -> std::expected<boost::asio::ip::udp::endpoint, modulus::error> {
if (!state || !state->socket) { if (!state || !state->socket) {
return ligo::detail::fail<boost::asio::ip::udp::endpoint>(ligo::detail::error_unavailable, "UDP channel is not initialized"); return ligo::detail::fail<boost::asio::ip::udp::endpoint>(ligo::detail::error_unavailable, "UDP channel is not initialized");
} }
@@ -830,7 +830,7 @@ auto close_channel(std::shared_ptr<reliable_channel_state> state) -> void {
}); });
} }
auto channel_drained(std::shared_ptr<reliable_channel_state> const& state) -> std::bool_t { auto channel_drained(std::shared_ptr<reliable_channel_state> const &state) -> std::bool_t {
return !state || (state->active_operations == 0 && !state->receive_loop_started); return !state || (state->active_operations == 0 && !state->receive_loop_started);
} }
@@ -842,7 +842,7 @@ auto async_close_channel_on_strand(std::shared_ptr<reliable_channel_state> state
auto deadline_timer = std::make_shared<boost::asio::steady_timer>(state->strand); auto deadline_timer = std::make_shared<boost::asio::steady_timer>(state->strand);
auto timed_out = std::make_shared<std::atomic<std::bool_t>>(false); auto timed_out = std::make_shared<std::atomic<std::bool_t>>(false);
deadline_timer->expires_after(timeout); deadline_timer->expires_after(timeout);
deadline_timer->async_wait([state, deadline_timer, timed_out](boost::system::error_code const& error) { deadline_timer->async_wait([state, deadline_timer, timed_out](boost::system::error_code const &error) {
if (!error) { if (!error) {
timed_out->store(true, std::memory_order_release); timed_out->store(true, std::memory_order_release);
notify_close_waiters(state); notify_close_waiters(state);
@@ -876,7 +876,7 @@ auto async_close_channel(std::shared_ptr<reliable_channel_state> state, std::chr
co_return co_await boost::asio::co_spawn(state->strand, async_close_channel_on_strand(state, timeout), boost::asio::use_awaitable); co_return co_await boost::asio::co_spawn(state->strand, async_close_channel_on_strand(state, timeout), boost::asio::use_awaitable);
} }
auto endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::udp::endpoint, modulus::error> { auto endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::udp::endpoint, modulus::error> {
auto local_parts = ligo::detail::parse_server_endpoint_text(config.bind); auto local_parts = ligo::detail::parse_server_endpoint_text(config.bind);
if (!local_parts) { if (!local_parts) {
return ligo::detail::fail<ligo::udp::endpoint>(local_parts.error()); return ligo::detail::fail<ligo::udp::endpoint>(local_parts.error());
@@ -899,19 +899,20 @@ auto endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<
}); });
} }
auto ssl_ctx_deleter::operator()(SSL_CTX* context) const noexcept -> void { auto ssl_ctx_deleter::operator()(SSL_CTX *context) const noexcept -> void {
SSL_CTX_free(context); SSL_CTX_free(context);
} }
auto ssl_deleter::operator()(SSL* ssl) const noexcept -> void { auto ssl_deleter::operator()(SSL *ssl) const noexcept -> void {
SSL_free(ssl); SSL_free(ssl);
} }
auto bio_deleter::operator()(BIO* bio) const noexcept -> void { auto bio_deleter::operator()(BIO *bio) const noexcept -> void {
BIO_free(bio); BIO_free(bio);
} }
reliable_channel_state::reliable_channel_state(std::unique_ptr<boost::asio::ip::udp::socket> socket, boost::asio::ip::udp::endpoint local_endpoint, boost::asio::ip::udp::endpoint peer, ligo::udp_reliability_config config, ligo::security_config security, std::uint64_t session_id, std::unique_ptr<dtls_state> dtls) : socket{std::move(socket)}, local_endpoint{std::move(local_endpoint)}, peer{std::move(peer)}, config{config}, security{std::move(security)}, dtls{std::move(dtls)}, session_id{session_id} { reliable_channel_state::reliable_channel_state(std::unique_ptr<boost::asio::ip::udp::socket> socket, boost::asio::ip::udp::endpoint local_endpoint, boost::asio::ip::udp::endpoint peer, ligo::udp_reliability_config config, ligo::security_config security, std::uint64_t session_id, std::unique_ptr<dtls_state> dtls)
: socket{std::move(socket)}, local_endpoint{std::move(local_endpoint)}, peer{std::move(peer)}, config{config}, security{std::move(security)}, dtls{std::move(dtls)}, session_id{session_id} {
notify_timer.expires_at(std::chrono::steady_clock::time_point::max()); notify_timer.expires_at(std::chrono::steady_clock::time_point::max());
close_timer.expires_at(std::chrono::steady_clock::time_point::max()); close_timer.expires_at(std::chrono::steady_clock::time_point::max());
} }
@@ -922,7 +923,7 @@ ligo::udp::client::~client() {
close(); close();
} }
auto ligo::udp::client::operator=(ligo::udp::client&& other) noexcept -> ligo::udp::client& { auto ligo::udp::client::operator=(ligo::udp::client &&other) noexcept -> ligo::udp::client & {
if (this != &other) { if (this != &other) {
close(); close();
state_ = std::move(other.state_); state_ = std::move(other.state_);
@@ -932,15 +933,16 @@ auto ligo::udp::client::operator=(ligo::udp::client&& other) noexcept -> ligo::u
auto ligo::udp::client::open(boost::asio::any_io_executor executor, boost::asio::ip::udp::endpoint peer, ligo::udp::client_options options) -> std::expected<ligo::udp::client, modulus::error> { auto ligo::udp::client::open(boost::asio::any_io_executor executor, boost::asio::ip::udp::endpoint peer, ligo::udp::client_options options) -> std::expected<ligo::udp::client, modulus::error> {
return ligo::udp::client::bind(std::move(executor), { return ligo::udp::client::bind(std::move(executor), {
.local = { .local = {
peer.protocol(), peer.protocol(),
0, 0,
}, },
.peer = std::move(peer), .peer = std::move(peer),
}, options); },
options);
} }
auto ligo::udp::client::open(boost::asio::io_context& io, boost::asio::ip::udp::endpoint peer, ligo::udp::client_options options) -> std::expected<ligo::udp::client, modulus::error> { auto ligo::udp::client::open(boost::asio::io_context &io, boost::asio::ip::udp::endpoint peer, ligo::udp::client_options options) -> std::expected<ligo::udp::client, modulus::error> {
return ligo::udp::client::open(io.get_executor(), std::move(peer), options); return ligo::udp::client::open(io.get_executor(), std::move(peer), options);
} }
@@ -954,7 +956,7 @@ auto ligo::udp::client::bind(boost::asio::any_io_executor executor, ligo::udp::e
}); });
} }
auto ligo::udp::client::bind(boost::asio::io_context& io, ligo::udp::endpoint endpoint, ligo::udp::client_options options) -> std::expected<ligo::udp::client, modulus::error> { auto ligo::udp::client::bind(boost::asio::io_context &io, ligo::udp::endpoint endpoint, ligo::udp::client_options options) -> std::expected<ligo::udp::client, modulus::error> {
return ligo::udp::client::bind(io.get_executor(), std::move(endpoint), options); return ligo::udp::client::bind(io.get_executor(), std::move(endpoint), options);
} }
@@ -992,13 +994,14 @@ auto ligo::udp::client::async_close(std::chrono::milliseconds timeout) -> boost:
co_return co_await async_close_channel(channel_state(state_), timeout); co_return co_await async_close_channel(channel_state(state_), timeout);
} }
ligo::udp::client::client(ligo::detail::state_handle state) : state_{std::move(state)} {} ligo::udp::client::client(ligo::detail::state_handle state)
: state_{std::move(state)} {}
ligo::udp::server::~server() { ligo::udp::server::~server() {
close(); close();
} }
auto ligo::udp::server::operator=(ligo::udp::server&& other) noexcept -> ligo::udp::server& { auto ligo::udp::server::operator=(ligo::udp::server &&other) noexcept -> ligo::udp::server & {
if (this != &other) { if (this != &other) {
close(); close();
state_ = std::move(other.state_); state_ = std::move(other.state_);
@@ -1016,7 +1019,7 @@ auto ligo::udp::server::bind(boost::asio::any_io_executor executor, ligo::udp::e
}); });
} }
auto ligo::udp::server::bind(boost::asio::io_context& io, ligo::udp::endpoint endpoint, ligo::udp::server_options options) -> std::expected<ligo::udp::server, modulus::error> { auto ligo::udp::server::bind(boost::asio::io_context &io, ligo::udp::endpoint endpoint, ligo::udp::server_options options) -> std::expected<ligo::udp::server, modulus::error> {
return ligo::udp::server::bind(io.get_executor(), std::move(endpoint), options); return ligo::udp::server::bind(io.get_executor(), std::move(endpoint), options);
} }
@@ -1054,24 +1057,25 @@ auto ligo::udp::server::async_close(std::chrono::milliseconds timeout) -> boost:
co_return co_await async_close_channel(channel_state(state_), timeout); co_return co_await async_close_channel(channel_state(state_), timeout);
} }
ligo::udp::server::server(ligo::detail::state_handle state) : state_{std::move(state)} {} ligo::udp::server::server(ligo::detail::state_handle state)
: state_{std::move(state)} {}
auto ligo::udp::client_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::udp::endpoint, modulus::error> { auto ligo::udp::client_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::udp::endpoint, modulus::error> {
return endpoint_from_config(config); return endpoint_from_config(config);
} }
auto ligo::udp::server_endpoint_from_config(ligo::endpoint_config const& config) -> std::expected<ligo::udp::endpoint, modulus::error> { auto ligo::udp::server_endpoint_from_config(ligo::endpoint_config const &config) -> std::expected<ligo::udp::endpoint, modulus::error> {
return endpoint_from_config(config); return endpoint_from_config(config);
} }
auto ligo::udp::client_options_from_config(ligo::endpoint_config const& config) -> ligo::udp::client_options { auto ligo::udp::client_options_from_config(ligo::endpoint_config const &config) -> ligo::udp::client_options {
return { return {
.reliability = config.udp, .reliability = config.udp,
.security = config.security, .security = config.security,
}; };
} }
auto ligo::udp::server_options_from_config(ligo::endpoint_config const& config) -> ligo::udp::server_options { auto ligo::udp::server_options_from_config(ligo::endpoint_config const &config) -> ligo::udp::server_options {
return { return {
.reliability = config.udp, .reliability = config.udp,
.security = config.security, .security = config.security,
+202 -200
View File
@@ -45,6 +45,7 @@
#include <test.hpp> #include <test.hpp>
BOOST_AUTO_TEST_CASE(ligo_asserts_follow_build_type) { BOOST_AUTO_TEST_CASE(ligo_asserts_follow_build_type) {
#if defined(LIGO_EXPECT_NDEBUG) && !defined(NDEBUG) #if defined(LIGO_EXPECT_NDEBUG) && !defined(NDEBUG)
BOOST_FAIL("release-style builds must define NDEBUG"); BOOST_FAIL("release-style builds must define NDEBUG");
@@ -54,14 +55,13 @@ BOOST_AUTO_TEST_CASE(ligo_asserts_follow_build_type) {
#endif #endif
} }
namespace { namespace {
auto test_network_failure(boost::system::error_code const& error) -> modulus::error { auto test_network_failure(boost::system::error_code const &error) -> modulus::error {
return ligo::detail::make_error(ligo::detail::error_unavailable, error.message()); return ligo::detail::make_error(ligo::detail::error_unavailable, error.message());
} }
auto run_two_threads(boost::asio::io_context& io) -> void { auto run_two_threads(boost::asio::io_context &io) -> void {
auto first = std::thread{[&] { auto first = std::thread{[&] {
io.run(); io.run();
}}; }};
@@ -72,7 +72,7 @@ auto run_two_threads(boost::asio::io_context& io) -> void {
second.join(); second.join();
} }
auto start_io_threads(boost::asio::io_context& io) -> std::vector<std::thread> { auto start_io_threads(boost::asio::io_context &io) -> std::vector<std::thread> {
auto threads = std::vector<std::thread>{}; auto threads = std::vector<std::thread>{};
threads.emplace_back([&] { threads.emplace_back([&] {
io.run(); io.run();
@@ -83,8 +83,8 @@ auto start_io_threads(boost::asio::io_context& io) -> std::vector<std::thread> {
return threads; return threads;
} }
auto join_io_threads(std::vector<std::thread>& threads) -> void { auto join_io_threads(std::vector<std::thread> &threads) -> void {
for (auto& thread : threads) { for (auto &thread : threads) {
thread.join(); thread.join();
} }
} }
@@ -239,7 +239,7 @@ auto test_dtls_server_verify_client_config() -> ligo::security_config {
}; };
} }
auto http_client_call(std::string host, std::uint16_t port, ligo::http::request const& request, ligo::http::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto http_client_call(std::string host, std::uint16_t port, ligo::http::request const &request, ligo::http::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
auto client = ligo::http::client{ auto client = ligo::http::client{
{ {
.host = std::move(host), .host = std::move(host),
@@ -250,21 +250,21 @@ auto http_client_call(std::string host, std::uint16_t port, ligo::http::request
return client.call(request); return client.call(request);
} }
auto http_client_call_selected(ligo::http::server& server, ligo::http::request const& request, ligo::http::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto http_client_call_selected(ligo::http::server &server, ligo::http::request const &request, ligo::http::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
return http_client_call("127.0.0.1", server.selected_port(), request, options); return http_client_call("127.0.0.1", server.selected_port(), request, options);
} }
auto http_client_call_selected_and_stop_impl(ligo::http::server& server, std::shared_ptr<ligo::http::request const> request, ligo::http::client_options options) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto http_client_call_selected_and_stop_impl(ligo::http::server &server, std::shared_ptr<ligo::http::request const> request, ligo::http::client_options options) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
auto result = co_await http_client_call_selected(server, *request, options); auto result = co_await http_client_call_selected(server, *request, options);
server.stop(); server.stop();
co_return result; co_return result;
} }
auto http_client_call_selected_and_stop(ligo::http::server& server, ligo::http::request const& request, ligo::http::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto http_client_call_selected_and_stop(ligo::http::server &server, ligo::http::request const &request, ligo::http::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
return http_client_call_selected_and_stop_impl(server, std::make_shared<ligo::http::request const>(request), options); return http_client_call_selected_and_stop_impl(server, std::make_shared<ligo::http::request const>(request), options);
} }
auto http_async_stop_after(ligo::http::server& server, std::chrono::milliseconds delay, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> { auto http_async_stop_after(ligo::http::server &server, std::chrono::milliseconds delay, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
auto timer = boost::asio::steady_timer{co_await boost::asio::this_coro::executor}; auto timer = boost::asio::steady_timer{co_await boost::asio::this_coro::executor};
timer.expires_after(delay); timer.expires_after(delay);
co_await timer.async_wait(boost::asio::as_tuple(boost::asio::use_awaitable)); co_await timer.async_wait(boost::asio::as_tuple(boost::asio::use_awaitable));
@@ -315,7 +315,7 @@ auto raw_http_post(std::uint16_t port, std::string target, std::string body) ->
co_return modulus::success(std::move(value)); co_return modulus::success(std::move(value));
} }
auto raw_http_post_selected_and_stop(ligo::http::server& server, std::string target, std::string body) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto raw_http_post_selected_and_stop(ligo::http::server &server, std::string target, std::string body) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
auto result = co_await raw_http_post(server.selected_port(), std::move(target), std::move(body)); auto result = co_await raw_http_post(server.selected_port(), std::move(target), std::move(body));
server.stop(); server.stop();
co_return result; co_return result;
@@ -357,13 +357,13 @@ auto raw_http_get(std::uint16_t port, std::string target) -> boost::asio::awaita
co_return modulus::success(std::move(response)); co_return modulus::success(std::move(response));
} }
auto raw_http_get_selected_and_stop(ligo::http::server& server, std::string target) -> boost::asio::awaitable<std::expected<boost::beast::http::response<boost::beast::http::string_body>, modulus::error>> { auto raw_http_get_selected_and_stop(ligo::http::server &server, std::string target) -> boost::asio::awaitable<std::expected<boost::beast::http::response<boost::beast::http::string_body>, modulus::error>> {
auto result = co_await raw_http_get(server.selected_port(), std::move(target)); auto result = co_await raw_http_get(server.selected_port(), std::move(target));
server.stop(); server.stop();
co_return result; co_return result;
} }
auto tcp_client_call(std::string host, std::uint16_t port, ligo::tcp::request const& request, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto tcp_client_call(std::string host, std::uint16_t port, ligo::tcp::request const &request, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
auto client = ligo::tcp::client{ auto client = ligo::tcp::client{
{ {
.host = std::move(host), .host = std::move(host),
@@ -374,21 +374,21 @@ auto tcp_client_call(std::string host, std::uint16_t port, ligo::tcp::request co
return client.call(request); return client.call(request);
} }
auto tcp_client_call_selected(ligo::tcp::server& server, ligo::tcp::request const& request, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto tcp_client_call_selected(ligo::tcp::server &server, ligo::tcp::request const &request, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
return tcp_client_call("127.0.0.1", server.selected_port(), request, options); return tcp_client_call("127.0.0.1", server.selected_port(), request, options);
} }
auto tcp_client_call_selected_and_stop_impl(ligo::tcp::server& server, std::shared_ptr<ligo::tcp::request const> request, ligo::tcp::client_options options) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto tcp_client_call_selected_and_stop_impl(ligo::tcp::server &server, std::shared_ptr<ligo::tcp::request const> request, ligo::tcp::client_options options) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
auto result = co_await tcp_client_call_selected(server, *request, options); auto result = co_await tcp_client_call_selected(server, *request, options);
server.stop(); server.stop();
co_return result; co_return result;
} }
auto tcp_client_call_selected_and_stop(ligo::tcp::server& server, ligo::tcp::request const& request, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto tcp_client_call_selected_and_stop(ligo::tcp::server &server, ligo::tcp::request const &request, ligo::tcp::client_options options = {}) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
return tcp_client_call_selected_and_stop_impl(server, std::make_shared<ligo::tcp::request const>(request), options); return tcp_client_call_selected_and_stop_impl(server, std::make_shared<ligo::tcp::request const>(request), options);
} }
auto tcp_async_stop_after(ligo::tcp::server& server, std::chrono::milliseconds delay, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> { auto tcp_async_stop_after(ligo::tcp::server &server, std::chrono::milliseconds delay, std::chrono::milliseconds timeout) -> boost::asio::awaitable<std::expected<void, modulus::error>> {
auto timer = boost::asio::steady_timer{co_await boost::asio::this_coro::executor}; auto timer = boost::asio::steady_timer{co_await boost::asio::this_coro::executor};
timer.expires_after(delay); timer.expires_after(delay);
co_await timer.async_wait(boost::asio::as_tuple(boost::asio::use_awaitable)); co_await timer.async_wait(boost::asio::as_tuple(boost::asio::use_awaitable));
@@ -405,31 +405,32 @@ auto accept_and_hold(std::shared_ptr<boost::asio::ip::tcp::acceptor> acceptor, s
co_await timer.async_wait(boost::asio::as_tuple(boost::asio::use_awaitable)); co_await timer.async_wait(boost::asio::as_tuple(boost::asio::use_awaitable));
} }
auto make_udp_socket(boost::asio::io_context& io) -> std::unique_ptr<boost::asio::ip::udp::socket> { auto make_udp_socket(boost::asio::io_context &io) -> std::unique_ptr<boost::asio::ip::udp::socket> {
return std::make_unique<boost::asio::ip::udp::socket>(io, boost::asio::ip::udp::endpoint{ return std::make_unique<boost::asio::ip::udp::socket>(io, boost::asio::ip::udp::endpoint{
boost::asio::ip::udp::v4(), boost::asio::ip::udp::v4(),
0, 0,
}); });
} }
auto udp_client_adopt(std::unique_ptr<boost::asio::ip::udp::socket> socket, boost::asio::ip::udp::endpoint peer, ligo::udp_reliability_config config = {}, std::uint64_t session_id = 1) -> std::expected<ligo::udp::client, modulus::error> { auto udp_client_adopt(std::unique_ptr<boost::asio::ip::udp::socket> socket, boost::asio::ip::udp::endpoint peer, ligo::udp_reliability_config config = {}, std::uint64_t session_id = 1) -> std::expected<ligo::udp::client, modulus::error> {
return ligo::udp::client::adopt(std::move(socket), std::move(peer), { return ligo::udp::client::adopt(std::move(socket), std::move(peer), {
.reliability = config, .reliability = config,
.session_id = session_id, .session_id = session_id,
}); });
} }
auto udp_client_bind(boost::asio::io_context& io, boost::asio::ip::udp::endpoint local, boost::asio::ip::udp::endpoint peer, ligo::udp_reliability_config config = {}, std::uint64_t session_id = 1) -> std::expected<ligo::udp::client, modulus::error> { auto udp_client_bind(boost::asio::io_context &io, boost::asio::ip::udp::endpoint local, boost::asio::ip::udp::endpoint peer, ligo::udp_reliability_config config = {}, std::uint64_t session_id = 1) -> std::expected<ligo::udp::client, modulus::error> {
return ligo::udp::client::bind(io, { return ligo::udp::client::bind(io, {
.local = std::move(local), .local = std::move(local),
.peer = std::move(peer), .peer = std::move(peer),
}, { },
.reliability = config, {
.session_id = session_id, .reliability = config,
}); .session_id = session_id,
});
} }
auto config_http_tcp_calls_and_stop(ligo::http::server& http_server, ligo::tcp::server& tcp_server) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> { auto config_http_tcp_calls_and_stop(ligo::http::server &http_server, ligo::tcp::server &tcp_server) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
auto tcp_payload = boost::json::object{}; auto tcp_payload = boost::json::object{};
tcp_payload["ok"] = true; tcp_payload["ok"] = true;
auto tcp_request = std::make_shared<ligo::tcp::request>(); auto tcp_request = std::make_shared<ligo::tcp::request>();
@@ -471,8 +472,9 @@ BOOST_AUTO_TEST_CASE(ligo_config_drives_http_tcp_and_udp_setup) {
udp_server_socket->local_endpoint().port(), udp_server_socket->local_endpoint().port(),
}; };
auto config_text = std::string{ auto config_text = std::string{
"{\"endpoints\":[", "{\"endpoints\":[",
}+ "{\"name\":\"http\",\"protocol\":\"http\",\"bind\":\"127.0.0.1:0\",\"timeout_ms\":1000}," + "{\"name\":\"tcp\",\"protocol\":\"tcp\",\"bind\":\"127.0.0.1:0\",\"timeout_ms\":1000}," + "{\"name\":\"udp-client\",\"protocol\":\"udp\",\"bind\":\"127.0.0.1:" + std::to_string(udp_client_endpoint.port()) + "\",\"connect\":\"127.0.0.1:" + std::to_string(udp_server_endpoint.port()) + "\",\"udp\":{\"resend_interval_ms\":5,\"max_in_flight\":2,\"session_timeout_ms\":1000}}," + "{\"name\":\"udp-server\",\"protocol\":\"udp\",\"bind\":\"127.0.0.1:" + std::to_string(udp_server_endpoint.port()) + "\",\"connect\":\"127.0.0.1:" + std::to_string(udp_client_endpoint.port()) + "\",\"udp\":{\"resend_interval_ms\":5,\"max_in_flight\":2,\"session_timeout_ms\":1000}}" + "]}"; } +
"{\"name\":\"http\",\"protocol\":\"http\",\"bind\":\"127.0.0.1:0\",\"timeout_ms\":1000}," + "{\"name\":\"tcp\",\"protocol\":\"tcp\",\"bind\":\"127.0.0.1:0\",\"timeout_ms\":1000}," + "{\"name\":\"udp-client\",\"protocol\":\"udp\",\"bind\":\"127.0.0.1:" + std::to_string(udp_client_endpoint.port()) + "\",\"connect\":\"127.0.0.1:" + std::to_string(udp_server_endpoint.port()) + "\",\"udp\":{\"resend_interval_ms\":5,\"max_in_flight\":2,\"session_timeout_ms\":1000}}," + "{\"name\":\"udp-server\",\"protocol\":\"udp\",\"bind\":\"127.0.0.1:" + std::to_string(udp_server_endpoint.port()) + "\",\"connect\":\"127.0.0.1:" + std::to_string(udp_client_endpoint.port()) + "\",\"udp\":{\"resend_interval_ms\":5,\"max_in_flight\":2,\"session_timeout_ms\":1000}}" + "]}";
auto configs = ligo::parse_endpoint_configs(boost::json::parse(config_text)); auto configs = ligo::parse_endpoint_configs(boost::json::parse(config_text));
BOOST_REQUIRE(configs); BOOST_REQUIRE(configs);
BOOST_REQUIRE((configs->size()) == (4u)); BOOST_REQUIRE((configs->size()) == (4u));
@@ -625,8 +627,7 @@ BOOST_AUTO_TEST_CASE(ligo_http_post_round_trip_dispatches_json_handler) {
{ {
.target = "/echo", .target = "/echo",
.payload = boost::json::parse(R"json({"ok":true})json"), .payload = boost::json::parse(R"json({"ok":true})json"),
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
@@ -690,8 +691,7 @@ BOOST_AUTO_TEST_CASE(ligo_http_tls_post_round_trip_dispatches_json_handler) {
{ {
.timeout = std::chrono::milliseconds{1000}, .timeout = std::chrono::milliseconds{1000},
.security = test_tls_client_config(), .security = test_tls_client_config(),
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
@@ -764,9 +764,10 @@ BOOST_AUTO_TEST_CASE(ligo_http_get_round_trip_dispatches_method_and_route) {
}; };
BOOST_REQUIRE(server.start()); BOOST_REQUIRE(server.start());
auto future = boost::asio::co_spawn(io, http_client_call_selected_and_stop(server, { auto future = boost::asio::co_spawn(io, http_client_call_selected_and_stop(server, {
.method = ligo::http::method::GET, .method = ligo::http::method::GET,
.target = "/method", .target = "/method",
}), boost::asio::use_future); }),
boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
BOOST_REQUIRE(result); BOOST_REQUIRE(result);
@@ -786,9 +787,10 @@ BOOST_AUTO_TEST_CASE(ligo_http_missing_route_returns_status) {
}; };
BOOST_REQUIRE(server.start()); BOOST_REQUIRE(server.start());
auto future = boost::asio::co_spawn(io, http_client_call_selected_and_stop(server, { auto future = boost::asio::co_spawn(io, http_client_call_selected_and_stop(server, {
.target = "/missing", .target = "/missing",
.payload = boost::json::object{}, .payload = boost::json::object{},
}), boost::asio::use_future); }),
boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
BOOST_CHECK(!(result)); BOOST_CHECK(!(result));
@@ -811,9 +813,10 @@ BOOST_AUTO_TEST_CASE(ligo_http_handler_failure_returns_status) {
}; };
BOOST_REQUIRE(server.start()); BOOST_REQUIRE(server.start());
auto future = boost::asio::co_spawn(io, http_client_call_selected_and_stop(server, { auto future = boost::asio::co_spawn(io, http_client_call_selected_and_stop(server, {
.target = "/boom", .target = "/boom",
.payload = boost::json::object{}, .payload = boost::json::object{},
}), boost::asio::use_future); }),
boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
BOOST_CHECK(!(result)); BOOST_CHECK(!(result));
@@ -895,8 +898,7 @@ BOOST_AUTO_TEST_CASE(ligo_http_client_rejects_oversized_response_body) {
{ {
.timeout = std::chrono::milliseconds{1000}, .timeout = std::chrono::milliseconds{1000},
.max_body_size = 32, .max_body_size = 32,
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
@@ -932,11 +934,12 @@ BOOST_AUTO_TEST_CASE(ligo_http_concurrent_client_calls_succeed) {
auto payload = boost::json::object{}; auto payload = boost::json::object{};
payload["index"] = index; payload["index"] = index;
futures.push_back(boost::asio::co_spawn(io, client.call({ futures.push_back(boost::asio::co_spawn(io, client.call({
.target = "/echo", .target = "/echo",
.payload = std::move(payload), .payload = std::move(payload),
}), boost::asio::use_future)); }),
boost::asio::use_future));
} }
for (auto& future : futures) { for (auto &future : futures) {
auto result = future.get(); auto result = future.get();
BOOST_REQUIRE(result); BOOST_REQUIRE(result);
BOOST_CHECK(result->is_object()); BOOST_CHECK(result->is_object());
@@ -971,8 +974,7 @@ BOOST_AUTO_TEST_CASE(ligo_http_async_stop_cancels_active_session) {
}, },
{ {
.timeout = std::chrono::milliseconds{200}, .timeout = std::chrono::milliseconds{200},
} }),
),
boost::asio::use_future); boost::asio::use_future);
auto stopped = boost::asio::co_spawn(io, http_async_stop_after(server, std::chrono::milliseconds{5}, std::chrono::milliseconds{200}), boost::asio::use_future); auto stopped = boost::asio::co_spawn(io, http_async_stop_after(server, std::chrono::milliseconds{5}, std::chrono::milliseconds{200}), boost::asio::use_future);
run_two_threads(io); run_two_threads(io);
@@ -1002,8 +1004,7 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_round_trip_dispatches_json_handler) {
{ {
.route = "echo", .route = "echo",
.payload = boost::json::parse(R"json({"ok":true})json"), .payload = boost::json::parse(R"json({"ok":true})json"),
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
@@ -1043,8 +1044,7 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_tls_round_trip_dispatches_json_handler) {
{ {
.timeout = std::chrono::milliseconds{1000}, .timeout = std::chrono::milliseconds{1000},
.security = test_tls_client_config(), .security = test_tls_client_config(),
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
@@ -1085,12 +1085,11 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_tls_verified_peer_succeeds) {
{ {
.timeout = std::chrono::milliseconds{1000}, .timeout = std::chrono::milliseconds{1000},
.security = test_tls_verified_client_config("localhost"), .security = test_tls_verified_client_config("localhost"),
} }),
),
boost::asio::use_future); boost::asio::use_future);
auto stopper = boost::asio::steady_timer{io}; auto stopper = boost::asio::steady_timer{io};
stopper.expires_after(std::chrono::milliseconds{200}); stopper.expires_after(std::chrono::milliseconds{200});
stopper.async_wait([&server](boost::system::error_code const&) { stopper.async_wait([&server](boost::system::error_code const &) {
server.stop(); server.stop();
}); });
io.run(); io.run();
@@ -1132,12 +1131,11 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_tls_hostname_mismatch_fails) {
{ {
.timeout = std::chrono::milliseconds{1000}, .timeout = std::chrono::milliseconds{1000},
.security = test_tls_verified_client_config("wrong.local"), .security = test_tls_verified_client_config("wrong.local"),
} }),
),
boost::asio::use_future); boost::asio::use_future);
auto stopper = boost::asio::steady_timer{io}; auto stopper = boost::asio::steady_timer{io};
stopper.expires_after(std::chrono::milliseconds{200}); stopper.expires_after(std::chrono::milliseconds{200});
stopper.async_wait([&server](boost::system::error_code const&) { stopper.async_wait([&server](boost::system::error_code const &) {
server.stop(); server.stop();
}); });
io.run(); io.run();
@@ -1204,9 +1202,10 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_missing_route_returns_status) {
}; };
BOOST_REQUIRE(server.start()); BOOST_REQUIRE(server.start());
auto future = boost::asio::co_spawn(io, tcp_client_call_selected_and_stop(server, { auto future = boost::asio::co_spawn(io, tcp_client_call_selected_and_stop(server, {
.route = "missing", .route = "missing",
.payload = boost::json::object{}, .payload = boost::json::object{},
}), boost::asio::use_future); }),
boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
BOOST_CHECK(!(result)); BOOST_CHECK(!(result));
@@ -1229,9 +1228,10 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_handler_failure_returns_status) {
}; };
BOOST_REQUIRE(server.start()); BOOST_REQUIRE(server.start());
auto future = boost::asio::co_spawn(io, tcp_client_call_selected_and_stop(server, { auto future = boost::asio::co_spawn(io, tcp_client_call_selected_and_stop(server, {
.route = "boom", .route = "boom",
.payload = boost::json::object{}, .payload = boost::json::object{},
}), boost::asio::use_future); }),
boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
BOOST_CHECK(!(result)); BOOST_CHECK(!(result));
@@ -1241,9 +1241,10 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_handler_failure_returns_status) {
BOOST_AUTO_TEST_CASE(ligo_tcp_connection_refused_returns_unavailable) { BOOST_AUTO_TEST_CASE(ligo_tcp_connection_refused_returns_unavailable) {
auto io = boost::asio::io_context{}; auto io = boost::asio::io_context{};
auto future = boost::asio::co_spawn(io, tcp_client_call("127.0.0.1", 9, { auto future = boost::asio::co_spawn(io, tcp_client_call("127.0.0.1", 9, {
.route = "echo", .route = "echo",
.payload = boost::json::object{}, .payload = boost::json::object{},
}), boost::asio::use_future); }),
boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
BOOST_CHECK(!(result)); BOOST_CHECK(!(result));
@@ -1253,9 +1254,9 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_connection_refused_returns_unavailable) {
BOOST_AUTO_TEST_CASE(ligo_tcp_stalled_peer_times_out) { BOOST_AUTO_TEST_CASE(ligo_tcp_stalled_peer_times_out) {
auto io = boost::asio::io_context{}; auto io = boost::asio::io_context{};
auto acceptor = std::make_shared<boost::asio::ip::tcp::acceptor>(io, boost::asio::ip::tcp::endpoint{ auto acceptor = std::make_shared<boost::asio::ip::tcp::acceptor>(io, boost::asio::ip::tcp::endpoint{
boost::asio::ip::tcp::v4(), boost::asio::ip::tcp::v4(),
0, 0,
}); });
auto held_socket = std::make_shared<boost::asio::ip::tcp::socket>(io); auto held_socket = std::make_shared<boost::asio::ip::tcp::socket>(io);
boost::asio::co_spawn(io, accept_and_hold(acceptor, held_socket, std::chrono::milliseconds{50}), boost::asio::detached); boost::asio::co_spawn(io, accept_and_hold(acceptor, held_socket, std::chrono::milliseconds{50}), boost::asio::detached);
auto future = boost::asio::co_spawn( auto future = boost::asio::co_spawn(
@@ -1269,8 +1270,7 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_stalled_peer_times_out) {
}, },
{ {
.timeout = std::chrono::milliseconds{20}, .timeout = std::chrono::milliseconds{20},
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
@@ -1306,11 +1306,12 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_concurrent_client_calls_succeed) {
auto payload = boost::json::object{}; auto payload = boost::json::object{};
payload["index"] = index; payload["index"] = index;
futures.push_back(boost::asio::co_spawn(io, client.call({ futures.push_back(boost::asio::co_spawn(io, client.call({
.route = "echo", .route = "echo",
.payload = std::move(payload), .payload = std::move(payload),
}), boost::asio::use_future)); }),
boost::asio::use_future));
} }
for (auto& future : futures) { for (auto &future : futures) {
auto result = future.get(); auto result = future.get();
BOOST_REQUIRE(result); BOOST_REQUIRE(result);
BOOST_CHECK(result->is_object()); BOOST_CHECK(result->is_object());
@@ -1345,8 +1346,7 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_async_stop_cancels_active_session) {
}, },
{ {
.timeout = std::chrono::milliseconds{200}, .timeout = std::chrono::milliseconds{200},
} }),
),
boost::asio::use_future); boost::asio::use_future);
auto stopped = boost::asio::co_spawn(io, tcp_async_stop_after(server, std::chrono::milliseconds{5}, std::chrono::milliseconds{200}), boost::asio::use_future); auto stopped = boost::asio::co_spawn(io, tcp_async_stop_after(server, std::chrono::milliseconds{5}, std::chrono::milliseconds{200}), boost::asio::use_future);
run_two_threads(io); run_two_threads(io);
@@ -1357,9 +1357,9 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_async_stop_cancels_active_session) {
BOOST_AUTO_TEST_CASE(ligo_tcp_connect_returns_connected_socket) { BOOST_AUTO_TEST_CASE(ligo_tcp_connect_returns_connected_socket) {
auto io = boost::asio::io_context{}; auto io = boost::asio::io_context{};
auto acceptor = std::make_shared<boost::asio::ip::tcp::acceptor>(io, boost::asio::ip::tcp::endpoint{ auto acceptor = std::make_shared<boost::asio::ip::tcp::acceptor>(io, boost::asio::ip::tcp::endpoint{
boost::asio::ip::tcp::v4(), boost::asio::ip::tcp::v4(),
0, 0,
}); });
auto held_socket = std::make_shared<boost::asio::ip::tcp::socket>(io); auto held_socket = std::make_shared<boost::asio::ip::tcp::socket>(io);
boost::asio::co_spawn(io, accept_and_hold(acceptor, held_socket, std::chrono::milliseconds{10}), boost::asio::detached); boost::asio::co_spawn(io, accept_and_hold(acceptor, held_socket, std::chrono::milliseconds{10}), boost::asio::detached);
auto result = boost::asio::co_spawn( auto result = boost::asio::co_spawn(
@@ -1372,8 +1372,7 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_connect_returns_connected_socket) {
}, },
{ {
.timeout = std::chrono::milliseconds{1000}, .timeout = std::chrono::milliseconds{1000},
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto socket = result.get(); auto socket = result.get();
@@ -1386,9 +1385,9 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_connect_returns_connected_socket) {
BOOST_AUTO_TEST_CASE(ligo_net_connect_returns_connected_socket) { BOOST_AUTO_TEST_CASE(ligo_net_connect_returns_connected_socket) {
auto io = boost::asio::io_context{}; auto io = boost::asio::io_context{};
auto acceptor = std::make_shared<boost::asio::ip::tcp::acceptor>(io, boost::asio::ip::tcp::endpoint{ auto acceptor = std::make_shared<boost::asio::ip::tcp::acceptor>(io, boost::asio::ip::tcp::endpoint{
boost::asio::ip::tcp::v4(), boost::asio::ip::tcp::v4(),
0, 0,
}); });
auto held_socket = std::make_shared<boost::asio::ip::tcp::socket>(io); auto held_socket = std::make_shared<boost::asio::ip::tcp::socket>(io);
boost::asio::co_spawn(io, accept_and_hold(acceptor, held_socket, std::chrono::milliseconds{10}), boost::asio::detached); boost::asio::co_spawn(io, accept_and_hold(acceptor, held_socket, std::chrono::milliseconds{10}), boost::asio::detached);
auto result = boost::asio::co_spawn( auto result = boost::asio::co_spawn(
@@ -1401,8 +1400,7 @@ BOOST_AUTO_TEST_CASE(ligo_net_connect_returns_connected_socket) {
}, },
{ {
.timeout = std::chrono::milliseconds{1000}, .timeout = std::chrono::milliseconds{1000},
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto socket = result.get(); auto socket = result.get();
@@ -1415,9 +1413,9 @@ BOOST_AUTO_TEST_CASE(ligo_net_connect_returns_connected_socket) {
BOOST_AUTO_TEST_CASE(ligo_net_listener_accept_returns_socket) { BOOST_AUTO_TEST_CASE(ligo_net_listener_accept_returns_socket) {
auto io = boost::asio::io_context{}; auto io = boost::asio::io_context{};
auto listener_result = ligo::net::listener::bind(io.get_executor(), { auto listener_result = ligo::net::listener::bind(io.get_executor(), {
.host = "127.0.0.1", .host = "127.0.0.1",
.port = 0, .port = 0,
}); });
BOOST_REQUIRE(listener_result); BOOST_REQUIRE(listener_result);
auto listener = std::move(*listener_result); auto listener = std::move(*listener_result);
BOOST_CHECK((listener.selected_port()) != (0)); BOOST_CHECK((listener.selected_port()) != (0));
@@ -1432,8 +1430,7 @@ BOOST_AUTO_TEST_CASE(ligo_net_listener_accept_returns_socket) {
}, },
{ {
.timeout = std::chrono::milliseconds{1000}, .timeout = std::chrono::milliseconds{1000},
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto accepted_socket = accepted.get(); auto accepted_socket = accepted.get();
@@ -1451,9 +1448,9 @@ BOOST_AUTO_TEST_CASE(ligo_net_listener_accept_returns_socket) {
BOOST_AUTO_TEST_CASE(ligo_net_byte_stream_round_trips_over_tcp) { BOOST_AUTO_TEST_CASE(ligo_net_byte_stream_round_trips_over_tcp) {
auto io = boost::asio::io_context{}; auto io = boost::asio::io_context{};
auto listener_result = ligo::net::listener::bind(io.get_executor(), { auto listener_result = ligo::net::listener::bind(io.get_executor(), {
.host = "127.0.0.1", .host = "127.0.0.1",
.port = 0, .port = 0,
}); });
BOOST_REQUIRE(listener_result); BOOST_REQUIRE(listener_result);
auto listener = std::move(*listener_result); auto listener = std::move(*listener_result);
auto accepted = boost::asio::co_spawn(io, listener.async_accept(), boost::asio::use_future); auto accepted = boost::asio::co_spawn(io, listener.async_accept(), boost::asio::use_future);
@@ -1467,8 +1464,7 @@ BOOST_AUTO_TEST_CASE(ligo_net_byte_stream_round_trips_over_tcp) {
}, },
{ {
.timeout = std::chrono::milliseconds{1000}, .timeout = std::chrono::milliseconds{1000},
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto accepted_socket = accepted.get(); auto accepted_socket = accepted.get();
@@ -1499,14 +1495,16 @@ BOOST_AUTO_TEST_CASE(ligo_net_byte_stream_round_trips_over_tcp) {
BOOST_AUTO_TEST_CASE(ligo_net_listener_tls_accept_rejects_non_tls_security_modes) { BOOST_AUTO_TEST_CASE(ligo_net_listener_tls_accept_rejects_non_tls_security_modes) {
auto io = boost::asio::io_context{}; auto io = boost::asio::io_context{};
auto listener_result = ligo::net::listener::bind(io.get_executor(), { auto listener_result = ligo::net::listener::bind(io.get_executor(), {
.host = "127.0.0.1", .host = "127.0.0.1",
.port = 0, .port = 0,
}); });
BOOST_REQUIRE(listener_result); BOOST_REQUIRE(listener_result);
auto listener = std::move(*listener_result); auto listener = std::move(*listener_result);
auto none = boost::asio::co_spawn(io, listener.async_accept_tls_configured({ auto none = boost::asio::co_spawn(io, listener.async_accept_tls_configured({
.mode = ligo::security_mode::NONE, .mode = ligo::security_mode::NONE,
}, std::chrono::milliseconds{1000}), boost::asio::use_future); },
std::chrono::milliseconds{1000}),
boost::asio::use_future);
io.run(); io.run();
auto result = none.get(); auto result = none.get();
BOOST_CHECK(!result); BOOST_CHECK(!result);
@@ -1517,9 +1515,9 @@ BOOST_AUTO_TEST_CASE(ligo_net_listener_tls_accept_rejects_non_tls_security_modes
BOOST_AUTO_TEST_CASE(ligo_net_listener_tls_handshake_succeeds) { BOOST_AUTO_TEST_CASE(ligo_net_listener_tls_handshake_succeeds) {
auto io = boost::asio::io_context{}; auto io = boost::asio::io_context{};
auto listener_result = ligo::net::listener::bind(io.get_executor(), { auto listener_result = ligo::net::listener::bind(io.get_executor(), {
.host = "127.0.0.1", .host = "127.0.0.1",
.port = 0, .port = 0,
}); });
BOOST_REQUIRE(listener_result); BOOST_REQUIRE(listener_result);
auto listener = std::move(*listener_result); auto listener = std::move(*listener_result);
auto accepted = boost::asio::co_spawn(io, listener.async_accept_tls_handshaken(test_tls_server_config(), std::chrono::milliseconds{1000}), boost::asio::use_future); auto accepted = boost::asio::co_spawn(io, listener.async_accept_tls_handshaken(test_tls_server_config(), std::chrono::milliseconds{1000}), boost::asio::use_future);
@@ -1534,8 +1532,7 @@ BOOST_AUTO_TEST_CASE(ligo_net_listener_tls_handshake_succeeds) {
{ {
.timeout = std::chrono::milliseconds{1000}, .timeout = std::chrono::milliseconds{1000},
}, },
test_tls_client_config() test_tls_client_config()),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto accepted_socket = accepted.get(); auto accepted_socket = accepted.get();
@@ -1565,8 +1562,7 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_raw_connect_rejects_secure_modes) {
.security = { .security = {
.mode = ligo::security_mode::TLS, .mode = ligo::security_mode::TLS,
}, },
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto result = future.get(); auto result = future.get();
@@ -1589,8 +1585,7 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_tls_connect_rejects_non_tls_security_modes) {
}, },
{ {
.mode = ligo::security_mode::NONE, .mode = ligo::security_mode::NONE,
} }),
),
boost::asio::use_future); boost::asio::use_future);
auto dtls = boost::asio::co_spawn( auto dtls = boost::asio::co_spawn(
io, io,
@@ -1605,8 +1600,7 @@ BOOST_AUTO_TEST_CASE(ligo_tcp_tls_connect_rejects_non_tls_security_modes) {
}, },
{ {
.mode = ligo::security_mode::DTLS, .mode = ligo::security_mode::DTLS,
} }),
),
boost::asio::use_future); boost::asio::use_future);
io.run(); io.run();
auto none_result = none.get(); auto none_result = none.get();
@@ -1685,15 +1679,15 @@ BOOST_AUTO_TEST_CASE(ligo_udp_dtls_reliable_round_trip_succeeds) {
.session_timeout = std::chrono::milliseconds{1000}, .session_timeout = std::chrono::milliseconds{1000},
}; };
auto client_result = ligo::udp::client::adopt(std::move(client_socket), server_endpoint, { auto client_result = ligo::udp::client::adopt(std::move(client_socket), server_endpoint, {
.reliability = config, .reliability = config,
.security = test_dtls_client_config(), .security = test_dtls_client_config(),
.session_id = 142, .session_id = 142,
}); });
auto server_result = ligo::udp::server::adopt(std::move(server_socket), client_endpoint, { auto server_result = ligo::udp::server::adopt(std::move(server_socket), client_endpoint, {
.reliability = config, .reliability = config,
.security = test_dtls_server_config(), .security = test_dtls_server_config(),
.session_id = 142, .session_id = 142,
}); });
BOOST_REQUIRE(client_result); BOOST_REQUIRE(client_result);
BOOST_REQUIRE(server_result); BOOST_REQUIRE(server_result);
auto client = std::move(*client_result); auto client = std::move(*client_result);
@@ -1726,15 +1720,15 @@ BOOST_AUTO_TEST_CASE(ligo_udp_dtls_verified_peer_succeeds) {
.session_timeout = std::chrono::milliseconds{1000}, .session_timeout = std::chrono::milliseconds{1000},
}; };
auto client_result = ligo::udp::client::adopt(std::move(client_socket), server_endpoint, { auto client_result = ligo::udp::client::adopt(std::move(client_socket), server_endpoint, {
.reliability = config, .reliability = config,
.security = test_dtls_verified_client_config("localhost"), .security = test_dtls_verified_client_config("localhost"),
.session_id = 143, .session_id = 143,
}); });
auto server_result = ligo::udp::server::adopt(std::move(server_socket), client_endpoint, { auto server_result = ligo::udp::server::adopt(std::move(server_socket), client_endpoint, {
.reliability = config, .reliability = config,
.security = test_dtls_server_config(), .security = test_dtls_server_config(),
.session_id = 143, .session_id = 143,
}); });
BOOST_REQUIRE(client_result); BOOST_REQUIRE(client_result);
BOOST_REQUIRE(server_result); BOOST_REQUIRE(server_result);
auto client = std::move(*client_result); auto client = std::move(*client_result);
@@ -1767,15 +1761,15 @@ BOOST_AUTO_TEST_CASE(ligo_udp_dtls_server_peer_verification_succeeds_with_client
.session_timeout = std::chrono::milliseconds{1000}, .session_timeout = std::chrono::milliseconds{1000},
}; };
auto client_result = ligo::udp::client::adopt(std::move(client_socket), server_endpoint, { auto client_result = ligo::udp::client::adopt(std::move(client_socket), server_endpoint, {
.reliability = config, .reliability = config,
.security = test_dtls_identified_client_config(), .security = test_dtls_identified_client_config(),
.session_id = 145, .session_id = 145,
}); });
auto server_result = ligo::udp::server::adopt(std::move(server_socket), client_endpoint, { auto server_result = ligo::udp::server::adopt(std::move(server_socket), client_endpoint, {
.reliability = config, .reliability = config,
.security = test_dtls_server_verify_client_config(), .security = test_dtls_server_verify_client_config(),
.session_id = 145, .session_id = 145,
}); });
BOOST_REQUIRE(client_result); BOOST_REQUIRE(client_result);
BOOST_REQUIRE(server_result); BOOST_REQUIRE(server_result);
auto client = std::move(*client_result); auto client = std::move(*client_result);
@@ -1808,15 +1802,15 @@ BOOST_AUTO_TEST_CASE(ligo_udp_dtls_server_peer_verification_fails_without_client
.session_timeout = std::chrono::milliseconds{100}, .session_timeout = std::chrono::milliseconds{100},
}; };
auto client_result = ligo::udp::client::adopt(std::move(client_socket), server_endpoint, { auto client_result = ligo::udp::client::adopt(std::move(client_socket), server_endpoint, {
.reliability = config, .reliability = config,
.security = test_dtls_client_config(), .security = test_dtls_client_config(),
.session_id = 146, .session_id = 146,
}); });
auto server_result = ligo::udp::server::adopt(std::move(server_socket), client_endpoint, { auto server_result = ligo::udp::server::adopt(std::move(server_socket), client_endpoint, {
.reliability = config, .reliability = config,
.security = test_dtls_server_verify_client_config(), .security = test_dtls_server_verify_client_config(),
.session_id = 146, .session_id = 146,
}); });
BOOST_REQUIRE(client_result); BOOST_REQUIRE(client_result);
BOOST_REQUIRE(server_result); BOOST_REQUIRE(server_result);
auto client = std::move(*client_result); auto client = std::move(*client_result);
@@ -1825,7 +1819,7 @@ BOOST_AUTO_TEST_CASE(ligo_udp_dtls_server_peer_verification_fails_without_client
auto received = boost::asio::co_spawn(io, server.receive(), boost::asio::use_future); auto received = boost::asio::co_spawn(io, server.receive(), boost::asio::use_future);
auto stopper = boost::asio::steady_timer{io}; auto stopper = boost::asio::steady_timer{io};
stopper.expires_after(std::chrono::milliseconds{200}); stopper.expires_after(std::chrono::milliseconds{200});
stopper.async_wait([&client, &server](boost::system::error_code const&) { stopper.async_wait([&client, &server](boost::system::error_code const &) {
client.close(); client.close();
server.close(); server.close();
}); });
@@ -1854,19 +1848,19 @@ BOOST_AUTO_TEST_CASE(ligo_udp_dtls_untrusted_peer_fails) {
.session_timeout = std::chrono::milliseconds{100}, .session_timeout = std::chrono::milliseconds{100},
}; };
auto client_result = ligo::udp::client::adopt(std::move(client_socket), server_endpoint, { auto client_result = ligo::udp::client::adopt(std::move(client_socket), server_endpoint, {
.reliability = config, .reliability = config,
.security = { .security = {
.mode = ligo::security_mode::DTLS, .mode = ligo::security_mode::DTLS,
.verify = ligo::verify_mode::PEER, .verify = ligo::verify_mode::PEER,
.peer_name = "localhost", .peer_name = "localhost",
}, },
.session_id = 144, .session_id = 144,
}); });
auto server_result = ligo::udp::server::adopt(std::move(server_socket), client_endpoint, { auto server_result = ligo::udp::server::adopt(std::move(server_socket), client_endpoint, {
.reliability = config, .reliability = config,
.security = test_dtls_server_config(), .security = test_dtls_server_config(),
.session_id = 144, .session_id = 144,
}); });
BOOST_REQUIRE(client_result); BOOST_REQUIRE(client_result);
BOOST_REQUIRE(server_result); BOOST_REQUIRE(server_result);
auto client = std::move(*client_result); auto client = std::move(*client_result);
@@ -1875,7 +1869,7 @@ BOOST_AUTO_TEST_CASE(ligo_udp_dtls_untrusted_peer_fails) {
auto received = boost::asio::co_spawn(io, server.receive(), boost::asio::use_future); auto received = boost::asio::co_spawn(io, server.receive(), boost::asio::use_future);
auto stopper = boost::asio::steady_timer{io}; auto stopper = boost::asio::steady_timer{io};
stopper.expires_after(std::chrono::milliseconds{200}); stopper.expires_after(std::chrono::milliseconds{200});
stopper.async_wait([&client, &server](boost::system::error_code const&) { stopper.async_wait([&client, &server](boost::system::error_code const &) {
client.close(); client.close();
server.close(); server.close();
}); });
@@ -1893,16 +1887,17 @@ BOOST_AUTO_TEST_CASE(ligo_udp_dtls_server_requires_identity) {
9, 9,
}; };
auto server = ligo::udp::server::bind(io.get_executor(), { auto server = ligo::udp::server::bind(io.get_executor(), {
.local = { .local = {
boost::asio::ip::udp::v4(), boost::asio::ip::udp::v4(),
0, 0,
}, },
.peer = peer, .peer = peer,
}, { },
.security = { {
.mode = ligo::security_mode::DTLS, .security = {
}, .mode = ligo::security_mode::DTLS,
}); },
});
BOOST_CHECK(!server); BOOST_CHECK(!server);
BOOST_CHECK((server.error().code()) == (ligo::detail::error_invalid_argument)); BOOST_CHECK((server.error().code()) == (ligo::detail::error_invalid_argument));
} }
@@ -1996,9 +1991,10 @@ BOOST_AUTO_TEST_CASE(ligo_udp_wrong_session_times_out) {
.session_timeout = std::chrono::milliseconds{20}, .session_timeout = std::chrono::milliseconds{20},
}; };
auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{ auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{
boost::asio::ip::udp::v4(), boost::asio::ip::udp::v4(),
0, 0,
}, receiver_endpoint, config, 7); },
receiver_endpoint, config, 7);
BOOST_REQUIRE(channel_result); BOOST_REQUIRE(channel_result);
auto channel = std::move(*channel_result); auto channel = std::move(*channel_result);
auto future = boost::asio::co_spawn(io, channel.send(boost::json::object{}), boost::asio::use_future); auto future = boost::asio::co_spawn(io, channel.send(boost::json::object{}), boost::asio::use_future);
@@ -2022,9 +2018,10 @@ BOOST_AUTO_TEST_CASE(ligo_udp_oversized_payload_returns_invalid_argument) {
receiver.local_endpoint().port(), receiver.local_endpoint().port(),
}; };
auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{ auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{
boost::asio::ip::udp::v4(), boost::asio::ip::udp::v4(),
0, 0,
}, receiver_endpoint); },
receiver_endpoint);
BOOST_REQUIRE(channel_result); BOOST_REQUIRE(channel_result);
auto channel = std::move(*channel_result); auto channel = std::move(*channel_result);
auto payload = boost::json::value{ auto payload = boost::json::value{
@@ -2051,15 +2048,16 @@ BOOST_AUTO_TEST_CASE(ligo_udp_close_wakes_pending_receive) {
right_socket.local_endpoint().port(), right_socket.local_endpoint().port(),
}; };
auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{ auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{
boost::asio::ip::udp::v4(), boost::asio::ip::udp::v4(),
0, 0,
}, right_endpoint); },
right_endpoint);
BOOST_REQUIRE(channel_result); BOOST_REQUIRE(channel_result);
auto channel = std::move(*channel_result); auto channel = std::move(*channel_result);
auto received = boost::asio::co_spawn(io, channel.receive(), boost::asio::use_future); auto received = boost::asio::co_spawn(io, channel.receive(), boost::asio::use_future);
auto timer = boost::asio::steady_timer{io}; auto timer = boost::asio::steady_timer{io};
timer.expires_after(std::chrono::milliseconds{5}); timer.expires_after(std::chrono::milliseconds{5});
timer.async_wait([&channel](boost::system::error_code const&) { timer.async_wait([&channel](boost::system::error_code const &) {
channel.close(); channel.close();
}); });
io.run(); io.run();
@@ -2082,9 +2080,10 @@ BOOST_AUTO_TEST_CASE(ligo_udp_async_close_drains_pending_receive) {
right_socket.local_endpoint().port(), right_socket.local_endpoint().port(),
}; };
auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{ auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{
boost::asio::ip::udp::v4(), boost::asio::ip::udp::v4(),
0, 0,
}, right_endpoint); },
right_endpoint);
BOOST_REQUIRE(channel_result); BOOST_REQUIRE(channel_result);
auto channel = std::move(*channel_result); auto channel = std::move(*channel_result);
auto received = boost::asio::co_spawn(io, channel.receive(), boost::asio::use_future); auto received = boost::asio::co_spawn(io, channel.receive(), boost::asio::use_future);
@@ -2115,9 +2114,10 @@ BOOST_AUTO_TEST_CASE(ligo_udp_async_close_drains_pending_send) {
.session_timeout = std::chrono::milliseconds{1000}, .session_timeout = std::chrono::milliseconds{1000},
}; };
auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{ auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{
boost::asio::ip::udp::v4(), boost::asio::ip::udp::v4(),
0, 0,
}, receiver_endpoint, config, 171); },
receiver_endpoint, config, 171);
BOOST_REQUIRE(channel_result); BOOST_REQUIRE(channel_result);
auto channel = std::move(*channel_result); auto channel = std::move(*channel_result);
auto sent = boost::asio::co_spawn(io, channel.send(boost::json::object{}), boost::asio::use_future); auto sent = boost::asio::co_spawn(io, channel.send(boost::json::object{}), boost::asio::use_future);
@@ -2143,9 +2143,10 @@ BOOST_AUTO_TEST_CASE(ligo_udp_async_close_moved_from_channel_is_ok) {
right_socket.local_endpoint().port(), right_socket.local_endpoint().port(),
}; };
auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{ auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{
boost::asio::ip::udp::v4(), boost::asio::ip::udp::v4(),
0, 0,
}, right_endpoint); },
right_endpoint);
BOOST_REQUIRE(channel_result); BOOST_REQUIRE(channel_result);
auto channel = std::move(*channel_result); auto channel = std::move(*channel_result);
auto moved = std::move(channel); auto moved = std::move(channel);
@@ -2169,9 +2170,10 @@ BOOST_AUTO_TEST_CASE(ligo_udp_async_close_rejects_invalid_timeout) {
right_socket.local_endpoint().port(), right_socket.local_endpoint().port(),
}; };
auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{ auto channel_result = udp_client_bind(io, boost::asio::ip::udp::endpoint{
boost::asio::ip::udp::v4(), boost::asio::ip::udp::v4(),
0, 0,
}, right_endpoint); },
right_endpoint);
BOOST_REQUIRE(channel_result); BOOST_REQUIRE(channel_result);
auto channel = std::move(*channel_result); auto channel = std::move(*channel_result);
auto closed = boost::asio::co_spawn(io, channel.async_close(std::chrono::milliseconds{0}), boost::asio::use_future); auto closed = boost::asio::co_spawn(io, channel.async_close(std::chrono::milliseconds{0}), boost::asio::use_future);