diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a72cf7..2e77ad9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,56 @@ +# 0.1.1 Third Pre-Release + +- Add mappings for common MIME types +- Heavily improved resource utilization +- Removed non-cached file backend +- Updated dockerfile to remove build files after build + +## Known Issues + +- Benchmark program does not include resource utilization + +## Benchmark Results + +Each benchmark makes 1000 requests requesting a 21.9Mb file using +100 users to the webserver running in a Docker container. + +The results from this benchmark vary quite a bit. About half the time, +Apache can beat anthracite. + +``` +=====[ Anthracite Benchmarking Tool ]===== +Requests : 1000 +Users/Threads: 100 + + +====[ anthracite ]===== +Average Response Time: 5.5128 seconds +p995 Response Time : 8.3105 seconds +p99 Response Time : 8.1796 seconds +p90 Response Time : 6.4393 seconds +p75 Response Time : 5.8587 seconds +p50 Response Time : 5.4393 seconds +Total Response Time : 5512.8397 seconds +====[ nginx ]===== +Average Response Time: 6.0201 seconds +p995 Response Time : 12.4648 seconds +p99 Response Time : 11.9635 seconds +p90 Response Time : 8.7204 seconds +p75 Response Time : 6.7331 seconds +p50 Response Time : 5.5341 seconds +Total Response Time : 6020.1369 seconds +====[ apache ]===== +Average Response Time: 5.9795 seconds +p995 Response Time : 12.8266 seconds +p99 Response Time : 11.6336 seconds +p90 Response Time : 7.1465 seconds +p75 Response Time : 6.4420 seconds +p50 Response Time : 5.8224 seconds +Total Response Time : 5979.5446 seconds +========== +Total Test Time : 179.7469 seconds +``` + # 0.1.0 Second Pre-Release - Allowed multiple clients to be handled at once via multithreadding diff --git a/Dockerfile b/Dockerfile index 54cd30a..67ee81b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,4 +4,9 @@ RUN apk add --no-cache build-base COPY ./src/ . RUN make build-release +FROM alpine +RUN apk add --no-cache build-base +COPY --from=build-env /anthracite /anthracite +COPY --from=build-env /www /www +COPY --from=build-env /error_pages /error_pages CMD ["/anthracite"] diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py index 76354eb..d5a6fdd 100644 --- a/benchmark/benchmark.py +++ b/benchmark/benchmark.py @@ -34,6 +34,7 @@ def make_request(request_number, server_name): response_times[server_name].append(response_time) else: print(f'Request {request_number}: Request failed with status code {response.status_code}') + print('=====[ Anthracite Benchmarking Tool ]=====') print(f'Requests : {num_requests}') print(f'Users/Threads: {num_users}\n\n') diff --git a/src/Makefile b/src/Makefile index b31b9d0..fa4a540 100644 --- a/src/Makefile +++ b/src/Makefile @@ -12,6 +12,9 @@ build-docker: run: build ./anthracite 8080 +run-test: build + ./anthracite 8080 ./test_www + debug: build gdb --args ./anthracite 8080 diff --git a/src/backends/file_backend.cpp b/src/backends/file_backend.cpp index a0eb5ad..a1fe361 100644 --- a/src/backends/file_backend.cpp +++ b/src/backends/file_backend.cpp @@ -4,28 +4,11 @@ class file_backend : public backend { private: unordered_map file_cache; - bool cache_enabled; - - unique_ptr handle_request_nocache(http_request& req) { - 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(); - return make_unique(buffer.str(), status); - } + string file_dir; unique_ptr handle_request_cache(http_request& req) { - string filename = req.path() == "/" ? "/index.html" : req.path(); - filename = "./www" + filename; + string filename = req.path() == "/" ? "index.html" : req.path(); + filename = file_dir + filename; auto file_info = file_cache.find(filename); int status = 200; @@ -35,7 +18,7 @@ private: file_info = file_cache.find(filename); } - return make_unique(file_info->second, status); + return make_unique(file_info->second, filename, status); } void populate_cache_dir(string dir) { @@ -55,18 +38,16 @@ private: } void populate_cache() { - populate_cache_dir("./www/"); + populate_cache_dir(file_dir); populate_cache_dir("./error_pages/"); } public: - file_backend(bool enable_cache) : cache_enabled(enable_cache) { - if(cache_enabled) { - populate_cache(); - } + file_backend(string dir = "./www") : file_dir(dir) { + populate_cache(); } unique_ptr handle_request(http_request& req) override { - return cache_enabled ? handle_request_cache(req) : handle_request_nocache(req); + return handle_request_cache(req); } }; diff --git a/src/http.cpp b/src/http.cpp index 7d08a9b..8e1a5f0 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -139,6 +139,27 @@ static unordered_map const http_status_map = { { 420, "ENHANCE YOUR CALM" } }; +static unordered_map const mime_types = { + { "html", "text/html" }, + { "css", "text/css" }, + + { "js", "application/javascript" }, + { "pdf", "application/pdf" }, + + { "ico", "image/x-icon" }, + { "jpg", "image/jpeg" }, + { "jpeg", "image/jpeg" }, + { "png", "image/png" }, + { "gif", "image/gif" }, + { "bmp", "image/bmp" }, + + { "mp4", "video/mp4" }, + { "avi", "video/x-msvideo" }, + { "mkv", "video/x-matroska" }, + { "mov", "video/quicktime" }, + { "wmv", "video/x-ms-wmv" }, +}; + class name_value { private: string _name; @@ -378,13 +399,15 @@ public: class http_response { private: int _status_code; - string _content; + string& _content; + string _filename; unordered_map _headers; // kinda goofy, whatever public: - http_response(string content, int status_code = 200) - : _content(std::move(content)) + http_response(string& content, string filename, int status_code = 200) + : _content(content) , _status_code(status_code) + , _filename(std::move(filename)) { } @@ -397,12 +420,22 @@ public: } } - string to_string() + string& content() + { + return _content; + } + + string header_to_string() { string response = ""; response += "HTTP/1.1 " + ::to_string(_status_code) + " " + http_status_map.find(_status_code)->second + "\r\n"; - - add_header(http_header("Content-Type", "text/html"), false); + string content_type = "text/html"; + 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", ::to_string(_content.length())), false); add_header(http_header("Server", "Anthracite/0.0.1"), false); @@ -411,8 +444,12 @@ public: } response += "\r\n"; - response += _content; return response; } + + string to_string() + { + return header_to_string() + _content; + } }; diff --git a/src/main.cpp b/src/main.cpp index db02ff9..89a0400 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ using namespace std; void log_request_and_response(http_request& req, unique_ptr& resp); constexpr int default_port = 80; +constexpr int max_worker_threads = 128; int active_threads = 0; mutex mtx; @@ -26,15 +27,15 @@ void handle_client(anthracite_socket s, file_backend& fb) http_request req(s); unique_ptr resp = fb.handle_request(req); log_request_and_response(req, resp); - s.send_message(resp->to_string()); + string header = resp->header_to_string(); + s.send_message(header); + s.send_message(resp->content()); resp.reset(); s.close_conn(); - { std::lock_guard lock(mtx); active_threads--; } - cv.notify_one(); } @@ -48,14 +49,14 @@ int main(int argc, char** argv) cout << "Initializing Anthracite" << endl; anthracite_socket s(port_number); - file_backend fb(true); + file_backend fb(argc > 2 ? argv[2] : "./www"); cout << "Initialization Complete" << endl; cout << "Listening for HTTP connections on port " << port_number << endl; - for (;;) { + while (true) { s.wait_for_conn(); std::unique_lock lock(mtx); - cv.wait(lock, [] { return active_threads < 20; }); + cv.wait(lock, [] { return active_threads < max_worker_threads; }); active_threads++; thread(handle_client, s, ref(fb)).detach(); } diff --git a/src/socket.cpp b/src/socket.cpp index ae840be..81a5f36 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -56,7 +56,7 @@ public: client_socket = -1; } - void send_message(string msg) + void send_message(string& msg) { if (client_socket == -1) { return; diff --git a/src/test_www/images/emma.bmp b/src/test_www/images/emma.bmp new file mode 100644 index 0000000..30a8027 Binary files /dev/null and b/src/test_www/images/emma.bmp differ diff --git a/src/test_www/images/favicon.ico b/src/test_www/images/favicon.ico new file mode 100644 index 0000000..edcb3e4 Binary files /dev/null and b/src/test_www/images/favicon.ico differ diff --git a/src/test_www/images/favicon_anim.ico b/src/test_www/images/favicon_anim.ico new file mode 100644 index 0000000..7004ff4 Binary files /dev/null and b/src/test_www/images/favicon_anim.ico differ diff --git a/src/test_www/images/lola.jpeg b/src/test_www/images/lola.jpeg new file mode 100644 index 0000000..106d145 Binary files /dev/null and b/src/test_www/images/lola.jpeg differ diff --git a/src/test_www/images/tini.png b/src/test_www/images/tini.png new file mode 100644 index 0000000..4ccd77e Binary files /dev/null and b/src/test_www/images/tini.png differ diff --git a/src/test_www/index.html b/src/test_www/index.html new file mode 100644 index 0000000..558da13 --- /dev/null +++ b/src/test_www/index.html @@ -0,0 +1,9 @@ + + Anthracite + + +
+

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.

