1 // Aleth: Ethereum C++ client, tools and libraries.
2 // Copyright 2015-2019 Aleth Authors.
3 // Licensed under the GNU General Public License, Version 3.
4 
5 #pragma once
6 
7 #include <map>
8 #include <memory>
9 #include <string>
10 #include <tuple>
11 #include <vector>
12 
13 #include <jsonrpccpp/common/exception.h>
14 #include <jsonrpccpp/common/procedure.h>
15 #include <jsonrpccpp/server/abstractserverconnector.h>
16 #include <jsonrpccpp/server/iprocedureinvokationhandler.h>
17 #include <jsonrpccpp/server/requesthandlerfactory.h>
18 
19 template <class I> using AbstractMethodPointer = void(I::*)(Json::Value const& _parameter, Json::Value& _result);
20 template <class I> using AbstractNotificationPointer = void(I::*)(Json::Value const& _parameter);
21 
22 template <class I>
23 class ServerInterface
24 {
25 public:
26     using MethodPointer = AbstractMethodPointer<I>;
27     using NotificationPointer = AbstractNotificationPointer<I>;
28 
29     using MethodBinding = std::tuple<jsonrpc::Procedure, AbstractMethodPointer<I>>;
30     using NotificationBinding = std::tuple<jsonrpc::Procedure, AbstractNotificationPointer<I>>;
31     using Methods = std::vector<MethodBinding>;
32     using Notifications = std::vector<NotificationBinding>;
33     struct RPCModule { std::string name; std::string version; };
34     using RPCModules = std::vector<RPCModule>;
35 
~ServerInterface()36     virtual ~ServerInterface() {}
methods()37     Methods const& methods() const { return m_methods; }
notifications()38     Notifications const& notifications() const { return m_notifications; }
39     /// @returns which interfaces (eth, admin, db, ...) this class implements in which version.
40     virtual RPCModules implementedModules() const = 0;
41 
42 protected:
bindAndAddMethod(jsonrpc::Procedure const & _proc,MethodPointer _pointer)43     void bindAndAddMethod(jsonrpc::Procedure const& _proc, MethodPointer _pointer) { m_methods.emplace_back(_proc, _pointer); }
bindAndAddNotification(jsonrpc::Procedure const & _proc,NotificationPointer _pointer)44     void bindAndAddNotification(jsonrpc::Procedure const& _proc, NotificationPointer _pointer) { m_notifications.emplace_back(_proc, _pointer); }
45 
46 private:
47     Methods m_methods;
48     Notifications m_notifications;
49 };
50 
51 template <class... Is>
52 class ModularServer: public jsonrpc::IProcedureInvokationHandler
53 {
54 public:
ModularServer()55     ModularServer()
56     : m_handler(jsonrpc::RequestHandlerFactory::createProtocolHandler(jsonrpc::JSONRPC_SERVER_V2, *this))
57     {
58         m_handler->AddProcedure(jsonrpc::Procedure("rpc_modules", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, NULL));
59         m_implementedModules = Json::objectValue;
60     }
modules(const Json::Value & request,Json::Value & response)61     inline virtual void modules(const Json::Value &request, Json::Value &response)
62     {
63         (void)request;
64         response = m_implementedModules;
65     }
66 
~ModularServer()67     virtual ~ModularServer() { StopListening(); }
68 
StartListening()69     virtual void StartListening()
70     {
71         for (auto const& connector: m_connectors)
72             connector->StartListening();
73     }
74 
StopListening()75     virtual void StopListening()
76     {
77         for (auto const& connector: m_connectors)
78             connector->StopListening();
79     }
80 
HandleMethodCall(jsonrpc::Procedure & _proc,Json::Value const & _input,Json::Value & _output)81     virtual void HandleMethodCall(jsonrpc::Procedure& _proc, Json::Value const& _input, Json::Value& _output) override
82     {
83         if (_proc.GetProcedureName() == "rpc_modules")
84             modules(_input, _output);
85     }
86 
HandleNotificationCall(jsonrpc::Procedure & _proc,Json::Value const & _input)87     virtual void HandleNotificationCall(jsonrpc::Procedure& _proc, Json::Value const& _input) override
88     {
89         (void)_proc;
90         (void)_input;
91     }
92 
93     /// server takes ownership of the connector
addConnector(jsonrpc::AbstractServerConnector * _connector)94     unsigned addConnector(jsonrpc::AbstractServerConnector* _connector)
95     {
96         m_connectors.emplace_back(_connector);
97         _connector->SetHandler(m_handler.get());
98         return m_connectors.size() - 1;
99     }
100 
connector(unsigned _i)101     jsonrpc::AbstractServerConnector* connector(unsigned _i) const
102     {
103         return m_connectors.at(_i).get();
104     }
105 
106 protected:
107     std::vector<std::unique_ptr<jsonrpc::AbstractServerConnector>> m_connectors;
108     std::unique_ptr<jsonrpc::IProtocolHandler> m_handler;
109     /// Mapping for implemented modules, to be filled by subclasses during construction.
110     Json::Value m_implementedModules;
111 };
112 
113 template <class I, class... Is>
114 class ModularServer<I, Is...> : public ModularServer<Is...>
115 {
116 public:
117     using MethodPointer = AbstractMethodPointer<I>;
118     using NotificationPointer = AbstractNotificationPointer<I>;
119 
120     ModularServer<I, Is...>(I* _i, Is*... _is): ModularServer<Is...>(_is...), m_interface(_i)
121     {
122         if (!m_interface)
123             return;
124         for (auto const& method: m_interface->methods())
125         {
126             m_methods[std::get<0>(method).GetProcedureName()] = std::get<1>(method);
127             this->m_handler->AddProcedure(std::get<0>(method));
128         }
129 
130         for (auto const& notification: m_interface->notifications())
131         {
132             m_notifications[std::get<0>(notification).GetProcedureName()] = std::get<1>(notification);
133             this->m_handler->AddProcedure(std::get<0>(notification));
134         }
135         // Store module with version.
136         for (auto const& module: m_interface->implementedModules())
137             this->m_implementedModules[module.name] = module.version;
138     }
139 
HandleMethodCall(jsonrpc::Procedure & _proc,Json::Value const & _input,Json::Value & _output)140     virtual void HandleMethodCall(jsonrpc::Procedure& _proc, Json::Value const& _input, Json::Value& _output) override
141     {
142         auto pointer = m_methods.find(_proc.GetProcedureName());
143         if (pointer != m_methods.end())
144         {
145             try
146             {
147                 (m_interface.get()->*(pointer->second))(_input, _output);
148             }
149             catch (Json::Exception const& ex)
150             {
151                 throw jsonrpc::JsonRpcException(
152                     jsonrpc::Errors::ERROR_RPC_INVALID_PARAMS, ex.what());
153             }
154         }
155         else
156             ModularServer<Is...>::HandleMethodCall(_proc, _input, _output);
157     }
158 
HandleNotificationCall(jsonrpc::Procedure & _proc,Json::Value const & _input)159     virtual void HandleNotificationCall(
160         jsonrpc::Procedure& _proc, Json::Value const& _input) override
161     {
162         auto pointer = m_notifications.find(_proc.GetProcedureName());
163         if (pointer != m_notifications.end())
164         {
165             try
166             {
167                 (m_interface.get()->*(pointer->second))(_input);
168             }
169             catch (Json::Exception const& ex)
170             {
171                 throw jsonrpc::JsonRpcException(
172                     jsonrpc::Errors::ERROR_RPC_INVALID_PARAMS, ex.what());
173             }
174         }
175         else
176             ModularServer<Is...>::HandleNotificationCall(_proc, _input);
177     }
178 
179 private:
180     std::unique_ptr<I> m_interface;
181     std::map<std::string, MethodPointer> m_methods;
182     std::map<std::string, NotificationPointer> m_notifications;
183 };
184