From 048365e385486a1af4bfd2efb0daa72180f43b6f Mon Sep 17 00:00:00 2001 From: Andreev Gregory Date: Sat, 27 Jul 2024 14:27:52 +0300 Subject: [PATCH] Initial test version --- .gitignore | 11 + README.md | 4 +- assets/css/test.css | 9 + assets/html/test.html | 16 ++ building/build_build_system.sh | 10 + building/main.cpp | 139 ++++++++++ .../engine_engine_number_9/baza.cpp | 41 +++ src/http_server/engine_engine_number_9/baza.h | 27 ++ .../engine_engine_number_9/baza_inter.h | 16 ++ .../engine_engine_number_9/baza_throw.h | 15 + .../static_asset_manager.cpp | 86 ++++++ .../connecting_assets/static_asset_manager.h | 50 ++++ .../http_structures/client_request.h | 19 ++ .../http_structures/client_request_parse.cpp | 101 +++++++ .../http_structures/client_request_parse.h | 41 +++ .../http_structures/response_gen.cpp | 41 +++ .../http_structures/response_gen.h | 21 ++ .../engine_engine_number_9/os_utils.cpp | 68 +++++ .../engine_engine_number_9/os_utils.h | 34 +++ .../running_mainloop.cpp | 257 ++++++++++++++++++ .../engine_engine_number_9/running_mainloop.h | 59 ++++ .../thread_synchronization.cpp | 161 +++++++++++ .../thread_synchronization.h | 109 ++++++++ src/web_chat/main.cpp | 56 ++++ 24 files changed, 1389 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 assets/css/test.css create mode 100644 assets/html/test.html create mode 100755 building/build_build_system.sh create mode 100644 building/main.cpp create mode 100644 src/http_server/engine_engine_number_9/baza.cpp create mode 100644 src/http_server/engine_engine_number_9/baza.h create mode 100644 src/http_server/engine_engine_number_9/baza_inter.h create mode 100644 src/http_server/engine_engine_number_9/baza_throw.h create mode 100644 src/http_server/engine_engine_number_9/connecting_assets/static_asset_manager.cpp create mode 100644 src/http_server/engine_engine_number_9/connecting_assets/static_asset_manager.h create mode 100644 src/http_server/engine_engine_number_9/http_structures/client_request.h create mode 100644 src/http_server/engine_engine_number_9/http_structures/client_request_parse.cpp create mode 100644 src/http_server/engine_engine_number_9/http_structures/client_request_parse.h create mode 100644 src/http_server/engine_engine_number_9/http_structures/response_gen.cpp create mode 100644 src/http_server/engine_engine_number_9/http_structures/response_gen.h create mode 100644 src/http_server/engine_engine_number_9/os_utils.cpp create mode 100644 src/http_server/engine_engine_number_9/os_utils.h create mode 100644 src/http_server/engine_engine_number_9/running_mainloop.cpp create mode 100644 src/http_server/engine_engine_number_9/running_mainloop.h create mode 100644 src/http_server/engine_engine_number_9/thread_synchronization.cpp create mode 100644 src/http_server/engine_engine_number_9/thread_synchronization.h create mode 100644 src/web_chat/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b3297a --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Never use CMAKE in production +CMakeLists.txt +cmake-build-debug/ +# Output of build system +built/ +# This is a compilated build system script +building/main +building/*.png +building/*.svg + +.idea/ diff --git a/README.md b/README.md index ae0e360..6c9997f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Collarbone Anihilation +# ИУ9-21Б Вэб-чат C.A -Веб-чат +Сделан на летней практике 5-ю первокурсниками ИУ9 # Список участников diff --git a/assets/css/test.css b/assets/css/test.css new file mode 100644 index 0000000..75b73f1 --- /dev/null +++ b/assets/css/test.css @@ -0,0 +1,9 @@ +.aaa {font-size: 50px} + +.ccc .aaa { + color: yellow; +} + +.ccc #bbb { + color: green; +} \ No newline at end of file diff --git a/assets/html/test.html b/assets/html/test.html new file mode 100644 index 0000000..a4ed2b7 --- /dev/null +++ b/assets/html/test.html @@ -0,0 +1,16 @@ + + + + + This is a test + + + +

Test Test Test

+

Test Test asdasdsa Test

+
+

Inside aaaa

+

Iside bbbb

