Merge pull request 'Initial test version' (#1) from bp1 into master
Reviewed-on: https://peppermintgingerbread.zip/collarbone-annihilation/iu9-ca-web-chat/pulls/1
This commit is contained in:
commit
190cdf25b3
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@ -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/
|
@ -1,6 +1,6 @@
|
||||
# Collarbone Anihilation
|
||||
# ИУ9-21Б Вэб-чат C.A
|
||||
|
||||
Веб-чат
|
||||
Сделан на летней практике 5-ю первокурсниками ИУ9
|
||||
|
||||
# Список участников
|
||||
|
||||
|
9
assets/css/test.css
Normal file
9
assets/css/test.css
Normal file
@ -0,0 +1,9 @@
|
||||
.aaa {font-size: 50px}
|
||||
|
||||
.ccc .aaa {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.ccc #bbb {
|
||||
color: green;
|
||||
}
|
16
assets/html/test.html
Normal file
16
assets/html/test.html
Normal file
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>This is a test</title>
|
||||
<link rel="stylesheet" href="/assets/css/test.css" >
|
||||
</head>
|
||||
<body>
|
||||
<h1> Test Test Test</h1>
|
||||
<p class="aaa"> Test Test asdasdsa Test</p>
|
||||
<div class="ccc">
|
||||
<p class="aaa"> Inside aaaa </p>
|
||||
<p id="bbb"> Iside bbbb </p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
10
building/build_build_system.sh
Executable file
10
building/build_build_system.sh
Executable file
@ -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
|
139
building/main.cpp
Normal file
139
building/main.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
#include <utility>
|
||||
|
||||
#include "regexis024_build_system.h"
|
||||
|
||||
std::vector<std::string> 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<std::string> 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<std::string> warning_flags = {"-Wall", "-Wno-unused-variable", "-Werror=return-type","-pedantic",
|
||||
"-Wno-unused-but-set-variable", "-Wno-reorder"};
|
||||
std::vector<std::string> version_flags = {"--std", "c++14", "-D", "_POSIX_C_SOURCE=200809L"};
|
||||
std::vector<std::string> debug_defines_release = {"_GLIBCXX_DEBUG"};
|
||||
std::vector<std::string> debug_defines_debug = {"_GLIBCXX_DEBUG", "DEBUG_ALLOW_LOUD"};
|
||||
std::vector<std::string> opt_flags_release = {"-g", "-O2"};
|
||||
std::vector<std::string> opt_flags_debug = {"-g", "-ggdb", "-O0"};
|
||||
|
||||
std::vector<std::string> getSomeRadFlags() const {
|
||||
std::vector<std::string> 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<ExternalLibraryTarget> ext_targets = {
|
||||
formExternalLibraryTargetWithNativeName("libjsonincpp"),
|
||||
formExternalLibraryTargetWithNativeName("sqlite3"),
|
||||
};
|
||||
|
||||
std::vector<CTarget> 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<std::string> 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());
|
||||
}
|
||||
}
|
41
src/http_server/engine_engine_number_9/baza.cpp
Normal file
41
src/http_server/engine_engine_number_9/baza.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "baza.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
27
src/http_server/engine_engine_number_9/baza.h
Normal file
27
src/http_server/engine_engine_number_9/baza.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef ENGINE_ENGINE_NUMBER_9_BAZA_H
|
||||
#define ENGINE_ENGINE_NUMBER_9_BAZA_H
|
||||
|
||||
#include <string>
|
||||
|
||||
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
|
16
src/http_server/engine_engine_number_9/baza_inter.h
Normal file
16
src/http_server/engine_engine_number_9/baza_inter.h
Normal file
@ -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
|
15
src/http_server/engine_engine_number_9/baza_throw.h
Normal file
15
src/http_server/engine_engine_number_9/baza_throw.h
Normal file
@ -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
|
@ -0,0 +1,86 @@
|
||||
#include "static_asset_manager.h"
|
||||
#include "../os_utils.h"
|
||||
#include "../baza_inter.h"
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
|
||||
namespace een9 {
|
||||
std::vector<std::string> detour_over_regular_folder(const std::string& path) {
|
||||
std::vector<std::string> result;
|
||||
int ret;
|
||||
|
||||
std::vector<std::string> 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<std::string> 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<StaticAssetManagerRule> new_rules) {
|
||||
RwlockWriteGuard lg(mut);
|
||||
sam.rules = std::move(new_rules);
|
||||
updateStaticAssetManager(sam);
|
||||
}
|
||||
}
|
@ -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 <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#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<StaticAssetManagerRulePostfixFilter> postfix_rules_type_assign;
|
||||
};
|
||||
|
||||
struct StaticAsset {
|
||||
std::string type;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
struct StaticAssetManager {
|
||||
std::vector<StaticAssetManagerRule> rules;
|
||||
|
||||
std::map<std::string, StaticAsset> 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<StaticAssetManagerRule> new_rules);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -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 <vector>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace een9 {
|
||||
struct ClientRequest {
|
||||
std::string method;
|
||||
std::string url;
|
||||
std::string http_version;
|
||||
std::vector<std::pair<std::string, std::string>> headers;
|
||||
bool has_body = false;
|
||||
std::string body;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -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<std::string> 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<std::string, std::string> 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) {}
|
||||
}
|
@ -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
|
@ -0,0 +1,41 @@
|
||||
#include "response_gen.h"
|
||||
#include "../baza_inter.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
namespace een9 {
|
||||
std::string form_http_server_response_header(const char* code, const std::map<std::string, std::string>& 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<std::string, std::string>& 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<std::string, std::string>& 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);
|
||||
}
|
||||
}
|
@ -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 <map>
|
||||
#include <string>
|
||||
|
||||
namespace een9 {
|
||||
std::string form_http_server_response_header(const char* code, const std::map<std::string, std::string>& headers);
|
||||
|
||||
std::string form_http_server_reponse_header_only(const char* code, const std::map<std::string, std::string>& headers);
|
||||
|
||||
std::string form_http_server_response_with_body(const char* code,
|
||||
const std::map<std::string, std::string>& 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
|
68
src/http_server/engine_engine_number_9/os_utils.cpp
Normal file
68
src/http_server/engine_engine_number_9/os_utils.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "os_utils.h"
|
||||
#include <utility>
|
||||
#include "baza_inter.h"
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
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 + "\"");
|
||||
}
|
||||
}
|
34
src/http_server/engine_engine_number_9/os_utils.h
Normal file
34
src/http_server/engine_engine_number_9/os_utils.h
Normal file
@ -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
|
257
src/http_server/engine_engine_number_9/running_mainloop.cpp
Normal file
257
src/http_server/engine_engine_number_9/running_mainloop.cpp
Normal file
@ -0,0 +1,257 @@
|
||||
#include "running_mainloop.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <poll.h>
|
||||
#include <assert.h>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#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<pthread_t> 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<UniqueFdWrapper> 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<pollfd> 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());
|
||||
}
|
||||
}
|
||||
}
|
59
src/http_server/engine_engine_number_9/running_mainloop.h
Normal file
59
src/http_server/engine_engine_number_9/running_mainloop.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef ENGINE_ENGINE_NUMBER_9_MAINLOOP_H
|
||||
#define ENGINE_ENGINE_NUMBER_9_MAINLOOP_H
|
||||
|
||||
#include "baza.h"
|
||||
#include <functional>
|
||||
#include <sys/time.h>
|
||||
#include "os_utils.h"
|
||||
#include "http_structures/client_request.h"
|
||||
#include <stdint.h>
|
||||
|
||||
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<std::string(const SlaveTask&, const ClientRequest&)> 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<uint16_t> 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
|
@ -0,0 +1,161 @@
|
||||
#include "thread_synchronization.h"
|
||||
#include "baza_inter.h"
|
||||
#include <assert.h>
|
||||
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);
|
||||
}
|
||||
}
|
109
src/http_server/engine_engine_number_9/thread_synchronization.h
Normal file
109
src/http_server/engine_engine_number_9/thread_synchronization.h
Normal file
@ -0,0 +1,109 @@
|
||||
#ifndef ENGINE_ENGINE_NUMBER_9_THREAD_SYNCHRONIZATION_H
|
||||
#define ENGINE_ENGINE_NUMBER_9_THREAD_SYNCHRONIZATION_H
|
||||
|
||||
#include "baza.h"
|
||||
#include <pthread.h>
|
||||
|
||||
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
|
56
src/web_chat/main.cpp
Normal file
56
src/web_chat/main.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include <engine_engine_number_9/baza_throw.h>
|
||||
#include <engine_engine_number_9/running_mainloop.h>
|
||||
#include <engine_engine_number_9/http_structures/response_gen.h>
|
||||
#include <signal.h>
|
||||
#include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
|
||||
#include <assert.h>
|
||||
|
||||
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 <file with settings> <assets folder>\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", "<h1> Not found! </h1>");
|
||||
};
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user