5 #ifndef PACKIO_JSON_RPC_RPC_H 
    6 #define PACKIO_JSON_RPC_RPC_H 
   11 #include <boost/json.hpp> 
   14 #include "../args_specs.h" 
   15 #include "../internal/config.h" 
   16 #include "../internal/expected.h" 
   17 #include "../internal/log.h" 
   18 #include "../internal/rpc.h" 
   19 #include "converters.h" 
   26 template <
typename... Args>
 
   27 constexpr 
bool positional_args_v = (!is_arg_v<Args> && ...);
 
   29 template <
typename... Args>
 
   30 constexpr 
bool named_args_v = 
sizeof...(Args) > 0 && (is_arg_v<Args> && ...);
 
   32 using id_type = boost::json::value;
 
   33 using native_type = boost::json::value;
 
   34 using string_type = boost::json::string;
 
   35 using packio::internal::expected;
 
   36 using packio::internal::unexpected;
 
   57         : parser_{std::make_unique<boost::json::stream_parser>()}
 
   62     expected<request, std::string> get_request()
 
   64         if (parsed_.empty()) {
 
   65             return unexpected{
"no request parsed"};
 
   67         auto object = std::move(parsed_.front());
 
   69         return parse_request(std::move(
object.as_object()));
 
   72     expected<response, std::string> get_response()
 
   74         if (parsed_.empty()) {
 
   75             return unexpected{
"no response parsed"};
 
   77         auto object = std::move(parsed_.front());
 
   79         return parse_response(std::move(
object.as_object()));
 
   84         return buffer_.data();
 
   87     std::size_t buffer_capacity()
 const 
   89         return buffer_.size();
 
   92     void buffer_consumed(std::size_t bytes)
 
   94         std::size_t parsed = 0;
 
   95         while (parsed < bytes) {
 
   96             parsed += parser_->write_some(
 
   97                 buffer_.data() + parsed, bytes - parsed);
 
   98             if (parser_->done()) {
 
   99                 parsed_.push(parser_->release());
 
  105     void reserve_buffer(std::size_t bytes)
 
  107         buffer_.resize(bytes);
 
  111     static expected<response, std::string> parse_response(boost::json::object&& res)
 
  113         auto id_it = res.find(
"id");
 
  114         auto result_it = res.find(
"result");
 
  115         auto error_it = res.find(
"error");
 
  117         if (id_it == res.end()) {
 
  118             return unexpected{
"missing id field"};
 
  120         if (result_it == res.end() && error_it == res.end()) {
 
  121             return unexpected{
"missing error and result field"};
 
  125         parsed.id = std::move(id_it->value());
 
  126         if (error_it != res.end()) {
 
  127             parsed.error = std::move(error_it->value());
 
  129         if (result_it != res.end()) {
 
  130             parsed.result = std::move(result_it->value());
 
  132         return {std::move(parsed)};
 
  135     static expected<request, std::string> parse_request(boost::json::object&& req)
 
  137         auto id_it = req.find(
"id");
 
  138         auto method_it = req.find(
"method");
 
  139         auto params_it = req.find(
"params");
 
  141         if (method_it == req.end()) {
 
  142             return unexpected{
"missing method field"};
 
  144         if (!method_it->value().is_string()) {
 
  145             return unexpected{
"method field is not a string"};
 
  149         parsed.method = std::string{
 
  150             method_it->value().get_string().data(),
 
  151             method_it->value().get_string().size(),
 
  153         if (params_it == req.end() || params_it->value().is_null()) {
 
  154             parsed.args = boost::json::array{};
 
  156         else if (!params_it->value().is_array() && !params_it->value().is_object()) {
 
  157             return unexpected{
"non-structured arguments are not supported"};
 
  160             parsed.args = std::move(params_it->value());
 
  163         if (id_it == req.end() || id_it->value().is_null()) {
 
  164             parsed.type = call_type::notification;
 
  167             parsed.type = call_type::request;
 
  168             parsed.id = std::move(id_it->value());
 
  170         return {std::move(parsed)};
 
  173     std::vector<char> buffer_;
 
  174     std::queue<boost::json::value> parsed_;
 
  175     std::unique_ptr<boost::json::stream_parser> parser_;
 
  198     static std::string format_id(
const id_type& 
id)
 
  200         return boost::json::serialize(
id);
 
  203     template <
typename... Args>
 
  204     static auto serialize_notification(std::string_view method, Args&&... args)
 
  205         -> std::enable_if_t<internal::positional_args_v<Args...>, std::string>
 
  207         auto res = boost::json::serialize(boost::json::object({
 
  210             {
"params", {boost::json::value_from(std::forward<Args>(args))...}},
 
  212         PACKIO_TRACE(
"notification: " + res);
 
  216     template <
typename... Args>
 
  217     static auto serialize_notification(std::string_view method, Args&&... args)
 
  218         -> std::enable_if_t<internal::named_args_v<Args...>, std::string>
 
  220         auto res = boost::json::serialize(boost::json::object({
 
  223             {
"params", {{args.name, boost::json::value_from(args.value)}...}},
 
  225         PACKIO_TRACE(
"notification: " + res);
 
  229     template <
typename... Args>
 
  230     static auto serialize_notification(std::string_view, Args&&...) -> std::enable_if_t<
 
  231         !internal::positional_args_v<Args...> && !internal::named_args_v<Args...>,
 
  235             internal::positional_args_v<Args...> || internal::named_args_v<Args...>,
 
  236             "JSON-RPC does not support mixed named and unnamed arguments");
 
  239     template <
typename... Args>
 
  240     static auto serialize_request(
 
  242         std::string_view method,
 
  244         -> std::enable_if_t<internal::positional_args_v<Args...>, std::string>
 
  246         auto res = boost::json::serialize(boost::json::object({
 
  249             {
"params", {boost::json::value_from(std::forward<Args>(args))...}},
 
  252         PACKIO_TRACE(
"request: " + res);
 
  256     template <
typename... Args>
 
  257     static auto serialize_request(
 
  259         std::string_view method,
 
  261         -> std::enable_if_t<internal::named_args_v<Args...>, std::string>
 
  263         auto res = boost::json::serialize(boost::json::object({
 
  266             {
"params", {{args.name, boost::json::value_from(args.value)}...}},
 
  269         PACKIO_TRACE(
"request: " + res);
 
  273     template <
typename... Args>
 
  274     static auto serialize_request(
const id_type&, std::string_view, Args&&...)
 
  276             !internal::positional_args_v<Args...> && !internal::named_args_v<Args...>,
 
  280             internal::positional_args_v<Args...> || internal::named_args_v<Args...>,
 
  281             "JSON-RPC does not support mixed named and unnamed arguments");
 
  284     static std::string serialize_response(
const id_type& 
id)
 
  286         return serialize_response(
id, boost::json::value{});
 
  289     template <
typename T>
 
  290     static std::string serialize_response(
const id_type& 
id, T&& value)
 
  292         auto res = boost::json::serialize(boost::json::object({
 
  295             {
"result", boost::json::value_from(std::forward<T>(value))},
 
  297         PACKIO_TRACE(
"response: " + res);
 
  301     template <
typename T>
 
  302     static std::string serialize_error_response(
const id_type& 
id, T&& value)
 
  304         auto res = boost::json::serialize(boost::json::object({
 
  309                  boost::json::object error = {
 
  311                      {
"data", std::forward<T>(value)},
 
  313                  if (error[
"data"].is_string()) {
 
  314                      error[
"message"] = error[
"data"];
 
  317                      error[
"message"] = 
"unknown error";
 
  322         PACKIO_TRACE(
"response: " + res);
 
  326     static net::const_buffer buffer(
const std::string& buf)
 
  328         return net::const_buffer(buf.data(), buf.size());
 
  331     template <
typename T, 
typename F>
 
  332     static internal::expected<T, std::string> extract_args(
 
  333         boost::json::value&& args,
 
  334         const args_specs<F>& specs)
 
  337             if (args.is_array()) {
 
  338                 return convert_positional_args<T>(args.get_array(), specs);
 
  340             else if (args.is_object()) {
 
  341                 return convert_named_args<T>(args.get_object(), specs);
 
  344                 throw std::runtime_error{
"arguments are not a structured type"};
 
  347         catch (
const std::exception& exc) {
 
  348             return internal::unexpected{
 
  349                 std::string{
"cannot convert arguments: "} + exc.what()};
 
  354     template <
typename T, 
typename F>
 
  355     static constexpr T convert_positional_args(
 
  356         const boost::json::array& array,
 
  357         const args_specs<F>& specs)
 
  359         return convert_positional_args<T>(
 
  360             array, specs, std::make_index_sequence<args_specs<F>::size()>());
 
  363     template <
typename T, 
typename F, std::size_t... Idxs>
 
  364     static constexpr T convert_positional_args(
 
  365         const boost::json::array& array,
 
  366         const args_specs<F>& specs,
 
  367         std::index_sequence<Idxs...>)
 
  369         if (!specs.options().allow_extra_arguments
 
  370             && array.size() > std::tuple_size_v<T>) {
 
  371             throw std::runtime_error{
"too many arguments"};
 
  374             if (Idxs < array.size()) {
 
  376                     return boost::json::value_to<std::tuple_element_t<Idxs, T>>(
 
  379                 catch (
const boost::system::system_error&) {
 
  380                     throw std::runtime_error{
 
  381                         "invalid type for argument " 
  382                         + specs.template get<Idxs>().name()};
 
  385             if (
const auto& value = specs.template get<Idxs>().default_value()) {
 
  388             throw std::runtime_error{
 
  389                 "no value for argument " + specs.template get<Idxs>().name()};
 
  393     template <
typename T, 
typename F>
 
  394     static constexpr T convert_named_args(
 
  395         const boost::json::object& args,
 
  396         const args_specs<F>& specs)
 
  398         return convert_named_args<T>(
 
  399             args, specs, std::make_index_sequence<args_specs<F>::size()>());
 
  402     template <
typename T, 
typename F, std::size_t... Idxs>
 
  403     static constexpr T convert_named_args(
 
  404         const boost::json::object& args,
 
  405         const args_specs<F>& specs,
 
  406         std::index_sequence<Idxs...>)
 
  408         if (!specs.options().allow_extra_arguments) {
 
  409             const std::array<
const std::string*, 
sizeof...(Idxs)>
 
  410                 available_arguments = {&specs.template get<Idxs>().name()...};
 
  411             for (
const auto& item : args) {
 
  412                 auto it = std::find_if(
 
  413                     available_arguments.begin(),
 
  414                     available_arguments.end(),
 
  415                     [&](
const std::string* arg) { return *arg == item.key(); });
 
  416                 if (it == available_arguments.end()) {
 
  417                     throw std::runtime_error{
 
  418                         "unexpected argument " + std::string(item.key())};
 
  424             auto it = args.find(specs.template get<Idxs>().name());
 
  425             if (it != args.end()) {
 
  427                     return boost::json::value_to<std::tuple_element_t<Idxs, T>>(
 
  430                 catch (
const boost::system::system_error&) {
 
  431                     throw std::runtime_error{
 
  432                         "invalid type for argument " 
  433                         + specs.template get<Idxs>().name()};
 
  436             if (
const auto& value = specs.template get<Idxs>().default_value()) {
 
  439             throw std::runtime_error{
 
  440                 "no value for argument " + specs.template get<Idxs>().name()};
 
The incremental parser for JSON-RPC objects.
Definition: rpc.h:54
 
The JSON-RPC protocol implementation.
Definition: rpc.h:181
 
internal::native_type native_type
The native type of the serialization library.
Definition: rpc.h:187
 
internal::id_type id_type
Type of the call ID.
Definition: rpc.h:184
 
The packio namespace.
Definition: arg.h:14
 
The object representing a client request.
Definition: rpc.h:39
 
The object representing the response to a call.
Definition: rpc.h:47