version 0.2.0

This commit is contained in:
Nicholas Orlowsky 2023-10-20 12:46:30 -04:00
parent d19c4efad3
commit 3dddee43f7
No known key found for this signature in database
GPG key ID: BE7DF0188A405E2B
32 changed files with 243 additions and 1020337 deletions

View file

@ -1,7 +1,7 @@
.PHONY: format lint build build-release build-docker run debug
build:
python3 ./error_gen.py
cd ./build && ./version.sh && python3 ./error_gen.py
g++ main.cpp --std=c++20 -g -o ./anthracite
build-release:
@ -19,6 +19,9 @@ run-test: build
debug: build
gdb --args ./anthracite 8080
debug-test: build
gdb --args ./anthracite 8080 ./test_www
format:
clang-format *.cpp -i

View file

@ -1,11 +1,12 @@
#include <filesystem>
#include <string>
#include "backend.cpp"
class file_backend : public backend {
private:
std::unordered_map<std::string,std::string> file_cache;
std::string file_dir;
std::unique_ptr<http_response> handle_request_cache(http_request& req) {
std::string filename = req.path() == "/" ? "/index.html" : req.path();
filename = file_dir + filename;
@ -13,9 +14,7 @@ private:
int status = http_status_codes::OK;
if (file_info == file_cache.end()) {
status = http_status_codes::NOT_FOUND;
filename = "./error_pages/404.html";
file_info = file_cache.find(filename);
return handle_error(http_status_codes::NOT_FOUND);
}
return std::make_unique<http_response>(file_info->second, filename, status);
@ -47,9 +46,22 @@ public:
populate_cache();
}
~file_backend() = default;
std::unique_ptr<http_response> handle_request(http_request& req) override {
return handle_request_cache(req);
}
std::unique_ptr<http_response> handle_error(const http_status_codes& error) {
std::string filename = "./error_pages/" + std::to_string(error) + ".html";
auto file_info = file_cache.find(filename);
http_status_codes status = error;
if (file_info == file_cache.end()) {
status = http_status_codes::NOT_FOUND;
filename = "./error_pages/404.html";
file_info = file_cache.find(filename);
}
return std::make_unique<http_response>(file_info->second, filename, status);
}
};

View file

