switch to jsonrpc++ external lib

This commit is contained in:
badaix 2017-01-30 21:35:14 +01:00
parent 89f14e4347
commit 58025a912b
9 changed files with 46 additions and 612 deletions

2
externals/jsonrpcpp vendored

@ -1 +1 @@
Subproject commit c7f25dab979f3118dfdf922aabcf305d19937bb1
Subproject commit df2084d291100eb6744c01deb3aff36048100ec2

View file

@ -13,9 +13,9 @@ else
TARGET_DIR ?= /usr
endif
CXXFLAGS += $(ADD_CFLAGS) -std=c++0x -Wall -Wno-unused-function -O3 -DASIO_STANDALONE -DVERSION=\"$(VERSION)\" -I. -I.. -isystem ../externals/asio/asio/include -I../externals/popl/include
CXXFLAGS += $(ADD_CFLAGS) -std=c++0x -Wall -Wno-unused-function -O3 -DASIO_STANDALONE -DVERSION=\"$(VERSION)\" -I. -I.. -isystem ../externals/asio/asio/include -I../externals/popl/include -I../externals/jsonrpcpp/lib -I../externals
LDFLAGS = -lvorbis -lvorbisenc -logg -lFLAC
OBJ = snapServer.o config.o controlServer.o controlSession.o streamServer.o streamSession.o json/jsonrpc.o streamreader/streamUri.o streamreader/streamManager.o streamreader/pcmStream.o streamreader/pipeStream.o streamreader/fileStream.o streamreader/processStream.o streamreader/airplayStream.o streamreader/spotifyStream.o streamreader/watchdog.o encoder/encoderFactory.o encoder/flacEncoder.o encoder/pcmEncoder.o encoder/oggEncoder.o ../common/log.o ../common/sampleFormat.o ../message/pcmChunk.o
OBJ = snapServer.o config.o controlServer.o controlSession.o streamServer.o streamSession.o streamreader/streamUri.o streamreader/streamManager.o streamreader/pcmStream.o streamreader/pipeStream.o streamreader/fileStream.o streamreader/processStream.o streamreader/airplayStream.o streamreader/spotifyStream.o streamreader/watchdog.o encoder/encoderFactory.o encoder/flacEncoder.o encoder/pcmEncoder.o encoder/oggEncoder.o ../common/log.o ../common/sampleFormat.o ../message/pcmChunk.o ../externals/jsonrpcpp/lib/jsonrp.o
ifeq ($(ENDIAN), BIG)

View file

@ -21,7 +21,7 @@
#include "common/log.h"
#include "common/utils.h"
#include "common/snapException.h"
#include "json/jsonrpc.h"
#include "jsonrp.hpp"
#include "config.h"
#include <iostream>

View file

@ -1 +0,0 @@
jsonrpctest

View file

@ -1,18 +0,0 @@
CXXFLAGS += $(ADD_CFLAGS) -std=c++0x -Wall -Wno-unused-function -O3 -I. -I../..
CXX = /usr/bin/g++
STRIP = strip
BIN = jsonrpctest
OBJ = jsonrpctest.o jsonrpc.o ../../common/log.o
all: $(OBJ)
$(CXX) $(CXXFLAGS) -o $(BIN) $(OBJ) $(LDFLAGS)
$(STRIP) $(BIN)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -rf $(BIN) $(OBJ) *~

View file

