Giter Site home page Giter Site logo

json-rpc-cxx's Introduction

json-rpc-cxx

Build status CircleCI GitHub codecov Language grade: C/C++ Codacy Badge GitHub tag (latest SemVer)

json-rpc-cxx-icon

A JSON-RPC 2.0 framework implemented in C++17 using the nlohmann's json for modern C++.

  • JSON-RPC 2.0 compliant client
  • JSON-RPC 2.0 compliant server
  • Transport agnostic interfaces
  • Compile time type mapping (using nlohmann's arbitrary type conversion)
  • Runtime type checking
  • Cross-platform (Windows, Linux, OSX)

Installation

mkdir build && cd build
cmake ..
sudo make install

Usage

Design goals

  • Easy to use interface
  • Type safety where possible
  • Avoid errors at compile time where possible
  • Test driven development
  • Choose expressiveness over speed
  • Minimal dependencies

License

This framework is licensed under MIT.

Dependencies

Developer information

json-rpc-cxx's People

Contributors

cinemast avatar freesurfer-rge avatar guruofquality avatar jgriffiths avatar meesong avatar yanivmo avatar zamazan4ik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

json-rpc-cxx's Issues

1.0 Send / Receive functions interface suggestion to support more use cases

Thank you for your work.

Not sure if you plan to do a 1.0 one day, but in case you do I wanted to suggest to do a more general interface then.

Since your library heavily depends on nlohman/json, it might be a good idea to just have the interfaces pass nlohman json objects instead of strings and leave the serialization / deserialization of those to the caller / consumer.

The reason for that idea is that it would allow to use other features, e.g. such as reading from / writing to streams: https://github.com/nlohmann/json#tofrom-streams-eg-files-string-streams

[BUG] - Using GetHandle with a member function using a const qualifier doesn't work

Say I have an object:

class Foo {
public:
    int bar() const {return 42;}
};

Using the MethodHandle GetHandle(&Foo::bar, foo) templated function doesn't work because there is no templated function for const functions.

Could you please add the following to your list of templated types:

namespace jsonrpccxx
{
template<typename T, typename ReturnType, typename... ParamTypes>
MethodHandle GetHandle(ReturnType (T::*method)(ParamTypes...) const, const T &instance)
{
    std::function<ReturnType(ParamTypes...)> function = [&instance, method](ParamTypes &&...params) -> ReturnType {
        return (instance.*method)(std::forward<ParamTypes>(params)...);
    };
    return GetHandle(function);
}

template<typename T, typename... ParamTypes>
NotificationHandle GetHandle(void (T::*method)(ParamTypes...) const, const T &instance)
{
    std::function<void(ParamTypes...)> function = [&instance, method](ParamTypes &&...params) -> void {
        return (instance.*method)(std::forward<ParamTypes>(params)...);
    };
    return GetHandle(function);
}
} // namespace jsonrpccxx

This will allow const functions of a const instance to be called as well.

How to use it for event listening?

I know it s not a bug, but I found the doc folder pretty much empty.

How to perform this from the client?

I m having a client interacting with a server through events. That means, that I send a request to the server which replies Ok to it and the server sends several others json response for each received events and this is on those events my client need to perform things.

The problem is I m having several different subscriptions, and I need the right function to be called when receiving an event. I m meaning the one matching the subscription.

compile error on VS2019 MFC app

Compiling code on VS2019 MFC app typemapper.hpp will got error.

class WarehouseServer
{
public:
    WarehouseServer() : products() {}

    bool AddProduct(const Product& p);
    const Product& Getproduct(const std::string& id);
    std::vector<Product> AllProducts();

    unsigned int Test(unsigned int a, unsigned int b) { return a + b; }

private:
    std::map<std::string, Product> products;
};

void Init()
{
       WarehouseServer app;
	rpcServer.Add("GetProduct", GetHandle(&WarehouseServer::Getproduct, app), { "id" });
	rpcServer.Add("AddProduct", GetHandle(&WarehouseServer::AddProduct, app), { "product" });
	rpcServer.Add("AllProducts", GetHandle(&WarehouseServer::AllProducts, app), {});
	rpcServer.Add("Test", GetHandle(&WarehouseServer::Test, app));
}

image

I found this https://stackoverflow.com/questions/27442885/syntax-error-with-stdnumeric-limitsmax one to fix the error. By the way how to find the api document.

Question about invoking a method in typemapper.hpp

Hi,
I am enjoying your library, so first of all, thank you.
I am running into an issue invoking a function which accepts a struct that includes only two simple enums classes:

enum class A {
  a1 = 0,
  a2
};
enum class B {
  b1 = 0,
  b2
};
struct some_struct {
  A a;
  B b;
}

I can see the correct conversion happening in my custom typemapper: void from_json(const nlohmann::json& j, some_struct str), however, when the handler then invokes the function which was bound (happens in typemapper.hpp in: method(params[index].get<typename std::decay<ParamTypes>::type>()...);) , the argument struct appears uninitialized, as if a copy didn't execute.
Can you suggest what is going wrong?

Consider OpenRPC integration

Hey there,

I work on a project called OpenRPC. Its a fork of OpenAPI, modified to suit JSON-RPC 2.0.

There are a lot of things you can use an OpenRPC document for in your projects. Let me know if you have any questions or need any help.

Cheers.

RPC methods expecting floats\doubles could be called with an integral type which would throw an exception

Hi,
I'm not sure, if there's much that could be done about this issue, but implicit type deductions for numbers in JSON for modern C++ are problematic when a handler expects a float\double param and the RPC is using an integral param. I will try to explain with an example:

json foo (const float b) {
  ...
}

JsonRpc2Server rpc_handler;
rpc_handler.Add("foo", GetHandle(foo), {"bar"});

If foo is invoked like this: {"method":"foo","params":{"bar":10}}, then nlohmann would determine that 10 is an unsigned number and the dispatcher will throw (invalid parameter: must be float, but is unsigned integer for parameter "bar") when it tries to invoke the method.

The only workaround I've found so far is to setup foo to expect a string, and then invoke it like this: {"method":"foo","params":{"bar":"10"}}.
If anyone has other\better ideas, I'd be happy to hear those.
Thanks.

RPC methods expecting json type argument throw exception if the argument is not of value_t::object

Hi,
I am trying to use RPC methods with arguments of type json.
So far i can only get a call to such a method to work if the argument is exactly of value_t::object
In all other cases there is an "invalid parameter" exception thrown.
For my use case i need the method to be invokeable with any, most importantly with "non-structured" json value types (e.g. bool/number/array or string)

I think the issue #18 is somewhat similar/related to this. Any ideas on this would be appreciated.
Thank you!

The following example illustrates what works and what not:

#include "inmemoryconnector.hpp"
#include <iostream>
#include <jsonrpccxx/client.hpp>
#include <jsonrpccxx/server.hpp>

using namespace jsonrpccxx;

std::string jsonToString(json j) { return j.dump(); }
int main() {
  JsonRpc2Server rpcServer;
  rpcServer.Add("jsonToString", GetHandle(&jsonToString), {"json"});
  InMemoryConnector inMemoryConnector(rpcServer);
  JsonRpcClient client(inMemoryConnector, version::v2);
  std::cout << jsonToString(true) << std::endl; // This works as expected
  {
    json j = json::parse(R"({"val" : true})");
    std::cout << client.CallMethod<std::string>(1, "jsonToString", {j}) << std::endl; // this also works as expected but is NOT what I want!
  }
  {
    json j = json::parse(R"(true)");
    std::cout << client.CallMethod<std::string>(1, "jsonToString", {j}); // this throws an exception
    //   what():  -32602: invalid parameter: must be object, but is boolean for parameter "json"
  }
  return 0;
}

Positive error codes are not recognized as integers in `JsonRpcException fromJson(const json &value)`

According to Niels in order to check if a number is integer it is not enough to compare the type to number_integer as executed in fromJson() [common.hpp]

bool has_code = has_key_type(value, "code", json::value_t::number_integer);

This is what the json library does:

    constexpr bool is_number_integer() const noexcept
    {
        return m_type == value_t::number_integer || m_type == value_t::number_unsigned;
    }

Easiest fix is:

bool has_code = has_key_type(value, "code", json::value_t::number_integer) || has_key_type(value, "code", json::value_t::number_unsigned);

Compiling fails with XCode 14.1 using MacOS 13.0 SDK

sprintf() has been deprecated in MacOS 13.0 SDK causing warnings to be emitted and since warnings are treated as errors, compilation fails...

In file included from /Users/svenn/GITroot/pixie-base-dependencies/json-rpc-cxx/test/main.cpp:2:
/json-rpc-cxx/vendor/doctest/doctest.h:3725:1: error: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Werror,-Wdeprecated-declarations]
DOCTEST_TO_STRING_OVERLOAD(char, "%d")
^
/json-rpc-cxx/vendor/doctest/doctest.h:3721:14: note: expanded from macro 'DOCTEST_TO_STRING_OVERLOAD'
        std::sprintf(buf, fmt, in);                                                                \
             ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk/usr/include/stdio.h:188:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^