+
diff --git a/src/test_www/test.css b/src/test_www/test.css new file mode 100644 index 0000000..15427d1 --- /dev/null +++ b/src/test_www/test.css @@ -0,0 +1,18 @@ +.cool-style { + background: linear-gradient(to right, #ef5350, #f48fb1, #7e57c2, #2196f3, #26c6da, #43a047, #eeff41, #f9a825, #ff5722); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.content { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; +} + +.content img { + max-width: 100%; + height: auto; + margin: 10px; +} diff --git a/src/test_www/test.html b/src/test_www/test.html new file mode 100644 index 0000000..b969beb --- /dev/null +++ b/src/test_www/test.html @@ -0,0 +1,25 @@ + + + + Anthracite + + + + +
+

Test Page!

+

Dogs

+
+ A border collie + A border collie + A corgi +
+

Trains

+
+ +
+
+ + diff --git a/src/test_www/videos/train_vid.mp4 b/src/test_www/videos/train_vid.mp4 new file mode 100644 index 0000000..8db6d05 Binary files /dev/null and b/src/test_www/videos/train_vid.mp4 differ diff --git a/src/www/index.html b/src/www/index.html index fae6298..558da13 100644 --- a/src/www/index.html +++ b/src/www/index.html @@ -1,3 +1,7 @@ + + Anthracite + +

Anthracite is Running!

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

diff --git a/src/www/large.html b/src/www/large.html deleted file mode 100644 index cb6730d..0000000 --- a/src/www/large.html +++ /dev/null @@ -1,1020251 +0,0 @@ - - diff --git a/src/www/test.html b/src/www/test.html deleted file mode 100644 index b269607..0000000 --- a/src/www/test.html +++ /dev/null @@ -1,3 +0,0 @@ -
-

Test Page!

-