@ -1,168 +0,0 @@
/***
This file is part of snapcast
Copyright (C) 2014-2016 Johannes Pohl
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "jsonrpc.h"
#include "common/log.h"
using namespace std;
namespace jsonrpc
{
Request::Request() : method(""), id()
{
}
void Request::parse(const std::string& json)
{
// 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
{
try
{
json_ = Json::parse(json);
}
catch (const exception& e)
{
throw RequestException(e.what(), -32700);
}
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<string>();
if (jsonrpc != "2.0")
throw InvalidRequestException("invalid jsonrpc value: " + jsonrpc, id);
if (json_.count("method") == 0)
throw InvalidRequestException("method is missing", id);
method = json_["method"].get<string>();
if (method.empty())
throw InvalidRequestException("method must not be empty", id);
params.clear();
try
{
if (json_["params"] != nullptr)
{
Json p = json_["params"];
for (Json::iterator it = p.begin(); it != p.end(); ++it)
params[it.key()] = it.value();
}
}
catch (const exception& e)
{
throw InvalidParamsException(e.what(), id);
}
}
catch (const RequestException& e)
{
throw;
}
catch (const exception& e)
{
throw InternalErrorException(e.what(), id);
}
}
Json Request::getResponse(const Json& result)
{
Json response = {
{"jsonrpc", "2.0"},
{"id", id.to_json()},
{"result", result}
};
return response;
}
Json Request::getError(int code, const std::string& message)
{
Json response = {
{"jsonrpc", "2.0"},
{"error", {
{"code", code},
{"message", message}
}},
};
return response;
}
bool Request::hasParam(const std::string& key)
{
return (params.find(key) != params.end());
}
Json Request::getParam(const std::string& key)
{
if (!hasParam(key))
throw InvalidParamsException(id);
return params[key];
}
/*
bool JsonRequest::isParam(size_t idx, const std::string& param)
{
if (idx >= params.size())
throw InvalidParamsException(*this);
return (params[idx] == param);
}
*/
Json Notification::getJson(const std::string& method, const Json& data)
{
Json notification = {
{"jsonrpc", "2.0"},
{"method", method},
{"params", data}
};
return notification;
}
}

View file

@ -1,317 +0,0 @@
/***
This file is part of snapcast
Copyright (C) 2014-2016 Johannes Pohl
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef JSON_RPC_H
#define JSON_RPC_H
#include <string>
#include <vector>
#include "externals/json.hpp"
#include "common/snapException.h"
using Json = nlohmann::json;
namespace jsonrpc
{
class Entity
{
public:
Entity()
{
}
virtual ~Entity()
{
}
virtual Json to_json() const = 0;
};
struct Id
{
enum class value_t : uint8_t
{
null = 0,
string,
integer
};
Id() : type(value_t::null), int_id(0), string_id("")
{
}
Id(int id) : type(value_t::integer), int_id(id), string_id("")
{
}
Id(const std::string& id) : type(value_t::string), int_id(0), string_id(id)
{
}
Id(const Json& json_id) : type(value_t::null)
{
if (json_id.is_null())
{
type = value_t::null;
}
else if (json_id.is_number_integer())
{
int_id = json_id.get<int>();
type = value_t::integer;
}
else if (json_id.is_string())
{
string_id = json_id.get<std::string>();
type = value_t::string;
}
else
throw std::invalid_argument("id must be integer, string or null");
}
Json to_json() const
{
if (type == value_t::string)
return string_id;
else if (type == value_t::integer)
return int_id;
else
return nullptr;
}
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 Error : public Entity
{
public:
Error(int code, const std::string& message, const Json& data = nullptr) : Entity(), code(code), message(message), data(data)
{
}
Error(const std::string& message, const Json& data = nullptr) : Error(-32603, message, data)
{
}
virtual Json to_json() const
{
Json j = {
{"code", code},
{"message", message},
};
if (!data.is_null())
j["data"] = data;
return j;
}
int code;
std::string message;
Json data;
};
class RequestException : public SnapException
{
Error error_;
Id id_;
public:
RequestException(const char* text, int errorCode = 0, const Id& requestId = Id()) : SnapException(text), error_(text, errorCode), id_(requestId)
{
}
RequestException(const std::string& text, int errorCode = 0, const Id& requestId = Id()) : SnapException(text), error_(text, errorCode), id_(requestId)
{
}
RequestException(const RequestException& e) : SnapException(e.what()), error_(error()), id_(e.id_)
{
}
virtual Error error() const noexcept
{
return error_;
}
Json getResponse() const noexcept
{
Json response = {
{"jsonrpc", "2.0"},
{"error", error_.to_json()},
{"id", id_.to_json()}
};
return response;
}
};
// -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 InvalidRequestException : public RequestException
{
public:
InvalidRequestException(const Id& requestId = Id()) : RequestException("invalid request", -32600, requestId)
{
}
InvalidRequestException(const std::string& message, const Id& requestId = Id()) : RequestException(message, -32600, requestId)
{
}
};
class MethodNotFoundException : public RequestException
{
public:
MethodNotFoundException(const Id& requestId = Id()) : RequestException("method not found", -32601, requestId)
{
}
MethodNotFoundException(const std::string& message, const Id& requestId = Id()) : RequestException(message, -32601, requestId)
{
}
};
class InvalidParamsException : public RequestException
{
public:
InvalidParamsException(const Id& requestId = Id()) : RequestException("invalid params", -32602, requestId)
{
}
InvalidParamsException(const std::string& message, const Id& requestId = Id()) : RequestException(message, -32602, requestId)
{
}
};
class InternalErrorException : public RequestException
{
public:
InternalErrorException(const Id& requestId = Id()) : RequestException("internal error", -32603, requestId)
{
}
InternalErrorException(const std::string& message, const Id& requestId = Id()) : RequestException(message, -32603, requestId)
{
}
};
/*
class Response
{
public:
Response();
void parse(const std::string& json);
std::string result;
Error error;
Id id;
protected:
Json json_;
};
*/
/// 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:
Request();
void parse(const std::string& json);
std::string method;
std::map<std::string, Json> params;
Id id;
Json getResponse(const Json& result);
Json getError(int code, const std::string& message);
Json getParam(const std::string& key);
bool hasParam(const std::string& key);
template<typename T>
T getParam(const std::string& key, const T& lowerRange, const T& upperRange)
{
T value = getParam(key).get<T>();
if (value < lowerRange)
throw InvalidParamsException(key + " out of range", id);
else if (value > upperRange)
throw InvalidParamsException(key + " out of range", id);
return value;
}
protected:
Json json_;
};
class Notification
{
public:
static Json getJson(const std::string& method, const Json& data);
};
class Batch
{
};
}
#endif

