From 079202ea713ae8479742e3892ab12c43735b16ae Mon Sep 17 00:00:00 2001 From: Bronek Kozicki <823856+Bronek@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:39:53 +0100 Subject: [PATCH] Add support for handlers version selection --- src/ripple/rpc/handlers/LedgerHandler.h | 23 +++---- src/ripple/rpc/handlers/Version.h | 23 +++---- src/ripple/rpc/impl/Handler.cpp | 83 ++++++++++++++++++++----- src/ripple/rpc/impl/Handler.h | 4 ++ src/ripple/rpc/impl/RPCHelpers.h | 2 + 5 files changed, 89 insertions(+), 46 deletions(-) diff --git a/src/ripple/rpc/handlers/LedgerHandler.h b/src/ripple/rpc/handlers/LedgerHandler.h index 77b361d3466..efa54187b76 100644 --- a/src/ripple/rpc/handlers/LedgerHandler.h +++ b/src/ripple/rpc/handlers/LedgerHandler.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace Json { class Object; @@ -58,23 +59,15 @@ class LedgerHandler void writeResult(Object&); - static char const* - name() - { - return "ledger"; - } + static constexpr char const name[] = "ledger"; - static Role - role() - { - return Role::USER; - } + static constexpr unsigned minApiVer = 1; - static Condition - condition() - { - return NO_CONDITION; - } + static constexpr unsigned maxApiVer = RPC::apiMaximumValidVersion; + + static constexpr Role role = Role::USER; + + static constexpr Condition condition = NO_CONDITION; private: JsonContext& context_; diff --git a/src/ripple/rpc/handlers/Version.h b/src/ripple/rpc/handlers/Version.h index a9f42b94993..8c320749553 100644 --- a/src/ripple/rpc/handlers/Version.h +++ b/src/ripple/rpc/handlers/Version.h @@ -20,6 +20,7 @@ #ifndef RIPPLED_RIPPLE_RPC_HANDLERS_VERSION_H #define RIPPLED_RIPPLE_RPC_HANDLERS_VERSION_H +#include #include namespace ripple { @@ -46,23 +47,15 @@ class VersionHandler setVersion(obj, apiVersion_, betaEnabled_); } - static char const* - name() - { - return "version"; - } + static constexpr char const* name = "version"; - static Role - role() - { - return Role::USER; - } + static constexpr unsigned minApiVer = 1; - static Condition - condition() - { - return NO_CONDITION; - } + static constexpr unsigned maxApiVer = RPC::apiMaximumValidVersion; + + static constexpr Role role = Role::USER; + + static constexpr Condition condition = NO_CONDITION; private: unsigned int apiVersion_; diff --git a/src/ripple/rpc/impl/Handler.cpp b/src/ripple/rpc/impl/Handler.cpp index dd898ee8722..df7ac4de098 100644 --- a/src/ripple/rpc/impl/Handler.cpp +++ b/src/ripple/rpc/impl/Handler.cpp @@ -22,6 +22,8 @@ #include #include +#include + namespace ripple { namespace RPC { namespace { @@ -57,6 +59,19 @@ handle(JsonContext& context, Object& object) return status; }; +template +Handler +handlerFrom() +{ + return { + HandlerImpl::name, + &handle, + HandlerImpl::role, + HandlerImpl::condition, + HandlerImpl::minApiVer, + HandlerImpl::maxApiVer}; +} + Handler const handlerArray[]{ // Some handlers not specified here are added to the table via addHandler() // Request-response methods @@ -174,14 +189,39 @@ Handler const handlerArray[]{ class HandlerTable { private: + using handler_table_t = std::multimap; + + // Use with equal_range to enforce that API range of a newly added handler + // does not overlap with API range of an existing handler with same name + bool + overlappingApiVersion( + std::pair range, + unsigned minVer, + unsigned maxVer) + { + assert(minVer <= maxVer); + assert(maxVer <= RPC::apiMaximumValidVersion); + + for (; range.first != range.second; range.first++) + { + if (range.first->second.minApiVer_ <= maxVer && + range.first->second.maxApiVer_ >= minVer) + return true; + } + return false; + } + template explicit HandlerTable(const Handler (&entries)[N]) { - for (std::size_t i = 0; i < N; ++i) + for (auto const& entry : entries) { - auto const& entry = entries[i]; - assert(table_.find(entry.name_) == table_.end()); - table_[entry.name_] = entry; + assert(!overlappingApiVersion( + table_.equal_range(entry.name_), + entry.minApiVer_, + entry.maxApiVer_)); + + table_.insert({entry.name_, entry}); } // This is where the new-style handlers are added. @@ -205,36 +245,47 @@ class HandlerTable version > (betaEnabled ? RPC::apiBetaVersion : RPC::apiMaximumSupportedVersion)) return nullptr; - auto i = table_.find(name); - return i == table_.end() ? nullptr : &i->second; + + auto const range = table_.equal_range(name); + auto const i = std::find_if( + range.first, range.second, [version](auto const& entry) { + return version >= entry.second.minApiVer_ && + version <= entry.second.maxApiVer_; + }); + + return i == range.second ? nullptr : &i->second; } std::vector getHandlerNames() const { std::vector ret; - ret.reserve(table_.size()); for (auto const& i : table_) - ret.push_back(i.second.name_); + { + // Note, table_ is always ordered, allowing such a simple check + if (ret.empty() || std::strcmp(ret.back(), i.second.name_) != 0) + ret.push_back(i.second.name_); + } + return ret; } private: - std::map table_; + handler_table_t table_; template void addHandler() { - assert(table_.find(HandlerImpl::name()) == table_.end()); + static_assert(HandlerImpl::minApiVer <= HandlerImpl::maxApiVer); + static_assert(HandlerImpl::maxApiVer <= RPC::apiMaximumValidVersion); - Handler h; - h.name_ = HandlerImpl::name(); - h.valueMethod_ = &handle; - h.role_ = HandlerImpl::role(); - h.condition_ = HandlerImpl::condition(); + assert(!overlappingApiVersion( + table_.equal_range(HandlerImpl::name), + HandlerImpl::minApiVer, + HandlerImpl::maxApiVer)); - table_[HandlerImpl::name()] = h; + table_.insert({HandlerImpl::name, handlerFrom()}); } }; diff --git a/src/ripple/rpc/impl/Handler.h b/src/ripple/rpc/impl/Handler.h index e2188ef51e7..9c4008bb6e7 100644 --- a/src/ripple/rpc/impl/Handler.h +++ b/src/ripple/rpc/impl/Handler.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -52,6 +53,9 @@ struct Handler Method valueMethod_; Role role_; RPC::Condition condition_; + + unsigned minApiVer_ = 1; + unsigned maxApiVer_ = apiMaximumValidVersion; }; Handler const* diff --git a/src/ripple/rpc/impl/RPCHelpers.h b/src/ripple/rpc/impl/RPCHelpers.h index eb02e6ea37a..516f66fc620 100644 --- a/src/ripple/rpc/impl/RPCHelpers.h +++ b/src/ripple/rpc/impl/RPCHelpers.h @@ -242,10 +242,12 @@ constexpr unsigned int apiVersionIfUnspecified = 1; constexpr unsigned int apiMinimumSupportedVersion = 1; constexpr unsigned int apiMaximumSupportedVersion = 1; constexpr unsigned int apiBetaVersion = 2; +constexpr unsigned int apiMaximumValidVersion = apiBetaVersion; static_assert(apiMinimumSupportedVersion >= apiVersionIfUnspecified); static_assert(apiMaximumSupportedVersion >= apiMinimumSupportedVersion); static_assert(apiBetaVersion >= apiMaximumSupportedVersion); +static_assert(apiMaximumValidVersion >= apiMaximumSupportedVersion); template void