diff --git a/src/WebSocketServer.cpp b/src/WebSocketServer.cpp index 585c7757..2a4d6dfc 100644 --- a/src/WebSocketServer.cpp +++ b/src/WebSocketServer.cpp @@ -239,6 +239,8 @@ void WebSocketServer::BroadcastEvent(uint64_t requiredIntent, std::string eventT it.second->IncrementOutgoingMessages(); break; } + if (errorCode) + blog(LOG_ERROR, "[WebSocketServer::BroadcastEvent] Error sending event message: %s", errorCode.message().c_str()); } } lock.unlock(); diff --git a/src/requesthandler/RequestHandler.cpp b/src/requesthandler/RequestHandler.cpp index a2a2b535..2e2dbcbc 100644 --- a/src/requesthandler/RequestHandler.cpp +++ b/src/requesthandler/RequestHandler.cpp @@ -4,7 +4,9 @@ const std::map RequestHandler::_handlerMap { + // General {"GetVersion", &RequestHandler::GetVersion}, + {"BroadcastCustomEvent", &RequestHandler::BroadcastCustomEvent}, }; RequestResult RequestHandler::ProcessRequest(const Request& request) diff --git a/src/requesthandler/RequestHandler.h b/src/requesthandler/RequestHandler.h index 7024cdc9..53b60f41 100644 --- a/src/requesthandler/RequestHandler.h +++ b/src/requesthandler/RequestHandler.h @@ -15,6 +15,7 @@ class RequestHandler { private: RequestResult GetVersion(const Request&); + RequestResult BroadcastCustomEvent(const Request&); static const std::map _handlerMap; }; diff --git a/src/requesthandler/RequestHandler_General.cpp b/src/requesthandler/RequestHandler_General.cpp index a0159f87..66f7387b 100644 --- a/src/requesthandler/RequestHandler_General.cpp +++ b/src/requesthandler/RequestHandler_General.cpp @@ -1,9 +1,28 @@ +#include "../obs-websocket.h" +#include "../WebSocketServer.h" + #include "RequestHandler.h" #include "../plugin-macros.generated.h" RequestResult RequestHandler::GetVersion(const Request& request) { - json ret{{"test", "pp"}}; - return RequestResult::Success(ret); + return RequestResult::Success(); +} + +RequestResult RequestHandler::BroadcastCustomEvent(const Request& request) +{ + RequestStatus::RequestStatus statusCode; + std::string comment; + if (!request.ValidateObject("eventData", statusCode, comment)) { + return RequestResult::Error(statusCode, comment); + } + + auto webSocketServer = GetWebSocketServer(); + if (!webSocketServer) + return RequestResult::Error(RequestStatus::RequestProcessingFailed, "Unable to send event."); + + webSocketServer->BroadcastEvent((1 << 0), "CustomEvent", request.RequestData["eventData"]); + + return RequestResult::Success(); } diff --git a/src/requesthandler/rpc/Request.cpp b/src/requesthandler/rpc/Request.cpp index 28948d17..710dcf34 100644 --- a/src/requesthandler/rpc/Request.cpp +++ b/src/requesthandler/rpc/Request.cpp @@ -2,13 +2,140 @@ #include "../../plugin-macros.generated.h" +json GetDefaultJsonObject(json requestData) +{ + if (!requestData.is_object()) + return json::object(); + else + return requestData; +} + Request::Request(uint8_t rpcVersion, bool ignoreNonFatalRequestChecks, std::string requestType, json requestData) : RpcVersion(rpcVersion), IgnoreNonFatalRequestChecks(ignoreNonFatalRequestChecks), + RequestData(GetDefaultJsonObject(requestData)), RequestType(requestType) { - if (!requestData.is_object()) - RequestData = json::object(); - else - RequestData = requestData; -} \ No newline at end of file +} + +const bool Request::ValidateBasic(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const +{ + if (!HasRequestData()) { + statusCode = RequestStatus::MissingRequestData; + comment = "Your request data is missing or invalid (non-object)"; + return false; + } + + if (!RequestData.contains(keyName)) { + statusCode = RequestStatus::MissingRequestParameter; + comment = std::string("Your request is missing the `") + keyName + "` parameter."; + return false; + } + + return true; +} + +const bool Request::ValidateNumber(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue, const double maxValue) const +{ + if (!ValidateBasic(keyName, statusCode, comment)) { + return false; + } + + if (!RequestData[keyName].is_number()) { + statusCode = RequestStatus::InvalidRequestParameterDataType; + comment = std::string("The parameter `") + keyName + "` must be a number."; + return false; + } + + double value = RequestData[keyName]; + if (value < minValue) { + statusCode = RequestStatus::RequestParameterOutOfRange; + comment = std::string("The parameter `") + keyName + "` is below the minimum of `" + std::to_string(minValue) + "`"; + return false; + } + if (value > maxValue) { + statusCode = RequestStatus::RequestParameterOutOfRange; + comment = std::string("The parameter `") + keyName + "` is above the maximum of `" + std::to_string(maxValue) + "`"; + return false; + } + + return true; +} + +const bool Request::ValidateString(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const +{ + if (!ValidateBasic(keyName, statusCode, comment)) { + return false; + } + + if (!RequestData[keyName].is_string()) { + statusCode = RequestStatus::InvalidRequestParameterDataType; + comment = std::string("The parameter `") + keyName + "` must be a string."; + return false; + } + + if (RequestData[keyName].get().empty() && !allowEmpty) { + statusCode = RequestStatus::RequestParameterEmpty; + comment = std::string("The parameter `") + keyName + "` must not be empty."; + return false; + } + + return true; +} + +const bool Request::ValidateBoolean(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const +{ + if (!ValidateBasic(keyName, statusCode, comment)) { + return false; + } + + if (!RequestData[keyName].is_boolean()) { + statusCode = RequestStatus::InvalidRequestParameterDataType; + comment = std::string("The parameter `") + keyName + "` must be boolean."; + return false; + } + + return true; +} + +const bool Request::ValidateObject(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const +{ + if (!ValidateBasic(keyName, statusCode, comment)) { + return false; + } + + if (!RequestData[keyName].is_object()) { + statusCode = RequestStatus::InvalidRequestParameterDataType; + comment = std::string("The parameter `") + keyName + "` must be an object."; + return false; + } + + if (RequestData[keyName].empty() && !allowEmpty) { + statusCode = RequestStatus::RequestParameterEmpty; + comment = std::string("The parameter `") + keyName + "` must not be empty."; + return false; + } + + return true; +} + +const bool Request::ValidateArray(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty) const +{ + if (!ValidateBasic(keyName, statusCode, comment)) { + return false; + } + + if (!RequestData[keyName].is_array()) { + statusCode = RequestStatus::InvalidRequestParameterDataType; + comment = std::string("The parameter `") + keyName + "` must be an array."; + return false; + } + + if (RequestData[keyName].empty() && !allowEmpty) { + statusCode = RequestStatus::RequestParameterEmpty; + comment = std::string("The parameter `") + keyName + "` must not be empty."; + return false; + } + + return true; +} diff --git a/src/requesthandler/rpc/Request.h b/src/requesthandler/rpc/Request.h index 650a8bb3..cb9b12d4 100644 --- a/src/requesthandler/rpc/Request.h +++ b/src/requesthandler/rpc/Request.h @@ -1,13 +1,28 @@ #pragma once +#include + +#include "RequestStatus.h" #include "../../utils/Utils.h" struct Request { - Request(uint8_t rpcVersion, bool ignoreNonFatalRequestChecks, std::string requestType, json requestData = nullptr); + Request(const uint8_t rpcVersion, const bool ignoreNonFatalRequestChecks, const std::string requestType, const json requestData = nullptr); - uint8_t RpcVersion; - bool IgnoreNonFatalRequestChecks; - std::string RequestType; - json RequestData; + const bool HasRequestData() const + { + return RequestData.is_object(); + } + + const bool ValidateBasic(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const; + const bool ValidateNumber(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const double minValue = -INFINITY, const double maxValue = INFINITY) const; + const bool ValidateString(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const; + const bool ValidateBoolean(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment) const; + const bool ValidateObject(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const; + const bool ValidateArray(const std::string keyName, RequestStatus::RequestStatus &statusCode, std::string &comment, const bool allowEmpty = false) const; + + const uint8_t RpcVersion; + const bool IgnoreNonFatalRequestChecks; + const std::string RequestType; + const json RequestData; }; \ No newline at end of file diff --git a/src/requesthandler/rpc/RequestStatus.h b/src/requesthandler/rpc/RequestStatus.h index f1a7a61d..f30902c8 100644 --- a/src/requesthandler/rpc/RequestStatus.h +++ b/src/requesthandler/rpc/RequestStatus.h @@ -16,6 +16,8 @@ namespace RequestStatus { // A required request parameter is missing MissingRequestParameter = 300, + // The request does not have a valid requestData object. + MissingRequestData = 301, // Generic invalid request parameter message InvalidRequestParameter = 400,