+
+ + \ No newline at end of file diff --git a/building/build_build_system.sh b/building/build_build_system.sh new file mode 100755 index 0000000..6cc3b46 --- /dev/null +++ b/building/build_build_system.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env sh + +BUILDING_DIR="./building" +[ -d "$BUILDING_DIR" ] || exit 1 +MAIN_FILE="$BUILDING_DIR/main.cpp" +[ -f "$MAIN_FILE" ] || exit 1 + +COOL_FLAGS="$(pkg-config --cflags regexis024-build-system)" + +g++ $COOL_FLAGS -o "$BUILDING_DIR/main" "$MAIN_FILE" || exit 1 \ No newline at end of file diff --git a/building/main.cpp b/building/main.cpp new file mode 100644 index 0000000..e2f33c5 --- /dev/null +++ b/building/main.cpp @@ -0,0 +1,139 @@ +#include + +#include "regexis024_build_system.h" + +std::vector getFromPkgConfig(const std::string& req, const std::string& name){ + std::string pc_stdout, pc_stderr; + CommandReturnCode rc = executeCommand_and_save_output({"pkg-config", "--" + req, name}, pc_stdout, pc_stderr); + ASSERT(rc.isOk(), "failed to use pkg-config beacause of:\n" + pc_stderr); + // todo: learn how pkg-config actually stores these options + std::vector result; + for (char ch: pc_stdout) { + if (result.empty()) + result.emplace_back(); + if (ch == ' ') + result.emplace_back(); + else + result.back() += ch; + } + return result; +} + +ExternalLibraryTarget formExternalLibraryTargetWithNativeName(const std::string& name) { + return {name, {getFromPkgConfig("cflags", name), getFromPkgConfig("libs", name)}}; +} + +struct CAWebChat { + /* Building runlevel */ + BuildUnitsArray runlevel_1; + /* Installation runlevel */ + BuildUnitsArray runlevel_2; + + std::string build_type; + + std::vector warning_flags = {"-Wall", "-Wno-unused-variable", "-Werror=return-type","-pedantic", + "-Wno-unused-but-set-variable", "-Wno-reorder"}; + std::vector version_flags = {"--std", "c++14", "-D", "_POSIX_C_SOURCE=200809L"}; + std::vector debug_defines_release = {"_GLIBCXX_DEBUG"}; + std::vector debug_defines_debug = {"_GLIBCXX_DEBUG", "DEBUG_ALLOW_LOUD"}; + std::vector opt_flags_release = {"-g", "-O2"}; + std::vector opt_flags_debug = {"-g", "-ggdb", "-O0"}; + + std::vector getSomeRadFlags() const { + std::vector my_flag_collection; + gxx_add_cli_options(my_flag_collection, warning_flags); + gxx_add_cli_options(my_flag_collection, version_flags); + if (build_type == "release") { + gxx_add_cli_defines(my_flag_collection, debug_defines_release); + gxx_add_cli_options(my_flag_collection, opt_flags_release); + } else if (build_type == "debug") { + gxx_add_cli_defines(my_flag_collection, debug_defines_debug); + gxx_add_cli_options(my_flag_collection, opt_flags_debug); + } + return my_flag_collection; + } + + CAWebChat(std::string _build_type, const NormalCBuildSystemCommandMeaning& cmd) + : build_type(std::move(_build_type)) + { + ASSERT(build_type == "release" || build_type == "debug", "Unknown build type"); + + std::vector ext_targets = { + formExternalLibraryTargetWithNativeName("libjsonincpp"), + formExternalLibraryTargetWithNativeName("sqlite3"), + }; + + std::vector my_targets; + + { CTarget T("engine_engine_number_9", "shared_library"); + T.additional_compilation_flags = getSomeRadFlags(); + T.proj_deps = {}; + T.units = { + "baza.cpp", + "thread_synchronization.cpp", + "os_utils.cpp", + "http_structures/client_request_parse.cpp", + "http_structures/response_gen.cpp", + "connecting_assets/static_asset_manager.cpp", + "running_mainloop.cpp", + }; + for (std::string& u: T.units) + u = "http_server/engine_engine_number_9/" + u; + T.include_pr = "http_server"; + T.include_ir = ""; + T.exported_headers = { + "baza.h", + "baza_throw.h", + "thread_synchronization.h", + "os_utils.h", + "connecting_assets/static_asset_manager.h", + "http_structures/client_request.h", + "http_structures/response_gen.h", + "running_mainloop.h", + }; + for (std::string& u: T.exported_headers) + u = "engine_engine_number_9/" + u; + + T.installation_dir = ""; + my_targets.push_back(T); + } + { CTarget T("iu9-ca-web-chat", "executable"); + T.additional_compilation_flags = getSomeRadFlags(); + T.proj_deps = {CTargetDependenceOnProjectsLibrary("engine_engine_number_9")}; + T.units = {"main.cpp"}; + for (std::string& u: T.units) + u = "web_chat/" + u; + T.include_pr = "web_chat"; + T.installation_dir = ""; + my_targets.push_back(T); + } + regular_ctargets_to_2bus_conversion(ext_targets, my_targets, runlevel_1, runlevel_2, + cmd.project_root, cmd.installation_root); + } +}; + +int main(int argc, char** argv) { + try { + ASSERT_pl(argc > 0); + assert(argc > 0); + std::vector args(argc - 1); + for (int i = 0; i + 1 < argc; i++) { + args[i] = argv[i + 1]; + } + NormalCBuildSystemCommandMeaning cmd; + regular_bs_cli_cmd_interpret(args, cmd); + CAWebChat bs("debug", cmd); + // std::string map = "Runlevel 1\n"; + // draw_bu_arr_in_dot(bs.runlevel_1, map); + // map += "Runlevel 2\n"; + // draw_bu_arr_in_dot(bs.runlevel_2, map); + // printf("%s", map.c_str()); + if (cmd.need_to_build) + complete_tasks_of_build_units(bs.runlevel_1); + umask(~0755); + if (cmd.need_to_install) + complete_tasks_of_build_units(bs.runlevel_2); + } catch (const buildSystemFailure& e) { + printf("Build system failure\n""%s\n", e.toString().c_str()); + } +} \ No newline at end of file diff --git a/src/http_server/engine_engine_number_9/baza.cpp b/src/http_server/engine_engine_number_9/baza.cpp new file mode 100644 index 0000000..d41b0ed --- /dev/null +++ b/src/http_server/engine_engine_number_9/baza.cpp @@ -0,0 +1,41 @@ +#include "baza.h" + +#include +#include + +namespace een9 { + ServerError::ServerError(const std::string &err, const std::string &file, const std::string &func, int line): err(err), + FILE(file), + func(func), + LINE(line) { + char buf[4096]; + snprintf(buf, 4096, "Error occured in function %s (line %d of %s)\n" + "Error: %s", + func.c_str(), LINE, FILE.c_str(), err.c_str()); + WHAT = buf; + } + + const char * ServerError::what() const noexcept { + return WHAT.c_str(); + } + + std::string prettyprint_errno(const std::string &pref) { + const char* d = strerrorname_np(errno); + return pref.empty() ? std::string(d) : std::string(pref) + ": " + d; + } + + /* This function is VITAL */ + bool strIn(const std::string &str, const char *arr[]) { + for (const char** elPtr = arr; *elPtr != NULL; elPtr += 1) { + if (str == (*elPtr)) + return true; + } + return false; + } + + bool endsIn(const std::string &a, const std::string &b) { + if (b.size() > a.size()) + return false; + return std::equal(a.end() - (ssize_t)b.size(), a.end(), b.begin()); + } +} diff --git a/src/http_server/engine_engine_number_9/baza.h b/src/http_server/engine_engine_number_9/baza.h new file mode 100644 index 0000000..221272c --- /dev/null +++ b/src/http_server/engine_engine_number_9/baza.h @@ -0,0 +1,27 @@ +#ifndef ENGINE_ENGINE_NUMBER_9_BAZA_H +#define ENGINE_ENGINE_NUMBER_9_BAZA_H + +#include + +namespace een9 { + class ServerError : public std::exception{ + std::string err; + std::string FILE; + std::string func; + int LINE; + std::string WHAT; + + public: + ServerError(const std::string &err, const std::string &file, const std::string &func, int line); + + const char *what() const noexcept override; + }; + + std::string prettyprint_errno(const std::string& pref); + + bool strIn(const std::string& str, const char* arr[]); + + bool endsIn(const std::string& a, const std::string& b); +} + +#endif diff --git a/src/http_server/engine_engine_number_9/baza_inter.h b/src/http_server/engine_engine_number_9/baza_inter.h new file mode 100644 index 0000000..c7987ea --- /dev/null +++ b/src/http_server/engine_engine_number_9/baza_inter.h @@ -0,0 +1,16 @@ +#ifndef ENGINE_ENGINE_NUMBER_9_BAZA_INTER_H +#define ENGINE_ENGINE_NUMBER_9_BAZA_INTER_H + +/* Do not export this file */ + +#include "baza.h" + +#define THROW(err) throw ServerError(err, __FILE__, __func__, __LINE__) +#define THROW_on_errno(err) THROW(prettyprint_errno(err)) +#define THROW_on_errno_pl() THROW(prettyprint_errno("")) +#define ASSERT(cond, err) do { if (!(cond)) { THROW(err); } } while (0); +#define ASSERT_pl(cond) ASSERT(cond, "Failed assertion `" #cond "`") +#define ASSERT_on_iret(iret, err) ASSERT((iret) >= 0, prettyprint_errno(err)); +#define ASSERT_on_iret_pl(iret) ASSERT(iret >= 0, prettyprint_errno("")); + +#endif diff --git a/src/http_server/engine_engine_number_9/baza_throw.h b/src/http_server/engine_engine_number_9/baza_throw.h new file mode 100644 index 0000000..03c31ef --- /dev/null +++ b/src/http_server/engine_engine_number_9/baza_throw.h @@ -0,0 +1,15 @@ +#ifndef ENGINE_ENGINE_NUMBER_9_BAZA_THROW_H +#define ENGINE_ENGINE_NUMBER_9_BAZA_THROW_H + +#include "baza.h" + +#define een9_THROW(err) throw een9::ServerError(err, __FILE__, __func__, __LINE__) +#define een9_THROW_on_errno(err) een9_THROW(een9::prettyprint_errno(err)) +#define een9_THROW_on_errno_pl() een9_THROW(een9::prettyprint_errno("")) +#define een9_ASSERT(cond, err) do { if (!(cond)) { een9_THROW(err); } } while (0); +#define een9_ASSERT_pl(cond) een9_ASSERT(cond, "Failed assertion `" #cond "`") +#define een9_ASSERT_on_iret(iret, err) een9_ASSERT((iret) >= 0, een9::prettyprint_errno(err)); +#define een9_ASSERT_on_iret_pl(iret) een9_ASSERT(iret >= 0, een9::prettyprint_errno("")); + + +#endif diff --git a/src/http_server/engine_engine_number_9/connecting_assets/static_asset_manager.cpp b/src/http_server/engine_engine_number_9/connecting_assets/static_asset_manager.cpp new file mode 100644 index 0000000..4688b3e --- /dev/null +++ b/src/http_server/engine_engine_number_9/connecting_assets/static_asset_manager.cpp @@ -0,0 +1,86 @@ +#include "static_asset_manager.h" +#include "../os_utils.h" +#include "../baza_inter.h" +#include +#include +#include +#include +#include + +namespace een9 { + std::vector detour_over_regular_folder(const std::string& path) { + std::vector result; + int ret; + + std::vector todo; + todo.emplace_back(); + while (!todo.empty()) { + std::string cur = std::move(todo.back()); + todo.pop_back(); + std::string path_to_cur_ent = path + "/" + cur; + struct stat info; + ret = stat(path_to_cur_ent.c_str(), &info); + ASSERT_on_iret(ret, "stat(\"" + cur + "\")"); + if (S_ISDIR(info.st_mode)) { + DIR* D = opendir(path_to_cur_ent.c_str()); + cur += "/"; + ASSERT(D != NULL, prettyprint_errno("opendir(\"" + cur +"\")")); + struct Guard1{ DIR*& D; ~Guard1(){ closedir(D); } } g1{D}; + while (true) { + errno = 0; + struct dirent* Dent = readdir(D); + if (Dent == NULL) { + if (errno == 0) + break; + THROW_on_errno("dirent in \"" + cur + "\""); + } + std::string child_entry = Dent->d_name; + if (child_entry != "." && child_entry != "..") + todo.push_back(cur + child_entry); + } + } else if (S_ISREG(info.st_mode)) { + result.push_back(cur); + } else { + THROW("unknown fs entry type \"" + cur + "\""); + } + } + return result; + } + + void updateStaticAssetManager(StaticAssetManager& sam) { + sam.url_to_asset.clear(); + for (const StaticAssetManagerRule& dir_rule: sam.rules) { + std::vector c_files = detour_over_regular_folder(dir_rule.directory); + for (const std::string& file: c_files) { + for (const StaticAssetManagerRulePostfixFilter& ext: dir_rule.postfix_rules_type_assign) { + if (endsIn(file, ext.required_postfix)) { + /* Found it! */ + StaticAsset etot{ext.assigned_type, }; + readFile(dir_rule.directory + "/" + file, etot.content); + sam.url_to_asset[dir_rule.url_prefix + file] = etot; + break; + } + } + } + } + } + + int StaticAssetManagerSlaveModule::get_asset(const std::string& url, StaticAsset& ret) { + RwlockReadGuard lg(mut); + if (sam.url_to_asset.count(url) == 0) + return -1; + ret = sam.url_to_asset[url]; + return 0; + } + + void StaticAssetManagerSlaveModule::update() { + RwlockWriteGuard lg(mut); + updateStaticAssetManager(sam); + } + + void StaticAssetManagerSlaveModule::update(std::vector new_rules) { + RwlockWriteGuard lg(mut); + sam.rules = std::move(new_rules); + updateStaticAssetManager(sam); + } +} diff --git a/src/http_server/engine_engine_number_9/connecting_assets/static_asset_manager.h b/src/http_server/engine_engine_number_9/connecting_assets/static_asset_manager.h new file mode 100644 index 0000000..2776e36 --- /dev/null +++ b/src/http_server/engine_engine_number_9/connecting_assets/static_asset_manager.h @@ -0,0 +1,50 @@ +#ifndef ENGINE_ENGINE_NUMBER_9_CONNECTING_ASSETS_STATIC_ASSET_MANAGER_H +#define ENGINE_ENGINE_NUMBER_9_CONNECTING_ASSETS_STATIC_ASSET_MANAGER_H + +#include +#include +#include +#include "../thread_synchronization.h" + +namespace een9 { + struct StaticAssetManagerRulePostfixFilter { + std::string required_postfix; + std::string assigned_type; + }; + + struct StaticAssetManagerRule { + std::string directory; + std::string url_prefix; // Better end with / + /* These are rules that filter name ending. First is a required name postfix, Second is a type of document + * that gets assigned to matching files + */ + std::vector postfix_rules_type_assign; + }; + + struct StaticAsset { + std::string type; + std::string content; + }; + + struct StaticAssetManager { + std::vector rules; + + std::map url_to_asset; + }; + + void updateStaticAssetManager(StaticAssetManager& sam); + + struct StaticAssetManagerSlaveModule { + RwlockObj mut; + StaticAssetManager sam; + + /* Returns newgative on failure. Still can throw execptions derived from std::execption */ + int get_asset(const std::string& url, StaticAsset& ret); + + void update(); + + void update(std::vector new_rules); + }; +} + +#endif diff --git a/src/http_server/engine_engine_number_9/http_structures/client_request.h b/src/http_server/engine_engine_number_9/http_structures/client_request.h new file mode 100644 index 0000000..3f4a656 --- /dev/null +++ b/src/http_server/engine_engine_number_9/http_structures/client_request.h @@ -0,0 +1,19 @@ +#ifndef ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_CLIENT_REQUEST_H +#define ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_CLIENT_REQUEST_H + +#include +#include +#include + +namespace een9 { + struct ClientRequest { + std::string method; + std::string url; + std::string http_version; + std::vector> headers; + bool has_body = false; + std::string body; + }; +} + +#endif diff --git a/src/http_server/engine_engine_number_9/http_structures/client_request_parse.cpp b/src/http_server/engine_engine_number_9/http_structures/client_request_parse.cpp new file mode 100644 index 0000000..a4d266b --- /dev/null +++ b/src/http_server/engine_engine_number_9/http_structures/client_request_parse.cpp @@ -0,0 +1,101 @@ +#include "client_request_parse.h" +#include "../baza_inter.h" + +namespace een9 { + constexpr size_t max_allowed_request_content_length = 1000000000; + + static const char* supported_http_versions[] = {"HTTP/0.9", "HTTP/1.0", "HTTP/1.1", NULL}; + static const char* supported_http_methods[] = {"GET", "POST", NULL}; + + void ClientRequestParser::feedByte(char b) { + if (finished) + THROW("Excess tailing bytes"); + if (collecting_body) { + res.body += b; + if (res.body.size() >= content_lenth) { + finished = true; + } + return; + } + if (line_end[line_end_progress] == b) { + line_end_progress++; + if (line_end_progress == line_end.size()) { + line_end_progress = 0; + /* Evaluating meaning of complete request line */ + if (i == 0) { + parseFirstLine(); + } else if (cur_line.empty()) { + processEndOfHeader(); + } else { + parseHeaderLine(); + } + cur_line = ""; + i++; + } + } else { + ASSERT_pl(line_end_progress == 0); + cur_line += b; + } + } + + bool ClientRequestParser::completed() const { + return finished; + } + + void ClientRequestParser::parseFirstLine() { + std::vector huyushki = {""}; + for (char ch: cur_line) { + if (ch == ' ') { + huyushki.emplace_back(); + } else { + huyushki.back() += ch; + } + } + ASSERT_pl(huyushki.size() == 3); + res.method = huyushki[0]; + // todo: decypher and check url + res.url = huyushki[1]; + res.http_version = huyushki[2]; + ASSERT_pl(strIn(res.method, supported_http_methods)); + ASSERT_pl(strIn(res.http_version, supported_http_versions)); + } + + void ClientRequestParser::parseHeaderLine() { + std::pair np; + static const std::string sep = ": "; + size_t sep_progress = 0; + bool reading_value = false; + for (char ch: cur_line) { + if (reading_value) { + np.second += ch; + } else if (sep[sep_progress] == ch) { + sep_progress++; + if (sep_progress == sep.size()) { + reading_value = true; + } + } else { + ASSERT_pl(sep_progress == 0); + np.first += ch; + } + } + res.headers.push_back(np); + } + + void ClientRequestParser::processEndOfHeader() { + for (auto& p: res.headers) { + if (p.first == "Content-Length") { + res.has_body = true; + long cl = std::stol(p.second); + ASSERT_pl(cl > 0 && cl <= max_allowed_request_content_length); + content_lenth = cl; + collecting_body = true; + break; + } + } + if (!collecting_body) { + finished = true; + } + } + + ClientRequestParser::ClientRequestParser(ClientRequest &res) : res(res) {} +} diff --git a/src/http_server/engine_engine_number_9/http_structures/client_request_parse.h b/src/http_server/engine_engine_number_9/http_structures/client_request_parse.h new file mode 100644 index 0000000..dfbabbc --- /dev/null +++ b/src/http_server/engine_engine_number_9/http_structures/client_request_parse.h @@ -0,0 +1,41 @@ +#ifndef ENGINE_ENGINE_NUMBER_9_HTTP_CLIENT_REQUEST_PARSE_H +#define ENGINE_ENGINE_NUMBER_9_HTTP_CLIENT_REQUEST_PARSE_H + +/* Do not export this file */ + +#include "../baza.h" +#include "client_request.h" + + +// todo: parse unicode % blocks in url + +namespace een9 { + struct ClientRequestParser { + ClientRequest& res; + + explicit ClientRequestParser(ClientRequest& res); + + void feedByte(char b); + + bool completed() const; + + bool finished = false; + /* internal parse data */ + ssize_t i = 0; + std::string cur_line; + bool collecting_body = false; + size_t content_lenth = 0; + + const std::string line_end = "\r\n"; + size_t line_end_progress = 0; + + /* argument stored in cur_line */ + void parseFirstLine(); + + void parseHeaderLine(); + + void processEndOfHeader(); + }; +} + +#endif diff --git a/src/http_server/engine_engine_number_9/http_structures/response_gen.cpp b/src/http_server/engine_engine_number_9/http_structures/response_gen.cpp new file mode 100644 index 0000000..ca3547d --- /dev/null +++ b/src/http_server/engine_engine_number_9/http_structures/response_gen.cpp @@ -0,0 +1,41 @@ +#include "response_gen.h" +#include "../baza_inter.h" +#include +#include + + +namespace een9 { + std::string form_http_server_response_header(const char* code, const std::map& headers) { + assert(strlen(code) == 3); + std::string result = std::string("HTTP/1.0 ") + code + " " + (code[0] < '4' ? "OK" : "ERROR") + "\r\n"; + for (auto& p: headers) + result += (p.first + ": " + p.second + "\r\n"); + return result; + } + + std::string form_http_server_reponse_header_only(const char* code, const std::map& headers) { + return form_http_server_response_header(code, headers) + "\r\n"; + } + + std::string form_http_server_response_with_body(const char* code, + const std::map& headers, + const std::string& body) + { + std::string result = form_http_server_response_header(code, headers) + + "Content-Length: " + std::to_string(body.size()) + "\r\n\r\n" + body; + return result; + } + + /* Message from server to client */ + std::string form_http_server_response_200(const std::string& Content_Type, const std::string& body) { + return form_http_server_response_with_body("200", { + {"Content-Type", Content_Type} + }, body); + } + + std::string form_http_server_response_404(const std::string& Content_Type, const std::string& body) { + return form_http_server_response_with_body("404", { + {"Content-Type", Content_Type} + }, body); + } +} diff --git a/src/http_server/engine_engine_number_9/http_structures/response_gen.h b/src/http_server/engine_engine_number_9/http_structures/response_gen.h new file mode 100644 index 0000000..898be0b --- /dev/null +++ b/src/http_server/engine_engine_number_9/http_structures/response_gen.h @@ -0,0 +1,21 @@ +#ifndef ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_RESPONSE_GEN_H +#define ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_RESPONSE_GEN_H + +#include +#include + +namespace een9 { + std::string form_http_server_response_header(const char* code, const std::map& headers); + + std::string form_http_server_reponse_header_only(const char* code, const std::map& headers); + + std::string form_http_server_response_with_body(const char* code, + const std::map& headers, + const std::string& body); + + std::string form_http_server_response_200(const std::string& Content_Type, const std::string& body); + + std::string form_http_server_response_404(const std::string& Content_Type, const std::string& body); +} + +#endif diff --git a/src/http_server/engine_engine_number_9/os_utils.cpp b/src/http_server/engine_engine_number_9/os_utils.cpp new file mode 100644 index 0000000..9e38cc5 --- /dev/null +++ b/src/http_server/engine_engine_number_9/os_utils.cpp @@ -0,0 +1,68 @@ +#include "os_utils.h" +#include +#include "baza_inter.h" +#include +#include +#include +#include + +namespace een9 { + UniqueFdWrapper::UniqueFdWrapper(int fd_): fd(fd_) {} + + UniqueFdWrapper::UniqueFdWrapper(UniqueFdWrapper &&formerOwner) noexcept { + fd = formerOwner.fd; + formerOwner.fd = -1; + } + + UniqueFdWrapper& UniqueFdWrapper::operator=(UniqueFdWrapper &&formerOwner) noexcept { + std::swap(fd, formerOwner.fd); + return *this; + } + + int UniqueFdWrapper::operator()() const { + return fd; + } + + UniqueFdWrapper::~UniqueFdWrapper() { + // printf("DEBUG!!! Closing fd = %d\n", fd); + if (fd >= 0) + close(fd); + } + + bool isNeededFsEntity(const std::string &path, mode_t t_mode) { + struct stat info; + errno = 0; + int ret = stat(path.c_str(), &info); + if (errno == 0) { + return (info.st_mode & S_IFMT) == t_mode; + } if (errno == ENOENT) + return false; + THROW_on_errno("stat\"" + path + "\""); + } + + bool isRegularFile(const std::string &path) { + return isNeededFsEntity(path, S_IFREG); + } + + bool isDirectory(const std::string &path) { + return isNeededFsEntity(path, S_IFDIR); + } + + void readFromFileDescriptor(int fd, std::string &result, const std::string &description) { + int ret; + char buf[2048]; + while ((ret = (int)read(fd, buf, 2048)) > 0) { + size_t oldN = result.size(); + result.resize(oldN + ret); + memcpy(&result[oldN], buf, ret); + } + ASSERT_on_iret(ret, "Reading from " + description); + } + + void readFile(const std::string &path, std::string &result) { + int fd = open(path.c_str(), O_RDONLY); + ASSERT_on_iret(fd, "Opening \"" + path + "\""); + UniqueFdWrapper fdw(fd); + readFromFileDescriptor(fdw(), result, "file \"" + path + "\""); + } +} \ No newline at end of file diff --git a/src/http_server/engine_engine_number_9/os_utils.h b/src/http_server/engine_engine_number_9/os_utils.h new file mode 100644 index 0000000..87f0bdf --- /dev/null +++ b/src/http_server/engine_engine_number_9/os_utils.h @@ -0,0 +1,34 @@ +#ifndef ENGINE_ENGINE_NUMBER_9_OS_UTILS_H +#define ENGINE_ENGINE_NUMBER_9_OS_UTILS_H + +#include "baza.h" + +namespace een9 { + class UniqueFdWrapper { + int fd = -1; + public: + explicit UniqueFdWrapper() = default; + explicit UniqueFdWrapper(int fd_); + + UniqueFdWrapper(const UniqueFdWrapper&) = delete; + UniqueFdWrapper& operator=(const UniqueFdWrapper&) = delete; + + UniqueFdWrapper(UniqueFdWrapper&& formerOwner) noexcept; + UniqueFdWrapper& operator=(UniqueFdWrapper&& formerOwner) noexcept; + + int operator()() const; + + ~UniqueFdWrapper(); + }; + + bool isRegularFile(const std::string& path); + + bool isDirectory(const std::string& path); + + /* result += read(fd); Argument description is for error handling */ + void readFromFileDescriptor(int fd, std::string& result, const std::string& description = ""); + + void readFile(const std::string& path, std::string& result); +} + +#endif diff --git a/src/http_server/engine_engine_number_9/running_mainloop.cpp b/src/http_server/engine_engine_number_9/running_mainloop.cpp new file mode 100644 index 0000000..1fca093 --- /dev/null +++ b/src/http_server/engine_engine_number_9/running_mainloop.cpp @@ -0,0 +1,257 @@ +#include "running_mainloop.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "thread_synchronization.h" +#include "os_utils.h" +#include "http_structures/client_request_parse.h" +#include "http_structures/response_gen.h" +#include "baza_inter.h" + +namespace een9 { + // todo: add timeout for multiple bytes, add more settings + ClientRequest process_connection_input(int fd, const EEN9_ServerTips& s_tips) { + ClientRequest res; + ClientRequestParser parser(res); + int ret; + char buf[2048]; + ASSERT_pl(!parser.completed()) + while ((ret = (int)recv(fd, buf, 2048, 0)) > 0) { + for (size_t i = 0; i < ret; i++) + parser.feedByte(buf[i]); + if (parser.completed()) + break; + } + ASSERT_on_iret(ret, "recv"); + ASSERT_pl(parser.completed()); + // printf("Log: worker received clients request\n%s\n", client_request.toString().c_str()); + return res; + } + + void process_connection_output(int fd, const std::string& server_response) { + size_t N = server_response.size(), i = 0; + while (i < N) { + int written = (int)send(fd, &server_response[i], std::min(2048lu, N - i), 0); + ASSERT_on_iret(written, "sending"); + ASSERT_pl(written > 0); + i += written; + } + printf("Log: worker: succesfully asnwered with response\n"); + } + + void process_connection(const SlaveTask& task, + const guest_core_t& guest_core) + { + ClientRequest client_request = process_connection_input(task.fd(), task.s_tips); + std::string server_response = guest_core(task, client_request); + process_connection_output(task.fd(), server_response); + } + + struct QElementHttpConnections { + SlaveTask task; + QElementHttpConnections* nxt = NULL; + + explicit QElementHttpConnections(SlaveTask task): task(std::move(task)) {} + }; + + struct WorkersTaskQueue { + QElementHttpConnections* first = NULL; + QElementHttpConnections** afterLastPtr; + size_t sz = 0; + + WorkersTaskQueue() { + afterLastPtr = &first; + } + + bool empty() const { + return sz == 0; + } + + size_t size() const { + return sz; + } + + void push_back(SlaveTask task) { + /* Throws a goddamn execption. Because why not. Ofcourse everything has to throw an exception */ + /* CLion says. Allocated memory is leaking. YOUR MOTHER IS LEAKING YOU FOOL!! MY CODE IS FINE!! */ + QElementHttpConnections* el = new QElementHttpConnections(std::move(task)); + /* Exception does not leave queue in incorrect state */ + *afterLastPtr = el; + afterLastPtr = &(el->nxt); + sz++; + } + + void pop_first(SlaveTask& ret_task) { + assert(!empty()); + ret_task = std::move(first->task); + if (sz == 1) { + delete first; + first = NULL; + afterLastPtr = &first; + sz = 0; + } else { + /* Before I popped the first, this element was second, but now it took place of the first */ + QElementHttpConnections* old_deut = first->nxt; + delete first; + first = old_deut; + sz--; + } + } + }; + + struct WorkersTaskEnv { + /* This alarm notifies about new tasks and termination signal. Because we are polite people, we don't cancel threads */ + CondVarBedObj corvee_bed; + WorkersTaskQueue queue; + bool& termination; + guest_core_t guest_core; + + WorkersTaskEnv(bool& term, guest_core_t g_c): termination(term), guest_core(std::move(g_c)){} + }; + + void* worker_func(void* wte_ptr) { + WorkersTaskEnv& wte = *((WorkersTaskEnv*)wte_ptr); + printf("Worker started\n"); + while (true) { + try { + MutexLockGuard cb_lg(wte.corvee_bed, __func__); + woke: + if (wte.termination) + break; + if (wte.queue.empty()) { + wte.corvee_bed.sleep(__func__); + goto woke; + } + SlaveTask task; + wte.queue.pop_first(task); + process_connection(task, wte.guest_core); + } catch (const std::exception& e) { + printf("Client request procession failure in worker\n"); + printf("%s\n", e.what()); + } + } + printf("Worker finished\n"); + return NULL; + } + + void configure_socket_rcvsndtimeo(int fd, timeval tv) { + int ret; + ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(timeval)); + ASSERT_on_iret_pl(ret); + ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(timeval)); + ASSERT_on_iret_pl(ret); + } + + // todo: retrieve address of connected client + + void electric_boogaloo(const MainloopParameters& params, bool& termination_trigger) { + WorkersTaskEnv wte(termination_trigger, params.guest_core); + ASSERT(params.slave_number > 0, "No workers spawned"); + size_t Nip = params.ports_to_listen.size(); + ASSERT(Nip > 0, "No open listeting addresses"); + + std::vector workers(params.slave_number); + for (size_t i = 0; i < params.slave_number; i++) { + pthread_create(&workers[i], NULL, worker_func, &wte); + } + + try { + int ret; + std::vector listening_socks(Nip); + for (size_t i = 0; i < Nip; i++) { + printf("Creating listening socket\n"); + uint16_t port = params.ports_to_listen[i]; + int listening_socket_fd = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_on_iret(listening_socket_fd, "Listening socket creation"); + UniqueFdWrapper listening_socket(listening_socket_fd); + printf("Listening socket created\n"); + sockaddr_in listening_address; + listening_address.sin_family = AF_INET; + listening_address.sin_port = htons(port); + uint32_t lca = (127u << 24) | 1; + listening_address.sin_addr.s_addr = htonl(lca); + int reuseaddr_nozero_option_value = 1; + ret = setsockopt(listening_socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_nozero_option_value, sizeof(int)); + ASSERT_on_iret(ret, "setting SO_REUSEADDR befire binding to address"); + ret = bind(listening_socket(), (const sockaddr*)&listening_address, sizeof(listening_address)); + ASSERT_on_iret(ret, "binding to INADDR_ANY:" + std::to_string(port)); + printf("Binded socket to address\n"); + ret = listen(listening_socket(), 128); + ASSERT_on_iret(ret, "listening for connections"); + printf("Listening socket succesfully started listening\n"); + listening_socks[i] = std::move(listening_socket); + } + std::vector pollfds(Nip); + for (size_t i = 0; i < Nip; i++) { + pollfds[i].fd = listening_socks[i](); + pollfds[i].events = POLLRDNORM; + } + printf("Entering mainloop\n"); + ASSERT(params.mainloop_recheck_interval_us > 0, "Incorrect poll timeout"); + while (true) { + MutexLockGuard lg1(wte.corvee_bed, "poller termination check"); + if (wte.termination) + break; + lg1.unlock(); + for (size_t i = 0; i < Nip; i++) { + pollfds[i].revents = 0; + } + errno = 0; + ret = poll(pollfds.data(), Nip, params.mainloop_recheck_interval_us); + if (errno == EINTR) + break; + ASSERT_on_iret(ret, "polling"); + for (size_t i = 0; i < Nip; i++) { + if ((pollfds[i].revents & POLLRDNORM)) { + try { + sockaddr client_address; + socklen_t client_addr_len = sizeof(client_address); + int session_sock = accept(pollfds[i].fd, &client_address, &client_addr_len); + ASSERT_on_iret(session_sock, "Failed to accept incoming connection"); + printf("Log: successful connection\n"); + UniqueFdWrapper session_sock_fdw(session_sock); + configure_socket_rcvsndtimeo(session_sock_fdw(), params.s_conf.request_timeout); + { MutexLockGuard lg2(wte.corvee_bed, "poller adds connection"); + SlaveTask task{ConnectionInfo{}, std::move(session_sock_fdw), + EEN9_ServerTips{wte.queue.size(), + params.s_conf.critical_load_1, params.s_conf.request_timeout}}; + if (wte.queue.size() < params.s_conf.critical_load_2) + wte.queue.push_back(std::move(task)); + } + wte.corvee_bed.din_don(); + } catch (const std::exception& e) { + printf("Error aceepting connection\n"); + printf("%s\n", e.what()); + } + } + } + } + } catch (const std::exception& e) { + printf("System failure 2\n"); + printf("%s\n", e.what()); + /* There is no need to tiptoe around this multi-access field. It is write-onle-and-for-good-kind */ + wte.termination = true; + wte.corvee_bed.wake_them_all(); + } + wte.termination = true; + wte.corvee_bed.wake_them_all(); + for (size_t i = 0; i < params.slave_number; i++) { + pthread_join(workers[i], NULL); + } + } + + void safe_electric_boogaloo(const MainloopParameters& params, bool& termination_trigger) { + try { + electric_boogaloo(params, termination_trigger); + } catch (const std::exception& e) { + printf("System failure\n"); + printf("%s\n", e.what()); + } + } +} \ No newline at end of file diff --git a/src/http_server/engine_engine_number_9/running_mainloop.h b/src/http_server/engine_engine_number_9/running_mainloop.h new file mode 100644 index 0000000..42850ee --- /dev/null +++ b/src/http_server/engine_engine_number_9/running_mainloop.h @@ -0,0 +1,59 @@ +#ifndef ENGINE_ENGINE_NUMBER_9_MAINLOOP_H +#define ENGINE_ENGINE_NUMBER_9_MAINLOOP_H + +#include "baza.h" +#include +#include +#include "os_utils.h" +#include "http_structures/client_request.h" +#include + +namespace een9 { + struct ConnectionInfo { + // todo: add server address field + // todo: add client address field + int type; // O_o why?? + }; + + /* This structure is passed to guest function. It contains server info that might be or might be not used + * by guest */ + struct EEN9_ServerTips { + size_t server_load; + size_t critical_load_1; + timeval recommended_timeout; + }; + + struct SlaveTask { + ConnectionInfo conn_info; + UniqueFdWrapper fd; + EEN9_ServerTips s_tips; + }; + + /* guest_core function must not throw anything that is not derived from std::exception */ + typedef std::function guest_core_t; + + struct ServersConfiguration { + size_t critical_load_1 = 90; + size_t critical_load_2 = 100; + + timeval request_timeout{20, 0}; + }; + + struct MainloopParameters { + bool do_logging = true; + bool open_admin_listener = true; + // todo: add support for unix socket address + uint16_t admin_listener_port = 12345; + guest_core_t guest_core; + size_t slave_number = 2; + std::vector ports_to_listen; + int mainloop_recheck_interval_us = 100; + + ServersConfiguration s_conf; + }; + + void electric_boogaloo(const MainloopParameters& params, bool& termination_trigger); + void safe_electric_boogaloo(const MainloopParameters& params, bool& termination_trigger); +} + +#endif diff --git a/src/http_server/engine_engine_number_9/thread_synchronization.cpp b/src/http_server/engine_engine_number_9/thread_synchronization.cpp new file mode 100644 index 0000000..1e44975 --- /dev/null +++ b/src/http_server/engine_engine_number_9/thread_synchronization.cpp @@ -0,0 +1,161 @@ +#include "thread_synchronization.h" +#include "baza_inter.h" +#include +namespace een9 { + /*==================================================== RwlockObj ============ */ + + void RwlockObj::unlock(int el, const char *who) { + assert(lock == el); + lock = 0; + if (me) + printf("Rwl %s unlocked by %s\n", me, who); + int ret = pthread_rwlock_unlock(&mut); + assert(ret == 0); + } + + RwlockObj::RwlockObj(const char *me): me(me) { + int ret = pthread_rwlock_init(&mut, NULL); + ASSERT_on_iret(ret, "pthread rwlock init"); + } + + void RwlockObj::read_lock(const char *who) { + int ret = pthread_rwlock_rdlock(&mut); + assert(ret == 0); + if (me) + printf("Rwl %s read-locked by %s\n", me, who); + lock = 1; + } + + void RwlockObj::write_lock(const char *who) { + int ret = pthread_rwlock_wrlock(&mut); + assert(ret == 0); + if (me) + printf("Rwl %s write-locked by %s\n", me, who); + lock = 2; + } + + void RwlockObj::read_unclock(const char *who) { + unlock(1, who); + } + + void RwlockObj::write_unclock(const char *who) { + unlock(2, who); + } + + RwlockObj::~RwlockObj() { + pthread_rwlock_destroy(&mut); + } + + /*==================================================== MutexObj ============ */ + + MutextObj::MutextObj(const char *me): me(me) { + int ret = pthread_mutex_init(&mut, NULL); + ASSERT_on_iret(ret == 0, "pthread mutex init"); + } + + void MutextObj::lock(const char *who) { + int ret = pthread_mutex_lock(&mut); + assert(ret == 0); + if (me) + printf("Mut %s locked by %s\n", me, who); + locked = 1; + } + + void MutextObj::unlock(const char *who) { + assert(locked == 1); + locked = 0; + if (me) + printf("Mut %s unlocked by %s\n", me, who); + int ret = pthread_mutex_unlock(&mut); + assert(ret == 0); + } + + MutextObj::~MutextObj() { + pthread_mutex_destroy(&mut); + } + + /*==================================================== CondVarBedObj ============ */ + + CondVarBedObj::CondVarBedObj(const char *me): MutextObj(me) { + int ret = pthread_cond_init(&alarm, NULL); + ASSERT_on_iret(ret, "pthread cond variable init"); + } + + void CondVarBedObj::sleep(const char *who) { + assert(locked == 1); + locked = 0; + if (me) + printf("Mut %s unlocked. Sleeping (%s)\n", me, who); + int ret = pthread_cond_wait(&alarm, &mut); + assert(ret == 0); + if (me) + printf("Mut %s locked. Woke up (%s)\n", me, who); + locked = 1; + } + + void CondVarBedObj::din_don() { + int ret = pthread_cond_signal(&alarm); + assert(ret == 0); + } + + void CondVarBedObj::wake_them_all() { + int ret = pthread_cond_broadcast(&alarm); + assert(ret == 0); + } + + CondVarBedObj::~CondVarBedObj() { + pthread_mutex_destroy(&mut); + pthread_cond_destroy(&alarm); + } + + /*==================================================== MutexLockGuard ============ */ + + MutexLockGuard::MutexLockGuard(MutextObj &mut, const char *who): mut(mut), who(who) { + mut.lock(who); + } + + void MutexLockGuard::unlock() { + assert(!premature_unlock); + premature_unlock = true; + mut.unlock(who); + } + + MutexLockGuard::~MutexLockGuard() { + if (!premature_unlock) + mut.unlock(who); + } + + /*==================================================== RwLockReadLockGuard ============ */ + + RwlockReadGuard::RwlockReadGuard(RwlockObj &mut, const char *who): mut(mut), who(who) { + mut.read_lock(who); + } + + void RwlockReadGuard::unlock() { + assert(!premature_unlock); + premature_unlock = true; + mut.read_unclock(who); + } + + RwlockReadGuard::~RwlockReadGuard() { + if (!premature_unlock) + mut.read_unclock(who); + } + + /*==================================================== RwLockWriteLockGuard ============ */ + + RwlockWriteGuard::RwlockWriteGuard(RwlockObj &mut, const char *who): mut(mut), who(who) { + mut.write_lock(who); + } + + void RwlockWriteGuard::unlock() { + assert(!premature_unlock); + premature_unlock = true; + mut.write_unclock(who); + } + + RwlockWriteGuard::~RwlockWriteGuard() { + if (!premature_unlock) + mut.write_unclock(who); + } +} \ No newline at end of file diff --git a/src/http_server/engine_engine_number_9/thread_synchronization.h b/src/http_server/engine_engine_number_9/thread_synchronization.h new file mode 100644 index 0000000..c88ba0e --- /dev/null +++ b/src/http_server/engine_engine_number_9/thread_synchronization.h @@ -0,0 +1,109 @@ +#ifndef ENGINE_ENGINE_NUMBER_9_THREAD_SYNCHRONIZATION_H +#define ENGINE_ENGINE_NUMBER_9_THREAD_SYNCHRONIZATION_H + +#include "baza.h" +#include + +namespace een9 { + class RwlockObj { + protected: + pthread_rwlock_t mut; + int lock = 0; + const char* me; + + void unlock(int el, const char* who); + + public: + RwlockObj(const RwlockObj& ) = delete; + RwlockObj& operator=(const RwlockObj& ) = delete; + + explicit RwlockObj(const char* me = NULL); + + void read_lock(const char* who = ""); + + void write_lock(const char* who = ""); + + void read_unclock(const char* who = ""); + + void write_unclock(const char* who = ""); + + ~RwlockObj(); + }; + + class MutextObj { + protected: + pthread_mutex_t mut; + int locked = 0; + const char* me; + + public: + MutextObj(const MutextObj&) = delete; + MutextObj& operator= (const MutextObj&) = delete; + + explicit MutextObj(const char* me = NULL); + + void lock(const char* who = ""); + + void unlock(const char* who = ""); + + ~MutextObj(); + }; + + class CondVarBedObj : public MutextObj{ + pthread_cond_t alarm; + /* In this case, variable `locked` check won't ensure proper mutex usage, but it can help sometimes */ + + public: + CondVarBedObj(const CondVarBedObj& ) = delete; + CondVarBedObj& operator=(const CondVarBedObj& ) = delete; + + explicit CondVarBedObj(const char* me = NULL); + + void sleep(const char* who = ""); + + void din_don(); + + void wake_them_all(); + + ~CondVarBedObj(); + }; + + class MutexLockGuard { + MutextObj& mut; + const char* who; + bool premature_unlock = false; + public: + explicit MutexLockGuard(MutextObj &mut, const char* who = ""); + + void unlock(); + + ~MutexLockGuard(); + }; + + class RwlockReadGuard { + RwlockObj& mut; + const char* who; + bool premature_unlock = false; + public: + explicit RwlockReadGuard(RwlockObj &mut, const char *who = ""); + + void unlock(); + + ~RwlockReadGuard(); + }; + + class RwlockWriteGuard { + RwlockObj& mut; + const char* who; + bool premature_unlock = false; + public: + explicit RwlockWriteGuard(RwlockObj &mut, const char *who = ""); + + void unlock(); + + ~RwlockWriteGuard(); + }; + +} + +#endif diff --git a/src/web_chat/main.cpp b/src/web_chat/main.cpp new file mode 100644 index 0000000..ff4869b --- /dev/null +++ b/src/web_chat/main.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include + +bool termination = false; + +void sigterm_action(int ) { + termination = true; +} + +int main(int argc, char** argv){ + een9_ASSERT_pl(argc > 0); + if (argc < 1 + 2) { + printf("Usage: %s \n", argv[0]); + exit(1); + } + // if () + if (!een9::isDirectory(argv[2])) { + printf("\"%s\" is not a directory\n", argv[2]); + } + std::string assets_dir = argv[2]; + + een9::StaticAssetManagerSlaveModule samI; + samI.update({ + een9::StaticAssetManagerRule{assets_dir + "/html", "/assets/html", {{".html", "text/html"}} }, + een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} }, + een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/js"}} }, + }); + + een9::MainloopParameters params; + params.guest_core = [&samI](const een9::SlaveTask& task, const een9::ClientRequest& req) -> std::string { + een9::StaticAsset sa; + int ret; + ret = samI.get_asset(req.url, sa); + if (ret >= 0) { + return een9::form_http_server_response_200(sa.type, sa.content); + } + if (req.url == "/" || req.url == "/index.html") { + ret = samI.get_asset("/assets/html/test.html", sa); + een9_ASSERT_pl(ret == 0); + return een9::form_http_server_response_200(sa.type, sa.content); + } + return een9::form_http_server_response_404("text/html", "

Not found!

"); + }; + params.ports_to_listen = {1025}; + params.slave_number = 8; + params.open_admin_listener = false; + + signal(SIGINT, sigterm_action); + + een9::safe_electric_boogaloo(params, termination); + return 0; +} \ No newline at end of file -- 2.45.2