Redesign jsonrpc. Add some dummy classes.

This commit is contained in:
badaix 2017-01-16 00:04:53 +01:00
parent f7e26b7d2d
commit 781f124f4d
5 changed files with 286 additions and 282 deletions

View file

@ -1,94 +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_REQUEST_ID_H
#define JSON_REQUEST_ID_H
#include <string>
#include <stdexcept>
#include "externals/json.hpp"
using Json = nlohmann::json;
struct req_id
{
enum class value_t : uint8_t
{
null = 0,
string,
integer
};
req_id() : type(value_t::null), int_id(0), string_id("")
{
}
req_id(int id) : type(value_t::integer), int_id(id), string_id("")
{
}
req_id(const std::string& id) : type(value_t::string), int_id(0), string_id(id)
{
}
req_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 toJson() 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 req_id &id)
{
out << id.toJson();
return out;
}
value_t type;
int int_id;
std::string string_id;
};
#endif

View file

@ -22,14 +22,15 @@
using namespace std; using namespace std;
namespace jsonrpc
{
Request::Request() : method(""), id()
JsonRequest::JsonRequest() : id(), method("")
{ {
} }
void JsonRequest::parse(const std::string& json) void Request::parse(const std::string& json)
{ {
// http://www.jsonrpc.org/specification // http://www.jsonrpc.org/specification
// code message meaning // code message meaning
@ -47,32 +48,32 @@ void JsonRequest::parse(const std::string& json)
} }
catch (const exception& e) catch (const exception& e)
{ {
throw JsonRequestException(e.what(), -32700); throw RequestException(e.what(), -32700);
} }
if (json_.count("id") == 0) if (json_.count("id") == 0)
throw JsonInvalidRequestException("id is missing"); throw InvalidRequestException("id is missing");
try try
{ {
id = req_id(json_["id"]); id = Id(json_["id"]);
} }
catch(const std::exception& e) catch(const std::exception& e)
{ {
throw JsonInvalidRequestException(e.what()); throw InvalidRequestException(e.what());
} }
if (json_.count("jsonrpc") == 0) if (json_.count("jsonrpc") == 0)
throw JsonInvalidRequestException("jsonrpc is missing", id); throw InvalidRequestException("jsonrpc is missing", id);
string jsonrpc = json_["jsonrpc"].get<string>(); string jsonrpc = json_["jsonrpc"].get<string>();
if (jsonrpc != "2.0") if (jsonrpc != "2.0")
throw JsonInvalidRequestException("invalid jsonrpc value: " + jsonrpc, id); throw InvalidRequestException("invalid jsonrpc value: " + jsonrpc, id);
if (json_.count("method") == 0) if (json_.count("method") == 0)
throw JsonInvalidRequestException("method is missing", id); throw InvalidRequestException("method is missing", id);
method = json_["method"].get<string>(); method = json_["method"].get<string>();
if (method.empty()) if (method.empty())
throw JsonInvalidRequestException("method must not be empty", id); throw InvalidRequestException("method must not be empty", id);
params.clear(); params.clear();
try try
@ -86,25 +87,25 @@ void JsonRequest::parse(const std::string& json)
} }
catch (const exception& e) catch (const exception& e)
{ {
throw JsonInvalidParamsException(e.what(), id); throw InvalidParamsException(e.what(), id);
} }
} }
catch (const JsonRequestException& e) catch (const RequestException& e)
{ {
throw; throw;
} }
catch (const exception& e) catch (const exception& e)
{ {
throw JsonInternalErrorException(e.what(), id); throw InternalErrorException(e.what(), id);
} }
} }
Json JsonRequest::getResponse(const Json& result) Json Request::getResponse(const Json& result)
{ {
Json response = { Json response = {
{"jsonrpc", "2.0"}, {"jsonrpc", "2.0"},
{"id", id.toJson()}, {"id", id.to_json()},
{"result", result} {"result", result}
}; };
@ -112,7 +113,7 @@ Json JsonRequest::getResponse(const Json& result)
} }
Json JsonRequest::getError(int code, const std::string& message) Json Request::getError(int code, const std::string& message)
{ {
Json response = { Json response = {
{"jsonrpc", "2.0"}, {"jsonrpc", "2.0"},
@ -127,16 +128,16 @@ Json JsonRequest::getError(int code, const std::string& message)
} }
bool JsonRequest::hasParam(const std::string& key) bool Request::hasParam(const std::string& key)
{ {
return (params.find(key) != params.end()); return (params.find(key) != params.end());
} }
Json JsonRequest::getParam(const std::string& key) Json Request::getParam(const std::string& key)
{ {
if (!hasParam(key)) if (!hasParam(key))
throw JsonInvalidParamsException(id); throw InvalidParamsException(id);
return params[key]; return params[key];
} }
@ -146,12 +147,12 @@ Json JsonRequest::getParam(const std::string& key)
bool JsonRequest::isParam(size_t idx, const std::string& param) bool JsonRequest::isParam(size_t idx, const std::string& param)
{ {
if (idx >= params.size()) if (idx >= params.size())
throw JsonInvalidParamsException(*this); throw InvalidParamsException(*this);
return (params[idx] == param); return (params[idx] == param);
} }
*/ */
Json JsonNotification::getJson(const std::string& method, Json data) Json Notification::getJson(const std::string& method, const Json& data)
{ {
Json notification = { Json notification = {
{"jsonrpc", "2.0"}, {"jsonrpc", "2.0"},
@ -162,3 +163,6 @@ Json JsonNotification::getJson(const std::string& method, Json data)
return notification; return notification;
} }
}

View file

@ -22,28 +22,251 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "externals/json.hpp" #include "externals/json.hpp"
#include "jsonrpcException.h" #include "common/snapException.h"
#include "jsonRequestId.h"
using Json = nlohmann::json; 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 /// JSON-RPC 2.0 request
/** /**
* Simple jsonrpc 2.0 parser with getters * Simple jsonrpc 2.0 parser with getters
* Currently no named parameters are supported, but only array parameters * Currently no named parameters are supported, but only array parameters
*/ */
class JsonRequest class Request
{ {
public: public:
JsonRequest(); Request();
void parse(const std::string& json); void parse(const std::string& json);
req_id id;
std::string method; std::string method;
std::map<std::string, Json> params; std::map<std::string, Json> params;
Id id;
Json getResponse(const Json& result); Json getResponse(const Json& result);
Json getError(int code, const std::string& message); Json getError(int code, const std::string& message);
@ -56,9 +279,9 @@ public:
{ {
T value = getParam(key).get<T>(); T value = getParam(key).get<T>();
if (value < lowerRange) if (value < lowerRange)
throw JsonInvalidParamsException(key + " out of range", id); throw InvalidParamsException(key + " out of range", id);
else if (value > upperRange) else if (value > upperRange)
throw JsonInvalidParamsException(key + " out of range", id); throw InvalidParamsException(key + " out of range", id);
return value; return value;
} }
@ -71,14 +294,23 @@ protected:
class JsonNotification class Notification
{ {
public: public:
static Json getJson(const std::string& method, Json data); static Json getJson(const std::string& method, const Json& data);
}; };
class Batch
{
};
}

View file

@ -1,138 +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_EXCEPTION_H
#define JSON_RPC_EXCEPTION_H
#include <string>
#include "externals/json.hpp"
#include "common/snapException.h"
#include "jsonRequestId.h"
using Json = nlohmann::json;
class JsonRequestException : public SnapException
{
int errorCode_;
req_id id_;
public:
JsonRequestException(const char* text, int errorCode = 0, const req_id& requestId = req_id()) : SnapException(text), errorCode_(errorCode), id_(requestId)
{
}
JsonRequestException(const std::string& text, int errorCode = 0, const req_id& requestId = req_id()) : SnapException(text), errorCode_(errorCode), id_(requestId)
{
}
// JsonRequestException(const JsonRequest& request, const std::string& text, int errorCode = 0) : SnapException(text), errorCode_(errorCode), id_(request.id)
// {
// }
JsonRequestException(const JsonRequestException& e) : SnapException(e.what()), errorCode_(e.errorCode()), id_(e.id_)
{
}
virtual int errorCode() const noexcept
{
return errorCode_;
}
Json getResponse() const noexcept
{
int errorCode = errorCode_;
if (errorCode == 0)
errorCode = -32603;
Json response = {
{"jsonrpc", "2.0"},
{"error", {
{"code", errorCode},
{"message", what()}
}},
{"id", id_.toJson()}
};
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 JsonInvalidRequestException : public JsonRequestException
{
public:
JsonInvalidRequestException(const req_id& requestId = req_id()) : JsonRequestException("invalid request", -32600, requestId)
{
}
JsonInvalidRequestException(const std::string& message, const req_id& requestId = req_id()) : JsonRequestException(message, -32600, requestId)
{
}
};
class JsonMethodNotFoundException : public JsonRequestException
{
public:
JsonMethodNotFoundException(const req_id& requestId = req_id()) : JsonRequestException("method not found", -32601, requestId)
{
}
JsonMethodNotFoundException(const std::string& message, const req_id& requestId = req_id()) : JsonRequestException(message, -32601, requestId)
{
}
};
class JsonInvalidParamsException : public JsonRequestException
{
public:
JsonInvalidParamsException(const req_id& requestId = req_id()) : JsonRequestException("invalid params", -32602, requestId)
{
}
JsonInvalidParamsException(const std::string& message, const req_id& requestId = req_id()) : JsonRequestException(message, -32602, requestId)
{
}
};
class JsonInternalErrorException : public JsonRequestException
{
public:
JsonInternalErrorException(const req_id& requestId = req_id()) : JsonRequestException("internal error", -32603, requestId)
{
}
JsonInternalErrorException(const std::string& message, const req_id& requestId = req_id()) : JsonRequestException(message, -32603, requestId)
{
}
};
#endif

View file

@ -43,7 +43,7 @@ void StreamServer::onStateChanged(const PcmStream* pcmStream, const ReaderState&
{ {
logO << "onStateChanged (" << pcmStream->getName() << "): " << state << "\n"; logO << "onStateChanged (" << pcmStream->getName() << "): " << state << "\n";
// logO << pcmStream->toJson().dump(4); // logO << pcmStream->toJson().dump(4);
json notification = JsonNotification::getJson("Stream.OnUpdate", pcmStream->toJson()); json notification = jsonrpc::Notification::getJson("Stream.OnUpdate", pcmStream->toJson());
controlServer_->send(notification.dump(), NULL); controlServer_->send(notification.dump(), NULL);
} }
@ -99,7 +99,7 @@ void StreamServer::onDisconnect(StreamSession* streamSession)
Config::instance().save(); Config::instance().save();
if (controlServer_ != nullptr) if (controlServer_ != nullptr)
{ {
json notification = JsonNotification::getJson("Client.OnDisconnect", clientInfo->toJson()); json notification = jsonrpc::Notification::getJson("Client.OnDisconnect", clientInfo->toJson());
controlServer_->send(notification.dump()); controlServer_->send(notification.dump());
} }
} }
@ -108,7 +108,7 @@ void StreamServer::onDisconnect(StreamSession* streamSession)
void StreamServer::onMessageReceived(ControlSession* controlSession, const std::string& message) void StreamServer::onMessageReceived(ControlSession* controlSession, const std::string& message)
{ {
JsonRequest request; jsonrpc::Request request;
try try
{ {
request.parse(message); request.parse(message);
@ -120,7 +120,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
{ {
ClientInfoPtr clientInfo = Config::instance().getClientInfo(request.getParam("client").get<string>()); ClientInfoPtr clientInfo = Config::instance().getClientInfo(request.getParam("client").get<string>());
if (clientInfo == nullptr) if (clientInfo == nullptr)
throw JsonInternalErrorException("Client not found", request.id); throw jsonrpc::InternalErrorException("Client not found", request.id);
if (request.method == "Client.GetStatus") if (request.method == "Client.GetStatus")
{ {
@ -139,7 +139,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
clientInfo->config.name = request.getParam("name").get<string>(); clientInfo->config.name = request.getParam("name").get<string>();
} }
else else
throw JsonMethodNotFoundException(request.id); throw jsonrpc::MethodNotFoundException(request.id);
if (request.method.find("Client.Set") == 0) if (request.method.find("Client.Set") == 0)
@ -160,7 +160,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
} }
/// Notify others /// Notify others
json notification = JsonNotification::getJson("Client.OnUpdate", clientInfo->toJson()); json notification = jsonrpc::Notification::getJson("Client.OnUpdate", clientInfo->toJson());
logO << "Notification: " << notification.dump() << "\n"; logO << "Notification: " << notification.dump() << "\n";
controlServer_->send(notification.dump(), controlSession); controlServer_->send(notification.dump(), controlSession);
} }
@ -169,7 +169,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
{ {
GroupPtr group = Config::instance().getGroup(request.getParam("group").get<string>()); GroupPtr group = Config::instance().getGroup(request.getParam("group").get<string>());
if (group == nullptr) if (group == nullptr)
throw JsonInternalErrorException("Group not found", request.id); throw jsonrpc::InternalErrorException("Group not found", request.id);
if (request.method == "Group.GetStatus") if (request.method == "Group.GetStatus")
{ {
@ -180,7 +180,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
string streamId = request.getParam("id").get<string>(); string streamId = request.getParam("id").get<string>();
PcmStreamPtr stream = streamManager_->getStream(streamId); PcmStreamPtr stream = streamManager_->getStream(streamId);
if (stream == nullptr) if (stream == nullptr)
throw JsonInternalErrorException("Stream not found", request.id); throw jsonrpc::InternalErrorException("Stream not found", request.id);
group->streamId = streamId; group->streamId = streamId;
@ -199,7 +199,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
} }
/// Notify others /// Notify others
json notification = JsonNotification::getJson("Group.OnUpdate", group->toJson()); json notification = jsonrpc::Notification::getJson("Group.OnUpdate", group->toJson());
logO << "Notification: " << notification.dump() << "\n"; logO << "Notification: " << notification.dump() << "\n";
controlServer_->send(notification.dump(), controlSession); controlServer_->send(notification.dump(), controlSession);
} }
@ -258,12 +258,12 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
result = {{"method", "Server.OnUpdate"}, {"params", serverJson}}; result = {{"method", "Server.OnUpdate"}, {"params", serverJson}};
/// Notify others: since at least two groups are affected, send a complete server update /// Notify others: since at least two groups are affected, send a complete server update
json notification = JsonNotification::getJson("Server.OnUpdate", serverJson); json notification = jsonrpc::Notification::getJson("Server.OnUpdate", serverJson);
logO << "Notification: " << notification.dump() << "\n"; logO << "Notification: " << notification.dump() << "\n";
controlServer_->send(notification.dump(), controlSession); controlServer_->send(notification.dump(), controlSession);
} }
else else
throw JsonMethodNotFoundException(request.id); throw jsonrpc::MethodNotFoundException(request.id);
} }
else if (request.method.find("Server.") == 0) else if (request.method.find("Server.") == 0)
{ {
@ -275,7 +275,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
{ {
ClientInfoPtr clientInfo = Config::instance().getClientInfo(request.getParam("client").get<string>()); ClientInfoPtr clientInfo = Config::instance().getClientInfo(request.getParam("client").get<string>());
if (clientInfo == nullptr) if (clientInfo == nullptr)
throw JsonInternalErrorException("Client not found", request.id); throw jsonrpc::InternalErrorException("Client not found", request.id);
Config::instance().remove(clientInfo); Config::instance().remove(clientInfo);
@ -283,29 +283,29 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
result = {{"method", "Server.OnUpdate"}, {"params", serverJson}}; result = {{"method", "Server.OnUpdate"}, {"params", serverJson}};
/// Notify others /// Notify others
json notification = JsonNotification::getJson("Server.OnUpdate", serverJson); json notification = jsonrpc::Notification::getJson("Server.OnUpdate", serverJson);
logO << "Notification: " << notification.dump() << "\n"; logO << "Notification: " << notification.dump() << "\n";
controlServer_->send(notification.dump(), controlSession); controlServer_->send(notification.dump(), controlSession);
} }
else else
throw JsonMethodNotFoundException(request.id); throw jsonrpc::MethodNotFoundException(request.id);
} }
else else
throw JsonMethodNotFoundException(request.id); throw jsonrpc::MethodNotFoundException(request.id);
Config::instance().save(); Config::instance().save();
string responseJson = request.getResponse(result).dump(); string responseJson = request.getResponse(result).dump();
logO << "Response: " << responseJson << "\n"; logO << "Response: " << responseJson << "\n";
controlSession->send(responseJson); controlSession->send(responseJson);
} }
catch (const JsonRequestException& e) catch (const jsonrpc::RequestException& e)
{ {
// logE << "JsonRequestException: " << e.getResponse().dump() << ", message: " << message << "\n"; // logE << "JsonRequestException: " << e.getResponse().dump() << ", message: " << message << "\n";
controlSession->send(e.getResponse().dump()); controlSession->send(e.getResponse().dump());
} }
catch (const exception& e) catch (const exception& e)
{ {
JsonInternalErrorException jsonException(e.what(), request.id); jsonrpc::InternalErrorException jsonException(e.what(), request.id);
controlSession->send(jsonException.getResponse().dump()); controlSession->send(jsonException.getResponse().dump());
} }
} }
@ -392,12 +392,12 @@ void StreamServer::onMessageReceived(StreamSession* connection, const msg::BaseM
if (newGroup) if (newGroup)
{ {
json serverJson = Config::instance().getServerStatus(streamManager_->toJson()); json serverJson = Config::instance().getServerStatus(streamManager_->toJson());
json notification = JsonNotification::getJson("Server.OnUpdate", serverJson); json notification = jsonrpc::Notification::getJson("Server.OnUpdate", serverJson);
controlServer_->send(notification.dump()); controlServer_->send(notification.dump());
} }
else else
{ {
json notification = JsonNotification::getJson("Client.OnConnect", client->toJson()); json notification = jsonrpc::Notification::getJson("Client.OnConnect", client->toJson());
//logO << notification.dump(4) << "\n"; //logO << notification.dump(4) << "\n";
controlServer_->send(notification.dump()); controlServer_->send(notification.dump());
} }