View file

@ -1,69 +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 "jsonrpc.h"
//example taken from: http://www.jsonrpc.org/specification#examples
int main(int argc, char* argv[])
{
//rpc call with positional parameters:
jsonrpc::Parser::parse(R"({"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1})");
jsonrpc::Parser::parse(R"({"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2})");
//rpc call with named parameters:
jsonrpc::Parser::parse(R"({"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3})");
jsonrpc::Parser::parse(R"({"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4})");
//a Notification:
jsonrpc::Parser::parse(R"({"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]})");
jsonrpc::Parser::parse(R"({"jsonrpc": "2.0", "method": "foobar"})");
//rpc call of non-existent method:
jsonrpc::Parser::parse(R"({"jsonrpc": "2.0", "method": "foobar", "id": "1"})");
//rpc call with invalid JSON:
jsonrpc::Parser::parse(R"({"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz])");
//rpc call with invalid Request object:
jsonrpc::Parser::parse(R"({"jsonrpc": "2.0", "method": 1, "params": "bar"})");
//rpc call Batch, invalid JSON:
jsonrpc::Parser::parse(R"([
{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
{"jsonrpc": "2.0", "method"
])");
//rpc call with an empty Array:
jsonrpc::Parser::parse(R"([])");
//rpc call with an invalid Batch (but not empty):
jsonrpc::Parser::parse(R"([1])");
//rpc call with invalid Batch:
jsonrpc::Parser::parse(R"([1,2,3])");
//rpc call Batch:
jsonrpc::Parser::parse(R"([
{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
{"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"},
{"foo": "boo"},
{"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
{"jsonrpc": "2.0", "method": "get_data", "id": "9"}
])");
//rpc call Batch (all notifications):
jsonrpc::Parser::parse(R"([
{"jsonrpc": "2.0", "method": "notify_sum", "params": [1,2,4]},
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}
])");
}

View file

