diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/rpc/server.cpp | 65 | ||||
-rw-r--r-- | src/rpc/server.h | 1 |
2 files changed, 57 insertions, 9 deletions
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 8a223d8aa8..1b94e10071 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -26,6 +26,7 @@ #include <boost/algorithm/string/case_conv.hpp> // for to_upper() #include <memory> // for unique_ptr +#include <unordered_map> using namespace RPCServer; using namespace std; @@ -268,11 +269,11 @@ UniValue stop(const JSONRPCRequest& jsonRequest) * Call Table */ static const CRPCCommand vRPCCommands[] = -{ // category name actor (function) okSafeMode - // --------------------- ------------------------ ----------------------- ---------- +{ // category name actor (function) okSafe argNames + // --------------------- ------------------------ ----------------------- ------ ---------- /* Overall control/query calls */ - { "control", "help", &help, true }, - { "control", "stop", &stop, true }, + { "control", "help", &help, true, {"command"} }, + { "control", "stop", &stop, true, {} }, }; CRPCTable::CRPCTable() @@ -379,12 +380,12 @@ void JSONRPCRequest::parse(const UniValue& valRequest) // Parse params UniValue valParams = find_value(request, "params"); - if (valParams.isArray()) - params = valParams.get_array(); + if (valParams.isArray() || valParams.isObject()) + params = valParams; else if (valParams.isNull()) params = UniValue(UniValue::VARR); else - throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); + throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object"); } static UniValue JSONRPCExecOne(const UniValue& req) @@ -420,6 +421,48 @@ std::string JSONRPCExecBatch(const UniValue& vReq) return ret.write() + "\n"; } +/** + * Process named arguments into a vector of positional arguments, based on the + * passed-in specification for the RPC call's arguments. + */ +static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::string>& argNames) +{ + JSONRPCRequest out = in; + out.params = UniValue(UniValue::VARR); + // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if + // there is an unknown one. + const std::vector<std::string>& keys = in.params.getKeys(); + const std::vector<UniValue>& values = in.params.getValues(); + std::unordered_map<std::string, const UniValue*> argsIn; + for (size_t i=0; i<keys.size(); ++i) { + argsIn[keys[i]] = &values[i]; + } + // Process expected parameters. + int hole = 0; + for (const std::string &argName: argNames) { + auto fr = argsIn.find(argName); + if (fr != argsIn.end()) { + for (int i = 0; i < hole; ++i) { + // Fill hole between specified parameters with JSON nulls, + // but not at the end (for backwards compatibility with calls + // that act based on number of specified parameters). + out.params.push_back(UniValue()); + } + hole = 0; + out.params.push_back(*fr->second); + argsIn.erase(fr); + } else { + hole += 1; + } + } + // If there are still arguments in the argsIn map, this is an error. + if (!argsIn.empty()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first); + } + // Return request with named arguments transformed to positional arguments + return out; +} + UniValue CRPCTable::execute(const JSONRPCRequest &request) const { // Return immediately if in warmup @@ -438,8 +481,12 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const try { - // Execute - return pcmd->actor(request); + // Execute, convert arguments to array if necessary + if (request.params.isObject()) { + return pcmd->actor(transformNamedArguments(request, pcmd->argNames)); + } else { + return pcmd->actor(request); + } } catch (const std::exception& e) { diff --git a/src/rpc/server.h b/src/rpc/server.h index f3cdf3c602..fed3d8c90f 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -136,6 +136,7 @@ public: std::string name; rpcfn_type actor; bool okSafeMode; + std::vector<std::string> argNames; }; /** |