RPA Toolkit
Added make_rpc_wrapper_const method.
[rpatk.git] / rexjsonrpc / rpcserver.h
1 /*
2  *  Sigmadrone
3  *  Copyright (c) 2013-2015 The Sigmadrone Developers
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  *  Martin Stoilov <martin@sigmadrone.org>
19  *  Svetoslav Vassilev <svassilev@sigmadrone.org>
20  */
21 #ifndef _RPCSERVER_H_
22 #define _RPCSERVER_H_
23
24 #include <string>
25 #include <map>
26 #include <stdexcept>
27 #include <iostream>
28 #include <functional>
29 #include "rexjson/rexjson++.h"
30
31 #ifndef ARRAYSIZE
32 #define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
33 #endif
34
35 namespace rexjson {
36
37 enum rpc_error_code
38 {
39     // Standard JSON-RPC 2.0 errors
40     RPC_INVALID_REQUEST  = -32600,
41     RPC_METHOD_NOT_FOUND = -32601,
42     RPC_INVALID_PARAMS   = -32602,
43     RPC_INTERNAL_ERROR   = -32603,
44     RPC_PARSE_ERROR      = -32700,
45
46     // General application defined errors
47     RPC_MISC_ERROR                  = -1,  // std::exception thrown in command handling
48 };
49
50 /*
51  * The rpc types correspond directly to the json value types like obj_type, array_type ...
52  * but they have values 2 pow n, so we can store them in unsigned int like (rpc_str_type|rpc_null_type)
53  * Then we use a vector of these bit masks to specify the parameter types we expect for
54  * the rpc calls.
55  *
56  * get_rpc_type will convert rexjson::value_type to one of the rpc types.
57  *
58  * create_json_spec will convert the rpc_type bit mask to an integer or array of integers
59  * correspoinding to rexjson value_type(s)
60  *
61  * rpc_types will convert json spec to a std::vector of rpc_type_ bitmasks.
62  *
63  */
64 static const unsigned int rpc_null_type = 1;
65 static const unsigned int rpc_obj_type = 2;
66 static const unsigned int rpc_array_type = 4;
67 static const unsigned int rpc_str_type = 8;
68 static const unsigned int rpc_bool_type = 16;
69 static const unsigned int rpc_int_type = 32;
70 static const unsigned int rpc_real_type = 64;
71
72
73 enum rpc_exec_mode {
74         execute = 0,    // normal execution
75         spec,                   // produce machine readable parameter specification
76         helpspec,               // produce a human readable parameter specification
77         help,                   // produce a help message
78 };
79
80 template <typename T> inline unsigned int get_rpc_type(T) { return rpc_int_type; }
81 template <> inline unsigned int get_rpc_type<bool>(bool) { return rpc_bool_type; }
82 template <> inline unsigned int get_rpc_type<double>(double) { return rpc_real_type; }
83 template <> inline unsigned int get_rpc_type<float>(float) { return rpc_real_type; }
84 template <> inline unsigned int get_rpc_type<int>(int) { return rpc_int_type; }
85 template <> inline unsigned int get_rpc_type<char>(char) { return rpc_int_type; }
86 template <> inline unsigned int get_rpc_type<std::string>(std::string) { return rpc_str_type; }
87
88
89 template<typename Ret, typename ...Args>
90 struct rpc_wrapperbase
91 {
92         rpc_wrapperbase(const std::function<Ret(Args...)>& f, std::string help_msg)
93                 : f_(f)
94                 , help_msg_(help_msg)
95         {
96                 json_types_ = make_types_array(std::tuple<Args...>(), std::index_sequence_for<Args...>());
97         }
98
99         rexjson::value call(const rexjson::array& params, rpc_exec_mode mode)
100         {
101                 auto is = std::index_sequence_for<Args...>();
102                 std::tuple<Args...> tuple = params_to_tuple(params, is);
103                 return call_with_tuple(tuple, is);
104         }
105
106         rexjson::value operator()(const rexjson::array& params, rpc_exec_mode mode)
107         {
108                 return call(params, mode);
109         }
110
111 protected:
112         template<std::size_t... is>
113         std::array<unsigned int, sizeof...(is)> make_types_array(const std::tuple<Args...>& tuple, std::index_sequence<is...>)
114         {
115                 return {{get_rpc_type(std::get<is>(tuple))...}};
116         }
117
118         template<std::size_t... is>
119         std::tuple<Args...> params_to_tuple(const rexjson::array& params, std::index_sequence<is...>)
120         {
121                 return std::make_tuple(Args{params[is].get_value<Args>()}...);
122         }
123
124         template<std::size_t... is>
125         Ret call_with_tuple(const std::tuple<Args...>& tuple, std::index_sequence<is...>)
126         {
127                 return f_(std::get<is>(tuple)...);
128         }
129
130         std::function<Ret(Args...)> f_;
131
132 public:
133         std::string help_msg_;
134         std::array<unsigned int, sizeof...(Args)> json_types_;
135 };
136
137 template<typename Ret, class ...Args>
138 struct rpc_wrapper : public rpc_wrapperbase<Ret, Args...>
139 {
140         using base = rpc_wrapperbase<Ret, Args...>;
141         using base::base;
142 };
143
144 template<class ...Args>
145 struct rpc_wrapper<void, Args...> : public rpc_wrapperbase<void, Args...>
146 {
147         using base = rpc_wrapperbase<void, Args...>;
148         using base::base;
149
150         rexjson::value call(const rexjson::array& params, rpc_exec_mode mode)
151         {
152                 auto is = std::index_sequence_for<Args...>();
153                 std::tuple<Args...> tuple = base::params_to_tuple(params, is);
154                 base::call_with_tuple(tuple, is);
155                 return std::string();
156         }
157
158         rexjson::value operator()(const rexjson::array& params, rpc_exec_mode mode)
159         {
160                 return call(params, mode);
161         }
162
163 };
164
165
166 template<typename Ret, class ...Args>
167 rpc_wrapper<Ret, Args...> make_rpc_wrapper(Ret (&f)(Args...), std::string help_msg = std::string())
168 {
169         return rpc_wrapper<Ret, Args...>(f, help_msg);
170 }
171
172 template<typename Ret, typename C, typename ...Args>
173 rpc_wrapper<Ret, Args...> make_rpc_wrapper(C* object, Ret (C::*f)(Args...), std::string help_msg = std::string())
174 {
175         return rpc_wrapper<Ret, Args...>([=](Args... args)->Ret {return (object->*f)(args...);}, help_msg);
176 }
177
178 template<typename Ret, typename C, typename ...Args>
179 rpc_wrapper<Ret, Args...> make_rpc_wrapper_const(const C* object, Ret (C::*f)(Args...) const, std::string help_msg = std::string())
180 {
181         return rpc_wrapper<Ret, Args...>([=](Args... args)->Ret {return (object->*f)(args...);}, help_msg);
182 }
183
184
185 struct rpc_server_dummy { };
186
187 template<typename T = rpc_server_dummy>
188 class rpc_server
189 {
190 public:
191
192         using rpc_method_type = std::function<rexjson::value(rexjson::array& params, rpc_exec_mode mode)>;
193         typedef std::map<std::string, rpc_method_type> method_map_type;
194
195         rpc_server()
196         {
197                 add("help", this, &rpc_server::rpc_help);
198                 add("spec", &rpc_server::rpc_spec);
199                 add("helpspec", &rpc_server::rpc_helpspec);
200         }
201
202         ~rpc_server()
203         {
204
205         }
206
207         rexjson::value rpc_spec(rexjson::array& params, rpc_exec_mode mode)
208         {
209                 static unsigned int types[] = { rpc_str_type };
210                 if (mode != execute) {
211                         if (mode == spec)
212                                 return create_json_spec(types, ARRAYSIZE(types));
213                         if (mode == helpspec)
214                                 return create_json_helpspec(types, ARRAYSIZE(types));
215                         return
216                                         "spec <\"name\">\n"
217                                         "\nGet the spec for the specified rpc name.\n"
218                                         "\nArguments:\n"
219                                         "1. \"name\"     (string, required) The name of of the rpc method to get the spec on\n"
220                                         "\nResult:\n"
221                                         "\"json\"     (string) The rpc call spec in json\n";
222                 }
223
224                 verify_parameters(params, types, ARRAYSIZE(types));
225                 rexjson::array ignored;
226                 return call_method_name(params[0], ignored, spec);
227         }
228
229         rexjson::value rpc_helpspec(rexjson::array& params, rpc_exec_mode mode)
230         {
231                 static unsigned int types[] = { rpc_str_type };
232                 if (mode != execute) {
233                         if (mode == spec)
234                                 return create_json_spec(types, ARRAYSIZE(types));
235                         if (mode == helpspec)
236                                 return create_json_helpspec(types, ARRAYSIZE(types));
237                         return
238                                         "helpspec <\"name\">\n"
239                                         "\nGet the helpspec for the specified rpc name.\n"
240                                         "\nArguments:\n"
241                                         "1. \"name\"     (string, required) The name of of the rpc method to get the spec on\n"
242                                         "\nResult:\n"
243                                         "\"json\"     (string) The rpc call helpspec\n";
244                 }
245
246                 verify_parameters(params, types, ARRAYSIZE(types));
247                 rexjson::array ignored;
248                 return call_method_name(params[0], ignored, helpspec);
249         }
250
251
252         rexjson::value rpc_help(rexjson::array& params, rpc_exec_mode mode)
253         {
254                 static unsigned int types[] = { (rpc_str_type | rpc_null_type) };
255                 if (mode != execute) {
256                         if (mode == spec)
257                                 return create_json_spec(types, ARRAYSIZE(types));
258                         if (mode == helpspec)
259                                 return create_json_helpspec(types, ARRAYSIZE(types));
260                         return
261                                         "help [\"command\"]\n"
262                                         "\nList all commands, or get help for a specified command.\n"
263                                         "\nArguments:\n"
264                                         "1. \"command\"     (string, optional) The command to get help on\n"
265                                         "\nResult:\n"
266                                         "\"text\"     (string) The help text\n";
267                 }
268
269                 verify_parameters(params, types, ARRAYSIZE(types));
270                 if (params[0].type() == rexjson::null_type) {
271                         std::string result;
272                         for (typename method_map_type::const_iterator it = map_.begin(); it != map_.end(); it++) {
273                                 result += it->first + "\n";
274
275 //                              rexjson::array ignored;
276 //                              std::string ret = call_method_name(rexjson::value(it->first), ignored, help).get_str();
277 //                              ret = ret.substr(ret.find('\n') + 1);
278 //                              result += ret.substr(0, ret.find('\n')) + "\n";
279                         }
280                         return result;
281                 }
282                 rexjson::array ignored;
283                 return call_method_name(params[0], ignored, help);
284         }
285
286
287         static unsigned int get_rpc_type(rexjson::value_type value_type)
288         {
289                 static const unsigned int rpc_types[] = {rpc_null_type, rpc_obj_type, rpc_array_type, rpc_str_type, rpc_bool_type, rpc_int_type, rpc_real_type};
290                 return rpc_types[value_type];
291         }
292
293         static rexjson::value create_json_spec(unsigned int *arr, size_t n)
294         {
295                 rexjson::array params;
296
297                 for (size_t i = 0; i < n; i++) {
298                         rexjson::array param;
299                         if (arr[i] & rpc_obj_type)
300                                 param.push_back(rexjson::obj_type);
301                         if (arr[i] & rpc_array_type)
302                                 param.push_back(rexjson::array_type);
303                         if (arr[i] & rpc_str_type)
304                                 param.push_back(rexjson::str_type);
305                         if (arr[i] & rpc_bool_type)
306                                 param.push_back(rexjson::bool_type);
307                         if (arr[i] & rpc_int_type)
308                                 param.push_back(rexjson::int_type);
309                         if (arr[i] & rpc_real_type)
310                                 param.push_back(rexjson::real_type);
311                         if (arr[i] & rpc_null_type)
312                                 param.push_back(rexjson::null_type);
313                         params.push_back((param.size() > 1) ? param : param[0]);
314                 }
315                 return params;
316         }
317
318         static std::vector<unsigned int> rpc_types(const rexjson::array& spec)
319         {
320                 std::vector<unsigned int> ret;
321
322                 for (size_t i = 0; i < spec.size(); i++) {
323                         if (spec[i].get_type() == rexjson::array_type) {
324                                 unsigned int rpc_types = 0;
325                                 for (size_t j = 0; j < spec[i].get_array().size(); j++) {
326                                         rpc_types |= get_rpc_type((rexjson::value_type)spec[i].get_array()[j].get_int());
327                                 }
328                                 ret.push_back(rpc_types);
329                         } else {
330                                 ret.push_back(get_rpc_type((rexjson::value_type)spec[i].get_int()));
331                         }
332                 }
333                 return ret;
334         }
335
336         static std::vector<unsigned int> rpc_types(const std::string strspec)
337         {
338                 rexjson::value jsonspec;
339
340                 jsonspec.read(strspec);
341                 return rpc_types(jsonspec.get_array());
342         }
343
344         static rexjson::value create_json_helpspec(unsigned int *arr, size_t n)
345         {
346                 rexjson::value ret = create_json_spec(arr, n);
347                 convert_types_to_strings(ret);
348                 return ret;
349         }
350
351         static rexjson::value noexec(rexjson::array& params, rpc_exec_mode mode, unsigned int *types, size_t ntypes, const std::string& help_msg)
352         {
353                 if (mode == spec)
354                         return create_json_spec(types, ntypes);
355                 if (mode == helpspec)
356                         return create_json_helpspec(types, ntypes);
357                 return help_msg;
358         }
359
360         static rexjson::object create_rpc_error(
361                         rpc_error_code code,
362                         const std::string& message)
363         {
364                 rexjson::object error;
365                 error["code"] = code;
366                 error["message"] = message;
367                 return error;
368         }
369
370         void add(const std::string& name, const rpc_method_type& method)
371         {
372                 if (name.empty())
373                         throw std::runtime_error("rpc_server::add, invalid name parameter");
374                 if (!method)
375                         throw std::runtime_error("rpc_server::add, invalid method parameter");
376                 map_[name] = method;
377         }
378
379         template <typename Type>
380         void add(const std::string& name, Type* object, rexjson::value (Type::*func)(rexjson::array& params, rpc_exec_mode mode))
381         {
382                 add(name, [=](rexjson::array& params, rpc_exec_mode mode)->rexjson::value{return (object->*func)(params, mode);});
383         }
384
385         void add(const std::string& name, rexjson::value (T::*func)(rexjson::array& params, rpc_exec_mode mode))
386         {
387                 add(name, [=](rexjson::array& params, rpc_exec_mode mode)->rexjson::value{return (static_cast <T*>(this)->*func)(params, mode);});
388         }
389
390         template <typename Ret, typename ...Args>
391         void add(const std::string& name, const rpc_wrapper<Ret, Args...>& wrap)
392         {
393                 add(name, [=](rexjson::array& params, rpc_exec_mode mode)->rexjson::value
394                 {
395                         rpc_wrapper<Ret, Args...> w = wrap;
396                         if (mode != execute)
397                                 return noexec(params, mode, w.json_types_.data(), w.json_types_.size(), w.help_msg_.c_str());
398                         return w.call(params, mode);
399                 });
400         }
401
402         rexjson::value call_method_name(const rexjson::value& methodname, rexjson::array& params, rpc_exec_mode mode = execute)
403         {
404                 if (methodname.get_type() != rexjson::str_type)
405                         throw create_rpc_error(RPC_INVALID_REQUEST, "method must be a string");
406                 typename method_map_type::const_iterator method_entry = map_.find(methodname.get_str());
407                 if (method_entry == map_.end())
408                         throw create_rpc_error(RPC_METHOD_NOT_FOUND, "method not found");
409                 return method_entry->second(params, mode);
410         }
411
412         rexjson::value call(const rexjson::value& val, rpc_exec_mode mode = execute)
413         {
414                 rexjson::object ret;
415                 rexjson::value result;
416                 rexjson::value error;
417                 rexjson::value id;
418                 rexjson::array params;
419
420                 try {
421                         if (val.get_type() != rexjson::obj_type)
422                                 throw create_rpc_error(RPC_PARSE_ERROR, "top-level object parse error");
423                         rexjson::object::const_iterator params_it = val.get_obj().find("params");
424                         if (params_it != val.get_obj().end() && params_it->second.type() != rexjson::array_type)
425                                 throw create_rpc_error(RPC_INVALID_REQUEST, "params must be an array");
426                         if (params_it != val.get_obj().end())
427                                 params = params_it->second.get_array();
428                         rexjson::object::const_iterator id_it = val.get_obj().find("id");
429                         if (id_it != val.get_obj().end())
430                                 id = id_it->second;
431                         rexjson::object::const_iterator method_it = val.get_obj().find("method");
432                         if (method_it == val.get_obj().end())
433                                 throw create_rpc_error(RPC_INVALID_REQUEST, "missing method");
434                         result = call_method_name(method_it->second, params, mode);
435                 } catch (rexjson::object& e) {
436                         error = e;
437                 } catch (std::exception& e) {
438                         rexjson::object errobj;
439                         errobj["message"] = e.what();
440                         errobj["code"] = RPC_MISC_ERROR;
441                         error = errobj;
442                 }
443                 ret["result"] = result;
444                 ret["id"] = id;
445                 ret["error"] = error;
446                 return ret;
447         }
448
449         rexjson::value call(const std::string& name, const rexjson::value& id, const rexjson::array& params, rpc_exec_mode mode = execute)
450         {
451                 rexjson::object req;
452                 req["id"] = id;
453                 req["method"] = rexjson::value(name);
454                 req["params"] = rexjson::value(params);
455                 return call(rexjson::value(req), mode);
456         }
457
458         rexjson::value call(const std::string& request, rpc_exec_mode mode = execute)
459         {
460                 rexjson::object ret;
461                 rexjson::value result;
462                 rexjson::value error;
463                 rexjson::value id;
464
465                 try {
466                         rexjson::value val;
467                         val.read(request);
468                         return call(val, mode);
469                 } catch (rexjson::object& e) {
470                         error = e;
471                 } catch (std::exception& e) {
472                         rexjson::object errobj;
473                         errobj["message"] = e.what();
474                         errobj["code"] = RPC_MISC_ERROR;
475                         error = errobj;
476                 }
477                 ret["result"] = result;
478                 ret["id"] = id;
479                 ret["error"] = error;
480                 return ret;
481         }
482
483 protected:
484         static void convert_types_to_strings(rexjson::value& val)
485         {
486                 if (val.get_type() == rexjson::int_type && val.get_int() >= rexjson::null_type && val.get_int() <= rexjson::real_type) {
487                         std::string strtypename = rexjson::value::get_typename(val.get_int());
488                         val = strtypename;
489                 } else if (val.get_type() == rexjson::array_type) {
490                         for (size_t i = 0; i < val.get_array().size(); i++) {
491                                 convert_types_to_strings(val.get_array()[i]);
492                         }
493                 }
494         }
495
496         static void verify_parameters(rexjson::array& params, unsigned int *types, size_t n)
497         {
498                 params.resize(n);
499                 for (size_t i = 0; i < n; i++) {
500                         rexjson::value_type value_type = params[i].get_type();
501                         if ((get_rpc_type(value_type) & types[i]) == 0) {
502                                 throw create_rpc_error(RPC_INVALID_PARAMS, "Invalid parameter: '" + params[i].write(false) + "'");
503                         }
504                 }
505         }
506
507 protected:
508         method_map_type map_;
509 };
510
511 } /* namespace rexjson */
512
513
514 #endif /* _RPCSERVER_H_ */