#include "rpc/dispatcher.h" #include #include "rpc/detail/client_error.h" #include "rpc/this_handler.h" namespace rpc { namespace detail { using detail::response; void dispatcher::dispatch(RPCLIB_MSGPACK::sbuffer const &msg) { RPCLIB_MSGPACK::unpacked unpacked; RPCLIB_MSGPACK::unpack(&unpacked, msg.data(), msg.size()); dispatch(unpacked.get()); } response dispatcher::dispatch(RPCLIB_MSGPACK::object const &msg, bool suppress_exceptions) { switch (msg.via.array.size) { case 3: return dispatch_notification(msg, suppress_exceptions); case 4: return dispatch_call(msg, suppress_exceptions); default: return response::empty(); } } response dispatcher::dispatch_call(RPCLIB_MSGPACK::object const &msg, bool suppress_exceptions) { call_t the_call; msg.convert(&the_call); // TODO: proper validation of protocol (and responding to it) // auto &&type = std::get<0>(the_call); // assert(type == 0); auto &&id = std::get<1>(the_call); auto &&name = std::get<2>(the_call); auto &&args = std::get<3>(the_call); auto it_func = funcs_.find(name); if (it_func != end(funcs_)) { LOG_DEBUG("Dispatching call to '{}'", name); try { auto result = (it_func->second)(args); return response::make_result(id, std::move(result)); } catch (rpc::detail::client_error &e) { return response::make_error( id, str(boost::format("rpclib: %s") % e.what())); } catch (std::exception &e) { if (!suppress_exceptions) { throw; } return response::make_error( id, str(boost::format("rpclib: function '%s' (called with %d " "arg(s)) " "threw an exception. The exception " "contained this information: %s.") % name % args.via.array.size % e.what())); } catch (rpc::detail::handler_error &) { // doing nothing, the exception was only thrown to // return immediately } catch (rpc::detail::handler_spec_response &) { // doing nothing, the exception was only thrown to // return immediately } catch (...) { if (!suppress_exceptions) { throw; } return response::make_error( id, str(boost::format("rpclib: function '%s' (called with %d " "arg(s)) threw an exception. The exception " "is not derived from std::exception. No " "further information available.") % name % args.via.array.size)); } } return response::make_error( id, str(boost::format("rpclib: server could not find " "function '%s' with argument count %d.") % name % args.via.array.size)); } response dispatcher::dispatch_notification(RPCLIB_MSGPACK::object const &msg, bool suppress_exceptions) { notification_t the_call; msg.convert(&the_call); // TODO: proper validation of protocol (and responding to it) // auto &&type = std::get<0>(the_call); // assert(type == static_cast(request_type::notification)); auto &&name = std::get<1>(the_call); auto &&args = std::get<2>(the_call); auto it_func = funcs_.find(name); if (it_func != end(funcs_)) { LOG_DEBUG("Dispatching call to '{}'", name); try { auto result = (it_func->second)(args); } catch (rpc::detail::handler_error &) { // doing nothing, the exception was only thrown to // return immediately } catch (rpc::detail::handler_spec_response &) { // doing nothing, the exception was only thrown to // return immediately } catch (...) { if (!suppress_exceptions) { throw; } } } return response::empty(); } void dispatcher::enforce_arg_count(std::string const &func, std::size_t found, std::size_t expected) { using detail::client_error; if (found != expected) { throw client_error( client_error::code::wrong_arity, str(boost::format( "Function '%s' was called with an invalid number of " "arguments. Expected: %d, got: %d") % func % expected % found)); } } } } /* rpc */