error_code enum

Hi, i started to use your project.
Why isn't there any enum for common error?

enum error_code {
    parse_error = -32700,
    invalid_request = -32600,
    method_not_found = -32601,
    invalid_params = -32602,
    internal_error = -32603,
  };

Have a nice day!

How to use http client with just URL? Not host/port?

In the other repo json-rpc-cpp, I was able to run this simple code to use JSON-rpc with the ethereum network:

int main() {
  // use ethereum main net node
  string url = "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161";
  string method = "eth_blockNumber";
  HttpClient client(url);
  Client c(client);

  // send empty parameters for eth_blockNumber
  Json::Value params;

  try {
    // send eth_blockNumber as RPC and print result to console
    cout << c.CallMethod(method, params) << endl;
  } catch (JsonRpcException &e) {
    cerr << e.what() << endl;
  }
}

I'm struggling to figure out how to do that with this repo, since the client creation requires a host/port combo. The other repo just needed a URL. For example, in this repo I'm trying to follow the warehouse example sort of by using CppHttpLibClientConnector.h (which I had to pull from examples since it wasn't in the include directory...)

  // use ethereum main net node from infura (got from metamask)
  string url = "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161";
  string method = "eth_blockNumber";

  CppHttpLibClientConnector httpClient(url,0); // Is there a way I can use a URL here???
  JsonRpcClient client(httpClient, version::v2);

  client.CallMethod<bool>(0, method, {});

Maybe I'm missing something in the repo, but does anyone know how I can create a client object in this repo with just a URL instead of host/port combo?

JsonRpc1Server?

While working on pull request for issue 35 I noticed I could find no JsonRpc 1 compliant server class, but it says here there is one:

- JSON-RPC 1.0 and 2.0 compliant server

json ProcessSingleRequest(json &request) {
if (!has_key_type(request, "jsonrpc", json::value_t::string) || request["jsonrpc"] != "2.0") {
throw JsonRpcException(invalid_request, R"(invalid request: missing jsonrpc field set to "2.0")");
}

Or is it just me dumbing out?

Transport agnostic interfaces

First of all this is really a great and useful framework.

I would like to raise the question or rather a discussion weather the interfaces are transport agnostic yes or no. If i am right in my conclusion they are not. Why?

  • The http client client.hpp and iclientconnector.hpp are implemented in a synchronous request/response pattern, therefore offering a Send method with return type std::string
  • A tcp client is preferably implemented in asynchronous send/receive pattern, therefore client.hpp and iclientconnector.hpp cannot be used 'agnostically' out of the box
  • For using json-rpc-cxx with a tcp client/server, the iclientconnector.hpp and client.hpp have to be extended by offering, e.g. a Post method without a return value and a MessageHandler, e.g.

iclientconnector.hpp

using MessageHandler = std::function<void(const std::string)>;
virtual void Post(const std::string &request) = 0;
MessageHandler OnMessage;

client.hpp

using MessageHandler = std::function<void(const JsonRpcResponse)>;
JsonRpcClient(IClientConnector &connector, version v) : connector(connector), v(v)
{
    connector.OnMessage = [this](const std::string &message)
    {
        json response = json::parse(message);
        JsonRpcResponse rpc_response{};
        ...
        OnMessage(rpc_response);
    }
}
...
MessageHandler OnMessage;

client.cpp

std::future<JsonRpcResponse> future;
std::promise<JsonRpcResponse> promise;
JsonRpcResponse response;
client.OnMessage = [&] (const JsonRpcResponse &response) mutable
{
    promise.set_value(response);
};
future = promise.get_future();
client.CallMethod(1, "GetProduct", {"0xff"});
response = future.get();
std::cout << response.result.dump() << std::endl;

compile error in test/server.cpp class TestServer method dirty_method()

/home/dpoole/src/json-rpc-cxx/test/server.cpp: In member function ‘int TestServer::dirty_method(int, int)’:
/home/dpoole/src/json-rpc-cxx/test/server.cpp:98:45: error: ignoring return value of ‘std::string std::__cxx11::to_string(int)’, declared with attribute ‘nodiscard’ [-Werror=unused-result]
   98 |   int dirty_method(int a, int b) { to_string(a+b); throw std::exception(); }
      |                                    ~~~~~~~~~^~~~~
In file included from /usr/include/c++/14/string:54,                           
                 from /usr/include/c++/14/bits/locale_classes.h:40, 
                 from /usr/include/c++/14/bits/ios_base.h:41,                  
                 from /usr/include/c++/14/streambuf:43,             
                 from /usr/include/c++/14/bits/streambuf_iterator.h:35,
                 from /usr/include/c++/14/iterator:66,
                 from /home/dpoole/src/json-rpc-cxx/vendor/nlohmann/json.hpp:53,
                 from /home/dpoole/src/json-rpc-cxx/include/jsonrpccxx/common.hpp:2,
                 from /home/dpoole/src/json-rpc-cxx/include/jsonrpccxx/server.hpp:3,
                 from /home/dpoole/src/json-rpc-cxx/test/testserverconnector.hpp:3,
                 from /home/dpoole/src/json-rpc-cxx/test/server.cpp:2:
/usr/include/c++/14/bits/basic_string.h:4240:3: note: declared here
 4240 |   to_string(int __val)                                                 
      |   ^~~~~~~~~                                                            
cc1plus: all warnings being treated as errors                          
make[2]: *** [CMakeFiles/jsonrpccpp-test.dir/build.make:132: CMakeFiles/jsonrpccpp-test.dir/test/server.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:85: CMakeFiles/jsonrpccpp-test.dir/all] Error 2
make: *** [Makefile:146: all] Error 2 

Fedora Linux 40 (Workstation Edition)
gcc (GCC) 14.1.1 20240620 (Red Hat 14.1.1-6)
GNU libc version: 2.39

std::to_string() is marked nodiscard so the test code:
int dirty_method(int a, int b) { to_string(a+b); throw std::exception(); }

needs to capture the return.

PR incoming.

using named parameters in a client invocation

Hi, there is a check in the dispatcher code that prevents passing named parameters:

... "invalid parameter: procedure doesn't support named parameter" ..

Why is that? I suspect it is because the (quite esoteric :)) createMethodHandle(... can only work with a sequence of parameters, is that right?

It is a pity because it prevents writing something very natural like this

json args;
args["arg1"] = 1;
args["arg2"] = 33;
client.CallMethod... (... ,args);

which was actually possible with libjson-rpc-cpp

crash with http server and multiple clients

I consistently crash when I have a single RPC Server using the supplied http server with multiple clients. I fixed it by adding a mutex in JsonRPCServer::HandleRequest() so it would only handle a single request at a time.

 std::string HandleRequest(const std::string &requestString) override {
       std::lock_guard<std::mutex> lock (mMutex) ;

     try {
       json request = json::parse(requestString);
       if (request.is_array()) {
         json result = json::array();
         ...

I don't expect my server to get hit exceptionally hard, so there's no issue for me.

Custom JSON Parsers Not Detected

I seem to be unable to successfully use methods whose arguments are types such as enum classes, where I have used nlohmann::json to provide serialisation and deserialisation routines. The method successfully binds, but when I invoke it using curl, there is an error response of invalid parameter: must be object, but is string for parameter \"e\"

The attached zip file contains a repro case. The enum itself is:

 enum class MyEnum { Zero, One, Two };

with serialisation provided by NLOHMANN_JSON_SERIALIZE_ENUM. There are then two methods which I'm attempting to call:

  int MyService::OptionOne( MyEnum e ) {
    std::cout << __FUNCTION__ << std::endl;
    return static_cast<int>(e);
  }

  int MyService::OptionTwo( std::string s ) {
    std::cout << __FUNCTION__ << std::endl;
    nlohmann::json j(s);

    MyEnum e = j.get<MyEnum>();

    return static_cast<int>(e);
  }

I can invoke the second via:

curl http://localhost:8483/jsonrpc -H "Content-Type application/json" --data '{ "method":"OptionTwo", "params":{"s":"One"}, "id":10, "jsonrpc": "2.0" }'

However, when I try to call the first:

curl http://localhost:8483/jsonrpc -H "Content-Type application/json" --data '{ "method":"OptionOne", "params":{"e":"One"}, "id":10, "jsonrpc": "2.0" }'

I see the error noted above.

jsonrpccxxissue.zip

Peer to peer support

JSON-RPC is fundamentally a p2p protocol. While many applications may have a client/server architecture, other applications find it useful to invoke methods bidirectionally over a connection.

We're looking to add our first C++ application to our network of JSON-RPC processes and would consider this project if it supported receiving and sending all sorts of JSON-RPC messages in both directions.

wget or curl command to qury server

I'd like to see what data is travelling between the http server and client, is there a specific format I can use with wget or curl to query the server from the command line?

The format I'm seeing is this, but I can't translate it to something curl or wget will use.

{"id":1,"jsonrpc":"2.0","method":"mycommand","params":["myargs"]}

in memory connector

Does the in memory connector only work in a single process and not across processes?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.