From 309ba08e344c938cfa1dcf7a95441999a0581141 Mon Sep 17 00:00:00 2001 From: Dora Date: Tue, 1 Jul 2025 19:05:20 +0200 Subject: [PATCH 1/4] Breaking it off from File Hosting --- README.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3c15a7e..f31c31e 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,6 @@ -# TNC File Hosting +# Web Base for C++ -The Night Club's very own basic image hosting web frontend. - -# Coing Guidelines - -- In most of my projects I have less header(h/hpp) files than implementation files(c/cpp). This is very much intentional. I implement parts of a header file for ease of reading. You are expected to NOT make 1 header per 1 implementation file!!! - -- When doing ``#include`` if the same include is used more than once in different implementation files that share a header file. Move the ``#include`` to the header file instead of having two of the same includes. - -- Codebase must adhere to DRY and KISS (DRY KISS) principles! +A good starting point for web development on C++. Do implement security stuff. # Building From 14394f1c9f304064bb0ad5b7a2cc7f388c50ffec Mon Sep 17 00:00:00 2001 From: cat Date: Wed, 2 Jul 2025 15:14:16 +0300 Subject: [PATCH 2/4] Start of the body processing --- src/HTTP.cpp | 82 +++++++++++++++++++++++++++++++++++++------------ src/Helpers.cpp | 10 ++++++ src/Helpers.hpp | 3 +- src/main.hpp | 3 +- 4 files changed, 76 insertions(+), 22 deletions(-) diff --git a/src/HTTP.cpp b/src/HTTP.cpp index 69c0a93..d3a542f 100644 --- a/src/HTTP.cpp +++ b/src/HTTP.cpp @@ -1,6 +1,13 @@ #include "Helpers.hpp" #include "main.hpp" +#include +#include #include +#include +#include +#include +#include +#include void HTTPrequest::start() { // Possible Logging here @@ -15,44 +22,79 @@ void HTTPrequest::readData() { // TODO: Read until headers, headers contains the file size then allocate that // much memory on buffer, and THEN start reading the file - sock.async_read_some( - buffer.prepare(2048), + + // HEADER read + asio::async_read_until( + sock, buffer, "\r\n\r\n", // its not the other way bud [this, self](std::error_code error, std::size_t packageSize) { if (!error) { buffer.commit(packageSize); - std::istream stream(&buffer); std::unordered_map request; + std::unordered_map args; + std::istream stream(&buffer); + uint64_t bitCount = 0; // This is HTTP main request std::string rq, key, value, type, path; - std::getline(stream, type, ' '); // HTTP request type - std::getline(stream, path, ' '); // HTTP path - std::getline(stream, rq); // Removes HTTP version part no need rn - // Rest of the request vals - while (buffer.size() > 0) { - std::getline(stream, key, ':'); - std::getline(stream, rq, ' '); // trim start - // some gangster shit right here - std::getline(stream, value); + Helpers::getlineAndCount(stream, bitCount, type, + ' '); // HTTP request type + Helpers::getlineAndCount(stream, bitCount, path, ' '); // HTTP path + Helpers::getlineAndCount(stream, bitCount, + rq); // Version, omitting for now - if (type == "GET") { + // Quick stop to process arguments + std::stringstream pathStream(path); + std::getline(pathStream, path, '?'); // Keeping the path pure - request.insert({key, value}); - } else { - std::cout << key << " " << value << "\n"; - } + while (!pathStream.eof()) { + std::getline(pathStream, key, '='); + std::getline(pathStream, value, '&'); + args.insert({key, value}); + std::cout << key << " " << value << std::endl; } - processRequest(type, path, request); // This writes data too - sock.close(); // Responded + // Rest of the request vals + while (bitCount < + packageSize - 2) { // -2 is due to last two \r\n\r\n combo + Helpers::getlineAndCount(stream, bitCount, key, ':'); + Helpers::getlineAndCount( + stream, bitCount, rq, + ' '); // trim start some gangster shit right here + Helpers::getlineAndCount(stream, bitCount, value); + request.insert({key, value}); + } + + // =================== + // BODY OF THE REQUEST + // =================== + std::size_t uploadSize = + std::stoull(request.at("Content-Length")) - buffer.size(); + asio::async_read( + sock, buffer.prepare(uploadSize), + asio::transfer_exactly(uploadSize), + [self, this](std::error_code error, std::size_t packageSize) { + buffer.commit(packageSize); + std::istream bodyStream(&buffer); + std::string body; + body.resize(buffer.size()); + bodyStream.read(body.data(), buffer.size()); + + std::cout << body << "\ntest" << std::endl; + buffer.consume(buffer.size()); + sock.close(); + }); + + processRequest(type, path, request, args); // This writes data too + sock.close(); } }); } void HTTPrequest::processRequest( std::string requestType, std::string requestPath, - std::unordered_map request) { + std::unordered_map request, + std::unordered_map args) { // // This is where we will process requests // diff --git a/src/Helpers.cpp b/src/Helpers.cpp index 71b9c04..90db964 100644 --- a/src/Helpers.cpp +++ b/src/Helpers.cpp @@ -12,6 +12,16 @@ std::string Helpers::ReadFile(std::string Path) { return contents.str(); } +void Helpers::getlineAndCount(std::basic_istream &stream, uint64_t &count, + std::string &string, char delimit) { + if (delimit == '\0') { + std::getline(stream, string); + } else { + std::getline(stream, string, delimit); + } + count += string.size() + 1; // Delimiter + return; +} std::string Helpers::GenerateResponse(std::string statusCode, std::string contentType, std::string content) { diff --git a/src/Helpers.hpp b/src/Helpers.hpp index bf5d91a..f1a44cf 100644 --- a/src/Helpers.hpp +++ b/src/Helpers.hpp @@ -7,7 +7,8 @@ namespace Helpers { std::string ReadFile(std::string Path); - +void getlineAndCount(std::basic_istream &stream, uint64_t &count, + std::string &string, char delimit = '\0'); std::string GenerateResponse(std::string statusCode, std::string contentType, std::string content); // =========== diff --git a/src/main.hpp b/src/main.hpp index 5925f63..fcfdece 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -32,7 +32,8 @@ private: HTTPrequest(asio::io_context &context); void readData(); void processRequest(std::string requestType, std::string requestPath, - std::unordered_map request); + std::unordered_map request, + std::unordered_map args); void writeData(std::string data); asio::ip::tcp::socket sock; From 0b483c69cb76269eac737141776e9cab5cb0bde8 Mon Sep 17 00:00:00 2001 From: cat Date: Wed, 2 Jul 2025 18:58:40 +0300 Subject: [PATCH 3/4] HTTP bodies should work nicely, files are reorganised, www is reverted --- src/Entry.cpp | 2 +- src/HTTP.cpp | 89 +------------------------------------- src/HTTPRequestProcess.cpp | 89 ++++++++++++++++++++++++++++++++++++++ src/Helpers.cpp | 3 +- src/{main.hpp => Main.hpp} | 2 +- src/Webserver.cpp | 2 +- www/index.html | 3 +- 7 files changed, 98 insertions(+), 92 deletions(-) create mode 100644 src/HTTPRequestProcess.cpp rename src/{main.hpp => Main.hpp} (98%) diff --git a/src/Entry.cpp b/src/Entry.cpp index de7ae3e..622edad 100644 --- a/src/Entry.cpp +++ b/src/Entry.cpp @@ -1,4 +1,4 @@ -#include "main.hpp" +#include "Main.hpp" #include #include diff --git a/src/HTTP.cpp b/src/HTTP.cpp index d3a542f..9aadd86 100644 --- a/src/HTTP.cpp +++ b/src/HTTP.cpp @@ -1,94 +1,9 @@ #include "Helpers.hpp" -#include "main.hpp" -#include -#include -#include -#include -#include -#include -#include -#include +#include "Main.hpp" void HTTPrequest::start() { // Possible Logging here - readData(); -} - -void HTTPrequest::readData() { - // - // Reading happens here - // - std::shared_ptr self(shared_from_this()); - - // TODO: Read until headers, headers contains the file size then allocate that - // much memory on buffer, and THEN start reading the file - - // HEADER read - asio::async_read_until( - sock, buffer, "\r\n\r\n", // its not the other way bud - [this, self](std::error_code error, std::size_t packageSize) { - if (!error) { - buffer.commit(packageSize); - std::unordered_map request; - std::unordered_map args; - std::istream stream(&buffer); - uint64_t bitCount = 0; - - // This is HTTP main request - std::string rq, key, value, type, path; - - Helpers::getlineAndCount(stream, bitCount, type, - ' '); // HTTP request type - Helpers::getlineAndCount(stream, bitCount, path, ' '); // HTTP path - Helpers::getlineAndCount(stream, bitCount, - rq); // Version, omitting for now - - // Quick stop to process arguments - std::stringstream pathStream(path); - std::getline(pathStream, path, '?'); // Keeping the path pure - - while (!pathStream.eof()) { - std::getline(pathStream, key, '='); - std::getline(pathStream, value, '&'); - args.insert({key, value}); - std::cout << key << " " << value << std::endl; - } - - // Rest of the request vals - while (bitCount < - packageSize - 2) { // -2 is due to last two \r\n\r\n combo - Helpers::getlineAndCount(stream, bitCount, key, ':'); - Helpers::getlineAndCount( - stream, bitCount, rq, - ' '); // trim start some gangster shit right here - Helpers::getlineAndCount(stream, bitCount, value); - request.insert({key, value}); - } - - // =================== - // BODY OF THE REQUEST - // =================== - std::size_t uploadSize = - std::stoull(request.at("Content-Length")) - buffer.size(); - asio::async_read( - sock, buffer.prepare(uploadSize), - asio::transfer_exactly(uploadSize), - [self, this](std::error_code error, std::size_t packageSize) { - buffer.commit(packageSize); - std::istream bodyStream(&buffer); - std::string body; - body.resize(buffer.size()); - bodyStream.read(body.data(), buffer.size()); - - std::cout << body << "\ntest" << std::endl; - buffer.consume(buffer.size()); - sock.close(); - }); - - processRequest(type, path, request, args); // This writes data too - sock.close(); - } - }); + processHTTPHeader(); } void HTTPrequest::processRequest( diff --git a/src/HTTPRequestProcess.cpp b/src/HTTPRequestProcess.cpp new file mode 100644 index 0000000..2f4a642 --- /dev/null +++ b/src/HTTPRequestProcess.cpp @@ -0,0 +1,89 @@ +#include "Helpers.hpp" +#include "Main.hpp" + +void HTTPrequest::processHTTPHeader() { + // + // Reading happens here + // + std::shared_ptr self(shared_from_this()); + + // Sadly due to the nature of how asio works this entire logic needs to be + // mostly in 1 method I do wager that I could break off the whole body + // processing into its own thing as well as a lot of inline methods + // specifically for argument and header processing + + // TODO: Remove boundary from the body. Do keep other stuff like + // file type and name + + // HEADER read + asio::async_read_until( + sock, buffer, "\r\n\r\n", // its not the other way bud + [this, self](std::error_code error, std::size_t packageSize) { + if (!error) { + buffer.commit(packageSize); + std::unordered_map request; + std::unordered_map args; + std::istream stream(&buffer); + uint64_t octetCount = 0; + + // This is HTTP main request + std::string rq, key, value, type, path; + + Helpers::getlineAndCount(stream, octetCount, type, + ' '); // HTTP request type + Helpers::getlineAndCount(stream, octetCount, path, ' '); // HTTP path + Helpers::getlineAndCount(stream, octetCount, + rq); // Version, omitting for now + + // Quick stop to process arguments + if (path.find('?') != std::string::npos) { + std::stringstream pathStream(path); + std::getline(pathStream, path, '?'); // Keeping the path pure + + while (!pathStream.eof()) { + std::getline(pathStream, key, '='); + std::getline(pathStream, value, '&'); + args.insert({key, value}); + } + } + + // Rest of the request vals + while (octetCount < + packageSize - 2) { // -2 is due to last two \r\n\r\n combo + Helpers::getlineAndCount(stream, octetCount, key, ':'); + Helpers::getlineAndCount( + stream, octetCount, rq, + ' '); // trim start some gangster shit right here + Helpers::getlineAndCount(stream, octetCount, value); + request.insert({key, value}); + } + + // =================== + // BODY OF THE REQUEST + // =================== + if (request.find("Content-Length") != request.end()) { + std::size_t uploadSize = std::stoull(request.at("Content-Length")) - + buffer.size(), + octetCount = 0; + + // This part needs to be sync since we want this specific instance + // execution to pause + while (octetCount < uploadSize) { + octetCount += asio::read(sock, buffer.prepare(uploadSize)); + } + + buffer.commit(octetCount); + std::istream bodyStream(&buffer); + std::string body; + body.resize(buffer.size()); + bodyStream.read(body.data(), buffer.size()); + + buffer.consume( + buffer.size()); // Just so the buffer is nice and clean + } + + processRequest(type, path, request, args); // This writes data too + sock.close(); + } + }); +} diff --git a/src/Helpers.cpp b/src/Helpers.cpp index 90db964..e68a012 100644 --- a/src/Helpers.cpp +++ b/src/Helpers.cpp @@ -27,7 +27,8 @@ std::string Helpers::GenerateResponse(std::string statusCode, std::string content) { std::stringstream output; output << "HTTP/1.1 " << statusCode << "\n" - << "Content-Type: " << contentType << "\nConnection: close\n\n" + << "Content-Type: " << contentType + << "\nContent-Length: " << content.size() << "\nConnection: close\n\n" << content; return output.str(); } diff --git a/src/main.hpp b/src/Main.hpp similarity index 98% rename from src/main.hpp rename to src/Main.hpp index fcfdece..bffc5c6 100644 --- a/src/main.hpp +++ b/src/Main.hpp @@ -30,7 +30,7 @@ public: private: HTTPrequest(asio::io_context &context); - void readData(); + void processHTTPHeader(); void processRequest(std::string requestType, std::string requestPath, std::unordered_map request, std::unordered_map args); diff --git a/src/Webserver.cpp b/src/Webserver.cpp index 9a49d47..906b895 100644 --- a/src/Webserver.cpp +++ b/src/Webserver.cpp @@ -1,4 +1,4 @@ -#include "main.hpp" +#include "Main.hpp" #include // Webserver is defined at main.hpp, you probably do not want to code here diff --git a/www/index.html b/www/index.html index 5a4af6f..3254160 100644 --- a/www/index.html +++ b/www/index.html @@ -8,7 +8,8 @@ -

Test

+

Welcome

+

POST test

From 64bc7d0fb7b8c0ee8e7becad47bd9a545c0f673c Mon Sep 17 00:00:00 2001 From: cat Date: Wed, 2 Jul 2025 18:59:26 +0300 Subject: [PATCH 4/4] Cmake is changed to reflect restructure --- CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0c4018..b752743 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,11 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_BUILD_TYPE Debug) set(CMAKE_CXX_FLAGS "-Wall -g -fsanitize=address") -add_executable(Webserver "src/Entry.cpp" "src/Webserver.cpp" "src/HTTP.cpp" - "libs/QuickDigest5/quickdigest5.cpp" "src/Helpers.cpp") +add_executable( + Webserver + "src/Entry.cpp" "src/Webserver.cpp" "src/HTTP.cpp" + "libs/QuickDigest5/quickdigest5.cpp" "src/Helpers.cpp" + "src/HTTPRequestProcess.cpp") add_custom_command( TARGET Webserver