From c9fdf3b85de3d0b16ed119d2135285407c99e514 Mon Sep 17 00:00:00 2001 From: Nicholas Orlowsky Date: Wed, 6 Sep 2023 16:44:29 -0400 Subject: [PATCH] init commit --- .gitignore | 1 + Dockerfile | 7 ++ Makefile | 16 +++++ README.md | 10 +++ error_pages/404.html | 12 ++++ main.cpp | 165 +++++++++++++++++++++++++++++++++++++++++++ www/index.html | 5 ++ 7 files changed, 216 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 error_pages/404.html create mode 100644 main.cpp create mode 100644 www/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..02ba98c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +anthracite diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4f1694d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM alpine as build-env + +RUN apk add --no-cache build-base +COPY . . +RUN make build-release + +CMD ["/anthracite"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..97a758d --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +.PHONY: format + +build: + g++ main.cpp -g -o anthracite + +build-release: + g++ main.cpp -O2 -o anthracite + +build-docker: + docker build . -t anthracite + +run: build + ./anthracite + +format: + clang-format *.cpp -i diff --git a/README.md b/README.md new file mode 100644 index 0000000..7a42753 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Anthracite +A web server written in C++ + +## Todo +- [x] Serve HTML Pages +- [ ] Fix glaring security issues +- [ ] Speed optimizations such as keepint the most visited html pages in memory +- [ ] Cleanup codebase +- [ ] Enable cache support +- [ ] Support newer HTTP versions diff --git a/error_pages/404.html b/error_pages/404.html new file mode 100644 index 0000000..50cdb27 --- /dev/null +++ b/error_pages/404.html @@ -0,0 +1,12 @@ + + Not Found + + +
+

404 - Not Found

+
+

Anthracite/0.0.1

+

Created by Nicholas Orlowsky - This is Open Source Software

+
+ + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..51ecf7e --- /dev/null +++ b/main.cpp @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class Socket { +private: + int server_socket; + int client_socket; + +public: + Socket(int port, int max_queue = 10) { + struct sockaddr_in address; + address.sin_family = AF_INET; + address.sin_port = htons(port); + address.sin_addr.s_addr = INADDR_ANY; + + server_socket = socket(AF_INET, SOCK_STREAM, 0); + int x = 1; + setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); + bind(server_socket, (struct sockaddr *)&address, sizeof(address)); + + ::listen(server_socket, max_queue); + } + + void wait_for_conn() { client_socket = accept(server_socket, NULL, NULL); } + + void close_conn() { + close(client_socket); + client_socket = -1; + } + + void send_message(string msg) { + if (client_socket == -1) { + return; + } + send(client_socket, &msg[0], msg.length(), 0); + } + + string recv_message(int buffer_size = 1024) { + if (client_socket == -1) { + return ""; + } + + char response[buffer_size + 1]; + recv(client_socket, response, sizeof(response), 0); + response[buffer_size] = '\0'; + return string(response); + } +}; + +enum http_method { GET, POST, PUT, PATCH, UNKNOWN }; +static unordered_map const http_method_map = { + {"GET", http_method::GET}}; + +static unordered_map const http_status_map = { + {200, "OK"}, + {404, "NOT FOUND"}, +}; + +class http_request { +private: + http_method _method; + string _path; + +public: + http_request(string raw_data) { + int state = 0; + string method = ""; + for (int i = 0; i < raw_data.length() && state != 2; i++) { + + if (raw_data[i] == ' ') { + state++; + continue; + } + + if (state == 0) { + method += raw_data[i]; + } else if (state == 1) { + _path += raw_data[i]; + } + } + + if (http_method_map.find(method) == http_method_map.end()) { + _method = http_method::UNKNOWN; + } else { + _method = http_method_map.find(method)->second; + } + } + + string path() { return _path; } + + http_method method() { return _method; } +}; + +class http_response { +private: + int _status_code; + string _content; + +public: + http_response(string content, int status_code = 200) { + _content = content; + _status_code = status_code; + } + + string to_string() { + string response = ""; + response += "HTTP/1.1 " + ::to_string(_status_code) + " " + + http_status_map.find(_status_code)->second + " \n"; + response += "Content-Type: text/html\n"; + response += "Content-Length: " + ::to_string(_content.length()) + "\n"; + response += "Server: Anthracite/0.0.1\n\n"; + response += _content; + return response; + } +}; + +int main() { + cout << "Initializing Server...\n"; + Socket s(8099); + cout << "Initialized, Awaiting Connections...\n"; + + while (true) { + s.wait_for_conn(); + cout << "Received Request...\n"; + http_request req(s.recv_message(8190)); + + string response = ""; + + switch (req.method()) { + case http_method::GET: { + string filename = req.path() == "/" ? "index.html" : req.path(); + filename = "./www/" + filename; + ifstream stream(filename); + + int status = 200; + if (!stream.is_open()) { + status = 404; + filename = "./error_pages/404.html"; + stream = ifstream(filename); + } + + stringstream buffer; + buffer << stream.rdbuf(); + http_response res(buffer.str(), status); + response = res.to_string(); + } break; + + default: + break; + } + cout << "Responding with: \n" << response << "\n"; + s.send_message(response); + s.close_conn(); + } + + return 0; +} diff --git a/www/index.html b/www/index.html new file mode 100644 index 0000000..fae6298 --- /dev/null +++ b/www/index.html @@ -0,0 +1,5 @@ +
+

Anthracite is Running!

+

If you are seeing this page, then Anthracite is configured correctly!

+

Add files to the "www" directory to begin serving your website.

+