format + README
This commit is contained in:
		
							parent
							
								
									71be773d49
								
							
						
					
					
						commit
						0ebdb34601
					
				
					 12 changed files with 370 additions and 345 deletions
				
			
		|  | @ -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::string file_extension = file_info->first.substr(file_info->first.rfind('.') + 1); | ||||
|      std::string content_type = "text/html"; | ||||
|     std::unique_ptr<http::response> resp = std::make_unique<http::response>(); | ||||
| 
 | ||||
|      auto mime_type = http::mime_types.find(file_extension); | ||||
|      if (mime_type != http::mime_types.end()) { | ||||
|          content_type = mime_type->second; | ||||
|      } | ||||
|     std::string file_extension = file_info->first.substr(file_info->first.rfind('.') + 1); | ||||
|     std::string content_type = "text/html"; | ||||
| 
 | ||||
|      resp->add_body_ref(&file_info->second); | ||||
|      resp->add_status(http::status_codes::OK); | ||||
|      resp->add_header(http::header("Content-Type", content_type), false); | ||||
|     auto mime_type = http::mime_types.find(file_extension); | ||||
|     if (mime_type != http::mime_types.end()) { | ||||
|         content_type = mime_type->second; | ||||
|     } | ||||
| 
 | ||||
|      return resp; | ||||
|   } | ||||
|     resp->add_body_ref(&file_info->second); | ||||
|     resp->add_status(http::status_codes::OK); | ||||
|     resp->add_header(http::header("Content-Type", content_type), false); | ||||
| 
 | ||||
|   void file_backend::populate_cache_dir(std::string dir) { | ||||
|     return resp; | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
|      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> 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> 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); | ||||
|     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 resp; | ||||
|   } | ||||
|     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); | ||||
| 
 | ||||
|     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_body_ref(std::string* body) { | ||||
|         _content = body;  | ||||
| } | ||||
| 
 | ||||
| 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_header(header header, bool override_existing) | ||||
|     { | ||||
|         if (override_existing || _headers.find(header.name()) == _headers.end()) { | ||||
|             _headers[header.name()] = header; | ||||
|         } | ||||
|     } | ||||
|     response += "\r\n"; | ||||
| 
 | ||||
|     void response::add_status(int  code) { | ||||
|         _status_code = code; | ||||
|     } | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
|     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…
	
	Add table
		Add a link
		
	
		Reference in a new issue