Added basic rest framework
This commit is contained in:
parent
918b735922
commit
595e04bc32
@ -1,6 +1,7 @@
|
||||
#include "Framework.h"
|
||||
|
||||
using namespace Logging;
|
||||
using namespace Rest;
|
||||
|
||||
Framework::Framework()
|
||||
{
|
||||
@ -13,7 +14,7 @@ Framework::Framework()
|
||||
|
||||
PostInit();
|
||||
|
||||
isRunning = true;
|
||||
XGControl::keepServerRunning = true;
|
||||
Run();
|
||||
|
||||
return;
|
||||
@ -32,7 +33,7 @@ Framework::~Framework()
|
||||
|
||||
void Framework::Run()
|
||||
{
|
||||
while (isRunning)
|
||||
while (XGControl::keepServerRunning)
|
||||
{
|
||||
restInterface->Update();
|
||||
}
|
||||
@ -48,6 +49,7 @@ void Framework::Run()
|
||||
void Framework::PreInit()
|
||||
{
|
||||
LogHistory::PreInit();
|
||||
RestQueryHandler::PreInit();
|
||||
|
||||
return;
|
||||
}
|
||||
@ -70,6 +72,7 @@ void Framework::OnExit()
|
||||
void Framework::PostExit()
|
||||
{
|
||||
LogHistory::PostExit();
|
||||
RestQueryHandler::PostExit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "Logger.h"
|
||||
#include "LogHistory.h"
|
||||
#include "RestInterface.h"
|
||||
#include "XGControl.h"
|
||||
|
||||
class Framework
|
||||
{
|
||||
@ -9,18 +10,17 @@ public:
|
||||
Framework();
|
||||
~Framework();
|
||||
|
||||
void Run();
|
||||
|
||||
private:
|
||||
void Run();
|
||||
|
||||
void PostInit();
|
||||
void OnExit();
|
||||
void PreInit();
|
||||
void PostExit();
|
||||
|
||||
RestInterface* restInterface;
|
||||
Rest::RestInterface* restInterface;
|
||||
|
||||
Log* log;
|
||||
Logging::Logger* log;
|
||||
|
||||
bool isRunning = true;
|
||||
};
|
||||
|
@ -948,6 +948,7 @@ void JsonArray::Parse(const std::string jsonCode)
|
||||
bool areWeInString = false;
|
||||
bool areWeInCode = 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++)
|
||||
{
|
||||
@ -957,13 +958,25 @@ void JsonArray::Parse(const std::string jsonCode)
|
||||
{
|
||||
start = i;
|
||||
areWeInCode = true;
|
||||
areWeBetweenCommaAndValue = true;
|
||||
}
|
||||
|
||||
else if ((!areWeInString) && (areWeInCode) && (arrayBracketLevel == 1) && (curlyBracketLevel == 0) && ((c == ',') || (i == minifiedCode.length() - 1)))
|
||||
{
|
||||
if (c != ',') areWeBetweenCommaAndValue = false;
|
||||
end = i;
|
||||
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));
|
||||
@ -984,6 +997,7 @@ void JsonArray::Parse(const std::string jsonCode)
|
||||
// Someone fucked up his json code
|
||||
if (arrayBracketLevel != 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();
|
||||
}
|
||||
|
||||
@ -1090,7 +1104,7 @@ std::string StringHelpers::Replace(const std::string str, const std::string find
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string StringHelpers::Escape(std::string str)
|
||||
std::string StringHelpers::Escape(const std::string str)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
@ -1120,7 +1134,6 @@ std::string StringHelpers::Escape(std::string str)
|
||||
ss << "\\\\";
|
||||
break;
|
||||
default:
|
||||
|
||||
if (str[i] < 0) ss << EscapeUTF8(str[i]);
|
||||
else ss << str[i];
|
||||
}
|
||||
@ -1671,6 +1684,11 @@ double JsonData::GetFloatPrecision() const
|
||||
return customFloatPrecision;
|
||||
}
|
||||
|
||||
bool JsonData::IsOfNumericType() const
|
||||
{
|
||||
return (dataType == JSON_DATA_TYPE::INT) || (dataType == JSON_DATA_TYPE::FLOAT);
|
||||
}
|
||||
|
||||
bool JsonData::GetBoolData() const
|
||||
{
|
||||
JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::BOOL;
|
||||
@ -2963,22 +2981,43 @@ void JsonBlock::Parse(const std::string jsonCode)
|
||||
bool areWeInString = false;
|
||||
bool areWeInCode = 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++)
|
||||
{
|
||||
const char c = minifiedCode[i];
|
||||
|
||||
if ((!areWeInString) && (c == '\"') && (arrayBracketLevel == 0) && (curlyBracketLevel == 1) && (!areWeInCode))
|
||||
if ((!areWeInString) && (arrayBracketLevel == 0) && (curlyBracketLevel == 1) && (!areWeInCode))
|
||||
{
|
||||
if (c == '\"')
|
||||
{
|
||||
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 == '}')))
|
||||
{
|
||||
end = i;
|
||||
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 :)
|
||||
@ -2987,7 +3026,7 @@ void JsonBlock::Parse(const std::string jsonCode)
|
||||
if ((c == '\"') && (!areWeInString)) areWeInString = true;
|
||||
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++;
|
||||
else if ((c == ']') && (!areWeInString)) arrayBracketLevel--;
|
||||
else if ((c == '{') && (!areWeInString)) curlyBracketLevel++;
|
||||
@ -3001,6 +3040,7 @@ void JsonBlock::Parse(const std::string jsonCode)
|
||||
if (arrayBracketLevel != 0) throw JsonParsingMissingBracketsException();
|
||||
if (curlyBracketLevel != 0) throw JsonParsingMissingBracketsException();
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -1053,7 +1053,7 @@ namespace JasonPP
|
||||
/// </summary>
|
||||
/// <param name="str">The original string to work in</param>
|
||||
/// <returns>The string with escaped characters</returns>
|
||||
static std::string Escape(std::string str);
|
||||
static std::string Escape(const std::string str);
|
||||
|
||||
/// <summary>
|
||||
/// Will unescape json strings
|
||||
@ -1241,6 +1241,12 @@ namespace JasonPP
|
||||
/// <returns>The json data type of this object</returns>
|
||||
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>
|
||||
/// Returns the bool value of this element, exception if of wrong type
|
||||
/// </summary>
|
||||
@ -2136,7 +2142,7 @@ namespace JasonPP
|
||||
};
|
||||
}
|
||||
|
||||
#define JASONPP_VERSION (1.01)
|
||||
#define JASONPP_VERSION (1.02)
|
||||
|
||||
namespace JasonPP
|
||||
{
|
||||
|
@ -23,9 +23,9 @@ void LogHistory::PostExit()
|
||||
return;
|
||||
}
|
||||
|
||||
void LogHistory::AddLogToHistory(LogEntry* _newEntry)
|
||||
void LogHistory::AddLogToHistory(LogEntry* newEntry)
|
||||
{
|
||||
history->push_back(_newEntry);
|
||||
history->push_back(newEntry);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ namespace Logging
|
||||
static std::vector<LogEntry*>* GetLogHistory() { return history; }
|
||||
|
||||
private:
|
||||
static void AddLogToHistory(LogEntry* _newEntry);
|
||||
static void AddLogToHistory(LogEntry* newEntry);
|
||||
|
||||
static std::vector<LogEntry*>* history;
|
||||
|
||||
|
@ -2,15 +2,15 @@
|
||||
|
||||
using namespace Logging;
|
||||
|
||||
Logger::Logger(std::string _identifier)
|
||||
Logger::Logger(std::string identifier)
|
||||
{
|
||||
isInitialized = true;
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
rawIdentifier = _identifier;
|
||||
rawIdentifier = identifier;
|
||||
ss << "[" << rawIdentifier << "]";
|
||||
identifier = ss.str();
|
||||
this->identifier = ss.str();
|
||||
|
||||
return;
|
||||
}
|
||||
@ -25,12 +25,12 @@ void Logger::Clear()
|
||||
return;
|
||||
}
|
||||
|
||||
void Logger::Set(std::string _str)
|
||||
void Logger::Set(std::string str)
|
||||
{
|
||||
if (!IsInitializedSanityCheck()) return;
|
||||
|
||||
Clear();
|
||||
cout << _str;
|
||||
cout << str;
|
||||
|
||||
return;
|
||||
}
|
||||
@ -44,7 +44,7 @@ std::string Logger::Flush()
|
||||
tm currTm;
|
||||
localtime_s(&currTm, &currTime);
|
||||
char timeBuf[256];
|
||||
strftime(timeBuf, 100, "%d.%m.%Y - %T", currTm);
|
||||
strftime(timeBuf, 100, "%d.%m.%Y - %T", &currTm);
|
||||
|
||||
std::stringstream bufOut;
|
||||
bufOut << "<" << timeBuf << "> " << identifier << TypeToPrefix(type) << ": " << cout.str();
|
||||
@ -56,25 +56,25 @@ std::string Logger::Flush()
|
||||
newEntry->type = type;
|
||||
LogHistory::AddLogToHistory(newEntry);
|
||||
|
||||
std::cout << TypeToColor(type) << bufOut.str() << "\033[0m" << std::endl;
|
||||
std::cout << bufOut.str() << std::endl;
|
||||
|
||||
Clear();
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string Logger::Type(LOG_TYPE _type)
|
||||
std::string Logger::Type(LOG_TYPE type)
|
||||
{
|
||||
if (!IsInitializedSanityCheck()) return "";
|
||||
|
||||
|
||||
type = _type;
|
||||
this->type = type;
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string Logger::TypeToPrefix(LOG_TYPE _type)
|
||||
std::string Logger::TypeToPrefix(LOG_TYPE type)
|
||||
{
|
||||
switch (_type)
|
||||
switch (type)
|
||||
{
|
||||
case LOG_TYPE::LOG:
|
||||
return "";
|
||||
@ -94,7 +94,7 @@ std::string Logger::TypeToPrefix(LOG_TYPE _type)
|
||||
|
||||
std::string Logger::TypeToColor(LOG_TYPE _type)
|
||||
{
|
||||
switch (_type)
|
||||
/*switch (_type)
|
||||
{
|
||||
case LOG_TYPE::LOG:
|
||||
return "\033[0m";
|
||||
@ -109,7 +109,8 @@ std::string Logger::TypeToColor(LOG_TYPE _type)
|
||||
return "\033[0m";
|
||||
}
|
||||
|
||||
return "\033[0m";
|
||||
return "\033[0m";*/
|
||||
return "";
|
||||
}
|
||||
|
||||
bool Logger::IsInitializedSanityCheck()
|
||||
@ -117,7 +118,8 @@ bool Logger::IsInitializedSanityCheck()
|
||||
if (!isInitialized)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -7,43 +7,43 @@
|
||||
|
||||
namespace Logging
|
||||
{
|
||||
class Log
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
|
||||
public:
|
||||
//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
|
||||
void Clear();
|
||||
|
||||
//Sets the buffered string
|
||||
void Set(std::string _str);
|
||||
void Set(std::string str);
|
||||
|
||||
//Prints the buffered string to the console and clears it
|
||||
std::string Flush();
|
||||
|
||||
//Sets a custom log type
|
||||
std::string Type(LOG_TYPE _type);
|
||||
std::string Type(LOG_TYPE type);
|
||||
|
||||
//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
|
||||
std::string Err() { return Type(ERR); }
|
||||
std::string Err() { return Type(LOG_TYPE::ERR); }
|
||||
|
||||
std::stringstream cout;
|
||||
|
||||
private:
|
||||
std::string TypeToPrefix(LOG_TYPE _type);
|
||||
std::string TypeToColor(LOG_TYPE _type);
|
||||
std::string TypeToPrefix(LOG_TYPE type);
|
||||
std::string TypeToColor(LOG_TYPE type);
|
||||
|
||||
bool IsInitializedSanityCheck();
|
||||
|
||||
std::string identifier;
|
||||
std::string rawIdentifier;
|
||||
LOG_TYPE type = LOG;
|
||||
LOG_TYPE type = LOG_TYPE::LOG;
|
||||
|
||||
bool isInitialized = false;
|
||||
};
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "RestInterface.h"
|
||||
|
||||
using namespace Logging;
|
||||
using namespace Rest;
|
||||
using namespace JasonPP;
|
||||
|
||||
RestInterface::RestInterface()
|
||||
{
|
||||
@ -28,7 +30,6 @@ void RestInterface::PostInit()
|
||||
{
|
||||
isBootedSuccessfully = InitWebServer();
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -64,45 +65,42 @@ void RestInterface::Update()
|
||||
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_printf(_c, _str.c_str());
|
||||
mg_send_head(c, httpStatusCode, 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)
|
||||
void RestInterface::EventHandler(mg_connection* pNc, int ev, void* p)
|
||||
{
|
||||
switch (_ev)
|
||||
switch (ev)
|
||||
{
|
||||
case MG_EV_HTTP_REQUEST:
|
||||
|
||||
/*
|
||||
StringParser sp(_pNc->recv_mbuf.buf);
|
||||
http_message* hpm = (http_message*)p;
|
||||
std::string requestBodyRaw = FixUnterminatedString(hpm->body.p, hpm->body.len);
|
||||
|
||||
sp.Skip("GET /");
|
||||
std::string rawRequest = sp.ExtSeek(" HTTP/");
|
||||
std::string jsonQuery = StringTools::UrlDecode(rawRequest);
|
||||
if (IsJsonValid(requestBodyRaw))
|
||||
{
|
||||
Json requestBody;
|
||||
requestBody.Parse(requestBodyRaw);
|
||||
|
||||
std::string queryResult = RestAPIQueryHandler::ProcessQuery(jsonQuery);
|
||||
JsonBlock responseBody;
|
||||
HTTP_STATUS_CODE returnCode;
|
||||
RestQueryHandler::ProcessQuery(requestBody, responseBody, returnCode);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -115,3 +113,15 @@ void RestInterface::OnExit()
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -4,13 +4,17 @@
|
||||
#include <sstream>
|
||||
#include "mongoose.h"
|
||||
#include "Logger.h"
|
||||
#include "RestResponseTemplates.h"
|
||||
#include "RestQueryHandler.h"
|
||||
|
||||
#define WEBAPI_SERVER_POLLRATE 100
|
||||
#define WEBAPI_SERVER_PORT "6969"
|
||||
|
||||
class RestInterface
|
||||
namespace Rest
|
||||
{
|
||||
public:
|
||||
class RestInterface
|
||||
{
|
||||
public:
|
||||
RestInterface();
|
||||
~RestInterface();
|
||||
|
||||
@ -18,20 +22,19 @@ public:
|
||||
void Update();
|
||||
void OnExit();
|
||||
|
||||
private:
|
||||
private:
|
||||
bool InitWebServer();
|
||||
|
||||
static void EventHandler(struct mg_connection* _pNc, int _ev, void* _p);
|
||||
|
||||
static void ServeStringToConnection(struct mg_connection* _c, std::string _str);
|
||||
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 std::string FixUnterminatedString(const char* cstr, const std::size_t len);
|
||||
|
||||
|
||||
struct mg_mgr* pMgr;
|
||||
struct mg_connection* pNc;
|
||||
static mg_serve_http_opts pServeOpts;
|
||||
|
||||
Logging::Logger* log;
|
||||
|
||||
bool isBootedSuccessfully;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
92
Tubio/RestQueryHandler.cpp
Normal file
92
Tubio/RestQueryHandler.cpp
Normal 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
25
Tubio/RestQueryHandler.h
Normal 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;
|
||||
};
|
||||
}
|
89
Tubio/RestResponseTemplates.cpp
Normal file
89
Tubio/RestResponseTemplates.cpp
Normal 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)
|
||||
});
|
||||
}
|
33
Tubio/RestResponseTemplates.h
Normal file
33
Tubio/RestResponseTemplates.h
Normal 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);
|
||||
};
|
||||
}
|
@ -145,7 +145,10 @@
|
||||
<ClCompile Include="LogHistory.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="mongoose.c" />
|
||||
<ClCompile Include="RestQueryHandler.cpp" />
|
||||
<ClCompile Include="RestResponseTemplates.cpp" />
|
||||
<ClCompile Include="RestInterface.cpp" />
|
||||
<ClCompile Include="XGControl.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Framework.h" />
|
||||
@ -154,7 +157,10 @@
|
||||
<ClInclude Include="LogHistory.h" />
|
||||
<ClInclude Include="LogTypes.h" />
|
||||
<ClInclude Include="mongoose.h" />
|
||||
<ClInclude Include="RestQueryHandler.h" />
|
||||
<ClInclude Include="RestResponseTemplates.h" />
|
||||
<ClInclude Include="RestInterface.h" />
|
||||
<ClInclude Include="XGControl.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
@ -39,6 +39,15 @@
|
||||
<ClCompile Include="RestInterface.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</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>
|
||||
<ClInclude Include="JasonPP.hpp">
|
||||
@ -62,5 +71,14 @@
|
||||
<ClInclude Include="RestInterface.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</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>
|
||||
</Project>
|
3
Tubio/XGControl.cpp
Normal file
3
Tubio/XGControl.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include "XGControl.h"
|
||||
|
||||
bool XGControl::keepServerRunning = false;
|
11
Tubio/XGControl.h
Normal file
11
Tubio/XGControl.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
/// <summary>
|
||||
/// Class to house control variables
|
||||
/// </summary>
|
||||
class XGControl
|
||||
{
|
||||
public:
|
||||
static bool keepServerRunning;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user