Maurice Grönwoldt
5bb68a7d02
We have no make install support... so we don't need to have everything as a single-header and lib file.
148 lines
4.8 KiB
C++
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
|