@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "json/jsonrpc.h"
#include "jsonrp.hpp"
#include "streamServer.h"
#include "message/time.h"
#include "message/hello.h"
@ -43,7 +43,7 @@ void StreamServer::onStateChanged(const PcmStream* pcmStream, const ReaderState&
{
logO << "onStateChanged (" << pcmStream->getName() << "): " << state << "\n";
// logO << pcmStream->toJson().dump(4);
json notification = jsonrpc::Notification::getJson("Stream.OnUpdate", pcmStream->toJson());
json notification = jsonrpcpp::Notification("Stream.OnUpdate", Json({"data", pcmStream->toJson()})).to_json();
controlServer_->send(notification.dump(), NULL);
}
@ -99,7 +99,7 @@ void StreamServer::onDisconnect(StreamSession* streamSession)
Config::instance().save();
if (controlServer_ != nullptr)
{
json notification = jsonrpc::Notification::getJson("Client.OnDisconnect", clientInfo->toJson());
json notification = jsonrpcpp::Notification("Client.OnDisconnect", Json({"data", clientInfo->toJson()})).to_json();
controlServer_->send(notification.dump());
}
}
@ -108,19 +108,20 @@ void StreamServer::onDisconnect(StreamSession* streamSession)
void StreamServer::onMessageReceived(ControlSession* controlSession, const std::string& message)
{
jsonrpc::Request request;
logO << "onMessageReceived: " << message << "\n";
jsonrpcpp::Request request;
try
{
request.parse(message);
request.parse(Json::parse(message));
logO << "method: " << request.method << ", " << "id: " << request.id << "\n";
json result;
if (request.method.find("Client.") == 0)
{
ClientInfoPtr clientInfo = Config::instance().getClientInfo(request.getParam("client").get<string>());
ClientInfoPtr clientInfo = Config::instance().getClientInfo(request.params.get("client"));
if (clientInfo == nullptr)
throw jsonrpc::InternalErrorException("Client not found", request.id);
throw jsonrpcpp::InternalErrorException("Client not found", request.id);
if (request.method == "Client.GetStatus")
{
@ -128,18 +129,23 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
}
else if (request.method == "Client.SetVolume")
{
clientInfo->config.volume.fromJson(request.getParam("volume"));
clientInfo->config.volume.fromJson(request.params.get("volume"));
}
else if (request.method == "Client.SetLatency")
{
clientInfo->config.latency = request.getParam<int>("latency", -10000, settings_.bufferMs);
int latency = request.params.get("latency");
if (latency < -10000)
latency = -10000;
else if (latency > settings_.bufferMs)
latency = settings_.bufferMs;
clientInfo->config.latency = latency; //, -10000, settings_.bufferMs);
}
else if (request.method == "Client.SetName")
{
clientInfo->config.name = request.getParam("name").get<string>();
clientInfo->config.name = request.params.get("name");
}
else
throw jsonrpc::MethodNotFoundException(request.id);
throw jsonrpcpp::MethodNotFoundException(request.id);
if (request.method.find("Client.Set") == 0)
@ -148,7 +154,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
result = {{"method", "Client.OnUpdate"}, {"params", clientInfo->toJson()}};
/// Update client
session_ptr session = getStreamSession(request.getParam("client").get<string>());
session_ptr session = getStreamSession(request.params.get("client"));
if (session != nullptr)
{
msg::ServerSettings serverSettings;
@ -160,16 +166,16 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
}
/// Notify others
json notification = jsonrpc::Notification::getJson("Client.OnUpdate", clientInfo->toJson());
json notification = jsonrpcpp::Notification("Client.OnUpdate", Json({"data", clientInfo->toJson()})).to_json();
logO << "Notification: " << notification.dump() << "\n";
controlServer_->send(notification.dump(), controlSession);
}
}
else if (request.method.find("Group.") == 0)
{
GroupPtr group = Config::instance().getGroup(request.getParam("group").get<string>());
GroupPtr group = Config::instance().getGroup(request.params.get("group"));
if (group == nullptr)
throw jsonrpc::InternalErrorException("Group not found", request.id);
throw jsonrpcpp::InternalErrorException("Group not found", request.id);
if (request.method == "Group.GetStatus")
{
@ -177,10 +183,10 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
}
else if (request.method == "Group.SetStream")
{
string streamId = request.getParam("id").get<string>();
string streamId = request.params.get("id");
PcmStreamPtr stream = streamManager_->getStream(streamId);
if (stream == nullptr)
throw jsonrpc::InternalErrorException("Stream not found", request.id);
throw jsonrpcpp::InternalErrorException("Stream not found", request.id);
group->streamId = streamId;
@ -199,14 +205,14 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
}
/// Notify others
json notification = jsonrpc::Notification::getJson("Group.OnUpdate", group->toJson());
json notification = jsonrpcpp::Notification("Group.OnUpdate", Json({"data", group->toJson()})).to_json();
logO << "Notification: " << notification.dump() << "\n";
controlServer_->send(notification.dump(), controlSession);
}
else if (request.method == "Group.SetClients")
{
vector<string> clients = request.getParam("clients").get<vector<string>>();
string groupId = request.getParam("group").get<string>();
vector<string> clients = request.params.get("clients");
string groupId = request.params.get("group");
GroupPtr group = Config::instance().getGroup(groupId);
/// Remove clients from group
@ -258,12 +264,12 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
result = {{"method", "Server.OnUpdate"}, {"params", serverJson}};
/// Notify others: since at least two groups are affected, send a complete server update
json notification = jsonrpc::Notification::getJson("Server.OnUpdate", serverJson);
json notification = jsonrpcpp::Notification("Server.OnUpdate", Json({"data", serverJson})).to_json();
logO << "Notification: " << notification.dump() << "\n";
controlServer_->send(notification.dump(), controlSession);
}
else
throw jsonrpc::MethodNotFoundException(request.id);
throw jsonrpcpp::MethodNotFoundException(request.id);
}
else if (request.method.find("Server.") == 0)
{
@ -273,9 +279,9 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
}
else if (request.method == "Server.DeleteClient")
{
ClientInfoPtr clientInfo = Config::instance().getClientInfo(request.getParam("client").get<string>());
ClientInfoPtr clientInfo = Config::instance().getClientInfo(request.params.get("client"));
if (clientInfo == nullptr)
throw jsonrpc::InternalErrorException("Client not found", request.id);
throw jsonrpcpp::InternalErrorException("Client not found", request.id);
Config::instance().remove(clientInfo);
@ -283,30 +289,31 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
result = {{"method", "Server.OnUpdate"}, {"params", serverJson}};
/// Notify others
json notification = jsonrpc::Notification::getJson("Server.OnUpdate", serverJson);
json notification = jsonrpcpp::Notification("Server.OnUpdate", Json({"data", serverJson})).to_json();
logO << "Notification: " << notification.dump() << "\n";
controlServer_->send(notification.dump(), controlSession);
}
else
throw jsonrpc::MethodNotFoundException(request.id);
throw jsonrpcpp::MethodNotFoundException(request.id);
}
else
throw jsonrpc::MethodNotFoundException(request.id);
throw jsonrpcpp::MethodNotFoundException(request.id);
Config::instance().save();
string responseJson = request.getResponse(result).dump();
string responseJson = jsonrpcpp::Response(request, result).to_json().dump();
logO << "Response: " << responseJson << "\n";
controlSession->send(responseJson);
}
catch (const jsonrpc::RequestException& e)
catch (const jsonrpcpp::RequestException& e)
{
// logE << "JsonRequestException: " << e.getResponse().dump() << ", message: " << message << "\n";
controlSession->send(e.getResponse().dump());
logE << "StreamServer::onMessageReceived JsonRequestException: " << e.to_json().dump() << ", message: " << message << "\n";
controlSession->send(e.to_json().dump());
}
catch (const exception& e)
{
jsonrpc::InternalErrorException jsonException(e.what(), request.id);
controlSession->send(jsonException.getResponse().dump());
logE << "StreamServer::onMessageReceived exception: " << e.what() << ", message: " << message << "\n";
jsonrpcpp::InternalErrorException jsonException(e.what(), request.id);
controlSession->send(jsonException.to_json().dump()); //jsonrpcpp::Response(jsonException).to_json().dump());
}
}
@ -392,12 +399,12 @@ void StreamServer::onMessageReceived(StreamSession* connection, const msg::BaseM
if (newGroup)
{
json serverJson = Config::instance().getServerStatus(streamManager_->toJson());
json notification = jsonrpc::Notification::getJson("Server.OnUpdate", serverJson);
json notification = jsonrpcpp::Notification("Server.OnUpdate", Json({"data", serverJson})).to_json();
controlServer_->send(notification.dump());
}
else
{
json notification = jsonrpc::Notification::getJson("Client.OnConnect", client->toJson());
json notification = jsonrpcpp::Notification("Client.OnConnect", Json({"data", client->toJson()})).to_json();
//logO << notification.dump(4) << "\n";
controlServer_->send(notification.dump());
}