+15
-10
@@ -549,16 +549,8 @@ 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);
|
||||
}
|
||||
|
||||
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<std::expected<boost::json::value, modulus::error>> {
|
||||
auto response = co_await exchange(request);
|
||||
auto unwrap_http_response(boost::asio::awaitable<std::expected<ligo::http::response, modulus::error>> operation) -> boost::asio::awaitable<std::expected<boost::json::value, modulus::error>> {
|
||||
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<std::expected<boost::json::value, modulus::error>> {
|
||||
// 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<std::expected<ligo::http::response, modulus::error>> {
|
||||
return call_http_endpoint(endpoint_, std::make_shared<ligo::http::request const>(request), options_);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ target_compile_definitions(ligo_test PRIVATE
|
||||
$<$<CONFIG:Debug>:LIGO_EXPECT_DEBUG_ASSERTS>
|
||||
$<$<NOT:$<CONFIG:Debug>>: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}
|
||||
|
||||
+41
-3
@@ -1,6 +1,7 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user