From 6eb7dba352c8f52064496ad3f619c59b00a576f0 Mon Sep 17 00:00:00 2001 From: Ernest Zaslavsky Date: Mon, 26 Jan 2026 14:37:25 +0200 Subject: [PATCH] connection_factory: introduce TTL timer Add a TTL-based timer to connection_factory to automatically refresh resolved host name addresses when they expire. --- utils/http.cc | 54 +++++++++++++++++++++++++++++++++++---------------- utils/http.hh | 9 +++++---- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/utils/http.cc b/utils/http.cc index 0e3869b22a..cdbf5da507 100644 --- a/utils/http.cc +++ b/utils/http.cc @@ -23,12 +23,6 @@ future> utils::http::system_trust_crede co_return system_trust_credentials; } -future<> utils::http::dns_connection_factory::init_addresses() { - auto hent = co_await net::dns::get_host_by_name(_host, net::inet_address::family::INET); - _addr_list = std::move(hent.addr_list); - _logger.debug("Initialized addresses={}", _addr_list); -} - future<> utils::http::dns_connection_factory::init_credentials() { if (_use_https && !_creds) { _creds = co_await system_trust_credentials(); @@ -40,14 +34,28 @@ future<> utils::http::dns_connection_factory::init_credentials() { } future utils::http::dns_connection_factory::get_address() { - if (!_addr_init) [[unlikely]] { - auto units = co_await get_units(_init_semaphore, 1); - if (!_addr_init) { - co_await init_addresses(); - _addr_init = true; - } + auto get_addr = [this] -> net::inet_address { + const auto& addresses = _addr_list.value(); + return addresses[_addr_pos++ % addresses.size()]; + }; + + if (_addr_list) { + co_return get_addr(); } - co_return _addr_list[_addr_pos++ % _addr_list.size()]; + auto units = co_await get_units(_init_semaphore, 1); + if (!_addr_list) { + auto hent = co_await net::dns::get_host_by_name(_host, net::inet_address::family::INET); + _address_ttl = std::ranges::min_element(hent.addr_entries, [](const net::hostent::address_entry& lhs, const net::hostent::address_entry& rhs) { + return lhs.ttl < rhs.ttl; + })->ttl; + if (_address_ttl.count() == 0) { + co_return hent.addr_entries[_addr_pos++ % hent.addr_entries.size()].addr; + } + _addr_list = hent.addr_entries | std::views::transform(&net::hostent::address_entry::addr) | std::ranges::to(); + _addr_update_timer.rearm(lowres_clock::now() + _address_ttl); + } + + co_return get_addr(); } future> utils::http::dns_connection_factory::get_creds() { @@ -61,8 +69,8 @@ future> utils::http::dns_connection_fac co_return _creds; } -future utils::http::dns_connection_factory::connect() { - auto socket_addr = socket_address(co_await get_address(), _port); +future utils::http::dns_connection_factory::connect(net::inet_address address) { + auto socket_addr = socket_address(address, _port); if (auto creds = co_await get_creds()) { _logger.debug("Making new HTTPS connection addr={} host={}", socket_addr, _host); co_return co_await tls::connect(creds, socket_addr, tls::tls_options{.server_name = _host}); @@ -78,7 +86,13 @@ utils::http::dns_connection_factory::dns_connection_factory(std::string host, in , _port(port) , _logger(logger) ,_creds(std::move(certs)) - ,_use_https(use_https) { + , _use_https(use_https) + , _addr_update_timer([this] { + if (auto units = try_get_units(_init_semaphore, 1)) { + _addr_list.reset(); + } + }) { + _addr_update_timer.arm(lowres_clock::now()); } utils::http::dns_connection_factory::dns_connection_factory(std::string uri, logging::logger& logger, shared_ptr certs) @@ -92,7 +106,13 @@ utils::http::dns_connection_factory::dns_connection_factory(std::string uri, log {} future utils::http::dns_connection_factory::make(abort_source*) { - co_return co_await connect(); + auto address = co_await get_address(); + co_return co_await connect(address); +} + +future<> utils::http::dns_connection_factory::close() { + _addr_update_timer.cancel(); + co_await get_units(_init_semaphore, 1); } static const char HTTPS[] = "https"; diff --git a/utils/http.hh b/utils/http.hh index 089a8b5039..3a49481b54 100644 --- a/utils/http.hh +++ b/utils/http.hh @@ -27,24 +27,25 @@ protected: int _port; logging::logger& _logger; semaphore _init_semaphore{1}; - bool _addr_init = false; bool _creds_init = false; - std::vector _addr_list; + std::optional> _addr_list; shared_ptr _creds; uint16_t _addr_pos{0}; bool _use_https; + std::chrono::seconds _address_ttl{0}; + timer _addr_update_timer; - future<> init_addresses(); future<> init_credentials(); future get_address(); future> get_creds(); - future connect(); + future connect(net::inet_address address); public: dns_connection_factory(dns_connection_factory&&); dns_connection_factory(std::string host, int port, bool use_https, logging::logger& logger, shared_ptr = {}); dns_connection_factory(std::string endpoint_url, logging::logger& logger, shared_ptr = {}); virtual future make(abort_source*) override; + future<> close() override; }; // simple URL parser, just enough to handle required aspects for normal endpoint usage