mirror of
https://github.com/boostorg/redis.git
synced 2026-01-19 04:42:09 +00:00
* Adds support for terminal and partial cancellation to async_run.
* Makes basic_connection::cancel use per-operation cancellation under the hood.
* Fixes a number of race conditions during cancellation which could cause the cancellation to be ignored. This could happen if the cancellation is delivered while an async handler is pending execution.
* Deprecates operation::{resolve, connect, ssl_handshake, reconnection, health_check}, in favor of operation::run. Calling basic_connection::cancel with these values (excepting reconnection) is now equivalent to using operation::run.
* Fixes a problem in the health checker that caused ping timeouts to be reported as cancellations.
* Sanitizes how the parallel group computes its final error code.
* Simplifies the reader, writer and health checker to not care about connection cancellation. This is now the responsibility of the parallel group.
* Removes an unnecessary setup_cancellation action in the reader FSM.
* Adds documentation regarding per-operation cancellation to async_receive.
* Adds additional health checker tests.
* Adds async_run per-operation cancellation tests.
* Adds reader FSM cancellation tests.
* Makes test_conn_exec_retry tests more resilient.
* Removes leftovers in the UNIX and TLS reconnection tests. These were required due to race conditions that have already been fixed.
close #318
close #319
189 lines
5.9 KiB
C++
189 lines
5.9 KiB
C++
/* 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 <boost/redis/connection.hpp>
|
|
#include <boost/redis/ignore.hpp>
|
|
|
|
#include <boost/asio/ssl/host_name_verification.hpp>
|
|
#include <boost/asio/steady_timer.hpp>
|
|
#include <boost/system/error_code.hpp>
|
|
|
|
#include <cstddef>
|
|
#include <string_view>
|
|
#define BOOST_TEST_MODULE conn_tls
|
|
#include <boost/test/included/unit_test.hpp>
|
|
|
|
#include "common.hpp"
|
|
|
|
namespace net = boost::asio;
|
|
using namespace boost::redis;
|
|
using namespace std::chrono_literals;
|
|
using boost::system::error_code;
|
|
|
|
namespace {
|
|
|
|
// CA certificate that signed the test server's certificate.
|
|
// This is a self-signed CA created for testing purposes.
|
|
// This must match tools/tls/ca.crt contents
|
|
static constexpr const char* ca_certificate = R"%(-----BEGIN CERTIFICATE-----
|
|
MIIDhzCCAm+gAwIBAgIUZGttu4o/Exs08EHCneeD3gHw7KkwDQYJKoZIhvcNAQEL
|
|
BQAwUjELMAkGA1UEBhMCRVMxGjAYBgNVBAoMEUJvb3N0LlJlZGlzIENJIENBMQsw
|
|
CQYDVQQLDAJJVDEaMBgGA1UEAwwRYm9vc3QtcmVkaXMtY2ktY2EwIBcNMjUwNjA3
|
|
MTI0NzUwWhgPMjA4MDAzMTAxMjQ3NTBaMFIxCzAJBgNVBAYTAkVTMRowGAYDVQQK
|
|
DBFCb29zdC5SZWRpcyBDSSBDQTELMAkGA1UECwwCSVQxGjAYBgNVBAMMEWJvb3N0
|
|
LXJlZGlzLWNpLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu7XV
|
|
sOoHB2J/5VtyJmMOzxhBbHKyQgW1YnMvYIb1JqIm7VuICA831SUw76n3j8mIK3zz
|
|
FfK2eYyUWf4Uo2j3uxmXDyjujqzIaUJNLcB53CQXkmIbqDigNhzUTPZ5A2MQ7xT+
|
|
t1eDbjsZ7XIM+aTShgtrpyxiccsgPJ3/XXme2RrqKeNvYsTYY6pquWZdyLOg/LOH
|
|
IeSJyL1/eQDRu/GsZjnR8UOE6uHfbjrLWls7Tifj/1IueVYCEhQZpJSWS8aUMLBZ
|
|
fi+t9YMCCK4DGy+6QlznGgVqdFFbTUt2C7tzqz+iF5dxJ8ogKMUPEeFrWiZpozoS
|
|
t60jV8fKwdXz854jLQIDAQABo1MwUTAdBgNVHQ4EFgQU2SoWvvZUW8JiDXtyuXZK
|
|
deaYYBswHwYDVR0jBBgwFoAU2SoWvvZUW8JiDXtyuXZKdeaYYBswDwYDVR0TAQH/
|
|
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAqY4hGcdCFFPL4zveSDhR9H/akjae
|
|
uXbpo/9sHZd8e3Y4BtD8K05xa3417H9u5+S2XtyLQg5MON6J2LZueQEtE3wiR3ja
|
|
QIWbizqp8W54O5hTLQs6U/mWggfuL2R/HUw7ab4M8JobwHNEMK/WKZW71z0So/kk
|
|
W3wC0+1RH2PjMOZrCIflsD7EXYKIIr9afypAbhCQmCfu/GELuNx+LmaPi5JP4TTE
|
|
tDdhzWL04JLcZnA0uXb2Mren1AR9yKYH2I5tg5kQ3Bn/6v9+JiUhiejP3Vcbw84D
|
|
yFwRzN54bLanrJNILJhHPwnNIABXOtGUV05SZbYazJpiMst1a6eqDZhv/Q==
|
|
-----END CERTIFICATE-----)%";
|
|
|
|
static config make_tls_config()
|
|
{
|
|
config cfg;
|
|
cfg.use_ssl = true;
|
|
cfg.addr.host = get_server_hostname();
|
|
cfg.addr.port = "6380";
|
|
return cfg;
|
|
}
|
|
|
|
// Using the default TLS context allows establishing TLS connections and execute requests
|
|
BOOST_AUTO_TEST_CASE(exec_default_ssl_context)
|
|
{
|
|
auto const cfg = make_tls_config();
|
|
constexpr std::string_view ping_value = "Kabuf";
|
|
|
|
request req;
|
|
req.push("PING", ping_value);
|
|
|
|
response<std::string> resp;
|
|
|
|
net::io_context ioc;
|
|
connection conn{ioc};
|
|
|
|
// The custom server uses a certificate signed by a CA
|
|
// that is not trusted by default - skip verification.
|
|
conn.next_layer().set_verify_mode(net::ssl::verify_none);
|
|
|
|
bool exec_finished = false, run_finished = false;
|
|
|
|
conn.async_exec(req, resp, [&](error_code ec, std::size_t) {
|
|
exec_finished = true;
|
|
BOOST_TEST(ec == error_code());
|
|
conn.cancel();
|
|
});
|
|
|
|
conn.async_run(cfg, {}, [&](error_code ec) {
|
|
run_finished = true;
|
|
BOOST_TEST(ec == net::error::operation_aborted);
|
|
});
|
|
|
|
ioc.run_for(test_timeout);
|
|
|
|
BOOST_TEST(exec_finished);
|
|
BOOST_TEST(run_finished);
|
|
BOOST_TEST(std::get<0>(resp).value() == ping_value);
|
|
}
|
|
|
|
// Users can pass a custom context with TLS config
|
|
BOOST_AUTO_TEST_CASE(exec_custom_ssl_context)
|
|
{
|
|
auto const cfg = make_tls_config();
|
|
constexpr std::string_view ping_value = "Kabuf";
|
|
|
|
request req;
|
|
req.push("PING", ping_value);
|
|
|
|
response<std::string> resp;
|
|
|
|
net::io_context ioc;
|
|
net::ssl::context ctx{net::ssl::context::tls_client};
|
|
|
|
// Configure the SSL context to trust the CA that signed the server's certificate.
|
|
// The test certificate uses "redis" as its common name, regardless of the actual server's hostname
|
|
ctx.add_certificate_authority(net::const_buffer(ca_certificate, std::strlen(ca_certificate)));
|
|
ctx.set_verify_mode(net::ssl::verify_peer);
|
|
ctx.set_verify_callback(net::ssl::host_name_verification("redis"));
|
|
|
|
connection conn{ioc, std::move(ctx)};
|
|
|
|
bool exec_finished = false, run_finished = false;
|
|
|
|
conn.async_exec(req, resp, [&](error_code ec, std::size_t) {
|
|
exec_finished = true;
|
|
BOOST_TEST(ec == error_code());
|
|
conn.cancel();
|
|
});
|
|
|
|
conn.async_run(cfg, {}, [&](error_code ec) {
|
|
run_finished = true;
|
|
BOOST_TEST(ec == net::error::operation_aborted);
|
|
});
|
|
|
|
ioc.run_for(test_timeout);
|
|
|
|
BOOST_TEST(exec_finished);
|
|
BOOST_TEST(run_finished);
|
|
BOOST_TEST(std::get<0>(resp).value() == ping_value);
|
|
}
|
|
|
|
// After an error, a TLS connection can recover.
|
|
// Force an error using QUIT, then issue a regular request to verify that we could reconnect
|
|
BOOST_AUTO_TEST_CASE(reconnection)
|
|
{
|
|
// Setup
|
|
net::io_context ioc;
|
|
net::steady_timer timer{ioc};
|
|
connection conn{ioc};
|
|
auto cfg = make_tls_config();
|
|
cfg.reconnect_wait_interval = 10ms; // make the test run faster
|
|
|
|
request ping_request;
|
|
ping_request.push("PING", "some_value");
|
|
ping_request.get_config().cancel_if_unresponded = false;
|
|
ping_request.get_config().cancel_on_connection_lost = false;
|
|
|
|
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);
|
|
}
|
|
|
|
} // namespace
|