From e4125d4c4b603f68c12146f05390ac401a1f74aa Mon Sep 17 00:00:00 2001 From: khodowany Date: Tue, 6 Oct 2026 04:12:27 -0500 Subject: [PATCH] Fix --- src/http.cpp | 25 +++++++++++++++---------- tests/CMakeLists.txt | 2 +- tests/test.cpp | 44 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/src/http.cpp b/src/http.cpp index 2818358..fb17131 100755 --- a/src/http.cpp +++ b/src/http.cpp @@ -549,16 +549,8 @@ auto call_http_endpoint(ligo::http::endpoint endpoint, std::shared_ptr boost::asio::awaitable> { - auto response = co_await exchange(request); +auto unwrap_http_response(boost::asio::awaitable> operation) -> boost::asio::awaitable> { + auto response = co_await std::move(operation); if (!response) { co_return std::unexpected(response.error()); } @@ -570,6 +562,19 @@ auto ligo::http::client::call(ligo::http::request const &request) const -> boost co_return ligo::detail::unwrap_response_envelope(std::move(body), "HTTP"); } +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) + : ligo::detail::stream_listener{std::move(executor), std::move(router)}, 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> { + // Capture inputs now: the client and request may expire before the coroutine runs. + return unwrap_http_response(exchange(request)); +} + auto ligo::http::client::exchange(ligo::http::request const &request) const -> boost::asio::awaitable> { return call_http_endpoint(endpoint_, std::make_shared(request), options_); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 12547b7..a767248 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,7 +14,7 @@ target_compile_definitions(ligo_test PRIVATE $<$:LIGO_EXPECT_DEBUG_ASSERTS> $<$>:LIGO_EXPECT_NDEBUG> ) -add_test(NAME ligo COMMAND ligo_test) +add_test(NAME ligo COMMAND ligo_test --log_level=test_suite) set_tests_properties(ligo PROPERTIES TIMEOUT 60) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} diff --git a/tests/test.cpp b/tests/test.cpp index 6a12e0e..4e55764 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -2257,6 +2258,38 @@ BOOST_AUTO_TEST_CASE(ligo_udp_multithreaded_io_context_round_trip_succeeds) { BOOST_CHECK(message->payload.as_object().at("ok").as_bool()); } +BOOST_AUTO_TEST_CASE(ligo_http_client_operations_capture_inputs_before_await) { + auto io = boost::asio::io_context{}; + auto client = ligo::http::client{{"127.0.0.1", 1}}; + auto request = ligo::http::request{.target = ""}; + auto call = client.call(request); + auto exchange = client.exchange(request); + client = ligo::http::client{{"127.0.0.1", 0}}; + request.target = "/"; + auto called = boost::asio::co_spawn(io, std::move(call), boost::asio::use_future); + auto exchanged = boost::asio::co_spawn(io, std::move(exchange), boost::asio::use_future); + io.run(); + auto call_result = called.get(); + BOOST_REQUIRE(!call_result); + BOOST_CHECK_EQUAL(call_result.error().code(), ligo::detail::error_invalid_argument); + BOOST_CHECK_EQUAL(call_result.error().message(), "HTTP target must not be empty"); + auto exchange_result = exchanged.get(); + BOOST_REQUIRE(!exchange_result); + BOOST_CHECK_EQUAL(exchange_result.error().code(), ligo::detail::error_invalid_argument); + BOOST_CHECK_EQUAL(exchange_result.error().message(), "HTTP target must not be empty"); +} + +BOOST_AUTO_TEST_CASE(ligo_http_client_call_outlives_client_and_request) { + auto io = boost::asio::io_context{}; + // Both the helper's client and this temporary request expire before io.run(). + auto future = boost::asio::co_spawn(io, http_client_call("127.0.0.1", 1, {.target = ""}), boost::asio::use_future); + io.run(); + auto result = future.get(); + BOOST_REQUIRE(!result); + BOOST_CHECK_EQUAL(result.error().code(), ligo::detail::error_invalid_argument); + BOOST_CHECK_EQUAL(result.error().message(), "HTTP target must not be empty"); +} + BOOST_AUTO_TEST_CASE(ligo_response_metadata_is_preserved_by_router) { auto io = boost::asio::io_context{}; auto router = ligo::service_router{}; @@ -2314,9 +2347,14 @@ BOOST_AUTO_TEST_CASE(ligo_http_raw_exchange_headers_query_and_status) { auto duplicate = co_await client.exchange(duplicate_request); BOOST_REQUIRE(duplicate); BOOST_CHECK_EQUAL(duplicate->status, 400); - server.stop(); }; - auto future = boost::asio::co_spawn(io, exercise(), boost::asio::use_future); + auto failure = std::exception_ptr{}; + boost::asio::co_spawn(io, exercise(), [&](std::exception_ptr error) { + failure = error; + server.stop(); + }); io.run(); - future.get(); + if (failure) { + std::rethrow_exception(failure); + } }