diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index f8039119..fa3fa482 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -140,17 +141,17 @@ void boost_redis_from_bulk(T& t, resp3::basic_node const& node, system:: //================================================ template -auto prepare_done(T&) noexcept +inline auto prepare_done(T*) noexcept { return [] { }; } -template -auto prepare_done(generic_flat_response& resp) noexcept +template <> +inline auto prepare_done(generic_flat_response* resp) noexcept { - return [resp]() mutable { - if (resp.has_value()) { - resp.value().set_view(); + return [resp] { + if (resp->has_value()) { + resp->value().set_view(); } }; } diff --git a/include/boost/redis/resp3/node.hpp b/include/boost/redis/resp3/node.hpp index 85147b1c..34a99aa6 100644 --- a/include/boost/redis/resp3/node.hpp +++ b/include/boost/redis/resp3/node.hpp @@ -63,8 +63,12 @@ struct offset_string { std::string_view data; std::size_t offset{}; std::size_t size{}; + + operator std::string() const { return std::string{data}; } }; +inline std::ostream& operator<<(std::ostream& os, offset_string const& s) { return os << s.data; } + using offset_node = basic_node; } // namespace boost::redis::resp3 diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index 9a39d713..3053bbae 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -49,6 +49,18 @@ public: view_.reserve(num_nodes); } + void clear() + { + data_.clear(); + view_.clear(); + } + + std::size_t size() const noexcept { return view_.size(); } + bool empty() noexcept { return view_.empty(); } + + resp3::offset_node& at(std::size_t index) { return view_.at(index); } + resp3::offset_node const& at(std::size_t index) const { return view_.at(index); } + std::vector const& view() const { return view_; } std::vector& view() { return view_; } diff --git a/test/test_any_adapter.cpp b/test/test_any_adapter.cpp index f0345ac1..6d4ee82e 100644 --- a/test/test_any_adapter.cpp +++ b/test/test_any_adapter.cpp @@ -13,6 +13,7 @@ #include using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::response; using boost::redis::ignore; using boost::redis::any_adapter; @@ -24,10 +25,12 @@ BOOST_AUTO_TEST_CASE(any_adapter_response_types) response r1; response r2; generic_response r3; + generic_flat_response r4; BOOST_CHECK_NO_THROW(any_adapter{r1}); BOOST_CHECK_NO_THROW(any_adapter{r2}); BOOST_CHECK_NO_THROW(any_adapter{r3}); + BOOST_CHECK_NO_THROW(any_adapter{r4}); BOOST_CHECK_NO_THROW(any_adapter{ignore}); } diff --git a/test/test_conn_check_health.cpp b/test/test_conn_check_health.cpp index 78062e3b..d101a52a 100644 --- a/test/test_conn_check_health.cpp +++ b/test/test_conn_check_health.cpp @@ -262,4 +262,4 @@ int main() test_flexible().run(); return boost::report_errors(); -} \ No newline at end of file +} diff --git a/test/test_conn_exec.cpp b/test/test_conn_exec.cpp index 28a23497..c2d4fc42 100644 --- a/test/test_conn_exec.cpp +++ b/test/test_conn_exec.cpp @@ -26,7 +26,7 @@ namespace net = boost::asio; using boost::redis::config; using boost::redis::connection; -using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::ignore; using boost::redis::operation; using boost::redis::request; diff --git a/test/test_conn_exec_cancel.cpp b/test/test_conn_exec_cancel.cpp index 939cd361..1870d784 100644 --- a/test/test_conn_exec_cancel.cpp +++ b/test/test_conn_exec_cancel.cpp @@ -32,7 +32,7 @@ using boost::redis::operation; using boost::redis::error; using boost::redis::request; using boost::redis::response; -using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::ignore; using boost::redis::ignore_t; using boost::redis::logger; diff --git a/test/test_conn_exec_cancel2.cpp b/test/test_conn_exec_cancel2.cpp new file mode 100644 index 00000000..08337461 --- /dev/null +++ b/test/test_conn_exec_cancel2.cpp @@ -0,0 +1,95 @@ +/* Copyright (c) 2018-2022 Marcelo Zimbres Silva (mzimbres@gmail.com) + * + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE.txt) + */ + +#include + +#include +#define BOOST_TEST_MODULE conn_exec_cancel +#include + +#include "common.hpp" + +#include + +#ifdef BOOST_ASIO_HAS_CO_AWAIT + +// NOTE1: Sends hello separately. I have observed that if hello and +// blpop are sent toguether, Redis will send the response of hello +// right away, not waiting for blpop. That is why we have to send it +// separately. + +namespace net = boost::asio; +using error_code = boost::system::error_code; +using boost::redis::operation; +using boost::redis::request; +using boost::redis::response; +using boost::redis::generic_flat_response; +using boost::redis::ignore; +using boost::redis::ignore_t; +using boost::redis::config; +using boost::redis::logger; +using boost::redis::connection; +using namespace std::chrono_literals; + +namespace { + +auto async_ignore_explicit_cancel_of_req_written() -> net::awaitable +{ + auto ex = co_await net::this_coro::executor; + + generic_flat_response gresp; + auto conn = std::make_shared(ex); + + run(conn); + + net::steady_timer st{ex}; + st.expires_after(std::chrono::seconds{1}); + + // See NOTE1. + request req0; + req0.push("PING", "async_ignore_explicit_cancel_of_req_written"); + co_await conn->async_exec(req0, gresp); + + request req1; + req1.push("BLPOP", "any", 3); + + bool seen = false; + conn->async_exec(req1, gresp, [&](error_code ec, std::size_t) { + // No error should occur since the cancellation should be ignored + std::cout << "async_exec (1): " << ec.message() << std::endl; + BOOST_TEST(ec == error_code()); + seen = true; + }); + + // Will complete while BLPOP is pending. + error_code ec; + co_await st.async_wait(net::redirect_error(ec)); + conn->cancel(operation::exec); + + BOOST_TEST(ec == error_code()); + + request req2; + req2.push("PING"); + + // Test whether the connection remains usable after a call to + // cancel(exec). + co_await conn->async_exec(req2, gresp, net::redirect_error(ec)); + conn->cancel(); + + BOOST_TEST(ec == error_code()); + BOOST_TEST(seen); +} + +BOOST_AUTO_TEST_CASE(test_ignore_explicit_cancel_of_req_written) +{ + run_coroutine_test(async_ignore_explicit_cancel_of_req_written()); +} + +} // namespace + +#else +BOOST_AUTO_TEST_CASE(dummy) { } +#endif diff --git a/test/test_conn_exec_error.cpp b/test/test_conn_exec_error.cpp index 5a121929..19638e2d 100644 --- a/test/test_conn_exec_error.cpp +++ b/test/test_conn_exec_error.cpp @@ -21,7 +21,7 @@ using error_code = boost::system::error_code; using boost::redis::connection; using boost::redis::request; using boost::redis::response; -using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::ignore; using boost::redis::ignore_t; using boost::redis::error; @@ -266,7 +266,7 @@ BOOST_AUTO_TEST_CASE(subscriber_wrong_syntax) conn->async_exec(req1, ignore, c1); - generic_response gresp; + generic_flat_response gresp; conn->set_receive_response(gresp); auto c3 = [&](error_code ec, std::size_t) {