forked from cat/WebBase
Compare commits
11 Commits
f5f1969acd
...
main
Author | SHA1 | Date | |
---|---|---|---|
5ebef48ed9 | |||
c83064f8fc | |||
b6e83e0216 | |||
7d6163b3dd | |||
336b8311cd | |||
b07711341f | |||
64bc7d0fb7 | |||
0b483c69cb | |||
063a4f3cf9 | |||
14394f1c9f | |||
309ba08e34 |
@@ -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
|
||||
|
20
README.md
20
README.md
@@ -1,15 +1,7 @@
|
||||
# TNC File Hosting
|
||||
# TNC File Hoster
|
||||
|
||||
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!
|
||||
|
||||
# Building
|
||||
|
||||
1. To build the project you must install the header-only (a.k.a not-boost version) of ASIO.
|
||||
@@ -21,3 +13,13 @@ The Night Club's very own basic image hosting web frontend.
|
||||
4. lastly run ``$ make``
|
||||
|
||||
You can run ``./Webserver`` to run the server fully.
|
||||
|
||||
|
||||
# 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!
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "main.hpp"
|
||||
#include "Main.hpp"
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
|
||||
|
101
src/HTTP.cpp
101
src/HTTP.cpp
@@ -1,103 +1,12 @@
|
||||
#include "Helpers.hpp"
|
||||
#include "main.hpp"
|
||||
#include <asio/completion_condition.hpp>
|
||||
#include <asio/impl/read_until.hpp>
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include "Main.hpp"
|
||||
|
||||
void HTTPrequest::start() {
|
||||
// Possible Logging here
|
||||
readData();
|
||||
processHTTPHeader();
|
||||
}
|
||||
|
||||
void HTTPrequest::readData() {
|
||||
//
|
||||
// Reading happens here
|
||||
//
|
||||
std::shared_ptr<HTTPrequest> 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<std::string, std::string> request;
|
||||
std::unordered_map<std::string, std::string> 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void HTTPrequest::processRequest(
|
||||
std::string requestType, std::string requestPath,
|
||||
std::unordered_map<std::string, std::string> request,
|
||||
std::unordered_map<std::string, std::string> args) {
|
||||
//
|
||||
// This is where we will process requests
|
||||
//
|
||||
void HTTPrequest::processRequest() {
|
||||
uint64_t pathHash = Helpers::Pathhash(requestPath);
|
||||
|
||||
// This is very much temp
|
||||
@@ -125,9 +34,7 @@ void HTTPrequest::processRequest(
|
||||
}
|
||||
|
||||
void HTTPrequest::writeData(std::string data) {
|
||||
//
|
||||
// Response here
|
||||
//
|
||||
// Logging here perhaps
|
||||
asio::async_write(sock, asio::buffer(data),
|
||||
[](std::error_code, std::size_t) {});
|
||||
}
|
||||
|
94
src/HTTPRequestProcess.cpp
Normal file
94
src/HTTPRequestProcess.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "Helpers.hpp"
|
||||
#include "Main.hpp"
|
||||
#include <istream>
|
||||
|
||||
// TODO: Remove boundary from the body. Do keep other stuff like
|
||||
// file type and name
|
||||
|
||||
void HTTPrequest::processHTTPHeader() {
|
||||
std::shared_ptr<HTTPrequest> self(shared_from_this());
|
||||
|
||||
// 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::size_t octetCount = 0;
|
||||
|
||||
// This is HTTP main request
|
||||
std::string rq;
|
||||
Helpers::getlineAndCount(stream, octetCount, requestType,
|
||||
' '); // HTTP request type
|
||||
Helpers::getlineAndCount(stream, octetCount, requestPath,
|
||||
' '); // HTTP path
|
||||
Helpers::getlineAndCount(stream, octetCount,
|
||||
rq); // Version, omitting for now
|
||||
|
||||
// Get arguments and other header stuff out of the way
|
||||
processArgs();
|
||||
processHeaderValues(stream, octetCount, packageSize);
|
||||
processBody();
|
||||
|
||||
processRequest(); // This writes data too
|
||||
sock.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ===================
|
||||
// Inline Helpers
|
||||
// ===================
|
||||
|
||||
void inline HTTPrequest::processArgs() {
|
||||
std::string key, value;
|
||||
if (requestPath.find('?') != std::string::npos) {
|
||||
std::stringstream pathStream(requestPath);
|
||||
std::getline(pathStream, requestPath, '?'); // Keeping the path pure
|
||||
|
||||
while (!pathStream.eof()) {
|
||||
std::getline(pathStream, key, '=');
|
||||
std::getline(pathStream, value, '&');
|
||||
args.insert({key, value});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void inline HTTPrequest::processHeaderValues(std::basic_istream<char> &stream,
|
||||
std::size_t &octetCount,
|
||||
std::size_t headerSize) {
|
||||
std::string key, value, empty;
|
||||
while (octetCount < headerSize - 2) { // -2 is due to last two \r\n\r\n combo
|
||||
Helpers::getlineAndCount(stream, octetCount, key, ':');
|
||||
Helpers::getlineAndCount(stream, octetCount, empty,
|
||||
' '); // trim start some gangster shit right here
|
||||
Helpers::getlineAndCount(stream, octetCount, value);
|
||||
headers.insert({key, value});
|
||||
}
|
||||
}
|
||||
|
||||
// ===================
|
||||
// BODY OF THE REQUEST
|
||||
// ===================
|
||||
|
||||
void inline HTTPrequest::processBody() {
|
||||
if (headers.find("Content-Length") != headers.end()) {
|
||||
std::size_t uploadSize =
|
||||
std::stoull(headers.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);
|
||||
bodyContent.resize(buffer.size());
|
||||
bodyStream.read(bodyContent.data(), buffer.size());
|
||||
|
||||
buffer.consume(buffer.size()); // Just so the buffer is nice and clean
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@
|
||||
using asocket = asio::ip::tcp::socket;
|
||||
using asocket_ptr = std::shared_ptr<asocket>;
|
||||
|
||||
// We handle HTTP requests with readData, writeData, and processRequest
|
||||
// We handle HTTP requests with processHTTPHeader, writeData, and processRequest
|
||||
class HTTPrequest : public std::enable_shared_from_this<HTTPrequest> {
|
||||
public:
|
||||
typedef std::shared_ptr<HTTPrequest> HTTPrequest_ptr;
|
||||
@@ -30,12 +30,23 @@ public:
|
||||
|
||||
private:
|
||||
HTTPrequest(asio::io_context &context);
|
||||
void readData();
|
||||
void processRequest(std::string requestType, std::string requestPath,
|
||||
std::unordered_map<std::string, std::string> request,
|
||||
std::unordered_map<std::string, std::string> args);
|
||||
void processRequest();
|
||||
void writeData(std::string data);
|
||||
|
||||
// Breaking Header to in lines
|
||||
void processHTTPHeader();
|
||||
void inline processArgs();
|
||||
void inline processHeaderValues(std::basic_istream<char> &stream,
|
||||
std::size_t &octetCount,
|
||||
std::size_t headerSize);
|
||||
void inline processBody();
|
||||
|
||||
// Request itself
|
||||
std::string requestType, requestPath;
|
||||
std::unordered_map<std::string, std::string> headers, args;
|
||||
std::string bodyContent;
|
||||
|
||||
// Networking
|
||||
asio::ip::tcp::socket sock;
|
||||
asio::streambuf buffer;
|
||||
};
|
@@ -1,4 +1,4 @@
|
||||
#include "main.hpp"
|
||||
#include "Main.hpp"
|
||||
#include <iostream>
|
||||
|
||||
// Webserver is defined at main.hpp, you probably do not want to code here
|
||||
|
@@ -8,7 +8,8 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Test</h1>
|
||||
<h1>Welcome</h1>
|
||||
<h1>POST test</h1>
|
||||
<form action="/upload" method="POST" enctype="multipart/form-data">
|
||||
<input type="file" name="uploaded_file">
|
||||
<input type="submit" value="Upload">
|
||||
|
Reference in New Issue
Block a user