diff --git a/server/json/jsonRequestId.h b/server/json/jsonRequestId.h deleted file mode 100644 index 7f177b49..00000000 --- a/server/json/jsonRequestId.h +++ /dev/null @@ -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 . -***/ - -#ifndef JSON_REQUEST_ID_H -#define JSON_REQUEST_ID_H - -#include -#include -#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(); - type = value_t::integer; - } - else if (json_id.is_string()) - { - string_id = json_id.get(); - 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 - diff --git a/server/json/jsonrpc.cpp b/server/json/jsonrpc.cpp index b21e2d41..b5ac19f0 100644 --- a/server/json/jsonrpc.cpp +++ b/server/json/jsonrpc.cpp @@ -22,14 +22,15 @@ using namespace std; +namespace jsonrpc +{ - -JsonRequest::JsonRequest() : id(), method("") +Request::Request() : method(""), id() { } -void JsonRequest::parse(const std::string& json) +void Request::parse(const std::string& json) { // http://www.jsonrpc.org/specification // code message meaning @@ -47,32 +48,32 @@ void JsonRequest::parse(const std::string& json) } catch (const exception& e) { - throw JsonRequestException(e.what(), -32700); + throw RequestException(e.what(), -32700); } if (json_.count("id") == 0) - throw JsonInvalidRequestException("id is missing"); + throw InvalidRequestException("id is missing"); try { - id = req_id(json_["id"]); + id = Id(json_["id"]); } catch(const std::exception& e) { - throw JsonInvalidRequestException(e.what()); + throw InvalidRequestException(e.what()); } if (json_.count("jsonrpc") == 0) - throw JsonInvalidRequestException("jsonrpc is missing", id); + throw InvalidRequestException("jsonrpc is missing", id); string jsonrpc = json_["jsonrpc"].get(); if (jsonrpc != "2.0") - throw JsonInvalidRequestException("invalid jsonrpc value: " + jsonrpc, id); + throw InvalidRequestException("invalid jsonrpc value: " + jsonrpc, id); if (json_.count("method") == 0) - throw JsonInvalidRequestException("method is missing", id); + throw InvalidRequestException("method is missing", id); method = json_["method"].get(); if (method.empty()) - throw JsonInvalidRequestException("method must not be empty", id); + throw InvalidRequestException("method must not be empty", id); params.clear(); try @@ -86,25 +87,25 @@ void JsonRequest::parse(const std::string& json) } catch (const exception& e) { - throw JsonInvalidParamsException(e.what(), id); + throw InvalidParamsException(e.what(), id); } } - catch (const JsonRequestException& e) + catch (const RequestException& e) { throw; } 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 = { {"jsonrpc", "2.0"}, - {"id", id.toJson()}, + {"id", id.to_json()}, {"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 = { {"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()); } -Json JsonRequest::getParam(const std::string& key) +Json Request::getParam(const std::string& key) { if (!hasParam(key)) - throw JsonInvalidParamsException(id); + throw InvalidParamsException(id); return params[key]; } @@ -146,12 +147,12 @@ Json JsonRequest::getParam(const std::string& key) bool JsonRequest::isParam(size_t idx, const std::string& param) { if (idx >= params.size()) - throw JsonInvalidParamsException(*this); + throw InvalidParamsException(*this); 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 = { {"jsonrpc", "2.0"}, @@ -162,3 +163,6 @@ Json JsonNotification::getJson(const std::string& method, Json data) return notification; } +} + + diff --git a/server/json/jsonrpc.h b/server/json/jsonrpc.h index d27df1a8..a83d4332 100644 --- a/server/json/jsonrpc.h +++ b/server/json/jsonrpc.h @@ -22,28 +22,251 @@ #include #include #include "externals/json.hpp" -#include "jsonrpcException.h" -#include "jsonRequestId.h" +#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(); + type = value_t::integer; + } + else if (json_id.is_string()) + { + string_id = json_id.get(); + 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 JsonRequest +class Request { public: - JsonRequest(); + Request(); void parse(const std::string& json); - req_id id; std::string method; std::map params; + Id id; Json getResponse(const Json& result); Json getError(int code, const std::string& message); @@ -56,9 +279,9 @@ public: { T value = getParam(key).get(); if (value < lowerRange) - throw JsonInvalidParamsException(key + " out of range", id); + throw InvalidParamsException(key + " out of range", id); else if (value > upperRange) - throw JsonInvalidParamsException(key + " out of range", id); + throw InvalidParamsException(key + " out of range", id); return value; } @@ -71,14 +294,23 @@ protected: -class JsonNotification +class Notification { public: - static Json getJson(const std::string& method, Json data); + static Json getJson(const std::string& method, const Json& data); }; +class Batch +{ + +}; + + +} + + diff --git a/server/json/jsonrpcException.h b/server/json/jsonrpcException.h deleted file mode 100644 index 3d26d0be..00000000 --- a/server/json/jsonrpcException.h +++ /dev/null @@ -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 . -***/ - -#ifndef JSON_RPC_EXCEPTION_H -#define JSON_RPC_EXCEPTION_H - -#include -#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 - diff --git a/server/streamServer.cpp b/server/streamServer.cpp index 9513d2b3..d61e479a 100644 --- a/server/streamServer.cpp +++ b/server/streamServer.cpp @@ -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 = JsonNotification::getJson("Stream.OnUpdate", pcmStream->toJson()); + json notification = jsonrpc::Notification::getJson("Stream.OnUpdate", pcmStream->toJson()); controlServer_->send(notification.dump(), NULL); } @@ -99,7 +99,7 @@ void StreamServer::onDisconnect(StreamSession* streamSession) Config::instance().save(); if (controlServer_ != nullptr) { - json notification = JsonNotification::getJson("Client.OnDisconnect", clientInfo->toJson()); + json notification = jsonrpc::Notification::getJson("Client.OnDisconnect", clientInfo->toJson()); controlServer_->send(notification.dump()); } } @@ -108,7 +108,7 @@ void StreamServer::onDisconnect(StreamSession* streamSession) void StreamServer::onMessageReceived(ControlSession* controlSession, const std::string& message) { - JsonRequest request; + jsonrpc::Request request; try { request.parse(message); @@ -120,7 +120,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std:: { ClientInfoPtr clientInfo = Config::instance().getClientInfo(request.getParam("client").get()); if (clientInfo == nullptr) - throw JsonInternalErrorException("Client not found", request.id); + throw jsonrpc::InternalErrorException("Client not found", request.id); if (request.method == "Client.GetStatus") { @@ -139,7 +139,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std:: clientInfo->config.name = request.getParam("name").get(); } else - throw JsonMethodNotFoundException(request.id); + throw jsonrpc::MethodNotFoundException(request.id); if (request.method.find("Client.Set") == 0) @@ -160,7 +160,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std:: } /// Notify others - json notification = JsonNotification::getJson("Client.OnUpdate", clientInfo->toJson()); + json notification = jsonrpc::Notification::getJson("Client.OnUpdate", clientInfo->toJson()); logO << "Notification: " << notification.dump() << "\n"; 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()); if (group == nullptr) - throw JsonInternalErrorException("Group not found", request.id); + throw jsonrpc::InternalErrorException("Group not found", request.id); if (request.method == "Group.GetStatus") { @@ -180,7 +180,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std:: string streamId = request.getParam("id").get(); PcmStreamPtr stream = streamManager_->getStream(streamId); if (stream == nullptr) - throw JsonInternalErrorException("Stream not found", request.id); + throw jsonrpc::InternalErrorException("Stream not found", request.id); group->streamId = streamId; @@ -199,7 +199,7 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std:: } /// Notify others - json notification = JsonNotification::getJson("Group.OnUpdate", group->toJson()); + json notification = jsonrpc::Notification::getJson("Group.OnUpdate", group->toJson()); logO << "Notification: " << notification.dump() << "\n"; controlServer_->send(notification.dump(), controlSession); } @@ -258,12 +258,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 = JsonNotification::getJson("Server.OnUpdate", serverJson); + json notification = jsonrpc::Notification::getJson("Server.OnUpdate", serverJson); logO << "Notification: " << notification.dump() << "\n"; controlServer_->send(notification.dump(), controlSession); } else - throw JsonMethodNotFoundException(request.id); + throw jsonrpc::MethodNotFoundException(request.id); } 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()); if (clientInfo == nullptr) - throw JsonInternalErrorException("Client not found", request.id); + throw jsonrpc::InternalErrorException("Client not found", request.id); Config::instance().remove(clientInfo); @@ -283,29 +283,29 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std:: result = {{"method", "Server.OnUpdate"}, {"params", serverJson}}; /// Notify others - json notification = JsonNotification::getJson("Server.OnUpdate", serverJson); + json notification = jsonrpc::Notification::getJson("Server.OnUpdate", serverJson); logO << "Notification: " << notification.dump() << "\n"; controlServer_->send(notification.dump(), controlSession); } else - throw JsonMethodNotFoundException(request.id); + throw jsonrpc::MethodNotFoundException(request.id); } else - throw JsonMethodNotFoundException(request.id); + throw jsonrpc::MethodNotFoundException(request.id); Config::instance().save(); string responseJson = request.getResponse(result).dump(); logO << "Response: " << responseJson << "\n"; controlSession->send(responseJson); } - catch (const JsonRequestException& e) + catch (const jsonrpc::RequestException& e) { // logE << "JsonRequestException: " << e.getResponse().dump() << ", message: " << message << "\n"; controlSession->send(e.getResponse().dump()); } catch (const exception& e) { - JsonInternalErrorException jsonException(e.what(), request.id); + jsonrpc::InternalErrorException jsonException(e.what(), request.id); controlSession->send(jsonException.getResponse().dump()); } } @@ -392,12 +392,12 @@ void StreamServer::onMessageReceived(StreamSession* connection, const msg::BaseM if (newGroup) { 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()); } else { - json notification = JsonNotification::getJson("Client.OnConnect", client->toJson()); + json notification = jsonrpc::Notification::getJson("Client.OnConnect", client->toJson()); //logO << notification.dump(4) << "\n"; controlServer_->send(notification.dump()); }