From bd7402e45786a133b1ef4245c8a27162f491975b Mon Sep 17 00:00:00 2001 From: Pasha Barahimi Date: Wed, 6 Dec 2023 00:43:19 +0330 Subject: [PATCH 01/23] Change travisCI to github actions --- .github/workflows/ci.yml | 38 ++++++++++++++++++++++++++++++++++++++ .travis.yml | 29 ----------------------------- README.md | 2 +- 3 files changed, 39 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f426e1a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +name: CI + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Run lint + uses: jidicula/clang-format-action@v4.11.0 + with: + clang-format-version: "13" + fallback-style: "LLVM" + + build-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y g++ make + - name: Build + run: make + env: + CC: gcc + CXX: g++ + + build-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - name: Build + run: make + env: + CC: gcc + CXX: g++ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 67765a8..0000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -before_install: - - "eval \"${MATRIX_EVAL}\"" -language: cpp -matrix: - include: - - - env: - - "MATRIX_EVAL=\"TESTENV=lint && STYLE=LLVM\"" - os: osx - script: - - "find . -name *.h -exec bash -c 'cmp <(clang-format --style=LLVM $0) $0' {} \\;" - - "find . -name *.hpp -exec bash -c 'cmp <(clang-format --style=LLVM $0) $0' {} \\;" - - "find . -name *.c -exec bash -c 'cmp <(clang-format --style=LLVM $0) $0' {} \\;" - - "find . -name *.cpp -exec bash -c 'cmp <(clang-format --style=LLVM $0) $0' {} \\;" - - - before_install: - - "sudo apt-get update" - - "sudo apt-get install -y g++ make" - env: - - "MATRIX_EVAL=\"TESTENV=build && CC=gcc && CXX=g++\"" - os: linux - script: - - make - - - env: - - "MATRIX_EVAL=\"TESTENV=build && CC=gcc && CXX=g++\"" - os: osx - script: - - make diff --git a/README.md b/README.md index c959749..1367456 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ AP HTTP === -[![Travis (.org)](https://travis-ci.com/UTAP/APHTTP.svg)](https://travis-ci.com/UTAP/APHTTP) + [![code style: LLVM](https://img.shields.io/badge/code_style-LLVM-brightgreen.svg)](https://llvm.org/docs/CodingStandards.html) [![Release](https://img.shields.io/github/release/UTAP/APHTTP.svg)](https://github.com/UTAP/APHTTP/releases/latest) [![Wiki](https://img.shields.io/badge/GitHub-Wiki-yellowgreen.svg)](https://github.com/UTAP/APHTTP/wiki) From 6320fd9bb247d0f663711eb28fdef25b344f5157 Mon Sep 17 00:00:00 2001 From: Pasha Barahimi Date: Wed, 6 Dec 2023 01:24:59 +0330 Subject: [PATCH 02/23] Fix formattings --- examples/main.cpp | 2 +- server/server.cpp | 41 +++++++++++++++++++++------------------ utils/template_parser.cpp | 27 ++++++++++++++------------ utils/template_parser.hpp | 2 +- utils/utilities.cpp | 5 +++-- 5 files changed, 42 insertions(+), 35 deletions(-) diff --git a/examples/main.cpp b/examples/main.cpp index 1e5c549..fd35afb 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -20,7 +20,7 @@ int main(int argc, char **argv) { server.get("/", new ShowPage("static/home.html")); server.get("/colors", new ColorHandler("template/colors.html")); server.run(); - } catch (const Server::Exception& e) { + } catch (const Server::Exception &e) { cerr << e.getMessage() << endl; } } diff --git a/server/server.cpp b/server/server.cpp index da418e1..e925a99 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -15,17 +15,16 @@ #include "../utils/utilities.hpp" - #ifdef _WIN32 #ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 //win xp +#define _WIN32_WINNT 0x0501 // win xp #endif -#include #include +#include #else -//POSIX sockets -#include +// POSIX sockets #include +#include #include //close() #endif @@ -39,17 +38,19 @@ #define GETSOCKETERRNO() (errno) #endif -static const char* getSocketError() { +static const char *getSocketError() { #ifdef _WIN32 - static char message[256]; - message[0] = '\0'; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, WSAGetLastError(), 0, (LPSTR)&message, sizeof(message), NULL); - char *newline = strrchr(message, '\n'); - if (newline) *newline = '\0'; - return message; + static char message[256]; + message[0] = '\0'; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, WSAGetLastError(), 0, (LPSTR)&message, sizeof(message), + NULL); + char *newline = strrchr(message, '\n'); + if (newline) + *newline = '\0'; + return message; #else - return strerror(errno); + return strerror(errno); #endif } @@ -234,7 +235,7 @@ Request *parseRawReq(char *headersRaw, size_t length) { } break; } } - } catch (const Server::Exception&) { + } catch (const Server::Exception &) { throw; } catch (...) { throw Server::Exception("Error on parsing request"); @@ -247,7 +248,8 @@ Server::Server(int _port) : port(_port) { WSADATA wsa_data; int initializeResult = WSAStartup(MAKEWORD(2, 2), &wsa_data); if (initializeResult != 0) { - throw Exception("Error: WinSock WSAStartup failed: " + string(getSocketError())); + throw Exception("Error: WinSock WSAStartup failed: " + + string(getSocketError())); } #endif @@ -257,7 +259,8 @@ Server::Server(int _port) : port(_port) { int sc_option = 1; #ifdef _WIN32 - setsockopt(sc, SOL_SOCKET, SO_REUSEADDR, (char*)&sc_option, sizeof(sc_option)); + setsockopt(sc, SOL_SOCKET, SO_REUSEADDR, (char *)&sc_option, + sizeof(sc_option)); #else setsockopt(sc, SOL_SOCKET, SO_REUSEADDR, &sc_option, sizeof(sc_option)); #endif @@ -300,7 +303,7 @@ void Server::run() { throw Exception("Error on accept: " + string(getSocketError())); Response *res = NULL; try { - char* data = new char[BUFSIZE + 1]; + char *data = new char[BUFSIZE + 1]; size_t recv_len, recv_total_len = 0; Request *req = NULL; while (!req) { @@ -330,7 +333,7 @@ void Server::run() { res = notFoundHandler->callback(req); } delete req; - } catch (const Exception& exc) { + } catch (const Exception &exc) { delete res; res = ServerErrorHandler::callback(exc.getMessage()); } diff --git a/utils/template_parser.cpp b/utils/template_parser.cpp index 3ca191a..bb7db11 100644 --- a/utils/template_parser.cpp +++ b/utils/template_parser.cpp @@ -2,10 +2,11 @@ #include using namespace std; -static std::string mkdirNoErrors(const std::string& dirName) { -//do not error if dir already exists (-p flag on linux) +static std::string mkdirNoErrors(const std::string &dirName) { +// do not error if dir already exists (-p flag on linux) #ifdef _WIN32 - return ("(if not exist \"" + dirName + "\" " + SysCmd::mkdir + "\"" + dirName + "\")"); + return ("(if not exist \"" + dirName + "\" " + SysCmd::mkdir + "\"" + + dirName + "\")"); #else return (SysCmd::mkdir + "-p \"" + dirName + "\""); #endif @@ -19,7 +20,8 @@ const std::string localTemplate(const int parserNum) { TemplateParser::TemplateParser(string _filePath) { filePath = _filePath; variableCount = 0; - programName = to_string(TemplateParser::lastParserNum) + SysCmd::fileExtention; + programName = + to_string(TemplateParser::lastParserNum) + SysCmd::fileExtention; parserNum = TemplateParser::lastParserNum++; code = ""; parseTemplate(); @@ -34,7 +36,7 @@ string TemplateParser::getHtml(map _context) { void TemplateParser::parseTemplate() { string unparsedTemplate = readFile(filePath); int parsePointer = 0; - while (parsePointer < (signed int) unparsedTemplate.size()) { + while (parsePointer < (signed int)unparsedTemplate.size()) { int begin = findBeginOfCodeBlock(parsePointer, unparsedTemplate); int end = findEndOfCodeBlock(parsePointer, unparsedTemplate); if (begin < 0) @@ -101,16 +103,16 @@ void TemplateParser::compileCode() { throw Server::Exception("Can not write generated template code!"); string cmd = mkdirNoErrors(outputFolder) + " && " + cc + " " + toCompileFile + - " " + utilitiesPath + " -o " + outputFolder + SysCmd::slash + programName + - "&& " + SysCmd::rm + toCompileFile; + " " + utilitiesPath + " -o " + outputFolder + SysCmd::slash + + programName + "&& " + SysCmd::rm + toCompileFile; string error = "Can not compile template " + filePath; TemplateUtils::runSystemCommand(cmd, error); } string TemplateParser::runGeneratedCode() { - string cmd = - SysCmd::programStart + outputFolder + SysCmd::slash + programName + " " + " > " + staticTemplate; + string cmd = SysCmd::programStart + outputFolder + SysCmd::slash + + programName + " " + " > " + staticTemplate; string error = "Error in running template " + filePath; TemplateUtils::runSystemCommand(cmd, error); @@ -162,7 +164,8 @@ void TemplateParser::deleteExecutable() { } void TemplateParser::deleteLocalTemplate() { - string cmd = SysCmd::rm + outputFolder + SysCmd::slash + localTemplate(parserNum); + string cmd = + SysCmd::rm + outputFolder + SysCmd::slash + localTemplate(parserNum); string error = "Error in deleting local template at " + outputFolder + "/" + localTemplate(parserNum); TemplateUtils::runSystemCommand(cmd, error); @@ -172,8 +175,8 @@ void TemplateParser::TemplateUtils::runSystemCommand(string command, string error) { int ret = system(command.c_str()); #ifdef _WIN32 - if(ret != 0) { - throw Server::Exception(error); + if (ret != 0) { + throw Server::Exception(error); } #else if (WEXITSTATUS(ret) != EXIT_SUCCESS) { diff --git a/utils/template_parser.hpp b/utils/template_parser.hpp index 82197ed..f4bce49 100644 --- a/utils/template_parser.hpp +++ b/utils/template_parser.hpp @@ -22,7 +22,7 @@ const std::string mkdir = "mkdir "; const std::string slash = "/"; const std::string fileExtention = ".o"; #endif -} +} // namespace SysCmd const std::string beginCodeBlockTag = "<%"; const std::string endCodeBlockTag = "%>"; diff --git a/utils/utilities.cpp b/utils/utilities.cpp index ac0302c..e80f0bb 100644 --- a/utils/utilities.cpp +++ b/utils/utilities.cpp @@ -26,7 +26,8 @@ bool comp::operator()(const string &lhs, const string &rhs) const { string readFile(const char *filename) { ifstream infile; infile.open(filename, infile.binary); - if (!infile.is_open()) return string(); + if (!infile.is_open()) + return string(); infile.seekg(0, infile.end); size_t length = infile.tellg(); @@ -34,7 +35,7 @@ string readFile(const char *filename) { if (length > BUFFER_SIZE) length = BUFFER_SIZE; - char* buffer = new char[length + 1]; + char *buffer = new char[length + 1]; infile.read(buffer, length); From 777e0621618a47128d2e8ddfaa713e9d1eacc276 Mon Sep 17 00:00:00 2001 From: Pasha Barahimi Date: Wed, 6 Dec 2023 09:20:00 +0330 Subject: [PATCH 03/23] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1367456..8301478 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ AP HTTP === [![code style: LLVM](https://img.shields.io/badge/code_style-LLVM-brightgreen.svg)](https://llvm.org/docs/CodingStandards.html) -[![Release](https://img.shields.io/github/release/UTAP/APHTTP.svg)](https://github.com/UTAP/APHTTP/releases/latest) -[![Wiki](https://img.shields.io/badge/GitHub-Wiki-yellowgreen.svg)](https://github.com/UTAP/APHTTP/wiki) +[![Release](https://img.shields.io/github/release/UTAP/APHTTP.svg)](https://github.com/AP_ECE_UT/APHTTP/releases/latest) +[![Wiki](https://img.shields.io/badge/GitHub-Wiki-yellowgreen.svg)](https://github.com/AP-ECE-UT/APHTTP/wiki) **AP HTTP::_server_** is a simple web application server-side blocking framework for C++ based on simplified versions of [W++](http://konteck.github.io/wpp/), [HappyHTTP](http://scumways.com/happyhttp/happyhttp.html), and [cpp-netlib](http://cpp-netlib.org/). From 6d18df10ec5932db7f56f27d3f1e177d86751b13 Mon Sep 17 00:00:00 2001 From: MisaghM Date: Sat, 23 Dec 2023 13:57:42 +0330 Subject: [PATCH 04/23] Update README and add .clang-format --- .clang-format | 21 +++++++++++++++++++++ README.md | 9 +++++---- 2 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2bbe336 --- /dev/null +++ b/.clang-format @@ -0,0 +1,21 @@ +--- +BasedOnStyle: Google +BreakBeforeBraces: Custom +BraceWrapping: + BeforeCatch: true + BeforeElse: true + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +IndentWidth: 4 +AccessModifierOffset: -4 +IndentCaseLabels: false +NamespaceIndentation: Inner +ColumnLimit: 0 +AlignConsecutiveMacros: Consecutive +DerivePointerAlignment: false +SpacesBeforeTrailingComments: 1 +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +... diff --git a/README.md b/README.md index 8301478..ddbbee5 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ AP HTTP === -[![code style: LLVM](https://img.shields.io/badge/code_style-LLVM-brightgreen.svg)](https://llvm.org/docs/CodingStandards.html) -[![Release](https://img.shields.io/github/release/UTAP/APHTTP.svg)](https://github.com/AP_ECE_UT/APHTTP/releases/latest) -[![Wiki](https://img.shields.io/badge/GitHub-Wiki-yellowgreen.svg)](https://github.com/AP-ECE-UT/APHTTP/wiki) +[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/AP-ECE-UT/APHTTP/license.md) +[![Release](https://img.shields.io/github/release/AP-ECE-UT/APHTTP.svg?color=brightgreen)](https://github.com/AP-ECE-UT/APHTTP/releases/latest) +[![Wiki](https://img.shields.io/badge/GitHub-Wiki-red.svg)](https://github.com/AP-ECE-UT/APHTTP/wiki) -**AP HTTP::_server_** is a simple web application server-side blocking framework for C++ based on simplified versions of [W++](http://konteck.github.io/wpp/), [HappyHTTP](http://scumways.com/happyhttp/happyhttp.html), and [cpp-netlib](http://cpp-netlib.org/). +**AP HTTP::_server_** is a simple web application server-side blocking framework for C++. +This library is based on simplified versions of [W++](http://konteck.github.io/wpp/), [HappyHTTP](http://scumways.com/happyhttp/happyhttp.html), and [cpp-netlib](http://cpp-netlib.org/). From 51c75647451ac75a1f5d839653fad58b49d174af Mon Sep 17 00:00:00 2001 From: MisaghM Date: Sat, 23 Dec 2023 14:01:01 +0330 Subject: [PATCH 05/23] Reformat and fix header guards Change buffer size to constexpr Use std::tolower --- examples/handlers.cpp | 83 +++--- examples/handlers.hpp | 19 +- examples/main.cpp | 40 +-- examples/my_server.hpp | 8 +- server/route.cpp | 11 +- server/route.hpp | 27 +- server/server.cpp | 603 ++++++++++++++++++++------------------ server/server.hpp | 83 +++--- utils/include.hpp | 14 +- utils/request.cpp | 192 ++++++------ utils/request.hpp | 67 +++-- utils/response.cpp | 85 +++--- utils/response.hpp | 7 +- utils/template_parser.cpp | 248 ++++++++-------- utils/template_parser.hpp | 80 ++--- utils/utilities.cpp | 352 +++++++++++----------- utils/utilities.hpp | 36 +-- 17 files changed, 996 insertions(+), 959 deletions(-) diff --git a/examples/handlers.cpp b/examples/handlers.cpp index e88ddba..7fc6441 100644 --- a/examples/handlers.cpp +++ b/examples/handlers.cpp @@ -2,53 +2,54 @@ using namespace std; -Response *RandomNumberHandler::callback(Request *req) { - Response *res = new Response; - res->setHeader("Content-Type", "text/html"); - string body; - body += ""; - body += ""; - body += ""; - body += "

AP HTTP

"; - body += "

"; - body += "a random number in [1, 10] is: "; - body += to_string(rand() % 10 + 1); - body += "

"; - body += "

"; - body += "SeddionId: "; - body += req->getSessionId(); - body += "

"; - body += ""; - body += ""; - res->setBody(body); - return res; +Response* RandomNumberHandler::callback(Request* req) { + Response* res = new Response; + res->setHeader("Content-Type", "text/html"); + string body; + body += ""; + body += ""; + body += ""; + body += "

AP HTTP

"; + body += "

"; + body += "a random number in [1, 10] is: "; + body += to_string(rand() % 10 + 1); + body += "

"; + body += "

"; + body += "SeddionId: "; + body += req->getSessionId(); + body += "

"; + body += ""; + body += ""; + res->setBody(body); + return res; } -Response *LoginHandler::callback(Request *req) { - string username = req->getBodyParam("username"); - string password = req->getBodyParam("password"); - if (username == "root") - throw Server::Exception("Remote root access has been disabled."); - cout << "username: " << username << ",\tpassword: " << password << endl; - Response *res = Response::redirect("/rand"); - res->setSessionId("SID"); - return res; +Response* LoginHandler::callback(Request* req) { + string username = req->getBodyParam("username"); + string password = req->getBodyParam("password"); + if (username == "root") + throw Server::Exception("Remote root access has been disabled."); + cout << "username: " << username << ",\tpassword: " << password << endl; + Response* res = Response::redirect("/rand"); + res->setSessionId("SID"); + return res; } -Response *UploadHandler::callback(Request *req) { - string name = req->getBodyParam("file_name"); - string file = req->getBodyParam("file"); - cout << name << " (" << file.size() << "B):\n" << file << endl; - Response *res = Response::redirect("/"); - return res; +Response* UploadHandler::callback(Request* req) { + string name = req->getBodyParam("file_name"); + string file = req->getBodyParam("file"); + cout << name << " (" << file.size() << "B):\n" + << file << endl; + Response* res = Response::redirect("/"); + return res; } ColorHandler::ColorHandler(string filePath) : TemplateHandler(filePath) {} -map ColorHandler::handle(Request *req) { - map context; - string newName = "I am " + req->getQueryParam("name"); - context["name"] = newName; - context["color"] = req->getQueryParam("color"); - return context; +map ColorHandler::handle(Request* req) { + map context; + string newName = "I am " + req->getQueryParam("name"); + context["name"] = newName; + context["color"] = req->getQueryParam("color"); + return context; } diff --git a/examples/handlers.hpp b/examples/handlers.hpp index 987d310..cc25b84 100644 --- a/examples/handlers.hpp +++ b/examples/handlers.hpp @@ -1,30 +1,31 @@ -#ifndef _MY_HANDLERS_ -#define _MY_HANDLERS_ +#ifndef HANDLERS_HPP_INCLUDE +#define HANDLERS_HPP_INCLUDE -#include "../server/server.hpp" #include // for rand and srand #include // for time #include +#include "../server/server.hpp" + class RandomNumberHandler : public RequestHandler { public: - Response *callback(Request *); + Response* callback(Request*); }; class LoginHandler : public RequestHandler { public: - Response *callback(Request *); + Response* callback(Request*); }; class UploadHandler : public RequestHandler { public: - Response *callback(Request *); + Response* callback(Request*); }; class ColorHandler : public TemplateHandler { public: - ColorHandler(std::string filePath); - std::map handle(Request *req); + ColorHandler(std::string filePath); + std::map handle(Request* req); }; -#endif +#endif // HANDLERS_HPP_INCLUDE diff --git a/examples/main.cpp b/examples/main.cpp index fd35afb..c352509 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -1,26 +1,28 @@ -#include "handlers.hpp" -#include "my_server.hpp" #include // for rand and srand #include // for time #include +#include "handlers.hpp" +#include "my_server.hpp" + using namespace std; -int main(int argc, char **argv) { - srand(time(NULL)); // for rand - try { - MyServer server(argc > 1 ? atoi(argv[1]) : 5000); - server.setNotFoundErrPage("static/404.html"); - server.get("/login", new ShowPage("static/logincss.html")); - server.post("/login", new LoginHandler()); - server.get("/up", new ShowPage("static/upload_form.html")); - server.post("/up", new UploadHandler()); - server.get("/rand", new RandomNumberHandler()); - server.get("/home.png", new ShowImage("static/home.png")); - server.get("/", new ShowPage("static/home.html")); - server.get("/colors", new ColorHandler("template/colors.html")); - server.run(); - } catch (const Server::Exception &e) { - cerr << e.getMessage() << endl; - } +int main(int argc, char** argv) { + srand(time(NULL)); // for rand + try { + MyServer server(argc > 1 ? atoi(argv[1]) : 5000); + server.setNotFoundErrPage("static/404.html"); + server.get("/login", new ShowPage("static/logincss.html")); + server.post("/login", new LoginHandler()); + server.get("/up", new ShowPage("static/upload_form.html")); + server.post("/up", new UploadHandler()); + server.get("/rand", new RandomNumberHandler()); + server.get("/home.png", new ShowImage("static/home.png")); + server.get("/", new ShowPage("static/home.html")); + server.get("/colors", new ColorHandler("template/colors.html")); + server.run(); + } + catch (const Server::Exception& e) { + cerr << e.getMessage() << endl; + } } diff --git a/examples/my_server.hpp b/examples/my_server.hpp index 4543073..e3e88db 100644 --- a/examples/my_server.hpp +++ b/examples/my_server.hpp @@ -1,11 +1,11 @@ -#ifndef __MY_SERVER__ -#define __MY_SERVER__ +#ifndef MY_SERVER_HPP_INCLUDE +#define MY_SERVER_HPP_INCLUDE #include "../server/server.hpp" class MyServer : public Server { public: - MyServer(int port = 5000); + MyServer(int port = 5000); }; -#endif +#endif // MY_SERVER_HPP_INCLUDE diff --git a/server/route.cpp b/server/route.cpp index 3a551d5..19d15d6 100644 --- a/server/route.cpp +++ b/server/route.cpp @@ -1,19 +1,20 @@ #include "route.hpp" + #include "server.hpp" using namespace std; Route::Route(Method _method, string _path) { - method = _method; - path = _path; + method = _method; + path = _path; } -void Route::setHandler(RequestHandler *_handler) { handler = _handler; } +void Route::setHandler(RequestHandler* _handler) { handler = _handler; } bool Route::isMatch(Method _method, string url) { - return (url == path) && (_method == method); + return (url == path) && (_method == method); } -Response *Route::handle(Request *req) { return handler->callback(req); } +Response* Route::handle(Request* req) { return handler->callback(req); } Route::~Route() { delete handler; } diff --git a/server/route.hpp b/server/route.hpp index 8417f75..381222f 100644 --- a/server/route.hpp +++ b/server/route.hpp @@ -1,23 +1,26 @@ -#ifndef __ROUTE__ -#define __ROUTE__ +#ifndef ROUTE_HPP_INCLUDE +#define ROUTE_HPP_INCLUDE + +#include + #include "../utils/include.hpp" #include "../utils/request.hpp" #include "../utils/response.hpp" -#include class RequestHandler; class Route { private: - Method method; - std::string path; - RequestHandler *handler; + Method method; + std::string path; + RequestHandler* handler; public: - Route(Method _method, std::string _path); - ~Route(); - bool isMatch(Method, std::string url); - Response *handle(Request *req); - void setHandler(RequestHandler *_handler); + Route(Method _method, std::string _path); + ~Route(); + bool isMatch(Method, std::string url); + Response* handle(Request* req); + void setHandler(RequestHandler* _handler); }; -#endif + +#endif // ROUTE_HPP_INCLUDE diff --git a/server/server.cpp b/server/server.cpp index e925a99..a90cf9f 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -30,333 +30,350 @@ #ifdef _WIN32 #define ISVALIDSOCKET(s) ((s) != INVALID_SOCKET) -#define CLOSESOCKET(s) closesocket(s) +#define CLOSESOCKET(s) closesocket(s) #define GETSOCKETERRNO() (WSAGetLastError()) #else #define ISVALIDSOCKET(s) ((s) >= 0) -#define CLOSESOCKET(s) close(s) +#define CLOSESOCKET(s) close(s) #define GETSOCKETERRNO() (errno) #endif -static const char *getSocketError() { +static const char* getSocketError() { #ifdef _WIN32 - static char message[256]; - message[0] = '\0'; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, WSAGetLastError(), 0, (LPSTR)&message, sizeof(message), - NULL); - char *newline = strrchr(message, '\n'); - if (newline) - *newline = '\0'; - return message; + static char message[256]; + message[0] = '\0'; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, WSAGetLastError(), 0, (LPSTR)&message, sizeof(message), + NULL); + char* newline = strrchr(message, '\n'); + if (newline) + *newline = '\0'; + return message; #else - return strerror(errno); + return strerror(errno); #endif } using namespace std; class NotFoundHandler : public RequestHandler { - string notFoundErrPage; + string notFoundErrPage; public: - NotFoundHandler(string notFoundErrPage = "") - : notFoundErrPage(notFoundErrPage) {} - Response *callback(Request *req) { - Response *res = new Response(404); - if (!notFoundErrPage.empty()) { - res->setHeader("Content-Type", "text/" + getExtension(notFoundErrPage)); - res->setBody(readFile(notFoundErrPage.c_str())); + NotFoundHandler(string notFoundErrPage = "") + : notFoundErrPage(notFoundErrPage) {} + Response* callback(Request* req) { + Response* res = new Response(404); + if (!notFoundErrPage.empty()) { + res->setHeader("Content-Type", "text/" + getExtension(notFoundErrPage)); + res->setBody(readFile(notFoundErrPage.c_str())); + } + return res; } - return res; - } }; class ServerErrorHandler { public: - static Response *callback(string msg) { - Response *res = new Response(500); - res->setHeader("Content-Type", "application/json"); - res->setBody("{ \"code\": \"500\", \"message\": \"" + msg + "\" }\n"); - return res; - } + static Response* callback(string msg) { + Response* res = new Response(500); + res->setHeader("Content-Type", "application/json"); + res->setBody("{ \"code\": \"500\", \"message\": \"" + msg + "\" }\n"); + return res; + } }; -void split(string str, string separator, int max, vector &results) { - int i = 0; - size_t found = str.find_first_of(separator); - - while (found != string::npos) { - if (found > 0) - results.push_back(str.substr(0, found)); - str = str.substr(found + 1); - found = str.find_first_of(separator); - - if (max > -1 && ++i == max) - break; - } - if (str.length() > 0) - results.push_back(str); -} +void split(string str, string separator, int max, vector& results) { + int i = 0; + size_t found = str.find_first_of(separator); + + while (found != string::npos) { + if (found > 0) + results.push_back(str.substr(0, found)); + str = str.substr(found + 1); + found = str.find_first_of(separator); -Request *parseRawReq(char *headersRaw, size_t length) { - Request *req = nullptr; - string boundary; - string lastFieldKey; - string lastFieldValue; - bool shouldBeEmpty; - try { - enum State { REQ, HEADER, BODY, BODY_HEADER, BODY_BODY }; - State state = REQ; - vector headers = split(string(headersRaw), "\r\n", false); - for (size_t i = 0; i < length; i++) { - if (!headersRaw[i]) - throw Server::Exception("Unsupported binary data in request."); + if (max > -1 && ++i == max) + break; } - size_t realBodySize = - string(headersRaw).size() - - split(string(headersRaw), "\r\n\r\n", false)[0].size() - - string("\r\n\r\n").size(); - for (size_t headerIndex = 0; headerIndex < headers.size(); headerIndex++) { - string line = headers[headerIndex]; - switch (state) { - case REQ: { - vector R = split(line, " ", false); - if (R.size() != 3) { - throw Server::Exception("Invalid header (request line)"); - } - req = new Request(R[0]); - req->setPath(R[1]); - size_t pos = req->getPath().find('?'); - if (pos != string::npos && pos != req->getPath().size() - 1) { - vector Q1 = split(req->getPath().substr(pos + 1), "&", false); - for (vector::size_type q = 0; q < Q1.size(); q++) { - vector Q2 = split(Q1[q], "=", false); - if (Q2.size() == 2) - req->setQueryParam(Q2[0], Q2[1], false); - else - throw Server::Exception("Invalid query"); - } - } - req->setPath(req->getPath().substr(0, pos)); - state = HEADER; - } break; - case HEADER: { - if (line == "") { - state = BODY; - if (req->getHeader("Content-Type") - .substr(0, string("multipart/form-data").size()) == - "multipart/form-data") { - boundary = - req->getHeader("Content-Type") - .substr(req->getHeader("Content-Type").find("boundary=") + - string("boundary=").size()); - } - break; - } - vector R = split(line, ": ", false); - if (R.size() != 2) - throw Server::Exception("Invalid header"); - req->setHeader(R[0], R[1], false); - if (toLowerCase(R[0]) == toLowerCase("Content-Length")) - if (realBodySize != (size_t)atol(R[1].c_str())) - return NULL; - } break; - case BODY: { - if (req->getHeader("Content-Type") == "") { - } else if (req->getHeader("Content-Type") == - "application/x-www-form-urlencoded") { - vector body = split(line, "&", false); - for (size_t i = 0; i < body.size(); i++) { - vector field = split(body[i], "=", false); - if (field.size() == 2) - req->setBodyParam(field[0], field[1], false); - else if (field.size() == 1) - req->setBodyParam(field[0], "", false); - else - throw Server::Exception("Invalid body"); - } - } else if (req->getHeader("Content-Type") - .substr(0, string("multipart/form-data").size()) == - "multipart/form-data") { - if (line == "--" + boundary || line == "--" + boundary + "--") { - lastFieldKey = ""; - lastFieldValue = ""; - shouldBeEmpty = false; - state = BODY_HEADER; - } - } else { - throw Server::Exception("Unsupported body type: " + - req->getHeader("Content-Type")); - } - } break; - case BODY_HEADER: { - if (line == "") { - state = BODY_BODY; - break; + if (str.length() > 0) + results.push_back(str); +} + +Request* parseRawReq(char* headersRaw, size_t length) { + Request* req = nullptr; + string boundary; + string lastFieldKey; + string lastFieldValue; + bool shouldBeEmpty; + try { + enum State { + REQ, + HEADER, + BODY, + BODY_HEADER, + BODY_BODY, + }; + State state = REQ; + vector headers = split(string(headersRaw), "\r\n", false); + for (size_t i = 0; i < length; i++) { + if (!headersRaw[i]) + throw Server::Exception("Unsupported binary data in request."); } - vector R = split(line, ": ", false); - if (R.size() != 2) - throw Server::Exception("Invalid header"); - if (toLowerCase(R[0]) == toLowerCase("Content-Disposition")) { - vector A = split(R[1], "; ", false); - for (size_t i = 0; i < A.size(); i++) { - vector attr = split(A[i], "=", false); - if (attr.size() == 2) { - if (toLowerCase(attr[0]) == toLowerCase("name")) { - lastFieldKey = attr[1].substr(1, attr[1].size() - 2); - } - } else if (attr.size() == 1) { - } else - throw Server::Exception("Invalid body attribute"); - } - } else if (toLowerCase(R[0]) == toLowerCase("Content-Type")) { - if (toLowerCase(R[1]) == toLowerCase("application/octet-stream")) - shouldBeEmpty = true; - else if (toLowerCase(R[1].substr(0, R[1].find("/"))) != - toLowerCase("text")) - throw Server::Exception("Unsupported file type: " + R[1]); + size_t realBodySize = + string(headersRaw).size() - + split(string(headersRaw), "\r\n\r\n", false)[0].size() - + string("\r\n\r\n").size(); + for (size_t headerIndex = 0; headerIndex < headers.size(); headerIndex++) { + string line = headers[headerIndex]; + switch (state) { + case REQ: { + vector R = split(line, " ", false); + if (R.size() != 3) { + throw Server::Exception("Invalid header (request line)"); + } + req = new Request(R[0]); + req->setPath(R[1]); + size_t pos = req->getPath().find('?'); + if (pos != string::npos && pos != req->getPath().size() - 1) { + vector Q1 = split(req->getPath().substr(pos + 1), "&", false); + for (vector::size_type q = 0; q < Q1.size(); q++) { + vector Q2 = split(Q1[q], "=", false); + if (Q2.size() == 2) + req->setQueryParam(Q2[0], Q2[1], false); + else + throw Server::Exception("Invalid query"); + } + } + req->setPath(req->getPath().substr(0, pos)); + state = HEADER; + } break; + case HEADER: { + if (line == "") { + state = BODY; + if (req->getHeader("Content-Type") + .substr(0, string("multipart/form-data").size()) == + "multipart/form-data") { + boundary = + req->getHeader("Content-Type") + .substr(req->getHeader("Content-Type").find("boundary=") + + string("boundary=").size()); + } + break; + } + vector R = split(line, ": ", false); + if (R.size() != 2) + throw Server::Exception("Invalid header"); + req->setHeader(R[0], R[1], false); + if (toLowerCase(R[0]) == toLowerCase("Content-Length")) + if (realBodySize != (size_t)atol(R[1].c_str())) + return NULL; + } break; + case BODY: { + if (req->getHeader("Content-Type") == "") { + } + else if (req->getHeader("Content-Type") == + "application/x-www-form-urlencoded") { + vector body = split(line, "&", false); + for (size_t i = 0; i < body.size(); i++) { + vector field = split(body[i], "=", false); + if (field.size() == 2) + req->setBodyParam(field[0], field[1], false); + else if (field.size() == 1) + req->setBodyParam(field[0], "", false); + else + throw Server::Exception("Invalid body"); + } + } + else if (req->getHeader("Content-Type") + .substr(0, string("multipart/form-data").size()) == + "multipart/form-data") { + if (line == "--" + boundary || line == "--" + boundary + "--") { + lastFieldKey = ""; + lastFieldValue = ""; + shouldBeEmpty = false; + state = BODY_HEADER; + } + } + else { + throw Server::Exception("Unsupported body type: " + + req->getHeader("Content-Type")); + } + } break; + case BODY_HEADER: { + if (line == "") { + state = BODY_BODY; + break; + } + vector R = split(line, ": ", false); + if (R.size() != 2) + throw Server::Exception("Invalid header"); + if (toLowerCase(R[0]) == toLowerCase("Content-Disposition")) { + vector A = split(R[1], "; ", false); + for (size_t i = 0; i < A.size(); i++) { + vector attr = split(A[i], "=", false); + if (attr.size() == 2) { + if (toLowerCase(attr[0]) == toLowerCase("name")) { + lastFieldKey = attr[1].substr(1, attr[1].size() - 2); + } + } + else if (attr.size() == 1) { + } + else + throw Server::Exception("Invalid body attribute"); + } + } + else if (toLowerCase(R[0]) == toLowerCase("Content-Type")) { + if (toLowerCase(R[1]) == toLowerCase("application/octet-stream")) + shouldBeEmpty = true; + else if (toLowerCase(R[1].substr(0, R[1].find("/"))) != + toLowerCase("text")) + throw Server::Exception("Unsupported file type: " + R[1]); + } + } break; + case BODY_BODY: { + if (line == "--" + boundary || line == "--" + boundary + "--") { + req->setBodyParam(lastFieldKey, + lastFieldValue.substr(string("\r\n").size()), + false); + lastFieldKey = ""; + lastFieldValue = ""; + state = BODY_HEADER; + shouldBeEmpty = false; + } + else if (shouldBeEmpty && !line.empty()) + throw Server::Exception("Unsupported file type: " + + string("application/octet-stream")); + else + lastFieldValue += "\r\n" + line; + } break; + } } - } break; - case BODY_BODY: { - if (line == "--" + boundary || line == "--" + boundary + "--") { - req->setBodyParam(lastFieldKey, - lastFieldValue.substr(string("\r\n").size()), - false); - lastFieldKey = ""; - lastFieldValue = ""; - state = BODY_HEADER; - shouldBeEmpty = false; - } else if (shouldBeEmpty && !line.empty()) - throw Server::Exception("Unsupported file type: " + - string("application/octet-stream")); - else - lastFieldValue += "\r\n" + line; - } break; - } } - } catch (const Server::Exception &) { - throw; - } catch (...) { - throw Server::Exception("Error on parsing request"); - } - return req; + catch (const Server::Exception&) { + throw; + } + catch (...) { + throw Server::Exception("Error on parsing request"); + } + return req; } Server::Server(int _port) : port(_port) { #ifdef _WIN32 - WSADATA wsa_data; - int initializeResult = WSAStartup(MAKEWORD(2, 2), &wsa_data); - if (initializeResult != 0) { - throw Exception("Error: WinSock WSAStartup failed: " + - string(getSocketError())); - } + WSADATA wsa_data; + int initializeResult = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (initializeResult != 0) { + throw Exception("Error: WinSock WSAStartup failed: " + + string(getSocketError())); + } #endif - notFoundHandler = new NotFoundHandler(); + notFoundHandler = new NotFoundHandler(); - sc = socket(AF_INET, SOCK_STREAM, 0); - int sc_option = 1; + sc = socket(AF_INET, SOCK_STREAM, 0); + int sc_option = 1; #ifdef _WIN32 - setsockopt(sc, SOL_SOCKET, SO_REUSEADDR, (char *)&sc_option, - sizeof(sc_option)); + setsockopt(sc, SOL_SOCKET, SO_REUSEADDR, (char*)&sc_option, + sizeof(sc_option)); #else - setsockopt(sc, SOL_SOCKET, SO_REUSEADDR, &sc_option, sizeof(sc_option)); + setsockopt(sc, SOL_SOCKET, SO_REUSEADDR, &sc_option, sizeof(sc_option)); #endif - if (!ISVALIDSOCKET(sc)) - throw Exception("Error on opening socket: " + string(getSocketError())); + if (!ISVALIDSOCKET(sc)) + throw Exception("Error on opening socket: " + string(getSocketError())); - struct sockaddr_in serv_addr; - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(port); + struct sockaddr_in serv_addr; + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); - if (::bind(sc, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) { - throw Exception("Error on binding: " + string(getSocketError())); - } + if (::bind(sc, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) { + throw Exception("Error on binding: " + string(getSocketError())); + } } -void Server::get(string path, RequestHandler *handler) { - Route *route = new Route(GET, path); - route->setHandler(handler); - routes.push_back(route); +void Server::get(string path, RequestHandler* handler) { + Route* route = new Route(GET, path); + route->setHandler(handler); + routes.push_back(route); } -void Server::post(string path, RequestHandler *handler) { - Route *route = new Route(POST, path); - route->setHandler(handler); - routes.push_back(route); +void Server::post(string path, RequestHandler* handler) { + Route* route = new Route(POST, path); + route->setHandler(handler); + routes.push_back(route); } void Server::run() { - ::listen(sc, 10); - - struct sockaddr_in cli_addr; - socklen_t clilen; - clilen = sizeof(cli_addr); - SOCKET newsc; - - while (true) { - newsc = ::accept(sc, (struct sockaddr *)&cli_addr, &clilen); - if (!ISVALIDSOCKET(newsc)) - throw Exception("Error on accept: " + string(getSocketError())); - Response *res = NULL; - try { - char *data = new char[BUFSIZE + 1]; - size_t recv_len, recv_total_len = 0; - Request *req = NULL; - while (!req) { - recv_len = - recv(newsc, data + recv_total_len, BUFSIZE - recv_total_len, 0); - if (recv_len > 0) { - recv_total_len += recv_len; - data[recv_total_len >= 0 ? recv_total_len : 0] = 0; - req = parseRawReq(data, recv_total_len); - } else - break; - } - delete[] data; - if (!recv_total_len) { - CLOSESOCKET(newsc); - continue; - } - req->log(); - size_t i = 0; - for (; i < routes.size(); i++) { - if (routes[i]->isMatch(req->getMethod(), req->getPath())) { - res = routes[i]->handle(req); - break; + ::listen(sc, 10); + + struct sockaddr_in cli_addr; + socklen_t clilen; + clilen = sizeof(cli_addr); + SOCKET newsc; + + while (true) { + newsc = ::accept(sc, (struct sockaddr*)&cli_addr, &clilen); + if (!ISVALIDSOCKET(newsc)) + throw Exception("Error on accept: " + string(getSocketError())); + Response* res = NULL; + try { + char* data = new char[BUFSIZE + 1]; + size_t recv_len, recv_total_len = 0; + Request* req = NULL; + while (!req) { + recv_len = + recv(newsc, data + recv_total_len, BUFSIZE - recv_total_len, 0); + if (recv_len > 0) { + recv_total_len += recv_len; + data[recv_total_len >= 0 ? recv_total_len : 0] = 0; + req = parseRawReq(data, recv_total_len); + } + else + break; + } + delete[] data; + if (!recv_total_len) { + CLOSESOCKET(newsc); + continue; + } + req->log(); + size_t i = 0; + for (; i < routes.size(); i++) { + if (routes[i]->isMatch(req->getMethod(), req->getPath())) { + res = routes[i]->handle(req); + break; + } + } + if (i == routes.size() && notFoundHandler) { + res = notFoundHandler->callback(req); + } + delete req; + } + catch (const Exception& exc) { + delete res; + res = ServerErrorHandler::callback(exc.getMessage()); } - } - if (i == routes.size() && notFoundHandler) { - res = notFoundHandler->callback(req); - } - delete req; - } catch (const Exception &exc) { - delete res; - res = ServerErrorHandler::callback(exc.getMessage()); + int si; + res->log(); + string res_data = res->print(si); + delete res; + int wr = send(newsc, res_data.c_str(), si, 0); + if (wr != si) + throw Exception("Send error: " + string(getSocketError())); + CLOSESOCKET(newsc); } - int si; - res->log(); - string res_data = res->print(si); - delete res; - int wr = send(newsc, res_data.c_str(), si, 0); - if (wr != si) - throw Exception("Send error: " + string(getSocketError())); - CLOSESOCKET(newsc); - } } Server::~Server() { - if (sc >= 0) - CLOSESOCKET(sc); - delete notFoundHandler; - for (size_t i = 0; i < routes.size(); ++i) - delete routes[i]; + if (sc >= 0) + CLOSESOCKET(sc); + delete notFoundHandler; + for (size_t i = 0; i < routes.size(); ++i) + delete routes[i]; #ifdef _WIN32 - WSACleanup(); + WSACleanup(); #endif } @@ -365,15 +382,15 @@ Server::Exception::Exception(const string msg) { message = msg; } string Server::Exception::getMessage() const { return message; } ShowFile::ShowFile(string _filePath, string _fileType) { - filePath = _filePath; - fileType = _fileType; + filePath = _filePath; + fileType = _fileType; } -Response *ShowFile::callback(Request *req) { - Response *res = new Response; - res->setHeader("Content-Type", fileType); - res->setBody(readFile(filePath.c_str())); - return res; +Response* ShowFile::callback(Request* req) { + Response* res = new Response; + res->setHeader("Content-Type", fileType); + res->setBody(readFile(filePath.c_str())); + return res; } ShowPage::ShowPage(string filePath) @@ -383,27 +400,27 @@ ShowImage::ShowImage(string filePath) : ShowFile(filePath, "image/" + getExtension(filePath)) {} void Server::setNotFoundErrPage(std::string notFoundErrPage) { - delete notFoundHandler; - notFoundHandler = new NotFoundHandler(notFoundErrPage); + delete notFoundHandler; + notFoundHandler = new NotFoundHandler(notFoundErrPage); } RequestHandler::~RequestHandler() {} TemplateHandler::TemplateHandler(string _filePath) { - filePath = _filePath; - parser = new TemplateParser(filePath); + filePath = _filePath; + parser = new TemplateParser(filePath); } -Response *TemplateHandler::callback(Request *req) { - map context; - context = this->handle(req); - Response *res = new Response; - res->setHeader("Content-Type", "text/html"); - res->setBody(parser->getHtml(context)); - return res; +Response* TemplateHandler::callback(Request* req) { + map context; + context = this->handle(req); + Response* res = new Response; + res->setHeader("Content-Type", "text/html"); + res->setBody(parser->getHtml(context)); + return res; } -map TemplateHandler::handle(Request *req) { - map context; - return context; +map TemplateHandler::handle(Request* req) { + map context; + return context; } diff --git a/server/server.hpp b/server/server.hpp index 507c098..fb01f8e 100644 --- a/server/server.hpp +++ b/server/server.hpp @@ -1,13 +1,15 @@ -#ifndef __SERVER__ -#define __SERVER__ +#ifndef SERVER_HPP_INCLUDE +#define SERVER_HPP_INCLUDE + +#include +#include +#include + #include "../utils/include.hpp" #include "../utils/request.hpp" #include "../utils/response.hpp" #include "../utils/template_parser.hpp" #include "route.hpp" -#include -#include -#include #ifdef _WIN32 typedef unsigned SOCKET; @@ -19,64 +21,63 @@ class TemplateParser; class RequestHandler { public: - virtual ~RequestHandler(); - virtual Response *callback(Request *req) = 0; + virtual ~RequestHandler(); + virtual Response* callback(Request* req) = 0; }; class ShowFile : public RequestHandler { - std::string filePath; - std::string fileType; + std::string filePath; + std::string fileType; public: - ShowFile(std::string filePath, std::string fileType); - Response *callback(Request *req); + ShowFile(std::string filePath, std::string fileType); + Response* callback(Request* req); }; class ShowPage : public ShowFile { - public: - ShowPage(std::string _filePath); + ShowPage(std::string _filePath); }; class ShowImage : public ShowFile { - public: - ShowImage(std::string _filePath); + ShowImage(std::string _filePath); }; class TemplateHandler : public RequestHandler { - std::string filePath; - TemplateParser *parser; + std::string filePath; + TemplateParser* parser; public: - TemplateHandler(std::string _filePath); - Response *callback(Request *req); - virtual std::map handle(Request *req); + TemplateHandler(std::string _filePath); + Response* callback(Request* req); + virtual std::map handle(Request* req); }; class Server { public: - Server(int port = 5000); - ~Server(); - void run(); - void get(std::string path, RequestHandler *handler); - void post(std::string path, RequestHandler *handler); - void setNotFoundErrPage(std::string); - - class Exception : public std::exception { - public: - Exception() {} - Exception(const std::string); - std::string getMessage() const; - - private: - std::string message; - }; + Server(int port = 5000); + ~Server(); + void run(); + void get(std::string path, RequestHandler* handler); + void post(std::string path, RequestHandler* handler); + void setNotFoundErrPage(std::string); + + class Exception : public std::exception { + public: + Exception() {} + Exception(const std::string); + std::string getMessage() const; + + private: + std::string message; + }; private: - SOCKET sc; - int port; - std::vector routes; - RequestHandler *notFoundHandler; + SOCKET sc; + int port; + std::vector routes; + RequestHandler* notFoundHandler; }; -#endif + +#endif // SERVER_HPP_INCLUDE diff --git a/utils/include.hpp b/utils/include.hpp index 087c7fd..af300e5 100644 --- a/utils/include.hpp +++ b/utils/include.hpp @@ -1,7 +1,11 @@ -#ifndef __INCLUDE__ -#define __INCLUDE__ +#ifndef INCLUDE_HPP_INCLUDE +#define INCLUDE_HPP_INCLUDE -#define BUFSIZE 4145152 -enum Method { GET, POST }; +constexpr int BUFSIZE = 10 * 1024 * 1024; // 10MB -#endif +enum Method { + GET, + POST +}; + +#endif // INCLUDE_HPP_INCLUDE diff --git a/utils/request.cpp b/utils/request.cpp index 611e182..8c87d09 100644 --- a/utils/request.cpp +++ b/utils/request.cpp @@ -1,17 +1,19 @@ #include "request.hpp" -#include "../utils/utilities.hpp" + #include #include #include #include +#include "../utils/utilities.hpp" + using namespace std; Request::Request(string _method) { - if (_method == "GET") - method = GET; - if (_method == "POST") - method = POST; + if (_method == "GET") + method = GET; + if (_method == "POST") + method = POST; } string Request::getQueryParam(string key) { return urlDecode(query[key]); } @@ -29,131 +31,131 @@ Method Request::getMethod() { return method; } void Request::setMethod(Method _method) { method = _method; } void Request::setQueryParam(string key, string value, bool encode) { - query[key] = encode ? urlEncode(value) : value; + query[key] = encode ? urlEncode(value) : value; } void Request::setBodyParam(string key, string value, bool encode) { - body[key] = encode ? urlEncode(value) : value; + body[key] = encode ? urlEncode(value) : value; } void Request::setHeader(string key, string value, bool encode) { - headers[key] = encode ? urlEncode(value) : value; + headers[key] = encode ? urlEncode(value) : value; } string Request::getBody() { - string bs = ""; - for (auto it = body.begin(); !body.empty() && it != body.end(); it++) - bs += it->first + "=" + it->second + "&"; - return bs; + string bs = ""; + for (auto it = body.begin(); !body.empty() && it != body.end(); it++) + bs += it->first + "=" + it->second + "&"; + return bs; } string Request::getSessionId() { - string cookie = getHeader("cookie"); - if (cookie == "") + string cookie = getHeader("cookie"); + if (cookie == "") + return ""; + vector v = split(cookie, ";"); + for (string kv : v) { + vector k = split(kv, "="); + if (k[0] == "sessionId") + return k[1]; + } return ""; - vector v = split(cookie, ";"); - for (string kv : v) { - vector k = split(kv, "="); - if (k[0] == "sessionId") - return k[1]; - } - return ""; } void Request::log() { - const string NC = "\033[0;39m"; - const string K = "\033[1m"; - const string H = "\033[33;1m"; - string log = ""; - log += H + string("------- Request --------") + NC + string("\n"); - log += - K + string("Method:\t") + NC + (method ? "POST" : "GET") + string("\n"); - log += K + string("Path:\t") + NC + path + string("\n"); - log += K + string("Headers:") + NC + string("\n"); - for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) - log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + + const string NC = "\033[0;39m"; + const string K = "\033[1m"; + const string H = "\033[33;1m"; + string log = ""; + log += H + string("------- Request --------") + NC + string("\n"); + log += + K + string("Method:\t") + NC + (method ? "POST" : "GET") + string("\n"); + log += K + string("Path:\t") + NC + path + string("\n"); + log += K + string("Headers:") + NC + string("\n"); + for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) + log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + + string("\n"); + log += "[ " + K + string("SessionId:\t") + NC + this->getSessionId() + " ]" + string("\n"); - log += "[ " + K + string("SessionId:\t") + NC + this->getSessionId() + " ]" + - string("\n"); - log += K + string("Query:") + NC + string("\n"); - for (auto it = query.begin(); !query.empty() && it != query.end(); it++) - log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + - string("\n"); - log += K + string("Body:") + NC + string("\n"); - for (auto it = body.begin(); !body.empty() && it != body.end(); it++) - log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + - string("\n"); - log += H + string("------------------------") + NC + string("\n"); - cerr << log << endl; + log += K + string("Query:") + NC + string("\n"); + for (auto it = query.begin(); !query.empty() && it != query.end(); it++) + log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + + string("\n"); + log += K + string("Body:") + NC + string("\n"); + for (auto it = body.begin(); !body.empty() && it != body.end(); it++) + log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + + string("\n"); + log += H + string("------------------------") + NC + string("\n"); + cerr << log << endl; } cimap Request::getHeaders() { - vector res; - for (map::iterator i = headers.begin(); - !headers.empty() && i != headers.end(); i++) { - res.push_back(i->first); - res.push_back(i->second); - } - return headers; + vector res; + for (map::iterator i = headers.begin(); + !headers.empty() && i != headers.end(); i++) { + res.push_back(i->first); + res.push_back(i->second); + } + return headers; } string Request::getQueryString() { - if (query.empty()) - return ""; - string res = "?"; - for (map::iterator i = query.begin(); - !query.empty() && i != query.end(); i++) { - res += i->first; - res += "="; - res += i->second; - res += "&"; - } - return res; + if (query.empty()) + return ""; + string res = "?"; + for (map::iterator i = query.begin(); + !query.empty() && i != query.end(); i++) { + res += i->first; + res += "="; + res += i->second; + res += "&"; + } + return res; } string Request::getHeadersString() { - string headerString = ""; - for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) - headerString += it->first + "=" + it->second + "&"; - return headerString; + string headerString = ""; + for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) + headerString += it->first + "=" + it->second + "&"; + return headerString; } void Request::setHeaders(string _headers) { - headers = getCimapFromString(_headers); + headers = getCimapFromString(_headers); } void Request::setQuery(std::string _query) { - _query = _query.substr(1); - query = getCimapFromString(_query); + _query = _query.substr(1); + query = getCimapFromString(_query); } void Request::setBody(std::string _body) { body = getCimapFromString(_body); } -void Request::serializeToFile(Request *req, string filePath) { - string reqString = to_string(req->getMethod()); - reqString += "\n"; - reqString += req->getPath(); - reqString += "\n"; - reqString += req->getHeadersString(); - reqString += "\n"; - reqString += req->getBody(); - reqString += "\n"; - reqString += req->getQueryString(); - writeToFile(reqString, filePath); +void Request::serializeToFile(Request* req, string filePath) { + string reqString = to_string(req->getMethod()); + reqString += "\n"; + reqString += req->getPath(); + reqString += "\n"; + reqString += req->getHeadersString(); + reqString += "\n"; + reqString += req->getBody(); + reqString += "\n"; + reqString += req->getQueryString(); + writeToFile(reqString, filePath); } -void Request::deserializeFromFile(Request *req, string filePath) { - vector fields = tokenize(readFile(filePath), '\n'); - switch (fields.size()) { - case 5: - req->setQuery(fields[4]); - case 4: - req->setBody(fields[3]); - case 3: - req->setHeaders(fields[2]); - case 2: - req->setPath(fields[1]); - case 1: - req->setMethod(stoi(fields[0]) == GET ? GET : POST); - } -} \ No newline at end of file +void Request::deserializeFromFile(Request* req, string filePath) { + vector fields = tokenize(readFile(filePath), '\n'); + switch (fields.size()) { + case 5: + req->setQuery(fields[4]); + case 4: + req->setBody(fields[3]); + case 3: + req->setHeaders(fields[2]); + case 2: + req->setPath(fields[1]); + case 1: + req->setMethod(stoi(fields[0]) == GET ? GET : POST); + } +} diff --git a/utils/request.hpp b/utils/request.hpp index 081eed0..9361113 100644 --- a/utils/request.hpp +++ b/utils/request.hpp @@ -1,40 +1,43 @@ -#ifndef __REQUEST__ -#define __REQUEST__ +#ifndef REQUEST_HPP_INCLUDE +#define REQUEST_HPP_INCLUDE + +#include + #include "../utils/include.hpp" #include "../utils/utilities.hpp" -#include class Request { public: - Request(std::string method = "GET"); - std::string getPath(); - void setPath(std::string); - Method getMethod(); - void setMethod(Method); - std::string getQueryParam(std::string key); - void setQueryParam(std::string key, std::string value, bool encode = true); - std::string getBodyParam(std::string key); - void setBodyParam(std::string key, std::string value, bool encode = true); - std::string getHeader(std::string key); - void setHeader(std::string key, std::string value, bool encode = true); - std::string getBody(); - std::string getSessionId(); - void setSessionId(std::string); - std::string getQueryString(); - cimap getHeaders(); - std::string getHeadersString(); - void setHeaders(std::string); - void setQuery(std::string); - void setBody(std::string); - void log(); - static void serializeToFile(Request *req, std::string filePath); - static void deserializeFromFile(Request *req, std::string filePath); + Request(std::string method = "GET"); + std::string getPath(); + void setPath(std::string); + Method getMethod(); + void setMethod(Method); + std::string getQueryParam(std::string key); + void setQueryParam(std::string key, std::string value, bool encode = true); + std::string getBodyParam(std::string key); + void setBodyParam(std::string key, std::string value, bool encode = true); + std::string getHeader(std::string key); + void setHeader(std::string key, std::string value, bool encode = true); + std::string getBody(); + std::string getSessionId(); + void setSessionId(std::string); + std::string getQueryString(); + cimap getHeaders(); + std::string getHeadersString(); + void setHeaders(std::string); + void setQuery(std::string); + void setBody(std::string); + void log(); + static void serializeToFile(Request* req, std::string filePath); + static void deserializeFromFile(Request* req, std::string filePath); private: - std::string path; - Method method; - cimap headers; - cimap query; - cimap body; + std::string path; + Method method; + cimap headers; + cimap query; + cimap body; }; -#endif + +#endif // REQUEST_HPP_INCLUDE diff --git a/utils/response.cpp b/utils/response.cpp index f332dcf..bf21039 100644 --- a/utils/response.cpp +++ b/utils/response.cpp @@ -1,4 +1,5 @@ #include "response.hpp" + #include #include #include @@ -6,19 +7,19 @@ using namespace std; map getHttpPhrases() { - map httpPhrase; - httpPhrase[200] = "OK"; - httpPhrase[303] = "See Other"; - httpPhrase[404] = "Not Found"; - return httpPhrase; + map httpPhrase; + httpPhrase[200] = "OK"; + httpPhrase[303] = "See Other"; + httpPhrase[404] = "Not Found"; + return httpPhrase; } map httpPhrase = getHttpPhrases(); Response::Response(int code) { - this->code = code; - this->phrase = httpPhrase[code]; - this->headers["Content-Type"] = "text/plain"; + this->code = code; + this->phrase = httpPhrase[code]; + this->headers["Content-Type"] = "text/plain"; } int Response::getStatusCode() { return code; } @@ -26,42 +27,42 @@ int Response::getStatusCode() { return code; } string Response::getStatusPhrase() { return phrase; } void Response::setStatus(int _code, string _phrase) { - phrase = _phrase; - code = _code; + phrase = _phrase; + code = _code; } void Response::setStatus(int _code) { setStatus(_code, httpPhrase[_code]); } -string Response::print(int &size) { - string header = ""; - header += "HTTP/1.0 " + to_string(code) + " " + phrase + "\r\n"; - header += "Server: " + SERVER_NAME + " \r\n"; - header += "Content-Length: " + to_string(body.size()) + "\r\n"; - for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) - header += it->first + ": " + it->second + "\r\n"; - header += "\r\n"; - size = header.size() + body.size(); - return header + body; +string Response::print(int& size) { + string header = ""; + header += "HTTP/1.0 " + to_string(code) + " " + phrase + "\r\n"; + header += "Server: " + SERVER_NAME + " \r\n"; + header += "Content-Length: " + to_string(body.size()) + "\r\n"; + for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) + header += it->first + ": " + it->second + "\r\n"; + header += "\r\n"; + size = header.size() + body.size(); + return header + body; } void Response::log(bool showBody) { - const string NC = "\033[0;39m"; - const string K = "\033[1m"; - const string H = "\033[34;1m"; - const string G = "\033[32m"; - const string R = "\033[31m"; - string log = ""; - log += H + string("------- Response -------") + NC + string("\n"); - log += K + string("Status:\t") + NC + (code == 200 ? G : R) + - to_string(code) + " " + phrase + NC + string("\n"); - log += K + string("Headers:") + NC + string("\n"); - for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) - log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + - string("\n"); - if (showBody) - log += K + string("Body:\n") + NC + body + string("\n"); - log += H + string("------------------------") + NC + string("\n"); - cerr << log << endl; + const string NC = "\033[0;39m"; + const string K = "\033[1m"; + const string H = "\033[34;1m"; + const string G = "\033[32m"; + const string R = "\033[31m"; + string log = ""; + log += H + string("------- Response -------") + NC + string("\n"); + log += K + string("Status:\t") + NC + (code == 200 ? G : R) + + to_string(code) + " " + phrase + NC + string("\n"); + log += K + string("Headers:") + NC + string("\n"); + for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) + log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + + string("\n"); + if (showBody) + log += K + string("Body:\n") + NC + body + string("\n"); + log += H + string("------------------------") + NC + string("\n"); + cerr << log << endl; } void Response::setHeader(string name, string value) { headers[name] = value; } @@ -71,11 +72,11 @@ void Response::setBody(string _body) { body = _body; } string Response::getHeader(string name) { return ""; } void Response::setSessionId(string sessionId) { - setHeader("set-cookie", "sessionId=" + sessionId + ";"); + setHeader("set-cookie", "sessionId=" + sessionId + ";"); } -Response *Response::redirect(string url) { - Response *res = new Response(303); - res->setHeader("Location", url); - return res; +Response* Response::redirect(string url) { + Response* res = new Response(303); + res->setHeader("Location", url); + return res; } diff --git a/utils/response.hpp b/utils/response.hpp index ff71ca1..af51458 100644 --- a/utils/response.hpp +++ b/utils/response.hpp @@ -1,5 +1,6 @@ -#ifndef __RESPONSE__ -#define __RESPONSE__ +#ifndef RESPONSE_HPP_INCLUDE +#define RESPONSE_HPP_INCLUDE + #include "../utils/include.hpp" #include "../utils/utilities.hpp" #include @@ -29,4 +30,4 @@ class Response { cimap headers; }; -#endif +#endif // RESPONSE_HPP_INCLUDE diff --git a/utils/template_parser.cpp b/utils/template_parser.cpp index bb7db11..e5ef768 100644 --- a/utils/template_parser.cpp +++ b/utils/template_parser.cpp @@ -1,206 +1,206 @@ #include "template_parser.hpp" + #include using namespace std; -static std::string mkdirNoErrors(const std::string &dirName) { +static std::string mkdirNoErrors(const std::string& dirName) { // do not error if dir already exists (-p flag on linux) #ifdef _WIN32 - return ("(if not exist \"" + dirName + "\" " + SysCmd::mkdir + "\"" + - dirName + "\")"); + return ("(if not exist \"" + dirName + "\" " + SysCmd::mkdir + "\"" + + dirName + "\")"); #else - return (SysCmd::mkdir + "-p \"" + dirName + "\""); + return (SysCmd::mkdir + "-p \"" + dirName + "\""); #endif } int TemplateParser::lastParserNum = 0; const std::string localTemplate(const int parserNum) { - return "local" + std::to_string(parserNum) + ".html"; + return "local" + std::to_string(parserNum) + ".html"; } TemplateParser::TemplateParser(string _filePath) { - filePath = _filePath; - variableCount = 0; - programName = - to_string(TemplateParser::lastParserNum) + SysCmd::fileExtention; - parserNum = TemplateParser::lastParserNum++; - code = ""; - parseTemplate(); - makeExecutableTemplate(); + filePath = _filePath; + variableCount = 0; + programName = + to_string(TemplateParser::lastParserNum) + SysCmd::fileExtention; + parserNum = TemplateParser::lastParserNum++; + code = ""; + parseTemplate(); + makeExecutableTemplate(); } string TemplateParser::getHtml(map _context) { - TemplateUtils::writeMapToFile(outputFolder + "/" + mapFile, &_context); - return runGeneratedCode(); + TemplateUtils::writeMapToFile(outputFolder + "/" + mapFile, &_context); + return runGeneratedCode(); } void TemplateParser::parseTemplate() { - string unparsedTemplate = readFile(filePath); - int parsePointer = 0; - while (parsePointer < (signed int)unparsedTemplate.size()) { - int begin = findBeginOfCodeBlock(parsePointer, unparsedTemplate); - int end = findEndOfCodeBlock(parsePointer, unparsedTemplate); - if (begin < 0) - break; - appendHTMLToCode(parsePointer, begin, unparsedTemplate); - appendCodeBlockToCode(begin, end, unparsedTemplate); - parsePointer = end + endCodeBlockTag.size(); - } - appendHTMLToCode(parsePointer, unparsedTemplate.size(), unparsedTemplate); + string unparsedTemplate = readFile(filePath); + int parsePointer = 0; + while (parsePointer < (signed int)unparsedTemplate.size()) { + int begin = findBeginOfCodeBlock(parsePointer, unparsedTemplate); + int end = findEndOfCodeBlock(parsePointer, unparsedTemplate); + if (begin < 0) + break; + appendHTMLToCode(parsePointer, begin, unparsedTemplate); + appendCodeBlockToCode(begin, end, unparsedTemplate); + parsePointer = end + endCodeBlockTag.size(); + } + appendHTMLToCode(parsePointer, unparsedTemplate.size(), unparsedTemplate); } int TemplateParser::findBeginOfCodeBlock(int startPosition, - string &unparsedTemplate) { - return findSubStrPosition(unparsedTemplate, beginCodeBlockTag, startPosition); + string& unparsedTemplate) { + return findSubStrPosition(unparsedTemplate, beginCodeBlockTag, startPosition); } int TemplateParser::findEndOfCodeBlock(int startPosition, - string &unparsedTemplate) { - return findSubStrPosition(unparsedTemplate, endCodeBlockTag, startPosition); + string& unparsedTemplate) { + return findSubStrPosition(unparsedTemplate, endCodeBlockTag, startPosition); } void TemplateParser::appendHTMLToCode(int begin, int end, - string const &unparsedTemplate) { - code += "\nstring __variable" + to_string(variableCount) + ";"; - code += "\n__variable" + to_string(variableCount) + - " = __unparsedTemplate__.substr("; - code += to_string(begin) + ", " + to_string(end - begin) + ");"; - code += "\ncout << __variable" + to_string(variableCount) + ";"; - variableCount++; + string const& unparsedTemplate) { + code += "\nstring __variable" + to_string(variableCount) + ";"; + code += "\n__variable" + to_string(variableCount) + + " = __unparsedTemplate__.substr("; + code += to_string(begin) + ", " + to_string(end - begin) + ");"; + code += "\ncout << __variable" + to_string(variableCount) + ";"; + variableCount++; } void TemplateParser::appendCodeBlockToCode(int begin, int end, - string &unparsedTemplate) { - if (end <= begin || begin < 0) - throw Server::Exception("Can not parse template " + filePath); - int codeBlockSize = end - begin - beginCodeBlockTag.size(); - code += - unparsedTemplate.substr(begin + beginCodeBlockTag.size(), codeBlockSize); + string& unparsedTemplate) { + if (end <= begin || begin < 0) + throw Server::Exception("Can not parse template " + filePath); + int codeBlockSize = end - begin - beginCodeBlockTag.size(); + code += + unparsedTemplate.substr(begin + beginCodeBlockTag.size(), codeBlockSize); } void TemplateParser::makeExecutableTemplate() { - generateCode(); - compileCode(); - makeLocalTemplate(); + generateCode(); + compileCode(); + makeLocalTemplate(); } void TemplateParser::makeLocalTemplate() { - string templateContent = readFile(filePath); - if (writeToFile(templateContent, - outputFolder + "/" + localTemplate(parserNum)) < 0) - throw Server::Exception("Can not write template to local " + outputFolder + - "folder"); + string templateContent = readFile(filePath); + if (writeToFile(templateContent, + outputFolder + "/" + localTemplate(parserNum)) < 0) + throw Server::Exception("Can not write template to local " + outputFolder + + "folder"); } void TemplateParser::generateCode() { - addReadFromTemplateToCode(); - addContextMapToCode(); - addIncludesToCode(); - addReturnToCode(); + addReadFromTemplateToCode(); + addContextMapToCode(); + addIncludesToCode(); + addReturnToCode(); } void TemplateParser::compileCode() { - if (writeToFile(code, toCompileFile) < 0) - throw Server::Exception("Can not write generated template code!"); + if (writeToFile(code, toCompileFile) < 0) + throw Server::Exception("Can not write generated template code!"); - string cmd = mkdirNoErrors(outputFolder) + " && " + cc + " " + toCompileFile + - " " + utilitiesPath + " -o " + outputFolder + SysCmd::slash + - programName + "&& " + SysCmd::rm + toCompileFile; - string error = "Can not compile template " + filePath; - TemplateUtils::runSystemCommand(cmd, error); + string cmd = mkdirNoErrors(outputFolder) + " && " + cc + " " + toCompileFile + + " " + utilitiesPath + " -o " + outputFolder + SysCmd::slash + + programName + "&& " + SysCmd::rm + toCompileFile; + string error = "Can not compile template " + filePath; + TemplateUtils::runSystemCommand(cmd, error); } string TemplateParser::runGeneratedCode() { + string cmd = SysCmd::programStart + outputFolder + SysCmd::slash + + programName + " " + " > " + staticTemplate; + string error = "Error in running template " + filePath; + TemplateUtils::runSystemCommand(cmd, error); - string cmd = SysCmd::programStart + outputFolder + SysCmd::slash + - programName + " " + " > " + staticTemplate; - string error = "Error in running template " + filePath; - TemplateUtils::runSystemCommand(cmd, error); - - string html = readFile(staticTemplate); + string html = readFile(staticTemplate); - cmd = SysCmd::rm + staticTemplate; - error = "Error in deleting static template for " + filePath; - TemplateUtils::runSystemCommand(cmd, error); + cmd = SysCmd::rm + staticTemplate; + error = "Error in deleting static template for " + filePath; + TemplateUtils::runSystemCommand(cmd, error); - return html; + return html; } void TemplateParser::addIncludesToCode() { - string include = "#include \n"; - include += "#include \n"; - include += "#include \n"; - include += "#include \n"; - include += "#include \"" + utilitiesHeaderPath + "\"\n"; - include += "using namespace std;\n"; - code = include + "int main(int argc, char const *argv[])\n{\n" + code + "\n"; + string include = "#include \n"; + include += "#include \n"; + include += "#include \n"; + include += "#include \n"; + include += "#include \"" + utilitiesHeaderPath + "\"\n"; + include += "using namespace std;\n"; + code = include + "int main(int argc, char const *argv[])\n{\n" + code + "\n"; } void TemplateParser::addReadFromTemplateToCode() { - code = "string __unparsedTemplate__ = readFile(\"" + outputFolder + "/" + - localTemplate(parserNum) + "\");\n" + code; + code = "string __unparsedTemplate__ = readFile(\"" + outputFolder + "/" + + localTemplate(parserNum) + "\");\n" + code; } void TemplateParser::addReturnToCode() { code += "return 0;\n}\n"; } void TemplateParser::addContextMapToCode() { - string mapCode = "std::map context;\n"; - // `mapFile` should be changed if we want to handle requests - // in a multi-thread non-blocking way - mapCode += - "readMapFromFile(\"" + outputFolder + "/" + mapFile + "\", &context);\n"; - code = mapCode + code; + string mapCode = "std::map context;\n"; + // `mapFile` should be changed if we want to handle requests + // in a multi-thread non-blocking way + mapCode += + "readMapFromFile(\"" + outputFolder + "/" + mapFile + "\", &context);\n"; + code = mapCode + code; } TemplateParser::~TemplateParser() { - deleteExecutable(); - deleteLocalTemplate(); + deleteExecutable(); + deleteLocalTemplate(); } void TemplateParser::deleteExecutable() { - string cmd = SysCmd::rm + outputFolder + SysCmd::slash + programName; - string error = "Error in deleting executable file at " + outputFolder + "/" + - programName; - TemplateUtils::runSystemCommand(cmd, error); + string cmd = SysCmd::rm + outputFolder + SysCmd::slash + programName; + string error = "Error in deleting executable file at " + outputFolder + "/" + + programName; + TemplateUtils::runSystemCommand(cmd, error); } void TemplateParser::deleteLocalTemplate() { - string cmd = - SysCmd::rm + outputFolder + SysCmd::slash + localTemplate(parserNum); - string error = "Error in deleting local template at " + outputFolder + "/" + - localTemplate(parserNum); - TemplateUtils::runSystemCommand(cmd, error); + string cmd = + SysCmd::rm + outputFolder + SysCmd::slash + localTemplate(parserNum); + string error = "Error in deleting local template at " + outputFolder + "/" + + localTemplate(parserNum); + TemplateUtils::runSystemCommand(cmd, error); } void TemplateParser::TemplateUtils::runSystemCommand(string command, string error) { - int ret = system(command.c_str()); + int ret = system(command.c_str()); #ifdef _WIN32 - if (ret != 0) { - throw Server::Exception(error); - } + if (ret != 0) { + throw Server::Exception(error); + } #else - if (WEXITSTATUS(ret) != EXIT_SUCCESS) { - throw Server::Exception(error); - } + if (WEXITSTATUS(ret) != EXIT_SUCCESS) { + throw Server::Exception(error); + } #endif } int TemplateParser::TemplateUtils::writeMapToFile( - std::string fname, std::map *m) { - int count = 0; - if (m->empty()) - return 0; - - FILE *fp = fopen(fname.c_str(), "w"); - if (!fp) - return -errno; - - for (std::map::iterator it = m->begin(); - it != m->end(); it++) { - fprintf(fp, "%s=%s\n", it->first.c_str(), it->second.c_str()); - count++; - } - - fclose(fp); - return count; -} \ No newline at end of file + std::string fname, std::map* m) { + int count = 0; + if (m->empty()) + return 0; + + FILE* fp = fopen(fname.c_str(), "w"); + if (!fp) + return -errno; + + for (std::map::iterator it = m->begin(); + it != m->end(); it++) { + fprintf(fp, "%s=%s\n", it->first.c_str(), it->second.c_str()); + count++; + } + + fclose(fp); + return count; +} diff --git a/utils/template_parser.hpp b/utils/template_parser.hpp index f4bce49..34426bf 100644 --- a/utils/template_parser.hpp +++ b/utils/template_parser.hpp @@ -1,13 +1,15 @@ -#ifndef __TEMPLATE_PARSER__ -#define __TEMPLATE_PARSER__ -#include "../server/server.hpp" -#include "../utils/request.hpp" -#include "../utils/utilities.hpp" +#ifndef TEMPLATE_PARSER_HPP_INCLUDE +#define TEMPLATE_PARSER_HPP_INCLUDE + #include #include #include #include +#include "../server/server.hpp" +#include "../utils/request.hpp" +#include "../utils/utilities.hpp" + namespace SysCmd { #ifdef _WIN32 const std::string rm = "del "; @@ -38,42 +40,42 @@ const std::string localTemplate(const int parserNum); class TemplateParser { private: - static int lastParserNum; - int parserNum; - std::string filePath; - std::string code; - std::map context; - int variableCount; - std::string html; - std::string programName; + static int lastParserNum; + int parserNum; + std::string filePath; + std::string code; + std::map context; + int variableCount; + std::string html; + std::string programName; - void parseTemplate(); - int findBeginOfCodeBlock(int startPosition, std::string &unparsedTemplate); - int findEndOfCodeBlock(int startPosition, std::string &unparsedTemplate); - void appendHTMLToCode(int begin, int end, std::string const &html); - void appendCodeBlockToCode(int begin, int end, std::string &unparsedTemplate); - void generateCode(); - void addIncludesToCode(); - void addReadFromTemplateToCode(); - void addReturnToCode(); - void addContextMapToCode(); - std::string runGeneratedCode(); - void makeExecutableTemplate(); - void makeLocalTemplate(); - void compileCode(); - void deleteExecutable(); - void deleteLocalTemplate(); - class TemplateUtils { - public: - static void runSystemCommand(std::string command, std::string errorMessage); - static int writeMapToFile(std::string fname, - std::map *m); - }; + void parseTemplate(); + int findBeginOfCodeBlock(int startPosition, std::string& unparsedTemplate); + int findEndOfCodeBlock(int startPosition, std::string& unparsedTemplate); + void appendHTMLToCode(int begin, int end, std::string const& html); + void appendCodeBlockToCode(int begin, int end, std::string& unparsedTemplate); + void generateCode(); + void addIncludesToCode(); + void addReadFromTemplateToCode(); + void addReturnToCode(); + void addContextMapToCode(); + std::string runGeneratedCode(); + void makeExecutableTemplate(); + void makeLocalTemplate(); + void compileCode(); + void deleteExecutable(); + void deleteLocalTemplate(); + class TemplateUtils { + public: + static void runSystemCommand(std::string command, std::string errorMessage); + static int writeMapToFile(std::string fname, + std::map* m); + }; public: - TemplateParser(std::string _filePath); - ~TemplateParser(); - std::string getHtml(std::map _context); + TemplateParser(std::string _filePath); + ~TemplateParser(); + std::string getHtml(std::map _context); }; -#endif \ No newline at end of file +#endif // TEMPLATE_PARSER_HPP_INCLUDE diff --git a/utils/utilities.cpp b/utils/utilities.cpp index e80f0bb..c97c453 100644 --- a/utils/utilities.cpp +++ b/utils/utilities.cpp @@ -1,4 +1,5 @@ #include "utilities.hpp" + #include #include #include @@ -8,220 +9,215 @@ using namespace std; -char easyToLowerCase(char in) { - if (in <= 'Z' && in >= 'A') - return in - ('Z' - 'z'); - return in; -} - string toLowerCase(string s) { - transform(s.begin(), s.end(), s.begin(), easyToLowerCase); - return s; + transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + return s; } -bool comp::operator()(const string &lhs, const string &rhs) const { - return toLowerCase(lhs) < toLowerCase(rhs); +bool comp::operator()(const string& lhs, const string& rhs) const { + return toLowerCase(lhs) < toLowerCase(rhs); } -string readFile(const char *filename) { - ifstream infile; - infile.open(filename, infile.binary); - if (!infile.is_open()) - return string(); +string readFile(const char* filename) { + ifstream infile; + infile.open(filename, ios_base::binary); + if (!infile.is_open()) + return string(); - infile.seekg(0, infile.end); - size_t length = infile.tellg(); - infile.seekg(0, infile.beg); + infile.seekg(0, infile.end); + size_t length = infile.tellg(); + infile.seekg(0, infile.beg); - if (length > BUFFER_SIZE) - length = BUFFER_SIZE; - char *buffer = new char[length + 1]; + if (length > BUFFER_SIZE) + length = BUFFER_SIZE; + char* buffer = new char[length + 1]; - infile.read(buffer, length); + infile.read(buffer, length); - string s(buffer, length); - delete[] buffer; - return s; + string s(buffer, length); + delete[] buffer; + return s; } string readFile(string filename) { return readFile(filename.c_str()); } vector split(string s, string delimiter, bool trim) { - vector tokens; - if (trim) - s.erase(remove(s.begin(), s.end(), ' '), s.end()); - size_t pos = 0; - string token; - while ((pos = s.find(delimiter)) != string::npos) { - token = s.substr(0, pos); - tokens.push_back(token); - s.erase(0, pos + delimiter.length()); - } - tokens.push_back(s); - return tokens; + vector tokens; + if (trim) + s.erase(remove(s.begin(), s.end(), ' '), s.end()); + size_t pos = 0; + string token; + while ((pos = s.find(delimiter)) != string::npos) { + token = s.substr(0, pos); + tokens.push_back(token); + s.erase(0, pos + delimiter.length()); + } + tokens.push_back(s); + return tokens; } void printVector(vector v) { - for (string s : v) - cout << s << endl; + for (string s : v) + cout << s << endl; } -string urlEncode(string const &str) { - char encode_buf[4]; - string result; - encode_buf[0] = '%'; - result.reserve(str.size()); - - // character selection for this algorithm is based on the following url: - // http://www.blooberry.com/indexdot/html/topics/urlencoding.htm - - for (size_t pos = 0; pos < str.size(); ++pos) { - switch (str[pos]) { - default: - if (str[pos] >= 32 && str[pos] < 127) { - // character does not need to be escaped - result += str[pos]; - break; - } - // else pass through to next case - case '$': - case '&': - case '+': - case ',': - case '/': - case ':': - case ';': - case '=': - case '?': - case '@': - case '"': - case '<': - case '>': - case '#': - case '%': - case '{': - case '}': - case '|': - case '\\': - case '^': - case '~': - case '[': - case ']': - case '`': - // the character needs to be encoded - sprintf(encode_buf + 1, "%02X", str[pos]); - result += encode_buf; - break; - } - }; - return result; +string urlEncode(string const& str) { + char encode_buf[4]; + string result; + encode_buf[0] = '%'; + result.reserve(str.size()); + + // character selection for this algorithm is based on the following url: + // http://www.blooberry.com/indexdot/html/topics/urlencoding.htm + + for (size_t pos = 0; pos < str.size(); ++pos) { + switch (str[pos]) { + default: + if (str[pos] >= 32 && str[pos] < 127) { + // character does not need to be escaped + result += str[pos]; + break; + } + // else pass through to next case + case '$': + case '&': + case '+': + case ',': + case '/': + case ':': + case ';': + case '=': + case '?': + case '@': + case '"': + case '<': + case '>': + case '#': + case '%': + case '{': + case '}': + case '|': + case '\\': + case '^': + case '~': + case '[': + case ']': + case '`': + // the character needs to be encoded + sprintf(encode_buf + 1, "%02X", str[pos]); + result += encode_buf; + break; + } + }; + return result; } -string urlDecode(string const &str) { - char decode_buf[3]; - string result; - result.reserve(str.size()); - - for (size_t pos = 0; pos < str.size(); ++pos) { - switch (str[pos]) { - case '+': - // convert to space character - result += ' '; - break; - case '%': - // decode hexidecimal value - if (pos + 2 < str.size()) { - decode_buf[0] = str[++pos]; - decode_buf[1] = str[++pos]; - decode_buf[2] = '\0'; - result += static_cast(strtol(decode_buf, nullptr, 16)); - } else { - // recover from error by not decoding character - result += '%'; - } - break; - default: - // character does not need to be escaped - result += str[pos]; +string urlDecode(string const& str) { + char decode_buf[3]; + string result; + result.reserve(str.size()); + + for (size_t pos = 0; pos < str.size(); ++pos) { + switch (str[pos]) { + case '+': + // convert to space character + result += ' '; + break; + case '%': + // decode hexidecimal value + if (pos + 2 < str.size()) { + decode_buf[0] = str[++pos]; + decode_buf[1] = str[++pos]; + decode_buf[2] = '\0'; + result += static_cast(strtol(decode_buf, nullptr, 16)); + } + else { + // recover from error by not decoding character + result += '%'; + } + break; + default: + // character does not need to be escaped + result += str[pos]; + } } - } - return result; + return result; } string getExtension(string filePath) { - size_t pos = filePath.find_last_of("."); - return filePath.substr(pos != string::npos ? pos + 1 : filePath.size()); + size_t pos = filePath.find_last_of("."); + return filePath.substr(pos != string::npos ? pos + 1 : filePath.size()); } -vector tokenize(const string &cnt, char delimiter) { - vector res; - istringstream is(cnt); - string part; - while (getline(is, part, delimiter)) - res.push_back(part); - return res; +vector tokenize(const string& cnt, char delimiter) { + vector res; + istringstream is(cnt); + string part; + while (getline(is, part, delimiter)) + res.push_back(part); + return res; } -void replaceAll(std::string &str, const std::string &from, - const std::string &to) { - if (from.empty()) - return; - size_t start_pos = 0; - while ((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); // In case 'to' contains 'from', like replacing - // 'x' with 'yx' - } +void replaceAll(std::string& str, const std::string& from, + const std::string& to) { + if (from.empty()) + return; + size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); // In case 'to' contains 'from', like replacing + // 'x' with 'yx' + } } -int findSubStrPosition(std::string &str, std::string const &subStr, - int const &pos) { - size_t found = str.find(subStr, pos); - if (found == string::npos) - return -1; - return found; +int findSubStrPosition(std::string& str, std::string const& subStr, + int const& pos) { + size_t found = str.find(subStr, pos); + if (found == string::npos) + return -1; + return found; } -int writeObjectToFile(const char *object, int size, - std::string const &filePath) { - ofstream file; - file.open(filePath, fstream::binary); - if (!file.is_open()) - return -1; - file.write(object, size); - file.close(); - return sizeof(object); +int writeObjectToFile(const char* object, int size, + std::string const& filePath) { + ofstream file; + file.open(filePath, fstream::binary); + if (!file.is_open()) + return -1; + file.write(object, size); + file.close(); + return sizeof(object); } -int writeToFile(std::string const &str, std::string const &filePath) { - return writeObjectToFile(str.c_str(), str.length(), filePath); +int writeToFile(std::string const& str, std::string const& filePath) { + return writeObjectToFile(str.c_str(), str.length(), filePath); } cimap getCimapFromString(std::string str) { - cimap m; - vector tokenized = tokenize(str, '&'); - for (auto token : tokenized) { - vector keyValue = tokenize(token, '='); - if (keyValue.size() != 2) - continue; - string key = keyValue[0]; - string value = keyValue[1]; - m[key] = value; - } - return m; + cimap m; + vector tokenized = tokenize(str, '&'); + for (auto token : tokenized) { + vector keyValue = tokenize(token, '='); + if (keyValue.size() != 2) + continue; + string key = keyValue[0]; + string value = keyValue[1]; + m[key] = value; + } + return m; +} +int readMapFromFile(std::string fname, std::map* m) { + std::ifstream inputStream(fname); + if (!inputStream.is_open()) + return -errno; + + std::string line; + while (std::getline(inputStream, line)) { + auto tokens = tokenize(line, '='); + // KEY VALUE + (*m)[tokens[0]] = tokens[(tokens.size() < 2) ? 0 : 1]; + } + + inputStream.close(); + return (*m).size(); } -int readMapFromFile(std::string fname, std::map *m) { - std::ifstream inputStream(fname); - if (!inputStream.is_open()) - return -errno; - - std::string line; - while (std::getline(inputStream, line)) { - auto tokens = tokenize(line, '='); - // KEY VALUE - (*m)[tokens[0]] = tokens[(tokens.size() < 2) ? 0 : 1]; - } - - inputStream.close(); - return (*m).size(); -} \ No newline at end of file diff --git a/utils/utilities.hpp b/utils/utilities.hpp index 8fe5bee..3d7d729 100644 --- a/utils/utilities.hpp +++ b/utils/utilities.hpp @@ -1,40 +1,42 @@ -#ifndef __UTILILITES__ -#define __UTILILITES__ +#ifndef UTILITIES_HPP_INCLUDE +#define UTILITIES_HPP_INCLUDE + #include #include #include #include -#define BUFFER_SIZE 4145152 +constexpr int BUFFER_SIZE = 10 * 1024 * 1024; // 10MB struct comp { - bool operator()(const std::string &lhs, const std::string &rhs) const; + bool operator()(const std::string& lhs, const std::string& rhs) const; }; typedef std::map cimap; // Case-Insensitive map -std::string readFile(const char *filename); +std::string readFile(const char* filename); std::string readFile(std::string filename); std::string getExtension(std::string filePath); void printVector(std::vector); std::vector split(std::string s, std::string d, bool trim = true); -std::string urlEncode(std::string const &); -std::string urlDecode(std::string const &); +std::string urlEncode(std::string const&); +std::string urlDecode(std::string const&); std::string toLowerCase(std::string); -std::vector tokenize(std::string const &, char delimiter); -void replaceAll(std::string &str, const std::string &from, - const std::string &to); +std::vector tokenize(std::string const&, char delimiter); +void replaceAll(std::string& str, const std::string& from, + const std::string& to); -int findSubStrPosition(std::string &str, std::string const &subStr, - int const &pos); -int writeObjectToFile(const char *object, int sizem, - std::string const &filePath); -int writeToFile(std::string const &str, std::string const &filePath); -int readMapFromFile(std::string fname, std::map *m); +int findSubStrPosition(std::string& str, std::string const& subStr, + int const& pos); +int writeObjectToFile(const char* object, int sizem, + std::string const& filePath); +int writeToFile(std::string const& str, std::string const& filePath); +int readMapFromFile(std::string fname, std::map* m); cimap getCimapFromString(std::string); -#endif + +#endif // UTILITIES_HPP_INCLUDE From 8a96e2d41be5cd2ec573154f8260b5c90192f686 Mon Sep 17 00:00:00 2001 From: MisaghM Date: Sun, 7 Jan 2024 18:21:10 +0330 Subject: [PATCH 06/23] Refactor utilities --- examples/handlers.cpp | 2 +- examples/main.cpp | 38 ++++--- examples/my_server.cpp | 3 - examples/my_server.hpp | 11 -- makefile | 10 +- server/server.cpp | 49 ++++----- utils/request.cpp | 48 +++++---- utils/request.hpp | 14 +-- utils/response.cpp | 2 +- utils/response.hpp | 2 +- utils/strutils.cpp | 76 ++++++++++++++ utils/strutils.hpp | 23 +++++ utils/template_parser.cpp | 38 +++---- utils/template_parser.hpp | 2 + utils/utilities.cpp | 207 ++++++++++++-------------------------- utils/utilities.hpp | 39 +++---- 16 files changed, 285 insertions(+), 279 deletions(-) delete mode 100644 examples/my_server.cpp delete mode 100644 examples/my_server.hpp create mode 100644 utils/strutils.cpp create mode 100644 utils/strutils.hpp diff --git a/examples/handlers.cpp b/examples/handlers.cpp index 7fc6441..3e44f56 100644 --- a/examples/handlers.cpp +++ b/examples/handlers.cpp @@ -15,7 +15,7 @@ Response* RandomNumberHandler::callback(Request* req) { body += to_string(rand() % 10 + 1); body += "

"; body += "

"; - body += "SeddionId: "; + body += "SessionId: "; body += req->getSessionId(); body += "

"; body += ""; diff --git a/examples/main.cpp b/examples/main.cpp index c352509..ba2f829 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -1,28 +1,34 @@ -#include // for rand and srand -#include // for time #include +#include +#include "../server/server.hpp" #include "handlers.hpp" -#include "my_server.hpp" -using namespace std; +void mapServerPaths(Server& server) { + server.setNotFoundErrPage("static/404.html"); + server.get("/login", new ShowPage("static/logincss.html")); + server.post("/login", new LoginHandler()); + server.get("/up", new ShowPage("static/upload_form.html")); + server.post("/up", new UploadHandler()); + server.get("/rand", new RandomNumberHandler()); + server.get("/home.png", new ShowImage("static/home.png")); + server.get("/", new ShowPage("static/home.html")); + server.get("/colors", new ColorHandler("template/colors.html")); +} int main(int argc, char** argv) { - srand(time(NULL)); // for rand try { - MyServer server(argc > 1 ? atoi(argv[1]) : 5000); - server.setNotFoundErrPage("static/404.html"); - server.get("/login", new ShowPage("static/logincss.html")); - server.post("/login", new LoginHandler()); - server.get("/up", new ShowPage("static/upload_form.html")); - server.post("/up", new UploadHandler()); - server.get("/rand", new RandomNumberHandler()); - server.get("/home.png", new ShowImage("static/home.png")); - server.get("/", new ShowPage("static/home.html")); - server.get("/colors", new ColorHandler("template/colors.html")); + int port = argc > 1 ? std::stoi(argv[1]) : 5000; + Server server(port); + mapServerPaths(server); + std::cout << "Server running on port: " << port << std::endl; server.run(); } + catch (const std::invalid_argument& e) { + std::cerr << e.what() << std::endl; + } catch (const Server::Exception& e) { - cerr << e.getMessage() << endl; + std::cerr << e.getMessage() << std::endl; } + return 0; } diff --git a/examples/my_server.cpp b/examples/my_server.cpp deleted file mode 100644 index ab13843..0000000 --- a/examples/my_server.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "my_server.hpp" - -MyServer::MyServer(int port) : Server(port) {} diff --git a/examples/my_server.hpp b/examples/my_server.hpp deleted file mode 100644 index e3e88db..0000000 --- a/examples/my_server.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef MY_SERVER_HPP_INCLUDE -#define MY_SERVER_HPP_INCLUDE - -#include "../server/server.hpp" - -class MyServer : public Server { -public: - MyServer(int port = 5000); -}; - -#endif // MY_SERVER_HPP_INCLUDE diff --git a/makefile b/makefile index 6d7f6f3..c3b49a5 100644 --- a/makefile +++ b/makefile @@ -25,6 +25,9 @@ $(BUILD_DIR)/request.o: utils/request.cpp utils/request.hpp utils/include.hpp ut $(BUILD_DIR)/utilities.o: utils/utilities.cpp utils/utilities.hpp $(CC) $(CF) -c utils/utilities.cpp -o $(BUILD_DIR)/utilities.o +$(BUILD_DIR)/strutils.o: utils/strutils.cpp utils/strutils.hpp + $(CC) $(CF) -c utils/strutils.cpp -o $(BUILD_DIR)/strutils.o + $(BUILD_DIR)/server.o: server/server.cpp server/server.hpp server/route.hpp utils/utilities.hpp utils/response.hpp utils/request.hpp utils/include.hpp utils/template_parser.hpp utils/template_parser.cpp $(CC) $(CF) -c server/server.cpp -o $(BUILD_DIR)/server.o @@ -34,14 +37,11 @@ $(BUILD_DIR)/route.o: server/route.cpp server/route.hpp utils/utilities.hpp util $(BUILD_DIR)/handlers.o: examples/handlers.cpp server/server.hpp utils/utilities.hpp utils/response.hpp utils/request.hpp utils/include.hpp $(CC) $(CF) -c examples/handlers.cpp -o $(BUILD_DIR)/handlers.o -$(BUILD_DIR)/my_server.o: examples/my_server.cpp server/server.hpp utils/utilities.hpp utils/response.hpp utils/request.hpp utils/include.hpp - $(CC) $(CF) -c examples/my_server.cpp -o $(BUILD_DIR)/my_server.o - $(BUILD_DIR)/main.o: examples/main.cpp server/server.hpp utils/utilities.hpp utils/response.hpp utils/request.hpp utils/include.hpp $(CC) $(CF) -c examples/main.cpp -o $(BUILD_DIR)/main.o -myserver.out: $(BUILD_DIR)/my_server.o $(BUILD_DIR)/main.o $(BUILD_DIR)/handlers.o $(BUILD_DIR)/response.o $(BUILD_DIR)/request.o $(BUILD_DIR)/utilities.o $(BUILD_DIR)/server.o $(BUILD_DIR)/route.o $(BUILD_DIR)/template_parser.o - $(CC) $(CF) $(BUILD_DIR)/my_server.o $(BUILD_DIR)/main.o $(BUILD_DIR)/handlers.o $(BUILD_DIR)/response.o $(BUILD_DIR)/request.o $(BUILD_DIR)/utilities.o $(BUILD_DIR)/server.o $(BUILD_DIR)/route.o $(BUILD_DIR)/template_parser.o $(LDLIBS) -o myserver.out +myserver.out: $(BUILD_DIR)/main.o $(BUILD_DIR)/handlers.o $(BUILD_DIR)/response.o $(BUILD_DIR)/request.o $(BUILD_DIR)/utilities.o $(BUILD_DIR)/strutils.o $(BUILD_DIR)/server.o $(BUILD_DIR)/route.o $(BUILD_DIR)/template_parser.o + $(CC) $(CF) $(BUILD_DIR)/main.o $(BUILD_DIR)/handlers.o $(BUILD_DIR)/response.o $(BUILD_DIR)/request.o $(BUILD_DIR)/utilities.o $(BUILD_DIR)/strutils.o $(BUILD_DIR)/server.o $(BUILD_DIR)/route.o $(BUILD_DIR)/template_parser.o $(LDLIBS) -o myserver.out .PHONY: clean clean: diff --git a/server/server.cpp b/server/server.cpp index a90cf9f..9d0ce6f 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -13,6 +13,7 @@ #include #include +#include "../utils/strutils.hpp" #include "../utils/utilities.hpp" #ifdef _WIN32 @@ -65,8 +66,8 @@ class NotFoundHandler : public RequestHandler { Response* callback(Request* req) { Response* res = new Response(404); if (!notFoundErrPage.empty()) { - res->setHeader("Content-Type", "text/" + getExtension(notFoundErrPage)); - res->setBody(readFile(notFoundErrPage.c_str())); + res->setHeader("Content-Type", "text/" + utils::getExtension(notFoundErrPage)); + res->setBody(utils::readFile(notFoundErrPage.c_str())); } return res; } @@ -114,20 +115,20 @@ Request* parseRawReq(char* headersRaw, size_t length) { BODY_BODY, }; State state = REQ; - vector headers = split(string(headersRaw), "\r\n", false); + vector headers = utils::split(string(headersRaw), "\r\n"); for (size_t i = 0; i < length; i++) { if (!headersRaw[i]) throw Server::Exception("Unsupported binary data in request."); } size_t realBodySize = string(headersRaw).size() - - split(string(headersRaw), "\r\n\r\n", false)[0].size() - + utils::split(string(headersRaw), "\r\n\r\n")[0].size() - string("\r\n\r\n").size(); for (size_t headerIndex = 0; headerIndex < headers.size(); headerIndex++) { string line = headers[headerIndex]; switch (state) { case REQ: { - vector R = split(line, " ", false); + vector R = utils::split(line, ' '); if (R.size() != 3) { throw Server::Exception("Invalid header (request line)"); } @@ -135,11 +136,11 @@ Request* parseRawReq(char* headersRaw, size_t length) { req->setPath(R[1]); size_t pos = req->getPath().find('?'); if (pos != string::npos && pos != req->getPath().size() - 1) { - vector Q1 = split(req->getPath().substr(pos + 1), "&", false); + vector Q1 = utils::split(req->getPath().substr(pos + 1), '&'); for (vector::size_type q = 0; q < Q1.size(); q++) { - vector Q2 = split(Q1[q], "=", false); + vector Q2 = utils::split(Q1[q], '='); if (Q2.size() == 2) - req->setQueryParam(Q2[0], Q2[1], false); + req->setQueryParam(Q2[0], Q2[1]); else throw Server::Exception("Invalid query"); } @@ -160,11 +161,11 @@ Request* parseRawReq(char* headersRaw, size_t length) { } break; } - vector R = split(line, ": ", false); + vector R = utils::split(line, ": "); if (R.size() != 2) throw Server::Exception("Invalid header"); req->setHeader(R[0], R[1], false); - if (toLowerCase(R[0]) == toLowerCase("Content-Length")) + if (utils::tolower(R[0]) == utils::tolower("Content-Length")) if (realBodySize != (size_t)atol(R[1].c_str())) return NULL; } break; @@ -173,9 +174,9 @@ Request* parseRawReq(char* headersRaw, size_t length) { } else if (req->getHeader("Content-Type") == "application/x-www-form-urlencoded") { - vector body = split(line, "&", false); + vector body = utils::split(line, '&'); for (size_t i = 0; i < body.size(); i++) { - vector field = split(body[i], "=", false); + vector field = utils::split(body[i], '='); if (field.size() == 2) req->setBodyParam(field[0], field[1], false); else if (field.size() == 1) @@ -204,15 +205,15 @@ Request* parseRawReq(char* headersRaw, size_t length) { state = BODY_BODY; break; } - vector R = split(line, ": ", false); + vector R = utils::split(line, ": "); if (R.size() != 2) throw Server::Exception("Invalid header"); - if (toLowerCase(R[0]) == toLowerCase("Content-Disposition")) { - vector A = split(R[1], "; ", false); + if (utils::tolower(R[0]) == utils::tolower("Content-Disposition")) { + vector A = utils::split(R[1], "; "); for (size_t i = 0; i < A.size(); i++) { - vector attr = split(A[i], "=", false); + vector attr = utils::split(A[i], '='); if (attr.size() == 2) { - if (toLowerCase(attr[0]) == toLowerCase("name")) { + if (utils::tolower(attr[0]) == utils::tolower("name")) { lastFieldKey = attr[1].substr(1, attr[1].size() - 2); } } @@ -222,11 +223,11 @@ Request* parseRawReq(char* headersRaw, size_t length) { throw Server::Exception("Invalid body attribute"); } } - else if (toLowerCase(R[0]) == toLowerCase("Content-Type")) { - if (toLowerCase(R[1]) == toLowerCase("application/octet-stream")) + else if (utils::tolower(R[0]) == utils::tolower("Content-Type")) { + if (utils::tolower(R[1]) == utils::tolower("application/octet-stream")) shouldBeEmpty = true; - else if (toLowerCase(R[1].substr(0, R[1].find("/"))) != - toLowerCase("text")) + else if (utils::tolower(R[1].substr(0, R[1].find("/"))) != + utils::tolower("text")) throw Server::Exception("Unsupported file type: " + R[1]); } } break; @@ -389,15 +390,15 @@ ShowFile::ShowFile(string _filePath, string _fileType) { Response* ShowFile::callback(Request* req) { Response* res = new Response; res->setHeader("Content-Type", fileType); - res->setBody(readFile(filePath.c_str())); + res->setBody(utils::readFile(filePath.c_str())); return res; } ShowPage::ShowPage(string filePath) - : ShowFile(filePath, "text/" + getExtension(filePath)) {} + : ShowFile(filePath, "text/" + utils::getExtension(filePath)) {} ShowImage::ShowImage(string filePath) - : ShowFile(filePath, "image/" + getExtension(filePath)) {} + : ShowFile(filePath, "image/" + utils::getExtension(filePath)) {} void Server::setNotFoundErrPage(std::string notFoundErrPage) { delete notFoundHandler; diff --git a/utils/request.cpp b/utils/request.cpp index 8c87d09..026244f 100644 --- a/utils/request.cpp +++ b/utils/request.cpp @@ -1,26 +1,28 @@ #include "request.hpp" +#include #include #include #include #include +#include "../utils/strutils.hpp" #include "../utils/utilities.hpp" using namespace std; -Request::Request(string _method) { - if (_method == "GET") +Request::Request(string method_) { + if (method_ == "GET") method = GET; - if (_method == "POST") + if (method_ == "POST") method = POST; } -string Request::getQueryParam(string key) { return urlDecode(query[key]); } +string Request::getQueryParam(string key) { return utils::urlDecode(query[key]); } -string Request::getBodyParam(string key) { return urlDecode(body[key]); } +string Request::getBodyParam(string key) { return utils::urlDecode(body[key]); } -string Request::getHeader(string key) { return urlDecode(headers[key]); } +string Request::getHeader(string key) { return utils::urlDecode(headers[key]); } string Request::getPath() { return path; } @@ -31,15 +33,15 @@ Method Request::getMethod() { return method; } void Request::setMethod(Method _method) { method = _method; } void Request::setQueryParam(string key, string value, bool encode) { - query[key] = encode ? urlEncode(value) : value; + query[key] = encode ? utils::urlEncode(value) : value; } void Request::setBodyParam(string key, string value, bool encode) { - body[key] = encode ? urlEncode(value) : value; + body[key] = encode ? utils::urlEncode(value) : value; } void Request::setHeader(string key, string value, bool encode) { - headers[key] = encode ? urlEncode(value) : value; + headers[key] = encode ? utils::urlEncode(value) : value; } string Request::getBody() { @@ -49,13 +51,19 @@ string Request::getBody() { return bs; } +static void trim(string& s) { + s.erase(std::remove(s.begin(), s.end(), ' '), s.end()); +} + string Request::getSessionId() { string cookie = getHeader("cookie"); if (cookie == "") return ""; - vector v = split(cookie, ";"); + trim(cookie); + vector v = utils::split(cookie, ";"); for (string kv : v) { - vector k = split(kv, "="); + trim(kv); + vector k = utils::split(kv, "="); if (k[0] == "sessionId") return k[1]; } @@ -73,23 +81,23 @@ void Request::log() { log += K + string("Path:\t") + NC + path + string("\n"); log += K + string("Headers:") + NC + string("\n"); for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) - log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + + log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + string("\n"); log += "[ " + K + string("SessionId:\t") + NC + this->getSessionId() + " ]" + string("\n"); log += K + string("Query:") + NC + string("\n"); for (auto it = query.begin(); !query.empty() && it != query.end(); it++) - log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + + log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + string("\n"); log += K + string("Body:") + NC + string("\n"); for (auto it = body.begin(); !body.empty() && it != body.end(); it++) - log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + + log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + string("\n"); log += H + string("------------------------") + NC + string("\n"); cerr << log << endl; } -cimap Request::getHeaders() { +utils::CiMap Request::getHeaders() { vector res; for (map::iterator i = headers.begin(); !headers.empty() && i != headers.end(); i++) { @@ -121,15 +129,15 @@ string Request::getHeadersString() { } void Request::setHeaders(string _headers) { - headers = getCimapFromString(_headers); + headers = utils::getCimapFromString(_headers); } void Request::setQuery(std::string _query) { _query = _query.substr(1); - query = getCimapFromString(_query); + query = utils::getCimapFromString(_query); } -void Request::setBody(std::string _body) { body = getCimapFromString(_body); } +void Request::setBody(std::string _body) { body = utils::getCimapFromString(_body); } void Request::serializeToFile(Request* req, string filePath) { string reqString = to_string(req->getMethod()); @@ -141,11 +149,11 @@ void Request::serializeToFile(Request* req, string filePath) { reqString += req->getBody(); reqString += "\n"; reqString += req->getQueryString(); - writeToFile(reqString, filePath); + utils::writeToFile(reqString, filePath); } void Request::deserializeFromFile(Request* req, string filePath) { - vector fields = tokenize(readFile(filePath), '\n'); + vector fields = utils::split(utils::readFile(filePath), '\n'); switch (fields.size()) { case 5: req->setQuery(fields[4]); diff --git a/utils/request.hpp b/utils/request.hpp index 9361113..f74bee0 100644 --- a/utils/request.hpp +++ b/utils/request.hpp @@ -23,11 +23,11 @@ class Request { std::string getSessionId(); void setSessionId(std::string); std::string getQueryString(); - cimap getHeaders(); + utils::CiMap getHeaders(); std::string getHeadersString(); - void setHeaders(std::string); - void setQuery(std::string); - void setBody(std::string); + void setHeaders(std::string headers); + void setQuery(std::string query); + void setBody(std::string body); void log(); static void serializeToFile(Request* req, std::string filePath); static void deserializeFromFile(Request* req, std::string filePath); @@ -35,9 +35,9 @@ class Request { private: std::string path; Method method; - cimap headers; - cimap query; - cimap body; + utils::CiMap headers; + utils::CiMap query; + utils::CiMap body; }; #endif // REQUEST_HPP_INCLUDE diff --git a/utils/response.cpp b/utils/response.cpp index bf21039..48ffd4a 100644 --- a/utils/response.cpp +++ b/utils/response.cpp @@ -57,7 +57,7 @@ void Response::log(bool showBody) { to_string(code) + " " + phrase + NC + string("\n"); log += K + string("Headers:") + NC + string("\n"); for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) - log += " " + urlDecode(it->first) + ": " + urlDecode(it->second) + + log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + string("\n"); if (showBody) log += K + string("Body:\n") + NC + body + string("\n"); diff --git a/utils/response.hpp b/utils/response.hpp index af51458..087b9e1 100644 --- a/utils/response.hpp +++ b/utils/response.hpp @@ -27,7 +27,7 @@ class Response { std::string phrase; std::string body; - cimap headers; + utils::CiMap headers; }; #endif // RESPONSE_HPP_INCLUDE diff --git a/utils/strutils.cpp b/utils/strutils.cpp new file mode 100644 index 0000000..55b6760 --- /dev/null +++ b/utils/strutils.cpp @@ -0,0 +1,76 @@ +#include "strutils.hpp" + +#include +#include +#include + +namespace utils { + +void trimLeft(std::string& str) { + str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); +} + +void trimRight(std::string& str) { + str.erase(std::find_if(str.rbegin(), str.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), + str.end()); +} + +void trim(std::string& str) { + trimRight(str); + trimLeft(str); +} + +std::vector split(const std::string& str, char delim) { + std::istringstream sstr(str); + std::string item; + std::vector result; + while (std::getline(sstr, item, delim)) { + result.push_back(std::move(item)); + } + return result; +} + +std::vector split(const std::string& str, const std::string& delim) { + std::string::size_type startPos = 0; + std::string::size_type endPos; + + std::vector result; + while ((endPos = str.find(delim, startPos)) != std::string::npos) { + result.push_back(str.substr(startPos, endPos - startPos)); + startPos = endPos + delim.size(); + } + result.push_back(str.substr(startPos)); + + return result; +} + +std::string toupper(const std::string& str) { + std::string res(str); + std::transform(res.begin(), res.end(), res.begin(), [](unsigned char ch) { + return static_cast(std::toupper(ch)); + }); + return res; +} + +std::string tolower(const std::string& str) { + std::string res(str); + std::transform(res.begin(), res.end(), res.begin(), [](unsigned char ch) { + return static_cast(std::tolower(ch)); + }); + return res; +} + +void replaceAll(std::string& str, const std::string& from, const std::string& to) { + if (from.empty()) return; + size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } +} + +} // namespace utils diff --git a/utils/strutils.hpp b/utils/strutils.hpp new file mode 100644 index 0000000..ef17d3b --- /dev/null +++ b/utils/strutils.hpp @@ -0,0 +1,23 @@ +#ifndef STRUTILS_HPP_INCLUDE +#define STRUTILS_HPP_INCLUDE + +#include +#include + +namespace utils { + +void trimLeft(std::string& str); +void trimRight(std::string& str); +void trim(std::string& str); + +std::vector split(const std::string& str, char delim); +std::vector split(const std::string& str, const std::string& delim); + +std::string toupper(const std::string& str); +std::string tolower(const std::string& str); + +void replaceAll(std::string& str, const std::string& from, const std::string& to); + +} // namespace strutils + +#endif // STRUTILS_HPP_INCLUDE diff --git a/utils/template_parser.cpp b/utils/template_parser.cpp index e5ef768..0cd646a 100644 --- a/utils/template_parser.cpp +++ b/utils/template_parser.cpp @@ -21,8 +21,7 @@ const std::string localTemplate(const int parserNum) { TemplateParser::TemplateParser(string _filePath) { filePath = _filePath; variableCount = 0; - programName = - to_string(TemplateParser::lastParserNum) + SysCmd::fileExtention; + programName = to_string(TemplateParser::lastParserNum) + SysCmd::fileExtention; parserNum = TemplateParser::lastParserNum++; code = ""; parseTemplate(); @@ -35,7 +34,7 @@ string TemplateParser::getHtml(map _context) { } void TemplateParser::parseTemplate() { - string unparsedTemplate = readFile(filePath); + string unparsedTemplate = utils::readFile(filePath); int parsePointer = 0; while (parsePointer < (signed int)unparsedTemplate.size()) { int begin = findBeginOfCodeBlock(parsePointer, unparsedTemplate); @@ -49,14 +48,16 @@ void TemplateParser::parseTemplate() { appendHTMLToCode(parsePointer, unparsedTemplate.size(), unparsedTemplate); } -int TemplateParser::findBeginOfCodeBlock(int startPosition, - string& unparsedTemplate) { - return findSubStrPosition(unparsedTemplate, beginCodeBlockTag, startPosition); +int TemplateParser::findBeginOfCodeBlock(int startPosition, string& unparsedTemplate) { + size_t found = unparsedTemplate.find(beginCodeBlockTag, startPosition); + if (found == std::string::npos) return -1; + return found; } -int TemplateParser::findEndOfCodeBlock(int startPosition, - string& unparsedTemplate) { - return findSubStrPosition(unparsedTemplate, endCodeBlockTag, startPosition); +int TemplateParser::findEndOfCodeBlock(int startPosition, string& unparsedTemplate) { + size_t found = unparsedTemplate.find(endCodeBlockTag, startPosition); + if (found == std::string::npos) return -1; + return found; } void TemplateParser::appendHTMLToCode(int begin, int end, @@ -85,9 +86,8 @@ void TemplateParser::makeExecutableTemplate() { } void TemplateParser::makeLocalTemplate() { - string templateContent = readFile(filePath); - if (writeToFile(templateContent, - outputFolder + "/" + localTemplate(parserNum)) < 0) + string templateContent = utils::readFile(filePath); + if (!utils::writeToFile(templateContent, outputFolder + "/" + localTemplate(parserNum))) throw Server::Exception("Can not write template to local " + outputFolder + "folder"); } @@ -100,11 +100,11 @@ void TemplateParser::generateCode() { } void TemplateParser::compileCode() { - if (writeToFile(code, toCompileFile) < 0) + if (!utils::writeToFile(code, toCompileFile)) throw Server::Exception("Can not write generated template code!"); string cmd = mkdirNoErrors(outputFolder) + " && " + cc + " " + toCompileFile + - " " + utilitiesPath + " -o " + outputFolder + SysCmd::slash + + " " + utilitiesPath + " " + strutilsPath + " -o " + outputFolder + SysCmd::slash + programName + "&& " + SysCmd::rm + toCompileFile; string error = "Can not compile template " + filePath; TemplateUtils::runSystemCommand(cmd, error); @@ -116,7 +116,7 @@ string TemplateParser::runGeneratedCode() { string error = "Error in running template " + filePath; TemplateUtils::runSystemCommand(cmd, error); - string html = readFile(staticTemplate); + string html = utils::readFile(staticTemplate); cmd = SysCmd::rm + staticTemplate; error = "Error in deleting static template for " + filePath; @@ -131,12 +131,13 @@ void TemplateParser::addIncludesToCode() { include += "#include \n"; include += "#include \n"; include += "#include \"" + utilitiesHeaderPath + "\"\n"; + include += "#include \"" + strutilsHeaderPath + "\"\n"; include += "using namespace std;\n"; code = include + "int main(int argc, char const *argv[])\n{\n" + code + "\n"; } void TemplateParser::addReadFromTemplateToCode() { - code = "string __unparsedTemplate__ = readFile(\"" + outputFolder + "/" + + code = "string __unparsedTemplate__ = utils::readFile(\"" + outputFolder + "/" + localTemplate(parserNum) + "\");\n" + code; } @@ -147,7 +148,7 @@ void TemplateParser::addContextMapToCode() { // `mapFile` should be changed if we want to handle requests // in a multi-thread non-blocking way mapCode += - "readMapFromFile(\"" + outputFolder + "/" + mapFile + "\", &context);\n"; + "utils::readMapFromFile(\"" + outputFolder + "/" + mapFile + "\", context);\n"; code = mapCode + code; } @@ -185,8 +186,7 @@ void TemplateParser::TemplateUtils::runSystemCommand(string command, #endif } -int TemplateParser::TemplateUtils::writeMapToFile( - std::string fname, std::map* m) { +int TemplateParser::TemplateUtils::writeMapToFile(std::string fname, std::map* m) { int count = 0; if (m->empty()) return 0; diff --git a/utils/template_parser.hpp b/utils/template_parser.hpp index 34426bf..3fc1f2a 100644 --- a/utils/template_parser.hpp +++ b/utils/template_parser.hpp @@ -30,6 +30,8 @@ const std::string beginCodeBlockTag = "<%"; const std::string endCodeBlockTag = "%>"; const std::string utilitiesHeaderPath = "utils/utilities.hpp"; const std::string utilitiesPath = "utils/utilities.cpp"; +const std::string strutilsHeaderPath = "utils/strutils.hpp"; +const std::string strutilsPath = "utils/strutils.cpp"; const std::string cc = "g++ -std=c++11 -Wall -pedantic"; const std::string compileDirectory = "templateCompile"; const std::string toCompileFile = "compiled.cpp"; diff --git a/utils/utilities.cpp b/utils/utilities.cpp index c97c453..32b544b 100644 --- a/utils/utilities.cpp +++ b/utils/utilities.cpp @@ -1,84 +1,53 @@ #include "utilities.hpp" -#include #include -#include -#include -#include #include -using namespace std; +#include "strutils.hpp" -string toLowerCase(string s) { - transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); - return s; -} +namespace utils { -bool comp::operator()(const string& lhs, const string& rhs) const { - return toLowerCase(lhs) < toLowerCase(rhs); +bool StringInsensitiveComp::operator()(const std::string& lhs, const std::string& rhs) const { + return tolower(lhs) < tolower(rhs); } -string readFile(const char* filename) { - ifstream infile; - infile.open(filename, ios_base::binary); - if (!infile.is_open()) - return string(); - - infile.seekg(0, infile.end); - size_t length = infile.tellg(); - infile.seekg(0, infile.beg); +std::string readFile(const std::string& filename) { + std::ifstream file(filename, std::ios_base::binary); + if (!file.is_open()) return {}; - if (length > BUFFER_SIZE) - length = BUFFER_SIZE; - char* buffer = new char[length + 1]; + file.seekg(0, file.end); + size_t length = file.tellg(); + file.seekg(0, file.beg); - infile.read(buffer, length); - - string s(buffer, length); - delete[] buffer; - return s; + if (length > BUFSIZE) { + length = BUFSIZE; + } + std::string buffer(length, '\0'); + file.read(&buffer[0], length); + return buffer; } -string readFile(string filename) { return readFile(filename.c_str()); } - -vector split(string s, string delimiter, bool trim) { - vector tokens; - if (trim) - s.erase(remove(s.begin(), s.end(), ' '), s.end()); - size_t pos = 0; - string token; - while ((pos = s.find(delimiter)) != string::npos) { - token = s.substr(0, pos); - tokens.push_back(token); - s.erase(0, pos + delimiter.length()); - } - tokens.push_back(s); - return tokens; +bool writeToFile(const std::string& str, const std::string& filename) { + std::ofstream file(filename, std::ios_base::binary); + if (!file.is_open()) return false; + file.write(str.c_str(), str.size()); + return true; } -void printVector(vector v) { - for (string s : v) - cout << s << endl; +std::string getExtension(const std::string& filename) { + size_t pos = filename.find_last_of('.'); + return filename.substr(pos != std::string::npos ? pos + 1 : filename.size()); } -string urlEncode(string const& str) { +std::string urlEncode(const std::string& url) { + // http://www.blooberry.com/indexdot/html/topics/urlencoding.htm char encode_buf[4]; - string result; encode_buf[0] = '%'; - result.reserve(str.size()); - - // character selection for this algorithm is based on the following url: - // http://www.blooberry.com/indexdot/html/topics/urlencoding.htm + std::string result; + result.reserve(url.size()); - for (size_t pos = 0; pos < str.size(); ++pos) { - switch (str[pos]) { - default: - if (str[pos] >= 32 && str[pos] < 127) { - // character does not need to be escaped - result += str[pos]; - break; - } - // else pass through to next case + for (size_t pos = 0; pos < url.size(); ++pos) { + switch (url[pos]) { case '$': case '&': case '+': @@ -103,121 +72,69 @@ string urlEncode(string const& str) { case '[': case ']': case '`': - // the character needs to be encoded - sprintf(encode_buf + 1, "%02X", str[pos]); + sprintf(encode_buf + 1, "%02X", url[pos]); result += encode_buf; break; + default: + if (url[pos] >= 32 && url[pos] < 127) { + result += url[pos]; + break; + } } }; return result; } -string urlDecode(string const& str) { +std::string urlDecode(const std::string& url) { char decode_buf[3]; - string result; - result.reserve(str.size()); + std::string result; + result.reserve(url.size()); - for (size_t pos = 0; pos < str.size(); ++pos) { - switch (str[pos]) { + for (size_t pos = 0; pos < url.size(); ++pos) { + switch (url[pos]) { case '+': - // convert to space character result += ' '; break; case '%': - // decode hexidecimal value - if (pos + 2 < str.size()) { - decode_buf[0] = str[++pos]; - decode_buf[1] = str[++pos]; + if (pos + 2 < url.size()) { + decode_buf[0] = url[++pos]; + decode_buf[1] = url[++pos]; decode_buf[2] = '\0'; result += static_cast(strtol(decode_buf, nullptr, 16)); } else { - // recover from error by not decoding character result += '%'; } break; default: - // character does not need to be escaped - result += str[pos]; + result += url[pos]; } } return result; } -string getExtension(string filePath) { - size_t pos = filePath.find_last_of("."); - return filePath.substr(pos != string::npos ? pos + 1 : filePath.size()); -} - -vector tokenize(const string& cnt, char delimiter) { - vector res; - istringstream is(cnt); - string part; - while (getline(is, part, delimiter)) - res.push_back(part); - return res; -} - -void replaceAll(std::string& str, const std::string& from, - const std::string& to) { - if (from.empty()) - return; - size_t start_pos = 0; - while ((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); // In case 'to' contains 'from', like replacing - // 'x' with 'yx' - } -} - -int findSubStrPosition(std::string& str, std::string const& subStr, - int const& pos) { - size_t found = str.find(subStr, pos); - if (found == string::npos) - return -1; - return found; -} - -int writeObjectToFile(const char* object, int size, - std::string const& filePath) { - ofstream file; - file.open(filePath, fstream::binary); - if (!file.is_open()) - return -1; - file.write(object, size); - file.close(); - return sizeof(object); -} - -int writeToFile(std::string const& str, std::string const& filePath) { - return writeObjectToFile(str.c_str(), str.length(), filePath); -} - -cimap getCimapFromString(std::string str) { - cimap m; - vector tokenized = tokenize(str, '&'); - for (auto token : tokenized) { - vector keyValue = tokenize(token, '='); - if (keyValue.size() != 2) - continue; - string key = keyValue[0]; - string value = keyValue[1]; - m[key] = value; +CiMap getCimapFromString(const std::string& str) { + CiMap m; + std::vector tokenized = split(str, '&'); + for (const std::string& token : tokenized) { + std::vector keyValue = split(token, '='); + if (keyValue.size() != 2) continue; + m[keyValue[0]] = keyValue[1]; } return m; } -int readMapFromFile(std::string fname, std::map* m) { - std::ifstream inputStream(fname); - if (!inputStream.is_open()) - return -errno; + +int readMapFromFile(const std::string& filename, std::map& m) { + std::ifstream infile(filename); + if (!infile.is_open()) return -1; std::string line; - while (std::getline(inputStream, line)) { - auto tokens = tokenize(line, '='); - // KEY VALUE - (*m)[tokens[0]] = tokens[(tokens.size() < 2) ? 0 : 1]; + while (std::getline(infile, line)) { + auto tokens = split(line, '='); + m[tokens[0]] = tokens[(tokens.size() < 2) ? 0 : 1]; } - inputStream.close(); - return (*m).size(); + return m.size(); } + +} // namespace utils diff --git a/utils/utilities.hpp b/utils/utilities.hpp index 3d7d729..2a400f9 100644 --- a/utils/utilities.hpp +++ b/utils/utilities.hpp @@ -1,42 +1,29 @@ #ifndef UTILITIES_HPP_INCLUDE #define UTILITIES_HPP_INCLUDE -#include #include #include -#include -constexpr int BUFFER_SIZE = 10 * 1024 * 1024; // 10MB +#include "include.hpp" -struct comp { +namespace utils { + +struct StringInsensitiveComp { bool operator()(const std::string& lhs, const std::string& rhs) const; }; -typedef std::map - cimap; // Case-Insensitive map - -std::string readFile(const char* filename); -std::string readFile(std::string filename); -std::string getExtension(std::string filePath); -void printVector(std::vector); -std::vector split(std::string s, std::string d, bool trim = true); - -std::string urlEncode(std::string const&); -std::string urlDecode(std::string const&); +using CiMap = std::map; -std::string toLowerCase(std::string); +std::string readFile(const std::string& filename); +bool writeToFile(const std::string& str, const std::string& filename); +std::string getExtension(const std::string& filename); -std::vector tokenize(std::string const&, char delimiter); -void replaceAll(std::string& str, const std::string& from, - const std::string& to); +std::string urlEncode(const std::string& url); +std::string urlDecode(const std::string& url); -int findSubStrPosition(std::string& str, std::string const& subStr, - int const& pos); -int writeObjectToFile(const char* object, int sizem, - std::string const& filePath); -int writeToFile(std::string const& str, std::string const& filePath); -int readMapFromFile(std::string fname, std::map* m); +CiMap getCimapFromString(const std::string& str); +int readMapFromFile(const std::string& filename, std::map& m); -cimap getCimapFromString(std::string); +} // namespace utils #endif // UTILITIES_HPP_INCLUDE From f3b67e5bafe539d22eb1cd98aea881c49e674419 Mon Sep 17 00:00:00 2001 From: MisaghM Date: Sun, 7 Jan 2024 19:23:45 +0330 Subject: [PATCH 07/23] Fix header decoding --- server/server.cpp | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/server/server.cpp b/server/server.cpp index 9d0ce6f..48f2bb8 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -83,23 +83,6 @@ class ServerErrorHandler { } }; -void split(string str, string separator, int max, vector& results) { - int i = 0; - size_t found = str.find_first_of(separator); - - while (found != string::npos) { - if (found > 0) - results.push_back(str.substr(0, found)); - str = str.substr(found + 1); - found = str.find_first_of(separator); - - if (max > -1 && ++i == max) - break; - } - if (str.length() > 0) - results.push_back(str); -} - Request* parseRawReq(char* headersRaw, size_t length) { Request* req = nullptr; string boundary; @@ -140,7 +123,7 @@ Request* parseRawReq(char* headersRaw, size_t length) { for (vector::size_type q = 0; q < Q1.size(); q++) { vector Q2 = utils::split(Q1[q], '='); if (Q2.size() == 2) - req->setQueryParam(Q2[0], Q2[1]); + req->setQueryParam(Q2[0], Q2[1], false); else throw Server::Exception("Invalid query"); } From 5e4afbf3a5cffc7482c0728a110c643ff161b803 Mon Sep 17 00:00:00 2001 From: MisaghM Date: Mon, 8 Jan 2024 11:58:35 +0330 Subject: [PATCH 08/23] Use string readfile --- server/server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/server.cpp b/server/server.cpp index 48f2bb8..82ffd6d 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -67,7 +67,7 @@ class NotFoundHandler : public RequestHandler { Response* res = new Response(404); if (!notFoundErrPage.empty()) { res->setHeader("Content-Type", "text/" + utils::getExtension(notFoundErrPage)); - res->setBody(utils::readFile(notFoundErrPage.c_str())); + res->setBody(utils::readFile(notFoundErrPage)); } return res; } @@ -373,7 +373,7 @@ ShowFile::ShowFile(string _filePath, string _fileType) { Response* ShowFile::callback(Request* req) { Response* res = new Response; res->setHeader("Content-Type", fileType); - res->setBody(utils::readFile(filePath.c_str())); + res->setBody(utils::readFile(filePath)); return res; } From c43e3efdd9e14498a0caf5cde243b2b0593cb816 Mon Sep 17 00:00:00 2001 From: MisaghM Date: Mon, 8 Jan 2024 18:24:03 +0330 Subject: [PATCH 09/23] Support for binary post data --- examples/handlers.cpp | 3 +- server/server.cpp | 216 +++++++++++++++++++----------------------- utils/strutils.cpp | 5 + utils/strutils.hpp | 1 + 4 files changed, 105 insertions(+), 120 deletions(-) diff --git a/examples/handlers.cpp b/examples/handlers.cpp index 3e44f56..011baa4 100644 --- a/examples/handlers.cpp +++ b/examples/handlers.cpp @@ -38,8 +38,7 @@ Response* LoginHandler::callback(Request* req) { Response* UploadHandler::callback(Request* req) { string name = req->getBodyParam("file_name"); string file = req->getBodyParam("file"); - cout << name << " (" << file.size() << "B):\n" - << file << endl; + utils::writeToFile(file, name); Response* res = Response::redirect("/"); return res; } diff --git a/server/server.cpp b/server/server.cpp index 82ffd6d..856ab72 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -83,12 +83,12 @@ class ServerErrorHandler { } }; -Request* parseRawReq(char* headersRaw, size_t length) { +Request* parseRawReq(char* reqData, size_t length) { Request* req = nullptr; string boundary; string lastFieldKey; string lastFieldValue; - bool shouldBeEmpty; + string reqDataStr(reqData, reqData + length); try { enum State { REQ, @@ -98,66 +98,55 @@ Request* parseRawReq(char* headersRaw, size_t length) { BODY_BODY, }; State state = REQ; - vector headers = utils::split(string(headersRaw), "\r\n"); - for (size_t i = 0; i < length; i++) { - if (!headersRaw[i]) - throw Server::Exception("Unsupported binary data in request."); + size_t endOfHeader = reqDataStr.find("\r\n\r\n"); + string reqHeader = reqDataStr.substr(0, endOfHeader); + string reqBody = reqDataStr.substr(endOfHeader + 4, reqDataStr.size()); + if (endOfHeader == string::npos) { + throw Server::Exception("End of request header not found."); } - size_t realBodySize = - string(headersRaw).size() - - utils::split(string(headersRaw), "\r\n\r\n")[0].size() - - string("\r\n\r\n").size(); - for (size_t headerIndex = 0; headerIndex < headers.size(); headerIndex++) { + vector headers = utils::split(reqHeader, "\r\n"); + if (reqHeader.find('\0') != string::npos) { + throw Server::Exception("Binary data in header."); + } + size_t realBodySize = length - endOfHeader - 4; // string("\r\n\r\n").size(); + + vector R = utils::split(headers[0], ' '); + if (R.size() != 3) { + throw Server::Exception("Invalid header (request line)"); + } + req = new Request(R[0]); + req->setPath(R[1]); + size_t pos = req->getPath().find('?'); + if (pos != string::npos && pos != req->getPath().size() - 1) { + vector Q1 = utils::split(req->getPath().substr(pos + 1), '&'); + for (vector::size_type q = 0; q < Q1.size(); q++) { + vector Q2 = utils::split(Q1[q], '='); + if (Q2.size() == 2) + req->setQueryParam(Q2[0], Q2[1], false); + else + throw Server::Exception("Invalid query"); + } + } + req->setPath(req->getPath().substr(0, pos)); + state = HEADER; + + for (size_t headerIndex = 1; headerIndex < headers.size(); headerIndex++) { string line = headers[headerIndex]; - switch (state) { - case REQ: { - vector R = utils::split(line, ' '); - if (R.size() != 3) { - throw Server::Exception("Invalid header (request line)"); - } - req = new Request(R[0]); - req->setPath(R[1]); - size_t pos = req->getPath().find('?'); - if (pos != string::npos && pos != req->getPath().size() - 1) { - vector Q1 = utils::split(req->getPath().substr(pos + 1), '&'); - for (vector::size_type q = 0; q < Q1.size(); q++) { - vector Q2 = utils::split(Q1[q], '='); - if (Q2.size() == 2) - req->setQueryParam(Q2[0], Q2[1], false); - else - throw Server::Exception("Invalid query"); - } - } - req->setPath(req->getPath().substr(0, pos)); - state = HEADER; - } break; - case HEADER: { - if (line == "") { - state = BODY; - if (req->getHeader("Content-Type") - .substr(0, string("multipart/form-data").size()) == - "multipart/form-data") { - boundary = - req->getHeader("Content-Type") - .substr(req->getHeader("Content-Type").find("boundary=") + - string("boundary=").size()); - } - break; - } - vector R = utils::split(line, ": "); - if (R.size() != 2) - throw Server::Exception("Invalid header"); - req->setHeader(R[0], R[1], false); - if (utils::tolower(R[0]) == utils::tolower("Content-Length")) - if (realBodySize != (size_t)atol(R[1].c_str())) - return NULL; - } break; - case BODY: { - if (req->getHeader("Content-Type") == "") { - } - else if (req->getHeader("Content-Type") == - "application/x-www-form-urlencoded") { - vector body = utils::split(line, '&'); + vector R = utils::split(line, ": "); + if (R.size() != 2) + throw Server::Exception("Invalid header"); + req->setHeader(R[0], R[1], false); + if (utils::tolower(R[0]) == utils::tolower("Content-Length")) + if (realBodySize != (size_t)atol(R[1].c_str())) + throw Server::Exception("Content-Length header does not match payload size"); + } + + string contentType = req->getHeader("Content-Type"); + if (realBodySize != 0 && !contentType.empty()) { + if (utils::startsWith(contentType, "application/x-www-form-urlencoded")) { + vector urlencodedParts = utils::split(reqBody, "\r\n"); + for (const string& part : urlencodedParts) { + vector body = utils::split(part, '&'); for (size_t i = 0; i < body.size(); i++) { vector field = utils::split(body[i], '='); if (field.size() == 2) @@ -168,76 +157,67 @@ Request* parseRawReq(char* headersRaw, size_t length) { throw Server::Exception("Invalid body"); } } - else if (req->getHeader("Content-Type") - .substr(0, string("multipart/form-data").size()) == - "multipart/form-data") { - if (line == "--" + boundary || line == "--" + boundary + "--") { - lastFieldKey = ""; - lastFieldValue = ""; - shouldBeEmpty = false; - state = BODY_HEADER; - } - } - else { - throw Server::Exception("Unsupported body type: " + - req->getHeader("Content-Type")); - } - } break; - case BODY_HEADER: { - if (line == "") { - state = BODY_BODY; - break; + } + else if (utils::startsWith(contentType, "multipart/form-data")) { + boundary = contentType.substr(contentType.find("boundary=") + 9); + size_t firstBoundary = reqBody.find("--" + boundary); + if (firstBoundary == string::npos) { + throw Server::Exception("Boundary data not found."); } - vector R = utils::split(line, ": "); - if (R.size() != 2) - throw Server::Exception("Invalid header"); - if (utils::tolower(R[0]) == utils::tolower("Content-Disposition")) { - vector A = utils::split(R[1], "; "); - for (size_t i = 0; i < A.size(); i++) { - vector attr = utils::split(A[i], '='); - if (attr.size() == 2) { - if (utils::tolower(attr[0]) == utils::tolower("name")) { - lastFieldKey = attr[1].substr(1, attr[1].size() - 2); + reqBody.erase(reqBody.begin(), reqBody.begin() + firstBoundary + 2 + boundary.size()); + + vector boundaries = utils::split(reqBody, "--" + boundary); + boundaries.pop_back(); + + for (string b : boundaries) { + b.pop_back(); // remove "\r\n" from start and end of each boundary + b.pop_back(); + b.erase(b.begin(), b.begin() + 2); + state = BODY_HEADER; + + size_t endOfBoundaryHeader = b.find("\r\n\r\n") + 4; + vector abc = utils::split(b.substr(0, endOfBoundaryHeader - 4), "\r\n"); + for (const string& line : abc) { + if (line.empty()) { + break; + } + vector R = utils::split(line, ": "); + if (R.size() != 2) throw Server::Exception("Invalid header"); + if (utils::tolower(R[0]) == utils::tolower("Content-Disposition")) { + vector A = utils::split(R[1], "; "); + for (size_t i = 0; i < A.size(); i++) { + vector attr = utils::split(A[i], '='); + if (attr.size() == 2) { + if (utils::tolower(attr[0]) == utils::tolower("name")) { + lastFieldKey = attr[1].substr(1, attr[1].size() - 2); + } + } + else if (attr.size() != 1) { + throw Server::Exception("Invalid body attribute"); + } } } - else if (attr.size() == 1) { + else if (utils::tolower(R[0]) == utils::tolower("Content-Type")) { + // if (utils::tolower(R[1]) == utils::tolower("application/octet-stream")) + // contentTypeIsOctetStream = true; + // else if (utils::tolower(R[1].substr(0, R[1].find("/"))) != utils::tolower("text")) + // throw Server::Exception("Unsupported file type: " + R[1]); } - else - throw Server::Exception("Invalid body attribute"); } + lastFieldValue = b.substr(endOfBoundaryHeader); + req->setBodyParam(lastFieldKey, lastFieldValue, false); } - else if (utils::tolower(R[0]) == utils::tolower("Content-Type")) { - if (utils::tolower(R[1]) == utils::tolower("application/octet-stream")) - shouldBeEmpty = true; - else if (utils::tolower(R[1].substr(0, R[1].find("/"))) != - utils::tolower("text")) - throw Server::Exception("Unsupported file type: " + R[1]); - } - } break; - case BODY_BODY: { - if (line == "--" + boundary || line == "--" + boundary + "--") { - req->setBodyParam(lastFieldKey, - lastFieldValue.substr(string("\r\n").size()), - false); - lastFieldKey = ""; - lastFieldValue = ""; - state = BODY_HEADER; - shouldBeEmpty = false; - } - else if (shouldBeEmpty && !line.empty()) - throw Server::Exception("Unsupported file type: " + - string("application/octet-stream")); - else - lastFieldValue += "\r\n" + line; - } break; + } + else { + throw Server::Exception("Unsupported body type: " + contentType); } } } catch (const Server::Exception&) { throw; } - catch (...) { - throw Server::Exception("Error on parsing request"); + catch (const std::exception& e) { + throw Server::Exception("Error on parsing request: " + std::string(e.what())); } return req; } diff --git a/utils/strutils.cpp b/utils/strutils.cpp index 55b6760..3b7b543 100644 --- a/utils/strutils.cpp +++ b/utils/strutils.cpp @@ -73,4 +73,9 @@ void replaceAll(std::string& str, const std::string& from, const std::string& to } } +bool startsWith(const std::string& str, const std::string& s) { + return str.size() >= s.size() && + std::equal(s.begin(), s.end(), str.begin()); +} + } // namespace utils diff --git a/utils/strutils.hpp b/utils/strutils.hpp index ef17d3b..8c9867e 100644 --- a/utils/strutils.hpp +++ b/utils/strutils.hpp @@ -17,6 +17,7 @@ std::string toupper(const std::string& str); std::string tolower(const std::string& str); void replaceAll(std::string& str, const std::string& from, const std::string& to); +bool startsWith(const std::string& str, const std::string& s); } // namespace strutils From e2551220df4ff806de50f4097ebcea4aec029a1c Mon Sep 17 00:00:00 2001 From: MisaghM Date: Mon, 8 Jan 2024 18:24:03 +0330 Subject: [PATCH 10/23] Support for binary post data --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index c3b49a5..c2568fd 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,5 @@ CC=g++ -STD=-std=c++11 -Wall -pedantic +STD=-std=c++11 -Wall -pedantic -g CF=$(STD) BUILD_DIR=build TEMPLATE_DIR=.template From 97f2f240a695960a035489c77c3d61ad8137e47e Mon Sep 17 00:00:00 2001 From: MisaghM Date: Mon, 8 Jan 2024 20:45:45 +0330 Subject: [PATCH 11/23] Fix large binary requests and refactor htmls --- examples/main.cpp | 8 +++-- server/server.cpp | 30 +++++++------------ static/404.html | 20 +++++++++---- static/home.html | 28 +++++++++++------- static/login.html | 30 ++++++++++++------- static/logincss.html | 32 ++++++++++++-------- static/moonlight.mp3 | Bin 0 -> 320827 bytes static/music.html | 16 ++++++++++ static/upload_form.html | 30 ++++++++++++------- template/colors.html | 63 +++++++++++++++++++++------------------- utils/request.cpp | 60 +++++++++++++++++++++++--------------- utils/request.hpp | 3 +- utils/utilities.cpp | 10 +++---- 13 files changed, 197 insertions(+), 133 deletions(-) create mode 100644 static/moonlight.mp3 create mode 100644 static/music.html diff --git a/examples/main.cpp b/examples/main.cpp index ba2f829..fa76fac 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -6,14 +6,16 @@ void mapServerPaths(Server& server) { server.setNotFoundErrPage("static/404.html"); + server.get("/", new ShowPage("static/home.html")); + server.get("/home.png", new ShowImage("static/home.png")); server.get("/login", new ShowPage("static/logincss.html")); server.post("/login", new LoginHandler()); server.get("/up", new ShowPage("static/upload_form.html")); server.post("/up", new UploadHandler()); - server.get("/rand", new RandomNumberHandler()); - server.get("/home.png", new ShowImage("static/home.png")); - server.get("/", new ShowPage("static/home.html")); server.get("/colors", new ColorHandler("template/colors.html")); + server.get("/rand", new RandomNumberHandler()); + server.get("/music", new ShowPage("static/music.html")); + server.get("/music/moonlight.mp3", new ShowFile("static/moonlight.mp3", "audio/mpeg")); } int main(int argc, char** argv) { diff --git a/server/server.cpp b/server/server.cpp index 856ab72..3f40e32 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -90,14 +90,6 @@ Request* parseRawReq(char* reqData, size_t length) { string lastFieldValue; string reqDataStr(reqData, reqData + length); try { - enum State { - REQ, - HEADER, - BODY, - BODY_HEADER, - BODY_BODY, - }; - State state = REQ; size_t endOfHeader = reqDataStr.find("\r\n\r\n"); string reqHeader = reqDataStr.substr(0, endOfHeader); string reqBody = reqDataStr.substr(endOfHeader + 4, reqDataStr.size()); @@ -128,7 +120,6 @@ Request* parseRawReq(char* reqData, size_t length) { } } req->setPath(req->getPath().substr(0, pos)); - state = HEADER; for (size_t headerIndex = 1; headerIndex < headers.size(); headerIndex++) { string line = headers[headerIndex]; @@ -138,7 +129,7 @@ Request* parseRawReq(char* reqData, size_t length) { req->setHeader(R[0], R[1], false); if (utils::tolower(R[0]) == utils::tolower("Content-Length")) if (realBodySize != (size_t)atol(R[1].c_str())) - throw Server::Exception("Content-Length header does not match payload size"); + return nullptr; } string contentType = req->getHeader("Content-Type"); @@ -150,9 +141,9 @@ Request* parseRawReq(char* reqData, size_t length) { for (size_t i = 0; i < body.size(); i++) { vector field = utils::split(body[i], '='); if (field.size() == 2) - req->setBodyParam(field[0], field[1], false); + req->setBodyParam(field[0], field[1], "application/x-www-form-urlencoded", false); else if (field.size() == 1) - req->setBodyParam(field[0], "", false); + req->setBodyParam(field[0], "", "application/x-www-form-urlencoded", false); else throw Server::Exception("Invalid body"); } @@ -173,7 +164,7 @@ Request* parseRawReq(char* reqData, size_t length) { b.pop_back(); // remove "\r\n" from start and end of each boundary b.pop_back(); b.erase(b.begin(), b.begin() + 2); - state = BODY_HEADER; + string boundaryContentType; size_t endOfBoundaryHeader = b.find("\r\n\r\n") + 4; vector abc = utils::split(b.substr(0, endOfBoundaryHeader - 4), "\r\n"); @@ -198,14 +189,14 @@ Request* parseRawReq(char* reqData, size_t length) { } } else if (utils::tolower(R[0]) == utils::tolower("Content-Type")) { - // if (utils::tolower(R[1]) == utils::tolower("application/octet-stream")) - // contentTypeIsOctetStream = true; - // else if (utils::tolower(R[1].substr(0, R[1].find("/"))) != utils::tolower("text")) - // throw Server::Exception("Unsupported file type: " + R[1]); + boundaryContentType = utils::tolower(R[1]); } } + if (boundaryContentType.empty()) { + boundaryContentType = "text/plain"; + } lastFieldValue = b.substr(endOfBoundaryHeader); - req->setBodyParam(lastFieldKey, lastFieldValue, false); + req->setBodyParam(lastFieldKey, lastFieldValue, boundaryContentType, false); } } else { @@ -286,8 +277,7 @@ void Server::run() { size_t recv_len, recv_total_len = 0; Request* req = NULL; while (!req) { - recv_len = - recv(newsc, data + recv_total_len, BUFSIZE - recv_total_len, 0); + recv_len = recv(newsc, data + recv_total_len, BUFSIZE - recv_total_len, 0); if (recv_len > 0) { recv_total_len += recv_len; data[recv_total_len >= 0 ? recv_total_len : 0] = 0; diff --git a/static/404.html b/static/404.html index ad4466f..8132b5d 100644 --- a/static/404.html +++ b/static/404.html @@ -1,8 +1,16 @@ - - -

AP HTTP

-

Err: 404

-

Requested page was not found!

- + + + + + + Page Not Found! + + + +

AP HTTP

+

Err: 404

+

Requested page was not found!

+ + diff --git a/static/home.html b/static/home.html index c0f67dd..042f8eb 100644 --- a/static/home.html +++ b/static/home.html @@ -1,13 +1,19 @@ - - -

AP HTTP

- -
- Go to login page -
- Go to upload page -
- Go to colors page - + + + + + + AP HTTP + + + +

AP HTTP

+ Home Logo
+ Go to login page
+ Go to upload page
+ Go to colors page
+ Go to music page + + diff --git a/static/login.html b/static/login.html index edbd72a..4aae4b4 100644 --- a/static/login.html +++ b/static/login.html @@ -1,14 +1,22 @@ - - -

AP HTTP

+ + + + + + Login Page + + + +

AP HTTP

+

Login

-
-
- - - -
-
- +
+ + + +
+
+ + diff --git a/static/logincss.html b/static/logincss.html index 964f468..e5f6ee1 100644 --- a/static/logincss.html +++ b/static/logincss.html @@ -1,14 +1,22 @@ - - -

AP HTTP

-
-
-

Login

- - - -
-
- + + + + + + Login Page + + + +

AP HTTP

+
+

Login

+
+ + + +
+
+ + diff --git a/static/moonlight.mp3 b/static/moonlight.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e253b9313a72405b208549029e6e4b1675b67b6e GIT binary patch literal 320827 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

VIVS0zMi7^s9A5I+Qyik!v%DZGd2=~0ip=# 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�+!Lb@TOy6v@sT@U|)*vz?W>> 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⪙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

}QRZ=OoQ<%2QM8+AN530gbGv^P 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 ^K(6VanDfN`KzY0}SA5;T%S!BZT^v+w-@ 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 Za>oBQ;?9@M^(+jpD4 + + + + + Music Page + + + +

Music

+ + + + diff --git a/static/upload_form.html b/static/upload_form.html index 5636664..670d4c4 100644 --- a/static/upload_form.html +++ b/static/upload_form.html @@ -1,14 +1,22 @@ - - -

AP HTTP

+ + + + + + Upload Form + + + +

AP HTTP

+

Upload

-
-
- - - -
-
- +
+ + + +
+
+ + diff --git a/template/colors.html b/template/colors.html index f055243..a3e92fe 100644 --- a/template/colors.html +++ b/template/colors.html @@ -4,63 +4,66 @@ - - Document + Colors Page
- Choose color : + Choose color: - Red - Navy - Green - -
- - Your name : - - - + + + +
+
+
<% - cout << context["name"]; - %> + cout << context["name"]; + %>
<% } %> <% - if(context["color"] == "navy") - { + if (context["color"] == "navy") { %> -
+
<% - cout << context["name"]; - %> + cout << context["name"]; + %>
<% } %> <% - if(context["color"] == "green") - { + if (context["color"] == "green") { %> -
+
<% - cout << context["name"]; - %> + cout << context["name"]; + %>
<% } %> - - \ No newline at end of file + diff --git a/utils/request.cpp b/utils/request.cpp index 026244f..1c6614e 100644 --- a/utils/request.cpp +++ b/utils/request.cpp @@ -20,7 +20,12 @@ Request::Request(string method_) { string Request::getQueryParam(string key) { return utils::urlDecode(query[key]); } -string Request::getBodyParam(string key) { return utils::urlDecode(body[key]); } +string Request::getBodyParam(string key) { + if (bodyTypes[key] == "application/x-www-form-urlencoded") { + return utils::urlDecode(body[key]); + } + else return body[key]; +} string Request::getHeader(string key) { return utils::urlDecode(headers[key]); } @@ -36,8 +41,9 @@ void Request::setQueryParam(string key, string value, bool encode) { query[key] = encode ? utils::urlEncode(value) : value; } -void Request::setBodyParam(string key, string value, bool encode) { +void Request::setBodyParam(string key, string value, string contentType, bool encode) { body[key] = encode ? utils::urlEncode(value) : value; + bodyTypes[key] = contentType; } void Request::setHeader(string key, string value, bool encode) { @@ -74,27 +80,35 @@ void Request::log() { const string NC = "\033[0;39m"; const string K = "\033[1m"; const string H = "\033[33;1m"; - string log = ""; - log += H + string("------- Request --------") + NC + string("\n"); - log += - K + string("Method:\t") + NC + (method ? "POST" : "GET") + string("\n"); - log += K + string("Path:\t") + NC + path + string("\n"); - log += K + string("Headers:") + NC + string("\n"); - for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) - log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + - string("\n"); - log += "[ " + K + string("SessionId:\t") + NC + this->getSessionId() + " ]" + - string("\n"); - log += K + string("Query:") + NC + string("\n"); - for (auto it = query.begin(); !query.empty() && it != query.end(); it++) - log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + - string("\n"); - log += K + string("Body:") + NC + string("\n"); - for (auto it = body.begin(); !body.empty() && it != body.end(); it++) - log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + - string("\n"); - log += H + string("------------------------") + NC + string("\n"); - cerr << log << endl; + + string log; + log += H + "------- Request --------" + NC + "\n"; + log += K + "Method:\t" + NC + (method == Method::POST ? "POST" : "GET") + "\n"; + log += K + "Path:\t" + NC + path + "\n"; + log += K + "SessionId:\t" + NC + this->getSessionId() + "\n"; + + log += K + "Headers:" + NC + "\n"; + for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) { + log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + "\n"; + } + + log += K + "Query:" + NC + "\n"; + for (auto it = query.begin(); !query.empty() && it != query.end(); it++) { + log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + "\n"; + } + + log += K + "Body:" + NC + "\n"; + for (auto it = body.begin(); !body.empty() && it != body.end(); it++) { + string type = bodyTypes[it->first]; + if (type == "application/x-www-urlencoded" || type == "text/plain") { + log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + "\n"; + } + else { + log += " " + utils::urlDecode(it->first) + ": \n"; + } + } + log += H + "------------------------" + NC + "\n"; + clog << log << endl; } utils::CiMap Request::getHeaders() { diff --git a/utils/request.hpp b/utils/request.hpp index f74bee0..e2edf03 100644 --- a/utils/request.hpp +++ b/utils/request.hpp @@ -16,7 +16,7 @@ class Request { std::string getQueryParam(std::string key); void setQueryParam(std::string key, std::string value, bool encode = true); std::string getBodyParam(std::string key); - void setBodyParam(std::string key, std::string value, bool encode = true); + void setBodyParam(std::string key, std::string value, std::string contentType = "text/plain", bool encode = true); std::string getHeader(std::string key); void setHeader(std::string key, std::string value, bool encode = true); std::string getBody(); @@ -38,6 +38,7 @@ class Request { utils::CiMap headers; utils::CiMap query; utils::CiMap body; + utils::CiMap bodyTypes; }; #endif // REQUEST_HPP_INCLUDE diff --git a/utils/utilities.cpp b/utils/utilities.cpp index 32b544b..6b050c0 100644 --- a/utils/utilities.cpp +++ b/utils/utilities.cpp @@ -48,6 +48,11 @@ std::string urlEncode(const std::string& url) { for (size_t pos = 0; pos < url.size(); ++pos) { switch (url[pos]) { + default: + if (url[pos] >= 32 && url[pos] < 127) { + result += url[pos]; + break; + } case '$': case '&': case '+': @@ -75,11 +80,6 @@ std::string urlEncode(const std::string& url) { sprintf(encode_buf + 1, "%02X", url[pos]); result += encode_buf; break; - default: - if (url[pos] >= 32 && url[pos] < 127) { - result += url[pos]; - break; - } } }; return result; From 3b7e4bf503e8afcb3581a1edcd4daf60071db137 Mon Sep 17 00:00:00 2001 From: MisaghM Date: Tue, 9 Jan 2024 01:20:15 +0330 Subject: [PATCH 12/23] Small fix --- server/server.cpp | 5 +---- utils/request.cpp | 8 ++++---- utils/response.cpp | 24 +++++++++++++----------- utils/response.hpp | 35 ++++++++++++++++++----------------- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/server/server.cpp b/server/server.cpp index 3f40e32..a7fdf6b 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -164,7 +164,7 @@ Request* parseRawReq(char* reqData, size_t length) { b.pop_back(); // remove "\r\n" from start and end of each boundary b.pop_back(); b.erase(b.begin(), b.begin() + 2); - string boundaryContentType; + string boundaryContentType = "text/plain"; size_t endOfBoundaryHeader = b.find("\r\n\r\n") + 4; vector abc = utils::split(b.substr(0, endOfBoundaryHeader - 4), "\r\n"); @@ -192,9 +192,6 @@ Request* parseRawReq(char* reqData, size_t length) { boundaryContentType = utils::tolower(R[1]); } } - if (boundaryContentType.empty()) { - boundaryContentType = "text/plain"; - } lastFieldValue = b.substr(endOfBoundaryHeader); req->setBodyParam(lastFieldKey, lastFieldValue, boundaryContentType, false); } diff --git a/utils/request.cpp b/utils/request.cpp index 1c6614e..35b5a5d 100644 --- a/utils/request.cpp +++ b/utils/request.cpp @@ -83,9 +83,9 @@ void Request::log() { string log; log += H + "------- Request --------" + NC + "\n"; - log += K + "Method:\t" + NC + (method == Method::POST ? "POST" : "GET") + "\n"; - log += K + "Path:\t" + NC + path + "\n"; - log += K + "SessionId:\t" + NC + this->getSessionId() + "\n"; + log += K + "Method: " + NC + (method == Method::POST ? "POST" : "GET") + "\n"; + log += K + "Path: " + NC + path + "\n"; + log += K + "SessionId: " + NC + this->getSessionId() + "\n"; log += K + "Headers:" + NC + "\n"; for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) { @@ -100,7 +100,7 @@ void Request::log() { log += K + "Body:" + NC + "\n"; for (auto it = body.begin(); !body.empty() && it != body.end(); it++) { string type = bodyTypes[it->first]; - if (type == "application/x-www-urlencoded" || type == "text/plain") { + if (type == "application/x-www-form-urlencoded" || type == "text/plain") { log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + "\n"; } else { diff --git a/utils/response.cpp b/utils/response.cpp index 48ffd4a..684f1b3 100644 --- a/utils/response.cpp +++ b/utils/response.cpp @@ -51,17 +51,19 @@ void Response::log(bool showBody) { const string H = "\033[34;1m"; const string G = "\033[32m"; const string R = "\033[31m"; - string log = ""; - log += H + string("------- Response -------") + NC + string("\n"); - log += K + string("Status:\t") + NC + (code == 200 ? G : R) + - to_string(code) + " " + phrase + NC + string("\n"); - log += K + string("Headers:") + NC + string("\n"); - for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) - log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + - string("\n"); - if (showBody) - log += K + string("Body:\n") + NC + body + string("\n"); - log += H + string("------------------------") + NC + string("\n"); + + string log; + log += H + "------- Response -------" + NC + "\n"; + log += K + "Status: " + NC + (code == 200 ? G : R) + to_string(code) + " " + phrase + NC + "\n"; + log += K + "Headers:" + NC + "\n"; + + for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) { + log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + "\n"; + } + if (showBody) { + log += K + "Body:\n" + NC + body + "\n"; + } + log += H + "------------------------" + NC + "\n"; cerr << log << endl; } diff --git a/utils/response.hpp b/utils/response.hpp index 087b9e1..137b4cb 100644 --- a/utils/response.hpp +++ b/utils/response.hpp @@ -1,33 +1,34 @@ #ifndef RESPONSE_HPP_INCLUDE #define RESPONSE_HPP_INCLUDE +#include + #include "../utils/include.hpp" #include "../utils/utilities.hpp" -#include const std::string SERVER_NAME = "AP HTTP Server"; class Response { public: - Response(int code = 200); - std::string print(int &); - void log(bool showBody = false); - void setHeader(std::string name, std::string value); - void setBody(std::string _body); - void setStatus(int code, std::string phrase); - void setStatus(int code); - int getStatusCode(); - std::string getStatusPhrase(); - std::string getHeader(std::string name); - void setSessionId(std::string sessionId); - static Response *redirect(std::string url); + Response(int code = 200); + std::string print(int&); + void log(bool showBody = false); + void setHeader(std::string name, std::string value); + void setBody(std::string _body); + void setStatus(int code, std::string phrase); + void setStatus(int code); + int getStatusCode(); + std::string getStatusPhrase(); + std::string getHeader(std::string name); + void setSessionId(std::string sessionId); + static Response* redirect(std::string url); private: - int code; + int code; - std::string phrase; - std::string body; - utils::CiMap headers; + std::string phrase; + std::string body; + utils::CiMap headers; }; #endif // RESPONSE_HPP_INCLUDE From 249769b7e0eabb794134cd6c56cc7222e19093ee Mon Sep 17 00:00:00 2001 From: MisaghM Date: Tue, 9 Jan 2024 01:31:20 +0330 Subject: [PATCH 13/23] Rename makefile --- makefile => Makefile | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename makefile => Makefile (100%) diff --git a/makefile b/Makefile similarity index 100% rename from makefile rename to Makefile From 80a8ed0006471d456783ecc16146435a955560f3 Mon Sep 17 00:00:00 2001 From: MisaghM Date: Tue, 9 Jan 2024 01:58:38 +0330 Subject: [PATCH 14/23] Update makefile --- Makefile | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index c2568fd..972a459 100644 --- a/Makefile +++ b/Makefile @@ -1,48 +1,49 @@ -CC=g++ -STD=-std=c++11 -Wall -pedantic -g -CF=$(STD) -BUILD_DIR=build -TEMPLATE_DIR=.template +CXX = g++ +CXXFLAGS = -std=c++11 -Wall -pedantic + +BUILD_DIR = build +TEMPLATE_DIR = .template +OUT_EXE = myserver.out ifeq ($(OS),Windows_NT) LDLIBS += -l Ws2_32 endif -all: $(BUILD_DIR) myserver.out +all: $(BUILD_DIR) $(OUT_EXE) + +$(OUT_EXE): $(BUILD_DIR)/main.o $(BUILD_DIR)/handlers.o $(BUILD_DIR)/response.o $(BUILD_DIR)/request.o $(BUILD_DIR)/utilities.o $(BUILD_DIR)/strutils.o $(BUILD_DIR)/server.o $(BUILD_DIR)/route.o $(BUILD_DIR)/template_parser.o + $(CXX) $(CXXFLAGS) $^ $(LDLIBS) -o $@ $(BUILD_DIR): mkdir -p $(BUILD_DIR) -$(BUILD_DIR)/template_parser.o: utils/template_parser.cpp utils/template_parser.hpp utils/request.cpp utils/request.hpp utils/utilities.hpp utils/utilities.cpp - $(CC) $(CF) -c utils/template_parser.cpp -o $(BUILD_DIR)/template_parser.o +$(BUILD_DIR)/template_parser.o: utils/template_parser.cpp utils/template_parser.hpp utils/request.cpp utils/request.hpp utils/utilities.hpp utils/utilities.cpp utils/strutils.hpp utils/strutils.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ $(BUILD_DIR)/response.o: utils/response.cpp utils/response.hpp utils/include.hpp - $(CC) $(CF) -c utils/response.cpp -o $(BUILD_DIR)/response.o + $(CXX) $(CXXFLAGS) -c $< -o $@ $(BUILD_DIR)/request.o: utils/request.cpp utils/request.hpp utils/include.hpp utils/utilities.hpp - $(CC) $(CF) -c utils/request.cpp -o $(BUILD_DIR)/request.o + $(CXX) $(CXXFLAGS) -c $< -o $@ -$(BUILD_DIR)/utilities.o: utils/utilities.cpp utils/utilities.hpp - $(CC) $(CF) -c utils/utilities.cpp -o $(BUILD_DIR)/utilities.o +$(BUILD_DIR)/utilities.o: utils/utilities.cpp utils/utilities.hpp utils/strutils.hpp + $(CXX) $(CXXFLAGS) -c $< -o $@ $(BUILD_DIR)/strutils.o: utils/strutils.cpp utils/strutils.hpp - $(CC) $(CF) -c utils/strutils.cpp -o $(BUILD_DIR)/strutils.o + $(CXX) $(CXXFLAGS) -c $< -o $@ -$(BUILD_DIR)/server.o: server/server.cpp server/server.hpp server/route.hpp utils/utilities.hpp utils/response.hpp utils/request.hpp utils/include.hpp utils/template_parser.hpp utils/template_parser.cpp - $(CC) $(CF) -c server/server.cpp -o $(BUILD_DIR)/server.o +$(BUILD_DIR)/server.o: server/server.cpp server/server.hpp server/route.hpp utils/utilities.hpp utils/strutils.hpp utils/response.hpp utils/request.hpp utils/include.hpp utils/template_parser.hpp utils/template_parser.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ $(BUILD_DIR)/route.o: server/route.cpp server/route.hpp utils/utilities.hpp utils/response.hpp utils/request.hpp utils/include.hpp - $(CC) $(CF) -c server/route.cpp -o $(BUILD_DIR)/route.o + $(CXX) $(CXXFLAGS) -c $< -o $@ $(BUILD_DIR)/handlers.o: examples/handlers.cpp server/server.hpp utils/utilities.hpp utils/response.hpp utils/request.hpp utils/include.hpp - $(CC) $(CF) -c examples/handlers.cpp -o $(BUILD_DIR)/handlers.o + $(CXX) $(CXXFLAGS) -c $< -o $@ $(BUILD_DIR)/main.o: examples/main.cpp server/server.hpp utils/utilities.hpp utils/response.hpp utils/request.hpp utils/include.hpp - $(CC) $(CF) -c examples/main.cpp -o $(BUILD_DIR)/main.o - -myserver.out: $(BUILD_DIR)/main.o $(BUILD_DIR)/handlers.o $(BUILD_DIR)/response.o $(BUILD_DIR)/request.o $(BUILD_DIR)/utilities.o $(BUILD_DIR)/strutils.o $(BUILD_DIR)/server.o $(BUILD_DIR)/route.o $(BUILD_DIR)/template_parser.o - $(CC) $(CF) $(BUILD_DIR)/main.o $(BUILD_DIR)/handlers.o $(BUILD_DIR)/response.o $(BUILD_DIR)/request.o $(BUILD_DIR)/utilities.o $(BUILD_DIR)/strutils.o $(BUILD_DIR)/server.o $(BUILD_DIR)/route.o $(BUILD_DIR)/template_parser.o $(LDLIBS) -o myserver.out + $(CXX) $(CXXFLAGS) -c $< -o $@ -.PHONY: clean +.PHONY: all clean clean: rm -rf $(BUILD_DIR) $(TEMPLATE_DIR) *.o *.out &> /dev/null From f853e95256720a0e78a7415b9c066aa039748811 Mon Sep 17 00:00:00 2001 From: Pasha Barahimi Date: Tue, 9 Jan 2024 09:55:37 +0330 Subject: [PATCH 15/23] Fix namespace comment --- utils/strutils.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/strutils.hpp b/utils/strutils.hpp index 8c9867e..4a945fc 100644 --- a/utils/strutils.hpp +++ b/utils/strutils.hpp @@ -19,6 +19,6 @@ std::string tolower(const std::string& str); void replaceAll(std::string& str, const std::string& from, const std::string& to); bool startsWith(const std::string& str, const std::string& s); -} // namespace strutils +} // namespace utils #endif // STRUTILS_HPP_INCLUDE From 51764db723f1e1d3bd11c958f7f453e403a97aef Mon Sep 17 00:00:00 2001 From: MisaghM Date: Mon, 13 May 2024 17:43:04 +0330 Subject: [PATCH 16/23] Refactor request class --- server/route.cpp | 4 +- server/route.hpp | 6 +- server/server.cpp | 4 +- utils/include.hpp | 5 -- utils/request.cpp | 202 +++++++++++++++++++--------------------------- utils/request.hpp | 50 ++++++------ 6 files changed, 113 insertions(+), 158 deletions(-) diff --git a/server/route.cpp b/server/route.cpp index 19d15d6..c50b27d 100644 --- a/server/route.cpp +++ b/server/route.cpp @@ -4,14 +4,14 @@ using namespace std; -Route::Route(Method _method, string _path) { +Route::Route(Request::Method _method, string _path) { method = _method; path = _path; } void Route::setHandler(RequestHandler* _handler) { handler = _handler; } -bool Route::isMatch(Method _method, string url) { +bool Route::isMatch(Request::Method _method, string url) { return (url == path) && (_method == method); } diff --git a/server/route.hpp b/server/route.hpp index 381222f..bac9285 100644 --- a/server/route.hpp +++ b/server/route.hpp @@ -11,14 +11,14 @@ class RequestHandler; class Route { private: - Method method; + Request::Method method; std::string path; RequestHandler* handler; public: - Route(Method _method, std::string _path); + Route(Request::Method _method, std::string _path); ~Route(); - bool isMatch(Method, std::string url); + bool isMatch(Request::Method, std::string url); Response* handle(Request* req); void setHandler(RequestHandler* _handler); }; diff --git a/server/server.cpp b/server/server.cpp index a7fdf6b..2895f1a 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -245,13 +245,13 @@ Server::Server(int _port) : port(_port) { } void Server::get(string path, RequestHandler* handler) { - Route* route = new Route(GET, path); + Route* route = new Route(Request::Method::GET, path); route->setHandler(handler); routes.push_back(route); } void Server::post(string path, RequestHandler* handler) { - Route* route = new Route(POST, path); + Route* route = new Route(Request::Method::POST, path); route->setHandler(handler); routes.push_back(route); } diff --git a/utils/include.hpp b/utils/include.hpp index af300e5..cc48f5e 100644 --- a/utils/include.hpp +++ b/utils/include.hpp @@ -3,9 +3,4 @@ constexpr int BUFSIZE = 10 * 1024 * 1024; // 10MB -enum Method { - GET, - POST -}; - #endif // INCLUDE_HPP_INCLUDE diff --git a/utils/request.cpp b/utils/request.cpp index 35b5a5d..e982cb6 100644 --- a/utils/request.cpp +++ b/utils/request.cpp @@ -6,82 +6,113 @@ #include #include -#include "../utils/strutils.hpp" -#include "../utils/utilities.hpp" +#include "strutils.hpp" -using namespace std; +Request::Request(Method method_) + : method(method_) {} -Request::Request(string method_) { - if (method_ == "GET") - method = GET; - if (method_ == "POST") - method = POST; +Request::Request(const std::string& method_) { + if (method_ == "GET") { + method = Method::GET; + } + else if (method_ == "POST") { + method = Method::POST; + } } -string Request::getQueryParam(string key) { return utils::urlDecode(query[key]); } +Request::Method Request::getMethod() const { + return method; +} -string Request::getBodyParam(string key) { - if (bodyTypes[key] == "application/x-www-form-urlencoded") { - return utils::urlDecode(body[key]); +std::string Request::getPath() const { + return path; +} + +std::string Request::getHeader(const std::string& key) const { + auto itr = headers.find(key); + if (itr == headers.end()) { + return {}; } - else return body[key]; + return utils::urlDecode(itr->second); } -string Request::getHeader(string key) { return utils::urlDecode(headers[key]); } +std::string Request::getBody() const { + std::string bs; + for (auto itr = body.begin(); !body.empty() && itr != body.end(); ++itr) { + bs += itr->first + "=" + itr->second + "&"; + } + return bs; +} -string Request::getPath() { return path; } +std::string Request::getQueryParam(const std::string& key) const { + auto itr = query.find(key); + if (itr == query.end()) { + return {}; + } + return utils::urlDecode(itr->second); +} -void Request::setPath(string _path) { path = _path; } +std::string Request::getBodyParam(const std::string& key) const { + auto bodyType = bodyTypes.find(key); + auto itr = body.find(key); + if (bodyType == bodyTypes.end() || itr == body.end()) { + return {}; + } -Method Request::getMethod() { return method; } + if (bodyType->second == "application/x-www-form-urlencoded") { + return utils::urlDecode(itr->second); + } + return itr->second; +} -void Request::setMethod(Method _method) { method = _method; } +static void trim(std::string& s) { + s.erase(std::remove(s.begin(), s.end(), ' '), s.end()); +} -void Request::setQueryParam(string key, string value, bool encode) { - query[key] = encode ? utils::urlEncode(value) : value; +std::string Request::getSessionId() const { + std::string cookie = getHeader("cookie"); + if (cookie.empty()) { + return {}; + } + trim(cookie); + std::vector v = utils::split(cookie, ";"); + for (std::string kv : v) { + trim(kv); + std::vector k = utils::split(kv, "="); + if (k[0] == "sessionId") { + return k[1]; + } + } + return {}; } -void Request::setBodyParam(string key, string value, string contentType, bool encode) { - body[key] = encode ? utils::urlEncode(value) : value; - bodyTypes[key] = contentType; +void Request::setPath(const std::string& _path) { + path = _path; } -void Request::setHeader(string key, string value, bool encode) { +void Request::setHeader(const std::string& key, const std::string& value, bool encode) { headers[key] = encode ? utils::urlEncode(value) : value; } -string Request::getBody() { - string bs = ""; - for (auto it = body.begin(); !body.empty() && it != body.end(); it++) - bs += it->first + "=" + it->second + "&"; - return bs; +void Request::setBody(const std::string& _body) { + body = utils::getCimapFromString(_body); } -static void trim(string& s) { - s.erase(std::remove(s.begin(), s.end(), ' '), s.end()); +void Request::setQueryParam(const std::string& key, const std::string& value, bool encode) { + query[key] = encode ? utils::urlEncode(value) : value; } -string Request::getSessionId() { - string cookie = getHeader("cookie"); - if (cookie == "") - return ""; - trim(cookie); - vector v = utils::split(cookie, ";"); - for (string kv : v) { - trim(kv); - vector k = utils::split(kv, "="); - if (k[0] == "sessionId") - return k[1]; - } - return ""; +void Request::setBodyParam(const std::string& key, const std::string& value, const std::string& contentType, bool encode) { + body[key] = encode ? utils::urlEncode(value) : value; + bodyTypes[key] = contentType; } void Request::log() { - const string NC = "\033[0;39m"; - const string K = "\033[1m"; - const string H = "\033[33;1m"; + const std::string NC = "\033[0;39m"; + const std::string K = "\033[1m"; + const std::string H = "\033[33;1m"; - string log; + std::string log; log += H + "------- Request --------" + NC + "\n"; log += K + "Method: " + NC + (method == Method::POST ? "POST" : "GET") + "\n"; log += K + "Path: " + NC + path + "\n"; @@ -99,7 +130,7 @@ void Request::log() { log += K + "Body:" + NC + "\n"; for (auto it = body.begin(); !body.empty() && it != body.end(); it++) { - string type = bodyTypes[it->first]; + std::string type = bodyTypes[it->first]; if (type == "application/x-www-form-urlencoded" || type == "text/plain") { log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + "\n"; } @@ -108,76 +139,5 @@ void Request::log() { } } log += H + "------------------------" + NC + "\n"; - clog << log << endl; -} - -utils::CiMap Request::getHeaders() { - vector res; - for (map::iterator i = headers.begin(); - !headers.empty() && i != headers.end(); i++) { - res.push_back(i->first); - res.push_back(i->second); - } - return headers; -} - -string Request::getQueryString() { - if (query.empty()) - return ""; - string res = "?"; - for (map::iterator i = query.begin(); - !query.empty() && i != query.end(); i++) { - res += i->first; - res += "="; - res += i->second; - res += "&"; - } - return res; -} - -string Request::getHeadersString() { - string headerString = ""; - for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) - headerString += it->first + "=" + it->second + "&"; - return headerString; -} - -void Request::setHeaders(string _headers) { - headers = utils::getCimapFromString(_headers); -} - -void Request::setQuery(std::string _query) { - _query = _query.substr(1); - query = utils::getCimapFromString(_query); -} - -void Request::setBody(std::string _body) { body = utils::getCimapFromString(_body); } - -void Request::serializeToFile(Request* req, string filePath) { - string reqString = to_string(req->getMethod()); - reqString += "\n"; - reqString += req->getPath(); - reqString += "\n"; - reqString += req->getHeadersString(); - reqString += "\n"; - reqString += req->getBody(); - reqString += "\n"; - reqString += req->getQueryString(); - utils::writeToFile(reqString, filePath); -} - -void Request::deserializeFromFile(Request* req, string filePath) { - vector fields = utils::split(utils::readFile(filePath), '\n'); - switch (fields.size()) { - case 5: - req->setQuery(fields[4]); - case 4: - req->setBody(fields[3]); - case 3: - req->setHeaders(fields[2]); - case 2: - req->setPath(fields[1]); - case 1: - req->setMethod(stoi(fields[0]) == GET ? GET : POST); - } + std::clog << log << std::endl; } diff --git a/utils/request.hpp b/utils/request.hpp index e2edf03..ed09cb2 100644 --- a/utils/request.hpp +++ b/utils/request.hpp @@ -3,38 +3,38 @@ #include -#include "../utils/include.hpp" -#include "../utils/utilities.hpp" +#include "utilities.hpp" class Request { public: - Request(std::string method = "GET"); - std::string getPath(); - void setPath(std::string); - Method getMethod(); - void setMethod(Method); - std::string getQueryParam(std::string key); - void setQueryParam(std::string key, std::string value, bool encode = true); - std::string getBodyParam(std::string key); - void setBodyParam(std::string key, std::string value, std::string contentType = "text/plain", bool encode = true); - std::string getHeader(std::string key); - void setHeader(std::string key, std::string value, bool encode = true); - std::string getBody(); - std::string getSessionId(); - void setSessionId(std::string); - std::string getQueryString(); - utils::CiMap getHeaders(); - std::string getHeadersString(); - void setHeaders(std::string headers); - void setQuery(std::string query); - void setBody(std::string body); + enum class Method { + GET, + POST + }; + + Request(Method method); + Request(const std::string& method); + + void setPath(const std::string& path); + void setHeader(const std::string& key, const std::string& value, bool encode = true); + void setBody(const std::string& body); + void setQueryParam(const std::string& key, const std::string& value, bool encode = true); + void setBodyParam(const std::string& key, const std::string& value, + const std::string& contentType = "text/plain", bool encode = true); + + Method getMethod() const; + std::string getPath() const; + std::string getHeader(const std::string& key) const; + std::string getBody() const; + std::string getQueryParam(const std::string& key) const; + std::string getBodyParam(const std::string& key) const; + std::string getSessionId() const; + void log(); - static void serializeToFile(Request* req, std::string filePath); - static void deserializeFromFile(Request* req, std::string filePath); private: - std::string path; Method method; + std::string path; utils::CiMap headers; utils::CiMap query; utils::CiMap body; From 208e1afc685eac106dce5a5c93674bbd151cb4da Mon Sep 17 00:00:00 2001 From: MisaghM Date: Mon, 13 May 2024 18:38:11 +0330 Subject: [PATCH 17/23] Add put and delete HTTP methods --- server/route.cpp | 24 ++++++++++++++---------- server/route.hpp | 15 ++++++++------- server/server.cpp | 24 +++++++++++++++++------- server/server.hpp | 12 +++++++++--- utils/request.cpp | 21 +++++++++++++-------- utils/request.hpp | 7 ++++++- 6 files changed, 67 insertions(+), 36 deletions(-) diff --git a/server/route.cpp b/server/route.cpp index c50b27d..70fdd7c 100644 --- a/server/route.cpp +++ b/server/route.cpp @@ -2,19 +2,23 @@ #include "server.hpp" -using namespace std; +Route::Route(Request::Method _method, const std::string& _path) + : method(_method), + path(_path), + handler(nullptr) {} -Route::Route(Request::Method _method, string _path) { - method = _method; - path = _path; +Route::~Route() { + delete handler; } -void Route::setHandler(RequestHandler* _handler) { handler = _handler; } - -bool Route::isMatch(Request::Method _method, string url) { - return (url == path) && (_method == method); +void Route::setHandler(RequestHandler* _handler) { + handler = _handler; } -Response* Route::handle(Request* req) { return handler->callback(req); } +Response* Route::handle(Request* req) { + return handler->callback(req); +} -Route::~Route() { delete handler; } +bool Route::isMatch(Request::Method _method, const std::string& url) { + return (_method == method) && (url == path); +} diff --git a/server/route.hpp b/server/route.hpp index bac9285..9c2b030 100644 --- a/server/route.hpp +++ b/server/route.hpp @@ -10,17 +10,18 @@ class RequestHandler; class Route { +public: + Route(Request::Method _method, const std::string& _path); + ~Route(); + + void setHandler(RequestHandler* _handler); + Response* handle(Request* req); + bool isMatch(Request::Method, const std::string& url); + private: Request::Method method; std::string path; RequestHandler* handler; - -public: - Route(Request::Method _method, std::string _path); - ~Route(); - bool isMatch(Request::Method, std::string url); - Response* handle(Request* req); - void setHandler(RequestHandler* _handler); }; #endif // ROUTE_HPP_INCLUDE diff --git a/server/server.cpp b/server/server.cpp index 2895f1a..1c570c6 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -244,16 +244,26 @@ Server::Server(int _port) : port(_port) { } } -void Server::get(string path, RequestHandler* handler) { - Route* route = new Route(Request::Method::GET, path); +void Server::mapRequest(const string& path, RequestHandler* handler, Request::Method method) { + Route* route = new Route(method, path); route->setHandler(handler); routes.push_back(route); } -void Server::post(string path, RequestHandler* handler) { - Route* route = new Route(Request::Method::POST, path); - route->setHandler(handler); - routes.push_back(route); +void Server::get(const std::string& path, RequestHandler* handler) { + mapRequest(path, handler, Request::Method::GET); +} + +void Server::post(const std::string& path, RequestHandler* handler) { + mapRequest(path, handler, Request::Method::POST); +} + +void Server::put(const std::string& path, RequestHandler* handler) { + mapRequest(path, handler, Request::Method::PUT); +} + +void Server::del(const std::string& path, RequestHandler* handler) { + mapRequest(path, handler, Request::Method::DEL); } void Server::run() { @@ -350,7 +360,7 @@ ShowPage::ShowPage(string filePath) ShowImage::ShowImage(string filePath) : ShowFile(filePath, "image/" + utils::getExtension(filePath)) {} -void Server::setNotFoundErrPage(std::string notFoundErrPage) { +void Server::setNotFoundErrPage(const std::string& notFoundErrPage) { delete notFoundHandler; notFoundHandler = new NotFoundHandler(notFoundErrPage); } diff --git a/server/server.hpp b/server/server.hpp index fb01f8e..c034bd6 100644 --- a/server/server.hpp +++ b/server/server.hpp @@ -58,10 +58,14 @@ class Server { public: Server(int port = 5000); ~Server(); + void run(); - void get(std::string path, RequestHandler* handler); - void post(std::string path, RequestHandler* handler); - void setNotFoundErrPage(std::string); + + void get(const std::string& path, RequestHandler* handler); + void post(const std::string& path, RequestHandler* handler); + void put(const std::string& path, RequestHandler* handler); + void del(const std::string& path, RequestHandler* handler); + void setNotFoundErrPage(const std::string& notFoundErrPage); class Exception : public std::exception { public: @@ -78,6 +82,8 @@ class Server { int port; std::vector routes; RequestHandler* notFoundHandler; + + void mapRequest(const std::string& path, RequestHandler* handler, Request::Method method); }; #endif // SERVER_HPP_INCLUDE diff --git a/utils/request.cpp b/utils/request.cpp index e982cb6..71ee249 100644 --- a/utils/request.cpp +++ b/utils/request.cpp @@ -1,22 +1,27 @@ #include "request.hpp" #include -#include #include -#include +#include #include #include "strutils.hpp" +const std::unordered_map Request::methodMap = { + {"GET", Request::Method::GET}, + {"POST", Request::Method::POST}, + {"PUT", Request::Method::PUT}, + {"DEL", Request::Method::DEL}, +}; + Request::Request(Method method_) : method(method_) {} -Request::Request(const std::string& method_) { - if (method_ == "GET") { - method = Method::GET; - } - else if (method_ == "POST") { - method = Method::POST; +Request::Request(const std::string& method_) + : Request(Method::GET) { + auto itr = methodMap.find(method_); + if (itr != methodMap.end()) { + method = itr->second; } } diff --git a/utils/request.hpp b/utils/request.hpp index ed09cb2..d582eeb 100644 --- a/utils/request.hpp +++ b/utils/request.hpp @@ -2,6 +2,7 @@ #define REQUEST_HPP_INCLUDE #include +#include #include "utilities.hpp" @@ -9,7 +10,9 @@ class Request { public: enum class Method { GET, - POST + POST, + PUT, + DEL, }; Request(Method method); @@ -39,6 +42,8 @@ class Request { utils::CiMap query; utils::CiMap body; utils::CiMap bodyTypes; + + static const std::unordered_map methodMap; }; #endif // REQUEST_HPP_INCLUDE From 5af75e585d809d0dc33d1127631c98b53cf34d82 Mon Sep 17 00:00:00 2001 From: MisaghM Date: Thu, 30 May 2024 20:41:02 +0330 Subject: [PATCH 18/23] Refactor response class --- examples/handlers.cpp | 2 +- server/server.cpp | 16 +++---- utils/include.hpp | 1 + utils/request.cpp | 16 +++---- utils/response.cpp | 104 ++++++++++++++++++++++-------------------- utils/response.hpp | 47 +++++++++++++------ utils/utilities.cpp | 1 + utils/utilities.hpp | 2 - 8 files changed, 105 insertions(+), 84 deletions(-) diff --git a/examples/handlers.cpp b/examples/handlers.cpp index 011baa4..f7be76a 100644 --- a/examples/handlers.cpp +++ b/examples/handlers.cpp @@ -3,7 +3,7 @@ using namespace std; Response* RandomNumberHandler::callback(Request* req) { - Response* res = new Response; + Response* res = new Response(); res->setHeader("Content-Type", "text/html"); string body; body += ""; diff --git a/server/server.cpp b/server/server.cpp index 1c570c6..4ceb250 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -64,7 +64,7 @@ class NotFoundHandler : public RequestHandler { NotFoundHandler(string notFoundErrPage = "") : notFoundErrPage(notFoundErrPage) {} Response* callback(Request* req) { - Response* res = new Response(404); + Response* res = new Response(Response::Status::notFound); if (!notFoundErrPage.empty()) { res->setHeader("Content-Type", "text/" + utils::getExtension(notFoundErrPage)); res->setBody(utils::readFile(notFoundErrPage)); @@ -76,7 +76,7 @@ class NotFoundHandler : public RequestHandler { class ServerErrorHandler { public: static Response* callback(string msg) { - Response* res = new Response(500); + Response* res = new Response(Response::Status::internalServerError); res->setHeader("Content-Type", "application/json"); res->setBody("{ \"code\": \"500\", \"message\": \"" + msg + "\" }\n"); return res; @@ -278,11 +278,11 @@ void Server::run() { newsc = ::accept(sc, (struct sockaddr*)&cli_addr, &clilen); if (!ISVALIDSOCKET(newsc)) throw Exception("Error on accept: " + string(getSocketError())); - Response* res = NULL; + Response* res = nullptr; try { char* data = new char[BUFSIZE + 1]; size_t recv_len, recv_total_len = 0; - Request* req = NULL; + Request* req = nullptr; while (!req) { recv_len = recv(newsc, data + recv_total_len, BUFSIZE - recv_total_len, 0); if (recv_len > 0) { @@ -315,9 +315,9 @@ void Server::run() { delete res; res = ServerErrorHandler::callback(exc.getMessage()); } - int si; res->log(); - string res_data = res->print(si); + string res_data = res->print(); + int si = res_data.size(); delete res; int wr = send(newsc, res_data.c_str(), si, 0); if (wr != si) @@ -348,7 +348,7 @@ ShowFile::ShowFile(string _filePath, string _fileType) { } Response* ShowFile::callback(Request* req) { - Response* res = new Response; + Response* res = new Response(); res->setHeader("Content-Type", fileType); res->setBody(utils::readFile(filePath)); return res; @@ -375,7 +375,7 @@ TemplateHandler::TemplateHandler(string _filePath) { Response* TemplateHandler::callback(Request* req) { map context; context = this->handle(req); - Response* res = new Response; + Response* res = new Response(); res->setHeader("Content-Type", "text/html"); res->setBody(parser->getHtml(context)); return res; diff --git a/utils/include.hpp b/utils/include.hpp index cc48f5e..1e1c0b8 100644 --- a/utils/include.hpp +++ b/utils/include.hpp @@ -1,6 +1,7 @@ #ifndef INCLUDE_HPP_INCLUDE #define INCLUDE_HPP_INCLUDE +const std::string SERVER_NAME = "AP HTTP Server"; constexpr int BUFSIZE = 10 * 1024 * 1024; // 10MB #endif // INCLUDE_HPP_INCLUDE diff --git a/utils/request.cpp b/utils/request.cpp index 71ee249..4a786c3 100644 --- a/utils/request.cpp +++ b/utils/request.cpp @@ -124,23 +124,23 @@ void Request::log() { log += K + "SessionId: " + NC + this->getSessionId() + "\n"; log += K + "Headers:" + NC + "\n"; - for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) { - log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + "\n"; + for (auto itr = headers.begin(); itr != headers.end(); itr++) { + log += " " + utils::urlDecode(itr->first) + ": " + utils::urlDecode(itr->second) + "\n"; } log += K + "Query:" + NC + "\n"; - for (auto it = query.begin(); !query.empty() && it != query.end(); it++) { - log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + "\n"; + for (auto itr = query.begin(); itr != query.end(); itr++) { + log += " " + utils::urlDecode(itr->first) + ": " + utils::urlDecode(itr->second) + "\n"; } log += K + "Body:" + NC + "\n"; - for (auto it = body.begin(); !body.empty() && it != body.end(); it++) { - std::string type = bodyTypes[it->first]; + for (auto itr = body.begin(); itr != body.end(); itr++) { + std::string type = bodyTypes[itr->first]; if (type == "application/x-www-form-urlencoded" || type == "text/plain") { - log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + "\n"; + log += " " + utils::urlDecode(itr->first) + ": " + utils::urlDecode(itr->second) + "\n"; } else { - log += " " + utils::urlDecode(it->first) + ": \n"; + log += " " + utils::urlDecode(itr->first) + ": \n"; } } log += H + "------------------------" + NC + "\n"; diff --git a/utils/response.cpp b/utils/response.cpp index 684f1b3..c97aa44 100644 --- a/utils/response.cpp +++ b/utils/response.cpp @@ -1,84 +1,88 @@ #include "response.hpp" -#include #include -#include -using namespace std; +#include "include.hpp" -map getHttpPhrases() { - map httpPhrase; - httpPhrase[200] = "OK"; - httpPhrase[303] = "See Other"; - httpPhrase[404] = "Not Found"; - return httpPhrase; -} +const std::unordered_map Response::httpPhraseMap = { + {Status::ok, "OK"}, + {Status::created, "Created"}, + + {Status::movedPermanently, "Moved Permanently"}, + {Status::seeOther, "See Other"}, -map httpPhrase = getHttpPhrases(); + {Status::badRequest, "Bad Request"}, + {Status::unauthorized, "Unauthorized"}, + {Status::forbidden, "Forbidden"}, + {Status::notFound, "Not Found"}, + {Status::methodNotAllowed, "Method Not Allowed"}, + {Status::conflict, "Conflict"}, + {Status::teapot, "I'm a teapot"}, -Response::Response(int code) { - this->code = code; - this->phrase = httpPhrase[code]; - this->headers["Content-Type"] = "text/plain"; + {Status::internalServerError, "Internal Server Error"}, + {Status::notImplemented, "Not Implemented"}, +}; + +Response::Response(Status code_) + : code(static_cast(code_)), + phrase(httpPhraseMap.find(code_)->second) { + headers["Content-Type"] = "text/plain"; } -int Response::getStatusCode() { return code; } +Response::Response(int _code, const std::string& _phrase) + : code(_code), + phrase(_phrase) { + headers["Content-Type"] = "text/plain"; +} -string Response::getStatusPhrase() { return phrase; } +void Response::setHeader(const std::string& key, const std::string& value) { + headers[key] = value; +} -void Response::setStatus(int _code, string _phrase) { - phrase = _phrase; - code = _code; +void Response::setBody(const std::string& _body) { + body = _body; } -void Response::setStatus(int _code) { setStatus(_code, httpPhrase[_code]); } +void Response::setSessionId(const std::string& sessionId) { + setHeader("set-cookie", "sessionId=" + sessionId + ";"); +} -string Response::print(int& size) { - string header = ""; - header += "HTTP/1.0 " + to_string(code) + " " + phrase + "\r\n"; +std::string Response::print() { + std::string header; + header += "HTTP/1.0 " + std::to_string(code) + " " + phrase + "\r\n"; header += "Server: " + SERVER_NAME + " \r\n"; - header += "Content-Length: " + to_string(body.size()) + "\r\n"; - for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) - header += it->first + ": " + it->second + "\r\n"; + header += "Content-Length: " + std::to_string(body.size()) + "\r\n"; + for (auto itr = headers.begin(); itr != headers.end(); ++itr) { + header += itr->first + ": " + itr->second + "\r\n"; + } header += "\r\n"; - size = header.size() + body.size(); return header + body; } void Response::log(bool showBody) { - const string NC = "\033[0;39m"; - const string K = "\033[1m"; - const string H = "\033[34;1m"; - const string G = "\033[32m"; - const string R = "\033[31m"; + const std::string NC = "\033[0;39m"; + const std::string K = "\033[1m"; + const std::string H = "\033[34;1m"; + const std::string G = "\033[32m"; + const std::string R = "\033[31m"; - string log; + std::string log; log += H + "------- Response -------" + NC + "\n"; - log += K + "Status: " + NC + (code == 200 ? G : R) + to_string(code) + " " + phrase + NC + "\n"; + log += K + "Status: " + NC + (code == 200 ? G : R) + std::to_string(code) + " " + phrase + NC + "\n"; log += K + "Headers:" + NC + "\n"; - for (auto it = headers.begin(); !headers.empty() && it != headers.end(); it++) { - log += " " + utils::urlDecode(it->first) + ": " + utils::urlDecode(it->second) + "\n"; + for (auto itr = headers.begin(); itr != headers.end(); ++itr) { + log += " " + utils::urlDecode(itr->first) + ": " + utils::urlDecode(itr->second) + "\n"; } if (showBody) { log += K + "Body:\n" + NC + body + "\n"; } log += H + "------------------------" + NC + "\n"; - cerr << log << endl; -} - -void Response::setHeader(string name, string value) { headers[name] = value; } - -void Response::setBody(string _body) { body = _body; } - -string Response::getHeader(string name) { return ""; } - -void Response::setSessionId(string sessionId) { - setHeader("set-cookie", "sessionId=" + sessionId + ";"); + std::clog << log << std::endl; } -Response* Response::redirect(string url) { - Response* res = new Response(303); +Response* Response::redirect(const std::string& url) { + Response* res = new Response(Status::seeOther); res->setHeader("Location", url); return res; } diff --git a/utils/response.hpp b/utils/response.hpp index 137b4cb..b03bbc0 100644 --- a/utils/response.hpp +++ b/utils/response.hpp @@ -2,33 +2,50 @@ #define RESPONSE_HPP_INCLUDE #include +#include -#include "../utils/include.hpp" #include "../utils/utilities.hpp" -const std::string SERVER_NAME = "AP HTTP Server"; - class Response { public: - Response(int code = 200); - std::string print(int&); + enum class Status { + ok = 200, + created = 201, + + movedPermanently = 301, + seeOther = 303, + + badRequest = 400, + unauthorized = 401, + forbidden = 403, + notFound = 404, + methodNotAllowed = 405, + conflict = 409, + teapot = 418, + + internalServerError = 500, + notImplemented = 501, + }; + + Response(Status code = Status::ok); + Response(int code, const std::string& phrase); + + void setHeader(const std::string& key, const std::string& value); + void setBody(const std::string& _body); + void setSessionId(const std::string& sessionId); + + std::string print(); void log(bool showBody = false); - void setHeader(std::string name, std::string value); - void setBody(std::string _body); - void setStatus(int code, std::string phrase); - void setStatus(int code); - int getStatusCode(); - std::string getStatusPhrase(); - std::string getHeader(std::string name); - void setSessionId(std::string sessionId); - static Response* redirect(std::string url); + + static Response* redirect(const std::string& url); private: int code; - std::string phrase; std::string body; utils::CiMap headers; + + static const std::unordered_map httpPhraseMap; }; #endif // RESPONSE_HPP_INCLUDE diff --git a/utils/utilities.cpp b/utils/utilities.cpp index 6b050c0..0ab120d 100644 --- a/utils/utilities.cpp +++ b/utils/utilities.cpp @@ -3,6 +3,7 @@ #include #include +#include "include.hpp" #include "strutils.hpp" namespace utils { diff --git a/utils/utilities.hpp b/utils/utilities.hpp index 2a400f9..dc09bc5 100644 --- a/utils/utilities.hpp +++ b/utils/utilities.hpp @@ -4,8 +4,6 @@ #include #include -#include "include.hpp" - namespace utils { struct StringInsensitiveComp { From 0aa6503e5635b1b4f75539179eebe95934b5dd9d Mon Sep 17 00:00:00 2001 From: MisaghM Date: Thu, 30 May 2024 20:50:39 +0330 Subject: [PATCH 19/23] Change strutils namespace name --- server/server.cpp | 40 ++++++++++++++++++++-------------------- utils/request.cpp | 4 ++-- utils/strutils.cpp | 4 ++-- utils/strutils.hpp | 4 ++-- utils/utilities.cpp | 8 ++++---- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/server/server.cpp b/server/server.cpp index 4ceb250..c79dc5f 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -96,13 +96,13 @@ Request* parseRawReq(char* reqData, size_t length) { if (endOfHeader == string::npos) { throw Server::Exception("End of request header not found."); } - vector headers = utils::split(reqHeader, "\r\n"); + vector headers = strutils::split(reqHeader, "\r\n"); if (reqHeader.find('\0') != string::npos) { throw Server::Exception("Binary data in header."); } size_t realBodySize = length - endOfHeader - 4; // string("\r\n\r\n").size(); - vector R = utils::split(headers[0], ' '); + vector R = strutils::split(headers[0], ' '); if (R.size() != 3) { throw Server::Exception("Invalid header (request line)"); } @@ -110,9 +110,9 @@ Request* parseRawReq(char* reqData, size_t length) { req->setPath(R[1]); size_t pos = req->getPath().find('?'); if (pos != string::npos && pos != req->getPath().size() - 1) { - vector Q1 = utils::split(req->getPath().substr(pos + 1), '&'); + vector Q1 = strutils::split(req->getPath().substr(pos + 1), '&'); for (vector::size_type q = 0; q < Q1.size(); q++) { - vector Q2 = utils::split(Q1[q], '='); + vector Q2 = strutils::split(Q1[q], '='); if (Q2.size() == 2) req->setQueryParam(Q2[0], Q2[1], false); else @@ -123,23 +123,23 @@ Request* parseRawReq(char* reqData, size_t length) { for (size_t headerIndex = 1; headerIndex < headers.size(); headerIndex++) { string line = headers[headerIndex]; - vector R = utils::split(line, ": "); + vector R = strutils::split(line, ": "); if (R.size() != 2) throw Server::Exception("Invalid header"); req->setHeader(R[0], R[1], false); - if (utils::tolower(R[0]) == utils::tolower("Content-Length")) + if (strutils::tolower(R[0]) == strutils::tolower("Content-Length")) if (realBodySize != (size_t)atol(R[1].c_str())) return nullptr; } string contentType = req->getHeader("Content-Type"); if (realBodySize != 0 && !contentType.empty()) { - if (utils::startsWith(contentType, "application/x-www-form-urlencoded")) { - vector urlencodedParts = utils::split(reqBody, "\r\n"); + if (strutils::startsWith(contentType, "application/x-www-form-urlencoded")) { + vector urlencodedParts = strutils::split(reqBody, "\r\n"); for (const string& part : urlencodedParts) { - vector body = utils::split(part, '&'); + vector body = strutils::split(part, '&'); for (size_t i = 0; i < body.size(); i++) { - vector field = utils::split(body[i], '='); + vector field = strutils::split(body[i], '='); if (field.size() == 2) req->setBodyParam(field[0], field[1], "application/x-www-form-urlencoded", false); else if (field.size() == 1) @@ -149,7 +149,7 @@ Request* parseRawReq(char* reqData, size_t length) { } } } - else if (utils::startsWith(contentType, "multipart/form-data")) { + else if (strutils::startsWith(contentType, "multipart/form-data")) { boundary = contentType.substr(contentType.find("boundary=") + 9); size_t firstBoundary = reqBody.find("--" + boundary); if (firstBoundary == string::npos) { @@ -157,7 +157,7 @@ Request* parseRawReq(char* reqData, size_t length) { } reqBody.erase(reqBody.begin(), reqBody.begin() + firstBoundary + 2 + boundary.size()); - vector boundaries = utils::split(reqBody, "--" + boundary); + vector boundaries = strutils::split(reqBody, "--" + boundary); boundaries.pop_back(); for (string b : boundaries) { @@ -167,19 +167,19 @@ Request* parseRawReq(char* reqData, size_t length) { string boundaryContentType = "text/plain"; size_t endOfBoundaryHeader = b.find("\r\n\r\n") + 4; - vector abc = utils::split(b.substr(0, endOfBoundaryHeader - 4), "\r\n"); + vector abc = strutils::split(b.substr(0, endOfBoundaryHeader - 4), "\r\n"); for (const string& line : abc) { if (line.empty()) { break; } - vector R = utils::split(line, ": "); + vector R = strutils::split(line, ": "); if (R.size() != 2) throw Server::Exception("Invalid header"); - if (utils::tolower(R[0]) == utils::tolower("Content-Disposition")) { - vector A = utils::split(R[1], "; "); + if (strutils::tolower(R[0]) == strutils::tolower("Content-Disposition")) { + vector A = strutils::split(R[1], "; "); for (size_t i = 0; i < A.size(); i++) { - vector attr = utils::split(A[i], '='); + vector attr = strutils::split(A[i], '='); if (attr.size() == 2) { - if (utils::tolower(attr[0]) == utils::tolower("name")) { + if (strutils::tolower(attr[0]) == strutils::tolower("name")) { lastFieldKey = attr[1].substr(1, attr[1].size() - 2); } } @@ -188,8 +188,8 @@ Request* parseRawReq(char* reqData, size_t length) { } } } - else if (utils::tolower(R[0]) == utils::tolower("Content-Type")) { - boundaryContentType = utils::tolower(R[1]); + else if (strutils::tolower(R[0]) == strutils::tolower("Content-Type")) { + boundaryContentType = strutils::tolower(R[1]); } } lastFieldValue = b.substr(endOfBoundaryHeader); diff --git a/utils/request.cpp b/utils/request.cpp index 4a786c3..6b85d6a 100644 --- a/utils/request.cpp +++ b/utils/request.cpp @@ -80,10 +80,10 @@ std::string Request::getSessionId() const { return {}; } trim(cookie); - std::vector v = utils::split(cookie, ";"); + std::vector v = strutils::split(cookie, ";"); for (std::string kv : v) { trim(kv); - std::vector k = utils::split(kv, "="); + std::vector k = strutils::split(kv, "="); if (k[0] == "sessionId") { return k[1]; } diff --git a/utils/strutils.cpp b/utils/strutils.cpp index 3b7b543..7664f0c 100644 --- a/utils/strutils.cpp +++ b/utils/strutils.cpp @@ -4,7 +4,7 @@ #include #include -namespace utils { +namespace strutils { void trimLeft(std::string& str) { str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](unsigned char ch) { @@ -78,4 +78,4 @@ bool startsWith(const std::string& str, const std::string& s) { std::equal(s.begin(), s.end(), str.begin()); } -} // namespace utils +} // namespace strutils diff --git a/utils/strutils.hpp b/utils/strutils.hpp index 4a945fc..65c8c1b 100644 --- a/utils/strutils.hpp +++ b/utils/strutils.hpp @@ -4,7 +4,7 @@ #include #include -namespace utils { +namespace strutils { void trimLeft(std::string& str); void trimRight(std::string& str); @@ -19,6 +19,6 @@ std::string tolower(const std::string& str); void replaceAll(std::string& str, const std::string& from, const std::string& to); bool startsWith(const std::string& str, const std::string& s); -} // namespace utils +} // namespace strutils #endif // STRUTILS_HPP_INCLUDE diff --git a/utils/utilities.cpp b/utils/utilities.cpp index 0ab120d..ca9266e 100644 --- a/utils/utilities.cpp +++ b/utils/utilities.cpp @@ -9,7 +9,7 @@ namespace utils { bool StringInsensitiveComp::operator()(const std::string& lhs, const std::string& rhs) const { - return tolower(lhs) < tolower(rhs); + return strutils::tolower(lhs) < strutils::tolower(rhs); } std::string readFile(const std::string& filename) { @@ -116,9 +116,9 @@ std::string urlDecode(const std::string& url) { CiMap getCimapFromString(const std::string& str) { CiMap m; - std::vector tokenized = split(str, '&'); + std::vector tokenized = strutils::split(str, '&'); for (const std::string& token : tokenized) { - std::vector keyValue = split(token, '='); + std::vector keyValue = strutils::split(token, '='); if (keyValue.size() != 2) continue; m[keyValue[0]] = keyValue[1]; } @@ -131,7 +131,7 @@ int readMapFromFile(const std::string& filename, std::map Date: Thu, 30 May 2024 23:08:02 +0330 Subject: [PATCH 20/23] Refactor handlers --- examples/handlers.cpp | 28 +++++++++++++++------------- examples/handlers.hpp | 15 +++++++-------- server/server.cpp | 26 ++++++++++++++------------ server/server.hpp | 28 ++++++++++++++++------------ utils/template_parser.cpp | 12 ++++-------- utils/template_parser.hpp | 24 +++++++++++++----------- 6 files changed, 69 insertions(+), 64 deletions(-) diff --git a/examples/handlers.cpp b/examples/handlers.cpp index f7be76a..ca4f258 100644 --- a/examples/handlers.cpp +++ b/examples/handlers.cpp @@ -1,18 +1,19 @@ #include "handlers.hpp" -using namespace std; +#include +#include Response* RandomNumberHandler::callback(Request* req) { Response* res = new Response(); res->setHeader("Content-Type", "text/html"); - string body; + std::string body; body += ""; body += ""; body += ""; body += "

AP HTTP

"; body += "

"; body += "a random number in [1, 10] is: "; - body += to_string(rand() % 10 + 1); + body += std::to_string(std::rand() % 10 + 1); body += "

"; body += "

"; body += "SessionId: "; @@ -25,29 +26,30 @@ Response* RandomNumberHandler::callback(Request* req) { } Response* LoginHandler::callback(Request* req) { - string username = req->getBodyParam("username"); - string password = req->getBodyParam("password"); - if (username == "root") + std::string username = req->getBodyParam("username"); + std::string password = req->getBodyParam("password"); + if (username == "root") { throw Server::Exception("Remote root access has been disabled."); - cout << "username: " << username << ",\tpassword: " << password << endl; + } + std::cout << "username: " << username << ",\tpassword: " << password << std::endl; Response* res = Response::redirect("/rand"); res->setSessionId("SID"); return res; } Response* UploadHandler::callback(Request* req) { - string name = req->getBodyParam("file_name"); - string file = req->getBodyParam("file"); + std::string name = req->getBodyParam("file_name"); + std::string file = req->getBodyParam("file"); utils::writeToFile(file, name); Response* res = Response::redirect("/"); return res; } -ColorHandler::ColorHandler(string filePath) : TemplateHandler(filePath) {} +ColorHandler::ColorHandler(const std::string& filePath) : TemplateHandler(filePath) {} -map ColorHandler::handle(Request* req) { - map context; - string newName = "I am " + req->getQueryParam("name"); +std::map ColorHandler::handle(Request* req) { + std::string newName = "I am " + req->getQueryParam("name"); + std::map context; context["name"] = newName; context["color"] = req->getQueryParam("color"); return context; diff --git a/examples/handlers.hpp b/examples/handlers.hpp index cc25b84..ed82807 100644 --- a/examples/handlers.hpp +++ b/examples/handlers.hpp @@ -1,31 +1,30 @@ #ifndef HANDLERS_HPP_INCLUDE #define HANDLERS_HPP_INCLUDE -#include // for rand and srand -#include // for time -#include +#include +#include #include "../server/server.hpp" class RandomNumberHandler : public RequestHandler { public: - Response* callback(Request*); + Response* callback(Request*) override; }; class LoginHandler : public RequestHandler { public: - Response* callback(Request*); + Response* callback(Request*) override; }; class UploadHandler : public RequestHandler { public: - Response* callback(Request*); + Response* callback(Request*) override; }; class ColorHandler : public TemplateHandler { public: - ColorHandler(std::string filePath); - std::map handle(Request* req); + ColorHandler(const std::string& filePath); + std::map handle(Request* req) override; }; #endif // HANDLERS_HPP_INCLUDE diff --git a/server/server.cpp b/server/server.cpp index c79dc5f..760e106 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -342,22 +342,21 @@ Server::Exception::Exception(const string msg) { message = msg; } string Server::Exception::getMessage() const { return message; } -ShowFile::ShowFile(string _filePath, string _fileType) { - filePath = _filePath; - fileType = _fileType; -} +ShowFile::ShowFile(const string& filePath, const string& fileType) + : filePath_(filePath), + fileType_(fileType) {} Response* ShowFile::callback(Request* req) { Response* res = new Response(); - res->setHeader("Content-Type", fileType); - res->setBody(utils::readFile(filePath)); + res->setHeader("Content-Type", fileType_); + res->setBody(utils::readFile(filePath_)); return res; } -ShowPage::ShowPage(string filePath) +ShowPage::ShowPage(const string& filePath) : ShowFile(filePath, "text/" + utils::getExtension(filePath)) {} -ShowImage::ShowImage(string filePath) +ShowImage::ShowImage(const string& filePath) : ShowFile(filePath, "image/" + utils::getExtension(filePath)) {} void Server::setNotFoundErrPage(const std::string& notFoundErrPage) { @@ -367,9 +366,12 @@ void Server::setNotFoundErrPage(const std::string& notFoundErrPage) { RequestHandler::~RequestHandler() {} -TemplateHandler::TemplateHandler(string _filePath) { - filePath = _filePath; - parser = new TemplateParser(filePath); +TemplateHandler::TemplateHandler(const string& filePath) + : filePath_(filePath), + parser_(new TemplateParser(filePath)) {} + +TemplateHandler::~TemplateHandler() { + delete parser_; } Response* TemplateHandler::callback(Request* req) { @@ -377,7 +379,7 @@ Response* TemplateHandler::callback(Request* req) { context = this->handle(req); Response* res = new Response(); res->setHeader("Content-Type", "text/html"); - res->setBody(parser->getHtml(context)); + res->setBody(parser_->getHtml(context)); return res; } diff --git a/server/server.hpp b/server/server.hpp index c034bd6..6e49869 100644 --- a/server/server.hpp +++ b/server/server.hpp @@ -26,32 +26,36 @@ class RequestHandler { }; class ShowFile : public RequestHandler { - std::string filePath; - std::string fileType; - public: - ShowFile(std::string filePath, std::string fileType); - Response* callback(Request* req); + ShowFile(const std::string& filePath, const std::string& fileType); + Response* callback(Request* req) override; + +private: + std::string filePath_; + std::string fileType_; }; class ShowPage : public ShowFile { public: - ShowPage(std::string _filePath); + ShowPage(const std::string& filePath); }; class ShowImage : public ShowFile { public: - ShowImage(std::string _filePath); + ShowImage(const std::string& filePath); }; class TemplateHandler : public RequestHandler { - std::string filePath; - TemplateParser* parser; - public: - TemplateHandler(std::string _filePath); - Response* callback(Request* req); + TemplateHandler(const std::string& filePath); + ~TemplateHandler(); + + Response* callback(Request* req) override; virtual std::map handle(Request* req); + +private: + std::string filePath_; + TemplateParser* parser_; }; class Server { diff --git a/utils/template_parser.cpp b/utils/template_parser.cpp index 0cd646a..aac4c0a 100644 --- a/utils/template_parser.cpp +++ b/utils/template_parser.cpp @@ -75,8 +75,7 @@ void TemplateParser::appendCodeBlockToCode(int begin, int end, if (end <= begin || begin < 0) throw Server::Exception("Can not parse template " + filePath); int codeBlockSize = end - begin - beginCodeBlockTag.size(); - code += - unparsedTemplate.substr(begin + beginCodeBlockTag.size(), codeBlockSize); + code += unparsedTemplate.substr(begin + beginCodeBlockTag.size(), codeBlockSize); } void TemplateParser::makeExecutableTemplate() { @@ -147,8 +146,7 @@ void TemplateParser::addContextMapToCode() { string mapCode = "std::map context;\n"; // `mapFile` should be changed if we want to handle requests // in a multi-thread non-blocking way - mapCode += - "utils::readMapFromFile(\"" + outputFolder + "/" + mapFile + "\", context);\n"; + mapCode += "utils::readMapFromFile(\"" + outputFolder + "/" + mapFile + "\", context);\n"; code = mapCode + code; } @@ -165,15 +163,13 @@ void TemplateParser::deleteExecutable() { } void TemplateParser::deleteLocalTemplate() { - string cmd = - SysCmd::rm + outputFolder + SysCmd::slash + localTemplate(parserNum); + string cmd = SysCmd::rm + outputFolder + SysCmd::slash + localTemplate(parserNum); string error = "Error in deleting local template at " + outputFolder + "/" + localTemplate(parserNum); TemplateUtils::runSystemCommand(cmd, error); } -void TemplateParser::TemplateUtils::runSystemCommand(string command, - string error) { +void TemplateParser::TemplateUtils::runSystemCommand(string command, string error) { int ret = system(command.c_str()); #ifdef _WIN32 if (ret != 0) { diff --git a/utils/template_parser.hpp b/utils/template_parser.hpp index 3fc1f2a..2f0b05e 100644 --- a/utils/template_parser.hpp +++ b/utils/template_parser.hpp @@ -41,7 +41,20 @@ const std::string mapFile = "map.txt"; const std::string localTemplate(const int parserNum); class TemplateParser { +public: + TemplateParser(std::string _filePath); + ~TemplateParser(); + + std::string getHtml(std::map _context); + private: + class TemplateUtils { + public: + static void runSystemCommand(std::string command, std::string errorMessage); + static int writeMapToFile(std::string fname, + std::map* m); + }; + static int lastParserNum; int parserNum; std::string filePath; @@ -67,17 +80,6 @@ class TemplateParser { void compileCode(); void deleteExecutable(); void deleteLocalTemplate(); - class TemplateUtils { - public: - static void runSystemCommand(std::string command, std::string errorMessage); - static int writeMapToFile(std::string fname, - std::map* m); - }; - -public: - TemplateParser(std::string _filePath); - ~TemplateParser(); - std::string getHtml(std::map _context); }; #endif // TEMPLATE_PARSER_HPP_INCLUDE From 03512a87ce89710ce9247749c7a292e8646adce0 Mon Sep 17 00:00:00 2001 From: MisaghM Date: Thu, 30 May 2024 23:19:07 +0330 Subject: [PATCH 21/23] Make private fields end with underscore --- server/route.cpp | 20 ++++++------- server/route.hpp | 13 ++++---- server/server.cpp | 74 ++++++++++++++++++++++++---------------------- server/server.hpp | 12 ++++---- utils/request.cpp | 70 +++++++++++++++++++++---------------------- utils/request.hpp | 18 +++++------ utils/response.cpp | 46 +++++++++++++++------------- utils/response.hpp | 16 +++++----- 8 files changed, 138 insertions(+), 131 deletions(-) diff --git a/server/route.cpp b/server/route.cpp index 70fdd7c..b1f1a22 100644 --- a/server/route.cpp +++ b/server/route.cpp @@ -2,23 +2,23 @@ #include "server.hpp" -Route::Route(Request::Method _method, const std::string& _path) - : method(_method), - path(_path), - handler(nullptr) {} +Route::Route(Request::Method method, const std::string& path) + : method_(method), + path_(path), + handler_(nullptr) {} Route::~Route() { - delete handler; + delete handler_; } -void Route::setHandler(RequestHandler* _handler) { - handler = _handler; +void Route::setHandler(RequestHandler* handler) { + handler_ = handler; } Response* Route::handle(Request* req) { - return handler->callback(req); + return handler_->callback(req); } -bool Route::isMatch(Request::Method _method, const std::string& url) { - return (_method == method) && (url == path); +bool Route::isMatch(Request::Method method, const std::string& url) { + return (method_ == method) && (url == path_); } diff --git a/server/route.hpp b/server/route.hpp index 9c2b030..9bfb921 100644 --- a/server/route.hpp +++ b/server/route.hpp @@ -3,25 +3,24 @@ #include -#include "../utils/include.hpp" #include "../utils/request.hpp" -#include "../utils/response.hpp" +class Response; class RequestHandler; class Route { public: - Route(Request::Method _method, const std::string& _path); + Route(Request::Method method, const std::string& path); ~Route(); - void setHandler(RequestHandler* _handler); + void setHandler(RequestHandler* handler); Response* handle(Request* req); bool isMatch(Request::Method, const std::string& url); private: - Request::Method method; - std::string path; - RequestHandler* handler; + Request::Method method_; + std::string path_; + RequestHandler* handler_; }; #endif // ROUTE_HPP_INCLUDE diff --git a/server/server.cpp b/server/server.cpp index 760e106..da74d04 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -58,24 +58,25 @@ static const char* getSocketError() { using namespace std; class NotFoundHandler : public RequestHandler { - string notFoundErrPage; - public: - NotFoundHandler(string notFoundErrPage = "") - : notFoundErrPage(notFoundErrPage) {} + NotFoundHandler(const string& notFoundErrPage = "") + : notFoundErrPage_(notFoundErrPage) {} Response* callback(Request* req) { Response* res = new Response(Response::Status::notFound); - if (!notFoundErrPage.empty()) { - res->setHeader("Content-Type", "text/" + utils::getExtension(notFoundErrPage)); - res->setBody(utils::readFile(notFoundErrPage)); + if (!notFoundErrPage_.empty()) { + res->setHeader("Content-Type", "text/" + utils::getExtension(notFoundErrPage_)); + res->setBody(utils::readFile(notFoundErrPage_)); } return res; } + +private: + string notFoundErrPage_; }; class ServerErrorHandler { public: - static Response* callback(string msg) { + static Response* callback(const string& msg) { Response* res = new Response(Response::Status::internalServerError); res->setHeader("Content-Type", "application/json"); res->setBody("{ \"code\": \"500\", \"message\": \"" + msg + "\" }\n"); @@ -210,7 +211,7 @@ Request* parseRawReq(char* reqData, size_t length) { return req; } -Server::Server(int _port) : port(_port) { +Server::Server(int port) : port_(port) { #ifdef _WIN32 WSADATA wsa_data; int initializeResult = WSAStartup(MAKEWORD(2, 2), &wsa_data); @@ -220,26 +221,26 @@ Server::Server(int _port) : port(_port) { } #endif - notFoundHandler = new NotFoundHandler(); + notFoundHandler_ = new NotFoundHandler(); - sc = socket(AF_INET, SOCK_STREAM, 0); + sc_ = socket(AF_INET, SOCK_STREAM, 0); int sc_option = 1; #ifdef _WIN32 - setsockopt(sc, SOL_SOCKET, SO_REUSEADDR, (char*)&sc_option, - sizeof(sc_option)); + setsockopt(sc_, SOL_SOCKET, SO_REUSEADDR, (char*)&sc_option, sizeof(sc_option)); #else - setsockopt(sc, SOL_SOCKET, SO_REUSEADDR, &sc_option, sizeof(sc_option)); + setsockopt(sc_, SOL_SOCKET, SO_REUSEADDR, &sc_option, sizeof(sc_option)); #endif - if (!ISVALIDSOCKET(sc)) + if (!ISVALIDSOCKET(sc_)) { throw Exception("Error on opening socket: " + string(getSocketError())); + } struct sockaddr_in serv_addr; serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(port); + serv_addr.sin_port = htons(port_); - if (::bind(sc, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) { + if (::bind(sc_, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) { throw Exception("Error on binding: " + string(getSocketError())); } } @@ -247,7 +248,7 @@ Server::Server(int _port) : port(_port) { void Server::mapRequest(const string& path, RequestHandler* handler, Request::Method method) { Route* route = new Route(method, path); route->setHandler(handler); - routes.push_back(route); + routes_.push_back(route); } void Server::get(const std::string& path, RequestHandler* handler) { @@ -267,7 +268,7 @@ void Server::del(const std::string& path, RequestHandler* handler) { } void Server::run() { - ::listen(sc, 10); + ::listen(sc_, 10); struct sockaddr_in cli_addr; socklen_t clilen; @@ -275,7 +276,7 @@ void Server::run() { SOCKET newsc; while (true) { - newsc = ::accept(sc, (struct sockaddr*)&cli_addr, &clilen); + newsc = ::accept(sc_, (struct sockaddr*)&cli_addr, &clilen); if (!ISVALIDSOCKET(newsc)) throw Exception("Error on accept: " + string(getSocketError())); Response* res = nullptr; @@ -300,14 +301,14 @@ void Server::run() { } req->log(); size_t i = 0; - for (; i < routes.size(); i++) { - if (routes[i]->isMatch(req->getMethod(), req->getPath())) { - res = routes[i]->handle(req); + for (; i < routes_.size(); i++) { + if (routes_[i]->isMatch(req->getMethod(), req->getPath())) { + res = routes_[i]->handle(req); break; } } - if (i == routes.size() && notFoundHandler) { - res = notFoundHandler->callback(req); + if (i == routes_.size() && notFoundHandler_) { + res = notFoundHandler_->callback(req); } delete req; } @@ -316,7 +317,7 @@ void Server::run() { res = ServerErrorHandler::callback(exc.getMessage()); } res->log(); - string res_data = res->print(); + string res_data = res->getResponse(); int si = res_data.size(); delete res; int wr = send(newsc, res_data.c_str(), si, 0); @@ -327,20 +328,21 @@ void Server::run() { } Server::~Server() { - if (sc >= 0) - CLOSESOCKET(sc); - delete notFoundHandler; - for (size_t i = 0; i < routes.size(); ++i) - delete routes[i]; - + if (sc_ >= 0) { + CLOSESOCKET(sc_); + } + delete notFoundHandler_; + for (size_t i = 0; i < routes_.size(); ++i) { + delete routes_[i]; + } #ifdef _WIN32 WSACleanup(); #endif } -Server::Exception::Exception(const string msg) { message = msg; } +Server::Exception::Exception(const string message) : message_(message) {} -string Server::Exception::getMessage() const { return message; } +string Server::Exception::getMessage() const { return message_; } ShowFile::ShowFile(const string& filePath, const string& fileType) : filePath_(filePath), @@ -360,8 +362,8 @@ ShowImage::ShowImage(const string& filePath) : ShowFile(filePath, "image/" + utils::getExtension(filePath)) {} void Server::setNotFoundErrPage(const std::string& notFoundErrPage) { - delete notFoundHandler; - notFoundHandler = new NotFoundHandler(notFoundErrPage); + delete notFoundHandler_; + notFoundHandler_ = new NotFoundHandler(notFoundErrPage); } RequestHandler::~RequestHandler() {} diff --git a/server/server.hpp b/server/server.hpp index 6e49869..70c8bde 100644 --- a/server/server.hpp +++ b/server/server.hpp @@ -74,18 +74,18 @@ class Server { class Exception : public std::exception { public: Exception() {} - Exception(const std::string); + Exception(const std::string message); std::string getMessage() const; private: - std::string message; + std::string message_; }; private: - SOCKET sc; - int port; - std::vector routes; - RequestHandler* notFoundHandler; + SOCKET sc_; + int port_; + std::vector routes_; + RequestHandler* notFoundHandler_; void mapRequest(const std::string& path, RequestHandler* handler, Request::Method method); }; diff --git a/utils/request.cpp b/utils/request.cpp index 6b85d6a..20048b8 100644 --- a/utils/request.cpp +++ b/utils/request.cpp @@ -7,60 +7,60 @@ #include "strutils.hpp" -const std::unordered_map Request::methodMap = { +const std::unordered_map Request::methodMap_ = { {"GET", Request::Method::GET}, {"POST", Request::Method::POST}, {"PUT", Request::Method::PUT}, {"DEL", Request::Method::DEL}, }; -Request::Request(Method method_) - : method(method_) {} +Request::Request(Method method) + : method_(method) {} -Request::Request(const std::string& method_) +Request::Request(const std::string& method) : Request(Method::GET) { - auto itr = methodMap.find(method_); - if (itr != methodMap.end()) { - method = itr->second; + auto itr = methodMap_.find(method); + if (itr != methodMap_.end()) { + method_ = itr->second; } } Request::Method Request::getMethod() const { - return method; + return method_; } std::string Request::getPath() const { - return path; + return path_; } std::string Request::getHeader(const std::string& key) const { - auto itr = headers.find(key); - if (itr == headers.end()) { + auto itr = headers_.find(key); + if (itr == headers_.end()) { return {}; } return utils::urlDecode(itr->second); } std::string Request::getBody() const { - std::string bs; - for (auto itr = body.begin(); !body.empty() && itr != body.end(); ++itr) { - bs += itr->first + "=" + itr->second + "&"; + std::string result; + for (auto itr = body_.begin(); itr != body_.end(); ++itr) { + result += itr->first + "=" + itr->second + "&"; } - return bs; + return result; } std::string Request::getQueryParam(const std::string& key) const { - auto itr = query.find(key); - if (itr == query.end()) { + auto itr = query_.find(key); + if (itr == query_.end()) { return {}; } return utils::urlDecode(itr->second); } std::string Request::getBodyParam(const std::string& key) const { - auto bodyType = bodyTypes.find(key); - auto itr = body.find(key); - if (bodyType == bodyTypes.end() || itr == body.end()) { + auto bodyType = bodyTypes_.find(key); + auto itr = body_.find(key); + if (bodyType == bodyTypes_.end() || itr == body_.end()) { return {}; } @@ -91,51 +91,51 @@ std::string Request::getSessionId() const { return {}; } -void Request::setPath(const std::string& _path) { - path = _path; +void Request::setPath(const std::string& path) { + path_ = path; } void Request::setHeader(const std::string& key, const std::string& value, bool encode) { - headers[key] = encode ? utils::urlEncode(value) : value; + headers_[key] = encode ? utils::urlEncode(value) : value; } -void Request::setBody(const std::string& _body) { - body = utils::getCimapFromString(_body); +void Request::setBody(const std::string& body) { + body_ = utils::getCimapFromString(body); } void Request::setQueryParam(const std::string& key, const std::string& value, bool encode) { - query[key] = encode ? utils::urlEncode(value) : value; + query_[key] = encode ? utils::urlEncode(value) : value; } void Request::setBodyParam(const std::string& key, const std::string& value, const std::string& contentType, bool encode) { - body[key] = encode ? utils::urlEncode(value) : value; - bodyTypes[key] = contentType; + body_[key] = encode ? utils::urlEncode(value) : value; + bodyTypes_[key] = contentType; } -void Request::log() { +void Request::log() const { const std::string NC = "\033[0;39m"; const std::string K = "\033[1m"; const std::string H = "\033[33;1m"; std::string log; log += H + "------- Request --------" + NC + "\n"; - log += K + "Method: " + NC + (method == Method::POST ? "POST" : "GET") + "\n"; - log += K + "Path: " + NC + path + "\n"; + log += K + "Method: " + NC + (method_ == Method::POST ? "POST" : "GET") + "\n"; + log += K + "Path: " + NC + path_ + "\n"; log += K + "SessionId: " + NC + this->getSessionId() + "\n"; log += K + "Headers:" + NC + "\n"; - for (auto itr = headers.begin(); itr != headers.end(); itr++) { + for (auto itr = headers_.begin(); itr != headers_.end(); itr++) { log += " " + utils::urlDecode(itr->first) + ": " + utils::urlDecode(itr->second) + "\n"; } log += K + "Query:" + NC + "\n"; - for (auto itr = query.begin(); itr != query.end(); itr++) { + for (auto itr = query_.begin(); itr != query_.end(); itr++) { log += " " + utils::urlDecode(itr->first) + ": " + utils::urlDecode(itr->second) + "\n"; } log += K + "Body:" + NC + "\n"; - for (auto itr = body.begin(); itr != body.end(); itr++) { - std::string type = bodyTypes[itr->first]; + for (auto itr = body_.begin(); itr != body_.end(); itr++) { + std::string type = bodyTypes_.find(itr->first)->second; if (type == "application/x-www-form-urlencoded" || type == "text/plain") { log += " " + utils::urlDecode(itr->first) + ": " + utils::urlDecode(itr->second) + "\n"; } diff --git a/utils/request.hpp b/utils/request.hpp index d582eeb..bbda386 100644 --- a/utils/request.hpp +++ b/utils/request.hpp @@ -33,17 +33,17 @@ class Request { std::string getBodyParam(const std::string& key) const; std::string getSessionId() const; - void log(); + void log() const; private: - Method method; - std::string path; - utils::CiMap headers; - utils::CiMap query; - utils::CiMap body; - utils::CiMap bodyTypes; - - static const std::unordered_map methodMap; + Method method_; + std::string path_; + utils::CiMap headers_; + utils::CiMap query_; + utils::CiMap body_; + utils::CiMap bodyTypes_; + + static const std::unordered_map methodMap_; }; #endif // REQUEST_HPP_INCLUDE diff --git a/utils/response.cpp b/utils/response.cpp index c97aa44..c882a2a 100644 --- a/utils/response.cpp +++ b/utils/response.cpp @@ -4,7 +4,7 @@ #include "include.hpp" -const std::unordered_map Response::httpPhraseMap = { +const std::unordered_map Response::phraseMap_ = { {Status::ok, "OK"}, {Status::created, "Created"}, @@ -23,43 +23,47 @@ const std::unordered_map Response::httpPhraseMap {Status::notImplemented, "Not Implemented"}, }; -Response::Response(Status code_) - : code(static_cast(code_)), - phrase(httpPhraseMap.find(code_)->second) { - headers["Content-Type"] = "text/plain"; +Response::Response(Status code) + : code_(static_cast(code)), + phrase_(phraseMap_.find(code)->second) { + headers_["Content-Type"] = "text/plain"; } -Response::Response(int _code, const std::string& _phrase) - : code(_code), - phrase(_phrase) { - headers["Content-Type"] = "text/plain"; +Response::Response(int code, const std::string& phrase) + : code_(code), + phrase_(phrase) { + headers_["Content-Type"] = "text/plain"; } void Response::setHeader(const std::string& key, const std::string& value) { - headers[key] = value; + headers_[key] = value; } -void Response::setBody(const std::string& _body) { - body = _body; +void Response::setBody(const std::string& body) { + body_ = body; } void Response::setSessionId(const std::string& sessionId) { setHeader("set-cookie", "sessionId=" + sessionId + ";"); } -std::string Response::print() { +std::string Response::getHeader() const { std::string header; - header += "HTTP/1.0 " + std::to_string(code) + " " + phrase + "\r\n"; + header += "HTTP/1.0 " + std::to_string(code_) + " " + phrase_ + "\r\n"; header += "Server: " + SERVER_NAME + " \r\n"; - header += "Content-Length: " + std::to_string(body.size()) + "\r\n"; - for (auto itr = headers.begin(); itr != headers.end(); ++itr) { + header += "Content-Length: " + std::to_string(body_.size()) + "\r\n"; + for (auto itr = headers_.begin(); itr != headers_.end(); ++itr) { header += itr->first + ": " + itr->second + "\r\n"; } header += "\r\n"; - return header + body; + return header; } -void Response::log(bool showBody) { +std::string Response::getResponse() const { + return getHeader() + body_; +} + +void Response::log(bool showBody) const { const std::string NC = "\033[0;39m"; const std::string K = "\033[1m"; const std::string H = "\033[34;1m"; @@ -68,14 +72,14 @@ void Response::log(bool showBody) { std::string log; log += H + "------- Response -------" + NC + "\n"; - log += K + "Status: " + NC + (code == 200 ? G : R) + std::to_string(code) + " " + phrase + NC + "\n"; + log += K + "Status: " + NC + (code_ == 200 ? G : R) + std::to_string(code_) + " " + phrase_ + NC + "\n"; log += K + "Headers:" + NC + "\n"; - for (auto itr = headers.begin(); itr != headers.end(); ++itr) { + for (auto itr = headers_.begin(); itr != headers_.end(); ++itr) { log += " " + utils::urlDecode(itr->first) + ": " + utils::urlDecode(itr->second) + "\n"; } if (showBody) { - log += K + "Body:\n" + NC + body + "\n"; + log += K + "Body:\n" + NC + body_ + "\n"; } log += H + "------------------------" + NC + "\n"; std::clog << log << std::endl; diff --git a/utils/response.hpp b/utils/response.hpp index b03bbc0..935dc90 100644 --- a/utils/response.hpp +++ b/utils/response.hpp @@ -34,18 +34,20 @@ class Response { void setBody(const std::string& _body); void setSessionId(const std::string& sessionId); - std::string print(); - void log(bool showBody = false); + std::string getHeader() const; + std::string getResponse() const; + + void log(bool showBody = false) const; static Response* redirect(const std::string& url); private: - int code; - std::string phrase; - std::string body; - utils::CiMap headers; + int code_; + std::string phrase_; + std::string body_; + utils::CiMap headers_; - static const std::unordered_map httpPhraseMap; + static const std::unordered_map phraseMap_; }; #endif // RESPONSE_HPP_INCLUDE From b0f0f489df8ebc45fba8ea27a7197cb0600d3d77 Mon Sep 17 00:00:00 2001 From: MisaghM Date: Wed, 5 Jun 2024 15:34:04 +0330 Subject: [PATCH 22/23] Refactor and add random number page to home --- examples/handlers.cpp | 26 +++++----- examples/main.cpp | 2 +- server/server.hpp | 2 +- static/home.html | 1 + static/music.html | 14 +++--- template/colors.html | 101 ++++++++++++++++++-------------------- utils/template_parser.cpp | 27 +++++----- 7 files changed, 87 insertions(+), 86 deletions(-) diff --git a/examples/handlers.cpp b/examples/handlers.cpp index ca4f258..ce10ab7 100644 --- a/examples/handlers.cpp +++ b/examples/handlers.cpp @@ -6,20 +6,23 @@ Response* RandomNumberHandler::callback(Request* req) { Response* res = new Response(); res->setHeader("Content-Type", "text/html"); + + std::string randomNumber = std::to_string(std::rand() % 10 + 1); std::string body; + body += ""; - body += ""; + body += ""; + + body += ""; + body += " Random Number Page"; + body += ""; + body += ""; - body += "

AP HTTP

"; - body += "

"; - body += "a random number in [1, 10] is: "; - body += std::to_string(std::rand() % 10 + 1); - body += "

"; - body += "

"; - body += "SessionId: "; - body += req->getSessionId(); - body += "

"; + body += "

AP HTTP

"; + body += "

A random number in [1, 10] is: " + randomNumber + "

"; + body += "

SessionId: " + req->getSessionId() + "

"; body += ""; + body += ""; res->setBody(body); return res; @@ -45,7 +48,8 @@ Response* UploadHandler::callback(Request* req) { return res; } -ColorHandler::ColorHandler(const std::string& filePath) : TemplateHandler(filePath) {} +ColorHandler::ColorHandler(const std::string& filePath) + : TemplateHandler(filePath) {} std::map ColorHandler::handle(Request* req) { std::string newName = "I am " + req->getQueryParam("name"); diff --git a/examples/main.cpp b/examples/main.cpp index fa76fac..dfa21c5 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -8,12 +8,12 @@ void mapServerPaths(Server& server) { server.setNotFoundErrPage("static/404.html"); server.get("/", new ShowPage("static/home.html")); server.get("/home.png", new ShowImage("static/home.png")); + server.get("/rand", new RandomNumberHandler()); server.get("/login", new ShowPage("static/logincss.html")); server.post("/login", new LoginHandler()); server.get("/up", new ShowPage("static/upload_form.html")); server.post("/up", new UploadHandler()); server.get("/colors", new ColorHandler("template/colors.html")); - server.get("/rand", new RandomNumberHandler()); server.get("/music", new ShowPage("static/music.html")); server.get("/music/moonlight.mp3", new ShowFile("static/moonlight.mp3", "audio/mpeg")); } diff --git a/server/server.hpp b/server/server.hpp index 70c8bde..620273b 100644 --- a/server/server.hpp +++ b/server/server.hpp @@ -73,7 +73,7 @@ class Server { class Exception : public std::exception { public: - Exception() {} + Exception() = default; Exception(const std::string message); std::string getMessage() const; diff --git a/static/home.html b/static/home.html index 042f8eb..63ad7e8 100644 --- a/static/home.html +++ b/static/home.html @@ -10,6 +10,7 @@

AP HTTP

Home Logo
+
Go to rand page
Go to login page
Go to upload page
Go to colors page
diff --git a/static/music.html b/static/music.html index 12604de..8478749 100644 --- a/static/music.html +++ b/static/music.html @@ -1,16 +1,16 @@ - - - Music Page + + + Music Page -

Music

- +

Music

+ diff --git a/template/colors.html b/template/colors.html index a3e92fe..5b79a78 100644 --- a/template/colors.html +++ b/template/colors.html @@ -2,68 +2,65 @@ - - - Colors Page + + + Colors Page -
- Choose color: - - - - -
-