This commit is contained in:
Nicholas Orlowsky 2023-10-17 13:16:31 -04:00
parent 38359bd957
commit d6eb46d310
No known key found for this signature in database
GPG key ID: BE7DF0188A405E2B
20 changed files with 178 additions and 1020295 deletions

View file

@ -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

View file

@ -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"]

View file

@ -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')

View file

@ -12,6 +12,9 @@ build-docker:
run: build
./anthracite 8080
run-test: build
./anthracite 8080 ./test_www
debug: build
gdb --args ./anthracite 8080

View file

@ -4,28 +4,11 @@
class file_backend : public backend {
private:
unordered_map<string, string> file_cache;
bool cache_enabled;
unique_ptr<http_response> 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<http_response>(buffer.str(), status);
}
string file_dir;
unique_ptr<http_response> 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<http_response>(file_info->second, status);
return make_unique<http_response>(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) {
file_backend(string dir = "./www") : file_dir(dir) {
populate_cache();
}
}
unique_ptr<http_response> handle_request(http_request& req) override {
return cache_enabled ? handle_request_cache(req) : handle_request_nocache(req);
return handle_request_cache(req);
}
};

View file

@ -139,6 +139,27 @@ static unordered_map<int, string> const http_status_map = {
{ 420, "ENHANCE YOUR CALM" }
};
static unordered_map<std::string, std::string> 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<string, http_header> _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;
}
};

View file

@ -16,6 +16,7 @@ using namespace std;
void log_request_and_response(http_request& req, unique_ptr<http_response>& 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<http_response> 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<std::mutex> 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<std::mutex> 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();
}

View file

@ -56,7 +56,7 @@ public:
client_socket = -1;
}
void send_message(string msg)
void send_message(string& msg)
{
if (client_socket == -1) {
return;

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

9
src/test_www/index.html Normal file
View file

@ -0,0 +1,9 @@
<head>
<title>Anthracite</title>
<link rel="icon" type="image/x-icon" href="/images/favicon.ico">
</head>
<center>
<h1>Anthracite is Running!</h1>
<p>If you are seeing this page, then Anthracite is configured correctly!</p>
<p>Add files to the "www" directory to begin serving your website.</p>
</center>

18
src/test_www/test.css Normal file
View file

@ -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;
}

25
src/test_www/test.html Normal file
View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>Anthracite</title>
<link rel="icon" type="image/x-icon" href="/images/favicon_anim.ico">
<link rel="stylesheet" href="test.css">
</head>
<body>
<center>
<h1 class="cool-style">Test Page!</h1>
<h2>Dogs</h2>
<div class="content">
<img src="/images/tini.png" alt="A border collie" />
<img src="/images/lola.jpeg" alt="A border collie" />
<img src="/images/emma.bmp" alt="A corgi" />
</div>
<h2>Trains</h2>
<div>
<video controls>
<source src="videos/train_vid.mp4" />
</video>
</div>
</center>
</body>
</html>

Binary file not shown.

View file

@ -1,3 +1,7 @@
<head>
<title>Anthracite</title>
<link rel="icon" type="image/x-icon" href="/images/favicon.ico">
</head>
<center>
<h1>Anthracite is Running!</h1>
<p>If you are seeing this page, then Anthracite is configured correctly!</p>

File diff suppressed because it is too large Load diff

View file

@ -1,3 +0,0 @@
<center>
<h1>Test Page!</h1>
</center>