Added basic rest framework

This commit is contained in:
Leon Etienne (ubuntu wsl) 2020-09-24 14:31:30 +02:00
parent 918b735922
commit 595e04bc32
20 changed files with 429 additions and 318 deletions

View File

@ -1,6 +1,7 @@
#include "Framework.h" #include "Framework.h"
using namespace Logging; using namespace Logging;
using namespace Rest;
Framework::Framework() Framework::Framework()
{ {
@ -13,7 +14,7 @@ Framework::Framework()
PostInit(); PostInit();
isRunning = true; XGControl::keepServerRunning = true;
Run(); Run();
return; return;
@ -32,7 +33,7 @@ Framework::~Framework()
void Framework::Run() void Framework::Run()
{ {
while (isRunning) while (XGControl::keepServerRunning)
{ {
restInterface->Update(); restInterface->Update();
} }
@ -48,6 +49,7 @@ void Framework::Run()
void Framework::PreInit() void Framework::PreInit()
{ {
LogHistory::PreInit(); LogHistory::PreInit();
RestQueryHandler::PreInit();
return; return;
} }
@ -70,6 +72,7 @@ void Framework::OnExit()
void Framework::PostExit() void Framework::PostExit()
{ {
LogHistory::PostExit(); LogHistory::PostExit();
RestQueryHandler::PostExit();
return; return;
} }

View File

@ -2,6 +2,7 @@
#include "Logger.h" #include "Logger.h"
#include "LogHistory.h" #include "LogHistory.h"
#include "RestInterface.h" #include "RestInterface.h"
#include "XGControl.h"
class Framework class Framework
{ {
@ -9,18 +10,17 @@ public:
Framework(); Framework();
~Framework(); ~Framework();
void Run();
private: private:
void Run();
void PostInit(); void PostInit();
void OnExit(); void OnExit();
void PreInit(); void PreInit();
void PostExit(); void PostExit();
RestInterface* restInterface; Rest::RestInterface* restInterface;
Log* log; Logging::Logger* log;
bool isRunning = true; bool isRunning = true;
}; };

View File

@ -948,6 +948,7 @@ void JsonArray::Parse(const std::string jsonCode)
bool areWeInString = false; bool areWeInString = false;
bool areWeInCode = false; bool areWeInCode = false;
bool isCharEscaped = false; bool isCharEscaped = false;
bool areWeBetweenCommaAndValue = false; // Has the parser found a comma, but is still looking for the next value?
for (std::size_t i = 0; i < minifiedCode.length(); i++) for (std::size_t i = 0; i < minifiedCode.length(); i++)
{ {
@ -957,13 +958,25 @@ void JsonArray::Parse(const std::string jsonCode)
{ {
start = i; start = i;
areWeInCode = true; areWeInCode = true;
areWeBetweenCommaAndValue = true;
} }
else if ((!areWeInString) && (areWeInCode) && (arrayBracketLevel == 1) && (curlyBracketLevel == 0) && ((c == ',') || (i == minifiedCode.length() - 1))) else if ((!areWeInString) && (areWeInCode) && (arrayBracketLevel == 1) && (curlyBracketLevel == 0) && ((c == ',') || (i == minifiedCode.length() - 1)))
{ {
if (c != ',') areWeBetweenCommaAndValue = false;
end = i; end = i;
areWeInCode = false; areWeInCode = false;
dataJsonSnippets.push_back(minifiedCode.substr(start, end - start));
std::string codeSnippet = minifiedCode.substr(start, end - start);
if (codeSnippet.length() > 0) // A json data snippet can't be of size 0!
{
dataJsonSnippets.push_back(codeSnippet);
}
else
{
// JsonElement too short to be valid.
throw JsonParsingGeneralException(std::string("Fucked up a comma around position ") + Jstring((long long int)start));
}
} }
if ((!areWeInString) && (c == ']') && (arrayBracketLevel == 1) && (i != minifiedCode.length() - 1)) throw JsonParsingExpectedEOFException(minifiedCode.substr(i, minifiedCode.length() - i)); if ((!areWeInString) && (c == ']') && (arrayBracketLevel == 1) && (i != minifiedCode.length() - 1)) throw JsonParsingExpectedEOFException(minifiedCode.substr(i, minifiedCode.length() - i));
@ -984,6 +997,7 @@ void JsonArray::Parse(const std::string jsonCode)
// Someone fucked up his json code // Someone fucked up his json code
if (arrayBracketLevel != 0) throw JsonParsingMissingBracketsException(); if (arrayBracketLevel != 0) throw JsonParsingMissingBracketsException();
if (curlyBracketLevel != 0) throw JsonParsingMissingBracketsException(); if (curlyBracketLevel != 0) throw JsonParsingMissingBracketsException();
if (areWeBetweenCommaAndValue) throw JsonParsingGeneralException("Unexpected EOF. Don't put a comma after the last value of a json array!");
if (areWeInString) throw JsonParsingMissingQuotesException(); if (areWeInString) throw JsonParsingMissingQuotesException();
} }
@ -1090,7 +1104,7 @@ std::string StringHelpers::Replace(const std::string str, const std::string find
return ss.str(); return ss.str();
} }
std::string StringHelpers::Escape(std::string str) std::string StringHelpers::Escape(const std::string str)
{ {
std::stringstream ss; std::stringstream ss;
@ -1120,7 +1134,6 @@ std::string StringHelpers::Escape(std::string str)
ss << "\\\\"; ss << "\\\\";
break; break;
default: default:
if (str[i] < 0) ss << EscapeUTF8(str[i]); if (str[i] < 0) ss << EscapeUTF8(str[i]);
else ss << str[i]; else ss << str[i];
} }
@ -1671,6 +1684,11 @@ double JsonData::GetFloatPrecision() const
return customFloatPrecision; return customFloatPrecision;
} }
bool JsonData::IsOfNumericType() const
{
return (dataType == JSON_DATA_TYPE::INT) || (dataType == JSON_DATA_TYPE::FLOAT);
}
bool JsonData::GetBoolData() const bool JsonData::GetBoolData() const
{ {
JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::BOOL; JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::BOOL;
@ -2963,22 +2981,43 @@ void JsonBlock::Parse(const std::string jsonCode)
bool areWeInString = false; bool areWeInString = false;
bool areWeInCode = false; bool areWeInCode = false;
bool isCharEscaped = false; bool isCharEscaped = false;
bool areWeBetweenCommaAndLabel = false; // Has the parser found a comma, but is still looking for the next label?
for (std::size_t i = 0; i < minifiedCode.length(); i++) for (std::size_t i = 0; i < minifiedCode.length(); i++)
{ {
const char c = minifiedCode[i]; const char c = minifiedCode[i];
if ((!areWeInString) && (c == '\"') && (arrayBracketLevel == 0) && (curlyBracketLevel == 1) && (!areWeInCode)) if ((!areWeInString) && (arrayBracketLevel == 0) && (curlyBracketLevel == 1) && (!areWeInCode))
{ {
start = i; if (c == '\"')
areWeInCode = true; {
start = i;
areWeInCode = true;
areWeBetweenCommaAndLabel = false;
}
else if (c == ',')
{
// Did not find a label between two commas!
throw JsonParsingGeneralException(std::string("Fucked up a comma around position ") + Jstring((long long int)i));
}
} }
else if ((areWeInCode) && (arrayBracketLevel == 0) && (curlyBracketLevel == 1) && (!areWeInString) && ((c == ',') || (c == '}'))) else if ((areWeInCode) && (arrayBracketLevel == 0) && (curlyBracketLevel == 1) && (!areWeInString) && ((c == ',') || (c == '}')))
{ {
end = i; end = i;
areWeInCode = false; areWeInCode = false;
elementCodeSnippets.push_back(minifiedCode.substr(start, end - start)); if (c == ',') areWeBetweenCommaAndLabel = true;
std::string codeSnippet = minifiedCode.substr(start, end - start);
if (codeSnippet.length() >= 4) // Minimum length for valid JsonElement code. "":0
{
elementCodeSnippets.push_back(codeSnippet);
}
else
{
// JsonElement too short to be valid.
throw JsonParsingGeneralException(std::string("Fucked up a comma around position ") + Jstring((long long int)start));
}
} }
// Our } at level 1 is not the last char? It should be! EXPECTED_END_OF_FILE_EXCEPTION :) // Our } at level 1 is not the last char? It should be! EXPECTED_END_OF_FILE_EXCEPTION :)
@ -2987,7 +3026,7 @@ void JsonBlock::Parse(const std::string jsonCode)
if ((c == '\"') && (!areWeInString)) areWeInString = true; if ((c == '\"') && (!areWeInString)) areWeInString = true;
else if ((c == '\"') && (!isCharEscaped) && (areWeInString)) areWeInString = false; else if ((c == '\"') && (!isCharEscaped) && (areWeInString)) areWeInString = false;
// No need to check for char escaping since we are already checking if we are in a string or not. Chars are ever escaped outside of strings // No need to check for char escaping since we are already checking if we are in a string or not. Chars are never escaped outside of strings
if ((c == '[') && (!areWeInString)) arrayBracketLevel++; if ((c == '[') && (!areWeInString)) arrayBracketLevel++;
else if ((c == ']') && (!areWeInString)) arrayBracketLevel--; else if ((c == ']') && (!areWeInString)) arrayBracketLevel--;
else if ((c == '{') && (!areWeInString)) curlyBracketLevel++; else if ((c == '{') && (!areWeInString)) curlyBracketLevel++;
@ -3001,6 +3040,7 @@ void JsonBlock::Parse(const std::string jsonCode)
if (arrayBracketLevel != 0) throw JsonParsingMissingBracketsException(); if (arrayBracketLevel != 0) throw JsonParsingMissingBracketsException();
if (curlyBracketLevel != 0) throw JsonParsingMissingBracketsException(); if (curlyBracketLevel != 0) throw JsonParsingMissingBracketsException();
if (areWeInString) throw JsonParsingMissingQuotesException(); if (areWeInString) throw JsonParsingMissingQuotesException();
if (areWeBetweenCommaAndLabel) throw JsonParsingGeneralException("Unexpected EOF. Don't put a comma after the last value of a json block!");
if ((elementCodeSnippets.size() == 0) && (minifiedCode.length() > 2)) throw JsonParsingGeneralException("Found no members in json block, but code is too long for no members"); if ((elementCodeSnippets.size() == 0) && (minifiedCode.length() > 2)) throw JsonParsingGeneralException("Found no members in json block, but code is too long for no members");
} }

View File

@ -1053,7 +1053,7 @@ namespace JasonPP
/// </summary> /// </summary>
/// <param name="str">The original string to work in</param> /// <param name="str">The original string to work in</param>
/// <returns>The string with escaped characters</returns> /// <returns>The string with escaped characters</returns>
static std::string Escape(std::string str); static std::string Escape(const std::string str);
/// <summary> /// <summary>
/// Will unescape json strings /// Will unescape json strings
@ -1241,6 +1241,12 @@ namespace JasonPP
/// <returns>The json data type of this object</returns> /// <returns>The json data type of this object</returns>
JSON_DATA_TYPE GetDataType() const { return dataType; }; JSON_DATA_TYPE GetDataType() const { return dataType; };
/// <summary>
/// Returns whether or not this JsonData is either of type FLOAT or INT
/// </summary>
/// <returns>Whether or not this JsonData is either of type FLOAT or INT</returns>
bool IsOfNumericType() const;
/// <summary> /// <summary>
/// Returns the bool value of this element, exception if of wrong type /// Returns the bool value of this element, exception if of wrong type
/// </summary> /// </summary>
@ -2136,7 +2142,7 @@ namespace JasonPP
}; };
} }
#define JASONPP_VERSION (1.01) #define JASONPP_VERSION (1.02)
namespace JasonPP namespace JasonPP
{ {

View File

@ -23,9 +23,9 @@ void LogHistory::PostExit()
return; return;
} }
void LogHistory::AddLogToHistory(LogEntry* _newEntry) void LogHistory::AddLogToHistory(LogEntry* newEntry)
{ {
history->push_back(_newEntry); history->push_back(newEntry);
return; return;
} }

