format + README
This commit is contained in:
parent
71be773d49
commit
0ebdb34601
|
@ -5,6 +5,7 @@ project(anthracite)
|
|||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
add_custom_target(build-version
|
||||
COMMAND cd ../build_supp && ./version.sh
|
||||
|
|
12
README.md
12
README.md
|
@ -3,9 +3,9 @@ A simple web server written in C++. Supports HTTP 1.0 & 1.1.
|
|||
|
||||
## Developing
|
||||
|
||||
To build/develop Anthracite, you must have C++20, Make, and Python3 installed.
|
||||
To build/develop Anthracite, you must have C++23, CMake, Make, and Python3 installed.
|
||||
|
||||
You can run Anthracite with: `make run`
|
||||
Create a `build/` directory, run `cmake ..`, and then `make` to build.
|
||||
|
||||
## Todo
|
||||
- [x] HTTP/1.0
|
||||
|
@ -14,14 +14,14 @@ You can run Anthracite with: `make run`
|
|||
- [x] Add module-based backend system for handling requests
|
||||
- [x] Multithreading
|
||||
- [x] HTTP/1.1
|
||||
- [ ] Improve benchmarking infrastructure
|
||||
- [x] Enhance logging
|
||||
- [-] Build out module-based backend system for handling requests
|
||||
- [ ] Faster parsing
|
||||
- [ ] HTTP/2
|
||||
- [ ] Improve benchmarking infrastructure
|
||||
- [ ] Fix glaring security issues
|
||||
- [ ] Proper error handling
|
||||
- [ ] User configuration
|
||||
- [ ] Build out module-based backend system for handling requests
|
||||
- [ ] HTTP/2
|
||||
- [ ] Enhance logging
|
||||
- [ ] Cleanup (this one will never truly be done)
|
||||
|
||||
## Screenshots
|
||||
|
|
2
format.sh
Executable file
2
format.sh
Executable file
|
@ -0,0 +1,2 @@
|
|||
git ls-files -- '*.cpp' '*.h' | xargs clang-format -i -style=file
|
||||
git diff --exit-code --color
|
|
@ -1,15 +1,15 @@
|
|||
#include "backends/file_backend.hpp"
|
||||
#include "./anthracite.hpp"
|
||||
#include "./log/log.hpp"
|
||||
#include "./socket/socket.hpp"
|
||||
#include "backends/file_backend.hpp"
|
||||
#include <condition_variable>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <netinet/in.h>
|
||||
#include <span>
|
||||
#include <sys/socket.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
#include <span>
|
||||
#include "./log/log.hpp"
|
||||
#include "./socket/socket.hpp"
|
||||
|
||||
using namespace anthracite;
|
||||
|
||||
|
@ -44,7 +44,7 @@ void handle_client(socket::anthracite_socket s, backends::backend& b, backends::
|
|||
thread_wait_condvar.notify_one();
|
||||
}
|
||||
|
||||
//int main(int argc, char** argv)
|
||||
// int main(int argc, char** argv)
|
||||
int anthracite_main(int argc, char** argv, backends::backend& be)
|
||||
{
|
||||
log::logger.initialize(log::LOG_LEVEL_INFO);
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
#include "./file_backend.hpp"
|
||||
#include "../log/log.hpp"
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace anthracite::backends {
|
||||
|
||||
std::unique_ptr<http::response> file_backend::handle_request_cache(http::request& req)
|
||||
{
|
||||
std::string filename = req.path() == "/" ? "/index.html" : req.path();
|
||||
filename = file_dir + filename;
|
||||
auto file_info = file_cache.find(filename);
|
||||
|
||||
std::unique_ptr<http::response> file_backend::handle_request_cache(http::request& req) {
|
||||
std::string filename = req.path() == "/" ? "/index.html" : req.path();
|
||||
filename = file_dir + filename;
|
||||
auto file_info = file_cache.find(filename);
|
||||
int status = http::status_codes::OK;
|
||||
if (file_info == file_cache.end()) {
|
||||
return handle_error(http::status_codes::NOT_FOUND);
|
||||
}
|
||||
|
||||
int status = http::status_codes::OK;
|
||||
if (file_info == file_cache.end()) {
|
||||
return handle_error(http::status_codes::NOT_FOUND);
|
||||
}
|
||||
std::unique_ptr<http::response> resp = std::make_unique<http::response>();
|
||||
|
||||
std::unique_ptr<http::response> resp = std::make_unique<http::response>();
|
||||
std::string file_extension = file_info->first.substr(file_info->first.rfind('.') + 1);
|
||||
std::string content_type = "text/html";
|
||||
|
||||
std::string file_extension = file_info->first.substr(file_info->first.rfind('.') + 1);
|
||||
std::string content_type = "text/html";
|
||||
auto mime_type = http::mime_types.find(file_extension);
|
||||
if (mime_type != http::mime_types.end()) {
|
||||
content_type = mime_type->second;
|
||||
}
|
||||
|
||||
auto mime_type = http::mime_types.find(file_extension);
|
||||
if (mime_type != http::mime_types.end()) {
|
||||
content_type = mime_type->second;
|
||||
}
|
||||
resp->add_body_ref(&file_info->second);
|
||||
resp->add_status(http::status_codes::OK);
|
||||
resp->add_header(http::header("Content-Type", content_type), false);
|
||||
|
||||
resp->add_body_ref(&file_info->second);
|
||||
resp->add_status(http::status_codes::OK);
|
||||
resp->add_header(http::header("Content-Type", content_type), false);
|
||||
return resp;
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
void file_backend::populate_cache_dir(std::string dir) {
|
||||
void file_backend::populate_cache_dir(std::string dir)
|
||||
{
|
||||
std::filesystem::recursive_directory_iterator cur = begin(std::filesystem::recursive_directory_iterator(dir));
|
||||
std::filesystem::recursive_directory_iterator fin = end(std::filesystem::recursive_directory_iterator(dir));
|
||||
|
||||
|
@ -48,40 +48,44 @@ namespace anthracite::backends {
|
|||
log::verbose << "File at " << filename << " cached (" << file_cache[filename].size() << " bytes)" << std::endl;
|
||||
++cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void file_backend::populate_cache() {
|
||||
void file_backend::populate_cache()
|
||||
{
|
||||
populate_cache_dir(file_dir);
|
||||
populate_cache_dir("./error_pages/");
|
||||
}
|
||||
}
|
||||
|
||||
file_backend::file_backend(std::string dir) : file_dir(std::move(dir)) {
|
||||
file_backend::file_backend(std::string dir)
|
||||
: file_dir(std::move(dir))
|
||||
{
|
||||
populate_cache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<http::response> file_backend::handle_request(http::request& req) {
|
||||
std::unique_ptr<http::response> file_backend::handle_request(http::request& req)
|
||||
{
|
||||
return handle_request_cache(req);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<http::response> file_backend::handle_error(const http::status_codes& error) {
|
||||
std::string filename = "./error_pages/" + std::to_string(error) + ".html";
|
||||
auto file_info = file_cache.find(filename);
|
||||
std::unique_ptr<http::response> file_backend::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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
std::unique_ptr<http::response> resp = std::make_unique<http::response>();
|
||||
std::unique_ptr<http::response> resp = std::make_unique<http::response>();
|
||||
|
||||
resp->add_body_ref(&file_info->second);
|
||||
resp->add_status(error);
|
||||
resp->add_header(http::header("Content-Type", "text/html"), false);
|
||||
resp->add_body_ref(&file_info->second);
|
||||
resp->add_status(error);
|
||||
resp->add_header(http::header("Content-Type", "text/html"), false);
|
||||
|
||||
return resp;
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -1,165 +1,169 @@
|
|||
#include "request.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "../log/log.hpp"
|
||||
#include "constants.hpp"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace anthracite::http {
|
||||
|
||||
request::request(std::string& raw_data, const std::string& client_ip)
|
||||
: _path(""), _client_ipaddr(client_ip)
|
||||
{
|
||||
request::request(std::string& raw_data, const std::string& client_ip)
|
||||
: _path("")
|
||||
, _client_ipaddr(client_ip)
|
||||
{
|
||||
|
||||
parser_state state = METHOD;
|
||||
parser_state state = METHOD;
|
||||
|
||||
std::string scratch = "";
|
||||
std::string scratch_2 = "";
|
||||
for (int i = 0; i < raw_data.length(); i++) {
|
||||
switch (state) {
|
||||
case METHOD: {
|
||||
if (raw_data[i] == ' ') {
|
||||
if (method_map.find(scratch) == method_map.end()) {
|
||||
_method = method::UNKNOWN;
|
||||
} else {
|
||||
_method = method_map.find(scratch)->second;
|
||||
}
|
||||
scratch = "";
|
||||
state = PATH;
|
||||
std::string scratch = "";
|
||||
std::string scratch_2 = "";
|
||||
for (int i = 0; i < raw_data.length(); i++) {
|
||||
switch (state) {
|
||||
case METHOD: {
|
||||
if (raw_data[i] == ' ') {
|
||||
if (method_map.find(scratch) == method_map.end()) {
|
||||
_method = method::UNKNOWN;
|
||||
} else {
|
||||
scratch += raw_data[i];
|
||||
_method = method_map.find(scratch)->second;
|
||||
}
|
||||
} break;
|
||||
|
||||
case PATH: {
|
||||
switch (raw_data[i]) {
|
||||
case ' ':
|
||||
state = VERSION;
|
||||
break;
|
||||
case '?':
|
||||
state = QUERY_PARAM_NAME;
|
||||
break;
|
||||
default:
|
||||
_path += raw_data[i];
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case QUERY_PARAM_NAME: {
|
||||
if (raw_data[i] == ' ') {
|
||||
scratch = "";
|
||||
state = VERSION;
|
||||
} else if (raw_data[i] == '=') {
|
||||
state = QUERY_PARAM_VALUE;
|
||||
} else {
|
||||
scratch += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case QUERY_PARAM_VALUE: {
|
||||
if (raw_data[i] == ' ') {
|
||||
_query_params[scratch] = query_param(scratch, scratch_2);
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = VERSION;
|
||||
} else if (raw_data[i] == '&') {
|
||||
_query_params[scratch] = query_param(scratch, scratch_2);
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = QUERY_PARAM_NAME;
|
||||
} else {
|
||||
scratch_2 += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case VERSION: {
|
||||
if (raw_data[i] == '\n') {
|
||||
_http_version = version_map.find(scratch)->second;
|
||||
scratch = "";
|
||||
state = HEADER_NAME;
|
||||
} else if (raw_data[i] != '\r') {
|
||||
scratch += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case HEADER_NAME: {
|
||||
if (raw_data[i] == '\n') {
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = BODY_CONTENT;
|
||||
break;
|
||||
} else if (raw_data[i] == ' ') {
|
||||
scratch = "";
|
||||
break;
|
||||
} else if (raw_data[i] == ':') {
|
||||
state = HEADER_VALUE;
|
||||
i++;
|
||||
} else {
|
||||
scratch += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case HEADER_VALUE: {
|
||||
if (raw_data[i] == '\n') {
|
||||
_headers[scratch] = header(scratch, scratch_2);
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = HEADER_NAME;
|
||||
} else if (raw_data[i] != '\r') {
|
||||
scratch_2 += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case BODY_CONTENT: {
|
||||
_body_content += raw_data[i];
|
||||
} break;
|
||||
scratch = "";
|
||||
state = PATH;
|
||||
} else {
|
||||
scratch += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case PATH: {
|
||||
switch (raw_data[i]) {
|
||||
case ' ':
|
||||
state = VERSION;
|
||||
break;
|
||||
case '?':
|
||||
state = QUERY_PARAM_NAME;
|
||||
break;
|
||||
default:
|
||||
_path += raw_data[i];
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case QUERY_PARAM_NAME: {
|
||||
if (raw_data[i] == ' ') {
|
||||
scratch = "";
|
||||
state = VERSION;
|
||||
} else if (raw_data[i] == '=') {
|
||||
state = QUERY_PARAM_VALUE;
|
||||
} else {
|
||||
scratch += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case QUERY_PARAM_VALUE: {
|
||||
if (raw_data[i] == ' ') {
|
||||
_query_params[scratch] = query_param(scratch, scratch_2);
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = VERSION;
|
||||
} else if (raw_data[i] == '&') {
|
||||
_query_params[scratch] = query_param(scratch, scratch_2);
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = QUERY_PARAM_NAME;
|
||||
} else {
|
||||
scratch_2 += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case VERSION: {
|
||||
if (raw_data[i] == '\n') {
|
||||
_http_version = version_map.find(scratch)->second;
|
||||
scratch = "";
|
||||
state = HEADER_NAME;
|
||||
} else if (raw_data[i] != '\r') {
|
||||
scratch += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case HEADER_NAME: {
|
||||
if (raw_data[i] == '\n') {
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = BODY_CONTENT;
|
||||
break;
|
||||
} else if (raw_data[i] == ' ') {
|
||||
scratch = "";
|
||||
break;
|
||||
} else if (raw_data[i] == ':') {
|
||||
state = HEADER_VALUE;
|
||||
i++;
|
||||
} else {
|
||||
scratch += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case HEADER_VALUE: {
|
||||
if (raw_data[i] == '\n') {
|
||||
_headers[scratch] = header(scratch, scratch_2);
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = HEADER_NAME;
|
||||
} else if (raw_data[i] != '\r') {
|
||||
scratch_2 += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case BODY_CONTENT: {
|
||||
_body_content += raw_data[i];
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string request::path() { return _path; }
|
||||
std::string request::path() { return _path; }
|
||||
|
||||
method request::get_method() { return _method; }
|
||||
method request::get_method() { return _method; }
|
||||
|
||||
std::string request::client_ip() { return _client_ipaddr; }
|
||||
std::string request::client_ip() { return _client_ipaddr; }
|
||||
|
||||
version request::get_http_version() {
|
||||
return _http_version;
|
||||
}
|
||||
version request::get_http_version()
|
||||
{
|
||||
return _http_version;
|
||||
}
|
||||
|
||||
bool request::is_supported_version() {
|
||||
//log::err << reverse_version_map.find(_http_version)->second << std::endl;
|
||||
return _http_version == HTTP_1_1 || _http_version == HTTP_1_0;
|
||||
}
|
||||
bool request::is_supported_version()
|
||||
{
|
||||
// log::err << reverse_version_map.find(_http_version)->second << std::endl;
|
||||
return _http_version == HTTP_1_1 || _http_version == HTTP_1_0;
|
||||
}
|
||||
|
||||
bool request::close_connection() {
|
||||
const auto& header = _headers.find("Connection");
|
||||
const bool found = header != _headers.end();
|
||||
bool request::close_connection()
|
||||
{
|
||||
const auto& header = _headers.find("Connection");
|
||||
const bool found = header != _headers.end();
|
||||
|
||||
if(found && header->second.value() == "keep-alive") {
|
||||
if (found && header->second.value() == "keep-alive") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string request::to_string()
|
||||
{
|
||||
std::string response = "";
|
||||
response += reverse_method_map.find(_method)->second + " " + _path + "?";
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto qp : _query_params) {
|
||||
response += qp.second.to_string() + "&";
|
||||
}
|
||||
std::string request::to_string()
|
||||
{
|
||||
std::string response = "";
|
||||
response += reverse_method_map.find(_method)->second + " " + _path + "?";
|
||||
|
||||
response += " " + reverse_version_map.find(_http_version)->second + "\r\n";
|
||||
|
||||
for (auto header : _headers) {
|
||||
response += header.second.to_string();
|
||||
}
|
||||
|
||||
response += "\r\n";
|
||||
response += _body_content;
|
||||
|
||||
return response;
|
||||
for (auto qp : _query_params) {
|
||||
response += qp.second.to_string() + "&";
|
||||
}
|
||||
|
||||
response += " " + reverse_version_map.find(_http_version)->second + "\r\n";
|
||||
|
||||
for (auto header : _headers) {
|
||||
response += header.second.to_string();
|
||||
}
|
||||
|
||||
response += "\r\n";
|
||||
response += _body_content;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -3,56 +3,59 @@
|
|||
|
||||
namespace anthracite::http {
|
||||
|
||||
response::response() {};
|
||||
response::response() {};
|
||||
|
||||
int response::status_code() { return _status_code; }
|
||||
int response::status_code() { return _status_code; }
|
||||
|
||||
void response::add_body(const std::string body) {
|
||||
_content_noref = body;
|
||||
_content = &_content_noref;
|
||||
void response::add_body(const std::string body)
|
||||
{
|
||||
_content_noref = body;
|
||||
_content = &_content_noref;
|
||||
}
|
||||
|
||||
void response::add_body_ref(std::string* body)
|
||||
{
|
||||
_content = body;
|
||||
}
|
||||
|
||||
void response::add_header(header header, bool override_existing)
|
||||
{
|
||||
if (override_existing || _headers.find(header.name()) == _headers.end()) {
|
||||
_headers[header.name()] = header;
|
||||
}
|
||||
}
|
||||
|
||||
void response::add_status(int code)
|
||||
{
|
||||
_status_code = code;
|
||||
}
|
||||
|
||||
std::string& response::content()
|
||||
{
|
||||
return *_content;
|
||||
}
|
||||
|
||||
std::string response::header_to_string()
|
||||
{
|
||||
std::string response = "";
|
||||
response += "HTTP/1.1 " + std::to_string(_status_code) + " " + status_map.find(_status_code)->second + "\r\n";
|
||||
|
||||
add_header(header("Content-Length", std::to_string(_content->length())), false);
|
||||
add_header(header("Server", ANTHRACITE_FULL_VERSION_STRING), false);
|
||||
add_header(header("Origin-Server", ANTHRACITE_FULL_VERSION_STRING), false);
|
||||
|
||||
for (auto header : _headers) {
|
||||
response += header.second.to_string();
|
||||
}
|
||||
|
||||
void response::add_body_ref(std::string* body) {
|
||||
_content = body;
|
||||
}
|
||||
response += "\r\n";
|
||||
|
||||
void response::add_header(header header, bool override_existing)
|
||||
{
|
||||
if (override_existing || _headers.find(header.name()) == _headers.end()) {
|
||||
_headers[header.name()] = header;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
void response::add_status(int code) {
|
||||
_status_code = code;
|
||||
}
|
||||
|
||||
std::string& response::content()
|
||||
{
|
||||
return *_content;
|
||||
}
|
||||
|
||||
std::string response::header_to_string()
|
||||
{
|
||||
std::string response = "";
|
||||
response += "HTTP/1.1 " + std::to_string(_status_code) + " " + status_map.find(_status_code)->second + "\r\n";
|
||||
|
||||
add_header(header("Content-Length", std::to_string(_content->length())), false);
|
||||
add_header(header("Server", ANTHRACITE_FULL_VERSION_STRING), false);
|
||||
add_header(header("Origin-Server", ANTHRACITE_FULL_VERSION_STRING), false);
|
||||
|
||||
for (auto header : _headers) {
|
||||
response += header.second.to_string();
|
||||
}
|
||||
|
||||
response += "\r\n";
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
std::string response::to_string()
|
||||
{
|
||||
return header_to_string() + *_content;
|
||||
}
|
||||
std::string response::to_string()
|
||||
{
|
||||
return header_to_string() + *_content;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -1,22 +1,29 @@
|
|||
#include "./log.hpp"
|
||||
|
||||
namespace anthracite::log {
|
||||
enum LOG_LEVEL Logger::_level = LOG_LEVEL_NONE;
|
||||
enum LOG_LEVEL Logger::_level = LOG_LEVEL_NONE;
|
||||
|
||||
// TODO: implement logger as a singleton to prevent duplicates
|
||||
Logger::Logger() = default;
|
||||
void Logger::initialize(enum LOG_LEVEL level) {
|
||||
_level = level;
|
||||
// TODO: implement logger as a singleton to prevent duplicates
|
||||
Logger::Logger() = default;
|
||||
void Logger::initialize(enum LOG_LEVEL level)
|
||||
{
|
||||
_level = level;
|
||||
}
|
||||
|
||||
LogBuf::LogBuf(std::ostream& output_stream, const std::string& tag, enum LOG_LEVEL level)
|
||||
: _output_stream(output_stream)
|
||||
, _tag(tag)
|
||||
, _level(level)
|
||||
{
|
||||
}
|
||||
|
||||
int LogBuf::sync()
|
||||
{
|
||||
if (this->_level <= logger._level) {
|
||||
std::cout << "[" << this->_tag << "] " << this->str();
|
||||
std::cout.flush();
|
||||
}
|
||||
|
||||
LogBuf::LogBuf(std::ostream& output_stream, const std::string& tag, enum LOG_LEVEL level) : _output_stream(output_stream), _tag(tag), _level(level) {}
|
||||
|
||||
int LogBuf::sync() {
|
||||
if (this->_level <= logger._level) {
|
||||
std::cout << "[" << this ->_tag << "] " << this->str();
|
||||
std::cout.flush();
|
||||
}
|
||||
this->str("");
|
||||
return 0;
|
||||
}
|
||||
this->str("");
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "./socket.hpp"
|
||||
#include <arpa/inet.h>
|
||||
#include <array>
|
||||
#include <malloc.h>
|
||||
|
@ -7,71 +8,69 @@
|
|||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include "./socket.hpp"
|
||||
|
||||
namespace anthracite::socket {
|
||||
|
||||
|
||||
anthracite_socket::anthracite_socket(int port, int max_queue)
|
||||
: server_socket(::socket(AF_INET, SOCK_STREAM, 0))
|
||||
, client_ip("")
|
||||
{
|
||||
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))
|
||||
, client_ip("")
|
||||
{
|
||||
struct sockaddr_in address {};
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = htons(port);
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
int reuse_opt = 1;
|
||||
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &reuse_opt, sizeof(reuse_opt));
|
||||
bind(server_socket, reinterpret_cast<struct sockaddr*>(&address), sizeof(address));
|
||||
int reuse_opt = 1;
|
||||
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &reuse_opt, sizeof(reuse_opt));
|
||||
bind(server_socket, reinterpret_cast<struct sockaddr*>(&address), sizeof(address));
|
||||
|
||||
listen(server_socket, max_queue);
|
||||
listen(server_socket, max_queue);
|
||||
}
|
||||
|
||||
void anthracite_socket::wait_for_conn()
|
||||
{
|
||||
client_ip = "";
|
||||
client_socket = accept(server_socket, reinterpret_cast<struct sockaddr*>(&client_addr), &client_addr_len);
|
||||
std::array<char, INET_ADDRSTRLEN> ip_str { 0 };
|
||||
inet_ntop(AF_INET, &client_addr.sin_addr, ip_str.data(), INET_ADDRSTRLEN);
|
||||
client_ip = std::string(ip_str.data());
|
||||
}
|
||||
|
||||
const std::string& anthracite_socket::get_client_ip()
|
||||
{
|
||||
return client_ip;
|
||||
}
|
||||
|
||||
void anthracite_socket::close_conn()
|
||||
{
|
||||
close(client_socket);
|
||||
client_socket = -1;
|
||||
}
|
||||
|
||||
void anthracite_socket::send_message(std::string& msg)
|
||||
{
|
||||
if (client_socket == -1) {
|
||||
return;
|
||||
}
|
||||
send(client_socket, &msg[0], msg.length(), 0);
|
||||
}
|
||||
|
||||
std::string anthracite_socket::recv_message(int buffer_size)
|
||||
{
|
||||
if (client_socket == -1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
void anthracite_socket::wait_for_conn()
|
||||
{
|
||||
client_ip = "";
|
||||
client_socket = accept(server_socket, reinterpret_cast<struct sockaddr*>(&client_addr), &client_addr_len);
|
||||
std::array<char, INET_ADDRSTRLEN> ip_str { 0 };
|
||||
inet_ntop(AF_INET, &client_addr.sin_addr, ip_str.data(), INET_ADDRSTRLEN);
|
||||
client_ip = std::string(ip_str.data());
|
||||
setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout_tv, sizeof timeout_tv);
|
||||
std::vector<char> response(buffer_size + 1);
|
||||
ssize_t result = recv(client_socket, response.data(), buffer_size + 1, 0);
|
||||
|
||||
if (result < 1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const std::string& anthracite_socket::get_client_ip()
|
||||
{
|
||||
return client_ip;
|
||||
}
|
||||
|
||||
void anthracite_socket::close_conn()
|
||||
{
|
||||
close(client_socket);
|
||||
client_socket = -1;
|
||||
}
|
||||
|
||||
void anthracite_socket::send_message(std::string& msg)
|
||||
{
|
||||
if (client_socket == -1) {
|
||||
return;
|
||||
}
|
||||
send(client_socket, &msg[0], msg.length(), 0);
|
||||
}
|
||||
|
||||
std::string anthracite_socket::recv_message(int buffer_size)
|
||||
{
|
||||
if (client_socket == -1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout_tv, sizeof timeout_tv);
|
||||
std::vector<char> response(buffer_size + 1);
|
||||
ssize_t result = recv(client_socket, response.data(), buffer_size + 1, 0);
|
||||
|
||||
if (result < 1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
response[buffer_size] = '\0';
|
||||
return { response.data() };
|
||||
}
|
||||
response[buffer_size] = '\0';
|
||||
return { response.data() };
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -17,9 +17,7 @@ private:
|
|||
std::string client_ip;
|
||||
struct sockaddr_in client_addr {};
|
||||
socklen_t client_addr_len {};
|
||||
static constexpr struct timeval timeout_tv {
|
||||
.tv_sec = 5, .tv_usec = 0
|
||||
};
|
||||
static const struct timeval timeout_tv;
|
||||
|
||||
public:
|
||||
anthracite_socket(int port, int max_queue = MAX_QUEUE_LENGTH);
|
||||
|
|
|
@ -1,30 +1,33 @@
|
|||
#include "../lib/anthracite.hpp"
|
||||
#include "../lib/backends/backend.hpp"
|
||||
#include "../lib/http/constants.hpp"
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
using namespace anthracite;
|
||||
|
||||
using CallbackType = std::unique_ptr<http::response> (*)(http::request&);
|
||||
using CallbackType = std::unique_ptr<http::response> (*)(http::request&);
|
||||
class api_backend : public backends::backend {
|
||||
|
||||
class RouteNode {
|
||||
public:
|
||||
|
||||
public:
|
||||
std::optional<CallbackType> callback;
|
||||
|
||||
RouteNode() : callback(std::nullopt) {}
|
||||
RouteNode()
|
||||
: callback(std::nullopt)
|
||||
{
|
||||
}
|
||||
std::unordered_map<std::string, RouteNode> routes;
|
||||
};
|
||||
|
||||
RouteNode root;
|
||||
|
||||
std::unique_ptr<http::response> default_route(http::request& req) {
|
||||
std::unique_ptr<http::response> default_route(http::request& req)
|
||||
{
|
||||
std::unique_ptr<http::response> resp = std::make_unique<http::response>();
|
||||
|
||||
resp->add_body("Not Found");
|
||||
|
@ -34,14 +37,15 @@ class api_backend : public backends::backend {
|
|||
return resp;
|
||||
}
|
||||
|
||||
std::unique_ptr<http::response> find_handler(http::request& req) {
|
||||
std::unique_ptr<http::response> find_handler(http::request& req)
|
||||
{
|
||||
std::string filename = req.path().substr(1);
|
||||
std::vector<std::string> result;
|
||||
std::stringstream ss (filename);
|
||||
std::stringstream ss(filename);
|
||||
std::string item;
|
||||
|
||||
RouteNode* cur = &root;
|
||||
while (getline (ss, item, '/')) {
|
||||
while (getline(ss, item, '/')) {
|
||||
if (cur->routes.find(item) == cur->routes.end()) {
|
||||
if (cur->routes.find("*") == cur->routes.end()) {
|
||||
break;
|
||||
|
@ -58,28 +62,28 @@ class api_backend : public backends::backend {
|
|||
} else {
|
||||
return default_route(req);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<http::response> handle_request(http::request& req) override {
|
||||
std::unique_ptr<http::response> handle_request(http::request& req) override
|
||||
{
|
||||
return find_handler(req);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
api_backend() {
|
||||
public:
|
||||
api_backend()
|
||||
{
|
||||
root.routes = std::unordered_map<std::string, RouteNode>();
|
||||
}
|
||||
|
||||
void register_endpoint(std::string pathspec, CallbackType callback) {
|
||||
void register_endpoint(std::string pathspec, CallbackType callback)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
std::stringstream ss (pathspec);
|
||||
std::stringstream ss(pathspec);
|
||||
std::string item;
|
||||
|
||||
RouteNode* cur = &root;
|
||||
while (getline (ss, item, '/')) {
|
||||
cur->routes[item] = RouteNode{};
|
||||
while (getline(ss, item, '/')) {
|
||||
cur->routes[item] = RouteNode {};
|
||||
cur = &cur->routes[item];
|
||||
}
|
||||
|
||||
|
@ -87,17 +91,19 @@ class api_backend : public backends::backend {
|
|||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<http::response> handle_request(http::request& req) {
|
||||
std::unique_ptr<http::response> resp = std::make_unique<http::response>();
|
||||
std::unique_ptr<http::response> handle_request(http::request& req)
|
||||
{
|
||||
std::unique_ptr<http::response> resp = std::make_unique<http::response>();
|
||||
|
||||
resp->add_body(R"({"user": "endpoint"}")");
|
||||
resp->add_header(http::header("Content-Type", "application/json"));
|
||||
resp->add_status(http::status_codes::OK);
|
||||
resp->add_body(R"({"user": "endpoint"}")");
|
||||
resp->add_header(http::header("Content-Type", "application/json"));
|
||||
resp->add_status(http::status_codes::OK);
|
||||
|
||||
return resp;
|
||||
return resp;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
auto args = std::span(argv, size_t(argc));
|
||||
api_backend ab;
|
||||
ab.register_endpoint("users/*", handle_request);
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
using namespace anthracite;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
auto args = std::span(argv, size_t(argc));
|
||||
backends::file_backend fb(argc > 2 ? args[2] : "./www");
|
||||
anthracite_main(argc, argv, fb);
|
||||
|
|
Loading…
Reference in a new issue