From 7ab988c0328478483204cf9140a4d894b38fe892 Mon Sep 17 00:00:00 2001 From: Dmitriy Khaustov aka xDimon Date: Wed, 4 Feb 2026 18:39:02 +0800 Subject: [PATCH] feature: ttl for user_agent --- include/libp2p/peer/user_agent_repository.hpp | 32 +++++++------ .../inmem_user_agent_repository.hpp | 13 ++++-- .../inmem_user_agent_repository.cpp | 46 ++++++++++++++----- src/protocol/identify/identify.cpp | 3 +- 4 files changed, 62 insertions(+), 32 deletions(-) diff --git a/include/libp2p/peer/user_agent_repository.hpp b/include/libp2p/peer/user_agent_repository.hpp index 974aac75..6fc39c1c 100644 --- a/include/libp2p/peer/user_agent_repository.hpp +++ b/include/libp2p/peer/user_agent_repository.hpp @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -18,24 +19,31 @@ namespace libp2p::peer { /** * @brief Storage for mapping between peer and its known protocols. */ - class UserAgentRepository { + class UserAgentRepository : public basic::GarbageCollectable { public: - virtual ~UserAgentRepository() = default; + using Milliseconds = std::chrono::milliseconds; + + ~UserAgentRepository() override = default; /** - * @brief Set agent for a peer. + * @brief Update user-agent with new {@param ttl} or insert new + * user-agent with new {@param ttl} * @param p peer - * @param agent type of agent-name - * @return peer error, if no peer {@param p} found + * @param ua user-agent type + * @param ttl ttl + * @return true/false if at least one new user-agent was added or not, */ - virtual void setUserAgent(const PeerId &p, std::string_view ua) = 0; + virtual void updateUserAgent(const PeerId &p, + std::string_view ua, + Milliseconds ttl) = 0; /** - * @brief Removes user-agent of the peer. + * @brief Bump ttl of a given peer {@param p} * @param p peer - * @return peer error, if no peer {@param p} found + * @param ttl time to live for user-agent + * @return error when no peer has been found */ - virtual void unsetUserAgent(const PeerId &) = 0; + virtual void updateTtl(const PeerId &p, Milliseconds ttl) = 0; /** * @brief Get user-agent by given peer {@param p} @@ -45,12 +53,6 @@ namespace libp2p::peer { */ [[nodiscard]] virtual std::optional getUserAgent( const PeerId &p) const = 0; - - /** - * @brief Returns set of peer ids known by this repository. - * @return unordered set of peers - */ - [[nodiscard]] virtual std::unordered_set getPeers() const = 0; }; } // namespace libp2p::peer diff --git a/include/libp2p/peer/user_agent_repository/inmem_user_agent_repository.hpp b/include/libp2p/peer/user_agent_repository/inmem_user_agent_repository.hpp index fcee2e78..019c621b 100644 --- a/include/libp2p/peer/user_agent_repository/inmem_user_agent_repository.hpp +++ b/include/libp2p/peer/user_agent_repository/inmem_user_agent_repository.hpp @@ -9,6 +9,7 @@ #include #include +#include #include namespace libp2p::peer { @@ -21,17 +22,21 @@ namespace libp2p::peer { public: ~InmemUserAgentRepository() override = default; - void setUserAgent(const PeerId &p, std::string_view ua) override; + void updateUserAgent(const PeerId &p, + std::string_view ua, + Milliseconds ttl) override; - void unsetUserAgent(const PeerId &p) override; + void updateTtl(const PeerId &p, Milliseconds ttl) override; [[nodiscard]] std::optional getUserAgent( const PeerId &p) const override; - [[nodiscard]] std::unordered_set getPeers() const override; + void collectGarbage() override; private: - std::unordered_map db_; + std::unordered_map> db_; + + [[nodiscard]] Clock::time_point calculateExpirationTime(const Milliseconds &ttl) const; }; } // namespace libp2p::peer diff --git a/src/peer/user_agent_repository/inmem_user_agent_repository.cpp b/src/peer/user_agent_repository/inmem_user_agent_repository.cpp index 9b039429..f134250e 100644 --- a/src/peer/user_agent_repository/inmem_user_agent_repository.cpp +++ b/src/peer/user_agent_repository/inmem_user_agent_repository.cpp @@ -8,31 +8,53 @@ #include -#include - namespace libp2p::peer { - void InmemUserAgentRepository::setUserAgent(const PeerId &p, - std::string_view ua) { - db_.insert_or_assign(p, ua); + Clock::time_point InmemUserAgentRepository::calculateExpirationTime( + const Milliseconds &ttl) const { + static const auto max_time = Clock::time_point::max(); + const auto now = Clock::now(); + if (now >= max_time - ttl) { + return max_time; + } + return now + ttl; + } + + void InmemUserAgentRepository::updateUserAgent(const PeerId &p, + std::string_view ua, + Milliseconds ttl) { + db_.insert_or_assign( + p, std::pair(std::string(ua), calculateExpirationTime(ttl))); } - void InmemUserAgentRepository::unsetUserAgent(const PeerId &p) { - db_.erase(p); + void InmemUserAgentRepository::updateTtl(const PeerId &p, Milliseconds ttl) { + if (ttl > std::chrono::milliseconds::zero()) { + if (auto it = db_.find(p); it != db_.end()) { + it->second.second = calculateExpirationTime(ttl); + } + } else { + db_.erase(p); + } } std::optional InmemUserAgentRepository::getUserAgent( const PeerId &p) const { if (auto it = db_.find(p); it != db_.end()) { - return it->second; + return it->second.first; } return std::nullopt; } - std::unordered_set InmemUserAgentRepository::getPeers() const { - return db_ - | std::ranges::views::transform([](const auto &p) { return p.first; }) - | std::ranges::to>(); + void InmemUserAgentRepository::collectGarbage() { + auto now = Clock::now(); + for (auto it = db_.begin(); it != db_.end();) { + const auto &expiration_time = it->second.second; + if (now >= expiration_time) { + it = db_.erase(it); + } else { + ++it; + } + } } } // namespace libp2p::peer diff --git a/src/protocol/identify/identify.cpp b/src/protocol/identify/identify.cpp index db984637..2e8f7340 100644 --- a/src/protocol/identify/identify.cpp +++ b/src/protocol/identify/identify.cpp @@ -121,7 +121,8 @@ namespace libp2p::protocol { peer_id, message.listen_addresses, peer::ttl::kRecentlyConnected) .value(); auto &user_agent_repo = peer_repo.getUserAgentRepository(); - user_agent_repo.setUserAgent(peer_id, message.agent_version); + user_agent_repo.updateUserAgent( + peer_id, message.agent_version, peer::ttl::kRecentlyConnected); host_->getBus().getChannel().publish(message); co_return outcome::success();