VWeb/Source/RequestHandler.cpp
Maurice Grönwoldt 5bb68a7d02 Split VWeb into smaller headers
We have no make install support... so we don't need to have everything as a single-header and lib file.
2023-09-16 16:29:03 +02:00

148 lines
4.8 KiB
C++

#include "Includes/VWeb.h"
#include "StringUtils.h"
namespace VWeb {
HttpMethod StringToHTTPMethod(std::string &method) {
static std::unordered_map<std::string, HttpMethod> s_StringToMethodMap{
{"get", HttpMethod::GET}, {"head", HttpMethod::HEAD},
{"options", HttpMethod::OPTIONS}, {"post", HttpMethod::POST},
{"put", HttpMethod::PUT}, {"patch", HttpMethod::PATCH},
{"delete", HttpMethod::DELETE}, {"fallback", HttpMethod::FALLBACK},
};
String::ToLowerCase(method);
if (s_StringToMethodMap.contains(method))
return s_StringToMethodMap[method];
return s_StringToMethodMap["fallback"];
}
bool ParseRequest(Ref<Request> &request) {
std::istringstream resp(request->Body);
std::string line;
std::string::size_type index;
while (std::getline(resp, line) && line != "\r") {
index = line.find(':', 0);
if (index != std::string::npos) {
auto key = line.substr(0, index);
String::Trim(key);
String::ToLowerCase(key);
request->Header(key).Add(String::TrimCopy(line.substr(index + 1)));
} else if (line.find("HTTP")) {
auto headers = String::Split(line, " ");
if (headers.size() != 3)
return false;
request->Method = StringToHTTPMethod(headers[0]);
request->URI = String::UrlDecode(headers[1]);
} else {
return false;
}
}
return true;
}
void ParseParameterString(Request &req, const std::string &toParse) {
auto data = String::Split(toParse, "&");
for (auto &item : data) {
auto d = String::Split(item, "=", 1);
req.Parameter(d[0]).Add(d.size() > 1 ? d[1] : "true");
}
}
std::string GetPostBody(const std::string& originalBody)
{
auto body = String::Split(originalBody, "\r\n\r\n", 1);
if (body.size() > 1 && ! body[body.size() - 1].empty())
return String::TrimCopy(String::UrlDecode(body[body.size() - 1]));
return {};
}
void ParseParameters(Request &request, RequestHandler &requestHandler) {
auto &uri = request.URI;
size_t hasURLParameters = uri.find('?');
if (hasURLParameters != std::string::npos) {
ParseParameterString(request, uri.substr(hasURLParameters + 1));
request.URI = uri.substr (0, hasURLParameters);
}
if (request.Method == HttpMethod::HEAD || request.Method == HttpMethod::GET ||
request.Method == HttpMethod::OPTIONS)
return;
// POST :D
auto &contentType = request.Header("content-type");
if (contentType.Size() == 0)
return;
auto &key = contentType.GetFirst();
if (key == "application/x-www-form-urlencoded") {
ParseParameterString(request, GetPostBody(request.Body));
} else {
if (requestHandler.HasBodyParser(key)) {
auto &func = requestHandler.GetBodyParser(key);
func(request, GetPostBody(request.Body));
}
}
}
struct RequestJob : public WorkerJob {
Ref<Request> MRequest{};
Ref<Router> MRouter{};
Ref<MiddleWareHandler> MMiddleWareHandler{};
RequestHandler *MRequestHandler{nullptr};
void Execute() override {
if (!ParseRequest(MRequest)) {
fprintf(stderr, "[Request] >> Request failed to parse\n");
SocketUtils::Close(MRequest->ID);
return;
}
ParseParameters(*MRequest, *MRequestHandler);
MRequest->CookieData = CreateRef<Cookies>();
MRequest->SessionData = CreateRef<Session>();
Ref<Response> response;
auto preValue = MMiddleWareHandler->HandlePre(MRequest);
if (preValue.has_value()) {
response = preValue.value();
response->SessionData = MRequest->SessionData;
response->CookieData = MRequest->CookieData;
} else {
response = MRouter->HandleRoute(MRequest);
}
MMiddleWareHandler->HandlePost(MRequest, response);
auto content = response->GetResponse();
MRequestHandler->AddSendResponse(
{0, (ssize_t)content.size(), MRequest->ID, content});
MMiddleWareHandler->Shutdown(MRequest, response);
}
};
RequestHandler::RequestHandler(const Ref<SocketManager> &manager)
: m_SocketManager(manager),
m_MiddleWare(CreateRef<MiddleWareHandler>()) {
m_MiddleWare = CreateRef<MiddleWareHandler>();
}
void RequestHandler::InitThreads(int count) {
m_Pool.SetThreadCount(count);
m_Pool.Create();
}
void RequestHandler::AddRequest(Ref<Request> &request) {
if (m_Router) {
auto job = CreateRef<RequestJob>();
job->MRequest = request;
job->MMiddleWareHandler = m_MiddleWare;
job->MRequestHandler = this;
job->MRouter = m_Router;
m_Pool.Dispatch(job);
}
}
void RequestHandler::Stop() { m_Pool.Stop(); }
void RequestHandler::SetRouter(Ref<Router> &router) { m_Router = router; }
void RequestHandler::AddSendResponse(SendData sendData) {
auto id = sendData.SocketID;
m_Server->m_OutRequest.Add(id, sendData);
if (!m_SocketManager->SetSendListen(id)) {
SocketUtils::Close(id);
m_Server->m_OutRequest.Remove(id);
}
}
} // namespace VWeb