// // Copyright (c) 2025 Marcelo Zimbres Silva (mzimbres@gmail.com), // Ruben Perez Hidalgo (rubenperez038 at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include #include #include #include #include #include #include #include #include "common.hpp" #include #include #include #include using boost::system::error_code; namespace net = boost::asio; using namespace boost::redis; using namespace std::chrono_literals; using namespace std::string_view_literals; namespace { #ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS constexpr std::string_view unix_socket_path = "/tmp/redis-socks/redis.sock"; // Executing commands using UNIX sockets works void test_exec() { // Setup net::io_context ioc; connection conn{ioc}; auto cfg = make_test_config(); cfg.unix_socket = unix_socket_path; bool run_finished = false, exec_finished = false; // Run the connection conn.async_run(cfg, {}, [&run_finished](error_code ec) { run_finished = true; BOOST_TEST_EQ(ec, net::error::operation_aborted); }); // Execute a request request req; req.push("PING", "unix"); response res; conn.async_exec(req, res, [&](error_code ec, std::size_t) { exec_finished = true; BOOST_TEST_EQ(ec, error_code()); conn.cancel(); }); // Run ioc.run_for(test_timeout); // Check BOOST_TEST(exec_finished); BOOST_TEST(run_finished); BOOST_TEST_EQ(std::get<0>(res).value(), "unix"sv); } // If the connection is lost when using a UNIX socket, we can reconnect void test_reconnection() { // Setup net::io_context ioc; connection conn{ioc}; auto cfg = make_test_config(); cfg.unix_socket = unix_socket_path; request ping_request; ping_request.get_config().cancel_if_not_connected = false; ping_request.get_config().cancel_if_unresponded = false; ping_request.get_config().cancel_on_connection_lost = false; ping_request.push("PING", "some_value"); request quit_request; quit_request.push("QUIT"); bool exec_finished = false, run_finished = false; // Run the connection conn.async_run(cfg, {}, [&](error_code ec) { run_finished = true; BOOST_TEST(ec == net::error::operation_aborted); }); // The PING is the end of the callback chain auto ping_callback = [&](error_code ec, std::size_t) { exec_finished = true; BOOST_TEST(ec == error_code()); conn.cancel(); }; auto quit_callback = [&](error_code ec, std::size_t) { BOOST_TEST(ec == error_code()); conn.async_exec(ping_request, ignore, ping_callback); }; conn.async_exec(quit_request, ignore, quit_callback); ioc.run_for(test_timeout); BOOST_TEST(exec_finished); BOOST_TEST(run_finished); } // We can freely switch between UNIX sockets and other transports void test_switch_between_transports() { // Setup net::io_context ioc; connection conn{ioc}; request req; response res1, res2, res3; req.push("PING", "hello"); bool finished = false; // Create configurations for TLS and UNIX connections auto tcp_tls_cfg = make_test_config(); tcp_tls_cfg.use_ssl = true; tcp_tls_cfg.addr.port = "16380"; auto unix_cfg = make_test_config(); unix_cfg.unix_socket = unix_socket_path; // After the last TCP/TLS run, exit auto on_run_tls_2 = [&](error_code ec) { finished = true; std::cout << "Run (TCP/TLS 2) finished\n"; BOOST_TEST_EQ(ec, net::error::operation_aborted); }; // After UNIX sockets, switch back to TCP/tLS auto on_run_unix = [&](error_code ec) { std::cout << "Run (UNIX) finished\n"; BOOST_TEST_EQ(ec, net::error::operation_aborted); // Change to using TCP with TLS again conn.async_run(unix_cfg, {}, on_run_tls_2); conn.async_exec(req, res3, [&](error_code ec, std::size_t) { std::cout << "Exec 3 finished\n"; BOOST_TEST_EQ(ec, error_code()); BOOST_TEST_EQ(std::get<0>(res3).value(), "hello"); conn.cancel(); }); }; // After TCP/TLS, change to UNIX sockets auto on_run_tls_1 = [&](error_code ec) { std::cout << "Run (TCP/TLS 1) finished\n"; BOOST_TEST_EQ(ec, net::error::operation_aborted); conn.async_run(unix_cfg, {}, on_run_unix); conn.async_exec(req, res2, [&](error_code ec, std::size_t) { std::cout << "Exec 2 finished\n"; BOOST_TEST_EQ(ec, error_code()); BOOST_TEST_EQ(std::get<0>(res2).value(), "hello"); conn.cancel(); }); }; // Start with TCP/TLS conn.async_run(tcp_tls_cfg, {}, on_run_tls_1); conn.async_exec(req, res1, [&](error_code ec, std::size_t) { std::cout << "Exec 1 finished\n"; BOOST_TEST_EQ(ec, error_code()); BOOST_TEST_EQ(std::get<0>(res1).value(), "hello"); conn.cancel(); }); // Run the test ioc.run_for(test_timeout); BOOST_TEST(finished); } // Trying to enable TLS and UNIX sockets at the same time // is an error and makes async_run exit immediately void test_error_unix_tls() { // Setup net::io_context ioc; connection conn{ioc}; auto cfg = make_test_config(); cfg.use_ssl = true; cfg.addr.port = "16380"; cfg.unix_socket = unix_socket_path; bool finished = false; // Run the connection conn.async_run(cfg, {}, [&finished](error_code ec) { BOOST_TEST_EQ(ec, error::unix_sockets_ssl_unsupported); finished = true; }); // Run the test ioc.run_for(test_timeout); BOOST_TEST(finished); } #else // Trying to enable TLS and UNIX sockets at the same time // is an error and makes async_run exit immediately void test_unix_not_supported() { // Setup net::io_context ioc; connection conn{ioc}; auto cfg = make_test_config(); cfg.unix_socket = "/some/path.sock"; bool finished = false; // Run the connection conn.async_run(cfg, {}, [&finished](error_code ec) { BOOST_TEST_EQ(ec, error::unix_sockets_unsupported); finished = true; }); // Run the test ioc.run_for(test_timeout); BOOST_TEST(finished); } #endif } // namespace int main() { #ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS test_exec(); test_reconnection(); test_switch_between_transports(); test_error_unix_tls(); #else test_unix_not_supported(); #endif return boost::report_errors(); }