2
0
mirror of https://github.com/boostorg/redis.git synced 2026-01-19 04:42:09 +00:00

Fixes TLS reconnection.

This commit is contained in:
Marcelo Zimbres
2024-01-03 22:34:13 +01:00
parent 112bba7222
commit 7ff82f319b
4 changed files with 114 additions and 11 deletions

View File

@@ -693,6 +693,11 @@ https://lists.boost.org/Archives/boost/2023/01/253944.php.
apps need only one connection for their entire application, which
makes the overhead of one ssl-context per connection negligible.
* ([Issue 169](https://github.com/boostorg/redis/issues/169)).
Allows setting a callback that is called before every attempt to
stablish a connection or reconnection. See `cpp20_intro_tls.cpp` for
an example.
### Boost 1.84 (First release in Boost)
* Deprecates the `async_receive` overload that takes a response. Users

View File

@@ -22,10 +22,16 @@ using boost::redis::connection;
auto verify_certificate(bool, asio::ssl::verify_context&) -> bool
{
std::cout << "set_verify_callback" << std::endl;
std::cout << "verify_certificate called" << std::endl;
return true;
}
auto prepare_callback = [](connection::next_layer_type& stream)
{
stream.set_verify_mode(asio::ssl::verify_peer);
stream.set_verify_callback(verify_certificate);
};
auto co_main(config cfg) -> asio::awaitable<void>
{
cfg.use_ssl = true;
@@ -35,6 +41,7 @@ auto co_main(config cfg) -> asio::awaitable<void>
cfg.addr.port = "6380";
auto conn = std::make_shared<connection>(co_await asio::this_coro::executor);
conn->set_prepare_callback(prepare_callback);
conn->async_run(cfg, {}, asio::consign(asio::detached, conn));
request req;
@@ -42,9 +49,6 @@ auto co_main(config cfg) -> asio::awaitable<void>
response<std::string> resp;
conn->next_layer().set_verify_mode(asio::ssl::verify_peer);
conn->next_layer().set_verify_callback(verify_certificate);
co_await conn->async_exec(req, resp, asio::deferred);
conn->cancel();

View File

@@ -34,6 +34,8 @@ struct reconnection_op {
{
BOOST_ASIO_CORO_REENTER (coro_) for (;;)
{
conn_->m_prepare_callback(conn_->next_layer());
BOOST_ASIO_CORO_YIELD
conn_->impl_.async_run(conn_->cfg_, logger_, std::move(self));
conn_->cancel(operation::receive);
@@ -78,6 +80,15 @@ public:
executor_type get_executor() noexcept
{ return impl_.get_executor(); }
/// Next layer type.
using next_layer_type = asio::ssl::stream<asio::basic_stream_socket<asio::ip::tcp, Executor>>;
/** Prepare callback type
*
* See set_prepare_callback for more information.
*/
using prepare_callback_type = std::function<void(next_layer_type&)>;
/// Rebinds the socket type to another executor.
template <class Executor1>
struct rebind_executor
@@ -313,6 +324,30 @@ public:
usage get_usage() const noexcept
{ return impl_.get_usage(); }
/** @brief Set the prepare callback
*
* This callback is called before every new connect or reconnect
* attempt. It is specially useful for SSL connections, for example
*
* @code
* auto verify_certificate(bool, asio::ssl::verify_context&) -> bool
* {
* std::cout << "verify_certificate called" << std::endl;
* return true;
* }
*
* auto prepare_callback = [](connection::next_layer_type& stream)
* {
* stream.set_verify_mode(asio::ssl::verify_peer);
* stream.set_verify_callback(verify_certificate);
* };
* @endcode
*/
void set_prepare_callback(prepare_callback_type callback)
{
m_prepare_callback = std::move(callback);
}
private:
using timer_type =
asio::basic_waitable_timer<
@@ -325,6 +360,7 @@ private:
config cfg_;
detail::connection_base<executor_type> impl_;
timer_type timer_;
prepare_callback_type m_prepare_callback = [](next_layer_type&){ };
};
/** \brief A basic_connection that type erases the executor.
@@ -341,6 +377,15 @@ public:
/// Executor type.
using executor_type = asio::any_io_executor;
/// Underlying connection type.
using underlying_type = basic_connection<executor_type>;
/// Next layer type.
using next_layer_type = underlying_type::next_layer_type;
/// Prepare callback type
using prepare_callback_type = underlying_type::prepare_callback_type;
/// Contructs from an executor.
explicit
connection(
@@ -429,6 +474,10 @@ public:
auto const& get_ssl_context() const noexcept
{ return impl_.get_ssl_context();}
/// Calls `boost::redis::basic_connection::set_prepare_callback`.
void set_prepare_callback(prepare_callback_type callback)
{ impl_.set_prepare_callback(std::move(callback)); }
private:
void
async_run_impl(
@@ -436,7 +485,7 @@ private:
logger l,
asio::any_completion_handler<void(boost::system::error_code)> token);
basic_connection<executor_type> impl_;
underlying_type impl_;
};
} // boost::redis

View File

@@ -17,6 +17,7 @@ using boost::redis::request;
using boost::redis::response;
using boost::redis::config;
using boost::redis::operation;
using boost::redis::ignore;
using boost::system::error_code;
bool verify_certificate(bool, net::ssl::verify_context&)
@@ -25,6 +26,12 @@ bool verify_certificate(bool, net::ssl::verify_context&)
return true;
}
auto prepare_callback = [](connection::next_layer_type& stream)
{
stream.set_verify_mode(net::ssl::verify_peer);
stream.set_verify_callback(verify_certificate);
};
config make_tls_config()
{
config cfg;
@@ -49,8 +56,7 @@ BOOST_AUTO_TEST_CASE(ping_internal_ssl_context)
net::io_context ioc;
connection conn{ioc};
conn.next_layer().set_verify_mode(net::ssl::verify_peer);
conn.next_layer().set_verify_callback(verify_certificate);
conn.set_prepare_callback(prepare_callback);
conn.async_exec(req, resp, [&](auto ec, auto) {
BOOST_TEST(!ec);
@@ -77,8 +83,7 @@ BOOST_AUTO_TEST_CASE(ping_custom_ssl_context)
net::io_context ioc;
net::ssl::context ctx{boost::asio::ssl::context::tls_client};
connection conn{ioc, std::move(ctx)};
conn.next_layer().set_verify_mode(net::ssl::verify_peer);
conn.next_layer().set_verify_callback(verify_certificate);
conn.set_prepare_callback(prepare_callback);
conn.async_exec(req, resp, [&](auto ec, auto) {
BOOST_TEST(!ec);
@@ -107,8 +112,7 @@ BOOST_AUTO_TEST_CASE(acl_does_not_allow_select)
net::io_context ioc;
connection conn{ioc};
conn.next_layer().set_verify_mode(net::ssl::verify_peer);
conn.next_layer().set_verify_callback(verify_certificate);
conn.set_prepare_callback(prepare_callback);
conn.async_exec(req, resp, [&](auto, auto) {
// TODO: We should not need this cancel here because
@@ -126,3 +130,44 @@ BOOST_AUTO_TEST_CASE(acl_does_not_allow_select)
BOOST_TEST(!!ec2);
}
BOOST_AUTO_TEST_CASE(tls_and_reconnection)
{
net::io_context ioc;
connection conn{ioc};
int counter = 0;
auto prepare_callback = [&](auto& stream)
{
++counter;
};
conn.set_prepare_callback(prepare_callback);
request req;
req.get_config().cancel_on_connection_lost = false;
req.push("PING", "str1");
req.push("QUIT");
conn.async_exec(req, ignore, [&](auto ec, auto) {
std::cout << "First: " << ec.message() << std::endl;
BOOST_TEST(!ec);
conn.async_exec(req, ignore, [&](auto ec, auto) {
std::cout << "Second: " << ec.message() << std::endl;
BOOST_TEST(!ec);
conn.async_exec(req, ignore, [&](auto ec, auto) {
std::cout << "Third: " << ec.message() << std::endl;
BOOST_TEST(!ec);
conn.cancel();
});
});
});
auto const cfg = make_tls_config();
conn.async_run(cfg, {}, [](auto) { });
ioc.run();
BOOST_CHECK_EQUAL(counter, 3);
}