View File

@ -26,7 +26,7 @@ namespace Logging
static std::vector<LogEntry*>* GetLogHistory() { return history; } static std::vector<LogEntry*>* GetLogHistory() { return history; }
private: private:
static void AddLogToHistory(LogEntry* _newEntry); static void AddLogToHistory(LogEntry* newEntry);
static std::vector<LogEntry*>* history; static std::vector<LogEntry*>* history;

View File

@ -2,15 +2,15 @@
using namespace Logging; using namespace Logging;
Logger::Logger(std::string _identifier) Logger::Logger(std::string identifier)
{ {
isInitialized = true; isInitialized = true;
std::stringstream ss; std::stringstream ss;
rawIdentifier = _identifier; rawIdentifier = identifier;
ss << "[" << rawIdentifier << "]"; ss << "[" << rawIdentifier << "]";
identifier = ss.str(); this->identifier = ss.str();
return; return;
} }
@ -25,12 +25,12 @@ void Logger::Clear()
return; return;
} }
void Logger::Set(std::string _str) void Logger::Set(std::string str)
{ {
if (!IsInitializedSanityCheck()) return; if (!IsInitializedSanityCheck()) return;
Clear(); Clear();
cout << _str; cout << str;
return; return;
} }
@ -44,7 +44,7 @@ std::string Logger::Flush()
tm currTm; tm currTm;
localtime_s(&currTm, &currTime); localtime_s(&currTm, &currTime);
char timeBuf[256]; char timeBuf[256];
strftime(timeBuf, 100, "%d.%m.%Y - %T", currTm); strftime(timeBuf, 100, "%d.%m.%Y - %T", &currTm);
std::stringstream bufOut; std::stringstream bufOut;
bufOut << "<" << timeBuf << "> " << identifier << TypeToPrefix(type) << ": " << cout.str(); bufOut << "<" << timeBuf << "> " << identifier << TypeToPrefix(type) << ": " << cout.str();
@ -56,25 +56,25 @@ std::string Logger::Flush()
newEntry->type = type; newEntry->type = type;
LogHistory::AddLogToHistory(newEntry); LogHistory::AddLogToHistory(newEntry);
std::cout << TypeToColor(type) << bufOut.str() << "\033[0m" << std::endl; std::cout << bufOut.str() << std::endl;
Clear(); Clear();
return ""; return "";
} }
std::string Logger::Type(LOG_TYPE _type) std::string Logger::Type(LOG_TYPE type)
{ {
if (!IsInitializedSanityCheck()) return ""; if (!IsInitializedSanityCheck()) return "";
type = _type; this->type = type;
return ""; return "";
} }
std::string Logger::TypeToPrefix(LOG_TYPE _type) std::string Logger::TypeToPrefix(LOG_TYPE type)
{ {
switch (_type) switch (type)
{ {
case LOG_TYPE::LOG: case LOG_TYPE::LOG:
return ""; return "";
@ -94,7 +94,7 @@ std::string Logger::TypeToPrefix(LOG_TYPE _type)
std::string Logger::TypeToColor(LOG_TYPE _type) std::string Logger::TypeToColor(LOG_TYPE _type)
{ {
switch (_type) /*switch (_type)
{ {
case LOG_TYPE::LOG: case LOG_TYPE::LOG:
return "\033[0m"; return "\033[0m";
@ -109,7 +109,8 @@ std::string Logger::TypeToColor(LOG_TYPE _type)
return "\033[0m"; return "\033[0m";
} }
return "\033[0m"; return "\033[0m";*/
return "";
} }
bool Logger::IsInitializedSanityCheck() bool Logger::IsInitializedSanityCheck()
@ -117,7 +118,8 @@ bool Logger::IsInitializedSanityCheck()
if (!isInitialized) if (!isInitialized)
{ {
Logger log("Logger"); //A Log object cannot always have a Log-member because of its recursive nature. Only create them when needed! Logger log("Logger"); //A Log object cannot always have a Log-member because of its recursive nature. Only create them when needed!
log.cout << log.Err() << "Attempted to use logger object without being initialized!" << log.Flush(); log.cout << log.Err() << "Attempted to use logger object without being initialized!";
log.Flush();
return false; return false;
} }

View File

@ -7,43 +7,43 @@
namespace Logging namespace Logging
{ {
class Log class Logger
{ {
public: public:
public: public:
//Creates a Logger object. Pass an identifier such as MySQL //Creates a Logger object. Pass an identifier such as MySQL
Log(std::string _identifier); Logger(std::string identifier);
//Clears the buffered string and resets the log type to default //Clears the buffered string and resets the log type to default
void Clear(); void Clear();
//Sets the buffered string //Sets the buffered string
void Set(std::string _str); void Set(std::string str);
//Prints the buffered string to the console and clears it //Prints the buffered string to the console and clears it
std::string Flush(); std::string Flush();
//Sets a custom log type //Sets a custom log type
std::string Type(LOG_TYPE _type); std::string Type(LOG_TYPE type);
//Sets the log type to warning //Sets the log type to warning
std::string Warn() { return Type(WARN); } std::string Warn() { return Type(LOG_TYPE::WARN); }
//Sets the log type to error //Sets the log type to error
std::string Err() { return Type(ERR); } std::string Err() { return Type(LOG_TYPE::ERR); }
std::stringstream cout; std::stringstream cout;
private: private:
std::string TypeToPrefix(LOG_TYPE _type); std::string TypeToPrefix(LOG_TYPE type);
std::string TypeToColor(LOG_TYPE _type); std::string TypeToColor(LOG_TYPE type);
bool IsInitializedSanityCheck(); bool IsInitializedSanityCheck();
std::string identifier; std::string identifier;
std::string rawIdentifier; std::string rawIdentifier;
LOG_TYPE type = LOG; LOG_TYPE type = LOG_TYPE::LOG;
bool isInitialized = false; bool isInitialized = false;
}; };

View File

@ -1,6 +1,8 @@
#include "RestInterface.h" #include "RestInterface.h"
using namespace Logging; using namespace Logging;
using namespace Rest;
using namespace JasonPP;
RestInterface::RestInterface() RestInterface::RestInterface()
{ {
@ -28,7 +30,6 @@ void RestInterface::PostInit()
{ {
isBootedSuccessfully = InitWebServer(); isBootedSuccessfully = InitWebServer();
return; return;
} }
@ -64,45 +65,42 @@ void RestInterface::Update()
return; return;
} }
void RestInterface::ServeStringToConnection(struct mg_connection* _c, std::string _str) void RestInterface::ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode)
{ {
mg_send_head(_c, 200, _str.length(), "content-type: application/json\nAccess-Control-Allow-Origin: *"); mg_send_head(c, httpStatusCode, str.length(), "content-type: application/json\nAccess-Control-Allow-Origin: *");
mg_printf(_c, _str.c_str()); mg_printf(c, str.c_str());
return; return;
} }
void RestInterface::EventHandler(mg_connection* pNc, int ev, void* p)
void RestInterface::EventHandler(mg_connection* _pNc, int _ev, void* _p)
{ {
switch (_ev) switch (ev)
{ {
case MG_EV_HTTP_REQUEST: case MG_EV_HTTP_REQUEST:
/* http_message* hpm = (http_message*)p;
StringParser sp(_pNc->recv_mbuf.buf); std::string requestBodyRaw = FixUnterminatedString(hpm->body.p, hpm->body.len);
sp.Skip("GET /"); if (IsJsonValid(requestBodyRaw))
std::string rawRequest = sp.ExtSeek(" HTTP/"); {
std::string jsonQuery = StringTools::UrlDecode(rawRequest); Json requestBody;
requestBody.Parse(requestBodyRaw);
JsonBlock responseBody;
HTTP_STATUS_CODE returnCode;
RestQueryHandler::ProcessQuery(requestBody, responseBody, returnCode);
std::string queryResult = RestAPIQueryHandler::ProcessQuery(jsonQuery); Json response(responseBody);
ServeStringToConnection(pNc, response.Render(), returnCode);
}
else
{
Json errorJson = RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Received json is fucked up");
ServeStringToConnection(pNc, errorJson.Render(), HTTP_STATUS_CODE::BAD_REQUEST);
}
ServeStringToConnection(_pNc, queryResult);
*/
//std::cout << _pNc->recv_mbuf.buf << std::endl;
http_message* hpm = (http_message*)_p;
char buf[500];
mg_get_http_var(&hpm->body, "data", buf, 500);
std::cout << buf << std::endl;
ServeStringToConnection(_pNc, _pNc->recv_mbuf.buf);
break; break;
} }
@ -115,3 +113,15 @@ void RestInterface::OnExit()
return; return;
} }
std::string RestInterface::FixUnterminatedString(const char* cstr, const std::size_t len)
{
std::stringstream ss;
for (std::size_t i = 0; i < len; i++)
{
ss << *(cstr + i);
}
return ss.str();
}

View File

@ -4,34 +4,37 @@
#include <sstream> #include <sstream>
#include "mongoose.h" #include "mongoose.h"
#include "Logger.h" #include "Logger.h"
#include "RestResponseTemplates.h"
#include "RestQueryHandler.h"
#define WEBAPI_SERVER_POLLRATE 100 #define WEBAPI_SERVER_POLLRATE 100
#define WEBAPI_SERVER_PORT "6969" #define WEBAPI_SERVER_PORT "6969"
class RestInterface namespace Rest
{ {
public: class RestInterface
RestInterface(); {
~RestInterface(); public:
RestInterface();
~RestInterface();
void PostInit(); void PostInit();
void Update(); void Update();
void OnExit(); void OnExit();
private: private:
bool InitWebServer(); bool InitWebServer();
static void EventHandler(struct mg_connection* _pNc, int _ev, void* _p); static void EventHandler(struct mg_connection* pNc, int ev, void* p);
static void ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode = 200);
static void ServeStringToConnection(struct mg_connection* _c, std::string _str); static std::string FixUnterminatedString(const char* cstr, const std::size_t len);
struct mg_mgr* pMgr; struct mg_mgr* pMgr;
struct mg_connection* pNc; struct mg_connection* pNc;
static mg_serve_http_opts pServeOpts;
Logging::Logger* log; Logging::Logger* log;
bool isBootedSuccessfully;
};
bool isBootedSuccessfully;
};
}

View File

@ -0,0 +1,92 @@
#include "RestQueryHandler.h"
using namespace Rest;
using namespace Logging;
using namespace JasonPP;
void RestQueryHandler::PreInit()
{
log = new Logger("RestQueryHandler");
return;
}
bool RestQueryHandler::ProcessQuery(const Json& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
{
if (!ValidateField("request", JSON_DATA_TYPE::STRING, request, responseBody))
{
responseCode = BAD_REQUEST;
return false;
}
JsonBlock requestBody = request.AsJson;
std::string requestName = requestBody.Get("request").AsString;
if (requestName == "kill_yourself") return KillYourself(requestBody, responseBody, responseCode);
responseBody.CloneFrom(RestResponseTemplates::GetByCode(NOT_FOUND, "The requested request was not found."));
responseCode = NOT_FOUND;
return false;
}
void Rest::RestQueryHandler::PostExit()
{
delete log;
log = nullptr;
return;
}
bool RestQueryHandler::KillYourself(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
{
XGControl::keepServerRunning = false;
log->cout << "Shutting down server upon rest request...";
log->Flush();
responseCode = OK;
responseBody.CloneFrom(RestResponseTemplates::GetByCode(OK));
responseBody.Set("message") = "Goodbye! :3";
return true;
}
bool RestQueryHandler::ValidateField(const std::string name, const JasonPP::JSON_DATA_TYPE type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere)
{
if (checkThat.GetDataType() != JSON_DATA_TYPE::JSON)
{
putErrorResponseHere = RestResponseTemplates::GetByCode(BAD_REQUEST, "The request body must be a json struct! No json array or similar...");
return false;
}
const JsonBlock& cachedJson = checkThat.AsJson;
if (cachedJson.DoesShorthandExist(name))
{
const JsonData& cached = cachedJson.ShorthandGet(name);
if ((cached.GetDataType() == type) ||
((cached.IsOfNumericType()) && (type == JSON_DATA_TYPE::INT)) ||
((cached.IsOfNumericType()) && (type == JSON_DATA_TYPE::FLOAT)))
{
return true;
}
else
{
std::stringstream ss;
ss << "Mandatory value \"" << name << "\" is of wrong type (" << JsonDataType2String(cached.GetDataType()) << ")" << std::endl;
ss << "Should be of type " << JsonDataType2String(type) << "! (Integers can be casted to floats)";
putErrorResponseHere.CloneFrom(RestResponseTemplates::GetByCode(BAD_REQUEST, ss.str()));
}
}
else
{
putErrorResponseHere.CloneFrom(RestResponseTemplates::GetByCode(BAD_REQUEST, std::string("Missing mandatory value '" + name + "'")));
return false;
}
return false;
}
Logger* RestQueryHandler::log;

25
Tubio/RestQueryHandler.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "JasonPP.hpp"
#include "RestResponseTemplates.h"
#include "XGControl.h"
#include "Logger.h"
namespace Rest
{
class RestQueryHandler
{
public:
static void PreInit();
static bool ProcessQuery(const JasonPP::Json& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static void PostExit();
private:
static bool KillYourself(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool ValidateField(const std::string name, const JasonPP::JSON_DATA_TYPE type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere);
static Logging::Logger* log;
};
}

View File

@ -0,0 +1,89 @@
#include "RestResponseTemplates.h"
using namespace JasonPP;
/*
Every query MUST have a value for "status"
Every query with status = "ERROR" MUST have a value for "description" and "message"!
*/
JsonBlock Rest::RestResponseTemplates::GetByCode(HTTP_STATUS_CODE code, std::string message)
{
switch (code)
{
case HTTP_STATUS_CODE::OK:
return OK();
case HTTP_STATUS_CODE::BAD_REQUEST:
return BadRequest(message);
case HTTP_STATUS_CODE::FORBIDDEN:
return Forbidden((message.length() > 0) ? message : "Could be disabled in the config file!");
case HTTP_STATUS_CODE::NOT_FOUND:
return NotFound((message.length() > 0) ? message : "not found");
case HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR:
return InternalServerError((message.length() > 0) ? message : "Well, that's not good.");
case HTTP_STATUS_CODE::NOT_IMPLEMENTED:
return NotImplemented(message);
}
return InternalServerError("No template found for this http-status-code");
}
JsonBlock Rest::RestResponseTemplates::OK()
{
return JsonBlock({
Ele("status", "OK"),
});
}
JsonBlock Rest::RestResponseTemplates::BadRequest(std::string message)
{
return JsonBlock({
Ele("status", "ERROR"),
Ele("description", "Bad request"),
Ele("message", message)
});
}
JsonBlock Rest::RestResponseTemplates::Unauthorized(std::string message)
{
return JsonBlock({
Ele("status", "ERROR"),
Ele("description", "Unauthorized"),
Ele("message", message)
});
}
JsonBlock Rest::RestResponseTemplates::Forbidden(std::string message)
{
return JsonBlock({
Ele("status", "ERROR"),
Ele("description", "Forbidden"),
Ele("message", message)
});
}
JsonBlock Rest::RestResponseTemplates::NotFound(std::string message)
{
return JsonBlock({
Ele("status", "ERROR"),
Ele("description", "Not found"),
Ele("message", message)
});
}
JsonBlock Rest::RestResponseTemplates::InternalServerError(std::string message)
{
return JsonBlock({
Ele("status", "ERROR"),
Ele("description", "Internal server error"),
Ele("message", message)
});
}
JsonBlock Rest::RestResponseTemplates::NotImplemented(std::string message)
{
return JsonBlock({
Ele("status", "ERROR"),
Ele("description", "Not implemented"),
Ele("message", message)
});
}

View File

@ -0,0 +1,33 @@
#pragma once
#include "JasonPP.hpp"
namespace Rest
{
enum HTTP_STATUS_CODE {
OK = 200,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501
};
class RestResponseTemplates
{
public:
static JasonPP::JsonBlock GetByCode(HTTP_STATUS_CODE code, std::string message = "");
private:
static JasonPP::JsonBlock OK();
static JasonPP::JsonBlock BadRequest(std::string message);
static JasonPP::JsonBlock Unauthorized(std::string message);
static JasonPP::JsonBlock Forbidden(std::string message);
static JasonPP::JsonBlock NotFound(std::string message);
static JasonPP::JsonBlock MethodNotAllowed(std::string message);
static JasonPP::JsonBlock InternalServerError(std::string message);
static JasonPP::JsonBlock NotImplemented(std::string message);
};
}

View File

@ -145,7 +145,10 @@
<ClCompile Include="LogHistory.cpp" /> <ClCompile Include="LogHistory.cpp" />
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="mongoose.c" /> <ClCompile Include="mongoose.c" />
<ClCompile Include="RestQueryHandler.cpp" />
<ClCompile Include="RestResponseTemplates.cpp" />
<ClCompile Include="RestInterface.cpp" /> <ClCompile Include="RestInterface.cpp" />
<ClCompile Include="XGControl.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Framework.h" /> <ClInclude Include="Framework.h" />
@ -154,7 +157,10 @@
<ClInclude Include="LogHistory.h" /> <ClInclude Include="LogHistory.h" />
<ClInclude Include="LogTypes.h" /> <ClInclude Include="LogTypes.h" />
<ClInclude Include="mongoose.h" /> <ClInclude Include="mongoose.h" />
<ClInclude Include="RestQueryHandler.h" />
<ClInclude Include="RestResponseTemplates.h" />
<ClInclude Include="RestInterface.h" /> <ClInclude Include="RestInterface.h" />
<ClInclude Include="XGControl.h" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View File

@ -39,6 +39,15 @@
<ClCompile Include="RestInterface.cpp"> <ClCompile Include="RestInterface.cpp">
<Filter>Quelldateien</Filter> <Filter>Quelldateien</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="RestResponseTemplates.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="RestQueryHandler.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="XGControl.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="JasonPP.hpp"> <ClInclude Include="JasonPP.hpp">
@ -62,5 +71,14 @@
<ClInclude Include="RestInterface.h"> <ClInclude Include="RestInterface.h">
<Filter>Headerdateien</Filter> <Filter>Headerdateien</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="RestResponseTemplates.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="RestQueryHandler.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="XGControl.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

3
Tubio/XGControl.cpp Normal file
View File

@ -0,0 +1,3 @@
#include "XGControl.h"
bool XGControl::keepServerRunning = false;

11
Tubio/XGControl.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
/// <summary>
/// Class to house control variables
/// </summary>
class XGControl
{
public:
static bool keepServerRunning;
};

View File

@ -1,116 +0,0 @@
#include "RestInterface.h"
using namespace Logging;
RestInterface::RestInterface()
{
pMgr = new mg_mgr();
pNc = nullptr;
log = new Logger("WebServer");
isBootedSuccessfully = false;
return;
}
RestInterface::~RestInterface()
{
delete pMgr;
delete log;
log = nullptr;
pMgr = nullptr;
return;
}
void RestInterface::PostInit()
{
isBootedSuccessfully = InitWebServer();
return;
}
bool RestInterface::InitWebServer()
{
mg_mgr_init(pMgr, NULL);
log->cout << "Starting web server on port " << WEBAPI_SERVER_PORT << "...";
log->Flush();
pNc = mg_bind(pMgr, WEBAPI_SERVER_PORT, this->EventHandler);
if (pNc == NULL)
{
log->cout << log->Err() << "Failed to boot the web server! - Unable to bind listener!";
log->Flush();
return false;
}
mg_set_protocol_http_websocket(pNc);
log->cout << "Started web server successfully!";
log->Flush();
isBootedSuccessfully = true;
return true;
}
void RestInterface::Update()
{
mg_mgr_poll(pMgr, WEBAPI_SERVER_POLLRATE);
return;
}
void RestInterface::ServeStringToConnection(struct mg_connection* _c, std::string _str)
{
mg_send_head(_c, 200, _str.length(), "content-type: application/json\nAccess-Control-Allow-Origin: *");
mg_printf(_c, _str.c_str());
return;
}
void RestInterface::EventHandler(mg_connection* _pNc, int _ev, void* _p)
{
switch (_ev)
{
case MG_EV_HTTP_REQUEST:
/*
StringParser sp(_pNc->recv_mbuf.buf);
sp.Skip("GET /");
std::string rawRequest = sp.ExtSeek(" HTTP/");
std::string jsonQuery = StringTools::UrlDecode(rawRequest);
std::string queryResult = RestAPIQueryHandler::ProcessQuery(jsonQuery);
ServeStringToConnection(_pNc, queryResult);
*/
//std::cout << _pNc->recv_mbuf.buf << std::endl;
http_message* hpm = (http_message*)_p;
char buf[500];
mg_get_http_var(&hpm->body, "data", buf, 500);
ServeStringToConnection(_pNc, _pNc->recv_mbuf.buf);
break;
}
return;
}
void RestInterface::OnExit()
{
mg_mgr_free(pMgr);
return;
}

View File

@ -1,114 +0,0 @@
#include "RestInterface.h"
using namespace Logging;
RestInterface::RestInterface()
{
pMgr = new mg_mgr();
pNc = nullptr;
log = new Logger("WebServer");
isBootedSuccessfully = false;
return;
}
RestInterface::~RestInterface()
{
delete pMgr;
delete log;
log = nullptr;
pMgr = nullptr;
return;
}
void RestInterface::PostInit()
{
isBootedSuccessfully = InitWebServer();
return;
}
bool RestInterface::InitWebServer()
{
mg_mgr_init(pMgr, NULL);
log->cout << "Starting web server on port " << WEBAPI_SERVER_PORT << "...";
log->Flush();
pNc = mg_bind(pMgr, WEBAPI_SERVER_PORT, this->EventHandler);
if (pNc == NULL)
{
log->cout << log->Err() << "Failed to boot the web server! - Unable to bind listener!";
log->Flush();
return false;
}
mg_set_protocol_http_websocket(pNc);
log->cout << "Started web server successfully!";
log->Flush();
isBootedSuccessfully = true;
return true;
}
void RestInterface::Update()
{
mg_mgr_poll(pMgr, WEBAPI_SERVER_POLLRATE);
return;
}
void RestInterface::ServeStringToConnection(struct mg_connection* _c, std::string _str)
{
mg_send_head(_c, 200, _str.length(), "content-type: application/json\nAccess-Control-Allow-Origin: *");
mg_printf(_c, _str.c_str());
return;
}
void RestInterface::EventHandler(mg_connection* _pNc, int _ev, void* _p)
{
switch (_ev)
{
case MG_EV_HTTP_REQUEST:
/*
StringParser sp(_pNc->recv_mbuf.buf);
sp.Skip("GET /");
std::string rawRequest = sp.ExtSeek(" HTTP/");
std::string jsonQuery = StringTools::UrlDecode(rawRequest);
std::string queryResult = RestAPIQueryHandler::ProcessQuery(jsonQuery);
ServeStringToConnection(_pNc, queryResult);
*/
std::cout << _pNc->recv_mbuf.buf << std::endl;
http_message* hpm = (http_message*)_p;
std::cout << hpm << std::endl;
ServeStringToConnection(_pNc, _pNc->recv_mbuf.buf);
break;
}
return;
}
void RestInterface::OnExit()
{
mg_mgr_free(pMgr);
return;
}