#include "Includes/VWeb.h" #include "StringUtils.h" namespace VWeb { HttpMethod StringToHTTPMethod(std::string &method) { static std::unordered_map 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) { 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 MRequest{}; Ref MRouter{}; Ref 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(); MRequest->SessionData = CreateRef(); Ref 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 &manager) : m_SocketManager(manager), m_MiddleWare(CreateRef()) { m_MiddleWare = CreateRef(); } void RequestHandler::InitThreads(int count) { m_Pool.SetThreadCount(count); m_Pool.Create(); } void RequestHandler::AddRequest(Ref &request) { if (m_Router) { auto job = CreateRef(); 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) { 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