From a820bd6f365a82fbd1840f03e29432dc0b72a6aa Mon Sep 17 00:00:00 2001 From: badaix Date: Sun, 1 Jul 2018 13:17:34 +0200 Subject: [PATCH] make jsonrpc++ header only --- CMakeLists.txt | 73 +- LICENSE | 2 +- Makefile | 6 +- {lib => include}/json.hpp | 0 include/jsonrpcpp.hpp | 1469 ++++++++++++++++++++++ jsonrpctest.cpp => jsonrpcpp_example.cpp | 4 +- lib/jsonrp.cpp | 1017 --------------- lib/jsonrp.hpp | 461 ------- 8 files changed, 1500 insertions(+), 1532 deletions(-) rename {lib => include}/json.hpp (100%) create mode 100644 include/jsonrpcpp.hpp rename jsonrpctest.cpp => jsonrpcpp_example.cpp (99%) delete mode 100644 lib/jsonrp.cpp delete mode 100644 lib/jsonrp.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b01441a..ccd9d0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,65 +1,42 @@ +# __ ____ __ __ _ ____ ____ ___ _ _ +# _( )/ ___) / \ ( ( \( _ \( _ \ / __)( ) ( ) +# / \) \\___ \( O )/ / ) / ) __/( (__(_ _)(_ _) +# \____/(____/ \__/ \_)__)(__\_)(__) \___)(_) (_) + +# This file is part of jsonrpc++ +# Copyright (C) 2017-2018 Johannes Pohl + +# This software may be modified and distributed under the terms +# of the MIT license. See the LICENSE file for details. + + cmake_minimum_required(VERSION 3.0.0) -project(jsonrpcpp VERSION 1.1.1 LANGUAGES CXX) +project(jsonrpcpp VERSION 1.2.0 LANGUAGES CXX) set(PROJECT_DESCRIPTION "C++ JSON-RPC 2.0 library") set(PROJECT_URL "https://github.com/badaix/jsonrpcpp") -option(BUILD_SHARED_LIBS "Build jsonrpcpp as a shared library" ON) -option(BUILD_STATIC_LIBS "Build jsonrpcpp as a static library" ON) -option(BUILD_TESTS "Build tests (run tests with make test)" ON) +option(BUILD_EXAMPLE "Build example (build jsonrpcpp_example demo)" ON) -if (NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS) - message(FATAL_ERROR "One or both of BUILD_SHARED_LIBS or BUILD_STATIC_LIBS" - "must be set to ON to build") -endif() +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_EXTENSIONS OFF) -if(NOT DEFINED CMAKE_INSTALL_LIBDIR) - SET(CMAKE_INSTALL_LIBDIR lib CACHE PATH "Output directory for libraries") -endif() if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) - SET(CMAKE_INSTALL_INCLUDEDIR include CACHE - PATH "Output directory for header files") + SET(CMAKE_INSTALL_INCLUDEDIR include CACHE + PATH "Output directory for header files") endif() -include_directories(lib) -set(JSONRPCPP_SOURCES lib/jsonrp.cpp) -if (BUILD_SHARED_LIBS) - add_library(jsonrpcpp SHARED "${JSONRPCPP_SOURCES}") - target_compile_features(jsonrpcpp PUBLIC cxx_std_11) - if(WIN32) - install(TARGETS jsonrpcpp RUNTIME DESTINATION "${CMAKE_INSTALL_LIBDIR}") - else() - install(TARGETS jsonrpcpp LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") - endif() - -endif (BUILD_SHARED_LIBS) +include_directories( + "include" +) -if (BUILD_STATIC_LIBS) - add_library(jsonrpcpp-static STATIC "${JSONRPCPP_SOURCES}") - set_target_properties(jsonrpcpp-static PROPERTIES OUTPUT_NAME jsonrpcpp) - target_compile_features(jsonrpcpp-static PUBLIC cxx_std_11) - install(TARGETS jsonrpcpp-static ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") -endif (BUILD_STATIC_LIBS) +if (BUILD_EXAMPLE) + add_executable(jsonrpcpp_example jsonrpcpp_example.cpp) +endif (BUILD_EXAMPLE) -if (BUILD_TESTS) - if (NOT BUILD_STATIC_LIBS) - message(FATAL_ERROR "Tests can only be built against static libraries " - "(set BUILD_STATIC_LIBS=ON)") - endif (NOT BUILD_STATIC_LIBS) - add_executable(jsonrpctest jsonrpctest.cpp) - target_link_libraries(jsonrpctest jsonrpcpp-static) - target_compile_features(jsonrpctest PUBLIC cxx_std_11) -endif (BUILD_TESTS) -install(FILES lib/jsonrp.hpp lib/json.hpp +install(FILES include/jsonrpcpp.hpp include/json.hpp DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/jsonrpcpp") -configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/jsonrpcpp.pc.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/jsonrpcpp.pc" - @ONLY) - -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/jsonrpcpp.pc" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/LICENSE b/LICENSE index ce4771a..6e566c9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Johannes Pohl +Copyright (c) 2017-2018 Johannes Pohl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index 8c9fad0..7aa04cd 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ -BIN = jsonrpctest +BIN = jsonrpcpp_example CXX = clang++ STRIP = strip -CXXFLAGS = -std=c++0x -Wall -O3 -Ilib -isystem lib/externals +CXXFLAGS = -std=c++11 -Wall -O3 -Iinclude -pedantic -Wextra -Wshadow -Wconversion -OBJ = jsonrpctest.o lib/jsonrp.o +OBJ = jsonrpcpp_example.o all: $(OBJ) diff --git a/lib/json.hpp b/include/json.hpp similarity index 100% rename from lib/json.hpp rename to include/json.hpp diff --git a/include/jsonrpcpp.hpp b/include/jsonrpcpp.hpp new file mode 100644 index 0000000..4c5121d --- /dev/null +++ b/include/jsonrpcpp.hpp @@ -0,0 +1,1469 @@ +/*** + __ ____ __ __ _ ____ ____ ___ _ _ + _( )/ ___) / \ ( ( \( _ \( _ \ / __)( ) ( ) + / \) \\___ \( O )/ / ) / ) __/( (__(_ _)(_ _) + \____/(____/ \__/ \_)__)(__\_)(__) \___)(_) (_) + version 1.2.0 + https://github.com/badaix/jsonrpcpp + + This file is part of jsonrpc++ + Copyright (C) 2017-2018 Johannes Pohl + + This software may be modified and distributed under the terms + of the MIT license. See the LICENSE file for details. +***/ + +/// http://patorjk.com/software/taag/#p=display&f=Graceful&t=JSONRPC%2B%2B + +#ifndef JSON_RPC_H +#define JSON_RPC_H + +#include +#include +#include +#include +#include "json.hpp" + + +using Json = nlohmann::json; + +namespace jsonrpcpp +{ + + +class Entity; +class Request; +class Notification; +class Parameter; +class Response; +class Error; +class Batch; + + +typedef std::shared_ptr entity_ptr; +typedef std::shared_ptr request_ptr; +typedef std::shared_ptr notification_ptr; +typedef std::shared_ptr parameter_ptr; +typedef std::shared_ptr response_ptr; +typedef std::shared_ptr error_ptr; +typedef std::shared_ptr batch_ptr; + + + +class Entity +{ +public: + enum class entity_t : uint8_t + { + unknown, + exception, + id, + error, + response, + request, + notification, + batch + }; + + Entity(entity_t type); + virtual ~Entity(); + + bool is_exception(); + bool is_id(); + bool is_error(); + bool is_response(); + bool is_request(); + bool is_notification(); + bool is_batch(); + + virtual std::string type_str() const; + + virtual Json to_json() const = 0; + virtual void parse_json(const Json& json) = 0; + + virtual void parse(const std::string& json_str); + virtual void parse(const char* json_str); + +protected: + entity_t entity; +}; + + + + + +class NullableEntity : public Entity +{ +public: + NullableEntity(entity_t type); + NullableEntity(entity_t type, std::nullptr_t); + virtual ~NullableEntity(); +#ifdef _MSC_VER + virtual operator bool() const +#else + virtual explicit operator bool() const +#endif + { + return !isNull; + } + +protected: + bool isNull; +}; + + + + + +class Id : public Entity +{ +public: + enum class value_t : uint8_t + { + null, + string, + integer + }; + + Id(); + Id(int id); + Id(const char* id); + Id(const std::string& id); + Id(const Json& json_id); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); + + friend std::ostream& operator<< (std::ostream &out, const Id &id) + { + out << id.to_json(); + return out; + } + + value_t type; + int int_id; + std::string string_id; +}; + + + + + +class Parameter : public NullableEntity +{ +public: + enum class value_t : uint8_t + { + null, + array, + map + }; + + Parameter(std::nullptr_t); + Parameter(const Json& json = nullptr); + Parameter(const std::string& key1, const Json& value1, + const std::string& key2 = "", const Json& value2 = nullptr, + const std::string& key3 = "", const Json& value3 = nullptr, + const std::string& key4 = "", const Json& value4 = nullptr); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); + + bool is_array() const; + bool is_map() const; + bool is_null() const; + + Json get(const std::string& key) const; + Json get(size_t idx) const; + bool has(const std::string& key) const; + bool has(size_t idx) const; + + template + T get(const std::string& key) const + { + return get(key).get(); + } + + template + T get(size_t idx) const + { + return get(idx).get(); + } + + template + T get(const std::string& key, const T& default_value) const + { + if (!has(key)) + return default_value; + else + return get(key); + } + + template + T get(size_t idx, const T& default_value) const + { + if (!has(idx)) + return default_value; + else + return get(idx); + } + + value_t type; + std::vector param_array; + std::map param_map; +}; + + + + + +class Error : public NullableEntity +{ +public: + Error(const Json& json = nullptr); + Error(std::nullptr_t); + Error(const std::string& message, int code, const Json& data = nullptr); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); + + int code; + std::string message; + Json data; +}; + + + +/// JSON-RPC 2.0 request +/** + * Simple jsonrpc 2.0 parser with getters + * Currently no named parameters are supported, but only array parameters + */ +class Request : public Entity +{ +public: + Request(const Json& json = nullptr); + Request(const Id& id, const std::string& method, const Parameter& params = nullptr); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); + + std::string method; + Parameter params; + Id id; +}; + + + + + +class RpcException : public std::exception +{ + char* text_; +public: + RpcException(const char* text); + RpcException(const std::string& text); + RpcException(const RpcException& e); + + virtual ~RpcException() throw(); + virtual const char* what() const noexcept; +}; + + + +class RpcEntityException : public RpcException, public Entity +{ +public: + Error error; + + RpcEntityException(const Error& error); + RpcEntityException(const std::string& text); + RpcEntityException(const RpcEntityException& e); + virtual Json to_json() const = 0; + +protected: + void parse_json(const Json& json) override; +}; + + + +class ParseErrorException : public RpcEntityException +{ +public: + ParseErrorException(const Error& error); + ParseErrorException(const ParseErrorException& e); + ParseErrorException(const std::string& data); + virtual Json to_json() const; +}; + + + +// -32600 Invalid Request The JSON sent is not a valid Request object. +// -32601 Method not found The method does not exist / is not available. +// -32602 Invalid params Invalid method parameter(s). +// -32603 Internal error Internal JSON-RPC error. + +class RequestException : public RpcEntityException +{ +public: + Id id; + + RequestException(const Error& error, const Id& requestId = Id()); + RequestException(const RequestException& e); + virtual Json to_json() const; +}; + + + +class InvalidRequestException : public RequestException +{ +public: + InvalidRequestException(const Id& requestId = Id()); + InvalidRequestException(const Request& request); + InvalidRequestException(const char* data, const Id& requestId = Id()); + InvalidRequestException(const std::string& data, const Id& requestId = Id()); +}; + + + +class MethodNotFoundException : public RequestException +{ +public: + MethodNotFoundException(const Id& requestId = Id()); + MethodNotFoundException(const Request& request); + MethodNotFoundException(const char* data, const Id& requestId = Id()); + MethodNotFoundException(const std::string& data, const Id& requestId = Id()); +}; + + + +class InvalidParamsException : public RequestException +{ +public: + InvalidParamsException(const Id& requestId = Id()); + InvalidParamsException(const Request& request); + InvalidParamsException(const char* data, const Id& requestId = Id()); + InvalidParamsException(const std::string& data, const Id& requestId = Id()); +}; + + + +class InternalErrorException : public RequestException +{ +public: + InternalErrorException(const Id& requestId = Id()); + InternalErrorException(const Request& request); + InternalErrorException(const char* data, const Id& requestId = Id()); + InternalErrorException(const std::string& data, const Id& requestId = Id()); +}; + + + + + +class Response : public Entity +{ +public: + Id id; + Json result; + Error error; + + Response(const Json& json = nullptr); + Response(const Id& id, const Json& result); + Response(const Id& id, const Error& error); + Response(const Request& request, const Json& result); + Response(const Request& request, const Error& error); + Response(const RequestException& exception); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); +}; + + + + + +class Notification : public Entity +{ +public: + std::string method; + Parameter params; + Notification(const Json& json = nullptr); + Notification(const char* method, const Parameter& params = nullptr); + Notification(const std::string& method, const Parameter& params); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); +}; + + + + +typedef std::function notification_callback; +typedef std::function request_callback; + + +class Parser +{ +public: + Parser(); + virtual ~Parser(); + + entity_ptr parse(const std::string& json_str); + entity_ptr parse_json(const Json& json); + + void register_notification_callback(const std::string& notification, notification_callback callback); + void register_request_callback(const std::string& request, request_callback callback); + + static entity_ptr do_parse(const std::string& json_str); + static entity_ptr do_parse_json(const Json& json); + static bool is_request(const std::string& json_str); + static bool is_request(const Json& json); + static bool is_notification(const std::string& json_str); + static bool is_notification(const Json& json); + static bool is_response(const std::string& json_str); + static bool is_response(const Json& json); + static bool is_batch(const std::string& json_str); + static bool is_batch(const Json& json); + +private: + std::map notification_callbacks_; + std::map request_callbacks_; +}; + + + + + +class Batch : public Entity +{ +public: + std::vector entities; + + Batch(const Json& json = nullptr); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); + + template + void add(const T& entity) + { + entities.push_back(std::make_shared(entity)); + } + + void add_ptr(const entity_ptr& entity) + { + entities.push_back(entity); + } +}; + + + + +/////////////////////////// Entity implementation ///////////////////////////// + +inline Entity::Entity(entity_t type) : entity(type) +{ +} + + +inline Entity::~Entity() +{ +} + + +inline bool Entity::is_exception() +{ + return (entity == entity_t::exception); +} + + +inline bool Entity::is_id() +{ + return (entity == entity_t::id); +} + + +inline bool Entity::is_error() +{ + return (entity == entity_t::error); +} + + +inline bool Entity::is_response() +{ + return (entity == entity_t::response); +} + + +inline bool Entity::is_request() +{ + return (entity == entity_t::request); +} + + +inline bool Entity::is_notification() +{ + return (entity == entity_t::notification); +} + + +inline bool Entity::is_batch() +{ + return (entity == entity_t::batch); +} + + +inline void Entity::parse(const char* json_str) +{ + // http://www.jsonrpc.org/specification + // code message meaning + // -32700 Parse error Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text. + // -32600 Invalid Request The JSON sent is not a valid Request object. + // -32601 Method not found The method does not exist / is not available. + // -32602 Invalid params Invalid method parameter(s). + // -32603 Internal error Internal JSON-RPC error. + // -32000 to -32099 Server error Reserved for implementation-defined server-errors. + try + { + parse_json(Json::parse(json_str)); + } + catch (const RpcException& e) + { + throw; + } + catch (const std::exception& e) + { + throw ParseErrorException(e.what()); + } +} + + +inline void Entity::parse(const std::string& json_str) +{ + parse(json_str.c_str()); +} + + +inline std::string Entity::type_str() const +{ + switch (entity) + { + case entity_t::unknown: + return "unknown"; + case entity_t::id: + return "id"; + case entity_t::exception: + return "exception"; + case entity_t::error: + return "error"; + case entity_t::response: + return "response"; + case entity_t::request: + return "request"; + case entity_t::notification: + return "notification"; + case entity_t::batch: + return "batch"; + default: + return "unknown"; + } +} + + + + + +/////////////////////////// NullableEntity implementation ///////////////////// + +inline NullableEntity::NullableEntity(entity_t type) : Entity(type), isNull(false) +{ +} + + +inline NullableEntity::NullableEntity(entity_t type, std::nullptr_t) : Entity(type), isNull(true) +{ +} + + +inline NullableEntity::~NullableEntity() +{ +}; + + + + + +/////////////////////////// Id implementation ///////////////////////////////// + +inline Id::Id() : Entity(entity_t::id), type(value_t::null), int_id(0), string_id("") +{ +} + + +inline Id::Id(int id) : Entity(entity_t::id), type(value_t::integer), int_id(id), string_id("") +{ +} + + +inline Id::Id(const char* id) : Entity(entity_t::id), type(value_t::string), int_id(0), string_id(id) +{ +} + + +inline Id::Id(const std::string& id) : Id(id.c_str()) +{ +} + + +inline Id::Id(const Json& json_id) : Entity(entity_t::id), type(value_t::null) +{ + parse_json(json_id); +} + + +inline void Id::parse_json(const Json& json) +{ + if (json.is_null()) + { + type = value_t::null; + } + else if (json.is_number_integer()) + { + int_id = json.get(); + type = value_t::integer; + } + else if (json.is_string()) + { + string_id = json.get(); + type = value_t::string; + } + else + throw std::invalid_argument("id must be integer, string or null"); +} + + +inline Json Id::to_json() const +{ + if (type == value_t::null) + return nullptr; + else if (type == value_t::string) + return string_id; + else if (type == value_t::integer) + return int_id; + + return nullptr; +} + + + + + +//////////////////////// Error implementation ///////////////////////////////// + +inline Parameter::Parameter(std::nullptr_t) : NullableEntity(entity_t::id, nullptr), type(value_t::null) +{ +} + + +inline Parameter::Parameter(const Json& json) : NullableEntity(entity_t::id), type(value_t::null) +{ + if (json != nullptr) + parse_json(json); +} + + +inline Parameter::Parameter(const std::string& key1, const Json& value1, + const std::string& key2, const Json& value2, + const std::string& key3, const Json& value3, + const std::string& key4, const Json& value4) : NullableEntity(entity_t::id), type(value_t::map) +{ + param_map[key1] = value1; + if (!key2.empty()) + param_map[key2] = value2; + if (!key3.empty()) + param_map[key3] = value3; + if (!key4.empty()) + param_map[key4] = value4; +} + + +inline void Parameter::parse_json(const Json& json) +{ + if (json.is_array()) + { + param_array = json.get>(); + param_map.clear(); + type = value_t::array; + } + else + { + param_map = json.get>(); + param_array.clear(); + type = value_t::map; + } +} + + +inline Json Parameter::to_json() const +{ + if (type == value_t::array) + return param_array; + else if (type == value_t::map) + return param_map; + else + return nullptr; +} + + +inline bool Parameter::is_array() const +{ + return type == value_t::array; +} + + +inline bool Parameter::is_map() const +{ + return type == value_t::map; +} + + +inline bool Parameter::is_null() const +{ + return isNull; +} + + +inline bool Parameter::has(const std::string& key) const +{ + if (type != value_t::map) + return false; + return (param_map.find(key) != param_map.end()); +} + + +inline Json Parameter::get(const std::string& key) const +{ + return param_map.at(key); +} + + +inline bool Parameter::has(size_t idx) const +{ + if (type != value_t::array) + return false; + return (param_array.size() > idx); +} + + +inline Json Parameter::get(size_t idx) const +{ + return param_array.at(idx); +} + + + + + +//////////////////////// Error implementation ///////////////////////////////// + +inline Error::Error(const Json& json) : Error("Internal error", -32603, nullptr) +{ + if (json != nullptr) + parse_json(json); +} + + +inline Error::Error(std::nullptr_t) : NullableEntity(entity_t::error, nullptr), code(0), message(""), data(nullptr) +{ +} + + +inline Error::Error(const std::string& message, int code, const Json& data) : NullableEntity(entity_t::error), code(code), message(message), data(data) +{ +} + + +inline void Error::parse_json(const Json& json) +{ + try + { + if (json.count("code") == 0) + throw RpcException("code is missing"); + code = json["code"]; + if (json.count("message") == 0) + throw RpcException("message is missing"); + message = json["message"]; + if (json.count("data")) + data = json["data"]; + else + data = nullptr; + } + catch (const RpcException& e) + { + throw; + } + catch (const std::exception& e) + { + throw RpcException(e.what()); + } +} + + +inline Json Error::to_json() const +{ + Json j = { + {"code", code}, + {"message", message}, + }; + + if (!data.is_null()) + j["data"] = data; + return j; +} + + + + + +////////////////////// Request implementation ///////////////////////////////// + +inline Request::Request(const Json& json) : Entity(entity_t::request), method(""), id() +{ + if (json != nullptr) + parse_json(json); +} + + +inline Request::Request(const Id& id, const std::string& method, const Parameter& params) : Entity(entity_t::request), method(method), params(params), id(id) +{ +} + + +inline void Request::parse_json(const Json& json) +{ + try + { + if (json.count("id") == 0) + throw InvalidRequestException("id is missing"); + + try + { + id = Id(json["id"]); + } + catch(const std::exception& e) + { + throw InvalidRequestException(e.what()); + } + + if (json.count("jsonrpc") == 0) + throw InvalidRequestException("jsonrpc is missing", id); + std::string jsonrpc = json["jsonrpc"].get(); + if (jsonrpc != "2.0") + throw InvalidRequestException("invalid jsonrpc value: " + jsonrpc, id); + + if (json.count("method") == 0) + throw InvalidRequestException("method is missing", id); + if (!json["method"].is_string()) + throw InvalidRequestException("method must be a string value", id); + method = json["method"]; + if (method.empty()) + throw InvalidRequestException("method must not be empty", id); + + if (json.count("params")) + params.parse_json(json["params"]); + else + params = nullptr; + } + catch (const RequestException& e) + { + throw; + } + catch (const std::exception& e) + { + throw InternalErrorException(e.what(), id); + } +} + + +inline Json Request::to_json() const +{ + Json json = { + {"jsonrpc", "2.0"}, + {"method", method}, + {"id", id.to_json()} + }; + + if (params) + json["params"] = params.to_json(); + + return json; +} + + + +inline RpcException::RpcException(const char* text) +{ + text_ = new char[std::strlen(text) + 1]; + std::strcpy(text_, text); +} + +inline RpcException::RpcException(const std::string& text) : RpcException(text.c_str()) +{ +} + +inline RpcException::RpcException(const RpcException& e) : RpcException(e.what()) +{ +} + +inline RpcException::~RpcException() throw() +{ + delete[] text_; +} + +inline const char* RpcException::what() const noexcept +{ + return text_; +} + + + + +inline RpcEntityException::RpcEntityException(const Error& error) : RpcException(error.message), Entity(entity_t::exception), error(error) +{ +} + +inline RpcEntityException::RpcEntityException(const RpcEntityException& e) : RpcException(e), Entity(entity_t::exception), error(e.error) +{ +} + +inline void RpcEntityException::parse_json(const Json& /*json*/) +{ +} + + + +inline ParseErrorException::ParseErrorException(const Error& error) : RpcEntityException(error) +{ +} + +inline ParseErrorException::ParseErrorException(const ParseErrorException& e) : RpcEntityException(e) +{ +} + +inline ParseErrorException::ParseErrorException(const std::string& data) : ParseErrorException(Error("Parse error", -32700, data)) +{ +} + +inline Json ParseErrorException::to_json() const +{ + Json response = { + {"jsonrpc", "2.0"}, + {"error", error.to_json()}, + {"id", nullptr} + }; + + return response; +} + + + + +inline RequestException::RequestException(const Error& error, const Id& requestId) : RpcEntityException(error), id(requestId) +{ +} + +inline RequestException::RequestException(const RequestException& e) : RpcEntityException(e), id(e.id) +{ +} + +inline Json RequestException::to_json() const +{ + Json response = { + {"jsonrpc", "2.0"}, + {"error", error.to_json()}, + {"id", id.to_json()} + }; + + return response; +} + + + + + +inline InvalidRequestException::InvalidRequestException(const Id& requestId) : RequestException(Error("Invalid request", -32600), requestId) +{ +} + +inline InvalidRequestException::InvalidRequestException(const Request& request) : InvalidRequestException(request.id) +{ +} + +inline InvalidRequestException::InvalidRequestException(const char* data, const Id& requestId) : RequestException(Error("Invalid request", -32600, data), requestId) +{ +} + +inline InvalidRequestException::InvalidRequestException(const std::string& data, const Id& requestId) : InvalidRequestException(data.c_str(), requestId) +{ +} + + + +inline MethodNotFoundException::MethodNotFoundException(const Id& requestId) : RequestException(Error("Method not found", -32601), requestId) +{ +} + +inline MethodNotFoundException::MethodNotFoundException(const Request& request) : MethodNotFoundException(request.id) +{ +} + +inline MethodNotFoundException::MethodNotFoundException(const char* data, const Id& requestId) : RequestException(Error("Method not found", -32601, data), requestId) +{ +} + +inline MethodNotFoundException::MethodNotFoundException(const std::string& data, const Id& requestId) : MethodNotFoundException(data.c_str(), requestId) +{ +} + + + +inline InvalidParamsException::InvalidParamsException(const Id& requestId) : RequestException(Error("Invalid params", -32602), requestId) +{ +} + +inline InvalidParamsException::InvalidParamsException(const Request& request) : InvalidParamsException(request.id) +{ +} + +inline InvalidParamsException::InvalidParamsException(const char* data, const Id& requestId) : RequestException(Error("Invalid params", -32602, data), requestId) +{ +} + +inline InvalidParamsException::InvalidParamsException(const std::string& data, const Id& requestId) : InvalidParamsException(data.c_str(), requestId) +{ +} + + + +inline InternalErrorException::InternalErrorException(const Id& requestId) : RequestException(Error("Internal error", -32603), requestId) +{ +} + +inline InternalErrorException::InternalErrorException(const Request& request) : InternalErrorException(request.id) +{ +} + +inline InternalErrorException::InternalErrorException(const char* data, const Id& requestId) : RequestException(Error("Internal error", -32603, data), requestId) +{ +} + +inline InternalErrorException::InternalErrorException(const std::string& data, const Id& requestId) : InternalErrorException(data.c_str(), requestId) +{ +} + + + +///////////////////// Response implementation ///////////////////////////////// + +inline Response::Response(const Json& json) : Entity(entity_t::response) +{ + if (json != nullptr) + parse_json(json); +} + + +inline Response::Response(const Id& id, const Json& result) : Entity(entity_t::response), id(id), result(result), error(nullptr) +{ +} + + +inline Response::Response(const Id& id, const Error& error) : Entity(entity_t::response), id(id), result(), error(error) +{ +} + + +inline Response::Response(const Request& request, const Json& result) : Response(request.id, result) +{ +} + + +inline Response::Response(const Request& request, const Error& error) : Response(request.id, error) +{ +} + + +inline Response::Response(const RequestException& exception) : Response(exception.id, exception.error) +{ +} + + +inline void Response::parse_json(const Json& json) +{ + try + { + error = nullptr; + result = nullptr; + if (json.count("jsonrpc") == 0) + throw RpcException("jsonrpc is missing"); + std::string jsonrpc = json["jsonrpc"].get(); + if (jsonrpc != "2.0") + throw RpcException("invalid jsonrpc value: " + jsonrpc); + if (json.count("id") == 0) + throw RpcException("id is missing"); + id = Id(json["id"]); + if (json.count("result")) + result = json["result"]; + else if (json.count("error")) + error.parse_json(json["error"]); + else + throw RpcException("response must contain result or error"); + } + catch (const RpcException& e) + { + throw; + } + catch (const std::exception& e) + { + throw RpcException(e.what()); + } +} + + +inline Json Response::to_json() const +{ + Json j = { + {"jsonrpc", "2.0"}, + {"id", id.to_json()}, + }; + + if (error) + j["error"] = error.to_json(); + else + j["result"] = result; + + return j; +} + + + + + +///////////////// Notification implementation ///////////////////////////////// + +inline Notification::Notification(const Json& json) : Entity(entity_t::notification) +{ + if (json != nullptr) + parse_json(json); +} + + +inline Notification::Notification(const char* method, const Parameter& params) : Entity(entity_t::notification), method(method), params(params) +{ +} + + +inline Notification::Notification(const std::string& method, const Parameter& params) : Notification(method.c_str(), params) +{ +} + + +inline void Notification::parse_json(const Json& json) +{ + try + { + if (json.count("jsonrpc") == 0) + throw RpcException("jsonrpc is missing"); + std::string jsonrpc = json["jsonrpc"].get(); + if (jsonrpc != "2.0") + throw RpcException("invalid jsonrpc value: " + jsonrpc); + + if (json.count("method") == 0) + throw RpcException("method is missing"); + if (!json["method"].is_string()) + throw RpcException("method must be a string value"); + method = json["method"]; + if (method.empty()) + throw RpcException("method must not be empty"); + + if (json.count("params")) + params.parse_json(json["params"]); + else + params = nullptr; + } + catch (const RpcException& e) + { + throw; + } + catch (const std::exception& e) + { + throw RpcException(e.what()); + } +} + + +inline Json Notification::to_json() const +{ + Json json = { + {"jsonrpc", "2.0"}, + {"method", method}, + }; + + if (params) + json["params"] = params.to_json(); + + return json; +} + + + + + +//////////////////////// Batch implementation ///////////////////////////////// + +inline Batch::Batch(const Json& json) : Entity(entity_t::batch) +{ + if (json != nullptr) + parse_json(json); +} + + +inline void Batch::parse_json(const Json& json) +{ +// cout << "Batch::parse: " << json.dump() << "\n"; + entities.clear(); + for (auto it = json.begin(); it != json.end(); ++it) + { +// cout << "x: " << it->dump() << "\n"; + entity_ptr entity(nullptr); + try + { + entity = Parser::do_parse_json(*it); + if (!entity) + entity = std::make_shared("Invalid Request", -32600); + } + catch(const RequestException& e) + { + entity = std::make_shared(e); + } + catch(const std::exception& e) + { + entity = std::make_shared(e.what(), -32600); + } + entities.push_back(entity); + } + if (entities.empty()) + throw InvalidRequestException(); +} + + +inline Json Batch::to_json() const +{ + Json result; + for (const auto& j: entities) + result.push_back(j->to_json()); + return result; +} + + +/*void Batch::add(const entity_ptr entity) +{ + entities.push_back(entity); +} +*/ + + + + +//////////////////////// Parser implementation //////////////////////////////// + +inline Parser::Parser() +{ + +} + + +inline Parser::~Parser() +{ + +} + + +inline void Parser::register_notification_callback(const std::string& notification, notification_callback callback) +{ + if (callback) + notification_callbacks_[notification] = callback; +} + + +inline void Parser::register_request_callback(const std::string& request, request_callback callback) +{ + if (callback) + request_callbacks_[request] = callback; +} + + +inline entity_ptr Parser::parse(const std::string& json_str) +{ + //std::cout << "parse: " << json_str << "\n"; + entity_ptr entity = do_parse(json_str); + if (entity && entity->is_notification()) + { + notification_ptr notification = std::dynamic_pointer_cast(entity); + if (notification_callbacks_.find(notification->method) != notification_callbacks_.end()) + { + notification_callback callback = notification_callbacks_[notification->method]; + if (callback) + callback(notification->params); + } + } + else if (entity && entity->is_request()) + { + request_ptr request = std::dynamic_pointer_cast(entity); + if (request_callbacks_.find(request->method) != request_callbacks_.end()) + { + request_callback callback = request_callbacks_[request->method]; + if (callback) + { + jsonrpcpp::response_ptr response = callback(request->id, request->params); + if (response) + return response; + } + } + } + return entity; +} + + +inline entity_ptr Parser::parse_json(const Json& json) +{ + return do_parse_json(json); +} + + +inline entity_ptr Parser::do_parse(const std::string& json_str) +{ + try + { + return do_parse_json(Json::parse(json_str)); + } + catch (const RpcException& e) + { + throw; + } + catch (const std::exception& e) + { + throw ParseErrorException(e.what()); + } + + return nullptr; +} + + +inline entity_ptr Parser::do_parse_json(const Json& json) +{ + try + { + if (is_request(json)) + return std::make_shared(json); + else if (is_notification(json)) + return std::make_shared(json); + else if (is_response(json)) + return std::make_shared(json); + else if (is_batch(json)) + return std::make_shared(json); + } + catch (const RpcException& e) + { + throw; + } + catch (const std::exception& e) + { + throw RpcException(e.what()); + } + + return nullptr; +} + + +inline bool Parser::is_request(const std::string& json_str) +{ + try + { + return is_request(Json::parse(json_str)); + } + catch (const std::exception& e) + { + return false; + } +} + + +inline bool Parser::is_request(const Json& json) +{ + return (json.count("method") && json.count("id")); +} + + +inline bool Parser::is_notification(const std::string& json_str) +{ + try + { + return is_notification(Json::parse(json_str)); + } + catch (const std::exception& e) + { + return false; + } +} + + +inline bool Parser:: is_notification(const Json& json) +{ + return (json.count("method") && (json.count("id") == 0)); +} + + +inline bool Parser::is_response(const std::string& json_str) +{ + try + { + return is_response(Json::parse(json_str)); + } + catch (const std::exception& e) + { + return false; + } +} + + +inline bool Parser::is_response(const Json& json) +{ + return (json.count("result") && json.count("id")); +} + + +inline bool Parser::is_batch(const std::string& json_str) +{ + try + { + return is_batch(Json::parse(json_str)); + } + catch (const std::exception& e) + { + return false; + } +} + + +inline bool Parser::is_batch(const Json& json) +{ + return (json.is_array()); +} + + +} //namespace jsonrpc + + + +#endif diff --git a/jsonrpctest.cpp b/jsonrpcpp_example.cpp similarity index 99% rename from jsonrpctest.cpp rename to jsonrpcpp_example.cpp index f4855dc..2f25033 100644 --- a/jsonrpctest.cpp +++ b/jsonrpcpp_example.cpp @@ -1,6 +1,6 @@ /*** This file is part of jsonrpc++ - Copyright (C) 2017 Johannes Pohl + Copyright (C) 2017-2018 Johannes Pohl This software may be modified and distributed under the terms of the MIT license. See the LICENSE file for details. @@ -8,7 +8,7 @@ #include -#include "jsonrp.hpp" +#include "jsonrpcpp.hpp" using namespace std; diff --git a/lib/jsonrp.cpp b/lib/jsonrp.cpp deleted file mode 100644 index 29b6938..0000000 --- a/lib/jsonrp.cpp +++ /dev/null @@ -1,1017 +0,0 @@ -/*** - This file is part of jsonrpc++ - Copyright (C) 2017 Johannes Pohl - - This software may be modified and distributed under the terms - of the MIT license. See the LICENSE file for details. -***/ - -#include "jsonrp.hpp" - - -using namespace std; - -namespace jsonrpcpp -{ - - -/////////////////////////// Entity implementation ///////////////////////////// - -Entity::Entity(entity_t type) : entity(type) -{ -} - - -Entity::~Entity() -{ -} - - -bool Entity::is_exception() -{ - return (entity == entity_t::exception); -} - - -bool Entity::is_id() -{ - return (entity == entity_t::id); -} - - -bool Entity::is_error() -{ - return (entity == entity_t::error); -} - - -bool Entity::is_response() -{ - return (entity == entity_t::response); -} - - -bool Entity::is_request() -{ - return (entity == entity_t::request); -} - - -bool Entity::is_notification() -{ - return (entity == entity_t::notification); -} - - -bool Entity::is_batch() -{ - return (entity == entity_t::batch); -} - - -void Entity::parse(const char* json_str) -{ - // http://www.jsonrpc.org/specification - // code message meaning - // -32700 Parse error Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text. - // -32600 Invalid Request The JSON sent is not a valid Request object. - // -32601 Method not found The method does not exist / is not available. - // -32602 Invalid params Invalid method parameter(s). - // -32603 Internal error Internal JSON-RPC error. - // -32000 to -32099 Server error Reserved for implementation-defined server-errors. - try - { - parse_json(Json::parse(json_str)); - } - catch (const RpcException& e) - { - throw; - } - catch (const exception& e) - { - throw ParseErrorException(e.what()); - } -} - - -void Entity::parse(const std::string& json_str) -{ - parse(json_str.c_str()); -} - - -std::string Entity::type_str() const -{ - switch (entity) - { - case entity_t::unknown: - return "unknown"; - case entity_t::id: - return "id"; - case entity_t::exception: - return "exception"; - case entity_t::error: - return "error"; - case entity_t::response: - return "response"; - case entity_t::request: - return "request"; - case entity_t::notification: - return "notification"; - case entity_t::batch: - return "batch"; - default: - return "unknown"; - } -} - - - - - -/////////////////////////// NullableEntity implementation ///////////////////// - -NullableEntity::NullableEntity(entity_t type) : Entity(type), isNull(false) -{ -} - - -NullableEntity::NullableEntity(entity_t type, std::nullptr_t) : Entity(type), isNull(true) -{ -} - - -NullableEntity::~NullableEntity() -{ -}; - - - - - -/////////////////////////// Id implementation ///////////////////////////////// - -Id::Id() : Entity(entity_t::id), type(value_t::null), int_id(0), string_id("") -{ -} - - -Id::Id(int id) : Entity(entity_t::id), type(value_t::integer), int_id(id), string_id("") -{ -} - - -Id::Id(const char* id) : Entity(entity_t::id), type(value_t::string), int_id(0), string_id(id) -{ -} - - -Id::Id(const std::string& id) : Id(id.c_str()) -{ -} - - -Id::Id(const Json& json_id) : Entity(entity_t::id), type(value_t::null) -{ - parse_json(json_id); -} - - -void Id::parse_json(const Json& json) -{ - if (json.is_null()) - { - type = value_t::null; - } - else if (json.is_number_integer()) - { - int_id = json.get(); - type = value_t::integer; - } - else if (json.is_string()) - { - string_id = json.get(); - type = value_t::string; - } - else - throw std::invalid_argument("id must be integer, string or null"); -} - - -Json Id::to_json() const -{ - if (type == value_t::null) - return nullptr; - else if (type == value_t::string) - return string_id; - else if (type == value_t::integer) - return int_id; - - return nullptr; -} - - - - - -//////////////////////// Error implementation ///////////////////////////////// - -Parameter::Parameter(std::nullptr_t) : NullableEntity(entity_t::id, nullptr), type(value_t::null) -{ -} - - -Parameter::Parameter(const Json& json) : NullableEntity(entity_t::id), type(value_t::null) -{ - if (json != nullptr) - parse_json(json); -} - - -Parameter::Parameter(const std::string& key1, const Json& value1, - const std::string& key2, const Json& value2, - const std::string& key3, const Json& value3, - const std::string& key4, const Json& value4) : NullableEntity(entity_t::id), type(value_t::map) -{ - param_map[key1] = value1; - if (!key2.empty()) - param_map[key2] = value2; - if (!key3.empty()) - param_map[key3] = value3; - if (!key4.empty()) - param_map[key4] = value4; -} - - -void Parameter::parse_json(const Json& json) -{ - if (json.is_array()) - { - param_array = json.get>(); - param_map.clear(); - type = value_t::array; - } - else - { - param_map = json.get>(); - param_array.clear(); - type = value_t::map; - } -} - - -Json Parameter::to_json() const -{ - if (type == value_t::array) - return param_array; - else if (type == value_t::map) - return param_map; - else - return nullptr; -} - - -bool Parameter::is_array() const -{ - return type == value_t::array; -} - - -bool Parameter::is_map() const -{ - return type == value_t::map; -} - - -bool Parameter::is_null() const -{ - return isNull; -} - - -bool Parameter::has(const std::string& key) const -{ - if (type != value_t::map) - return false; - return (param_map.find(key) != param_map.end()); -} - - -Json Parameter::get(const std::string& key) const -{ - return param_map.at(key); -} - - -bool Parameter::has(size_t idx) const -{ - if (type != value_t::array) - return false; - return (param_array.size() > idx); -} - - -Json Parameter::get(size_t idx) const -{ - return param_array.at(idx); -} - - - - - -//////////////////////// Error implementation ///////////////////////////////// - -Error::Error(const Json& json) : Error("Internal error", -32603, nullptr) -{ - if (json != nullptr) - parse_json(json); -} - - -Error::Error(std::nullptr_t) : NullableEntity(entity_t::error, nullptr), code(0), message(""), data(nullptr) -{ -} - - -Error::Error(const std::string& message, int code, const Json& data) : NullableEntity(entity_t::error), code(code), message(message), data(data) -{ -} - - -void Error::parse_json(const Json& json) -{ - try - { - if (json.count("code") == 0) - throw RpcException("code is missing"); - code = json["code"]; - if (json.count("message") == 0) - throw RpcException("message is missing"); - message = json["message"]; - if (json.count("data")) - data = json["data"]; - else - data = nullptr; - } - catch (const RpcException& e) - { - throw; - } - catch (const exception& e) - { - throw RpcException(e.what()); - } -} - - -Json Error::to_json() const -{ - Json j = { - {"code", code}, - {"message", message}, - }; - - if (!data.is_null()) - j["data"] = data; - return j; -} - - - - - -////////////////////// Request implementation ///////////////////////////////// - -Request::Request(const Json& json) : Entity(entity_t::request), method(""), id() -{ - if (json != nullptr) - parse_json(json); -} - - -Request::Request(const Id& id, const std::string& method, const Parameter& params) : Entity(entity_t::request), method(method), params(params), id(id) -{ -} - - -void Request::parse_json(const Json& json) -{ - try - { - if (json.count("id") == 0) - throw InvalidRequestException("id is missing"); - - try - { - id = Id(json["id"]); - } - catch(const std::exception& e) - { - throw InvalidRequestException(e.what()); - } - - if (json.count("jsonrpc") == 0) - throw InvalidRequestException("jsonrpc is missing", id); - string jsonrpc = json["jsonrpc"].get(); - if (jsonrpc != "2.0") - throw InvalidRequestException("invalid jsonrpc value: " + jsonrpc, id); - - if (json.count("method") == 0) - throw InvalidRequestException("method is missing", id); - if (!json["method"].is_string()) - throw InvalidRequestException("method must be a string value", id); - method = json["method"]; - if (method.empty()) - throw InvalidRequestException("method must not be empty", id); - - if (json.count("params")) - params.parse_json(json["params"]); - else - params = nullptr; - } - catch (const RequestException& e) - { - throw; - } - catch (const exception& e) - { - throw InternalErrorException(e.what(), id); - } -} - - -Json Request::to_json() const -{ - Json json = { - {"jsonrpc", "2.0"}, - {"method", method}, - {"id", id.to_json()} - }; - - if (params) - json["params"] = params.to_json(); - - return json; -} - - - -RpcException::RpcException(const char* text) -{ - text_ = new char[std::strlen(text) + 1]; - std::strcpy(text_, text); -} - -RpcException::RpcException(const std::string& text) : RpcException(text.c_str()) -{ -} - -RpcException::RpcException(const RpcException& e) : RpcException(e.what()) -{ -} - -RpcException::~RpcException() throw() -{ - delete[] text_; -} - -const char* RpcException::what() const noexcept -{ - return text_; -} - - - - -ParseErrorException::ParseErrorException(const Error& error) : RpcException(error.message), Entity(entity_t::exception), error(error) -{ -} - -ParseErrorException::ParseErrorException(const ParseErrorException& e) : RpcException(e.what()), Entity(entity_t::exception), error(e.error) -{ -} - -ParseErrorException::ParseErrorException(const std::string& data) : ParseErrorException(Error("Parse error", -32700, data)) -{ -} - -Json ParseErrorException::to_json() const -{ - Json response = { - {"jsonrpc", "2.0"}, - {"error", error.to_json()}, - {"id", nullptr} - }; - - return response; -} - -void ParseErrorException::parse_json(const Json& json) -{ -} - - - - -RequestException::RequestException(const Error& error, const Id& requestId) : RpcException(error.message), Entity(entity_t::exception), error(error), id(requestId) -{ -} - -RequestException::RequestException(const RequestException& e) : RpcException(e.what()), Entity(entity_t::exception), error(e.error), id(e.id) -{ -} - -Json RequestException::to_json() const -{ - Json response = { - {"jsonrpc", "2.0"}, - {"error", error.to_json()}, - {"id", id.to_json()} - }; - - return response; -} - -void RequestException::parse_json(const Json& json) -{ -} - - - - - -InvalidRequestException::InvalidRequestException(const Id& requestId) : RequestException(Error("Invalid request", -32600), requestId) -{ -} - -InvalidRequestException::InvalidRequestException(const Request& request) : InvalidRequestException(request.id) -{ -} - -InvalidRequestException::InvalidRequestException(const char* data, const Id& requestId) : RequestException(Error("Invalid request", -32600, data), requestId) -{ -} - -InvalidRequestException::InvalidRequestException(const std::string& data, const Id& requestId) : InvalidRequestException(data.c_str(), requestId) -{ -} - - - -MethodNotFoundException::MethodNotFoundException(const Id& requestId) : RequestException(Error("Method not found", -32601), requestId) -{ -} - -MethodNotFoundException::MethodNotFoundException(const Request& request) : MethodNotFoundException(request.id) -{ -} - -MethodNotFoundException::MethodNotFoundException(const char* data, const Id& requestId) : RequestException(Error("Method not found", -32601, data), requestId) -{ -} - -MethodNotFoundException::MethodNotFoundException(const std::string& data, const Id& requestId) : MethodNotFoundException(data.c_str(), requestId) -{ -} - - - -InvalidParamsException::InvalidParamsException(const Id& requestId) : RequestException(Error("Invalid params", -32602), requestId) -{ -} - -InvalidParamsException::InvalidParamsException(const Request& request) : InvalidParamsException(request.id) -{ -} - -InvalidParamsException::InvalidParamsException(const char* data, const Id& requestId) : RequestException(Error("Invalid params", -32602, data), requestId) -{ -} - -InvalidParamsException::InvalidParamsException(const std::string& data, const Id& requestId) : InvalidParamsException(data.c_str(), requestId) -{ -} - - - -InternalErrorException::InternalErrorException(const Id& requestId) : RequestException(Error("Internal error", -32603), requestId) -{ -} - -InternalErrorException::InternalErrorException(const Request& request) : InternalErrorException(request.id) -{ -} - -InternalErrorException::InternalErrorException(const char* data, const Id& requestId) : RequestException(Error("Internal error", -32603, data), requestId) -{ -} - -InternalErrorException::InternalErrorException(const std::string& data, const Id& requestId) : InternalErrorException(data.c_str(), requestId) -{ -} - - - -///////////////////// Response implementation ///////////////////////////////// - -Response::Response(const Json& json) : Entity(entity_t::response) -{ - if (json != nullptr) - parse_json(json); -} - - -Response::Response(const Id& id, const Json& result) : Entity(entity_t::response), id(id), result(result), error(nullptr) -{ -} - - -Response::Response(const Id& id, const Error& error) : Entity(entity_t::response), id(id), result(), error(error) -{ -} - - -Response::Response(const Request& request, const Json& result) : Response(request.id, result) -{ -} - - -Response::Response(const Request& request, const Error& error) : Response(request.id, error) -{ -} - - -Response::Response(const RequestException& exception) : Response(exception.id, exception.error) -{ -} - - -void Response::parse_json(const Json& json) -{ - try - { - error = nullptr; - result = nullptr; - if (json.count("jsonrpc") == 0) - throw RpcException("jsonrpc is missing"); - string jsonrpc = json["jsonrpc"].get(); - if (jsonrpc != "2.0") - throw RpcException("invalid jsonrpc value: " + jsonrpc); - if (json.count("id") == 0) - throw RpcException("id is missing"); - id = Id(json["id"]); - if (json.count("result")) - result = json["result"]; - else if (json.count("error")) - error.parse_json(json["error"]); - else - throw RpcException("response must contain result or error"); - } - catch (const RpcException& e) - { - throw; - } - catch (const exception& e) - { - throw RpcException(e.what()); - } -} - - -Json Response::to_json() const -{ - Json j = { - {"jsonrpc", "2.0"}, - {"id", id.to_json()}, - }; - - if (error) - j["error"] = error.to_json(); - else - j["result"] = result; - - return j; -} - - - - - -///////////////// Notification implementation ///////////////////////////////// - -Notification::Notification(const Json& json) : Entity(entity_t::notification) -{ - if (json != nullptr) - parse_json(json); -} - - -Notification::Notification(const char* method, const Parameter& params) : Entity(entity_t::notification), method(method), params(params) -{ -} - - -Notification::Notification(const std::string& method, const Parameter& params) : Notification(method.c_str(), params) -{ -} - - -void Notification::parse_json(const Json& json) -{ - try - { - if (json.count("jsonrpc") == 0) - throw RpcException("jsonrpc is missing"); - string jsonrpc = json["jsonrpc"].get(); - if (jsonrpc != "2.0") - throw RpcException("invalid jsonrpc value: " + jsonrpc); - - if (json.count("method") == 0) - throw RpcException("method is missing"); - if (!json["method"].is_string()) - throw RpcException("method must be a string value"); - method = json["method"]; - if (method.empty()) - throw RpcException("method must not be empty"); - - if (json.count("params")) - params.parse_json(json["params"]); - else - params = nullptr; - } - catch (const RpcException& e) - { - throw; - } - catch (const exception& e) - { - throw RpcException(e.what()); - } -} - - -Json Notification::to_json() const -{ - Json json = { - {"jsonrpc", "2.0"}, - {"method", method}, - }; - - if (params) - json["params"] = params.to_json(); - - return json; -} - - - - - -//////////////////////// Batch implementation ///////////////////////////////// - -Batch::Batch(const Json& json) : Entity(entity_t::batch) -{ - if (json != nullptr) - parse_json(json); -} - - -void Batch::parse_json(const Json& json) -{ -// cout << "Batch::parse: " << json.dump() << "\n"; - entities.clear(); - for (auto it = json.begin(); it != json.end(); ++it) - { -// cout << "x: " << it->dump() << "\n"; - entity_ptr entity(nullptr); - try - { - entity = Parser::do_parse_json(*it); - if (!entity) - entity = make_shared("Invalid Request", -32600); - } - catch(const RequestException& e) - { - entity = make_shared(e); - } - catch(const std::exception& e) - { - entity = make_shared(e.what(), -32600); - } - entities.push_back(entity); - } - if (entities.empty()) - throw InvalidRequestException(); -} - - -Json Batch::to_json() const -{ - Json result; - for (const auto& j: entities) - result.push_back(j->to_json()); - return result; -} - - -/*void Batch::add(const entity_ptr entity) -{ - entities.push_back(entity); -} -*/ - - - - -//////////////////////// Parser implementation //////////////////////////////// - -Parser::Parser() -{ - -} - - -Parser::~Parser() -{ - -} - - -void Parser::register_notification_callback(const std::string& notification, notification_callback callback) -{ - if (callback) - notification_callbacks_[notification] = callback; -} - - -void Parser::register_request_callback(const std::string& request, request_callback callback) -{ - if (callback) - request_callbacks_[request] = callback; -} - - -entity_ptr Parser::parse(const std::string& json_str) -{ - //std::cout << "parse: " << json_str << "\n"; - entity_ptr entity = do_parse(json_str); - if (entity && entity->is_notification()) - { - notification_ptr notification = dynamic_pointer_cast(entity); - if (notification_callbacks_.find(notification->method) != notification_callbacks_.end()) - { - notification_callback callback = notification_callbacks_[notification->method]; - if (callback) - callback(notification->params); - } - } - else if (entity && entity->is_request()) - { - request_ptr request = dynamic_pointer_cast(entity); - if (request_callbacks_.find(request->method) != request_callbacks_.end()) - { - request_callback callback = request_callbacks_[request->method]; - if (callback) - { - jsonrpcpp::response_ptr response = callback(request->id, request->params); - if (response) - return response; - } - } - } - return entity; -} - - -entity_ptr Parser::parse_json(const Json& json) -{ - return do_parse_json(json); -} - - -entity_ptr Parser::do_parse(const std::string& json_str) -{ - try - { - return do_parse_json(Json::parse(json_str)); - } - catch (const RpcException& e) - { - throw; - } - catch (const exception& e) - { - throw ParseErrorException(e.what()); - } - - return nullptr; -} - - -entity_ptr Parser::do_parse_json(const Json& json) -{ - try - { - if (is_request(json)) - return make_shared(json); - else if (is_notification(json)) - return make_shared(json); - else if (is_response(json)) - return make_shared(json); - else if (is_batch(json)) - return make_shared(json); - } - catch (const RpcException& e) - { - throw; - } - catch (const exception& e) - { - throw RpcException(e.what()); - } - - return nullptr; -} - - -bool Parser::is_request(const std::string& json_str) -{ - try - { - return is_request(Json::parse(json_str)); - } - catch (const exception& e) - { - return false; - } -} - - -bool Parser::is_request(const Json& json) -{ - return (json.count("method") && json.count("id")); -} - - -bool Parser::is_notification(const std::string& json_str) -{ - try - { - return is_notification(Json::parse(json_str)); - } - catch (const exception& e) - { - return false; - } -} - - -bool Parser:: is_notification(const Json& json) -{ - return (json.count("method") && (json.count("id") == 0)); -} - - -bool Parser::is_response(const std::string& json_str) -{ - try - { - return is_response(Json::parse(json_str)); - } - catch (const exception& e) - { - return false; - } -} - - -bool Parser::is_response(const Json& json) -{ - return (json.count("result") && json.count("id")); -} - - -bool Parser::is_batch(const std::string& json_str) -{ - try - { - return is_batch(Json::parse(json_str)); - } - catch (const exception& e) - { - return false; - } -} - - -bool Parser::is_batch(const Json& json) -{ - return (json.is_array()); -} - - - -} - - diff --git a/lib/jsonrp.hpp b/lib/jsonrp.hpp deleted file mode 100644 index cdda1d3..0000000 --- a/lib/jsonrp.hpp +++ /dev/null @@ -1,461 +0,0 @@ -/*** - __ ____ __ __ _ ____ ____ ___ _ _ - _( )/ ___) / \ ( ( \( _ \( _ \ / __)( ) ( ) - / \) \\___ \( O )/ / ) / ) __/( (__(_ _)(_ _) - \____/(____/ \__/ \_)__)(__\_)(__) \___)(_) (_) - version 1.1.1 - https://github.com/badaix/jsonrpcpp - - This file is part of jsonrpc++ - Copyright (C) 2017 Johannes Pohl - - This software may be modified and distributed under the terms - of the MIT license. See the LICENSE file for details. -***/ - -/// http://patorjk.com/software/taag/#p=display&f=Graceful&t=JSONRPC%2B%2B - -#ifndef JSON_RPC_H -#define JSON_RPC_H - -#include -#include -#include -#include -#include "json.hpp" - - -using Json = nlohmann::json; - -namespace jsonrpcpp -{ - - -class Entity; -class Request; -class Notification; -class Parameter; -class Response; -class Error; -class Batch; - - -typedef std::shared_ptr entity_ptr; -typedef std::shared_ptr request_ptr; -typedef std::shared_ptr notification_ptr; -typedef std::shared_ptr parameter_ptr; -typedef std::shared_ptr response_ptr; -typedef std::shared_ptr error_ptr; -typedef std::shared_ptr batch_ptr; - - - -class Entity -{ -public: - enum class entity_t : uint8_t - { - unknown, - exception, - id, - error, - response, - request, - notification, - batch - }; - - Entity(entity_t type); - virtual ~Entity(); - - bool is_exception(); - bool is_id(); - bool is_error(); - bool is_response(); - bool is_request(); - bool is_notification(); - bool is_batch(); - - virtual std::string type_str() const; - - virtual Json to_json() const = 0; - virtual void parse_json(const Json& json) = 0; - - virtual void parse(const std::string& json_str); - virtual void parse(const char* json_str); - -protected: - entity_t entity; -}; - - - - - -class NullableEntity : public Entity -{ -public: - NullableEntity(entity_t type); - NullableEntity(entity_t type, std::nullptr_t); - virtual ~NullableEntity(); -#ifdef _MSC_VER - virtual operator bool() const -#else - virtual explicit operator bool() const -#endif - { - return !isNull; - } - -protected: - bool isNull; -}; - - - - - -class Id : public Entity -{ -public: - enum class value_t : uint8_t - { - null, - string, - integer - }; - - Id(); - Id(int id); - Id(const char* id); - Id(const std::string& id); - Id(const Json& json_id); - - virtual Json to_json() const; - virtual void parse_json(const Json& json); - - friend std::ostream& operator<< (std::ostream &out, const Id &id) - { - out << id.to_json(); - return out; - } - - value_t type; - int int_id; - std::string string_id; -}; - - - - - -class Parameter : public NullableEntity -{ -public: - enum class value_t : uint8_t - { - null, - array, - map - }; - - Parameter(std::nullptr_t); - Parameter(const Json& json = nullptr); - Parameter(const std::string& key1, const Json& value1, - const std::string& key2 = "", const Json& value2 = nullptr, - const std::string& key3 = "", const Json& value3 = nullptr, - const std::string& key4 = "", const Json& value4 = nullptr); - - virtual Json to_json() const; - virtual void parse_json(const Json& json); - - bool is_array() const; - bool is_map() const; - bool is_null() const; - - Json get(const std::string& key) const; - Json get(size_t idx) const; - bool has(const std::string& key) const; - bool has(size_t idx) const; - - template - T get(const std::string& key) const - { - return get(key).get(); - } - - template - T get(size_t idx) const - { - return get(idx).get(); - } - - template - T get(const std::string& key, const T& default_value) const - { - if (!has(key)) - return default_value; - else - return get(key); - } - - template - T get(size_t idx, const T& default_value) const - { - if (!has(idx)) - return default_value; - else - return get(idx); - } - - value_t type; - std::vector param_array; - std::map param_map; -}; - - - - - -class Error : public NullableEntity -{ -public: - Error(const Json& json = nullptr); - Error(std::nullptr_t); - Error(const std::string& message, int code, const Json& data = nullptr); - - virtual Json to_json() const; - virtual void parse_json(const Json& json); - - int code; - std::string message; - Json data; -}; - - - -/// JSON-RPC 2.0 request -/** - * Simple jsonrpc 2.0 parser with getters - * Currently no named parameters are supported, but only array parameters - */ -class Request : public Entity -{ -public: - Request(const Json& json = nullptr); - Request(const Id& id, const std::string& method, const Parameter& params = nullptr); - - virtual Json to_json() const; - virtual void parse_json(const Json& json); - - std::string method; - Parameter params; - Id id; -}; - - - - - -class RpcException : public std::exception -{ - char* text_; -public: - RpcException(const char* text); - RpcException(const std::string& text); - RpcException(const RpcException& e); - - virtual ~RpcException() throw(); - virtual const char* what() const noexcept; -}; - - - - - -class ParseErrorException : public RpcException, public Entity -{ -public: - Error error; - - ParseErrorException(const Error& error); - ParseErrorException(const ParseErrorException& e); - ParseErrorException(const std::string& data); - virtual Json to_json() const; - -protected: - virtual void parse_json(const Json& json); -}; - - - -// -32600 Invalid Request The JSON sent is not a valid Request object. -// -32601 Method not found The method does not exist / is not available. -// -32602 Invalid params Invalid method parameter(s). -// -32603 Internal error Internal JSON-RPC error. - -class RequestException : public RpcException, public Entity -{ -public: - Error error; - Id id; - - RequestException(const Error& error, const Id& requestId = Id()); - RequestException(const RequestException& e); - virtual Json to_json() const; - -protected: - virtual void parse_json(const Json& json); -}; - - - -class InvalidRequestException : public RequestException -{ -public: - InvalidRequestException(const Id& requestId = Id()); - InvalidRequestException(const Request& request); - InvalidRequestException(const char* data, const Id& requestId = Id()); - InvalidRequestException(const std::string& data, const Id& requestId = Id()); -}; - - - -class MethodNotFoundException : public RequestException -{ -public: - MethodNotFoundException(const Id& requestId = Id()); - MethodNotFoundException(const Request& request); - MethodNotFoundException(const char* data, const Id& requestId = Id()); - MethodNotFoundException(const std::string& data, const Id& requestId = Id()); -}; - - - -class InvalidParamsException : public RequestException -{ -public: - InvalidParamsException(const Id& requestId = Id()); - InvalidParamsException(const Request& request); - InvalidParamsException(const char* data, const Id& requestId = Id()); - InvalidParamsException(const std::string& data, const Id& requestId = Id()); -}; - - - -class InternalErrorException : public RequestException -{ -public: - InternalErrorException(const Id& requestId = Id()); - InternalErrorException(const Request& request); - InternalErrorException(const char* data, const Id& requestId = Id()); - InternalErrorException(const std::string& data, const Id& requestId = Id()); -}; - - - - - -class Response : public Entity -{ -public: - Id id; - Json result; - Error error; - - Response(const Json& json = nullptr); - Response(const Id& id, const Json& result); - Response(const Id& id, const Error& error); - Response(const Request& request, const Json& result); - Response(const Request& request, const Error& error); - Response(const RequestException& exception); - - virtual Json to_json() const; - virtual void parse_json(const Json& json); -}; - - - - - -class Notification : public Entity -{ -public: - std::string method; - Parameter params; - Notification(const Json& json = nullptr); - Notification(const char* method, const Parameter& params = nullptr); - Notification(const std::string& method, const Parameter& params); - - virtual Json to_json() const; - virtual void parse_json(const Json& json); -}; - - - - -typedef std::function notification_callback; -typedef std::function request_callback; - - -class Parser -{ -public: - Parser(); - virtual ~Parser(); - - entity_ptr parse(const std::string& json_str); - entity_ptr parse_json(const Json& json); - - void register_notification_callback(const std::string& notification, notification_callback callback); - void register_request_callback(const std::string& request, request_callback callback); - - static entity_ptr do_parse(const std::string& json_str); - static entity_ptr do_parse_json(const Json& json); - static bool is_request(const std::string& json_str); - static bool is_request(const Json& json); - static bool is_notification(const std::string& json_str); - static bool is_notification(const Json& json); - static bool is_response(const std::string& json_str); - static bool is_response(const Json& json); - static bool is_batch(const std::string& json_str); - static bool is_batch(const Json& json); - -private: - std::map notification_callbacks_; - std::map request_callbacks_; -}; - - - - - -class Batch : public Entity -{ -public: - std::vector entities; - - Batch(const Json& json = nullptr); - - virtual Json to_json() const; - virtual void parse_json(const Json& json); - - template - void add(const T& entity) - { - entities.push_back(std::make_shared(entity)); - } - - void add_ptr(const entity_ptr& entity) - { - entities.push_back(entity); - } -}; - - - -} //namespace jsonrpc - - - -#endif