diff --git a/src/rpc/command.h b/src/rpc/command.h deleted file mode 100644 --- a/src/rpc/command.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2018-2019 The Bitcoin developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_RPC_COMMAND_H -#define BITCOIN_RPC_COMMAND_H - -#include - -#include - -#include - -/** - * Base class for all RPC commands. - */ -class RPCCommand : public boost::noncopyable { -private: - const std::string name; - - /* - * Child classes should define dependencies as private members. - * These dependencies must not be changed, or successive calls to the same - * command will give unexpected behavior. - */ - - // TODO: Parameter definitions (these will be used to generate help - // messages as well) - -public: - RPCCommand(std::string nameIn) : name(nameIn) {} - virtual ~RPCCommand() {} - - virtual UniValue Execute(const UniValue &args) const = 0; - - const std::string &GetName() const { return name; }; -}; - -#endif // BITCOIN_RPC_COMMAND_H diff --git a/src/rpc/commandwithargscontext.h b/src/rpc/commandwithargscontext.h new file mode 100644 --- /dev/null +++ b/src/rpc/commandwithargscontext.h @@ -0,0 +1,33 @@ +// Copyright (c) 2018-2019 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPC_COMMAND_WITH_ARGS_CONTEXT_H +#define BITCOIN_RPC_COMMAND_WITH_ARGS_CONTEXT_H + +#include "commandwithrequestcontext.h" +#include "jsonrpcrequest.h" + +#include + +#include + +#include + +/** + * By default, use RPCCommandWithArgsContext as the parent class for new RPC + * command classes that only depend on RPC arguments. + */ +class RPCCommandWithArgsContext : public RPCCommandWithRequestContext { +public: + RPCCommandWithArgsContext(std::string nameIn) + : RPCCommandWithRequestContext(nameIn) {} + + virtual UniValue Execute(const UniValue &args) const = 0; + + UniValue Execute(const JSONRPCRequest &request) const final { + return Execute(request.params); + } +}; + +#endif // BITCOIN_RPC_COMMAND_WITH_ARGS_CONTEXT_H diff --git a/src/rpc/commandwithrequestcontext.h b/src/rpc/commandwithrequestcontext.h new file mode 100644 --- /dev/null +++ b/src/rpc/commandwithrequestcontext.h @@ -0,0 +1,49 @@ +// Copyright (c) 2018-2019 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPC_COMMAND_WITH_REQUEST_CONTEXT_H +#define BITCOIN_RPC_COMMAND_WITH_REQUEST_CONTEXT_H + +#include + +#include + +#include + +class JSONRPCRequest; + +/** + * Base class for all RPC commands. RPCCommandWithRequestContext should only + * be inherited from directly if access to the entire request context is + * necessary. For more typical cases where only request arguments are + * required, see the RPCCommandWithArgsContext class. + */ +class RPCCommandWithRequestContext : public boost::noncopyable { +private: + const std::string name; + + /* + * Child classes should define dependencies as private members. + * These dependencies must not be changed, or successive calls to the same + * command will give unexpected behavior. + */ + + // TODO: Parameter definitions (these will be used to generate help + // messages as well) + +public: + RPCCommandWithRequestContext(std::string nameIn) : name(nameIn) {} + virtual ~RPCCommandWithRequestContext() {} + + /** + * It is recommended to override Execute(JSONRPCRequest) only if the entire + * request context is required. Otherwise, use RPCCommandWithArgsContext + * instead. + */ + virtual UniValue Execute(const JSONRPCRequest &request) const = 0; + + const std::string &GetName() const { return name; }; +}; + +#endif // BITCOIN_RPC_COMMAND_WITH_REQUEST_CONTEXT_H diff --git a/src/rpc/server.h b/src/rpc/server.h --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -8,7 +8,7 @@ #define BITCOIN_RPC_SERVER_H #include "amount.h" -#include "rpc/command.h" +#include "rpc/commandwithrequestcontext.h" #include "rpc/jsonrpcrequest.h" #include "rpc/protocol.h" #include "rwcollection.h" @@ -49,7 +49,8 @@ UniValue::VType type; }; -typedef std::map> RPCCommandMap; +typedef std::map> + RPCCommandMap; /** * Class for registering and managing all RPC calls. @@ -71,7 +72,7 @@ /** * Register an RPC command. */ - void RegisterCommand(std::unique_ptr command); + void RegisterCommand(std::unique_ptr command); }; /** diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -55,7 +55,7 @@ auto commandsReadView = commands.getReadView(); auto iter = commandsReadView->find(commandName); if (iter != commandsReadView.end()) { - return iter->second.get()->Execute(request.params); + return iter->second.get()->Execute(request); } } @@ -66,7 +66,8 @@ return tableRPC.execute(config, request); } -void RPCServer::RegisterCommand(std::unique_ptr command) { +void RPCServer::RegisterCommand( + std::unique_ptr command) { if (command != nullptr) { const std::string &commandName = command->GetName(); commands.getWriteView()->insert( diff --git a/src/test/rpc_server_tests.cpp b/src/test/rpc_server_tests.cpp --- a/src/test/rpc_server_tests.cpp +++ b/src/test/rpc_server_tests.cpp @@ -4,6 +4,7 @@ #include "chainparams.h" #include "config.h" +#include "rpc/commandwithargscontext.h" #include "rpc/jsonrpcrequest.h" #include "rpc/server.h" #include "util.h" @@ -16,13 +17,14 @@ BOOST_FIXTURE_TEST_SUITE(rpc_server_tests, TestingSetup) -class ArgsTestRPCCommand : public RPCCommand { +class ArgsTestRPCCommand : public RPCCommandWithArgsContext { public: - ArgsTestRPCCommand(std::string nameIn) : RPCCommand(nameIn) {} + ArgsTestRPCCommand(std::string nameIn) + : RPCCommandWithArgsContext(nameIn) {} UniValue Execute(const UniValue &args) const override { BOOST_CHECK_EQUAL(args["arg1"].get_str(), "value1"); - return UniValue("testing"); + return UniValue("testing1"); } }; @@ -33,7 +35,7 @@ BOOST_AUTO_TEST_CASE(rpc_server_execute_command) { DummyConfig config; RPCServer rpcServer; - const std::string commandName = "testcommand"; + const std::string commandName = "testcommand1"; rpcServer.RegisterCommand(MakeUnique(commandName)); UniValue args(UniValue::VOBJ); @@ -44,7 +46,7 @@ request.strMethod = commandName; request.params = args; UniValue output = rpcServer.ExecuteCommand(config, request); - BOOST_CHECK_EQUAL(output.get_str(), "testing"); + BOOST_CHECK_EQUAL(output.get_str(), "testing1"); // Not-registered commands throw an exception as expected JSONRPCRequest badCommandRequest; @@ -53,4 +55,37 @@ UniValue, isRpcMethodNotFound); } +class RequestContextRPCCommand : public RPCCommandWithRequestContext { +public: + RequestContextRPCCommand(std::string nameIn) + : RPCCommandWithRequestContext(nameIn) {} + + // Sanity check that Execute(JSONRPCRequest) is called correctly from + // RPCServer + UniValue Execute(const JSONRPCRequest &request) const override { + const UniValue args = request.params; + BOOST_CHECK_EQUAL(request.strMethod, "testcommand2"); + BOOST_CHECK_EQUAL(args["arg2"].get_str(), "value2"); + return UniValue("testing2"); + } +}; + +BOOST_AUTO_TEST_CASE(rpc_server_execute_command_from_request_context) { + DummyConfig config; + RPCServer rpcServer; + const std::string commandName = "testcommand2"; + rpcServer.RegisterCommand( + MakeUnique(commandName)); + + UniValue args(UniValue::VOBJ); + args.pushKV("arg2", "value2"); + + // Registered commands execute and return values correctly + JSONRPCRequest request; + request.strMethod = commandName; + request.params = args; + UniValue output = rpcServer.ExecuteCommand(config, request); + BOOST_CHECK_EQUAL(output.get_str(), "testing2"); +} + BOOST_AUTO_TEST_SUITE_END()