@ -3,16 +3,18 @@
import os
version = "0.1.5"
version = "Unknown"
def generate_error_page(error_code, error_title, error_description):
with open("version.txt", "r") as file:
version = file.read().strip()
def generate_error_page(error_code, error_title):
html = f"""<html>
<head><title>{error_title}</title></head>
<body>
<center>
<h1>{error_code} - {error_title}</h1>
<hr>
<p>{error_description}</p>
<p>Anthracite/{version}</p>
<p><small><a href="https://github.com/nickorlow/anthracite">This is Open Source Software</small></a></p>
</center>
@ -64,12 +66,11 @@ error_codes = {
}
error_dir = './error_pages'
error_dir = '../error_pages'
os.makedirs(error_dir, exist_ok=True)
for code, title in error_codes.items():
error_description = error_codes[code]
error_page = generate_error_page(code, title, error_description)
error_page = generate_error_page(code, title)
file_path = os.path.join(error_dir, f"{code}.html")
with open(file_path, "w") as file:
file.write(error_page)

3
src/build/version.sh Executable file
View file

@ -0,0 +1,3 @@
echo "#include <string>" > version.cpp
echo "const std::string ANTHRACITE_VERSION_STRING = \"$(cat version.txt)\";" >> version.cpp
echo "const std::string ANTHRACITE_FULL_VERSION_STRING = \"Anthracite/$(cat version.txt)\";" >> version.cpp

1
src/build/version.txt Normal file
View file

@ -0,0 +1 @@
0.2.0

View file

@ -219,6 +219,8 @@ enum http_version { HTTP_0_9,
HTTP_3_0 };
static std::unordered_map<std::string, http_version> const http_version_map = {
// This is because HTTP 0.9 didn't specify version in the header
{ "", HTTP_0_9 },
{ "HTTP/0.9", HTTP_0_9 },
{ "HTTP/1.0", HTTP_1_0 },
{ "HTTP/1.1", HTTP_1_1 },

View file

@ -20,8 +20,8 @@ public:
name_value(name_value&&) = default;
name_value& operator=(name_value&&) = default;
std::string name() { return _name; }
std::string value() { return _value; }
std::string& name() { return _name; }
std::string& value() { return _value; }
virtual std::string to_string() { return ""; }
};

View file

@ -5,3 +5,4 @@
#include "constants.cpp"
#include "header_query.cpp"
#include "../socket.cpp"
#include "../build/version.cpp"

View file

@ -19,10 +19,9 @@ private:
std::unordered_map<std::string, query_param> _query_params; // kinda goofy, whatever
public:
http_request(anthracite_socket& s)
: _path(""), _client_ipaddr(s.get_client_ip())
http_request(std::string& raw_data, std::string client_ip)
: _path(""), _client_ipaddr(client_ip)
{
std::string raw_data = s.recv_message(HTTP_HEADER_BYTES);
parser_state state = METHOD;
@ -136,6 +135,25 @@ public:
std::string client_ip() { return _client_ipaddr; }
http_version get_http_version() {
return _http_version;
}
bool is_supported_version() {
return _http_version == HTTP_1_1 || _http_version == HTTP_1_0;
}
bool close_connection() {
const auto& header = _headers.find("Connection");
const bool found = header != _headers.end();
if(found && header->second.value() == "keep-alive") {
return false;
}
return true;
}
std::string to_string()
{
std::string response = "";

View file

@ -32,17 +32,18 @@ public:
std::string header_to_string()
{
std::string response = "";
response += "HTTP/1.0 " + std::to_string(_status_code) + " " + http_status_map.find(_status_code)->second + "\r\n";
response += "HTTP/1.1 " + std::to_string(_status_code) + " " + http_status_map.find(_status_code)->second + "\r\n";
std::string content_type = "text/html";
std::string file_extension = _filename.substr(_filename.rfind('.') + 1);
auto mime_type = mime_types.find(file_extension);
if (mime_type != mime_types.end()) {
content_type = mime_type->second;
}
add_header(http_header("Content-Type", content_type), false);
add_header(http_header("Content-Length", std::to_string(_content.length())), false);
add_header(http_header("Server", "Anthracite/0.0.1"), false);
add_header(http_header("Origin-Server", "Anthracite/0.0.1"), false);
add_header(http_header("Server", ANTHRACITE_FULL_VERSION_STRING), false);
add_header(http_header("Origin-Server", ANTHRACITE_FULL_VERSION_STRING), false);
for (auto header : _headers) {
response += header.second.to_string();

View file

@ -17,15 +17,24 @@ void log_request_and_response(http_request& req, std::unique_ptr<http_response>&
constexpr int default_port = 80;
constexpr int max_worker_threads = 128;
void handle_client(anthracite_socket s, backend& b, std::mutex& thread_wait_mutex, std::condition_variable& thread_wait_condvar, int& active_threads)
void handle_client(anthracite_socket s, backend& b, file_backend& fb, std::mutex& thread_wait_mutex, std::condition_variable& thread_wait_condvar, int& active_threads)
{
http_request req(s);
std::unique_ptr<http_response> resp = b.handle_request(req);
log_request_and_response(req, resp);
std::string header = resp->header_to_string();
s.send_message(header);
s.send_message(resp->content());
resp.reset();
while(true) {
std::string raw_request = s.recv_message(HTTP_HEADER_BYTES);
if(raw_request == "") {
break;
}
http_request req(raw_request, s.get_client_ip());
std::unique_ptr<http_response> resp = req.is_supported_version() ? b.handle_request(req) : fb.handle_error(http_status_codes::HTTP_VERSION_NOT_SUPPORTED);
log_request_and_response(req, resp);
std::string header = resp->header_to_string();
s.send_message(header);
s.send_message(resp->content());
resp.reset();
if(req.close_connection()) {
break;
}
}
s.close_conn();
{
std::lock_guard<std::mutex> lock(thread_wait_mutex);
@ -58,7 +67,7 @@ int main(int argc, char** argv)
std::unique_lock<std::mutex> lock(thread_wait_mutex);
thread_wait_condvar.wait(lock, [active_threads] { return active_threads < max_worker_threads; });
active_threads++;
std::thread(handle_client, s, std::ref(fb), std::ref(thread_wait_mutex), std::ref(thread_wait_condvar), std::ref(active_threads)).detach();
std::thread(handle_client, s, std::ref(fb), std::ref(fb), std::ref(thread_wait_mutex), std::ref(thread_wait_condvar), std::ref(active_threads)).detach();
}
exit(0);

View file

@ -8,6 +8,7 @@
#include <sys/socket.h>
#include <unistd.h>
#include <unordered_map>
#include <sys/time.h>
constexpr int MAX_QUEUE_LENGTH = 100;
@ -70,8 +71,17 @@ public:
return "";
}
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
char response[buffer_size + 1];
recv(client_socket, response, sizeof(response), 0);
int result = recv(client_socket, response, sizeof(response), 0);
if (result < 1) {
return "";
}
response[buffer_size] = '\0';
return std::string(response);
}