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