mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Support API versioning
This commit is contained in:
@@ -61,6 +61,7 @@
|
||||
#include <ripple/protocol/STParsedJSON.h>
|
||||
#include <ripple/protocol/Protocol.h>
|
||||
#include <ripple/resource/Fees.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <ripple/beast/asio/io_latency_probe.h>
|
||||
#include <ripple/beast/core/LexicalCast.h>
|
||||
|
||||
@@ -1583,7 +1584,8 @@ bool ApplicationImp::setup()
|
||||
Resource::Charge loadType = Resource::feeReferenceRPC;
|
||||
Resource::Consumer c;
|
||||
RPC::Context context { journal ("RPCHandler"), jvCommand, *this,
|
||||
loadType, getOPs (), getLedgerMaster (), c, Role::ADMIN};
|
||||
loadType, getOPs (), getLedgerMaster(), c, Role::ADMIN,
|
||||
RPC::ApiMaximumSupportedVersion};
|
||||
|
||||
Json::Value jvResult;
|
||||
RPC::doCommand (context, jvResult);
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <ripple/protocol/SystemParameters.h>
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <ripple/rpc/ServerHandler.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <ripple/beast/core/LexicalCast.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
@@ -1360,6 +1361,20 @@ rpcCmdLineToJson (std::vector<std::string> const& args,
|
||||
|
||||
jvRequest = rpParser.parseCommand (args[0], jvRpcParams, true);
|
||||
|
||||
auto insert_api_version = [](Json::Value & jr){
|
||||
if( jr.isObject() &&
|
||||
!jr.isMember(jss::error) &&
|
||||
!jr.isMember(jss::api_version))
|
||||
{
|
||||
jr[jss::api_version] = RPC::ApiMaximumSupportedVersion;
|
||||
}
|
||||
};
|
||||
|
||||
if(jvRequest.isObject())
|
||||
insert_api_version(jvRequest);
|
||||
else if(jvRequest.isArray())
|
||||
std::for_each(jvRequest.begin(), jvRequest.end(), insert_api_version);
|
||||
|
||||
JLOG (j.trace()) << "RPC Request: " << jvRequest << std::endl;
|
||||
return jvRequest;
|
||||
}
|
||||
|
||||
@@ -123,6 +123,8 @@ JSS ( alternatives ); // out: PathRequest, RipplePathFind
|
||||
JSS ( amendment_blocked ); // out: NetworkOPs
|
||||
JSS ( amendments ); // in: AccountObjects, out: NetworkOPs
|
||||
JSS ( amount ); // out: AccountChannels
|
||||
JSS ( api_version); // in: many, out: Version
|
||||
JSS ( api_version_low); // out: Version
|
||||
JSS ( applied ); // out: SubmitTransaction
|
||||
JSS ( asks ); // out: Subscribe
|
||||
JSS ( assets ); // out: GatewayBalances
|
||||
@@ -265,6 +267,8 @@ JSS ( index ); // in: LedgerEntry, DownloadShard
|
||||
// field
|
||||
JSS ( info ); // out: ServerInfo, ConsensusInfo, FetchInfo
|
||||
JSS ( internal_command ); // in: Internal
|
||||
JSS ( invalid_API_version ); // out: Many, when a request has an invalid
|
||||
// version
|
||||
JSS ( io_latency_ms ); // out: NetworkOPs
|
||||
JSS ( ip ); // in: Connect, out: OverlayImpl
|
||||
JSS ( issuer ); // in: RipplePathFind, Subscribe,
|
||||
|
||||
@@ -55,6 +55,7 @@ struct Context
|
||||
LedgerMaster& ledgerMaster;
|
||||
Resource::Consumer& consumer;
|
||||
Role role;
|
||||
unsigned int apiVersion;
|
||||
std::shared_ptr<JobQueue::Coro> coro {};
|
||||
InfoSub::pointer infoSub {};
|
||||
Headers headers {};
|
||||
|
||||
@@ -33,7 +33,7 @@ struct Context;
|
||||
/** Execute an RPC command and store the results in a Json::Value. */
|
||||
Status doCommand (RPC::Context&, Json::Value&);
|
||||
|
||||
Role roleRequired (std::string const& method );
|
||||
Role roleRequired (unsigned int version, std::string const& method );
|
||||
|
||||
} // RPC
|
||||
} // ripple
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <ripple/rpc/impl/Handler.h>
|
||||
#include <ripple/rpc/handlers/Handlers.h>
|
||||
#include <ripple/rpc/handlers/Version.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace RPC {
|
||||
@@ -129,16 +130,21 @@ class HandlerTable {
|
||||
explicit
|
||||
HandlerTable (const Handler(&entries)[N])
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
for(auto v = RPC::ApiMinimumSupportedVersion; v <= RPC::ApiMaximumSupportedVersion; ++v)
|
||||
{
|
||||
auto const& entry = entries[i];
|
||||
assert (table_.find(entry.name_) == table_.end());
|
||||
table_[entry.name_] = entry;
|
||||
}
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
auto & innerTable = table_[versionToIndex(v)];
|
||||
auto const& entry = entries[i];
|
||||
assert (innerTable.find(entry.name_) == innerTable.end());
|
||||
innerTable[entry.name_] = entry;
|
||||
}
|
||||
|
||||
// This is where the new-style handlers are added.
|
||||
addHandler<LedgerHandler>();
|
||||
addHandler<VersionHandler>();
|
||||
// This is where the new-style handlers are added.
|
||||
// This is also where different versions of handlers are added.
|
||||
addHandler<LedgerHandler>(v);
|
||||
addHandler<VersionHandler>(v);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -148,29 +154,38 @@ class HandlerTable {
|
||||
return handlerTable;
|
||||
}
|
||||
|
||||
Handler const* getHandler(std::string name) const
|
||||
Handler const* getHandler(unsigned version, std::string name) const
|
||||
{
|
||||
auto i = table_.find(name);
|
||||
return i == table_.end() ? nullptr : &i->second;
|
||||
if(version > RPC::ApiMaximumSupportedVersion || version < RPC::ApiMinimumSupportedVersion)
|
||||
return nullptr;
|
||||
auto & innerTable = table_[versionToIndex(version)];
|
||||
auto i = innerTable.find(name);
|
||||
return i == innerTable.end() ? nullptr : &i->second;
|
||||
}
|
||||
|
||||
std::vector<char const*>
|
||||
getHandlerNames() const
|
||||
{
|
||||
std::vector<char const*> ret;
|
||||
ret.reserve(table_.size());
|
||||
for (auto const& i : table_)
|
||||
ret.push_back(i.second.name_);
|
||||
return ret;
|
||||
std::unordered_set<char const*> name_set;
|
||||
for ( int index = 0; index < table_.size(); ++index)
|
||||
{
|
||||
for(auto const& h : table_[index])
|
||||
{
|
||||
name_set.insert(h.second.name_);
|
||||
}
|
||||
}
|
||||
return std::vector<char const*>(name_set.begin(), name_set.end());
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, Handler> table_;
|
||||
std::array<std::map<std::string, Handler>, APINumberVersionSupported> table_;
|
||||
|
||||
template <class HandlerImpl>
|
||||
void addHandler()
|
||||
void addHandler(unsigned version)
|
||||
{
|
||||
assert (table_.find(HandlerImpl::name()) == table_.end());
|
||||
assert (version >= RPC::ApiMinimumSupportedVersion && version <= RPC::ApiMaximumSupportedVersion);
|
||||
auto & innerTable = table_[versionToIndex(version)];
|
||||
assert (innerTable.find(HandlerImpl::name()) == innerTable.end());
|
||||
|
||||
Handler h;
|
||||
h.name_ = HandlerImpl::name();
|
||||
@@ -178,15 +193,20 @@ class HandlerTable {
|
||||
h.role_ = HandlerImpl::role();
|
||||
h.condition_ = HandlerImpl::condition();
|
||||
|
||||
table_[HandlerImpl::name()] = h;
|
||||
innerTable[HandlerImpl::name()] = h;
|
||||
}
|
||||
|
||||
inline unsigned versionToIndex(unsigned version) const
|
||||
{
|
||||
return version - RPC::ApiMinimumSupportedVersion;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Handler const* getHandler(std::string const& name)
|
||||
Handler const* getHandler(unsigned version, std::string const& name)
|
||||
{
|
||||
return HandlerTable::instance().getHandler(name);
|
||||
return HandlerTable::instance().getHandler(version, name);
|
||||
}
|
||||
|
||||
std::vector<char const*>
|
||||
|
||||
@@ -51,7 +51,7 @@ struct Handler
|
||||
RPC::Condition condition_;
|
||||
};
|
||||
|
||||
Handler const* getHandler (std::string const&);
|
||||
Handler const* getHandler (unsigned int version, std::string const&);
|
||||
|
||||
/** Return a Json::objectValue with a single entry. */
|
||||
template <class Value>
|
||||
|
||||
@@ -141,7 +141,7 @@ error_code_i fillHandler (Context& context,
|
||||
|
||||
JLOG (context.j.trace()) << "COMMAND:" << strCommand;
|
||||
JLOG (context.j.trace()) << "REQUEST:" << context.params;
|
||||
auto handler = getHandler(strCommand);
|
||||
auto handler = getHandler(context.apiVersion, strCommand);
|
||||
|
||||
if (!handler)
|
||||
return rpcUNKNOWN_COMMAND;
|
||||
@@ -296,9 +296,9 @@ Status doCommand (
|
||||
return rpcUNKNOWN_COMMAND;
|
||||
}
|
||||
|
||||
Role roleRequired (std::string const& method)
|
||||
Role roleRequired (unsigned int version, std::string const& method)
|
||||
{
|
||||
auto handler = RPC::getHandler(method);
|
||||
auto handler = RPC::getHandler(version, method);
|
||||
|
||||
if (!handler)
|
||||
return Role::FORBID;
|
||||
|
||||
@@ -716,5 +716,24 @@ beast::SemanticVersion const firstVersion("1.0.0");
|
||||
beast::SemanticVersion const goodVersion("1.0.0");
|
||||
beast::SemanticVersion const lastVersion("1.0.0");
|
||||
|
||||
unsigned int getAPIVersionNumber(Json::Value const& jv)
|
||||
{
|
||||
static Json::Value const minVersion (RPC::ApiMinimumSupportedVersion);
|
||||
static Json::Value const maxVersion (RPC::ApiMaximumSupportedVersion);
|
||||
static Json::Value const invalidVersion (RPC::APIInvalidVersion);
|
||||
|
||||
Json::Value requestedVersion(RPC::APIVersionIfUnspecified);
|
||||
if(jv.isObject())
|
||||
{
|
||||
requestedVersion = jv.get (jss::api_version, requestedVersion);
|
||||
}
|
||||
if( !(requestedVersion.isInt() || requestedVersion.isUInt()) ||
|
||||
requestedVersion < minVersion || requestedVersion > maxVersion)
|
||||
{
|
||||
requestedVersion = invalidVersion;
|
||||
}
|
||||
return requestedVersion.asUInt();
|
||||
}
|
||||
|
||||
} // RPC
|
||||
} // ripple
|
||||
|
||||
@@ -114,10 +114,42 @@ parseRippleLibSeed(Json::Value const& params);
|
||||
std::pair<PublicKey, SecretKey>
|
||||
keypairForSignature(Json::Value const& params, Json::Value& error);
|
||||
|
||||
/**
|
||||
* API version numbers used in API version 1
|
||||
*/
|
||||
extern beast::SemanticVersion const firstVersion;
|
||||
extern beast::SemanticVersion const goodVersion;
|
||||
extern beast::SemanticVersion const lastVersion;
|
||||
|
||||
/**
|
||||
* API version numbers used in later API versions
|
||||
*
|
||||
* Requests with a version number in the range
|
||||
* [ApiMinimumSupportedVersion, ApiMaximumSupportedVersion]
|
||||
* are supported.
|
||||
*
|
||||
* Network Requests without explicit version numbers use
|
||||
* APIVersionIfUnspecified. APIVersionIfUnspecified is 1,
|
||||
* because all the RPC requests with a version >= 2 must
|
||||
* explicitly specify the version in the requests.
|
||||
* Note that APIVersionIfUnspecified will be lower than
|
||||
* ApiMinimumSupportedVersion when we stop supporting API
|
||||
* version 1.
|
||||
*
|
||||
* Command line Requests use ApiMaximumSupportedVersion.
|
||||
*/
|
||||
|
||||
constexpr unsigned int APIInvalidVersion = 0;
|
||||
constexpr unsigned int APIVersionIfUnspecified = 1;
|
||||
constexpr unsigned int ApiMinimumSupportedVersion = 1;
|
||||
constexpr unsigned int ApiMaximumSupportedVersion = 1;
|
||||
constexpr unsigned int APINumberVersionSupported = ApiMaximumSupportedVersion -
|
||||
ApiMinimumSupportedVersion + 1;
|
||||
|
||||
static_assert (ApiMinimumSupportedVersion >= APIVersionIfUnspecified);
|
||||
static_assert (ApiMaximumSupportedVersion >= ApiMinimumSupportedVersion);
|
||||
|
||||
|
||||
template <class Object>
|
||||
void
|
||||
setVersion(Object& parent)
|
||||
@@ -131,6 +163,20 @@ setVersion(Object& parent)
|
||||
std::pair<RPC::Status, LedgerEntryType>
|
||||
chooseLedgerEntryType(Json::Value const& params);
|
||||
|
||||
/**
|
||||
* Retrieve the api version number from the json value
|
||||
*
|
||||
* Note that APIInvalidVersion will be returned if
|
||||
* 1) the version number field has a wrong format
|
||||
* 2) the version number retrieved is out of the supported range
|
||||
* 3) the version number is unspecified and
|
||||
* APIVersionIfUnspecified is out of the supported range
|
||||
*
|
||||
* @param value a Json value that may or may not specifies
|
||||
* the api version number
|
||||
* @return the api version number
|
||||
*/
|
||||
unsigned int getAPIVersionNumber(const Json::Value & value);
|
||||
} // RPC
|
||||
} // ripple
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <ripple/server/Server.h>
|
||||
#include <ripple/server/impl/JSONRPCUtil.h>
|
||||
#include <ripple/rpc/impl/ServerHandlerImp.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/make_SSLContext.h>
|
||||
@@ -398,7 +399,9 @@ ServerHandlerImp::processSession(
|
||||
Resource::Charge loadType = Resource::feeReferenceRPC;
|
||||
try
|
||||
{
|
||||
if ((!jv.isMember(jss::command) && !jv.isMember(jss::method)) ||
|
||||
auto apiVersion = RPC::getAPIVersionNumber(jv);
|
||||
if (apiVersion == RPC::APIInvalidVersion ||
|
||||
(!jv.isMember(jss::command) && !jv.isMember(jss::method)) ||
|
||||
(jv.isMember(jss::command) && !jv[jss::command].isString()) ||
|
||||
(jv.isMember(jss::method) && !jv[jss::method].isString()) ||
|
||||
(jv.isMember(jss::command) && jv.isMember(jss::method) &&
|
||||
@@ -406,7 +409,8 @@ ServerHandlerImp::processSession(
|
||||
{
|
||||
jr[jss::type] = jss::response;
|
||||
jr[jss::status] = jss::error;
|
||||
jr[jss::error] = jss::missingCommand;
|
||||
jr[jss::error] = apiVersion == RPC::APIInvalidVersion ?
|
||||
jss::invalid_API_version : jss::missingCommand;
|
||||
jr[jss::request] = jv;
|
||||
if (jv.isMember (jss::id))
|
||||
jr[jss::id] = jv[jss::id];
|
||||
@@ -414,12 +418,15 @@ ServerHandlerImp::processSession(
|
||||
jr[jss::jsonrpc] = jv[jss::jsonrpc];
|
||||
if (jv.isMember(jss::ripplerpc))
|
||||
jr[jss::ripplerpc] = jv[jss::ripplerpc];
|
||||
if (jv.isMember(jss::api_version))
|
||||
jr[jss::api_version] = jv[jss::api_version];
|
||||
|
||||
is->getConsumer().charge(Resource::feeInvalidRPC);
|
||||
return jr;
|
||||
}
|
||||
|
||||
auto required = RPC::roleRequired(jv.isMember(jss::command) ?
|
||||
auto required = RPC::roleRequired(apiVersion,
|
||||
jv.isMember(jss::command) ?
|
||||
jv[jss::command].asString() :
|
||||
jv[jss::method].asString());
|
||||
auto role = requestRole(
|
||||
@@ -444,6 +451,7 @@ ServerHandlerImp::processSession(
|
||||
app_.getLedgerMaster(),
|
||||
is->getConsumer(),
|
||||
role,
|
||||
apiVersion,
|
||||
coro,
|
||||
is,
|
||||
{is->user(), is->forwarded_for()}
|
||||
@@ -500,6 +508,9 @@ ServerHandlerImp::processSession(
|
||||
jr[jss::jsonrpc] = jv[jss::jsonrpc];
|
||||
if (jv.isMember(jss::ripplerpc))
|
||||
jr[jss::ripplerpc] = jv[jss::ripplerpc];
|
||||
if (jv.isMember(jss::api_version))
|
||||
jr[jss::api_version] = jv[jss::api_version];
|
||||
|
||||
jr[jss::type] = jss::response;
|
||||
return jr;
|
||||
}
|
||||
@@ -545,6 +556,7 @@ make_json_error(Json::Int code, Json::Value&& message)
|
||||
Json::Int constexpr method_not_found = -32601;
|
||||
Json::Int constexpr server_overloaded = -32604;
|
||||
Json::Int constexpr forbidden = -32605;
|
||||
Json::Int constexpr wrong_version = -32606;
|
||||
|
||||
void
|
||||
ServerHandlerImp::processRequest (Port const& port,
|
||||
@@ -597,11 +609,40 @@ ServerHandlerImp::processRequest (Port const& port,
|
||||
continue;
|
||||
}
|
||||
|
||||
auto apiVersion = RPC::APIVersionIfUnspecified;
|
||||
if (jsonRPC.isMember(jss::params) &&
|
||||
jsonRPC[jss::params].isArray() &&
|
||||
jsonRPC[jss::params].size() > 0 &&
|
||||
jsonRPC[jss::params][0u].isObject())
|
||||
{
|
||||
apiVersion = RPC::getAPIVersionNumber(jsonRPC[jss::params][Json::UInt(0)]);
|
||||
}
|
||||
|
||||
if ( apiVersion == RPC::APIVersionIfUnspecified && batch)
|
||||
{
|
||||
// for batch request, api_version may be at a different level
|
||||
apiVersion = RPC::getAPIVersionNumber(jsonRPC);
|
||||
}
|
||||
|
||||
if(apiVersion == RPC::APIInvalidVersion)
|
||||
{
|
||||
if (!batch)
|
||||
{
|
||||
HTTPReply (400, jss::invalid_API_version.c_str(), output, rpcJ);
|
||||
return;
|
||||
}
|
||||
Json::Value r(Json::objectValue);
|
||||
r[jss::request] = jsonRPC;
|
||||
r[jss::error] = make_json_error(wrong_version, jss::invalid_API_version.c_str());
|
||||
reply.append(r);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
auto role = Role::FORBID;
|
||||
auto required = Role::FORBID;
|
||||
if (jsonRPC.isMember(jss::method) && jsonRPC[jss::method].isString())
|
||||
required = RPC::roleRequired(jsonRPC[jss::method].asString());
|
||||
required = RPC::roleRequired(apiVersion, jsonRPC[jss::method].asString());
|
||||
|
||||
if (jsonRPC.isMember(jss::params) &&
|
||||
jsonRPC[jss::params].isArray() &&
|
||||
@@ -778,7 +819,7 @@ ServerHandlerImp::processRequest (Port const& port,
|
||||
Resource::Charge loadType = Resource::feeReferenceRPC;
|
||||
|
||||
RPC::Context context {m_journal, params, app_, loadType, m_networkOPs,
|
||||
app_.getLedgerMaster(), usage, role, coro, InfoSub::pointer(),
|
||||
app_.getLedgerMaster(), usage, role, apiVersion, coro, InfoSub::pointer(),
|
||||
{user, forwardedFor}};
|
||||
Json::Value result;
|
||||
RPC::doCommand (context, result);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <ripple/resource/Fees.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/impl/Tuning.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <ripple/rpc/RPCHandler.h>
|
||||
#include <test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
@@ -221,7 +222,8 @@ public:
|
||||
Resource::Charge loadType = Resource::feeReferenceRPC;
|
||||
Resource::Consumer c;
|
||||
RPC::Context context { env.journal, {}, app, loadType,
|
||||
app.getOPs(), app.getLedgerMaster(), c, Role::USER};
|
||||
app.getOPs(), app.getLedgerMaster(), c, Role::USER,
|
||||
RPC::APIVersionIfUnspecified};
|
||||
|
||||
Json::Value params = Json::objectValue;
|
||||
params[jss::command] = "ripple_path_find";
|
||||
@@ -320,7 +322,8 @@ public:
|
||||
Resource::Charge loadType = Resource::feeReferenceRPC;
|
||||
Resource::Consumer c;
|
||||
RPC::Context context {env.journal, {}, app, loadType,
|
||||
app.getOPs(), app.getLedgerMaster(), c, Role::USER};
|
||||
app.getOPs(), app.getLedgerMaster(), c, Role::USER,
|
||||
RPC::APIVersionIfUnspecified};
|
||||
Json::Value result;
|
||||
gate g;
|
||||
// Test RPC::Tuning::max_src_cur source currencies.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
200
src/test/rpc/Version_test.cpp
Normal file
200
src/test/rpc/Version_test.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2017 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Version_test : public beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
testCorrectVersionNumber()
|
||||
{
|
||||
testcase ("right api_version: explicitly specified or filled by parser");
|
||||
|
||||
using namespace test::jtx;
|
||||
Env env {*this};
|
||||
|
||||
auto isCorrectReply = [](Json::Value const & re) -> bool
|
||||
{
|
||||
if(re.isMember(jss::error))
|
||||
return false;
|
||||
return re.isMember(jss::version);
|
||||
};
|
||||
|
||||
auto jrr = env.rpc("json", "version", "{\"api_version\": " +
|
||||
std::to_string(RPC::ApiMaximumSupportedVersion)
|
||||
+ "}") [jss::result];
|
||||
BEAST_EXPECT(isCorrectReply(jrr));
|
||||
|
||||
jrr = env.rpc("version") [jss::result];
|
||||
BEAST_EXPECT(isCorrectReply(jrr));
|
||||
}
|
||||
|
||||
void
|
||||
testWrongVersionNumber()
|
||||
{
|
||||
testcase ("wrong api_version: too low, too high, or wrong format");
|
||||
|
||||
using namespace test::jtx;
|
||||
Env env {*this};
|
||||
|
||||
auto get_error_what = [](Json::Value const &re) -> std::string
|
||||
{
|
||||
if (re.isMember("error_what"))
|
||||
if (re["error_what"].isString())
|
||||
return re["error_what"].asString();
|
||||
return {};
|
||||
};
|
||||
|
||||
auto re = env.rpc("json", "version", "{\"api_version\": " +
|
||||
std::to_string(RPC::ApiMinimumSupportedVersion - 1)
|
||||
+ "}");
|
||||
BEAST_EXPECT(get_error_what(re).find(jss::invalid_API_version.c_str()));
|
||||
|
||||
re = env.rpc("json", "version", "{\"api_version\": " +
|
||||
std::to_string(RPC::ApiMaximumSupportedVersion + 1)
|
||||
+ "}");
|
||||
BEAST_EXPECT(get_error_what(re).find(jss::invalid_API_version.c_str()));
|
||||
|
||||
re = env.rpc("json", "version", "{\"api_version\": \"a\"}");
|
||||
BEAST_EXPECT(get_error_what(re).find(jss::invalid_API_version.c_str()));
|
||||
}
|
||||
|
||||
void testGetAPIVersionNumber()
|
||||
{
|
||||
testcase ("test getAPIVersionNumber function");
|
||||
|
||||
unsigned int versionIfUnspecified =
|
||||
RPC::APIVersionIfUnspecified < RPC::ApiMinimumSupportedVersion ?
|
||||
RPC::APIInvalidVersion : RPC::APIVersionIfUnspecified;
|
||||
|
||||
Json::Value j_array = Json::Value (Json::arrayValue);
|
||||
Json::Value j_null = Json::Value (Json::nullValue);
|
||||
BEAST_EXPECT(RPC::getAPIVersionNumber(j_array) == versionIfUnspecified);
|
||||
BEAST_EXPECT(RPC::getAPIVersionNumber(j_null) == versionIfUnspecified);
|
||||
|
||||
Json::Value j_object = Json::Value (Json::objectValue);
|
||||
BEAST_EXPECT(RPC::getAPIVersionNumber(j_object) == versionIfUnspecified);
|
||||
j_object[jss::api_version] = RPC::APIVersionIfUnspecified;
|
||||
BEAST_EXPECT(RPC::getAPIVersionNumber(j_object) == versionIfUnspecified);
|
||||
|
||||
j_object[jss::api_version] = RPC::ApiMinimumSupportedVersion;
|
||||
BEAST_EXPECT(RPC::getAPIVersionNumber(j_object) == RPC::ApiMinimumSupportedVersion);
|
||||
j_object[jss::api_version] = RPC::ApiMaximumSupportedVersion;
|
||||
BEAST_EXPECT(RPC::getAPIVersionNumber(j_object) == RPC::ApiMaximumSupportedVersion);
|
||||
|
||||
j_object[jss::api_version] = RPC::ApiMinimumSupportedVersion - 1;
|
||||
BEAST_EXPECT(RPC::getAPIVersionNumber(j_object) == RPC::APIInvalidVersion);
|
||||
j_object[jss::api_version] = RPC::ApiMaximumSupportedVersion + 1;
|
||||
BEAST_EXPECT(RPC::getAPIVersionNumber(j_object) == RPC::APIInvalidVersion);
|
||||
|
||||
j_object[jss::api_version] = RPC::APIInvalidVersion;
|
||||
BEAST_EXPECT(RPC::getAPIVersionNumber(j_object) == RPC::APIInvalidVersion);
|
||||
j_object[jss::api_version] = "a";
|
||||
BEAST_EXPECT(RPC::getAPIVersionNumber(j_object) == RPC::APIInvalidVersion);
|
||||
}
|
||||
|
||||
void
|
||||
testBatch()
|
||||
{
|
||||
testcase ("batch, all good request");
|
||||
|
||||
using namespace test::jtx;
|
||||
Env env {*this};
|
||||
|
||||
auto const without_api_verion = std::string ("{ ") +
|
||||
"\"jsonrpc\": \"2.0\", "
|
||||
"\"ripplerpc\": \"2.0\", "
|
||||
"\"id\": 5, "
|
||||
"\"method\": \"version\", "
|
||||
"\"params\": {}}";
|
||||
auto const with_api_verion = std::string ("{ ") +
|
||||
"\"jsonrpc\": \"2.0\", "
|
||||
"\"ripplerpc\": \"2.0\", "
|
||||
"\"id\": 6, "
|
||||
"\"method\": \"version\", "
|
||||
"\"params\": { "
|
||||
"\"api_version\": " + std::to_string(RPC::ApiMaximumSupportedVersion) +
|
||||
"}}";
|
||||
auto re = env.rpc("json2", '[' + without_api_verion + ", " +
|
||||
with_api_verion + ']');
|
||||
|
||||
if( !BEAST_EXPECT( re.isArray() ))
|
||||
return;
|
||||
if( !BEAST_EXPECT( re.size() == 2 ))
|
||||
return;
|
||||
BEAST_EXPECT(re[0u].isMember(jss::result) &&
|
||||
re[0u][jss::result].isMember(jss::version));
|
||||
BEAST_EXPECT(re[1u].isMember(jss::result) &&
|
||||
re[1u][jss::result].isMember(jss::version));
|
||||
}
|
||||
|
||||
void
|
||||
testBatchFail()
|
||||
{
|
||||
testcase ("batch, with a bad request");
|
||||
|
||||
using namespace test::jtx;
|
||||
Env env {*this};
|
||||
|
||||
auto const without_api_verion = std::string ("{ ") +
|
||||
"\"jsonrpc\": \"2.0\", "
|
||||
"\"ripplerpc\": \"2.0\", "
|
||||
"\"id\": 5, "
|
||||
"\"method\": \"version\", "
|
||||
"\"params\": {}}";
|
||||
auto const with_wrong_api_verion = std::string ("{ ") +
|
||||
"\"jsonrpc\": \"2.0\", "
|
||||
"\"ripplerpc\": \"2.0\", "
|
||||
"\"id\": 6, "
|
||||
"\"method\": \"version\", "
|
||||
"\"params\": { "
|
||||
"\"api_version\": " +
|
||||
std::to_string(RPC::ApiMaximumSupportedVersion + 1) +
|
||||
"}}";
|
||||
auto re = env.rpc("json2", '[' + without_api_verion + ", " +
|
||||
with_wrong_api_verion + ']');
|
||||
|
||||
if( !BEAST_EXPECT( re.isArray() ))
|
||||
return;
|
||||
if( !BEAST_EXPECT( re.size() == 2 ))
|
||||
return;
|
||||
BEAST_EXPECT(re[0u].isMember(jss::result) &&
|
||||
re[0u][jss::result].isMember(jss::version));
|
||||
BEAST_EXPECT(re[1u].isMember(jss::error));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void run() override
|
||||
{
|
||||
testCorrectVersionNumber();
|
||||
testWrongVersionNumber();
|
||||
testGetAPIVersionNumber();
|
||||
testBatch();
|
||||
testBatchFail();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Version,rpc,ripple);
|
||||
|
||||
} // ripple
|
||||
@@ -53,3 +53,4 @@
|
||||
#include <test/rpc/TransactionEntry_test.cpp>
|
||||
#include <test/rpc/TransactionHistory_test.cpp>
|
||||
#include <test/rpc/ValidatorRPC_test.cpp>
|
||||
#include <test/rpc/Version_test.cpp>
|
||||
|
||||
Reference in New Issue
Block a user