1 #include "rpc/dispatcher.h"
2 #include <boost/format.hpp>
3 #include "rpc/detail/client_error.h"
4 #include "rpc/this_handler.h"
5 
6 namespace rpc {
7 namespace detail {
8 
9 using detail::response;
10 
dispatch(RPCLIB_MSGPACK::sbuffer const & msg)11 void dispatcher::dispatch(RPCLIB_MSGPACK::sbuffer const &msg) {
12     RPCLIB_MSGPACK::unpacked unpacked;
13     RPCLIB_MSGPACK::unpack(&unpacked, msg.data(), msg.size());
14     dispatch(unpacked.get());
15 }
16 
dispatch(RPCLIB_MSGPACK::object const & msg,bool suppress_exceptions)17 response dispatcher::dispatch(RPCLIB_MSGPACK::object const &msg,
18                               bool suppress_exceptions) {
19     switch (msg.via.array.size) {
20     case 3:
21         return dispatch_notification(msg, suppress_exceptions);
22     case 4:
23         return dispatch_call(msg, suppress_exceptions);
24     default:
25         return response::empty();
26     }
27 }
28 
dispatch_call(RPCLIB_MSGPACK::object const & msg,bool suppress_exceptions)29 response dispatcher::dispatch_call(RPCLIB_MSGPACK::object const &msg,
30                                    bool suppress_exceptions) {
31     call_t the_call;
32     msg.convert(&the_call);
33 
34     // TODO: proper validation of protocol (and responding to it)
35     // auto &&type = std::get<0>(the_call);
36     // assert(type == 0);
37 
38     auto &&id = std::get<1>(the_call);
39     auto &&name = std::get<2>(the_call);
40     auto &&args = std::get<3>(the_call);
41 
42     auto it_func = funcs_.find(name);
43 
44     if (it_func != end(funcs_)) {
45         LOG_DEBUG("Dispatching call to '{}'", name);
46         try {
47             auto result = (it_func->second)(args);
48             return response::make_result(id, std::move(result));
49         } catch (rpc::detail::client_error &e) {
50             return response::make_error(
51                 id, str(boost::format("rpclib: %s") % e.what()));
52         } catch (std::exception &e) {
53             if (!suppress_exceptions) {
54                 throw;
55             }
56             return response::make_error(
57                 id,
58                 str(boost::format("rpclib: function '%s' (called with %d "
59                                    "arg(s)) "
60                                    "threw an exception. The exception "
61                               "contained this information: %s.") %
62                                    name % args.via.array.size % e.what()));
63         } catch (rpc::detail::handler_error &) {
64             // doing nothing, the exception was only thrown to
65             // return immediately
66         } catch (rpc::detail::handler_spec_response &) {
67             // doing nothing, the exception was only thrown to
68             // return immediately
69         } catch (...) {
70             if (!suppress_exceptions) {
71                 throw;
72             }
73             return response::make_error(
74                 id,
75                 str(boost::format("rpclib: function '%s' (called with %d "
76                                    "arg(s)) threw an exception. The exception "
77                                    "is not derived from std::exception. No "
78                               "further information available.") %
79                                    name % args.via.array.size));
80         }
81     }
82     return response::make_error(
83         id, str(boost::format("rpclib: server could not find "
84                           "function '%s' with argument count %d.") %
85                                name % args.via.array.size));
86 }
87 
dispatch_notification(RPCLIB_MSGPACK::object const & msg,bool suppress_exceptions)88 response dispatcher::dispatch_notification(RPCLIB_MSGPACK::object const &msg,
89                                            bool suppress_exceptions) {
90     notification_t the_call;
91     msg.convert(&the_call);
92 
93     // TODO: proper validation of protocol (and responding to it)
94     // auto &&type = std::get<0>(the_call);
95     // assert(type == static_cast<uint8_t>(request_type::notification));
96 
97     auto &&name = std::get<1>(the_call);
98     auto &&args = std::get<2>(the_call);
99 
100     auto it_func = funcs_.find(name);
101 
102     if (it_func != end(funcs_)) {
103         LOG_DEBUG("Dispatching call to '{}'", name);
104         try {
105             auto result = (it_func->second)(args);
106         } catch (rpc::detail::handler_error &) {
107             // doing nothing, the exception was only thrown to
108             // return immediately
109         } catch (rpc::detail::handler_spec_response &) {
110             // doing nothing, the exception was only thrown to
111             // return immediately
112         } catch (...) {
113             if (!suppress_exceptions) {
114                 throw;
115             }
116         }
117     }
118     return response::empty();
119 }
120 
enforce_arg_count(std::string const & func,std::size_t found,std::size_t expected)121 void dispatcher::enforce_arg_count(std::string const &func, std::size_t found,
122                                    std::size_t expected) {
123     using detail::client_error;
124     if (found != expected) {
125         throw client_error(
126             client_error::code::wrong_arity,
127             str(boost::format(
128                 "Function '%s' was called with an invalid number of "
129                 "arguments. Expected: %d, got: %d") %
130                 func % expected % found));
131     }
132 }
133 
134 }
135 } /* rpc */
136