diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3e65d7d --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +RM = rm -rf +MKDIR = mkdir -p + +#CXX = g++ +CXXFLAGS += -std=c++14 -c -Wall -O2 +LDFLAGS = -ldl -lpthread -lrt -lgnutls + +DEFS = POSIX +DEFINES = $(patsubst %, -D%, $(DEFS) ) + +SOURCEDIR = ./src +BUILDDIR = ./build +OBJDIR = $(BUILDDIR)/obj + +SOURCES = $(shell find $(SOURCEDIR) -type f -name '*.cpp') +OBJECTS = $(patsubst $(SOURCEDIR)/%.cpp, $(OBJDIR)/%.o, $(SOURCES) ) + +EXECUTABLE = $(BUILDDIR)/httpserver + +.PHONY: all clean +all: $(BUILDDIR) $(EXECUTABLE) + +$(BUILDDIR): + $(MKDIR) $@ + +$(EXECUTABLE): $(OBJECTS) + $(CXX) $(OBJECTS) -o $@ $(LDFLAGS) + +$(OBJECTS) : $(OBJDIR)/%.o : $(SOURCEDIR)/%.cpp + @$(MKDIR) $(dir $@) + $(CXX) $(DEFINES) $(CXXFLAGS) $< -o $@ + +clean: + $(RM) $(OBJDIR) $(EXECUTABLE) diff --git a/README.md b/README.md index 0e1b53b..0546a29 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -httpserver -========== +# httpserver Http server is written on C++14 language. @@ -11,20 +10,19 @@ by using the parameter `server_module`. Sample application code: https://github.com/awwit/httpserverapp -Features --------- +## Features This http server support: * HTTP v1.1 * HTTPS (TLS) +* HTTP v2 (*need optimize cleanup of streams*) * Keep-Alive * WebSocket -* Get-Parted requests * X-Sendfile (header) +* Get-Parted requests -Dependencies ------------- +## Dependencies Common: @@ -34,11 +32,18 @@ Linux: `dl`, `pthread`, `rt`, `gnutls` Windows: `ws2_32.lib`, `libgnutls.dll.a` -Build ------ +## Build Linux: +```sh +git clone https://github.com/awwit/httpserver.git +cd httpserver +make +``` + +or + ```sh git clone https://github.com/awwit/httpserver.git cd httpserver @@ -57,8 +62,7 @@ cd build devenv ./../projects/msvs/httpserver.sln /build ``` -Server start ------------- +## Server start ```sh ./httpserver --start @@ -70,14 +74,12 @@ Or input a parameter `--config-path=` to set the directory with configurat Use the parameter `--server-name=` to define the name of web-server's instance. Instances can be used to run web-servers with different settings. -Server configuration --------------------- +## Server configuration Server (and its applications) setting is made using config-files. Examples of settings are located in the folder [samples](samples/). -License -======= +# License The source codes are licensed under the [AGPL](http://www.gnu.org/licenses/agpl.html), diff --git a/projects/msvs/httpserver.vcxproj b/projects/msvs/httpserver.vcxproj index 4808b5f..ce17aba 100644 --- a/projects/msvs/httpserver.vcxproj +++ b/projects/msvs/httpserver.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -19,57 +19,82 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + {A7023B84-DA18-3E3E-A9AE-B87F45CAC37F} @@ -84,7 +109,7 @@ MultiByte Application release\ - v140 + v141 true @@ -93,7 +118,7 @@ MultiByte Application release\ - v140 + v141 true @@ -102,7 +127,7 @@ MultiByte Application debug\ - v140 + v141 debug\ @@ -110,7 +135,7 @@ MultiByte Application debug\ - v140 + v141 diff --git a/projects/msvs/httpserver.vcxproj.filters b/projects/msvs/httpserver.vcxproj.filters index e539bf5..2968c4b 100644 --- a/projects/msvs/httpserver.vcxproj.filters +++ b/projects/msvs/httpserver.vcxproj.filters @@ -19,153 +19,228 @@ - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + + Source Files + + + Source Files + + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + Source Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files diff --git a/projects/qt-creator/httpserver.qbs b/projects/qt-creator/httpserver.qbs index 67eab75..20d1f24 100644 --- a/projects/qt-creator/httpserver.qbs +++ b/projects/qt-creator/httpserver.qbs @@ -1,76 +1,100 @@ import qbs Project { - CppApplication { - name: "httpserver" + CppApplication { + name: "httpserver" - cpp.cxxLanguageVersion: "c++14" + cpp.cxxLanguageVersion: "c++14" - cpp.defines: qbs.buildVariant == "debug" ? base : base.concat(["DEBUG"]) + cpp.defines: qbs.buildVariant == "debug" ? ["DEBUG"] : original - cpp.dynamicLibraries: base.concat(["gnutls"]) + cpp.dynamicLibraries: ["gnutls"] - Properties { - condition: qbs.targetOS.contains("linux") - cpp.defines: outer.concat(["POSIX"]) - cpp.dynamicLibraries: outer.concat(["dl", "pthread", "rt"]) - } + Properties { + condition: qbs.targetOS.contains("linux") + cpp.defines: outer.concat(["POSIX"]) + cpp.dynamicLibraries: outer.concat(["dl", "pthread", "rt"]) + } + Properties { + condition: qbs.targetOS.contains("windows") + cpp.defines: outer.concat(["WIN32", "NOMINMAX"]) + } - Properties { - condition: qbs.targetOS.contains("windows") - cpp.defines: outer.concat(["WIN32", "NOMINMAX"]) - } - - files: [ - "../../src/ConfigParser.cpp", - "../../src/ConfigParser.h", - "../../src/DataVariantAbstract.h", - "../../src/DataVariantFormUrlencoded.cpp", - "../../src/DataVariantFormUrlencoded.h", - "../../src/DataVariantMultipartFormData.cpp", - "../../src/DataVariantMultipartFormData.h", - "../../src/DataVariantTextPlain.cpp", - "../../src/DataVariantTextPlain.h", - "../../src/Event.cpp", - "../../src/Event.h", - "../../src/FileIncoming.cpp", - "../../src/FileIncoming.h", - "../../src/GlobalMutex.cpp", - "../../src/GlobalMutex.h", - "../../src/Main.cpp", - "../../src/Main.h", - "../../src/Module.cpp", - "../../src/Module.h", - "../../src/RawData.h", - "../../src/RequestParameters.cpp", - "../../src/RequestParameters.h", - "../../src/Server.cpp", - "../../src/Server.h", - "../../src/ServerApplicationDefaultSettings.h", - "../../src/ServerApplicationSettings.h", - "../../src/ServerApplicationsTree.cpp", - "../../src/ServerApplicationsTree.h", - "../../src/ServerRequest.h", - "../../src/ServerResponse.h", - "../../src/ServerStructuresArguments.h", - "../../src/SharedMemory.cpp", - "../../src/SharedMemory.h", - "../../src/SignalHandlers.cpp", - "../../src/SignalHandlers.h", - "../../src/Socket.cpp", - "../../src/Socket.h", - "../../src/SocketAdapter.cpp", - "../../src/SocketAdapter.h", - "../../src/SocketAdapterDefault.cpp", - "../../src/SocketAdapterDefault.h", - "../../src/SocketAdapterTls.cpp", - "../../src/SocketAdapterTls.h", - "../../src/SocketList.cpp", - "../../src/SocketList.h", - "../../src/System.cpp", - "../../src/System.h", - "../../src/Utils.cpp", - "../../src/Utils.h", - ] - } + files: [ + "../../src/transfer/AppRequest.h", + "../../src/transfer/AppResponse.h", + "../../src/server/ServerControls.cpp", + "../../src/server/ServerControls.h", + "../../src/server/ServerSettings.cpp", + "../../src/server/ServerSettings.h", + "../../src/server/SocketsQueue.h", + "../../src/server/config/ConfigParser.cpp", + "../../src/server/config/ConfigParser.h", + "../../src/server/data-variant/Abstract.cpp", + "../../src/server/data-variant/Abstract.h", + "../../src/server/data-variant/FormUrlencoded.cpp", + "../../src/server/data-variant/FormUrlencoded.h", + "../../src/server/data-variant/MultipartFormData.cpp", + "../../src/server/data-variant/MultipartFormData.h", + "../../src/server/data-variant/TextPlain.cpp", + "../../src/server/data-variant/TextPlain.h", + "../../src/server/protocol/ServerHttp2Protocol.cpp", + "../../src/server/protocol/ServerHttp2Protocol.h", + "../../src/server/protocol/ServerHttp2Stream.cpp", + "../../src/server/protocol/ServerHttp2Stream.h", + "../../src/server/protocol/ServerWebSocket.cpp", + "../../src/server/protocol/ServerWebSocket.h", + "../../src/socket/Adapter.cpp", + "../../src/socket/Adapter.h", + "../../src/socket/AdapterDefault.cpp", + "../../src/socket/AdapterDefault.h", + "../../src/socket/AdapterTls.cpp", + "../../src/socket/AdapterTls.h", + "../../src/socket/List.cpp", + "../../src/socket/List.h", + "../../src/system/Cache.h", + "../../src/utils/Event.cpp", + "../../src/utils/Event.h", + "../../src/transfer/FileIncoming.cpp", + "../../src/transfer/FileIncoming.h", + "../../src/system/GlobalMutex.cpp", + "../../src/system/GlobalMutex.h", + "../../src/transfer/http2/HPack.cpp", + "../../src/transfer/http2/HPack.h", + "../../src/transfer/http2/Http2.cpp", + "../../src/transfer/http2/Http2.h", + "../../src/transfer/HttpStatusCode.h", + "../../src/Main.cpp", + "../../src/Main.h", + "../../src/system/Module.cpp", + "../../src/system/Module.h", + "../../src/transfer/ProtocolVariant.h", + "../../src/server/Request.cpp", + "../../src/server/Request.h", + "../../src/server/Server.cpp", + "../../src/server/Server.h", + "../../src/server/ServerApplicationSettings.h", + "../../src/server/ServerApplicationsTree.cpp", + "../../src/server/ServerApplicationsTree.h", + "../../src/server/protocol/ServerHttp1.cpp", + "../../src/server/protocol/ServerHttp1.h", + "../../src/server/protocol/ServerHttp2.cpp", + "../../src/server/protocol/ServerHttp2.h", + "../../src/server/protocol/ServerProtocol.cpp", + "../../src/server/protocol/ServerProtocol.h", + "../../src/server/protocol/extensions/Sendfile.cpp", + "../../src/server/protocol/extensions/Sendfile.h", + "../../src/server/ServerStructuresArguments.h", + "../../src/system/SharedMemory.cpp", + "../../src/system/SharedMemory.h", + "../../src/SignalHandlers.cpp", + "../../src/SignalHandlers.h", + "../../src/socket/Socket.cpp", + "../../src/socket/Socket.h", + "../../src/system/System.cpp", + "../../src/system/System.h", + "../../src/utils/Utils.cpp", + "../../src/utils/Utils.h", + ] + } } diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp deleted file mode 100644 index 7f65321..0000000 --- a/src/ConfigParser.cpp +++ /dev/null @@ -1,701 +0,0 @@ - -#include "ConfigParser.h" -#include "ServerApplicationSettings.h" - -#include "Utils.h" - -#include -#include - -namespace HttpServer -{ - /** - * Config - include file - */ - bool ConfigParser::includeConfigFile(const std::string &fileName, std::string &strBuf, const std::size_t offset) - { - std::ifstream file(fileName); - - if ( ! file) - { - file.close(); - - std::cout << "Error: " << fileName << " - cannot be open;" << std::endl; - - return false; - } - - file.seekg(0, std::ifstream::end); - std::streamsize file_size = file.tellg(); - file.seekg(0, std::ifstream::beg); - - const std::streamsize file_size_max = 2 * 1024 * 1024; - - if (file_size_max < file_size) - { - file.close(); - - std::cout << "Error: " << fileName << " - is too large; max include file size = " << file_size_max << " bytes;" << std::endl; - - return false; - } - - if (file_size) - { - std::vector buf(file_size); - file.read(buf.data(), file_size); - - strBuf.insert(strBuf.begin() + offset, buf.cbegin(), buf.cend() ); - } - - file.close(); - - return true; - } - - /** - * Config - add application - */ - bool ConfigParser::addApplication( - const std::unordered_multimap &app, - const ServerApplicationDefaultSettings &defaults, - std::vector &modules, - ServerApplicationsTree &apps_tree - ) - { - auto const it_name = app.find("server_name"); - - if (app.cend() == it_name) - { - std::cout << "Error: application parameter 'server_name' has not been specified;" << std::endl; - return false; - } - - std::vector names; - - const std::string whitespace(" \t\n\v\f\r"); - - const std::string &app_name = it_name->second; - - size_t delimiter = app_name.find_first_of(whitespace); - - if (delimiter) - { - size_t cur_pos = 0; - - while (std::string::npos != delimiter) - { - std::string name = app_name.substr(cur_pos, delimiter - cur_pos); - names.emplace_back(std::move(name) ); - cur_pos = app_name.find_first_not_of(whitespace, delimiter + 1); - delimiter = app_name.find_first_of(whitespace, cur_pos); - } - - std::string name = app_name.substr(cur_pos); - names.emplace_back(std::move(name) ); - } - - auto const range_port = app.equal_range("listen"); - - if (range_port.first == range_port.second) - { - std::cout << "Error: application port is not set;" << std::endl; - return false; - } - - std::unordered_set ports; - std::unordered_set tls_ports; - - for (auto it = range_port.first; it != range_port.second; ++it) - { - const std::string &lis = it->second; - - const bool is_tls = std::string::npos != lis.find("tls"); - - const std::vector list = Utils::explode(lis, ' '); - - for (auto const &value : list) - { - const int port = std::strtol(value.c_str(), nullptr, 10); - - if (port) - { - if (is_tls) - { - tls_ports.emplace(port); - } - else - { - ports.emplace(port); - } - } - } - } - - std::string cert_file; - std::string key_file; - std::string chain_file; - std::string crl_file; - std::string stapling_file; - std::string dh_file; - - if (false == tls_ports.empty() ) - { - auto const it_ca_file = app.find("tls_certificate_chain"); - - if (app.cend() != it_ca_file) - { - chain_file = it_ca_file->second; - } - - auto const it_crl_file = app.find("tls_certificate_crl"); - - if (app.cend() != it_crl_file) - { - crl_file = it_crl_file->second; - } - - auto const it_stapling_file = app.find("tls_stapling_file"); - - if (app.cend() != it_stapling_file) - { - stapling_file = it_stapling_file->second; - } - - auto const it_dh_params_file = app.find("tls_dh_params_file"); - - if (app.cend() != it_dh_params_file) - { - dh_file = it_dh_params_file->second; - } - - auto const it_cert_file = app.find("tls_certificate"); - - if (app.cend() == it_cert_file) - { - std::cout << "Error: tls certificate file \"CERT\" has not been specified in configuration file;" << std::endl; - tls_ports.clear(); - } - else - { - cert_file = it_cert_file->second; - } - - auto const it_key_file = app.find("tls_certificate_key"); - - if (app.cend() == it_key_file) - { - std::cout << "Error: tls certificate key file \"KEY\" has not been specified in configuration file;" << std::endl; - tls_ports.clear(); - } - else - { - key_file = it_key_file->second; - } - } - - auto const it_root_dir = app.find("root_dir"); - - if (app.cend() == it_root_dir || it_root_dir->second.empty() ) - { - std::cout << "Error: application parameter 'root_dir' has not been specified;" << std::endl; - return false; - } - - auto const it_module = app.find("server_module"); - - if (app.cend() == it_module) - { - std::cout << "Error: application parameter 'server_module' has not been specified;" << std::endl; - return false; - } - - // TODO: get module realpath - - Module module(it_module->second); - - if (false == module.is_open() ) - { - std::cout << "Error: module '" << it_module->second << "' cannot be open;" << std::endl; - return false; - } - - void *(*addr)(void *) = nullptr; - - if (false == module.find("application_call", &addr) ) - { - std::cout << "Error: function 'application_call' not found in module '" << it_module->second << "';" << std::endl; - return false; - } - - std::function app_call = reinterpret_cast(addr); - - if ( ! app_call) - { - std::cout << "Error: invalid function 'application_call' in module '" << it_module->second << "';" << std::endl; - return false; - } - - if (false == module.find("application_clear", &addr) ) - { - std::cout << "Error: function 'application_clear' not found in module '" << it_module->second << "';" << std::endl; - return false; - } - - std::function app_clear = reinterpret_cast(addr); - - std::function app_init = std::function(); - - if (module.find("application_init", &addr) ) - { - app_init = reinterpret_cast(addr); - } - - std::function app_final = std::function(); - - if (module.find("application_final", &addr) ) - { - app_final = reinterpret_cast(addr); - } - - bool success = true; - - try - { - if (app_init) - { - success = app_init(); - } - } - catch (...) - { - success = false; - } - - if (false == success) - { - std::cout << "Warning: error when initializing application '" << it_module->second << "';" << std::endl; - return false; - } - - auto const it_temp_dir = app.find("temp_dir"); - - std::string temp_dir = app.cend() != it_temp_dir ? it_temp_dir->second : defaults.temp_dir; - - auto const it_request_max_size = app.find("request_max_size"); - - const size_t request_max_size = app.cend() != it_request_max_size ? std::strtoull(it_request_max_size->second.c_str(), nullptr, 10) : defaults.request_max_size; - - auto const it_module_update = app.find("server_module_update"); - - std::string module_update = app.cend() != it_module_update ? it_module_update->second : ""; - - // Calculate module index - size_t module_index = std::numeric_limits::max(); - - for (size_t i = 0; i < modules.size(); ++i) - { - if (modules[i] == module) - { - module_index = i; - break; - } - } - - if (std::numeric_limits::max() == module_index) - { - module_index = modules.size(); - modules.emplace_back(std::move(module) ); - } - - std::string root_dir = it_root_dir->second; - - #ifdef WIN32 - if ('\\' == root_dir.back() ) - { - root_dir.pop_back(); - } - #endif - - // Remove back slash from root_dir - if ('/' == root_dir.back() ) - { - root_dir.pop_back(); - } - - // Create application settings struct - ServerApplicationSettings *settings = new ServerApplicationSettings { - std::move(ports), - std::move(tls_ports), - - std::move(root_dir), - std::move(temp_dir), - request_max_size, - - module_index, - it_module->second, - std::move(module_update), - - std::move(cert_file), - std::move(key_file), - std::move(chain_file), - std::move(crl_file), - std::move(stapling_file), - std::move(dh_file), - - std::move(app_call), - std::move(app_clear), - std::move(app_init), - std::move(app_final) - }; - - // Add application names in tree - if (names.empty() ) - { - apps_tree.addApplication(app_name, settings); - } - else - { - for (size_t i = 0; i < names.size(); ++i) - { - apps_tree.addApplication(names[i], settings); - } - } - - return true; - } - - /** - * @brief ConfigParser::parseMimes - * @param fileName - * @param mimes_types - * @return bool - */ - bool ConfigParser::parseMimes(const std::string &fileName, std::unordered_map &mimes_types) - { - std::ifstream file(fileName); - - if ( ! file) - { - file.close(); - - std::cout << "Error: " << fileName << " - cannot be open;" << std::endl; - - return false; - } - - file.seekg(0, std::ifstream::end); - std::streamsize file_size = file.tellg(); - file.seekg(0, std::ifstream::beg); - - const std::streamsize file_size_max = 2048 * 1024; - - if (file_size_max < file_size) - { - file.close(); - - std::cout << "Error: " << fileName << " - is too large; max file size = " << file_size_max << " bytes;" << std::endl; - - return false; - } - - const std::string whitespace(" \t\v\f\r"); - - std::vector buf(file_size); - - file.read(buf.data(), file_size); - - const std::string str_buf(buf.cbegin(), buf.cend() ); - - size_t cur_pos = 0; - size_t end_pos = str_buf.find('\n', cur_pos); - - while (std::string::npos != end_pos) - { - cur_pos = str_buf.find_first_not_of(whitespace, cur_pos); - size_t delimiter = str_buf.find_first_of(whitespace, cur_pos); - - if (delimiter < end_pos) - { - std::string mime_type = str_buf.substr(cur_pos, delimiter - cur_pos); - - if ('#' != mime_type.front() ) - { - delimiter = str_buf.find_first_not_of(whitespace, delimiter); - - if (delimiter < end_pos) - { - std::string ext = str_buf.substr(delimiter, end_pos - delimiter); - - delimiter = ext.find_first_of(whitespace); - - if (std::string::npos != delimiter) - { - for (size_t ext_pos = 0; std::string::npos != ext_pos; ) - { - std::string ext_unit = ext.substr(ext_pos, std::string::npos != delimiter ? delimiter - ext_pos : std::string::npos); - - if (false == ext_unit.empty() ) - { - mimes_types.emplace(std::move(ext_unit), mime_type); - } - - ext_pos = ext.find_first_not_of(whitespace, delimiter); - - delimiter = ext.find_first_of(whitespace, ext_pos); - } - } - else - { - mimes_types.emplace(std::move(ext), std::move(mime_type) ); - } - } - } - } - - cur_pos = end_pos + 1; - - end_pos = str_buf.find('\n', cur_pos); - } - - return true; - } - - static size_t findBlockEnd(const std::string &str_buf, size_t str_pos) - { - size_t pos = str_buf.find('}', str_pos); - - while (std::string::npos != pos) - { - size_t begin_line = str_buf.rfind('\n', pos); - - if (std::string::npos == begin_line) - { - begin_line = 0; - } - - begin_line = str_buf.find_first_not_of("\r\n", begin_line); - - if ('#' == str_buf[begin_line]) - { - str_pos = str_buf.find_first_of("\r\n", pos); - } - else - { - break; - } - - pos = str_buf.find('}', str_pos); - } - - return pos; - } - - /** - * Config - parse - */ - bool ConfigParser::loadConfig( - const std::string &conf_file_name, - std::unordered_map &settings, - std::unordered_map &mimes_types, - std::vector &modules, - ServerApplicationsTree &apps_tree - ) - { - std::string str_buf; - - if (false == includeConfigFile(conf_file_name, str_buf) ) - { - return false; - } - - std::vector > applications; - - const std::string whitespace(" \t\n\v\f\r"); - - size_t cur_pos = 0; - size_t end_pos = str_buf.find(';', cur_pos); - size_t block_pos = 0; - - while (std::string::npos != end_pos) - { - block_pos = str_buf.find('{', cur_pos); - - if (end_pos < block_pos) - { - cur_pos = str_buf.find_first_not_of(whitespace, cur_pos); - size_t delimiter = str_buf.find_first_of(whitespace, cur_pos); - - if (delimiter < end_pos) - { - std::string param_name = str_buf.substr(cur_pos, delimiter - cur_pos); - - if ('#' != param_name.front() ) - { - cur_pos = str_buf.find_first_not_of(whitespace, delimiter + 1); - delimiter = str_buf.find_last_not_of(whitespace, end_pos); - - std::string param_value = str_buf.substr(cur_pos, delimiter - cur_pos); - - if ("include" == param_name) - { - this->includeConfigFile(param_value, str_buf, end_pos + 1); - } - else - { - settings.emplace(std::move(param_name), std::move(param_value) ); - } - } - else // if comment line - { - end_pos = str_buf.find_first_of("\r\n", cur_pos); - } - } - - cur_pos = end_pos; - - if (std::string::npos != cur_pos) - { - ++cur_pos; - } - } - else if (std::string::npos != block_pos) - { - cur_pos = str_buf.find_first_not_of(whitespace, cur_pos); - size_t delimiter = str_buf.find_first_of(whitespace, cur_pos); - - const std::string block_type_name = str_buf.substr(cur_pos, delimiter - cur_pos); - - if ('#' != block_type_name.front() ) - { - delimiter = str_buf.find_first_not_of(whitespace, delimiter); - - cur_pos = block_pos + 1; - size_t block_end = findBlockEnd(str_buf, cur_pos); - - if (std::string::npos == block_end) - { - std::cout << "Error: symbol '}' after '" << block_type_name << "' has not been found;" << std::endl - << "Parsing config aborted;" << std::endl; - - return false; - } - else if (delimiter == block_pos) - { - if ("server" == block_type_name) - { - std::unordered_multimap app; - - end_pos = str_buf.find(';', cur_pos); - - while (block_end > end_pos) - { - cur_pos = str_buf.find_first_not_of(whitespace, cur_pos); - delimiter = str_buf.find_first_of(whitespace, cur_pos); - - if (delimiter < end_pos) - { - std::string param_name = str_buf.substr(cur_pos, delimiter - cur_pos); - - if ('#' != param_name.front() ) - { - cur_pos = str_buf.find_first_not_of(whitespace, delimiter + 1); - delimiter = str_buf.find_last_not_of(whitespace, end_pos); - - std::string param_value = str_buf.substr(cur_pos, delimiter - cur_pos); - - if ("include" == param_name) - { - cur_pos = end_pos + 1; - this->includeConfigFile(param_value, str_buf, cur_pos); - block_end = findBlockEnd(str_buf, cur_pos); - } - else - { - app.emplace(std::move(param_name), std::move(param_value) ); - } - } - else // if comment line - { - end_pos = str_buf.find_first_of("\r\n", cur_pos); - } - } - - if (std::string::npos != end_pos) - { - ++end_pos; - } - - cur_pos = end_pos; - - end_pos = str_buf.find(';', cur_pos); - } - - applications.emplace_back(std::move(app) ); - } - else - { - std::cout << "Warning: " << block_type_name << " - unknown block type;" << std::endl; - } - } - else - { - std::cout << "Warning: after " << block_type_name << " expected '{' ;" << std::endl; - } - - cur_pos = block_end; - - if (std::string::npos != cur_pos) - { - ++cur_pos; - } - } - else // if comment line - { - cur_pos = str_buf.find_first_of("\r\n", cur_pos); - } - } - - end_pos = str_buf.find(';', cur_pos); - } - - auto const it_mimes = settings.find("mimes"); - - if (settings.cend() != it_mimes) - { - this->parseMimes(it_mimes->second, mimes_types); - } - else - { - std::cout << "Warning: mime types file is not set in configuration;" << std::endl; - } - - if (false == applications.empty() ) - { - auto const it_default_temp_dir = settings.find("default_temp_dir"); - - const std::string default_temp_dir = settings.cend() != it_default_temp_dir ? it_default_temp_dir->second : System::getTempDir(); - - auto const it_default_request_max_size = settings.find("request_max_size"); - - const size_t default_request_max_size = settings.cend() != it_default_request_max_size ? std::strtoull(it_default_request_max_size->second.c_str(), nullptr, 10) : 0; - - ServerApplicationDefaultSettings defaults { - default_temp_dir, - default_request_max_size - }; - - for (auto const &app : applications) - { - this->addApplication(app, defaults, modules, apps_tree); - } - } - - if (apps_tree.empty() ) - { - std::cout << "Notice: server does not contain applications;" << std::endl; - } - - return true; - } -}; \ No newline at end of file diff --git a/src/ConfigParser.h b/src/ConfigParser.h deleted file mode 100644 index 07af9c9..0000000 --- a/src/ConfigParser.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "ServerApplicationDefaultSettings.h" -#include "ServerApplicationsTree.h" -#include "Module.h" - -#include -#include - -namespace HttpServer -{ - class ConfigParser - { - private: - static bool includeConfigFile(const std::string &fileName, std::string &strBuf, const size_t offset = 0); - - static bool addApplication( - const std::unordered_multimap &app, - const ServerApplicationDefaultSettings &defaults, - std::vector &modules, - ServerApplicationsTree &apps_tree - ); - - static bool parseMimes(const std::string &fileName, std::unordered_map &mimes_types); - - public: - bool loadConfig( - const std::string &conf, - std::unordered_map &settings, - std::unordered_map &mimes_types, - std::vector &modules, - ServerApplicationsTree &apps_tree - ); - }; -}; \ No newline at end of file diff --git a/src/DataVariantAbstract.h b/src/DataVariantAbstract.h deleted file mode 100644 index 9058696..0000000 --- a/src/DataVariantAbstract.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "SocketAdapter.h" -#include "RequestParameters.h" - -namespace HttpServer -{ - class DataVariantAbstract - { - protected: - std::string data_variant_name; - - public: - inline std::string getName() const { return data_variant_name; } - - public: - /** - * virtual destructor - */ - virtual ~DataVariantAbstract() = default; - - /** - * @param const Socket &sock - сокет, откуда можно достать остальные данные - * @param std::string &str - первая часть полученных данных - * @param const size_t leftBytes - сколько осталось данных (в байтах) получить из сокета - * @param const std::unordered_map &contentParams - дополнительные параметры, описывающие формат данных - * @param request_data &rp - данные текущего запроса (заголовки, параметры, коллекции для хранения данных) - * - * @return bool - (true|false) - удачно ли были разобраны данные - */ - virtual bool parse - ( - const SocketAdapter &sock, - std::string &str, - const size_t leftBytes, - std::unordered_map &contentParams, - struct request_parameters &rp - ) = 0; - }; -}; \ No newline at end of file diff --git a/src/DataVariantFormUrlencoded.cpp b/src/DataVariantFormUrlencoded.cpp deleted file mode 100644 index 6dc674b..0000000 --- a/src/DataVariantFormUrlencoded.cpp +++ /dev/null @@ -1,62 +0,0 @@ - -#include "DataVariantFormUrlencoded.h" - -#include "Utils.h" - -namespace HttpServer -{ - DataVariantFormUrlencoded::DataVariantFormUrlencoded() - { - data_variant_name = "application/x-www-form-urlencoded"; - } - - bool DataVariantFormUrlencoded::parse - ( - const SocketAdapter &sock, - std::string &str, - const size_t leftBytes, - std::unordered_map &contentParams, - struct request_parameters &rp - ) - { - if (str.empty() ) - { - return true; - } - - for (size_t var_pos = 0, var_end = 0; std::string::npos != var_end; var_pos = var_end + 1) - { - // Поиск следующего параметра - var_end = str.find('&', var_pos); - - // Поиск значения параметра - size_t delimiter = str.find('=', var_pos); - - if (delimiter >= var_end) - { - // Получить имя параметра - std::string var_name = Utils::urlDecode(str.substr(var_pos, std::string::npos != var_end ? var_end - var_pos : std::string::npos) ); - - // Сохранить параметр с пустым значением - rp.incoming_data.emplace(std::move(var_name), ""); - } - else - { - // Получить имя параметра - std::string var_name = Utils::urlDecode(str.substr(var_pos, delimiter - var_pos) ); - - ++delimiter; - - // Получить значение параметра - std::string var_value = Utils::urlDecode(str.substr(delimiter, std::string::npos != var_end ? var_end - delimiter : std::string::npos) ); - - // Сохранить параметр и значение - rp.incoming_data.emplace(std::move(var_name), std::move(var_value) ); - } - } - - str.clear(); - - return true; - } -}; \ No newline at end of file diff --git a/src/DataVariantFormUrlencoded.h b/src/DataVariantFormUrlencoded.h deleted file mode 100644 index 0c848c1..0000000 --- a/src/DataVariantFormUrlencoded.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "DataVariantAbstract.h" - -namespace HttpServer -{ - class DataVariantFormUrlencoded: public DataVariantAbstract - { - public: - DataVariantFormUrlencoded(); - - public: - virtual bool parse - ( - const SocketAdapter &sock, - std::string & str, - const size_t leftBytes, - std::unordered_map &contentParams, - struct request_parameters &rp - ) override; - }; -}; \ No newline at end of file diff --git a/src/DataVariantMultipartFormData.cpp b/src/DataVariantMultipartFormData.cpp deleted file mode 100644 index c79b309..0000000 --- a/src/DataVariantMultipartFormData.cpp +++ /dev/null @@ -1,426 +0,0 @@ - -#include "Utils.h" -#include "DataVariantMultipartFormData.h" - -#include - -namespace HttpServer -{ - DataVariantMultipartFormData::DataVariantMultipartFormData() - { - data_variant_name = "multipart/form-data"; - } - - bool DataVariantMultipartFormData::append - ( - const SocketAdapter &sock, - const std::chrono::milliseconds &timeout, - std::vector &buf, - std::string &str_buf, - const std::string &data_end, - const size_t &leftBytes, - long &recv_len, - size_t &recv_total_len - ) - { - // Завершаем работу, если уже получено байт сколько нужно - if (recv_total_len >= leftBytes) - { - return false; - } - - // Получаем данные из сокета - recv_len = sock.nonblock_recv(buf, timeout); - - // Завершаем работу, если ошибка получения данных через сокет - if (recv_len <= 0) - { - return false; - } - - // Обновляем общее количество полученных данных - recv_total_len += recv_len; - - // Добавляем полученные данные к рабочему буферу - str_buf.append(buf.cbegin(), buf.cbegin() + recv_len); - - // Завершаем работу, если в буфере меньше чем окончание блока данных - if (str_buf.length() <= data_end.length() ) - { - return false; - } - - return true; - } - - bool DataVariantMultipartFormData::parse - ( - const SocketAdapter &sock, - std::string &str, - const size_t leftBytes, - std::unordered_map &contentParams, - struct request_parameters &rp - ) - { - // Проверить есть ли в параметрах разделитель блоков данных - auto const it = contentParams.find("boundary"); - - if (contentParams.cend() == it) - { - return false; - } - - // Определить признак начала блока данных - std::string block_delimiter("--" + it->second); - // Определить признак окончания всех данных - std::string data_end("--" + it->second + "--\r\n"); - - if (0 == str.find(data_end) ) - { - return true; - } - - data_end = "\r\n" + data_end; - - // Установить размер буфера данных - const size_t buf_len = (leftBytes >= 512 * 1024) ? 512 * 1024 : leftBytes; - - // Создание буферов - std::vector buf(buf_len); - - long recv_len; // Прочитано байт при последнем извлечении данных из сокета - size_t recv_total_len = 0; // Получено байт из сокета всего - - // Поиск разделителя блока данных - // str_cur — текущая позиция в буфере - size_t str_cur = str.find(block_delimiter); - - if (std::string::npos == str_cur) - { - // Получить следующий кусок данных - if (false == append(sock, rp.timeout, buf, str, data_end, leftBytes, recv_len, recv_total_len) ) - { - return false; - } - - // Поиск разделителя блока данных - str_cur = str.find(block_delimiter); - - if (std::string::npos == str_cur) - { - return false; - } - } - - str_cur += block_delimiter.length() + 2; - - block_delimiter = "\r\n" + block_delimiter; - - // Если найден конец данных - bool is_find_data_end = false; - - do - { - // Правильно ли был передан блок данных - bool is_block_valid = true; - - // Поиск конца заголовков блока данных - size_t headers_end = str.find("\r\n\r\n", str_cur); - - // Если конец не был найден, то - if (std::string::npos == headers_end) - { - // Получить следующий кусок данных - if (false == append(sock, rp.timeout, buf, str, data_end, leftBytes, recv_len, recv_total_len) ) - { - return false; - } - - // Провести повторный поиск - headers_end = str.find("\r\n\r\n", str_cur); - - // Если снова не найдено, то данные некорректны - if (std::string::npos == headers_end) - { - is_block_valid = false; - } - } - - if (is_block_valid) - { - // Разобрать заголовки блока данных - std::unordered_map headers; - - for (size_t line_end = str.find("\r\n", str_cur); str_cur < headers_end; line_end = str.find("\r\n", str_cur) ) - { - size_t delimiter = str.find(':', str_cur); - - if (std::string::npos == delimiter || delimiter > line_end) - { - std::string header_name = str.substr(str_cur, line_end - str_cur); - Utils::trim(header_name); - headers.emplace(std::move(header_name), ""); - } - else - { - std::string header_name = str.substr(str_cur, delimiter - str_cur); - Utils::trim(header_name); - - ++delimiter; - - std::string header_value = str.substr(delimiter, line_end - delimiter); - Utils::trim(header_value); - - headers.emplace(std::move(header_name), std::move(header_value) ); - } - - // Перейти к следующему заголовку - str_cur = line_end + 2; - } - - // Перейти к данным - str_cur += 2; - - // Определить источник блока данных - auto it = headers.find("Content-Disposition"); - - // Если заголовок определён - if (headers.cend() != it) - { - // Разобрать значение заголовка данных на параметры - std::unordered_map header_params; - - const std::string &header_value = it->second; - - size_t delimiter = header_value.find(';'); - - std::string content_disposition(header_value.substr(0, delimiter) ); - Utils::trim(content_disposition); - - // Проверить соответствие указанного формата - if ("form-data" == content_disposition && std::string::npos != delimiter) - { - // Получить параметры блока данных - for (size_t str_param_cur = delimiter + 1, str_param_end; std::string::npos != str_param_cur; str_param_cur = str_param_end) - { - str_param_end = header_value.find(';', str_param_cur); - delimiter = header_value.find('=', str_param_cur); - - if (std::string::npos == delimiter || delimiter > str_param_end) - { - std::string param_name = (std::string::npos == str_param_end) ? header_value.substr(str_param_cur) : header_value.substr(str_param_cur, str_param_end - str_param_cur); - Utils::trim(param_name); - header_params.emplace(std::move(param_name), ""); - } - else - { - std::string param_name = header_value.substr(str_param_cur, delimiter - str_param_cur); - Utils::trim(param_name); - - ++delimiter; - - delimiter = header_value.find('"', delimiter); - - if (std::string::npos == delimiter) - { - str_param_end = header_value.find(';', str_param_cur); - - std::string param_value = (std::string::npos == str_param_end) ? header_value.substr(delimiter) : header_value.substr(delimiter, str_param_end - delimiter); - Utils::trim(param_value); - - header_params.emplace(std::move(param_name), std::move(param_value) ); - } - else - { - ++delimiter; - - str_param_cur = header_value.find('"', delimiter); - str_param_end = header_value.find(';', str_param_cur); - - std::string param_value = (std::string::npos == str_param_cur) ? header_value.substr(delimiter) : header_value.substr(delimiter, str_param_cur - delimiter); - - header_params.emplace(std::move(param_name), std::move(param_value) ); - } - } - - if (std::string::npos != str_param_end) - { - ++str_param_end; - } - } - - // Поиск имени блока данных - auto const it_name = header_params.find("name"); - - if (header_params.cend() != it_name) - { - // Если данные пришли из файла - auto const it_filename = header_params.find("filename"); - - if (header_params.cend() != it_filename) - { - // Найти тип файла - auto const it_filetype = headers.find("Content-Type"); - - if (headers.cend() != it_filetype) - { - // Сгенерировать уникальное имя - std::string tmp_name = System::getTempDir() + Utils::getUniqueName(); - - // Создать файл - std::ofstream file(tmp_name, std::ofstream::trunc | std::ofstream::binary); - - // Если файл был создан и готов для работы - if (file.is_open() ) - { - // Смещение данных в буфере в начало - // str.assign(str.cbegin() + str_cur, str.cend() ); - str.erase(str.begin(), str.begin() + str_cur); - - // Поиск конца блока данных - size_t delimiter = str.find(block_delimiter); - - // Пока конец блока данных не найден - while (std::string::npos == delimiter) - { - // Добавить данные к значению - file.write(str.data(), str.length() - data_end.length() ); - - // str.assign(str.cend() - data_end.length(), str.cend() ); - str.erase(str.begin(), str.end() - data_end.length() ); - - // Получить следующий кусок данных - if (false == append(sock, rp.timeout, buf, str, data_end, leftBytes, recv_len, recv_total_len) ) - { - return false; - } - - // Поиск конца блока данных - delimiter = str.find(block_delimiter); - } - - // Добавить последнюю часть данных к значению - file.write(str.data(), delimiter); - - // Добавить данные в список - rp.incoming_files.emplace(it_name->second, FileIncoming(std::move(tmp_name), it_filetype->second, file.tellp() ) ); - - file.close(); - - // Если найден конец данных - if (str.find(data_end, delimiter) == delimiter) - { - is_find_data_end = true; - } - - str_cur = delimiter + block_delimiter.length() + 2; - } - else // Файл не смог быть открыт/создан - { - is_block_valid = false; - } - } - else // Тип файла не определён - { - is_block_valid = false; - } - } - else // Если данные пришли из формы - { - std::string value; - - // Смещение данных в буфере в начало - // str.assign(str.cbegin() + str_cur, str.cend() ); - str.erase(str.begin(), str.begin() + str_cur); - - // Поиск конца блока данных - size_t delimiter = str.find(block_delimiter); - - // Пока конец блока данных не найден - while (std::string::npos == delimiter) - { - // Добавить данные к значению - value.append(str.cbegin(), str.cend() - data_end.length() ); - - // str.assign(str.cend() - data_end.length(), str.cend() ); - str.erase(str.begin(), str.end() - data_end.length() ); - - // Получить следующий кусок данных - if (false == append(sock, rp.timeout, buf, str, data_end, leftBytes, recv_len, recv_total_len) ) - { - return false; - } - - // Поиск конца блока данных - delimiter = str.find(block_delimiter); - } - - // Добавить последнюю часть данных к значению - value.append(str.cbegin(), str.cbegin() + delimiter); - - // Добавить данные в список - rp.incoming_data.emplace(it_name->second, std::move(value) ); - - // Если найден конец данных - if (str.find(data_end, delimiter) == delimiter) - { - is_find_data_end = true; - } - - str_cur = delimiter + block_delimiter.length() + 2; - } - } - else // Имя блока данных не определено - { - is_block_valid = false; - } - } - else // Формат не соответствует - { - is_block_valid = false; - } - } - else // Если источник не определён - { - is_block_valid = false; - } - } - - // Если данные некорректны - if (false == is_block_valid) - { - // то блок данных пропускаем (ищем следующий блок) - str_cur = str.find(block_delimiter, str_cur); - - while (std::string::npos == str_cur) - { - // str.assign(str.cend() - data_end.length(), str.cend() ); - str.erase(str.begin(), str.end() - data_end.length() ); - - // Получить следующий кусок данных - if (false == append(sock, rp.timeout, buf, str, data_end, leftBytes, recv_len, recv_total_len) ) - { - return false; - } - - str_cur = str.find(block_delimiter); - } - - // Если найден конец данных - if (str.find(data_end, str_cur) == str_cur) - { - is_find_data_end = true; - } - - str_cur += block_delimiter.length() + 2; - } - - str.erase(str.begin(), str.begin() + str_cur); - - str_cur = 0; - } - while (false == is_find_data_end); - - return true; - } -}; \ No newline at end of file diff --git a/src/DataVariantMultipartFormData.h b/src/DataVariantMultipartFormData.h deleted file mode 100644 index 0a72b68..0000000 --- a/src/DataVariantMultipartFormData.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "DataVariantAbstract.h" - -namespace HttpServer -{ - class DataVariantMultipartFormData: public DataVariantAbstract - { - public: - DataVariantMultipartFormData(); - - protected: - static bool append - ( - const SocketAdapter &sock, - const std::chrono::milliseconds &timeout, - std::vector &buf, - std::string &str_buf, - const std::string &data_end, - const size_t &leftBytes, - long &recv_len, - size_t &recv_total_len - ); - - public: - virtual bool parse - ( - const SocketAdapter &sock, - std::string &str, - const size_t leftBytes, - std::unordered_map &contentParams, - struct request_parameters &rp - ) override; - }; -}; \ No newline at end of file diff --git a/src/DataVariantTextPlain.cpp b/src/DataVariantTextPlain.cpp deleted file mode 100644 index 2d5a6b1..0000000 --- a/src/DataVariantTextPlain.cpp +++ /dev/null @@ -1,60 +0,0 @@ - -#include "DataVariantTextPlain.h" - -namespace HttpServer -{ - DataVariantTextPlain::DataVariantTextPlain() - { - data_variant_name = "text/plain"; - } - - bool DataVariantTextPlain::parse - ( - const SocketAdapter &sock, - std::string &str, - const size_t leftBytes, - std::unordered_map &contentParams, - struct request_parameters &rp - ) - { - if (str.empty() ) - { - return true; - } - - for (size_t var_pos = 0, var_end = 0; std::string::npos != var_end; var_pos = var_end + 1) - { - // Поиск следующего параметра - var_end = str.find('&', var_pos); - - // Поиск значения параметра - size_t delimiter = str.find('=', var_pos); - - if (delimiter >= var_end) - { - // Получить имя параметра - std::string var_name = str.substr(var_pos, std::string::npos != var_end ? var_end - var_pos : std::string::npos); - - // Сохранить параметр с пустым значением - rp.incoming_data.emplace(std::move(var_name), ""); - } - else - { - // Получить имя параметра - std::string var_name = str.substr(var_pos, delimiter - var_pos); - - ++delimiter; - - // Получить значение параметра - std::string var_value = str.substr(delimiter, std::string::npos != var_end ? var_end - delimiter : std::string::npos); - - // Сохранить параметр и значение - rp.incoming_data.emplace(std::move(var_name), std::move(var_value) ); - } - } - - str.clear(); - - return true; - } -}; \ No newline at end of file diff --git a/src/DataVariantTextPlain.h b/src/DataVariantTextPlain.h deleted file mode 100644 index d3fb2ac..0000000 --- a/src/DataVariantTextPlain.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "DataVariantAbstract.h" - -namespace HttpServer -{ - class DataVariantTextPlain: public DataVariantAbstract - { - public: - DataVariantTextPlain(); - - public: - virtual bool parse - ( - const SocketAdapter &sock, - std::string &str, - const size_t leftBytes, - std::unordered_map &contentParams, - struct request_parameters &rp - ) override; - }; -}; \ No newline at end of file diff --git a/src/Event.cpp b/src/Event.cpp deleted file mode 100644 index 77eeb40..0000000 --- a/src/Event.cpp +++ /dev/null @@ -1,101 +0,0 @@ - -#include "Event.h" - -namespace HttpServer -{ - Event::Event(const bool _signaled, const bool _manually): signaled(_signaled), manually(_manually) - { - - } - - void Event::wait() - { - if (false == this->signaled.load() ) - { - std::unique_lock lck(this->mtx); - - do - { - this->cv.wait(lck); - } - while (false == this->signaled.load() ); - } - - if (false == this->manually) - { - this->signaled.store(false); - } - } - - bool Event::wait_for(const std::chrono::milliseconds &ms) - { - bool is_timeout = false; - - if (false == this->signaled.load() ) - { - std::unique_lock lck(this->mtx); - - is_timeout = false == this->cv.wait_for(lck, ms, [this] { return this->notifed(); } ); - } - - if (false == this->manually) - { - this->signaled.store(false); - } - - return is_timeout; - } - - bool Event::wait_until(const std::chrono::high_resolution_clock::time_point &tp) - { - bool is_timeout = false; - - if (false == this->signaled.load() ) - { - std::unique_lock lck(this->mtx); - - do - { - if (std::cv_status::timeout == this->cv.wait_until(lck, tp) ) - { - is_timeout = true; - break; - } - } - while (false == this->signaled.load() ); - } - - if (false == this->manually) - { - this->signaled.store(false); - } - - return is_timeout; - } - - void Event::notify() - { - this->signaled.store(true); - this->cv.notify_all(); - } - - void Event::notify(const size_t threadsCount) - { - this->signaled.store(true); - - for (size_t i = 0; i < threadsCount; ++i) - { - this->cv.notify_one(); - } - } - - void Event::reset() - { - this->signaled.store(false); - } - - bool Event::notifed() const - { - return this->signaled.load(); - } -}; \ No newline at end of file diff --git a/src/FileIncoming.cpp b/src/FileIncoming.cpp deleted file mode 100644 index 1526735..0000000 --- a/src/FileIncoming.cpp +++ /dev/null @@ -1,36 +0,0 @@ - -#include "FileIncoming.h" - -#include - -namespace HttpServer -{ - FileIncoming::FileIncoming(const std::string &fileName, const std::string &fileType, const size_t fileSize) - : file_name(fileName), file_type(fileType), file_size(fileSize) - { - - } - - FileIncoming::FileIncoming(const FileIncoming &obj) - : file_name(obj.file_name), file_type(obj.file_type), file_size(obj.file_size) - { - - } - - FileIncoming::FileIncoming(FileIncoming &&obj) - : file_name(std::move(obj.file_name) ), file_type(std::move(obj.file_type) ), file_size(obj.file_size) - { - obj.file_size = 0; - } - - bool FileIncoming::isExists() const - { - std::ifstream file(file_name, std::ifstream::binary); - - const bool is_exists = file.good(); - - file.close(); - - return is_exists; - } -}; \ No newline at end of file diff --git a/src/FileIncoming.h b/src/FileIncoming.h deleted file mode 100644 index 6c6fa97..0000000 --- a/src/FileIncoming.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include - -namespace HttpServer -{ - class FileIncoming - { - protected: - std::string file_name; - std::string file_type; - size_t file_size; - - private: - FileIncoming() = delete; - - public: - FileIncoming(const std::string &fileName, const std::string &fileType, const size_t fileSize); - FileIncoming(const FileIncoming &obj); - FileIncoming(FileIncoming &&obj); - - ~FileIncoming() = default; - - inline std::string getName() const - { - return file_name; - } - - inline std::string getType() const - { - return file_type; - } - - inline size_t getSize() const - { - return file_size; - } - - bool isExists() const; - }; -}; \ No newline at end of file diff --git a/src/Main.cpp b/src/Main.cpp index c7739e1..8aa2a4b 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -1,6 +1,6 @@ #include "Main.h" -#include "Server.h" +#include "server/Server.h" #include "SignalHandlers.h" #include @@ -17,34 +17,25 @@ int main(const int argc, const char *argv[]) int exitcode = EXIT_FAILURE; - if (1 < argc) - { + if (1 < argc) { auto const command = commands.find(argv[1]); - if (commands.cend() != command) - { + if (commands.cend() != command) { HttpServer::Server server; - if (bindSignalHandlers(&server) ) - { + if (bindSignalHandlers(&server) ) { exitcode = command->second(&server, argc, argv); - stopSignalHandlers(); } - } - else - { + } else { std::cout << "Unknown parameter, see --help" << std::endl; } } - else if (1 == argc) - { + else if (1 == argc) { std::cout << "Try to run with the parameter --help" << std::endl; - } - else - { + } else { std::cout << "The number of arguments cannot be equal to zero. Enter the parameter --help" << std::endl; } return exitcode; -} \ No newline at end of file +} diff --git a/src/Module.cpp b/src/Module.cpp deleted file mode 100644 index 50aa0b5..0000000 --- a/src/Module.cpp +++ /dev/null @@ -1,202 +0,0 @@ - -#include "Module.h" - -#ifdef WIN32 - #include - - #ifdef UNICODE - #include - #endif -#elif POSIX - #include - #include -#endif - -namespace HttpServer -{ - Module::Module(): lib_handle(nullptr) - { - - } - - Module::Module(const std::string &libPath): lib_handle(nullptr) - { - open(libPath); - } - - Module::Module(const Module &obj) : lib_handle(obj.lib_handle) - { - - } - - Module::Module(Module &&obj) : lib_handle(obj.lib_handle) - { - obj.lib_handle = nullptr; - } - - bool Module::is_open() const - { - return nullptr != this->lib_handle; - } - - bool Module::open(const std::string &libPath) - { - if (is_open() ) - { - close(); - } - - #ifdef WIN32 - const size_t pos_slash = libPath.rfind('\\'); - const size_t pos_slash_back = libPath.rfind('/'); - - size_t pos = std::string::npos; - - if (pos_slash != std::string::npos && pos_slash > pos_slash_back) - { - pos = pos_slash; - } - else if (pos_slash_back != std::string::npos) - { - pos = pos_slash_back; - } - - DLL_DIRECTORY_COOKIE cookie = nullptr; - - if (std::string::npos != pos) - { - std::wstring directory(libPath.cbegin(), libPath.cbegin() + pos + 1); - - cookie = ::AddDllDirectory(directory.data() ); - } - - #ifdef UNICODE - std::wstring_convert > converter; - const std::wstring lib_path = converter.from_bytes(libPath); - #else - const std::string &lib_path = libPath; - #endif - - lib_handle = ::LoadLibraryEx(lib_path.c_str(), 0, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); - - if (cookie) - { - ::RemoveDllDirectory(cookie); - } - #elif POSIX - lib_handle = ::dlopen(libPath.c_str(), RTLD_NOW | RTLD_LOCAL); - #else - #error "Undefine platform" - #endif - - if (nullptr == lib_handle) - { - #ifdef POSIX - std::cout << ::dlerror() << std::endl; - #endif - return false; - } - - return true; - } - - void Module::close() - { - if (lib_handle) - { - #ifdef WIN32 - ::FreeLibrary(lib_handle); - #elif POSIX - ::dlclose(lib_handle); - #else - #error "Undefine platform" - #endif - - lib_handle = nullptr; - } - } - - bool Module::find(const std::string &symbolName, void *(**addr)(void *) ) const - { - if (lib_handle) - { - #ifdef WIN32 - *addr = reinterpret_cast(::GetProcAddress(lib_handle, symbolName.c_str() ) ); - - return nullptr != *addr; - #elif POSIX - char *error = ::dlerror(); - - *addr = reinterpret_cast(::dlsym(lib_handle, symbolName.c_str() ) ); - - error = ::dlerror(); - - return nullptr == error; - #else - #error "Undefine platform" - #endif - } - - return false; - } - - bool Module::find(const char *symbolName, void *(**addr)(void *) ) const - { - if (lib_handle) - { - #ifdef WIN32 - *addr = reinterpret_cast(::GetProcAddress(lib_handle, symbolName) ); - - return nullptr != *addr; - #elif POSIX - char *error = ::dlerror(); - - *addr = reinterpret_cast(::dlsym(lib_handle, symbolName) ); - - error = ::dlerror(); - - return nullptr == error; - #else - #error "Undefine platform" - #endif - } - - return false; - } - - bool Module::operator ==(const Module &obj) const - { - return this->lib_handle == obj.lib_handle; - } - - bool Module::operator !=(const Module &obj) const - { - return this->lib_handle != obj.lib_handle; - } - - Module &Module::operator =(const Module &obj) - { - if (*this != obj) - { - close(); - - lib_handle = obj.lib_handle; - } - - return *this; - } - - Module &Module::operator =(Module &&obj) - { - if (*this != obj) - { - close(); - - lib_handle = obj.lib_handle; - - obj.lib_handle = nullptr; - } - - return *this; - } -}; \ No newline at end of file diff --git a/src/Module.h b/src/Module.h deleted file mode 100644 index a1dc301..0000000 --- a/src/Module.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#ifdef WIN32 - #include - #include -#endif - -#include - -namespace HttpServer -{ - class Module - { - protected: - #ifdef WIN32 - ::HMODULE lib_handle; - #elif POSIX - void *lib_handle; - #else - #error "Undefine platform" - #endif - - public: - Module(); - Module(const std::string &libPath); - Module(const Module &obj); - Module(Module &&obj); - - ~Module() = default; - - bool is_open() const; - - bool open(const std::string &libPath); - void close(); - - bool find(const std::string &symbolName, void *(**addr)(void *) ) const; - bool find(const char *symbolName, void *(**addr)(void *) ) const; - - bool operator ==(const Module &obj) const; - bool operator !=(const Module &obj) const; - - Module &operator =(const Module &obj); - Module &operator =(Module &&obj); - }; -}; \ No newline at end of file diff --git a/src/RawData.h b/src/RawData.h deleted file mode 100644 index a493382..0000000 --- a/src/RawData.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -namespace Utils -{ - struct raw_pair - { - char *key; - char *value; - }; - - struct raw_fileinfo - { - char *key; - char *file_name; - char *file_type; - size_t file_size; - }; -}; \ No newline at end of file diff --git a/src/RequestParameters.cpp b/src/RequestParameters.cpp deleted file mode 100644 index d3e1c39..0000000 --- a/src/RequestParameters.cpp +++ /dev/null @@ -1,21 +0,0 @@ - -#include "RequestParameters.h" - -namespace HttpServer -{ - void request_parameters::clear() - { - incoming_headers.clear(); - incoming_params.clear(); - incoming_data.clear(); - incoming_files.clear(); - - outgoing_headers.clear(); - - method.clear(); - version.clear(); - uri_reference.clear(); - - timeout = std::chrono::milliseconds::zero(); - } -}; \ No newline at end of file diff --git a/src/RequestParameters.h b/src/RequestParameters.h deleted file mode 100644 index 0d3e209..0000000 --- a/src/RequestParameters.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "FileIncoming.h" - -#include -#include - -namespace HttpServer -{ - struct request_parameters - { - std::unordered_map incoming_headers; - std::unordered_multimap incoming_params; - std::unordered_multimap incoming_data; - std::unordered_multimap incoming_files; - - std::unordered_map outgoing_headers; - - std::string method; - std::string version; - std::string uri_reference; - - std::chrono::milliseconds timeout; - - size_t keep_alive_count; - int connection_params; - int app_exit_code; - - void clear(); - }; -}; \ No newline at end of file diff --git a/src/Server.cpp b/src/Server.cpp deleted file mode 100644 index 6c53dea..0000000 --- a/src/Server.cpp +++ /dev/null @@ -1,2036 +0,0 @@ - -#include "Server.h" -#include "Utils.h" -#include "System.h" - -#include "DataVariantFormUrlencoded.h" -#include "DataVariantMultipartFormData.h" -#include "DataVariantTextPlain.h" -#include "FileIncoming.h" -#include "ServerRequest.h" -#include "ServerResponse.h" -#include "ConfigParser.h" -#include "SocketAdapterDefault.h" -#include "SocketAdapterTls.h" -#include "GlobalMutex.h" -#include "SharedMemory.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace HttpServer -{ - std::string Server::getMimeTypeByFileName(const std::string &fileName) const - { - const size_t ext_pos = fileName.rfind('.'); - std::string file_ext = std::string::npos != ext_pos ? fileName.substr(ext_pos + 1) : ""; - - const std::locale loc; - Utils::toLower(file_ext, loc); - - auto const it_mime = this->mimes_types.find(file_ext); - - return this->mimes_types.cend() != it_mime ? it_mime->second : "application/octet-stream"; - } - - std::vector > Server::getRanges( - const std::string &rangeHeader, - const size_t posSymEqual, - const size_t fileSize, - std::string &resultRangeHeader, - size_t &contentLength - ) const - { - std::vector > ranges; - - contentLength = 0; - - size_t delimiter = posSymEqual; // rangeHeader.find('='); - - const std::string range_unit_name(rangeHeader.cbegin(), rangeHeader.cbegin() + delimiter); - - static const std::unordered_map ranges_units { - {"bytes", 1} - }; - - auto const it_unit = ranges_units.find(range_unit_name); - - if (ranges_units.cend() == it_unit) - { - return ranges; - } - - const size_t range_unit = it_unit->second; - - for (size_t str_pos; std::string::npos != delimiter; ) - { - str_pos = delimiter + 1; - - delimiter = rangeHeader.find(',', str_pos); - - const size_t range_pos = rangeHeader.find('-', str_pos); - - if (range_pos < delimiter) - { - const std::string range_begin_str(rangeHeader.cbegin() + str_pos, rangeHeader.cbegin() + range_pos); - const std::string range_end_str(rangeHeader.cbegin() + range_pos + 1, std::string::npos == delimiter ? rangeHeader.cend() : rangeHeader.cbegin() + delimiter); - - if (false == range_begin_str.empty() ) - { - const size_t range_begin = std::strtoull(range_begin_str.c_str(), nullptr, 10) * range_unit; - - if (range_begin < fileSize) - { - if (false == range_end_str.empty() ) - { - size_t range_end = std::strtoull(range_end_str.c_str(), nullptr, 10) * range_unit; - - if (range_end >= range_begin) - { - if (range_end > fileSize) - { - range_end = fileSize; - } - - const size_t length = range_end - range_begin + 1; - - contentLength += length; - - resultRangeHeader += std::to_string(range_begin) + '-' + std::to_string(range_end) + ','; - - ranges.emplace_back(std::tuple {range_begin, length}); - } - } - else // if range_end_str empty - { - const size_t length = fileSize - range_begin; - - contentLength += length; - - resultRangeHeader += std::to_string(range_begin) + '-' + std::to_string(fileSize - 1) + ','; - - ranges.emplace_back(std::tuple {range_begin, length}); - } - } - } - else if (false == range_end_str.empty() ) // if range_begin_str empty - { - size_t range_end = std::strtoull(range_end_str.c_str(), nullptr, 10) * range_unit; - - const size_t length = range_end < fileSize ? fileSize - range_end : fileSize; - - const size_t range_begin = fileSize - length; - - range_end = fileSize - range_begin - 1; - - contentLength += length; - - resultRangeHeader += std::to_string(range_begin) + '-' + std::to_string(range_end) + ','; - - ranges.emplace_back(std::tuple {range_begin, length}); - } - } - } - - if (false == ranges.empty() ) - { - resultRangeHeader.back() = '/'; - - resultRangeHeader = "bytes " + resultRangeHeader + std::to_string(fileSize); - } - - return ranges; - } - - int Server::transferFilePart - ( - const SocketAdapter &clientSocket, - const std::chrono::milliseconds &timeout, - const std::string &fileName, - const time_t fileTime, - const size_t fileSize, - const std::string &rangeHeader, - const std::string &connectionHeader, - const std::string &dateHeader, - const bool headersOnly - ) const - { - const size_t pos_sym_equal = rangeHeader.find('='); - - if (std::string::npos == pos_sym_equal) - { - // HTTP 400 - std::string headers("HTTP/1.1 400 Bad Request\r\n"); - headers += connectionHeader + dateHeader + "\r\n"; - - clientSocket.nonblock_send(headers, timeout); - - return 0; - } - - std::string content_range_header; - - size_t content_length; - - const std::vector > ranges = this->getRanges(rangeHeader, pos_sym_equal, fileSize, content_range_header, content_length); - - if (0 == content_length) - { - // HTTP 416 - std::string headers("HTTP/1.1 416 Requested Range Not Satisfiable\r\n"); - headers += connectionHeader + dateHeader + "\r\n"; - - clientSocket.nonblock_send(headers, timeout); - - return 0; - } - - // Ranges transfer - std::ifstream file(fileName, std::ifstream::binary); - - if ( ! file) - { - file.close(); - - // HTTP 500 - std::string headers("HTTP/1.1 500 Internal Server Error\r\n"); - headers += connectionHeader + dateHeader + "\r\n"; - - clientSocket.nonblock_send(headers, timeout); - - return 0; - } - - const std::string file_mime_type = this->getMimeTypeByFileName(fileName); - - std::string headers("HTTP/1.1 206 Partial Content\r\n"); - headers += "Content-Type: " + file_mime_type + "\r\n" - + "Content-Length: " + std::to_string(content_length) + "\r\n" - + "Accept-Ranges: bytes\r\n" - + "Content-Range: " + content_range_header + "\r\n" - + "Last-Modified: " + Utils::getDatetimeAsString(fileTime, true) + "\r\n" - + connectionHeader + dateHeader + "\r\n"; - - // Отправить заголовки - if (clientSocket.nonblock_send(headers, timeout) < 0) - { - file.close(); - - return 0; - } - - if (false == headersOnly) - { - size_t position, length; - - for (auto const &range : ranges) - { - std::tie(position, length) = range; - - std::vector buf(length < 512 * 1024 ? length : 512 * 1024); - - file.seekg(position, file.beg); - - size_t send_size_left = length; - - long send_size; - - do - { - if (send_size_left < 512 * 1024) - { - buf.resize(send_size_left); - } - - file.read(buf.data(), buf.size() ); - send_size = clientSocket.nonblock_send(buf, file.gcount(), timeout); - - send_size_left -= send_size; - } - while (false == file.eof() && false == file.fail() && send_size > 0 && send_size_left); - } - } - - file.close(); - - return 1; - } - - /** - * Передача файла (или его части) - */ - int Server::transferFile - ( - const SocketAdapter &clientSocket, - const std::string &fileName, - const std::string &connectionHeader, - const bool headersOnly, - struct request_parameters &rp - ) const - { - // Get current time in GMT - const std::string date_header = "Date: " + Utils::getDatetimeAsString() + "\r\n"; - - time_t file_time; - size_t file_size; - - // Получить размер файла и дату последнего изменения - if (false == System::getFileSizeAndTimeGmt(fileName, &file_size, &file_time) ) - { - // HTTP 404 - std::string headers("HTTP/1.1 404 Not Found\r\n"); - headers += connectionHeader + date_header + "\r\n"; - - clientSocket.nonblock_send(headers, rp.timeout); - - return 0; - } - - // Check for If-Modified header - auto const it_modified = rp.incoming_headers.find("If-Modified-Since"); - - // Если найден заголовок проверки изменения файла (проверить, изменялся ли файл) - if (rp.incoming_headers.cend() != it_modified) - { - const time_t time_in_request = Utils::stringTimeToTimestamp(it_modified->second); - - if (file_time == time_in_request) - { - // HTTP 304 - std::string headers("HTTP/1.1 304 Not Modified\r\n"); - headers += connectionHeader + date_header + "\r\n"; - - clientSocket.nonblock_send(headers, rp.timeout); - - return 0; - } - } - - auto const it_range = rp.incoming_headers.find("Range"); - - // Range transfer - if (rp.incoming_headers.cend() != it_range) - { - return this->transferFilePart(clientSocket, rp.timeout, fileName, file_time, file_size, it_range->second, connectionHeader, date_header, headersOnly); - } - - // File transfer - std::ifstream file(fileName, std::ifstream::binary); - - if ( ! file) - { - file.close(); - - // HTTP 500 - std::string headers("HTTP/1.1 500 Internal Server Error\r\n"); - headers += connectionHeader + date_header + "\r\n"; - - clientSocket.nonblock_send(headers, rp.timeout); - - return 0; - } - - const std::string file_mime_type = this->getMimeTypeByFileName(fileName); - - std::string headers("HTTP/1.1 200 OK\r\n"); - headers += "Content-Type: " + file_mime_type + "\r\n" - + "Content-Length: " + std::to_string(file_size) + "\r\n" - + "Accept-Ranges: bytes\r\n" - + "Last-Modified: " + Utils::getDatetimeAsString(file_time, true) + "\r\n" - + connectionHeader + date_header + "\r\n"; - - // Отправить заголовки - if (clientSocket.nonblock_send(headers, rp.timeout) < 0) - { - file.close(); - - return 0; - } - - // Отправить файл - if (false == headersOnly && file_size) - { - std::vector buf(file_size < 512 * 1024 ? file_size : 512 * 1024); - - long send_size; - - do - { - file.read(reinterpret_cast(buf.data() ), buf.size() ); - send_size = clientSocket.nonblock_send(buf, file.gcount(), rp.timeout); - } - while (false == file.eof() && false == file.fail() && send_size > 0); - } - - file.close(); - - return 1; - } - - /** - * Парсинг переданных параметров (URI) - */ - bool Server::parseIncomingVars(std::unordered_multimap ¶ms, const std::string &uriParams) - { - if (uriParams.length() ) - { - for (size_t var_pos = 0, var_end = 0; std::string::npos != var_end; var_pos = var_end + 1) - { - // Поиск следующего параметра - var_end = uriParams.find('&', var_pos); - - // Поиск значения параметра - size_t delimiter = uriParams.find('=', var_pos); - - if (delimiter >= var_end) - { - // Получить имя параметра - std::string var_name = Utils::urlDecode(uriParams.substr(var_pos, std::string::npos != var_end ? var_end - var_pos : std::string::npos) ); - - // Сохранить параметр с пустым значением - params.emplace(std::move(var_name), ""); - } - else - { - // Получить имя параметра - std::string var_name = Utils::urlDecode(uriParams.substr(var_pos, delimiter - var_pos) ); - - ++delimiter; - - // Получить значение параметра - std::string var_value = Utils::urlDecode(uriParams.substr(delimiter, std::string::npos != var_end ? var_end - delimiter : std::string::npos) ); - - // Сохранить параметр и значение - params.emplace(std::move(var_name), std::move(var_value) ); - } - } - - return true; - } - - return false; - } - - void Server::sendStatus(const SocketAdapter &clientSocket, const std::chrono::milliseconds &timeout, const size_t statusCode) - { - static const std::unordered_map statuses { - {400, "Bad Request"}, - {404, "Not Found"}, - {413, "Request Entity Too Large"} - }; - - auto const it = statuses.find(statusCode); - - if (statuses.cend() != it) - { - const std::string &status = it->second; - - std::string headers("HTTP/1.1 " + std::to_string(statusCode) + ' ' + status + "\r\n\r\n"); - - clientSocket.nonblock_send(headers, timeout); - } - } - - /** - * Метод для обработки запроса - */ - int Server::threadRequestProc(SocketAdapter &clientSocket, const struct sockaddr_in &clientAddr) const - { - struct request_parameters rp; - - rp.keep_alive_count = 100; - - const size_t buf_len = 4096; - std::vector buf(buf_len); - - std::string str_buf; - - do - { - rp.app_exit_code = EXIT_FAILURE; - - // Подготовить параметры для получения данных - rp.timeout = std::chrono::milliseconds(5000); - - if (false == getRequest(clientSocket, buf, str_buf, rp) ) - { - break; - } - - if (int error_code = getRequestHeaders(str_buf, rp) ) - { - this->sendStatus(clientSocket, rp.timeout, error_code); - - break; - } - - const ServerApplicationSettings *app_sets = getApplicationSettings(rp); - - // Если приложение не найдено - if (nullptr == app_sets) - { - // HTTP 404 Not Found - this->sendStatus(clientSocket, rp.timeout, 404); - - break; - } - - if (int error_code = getRequestData(clientSocket, str_buf, *app_sets, rp) ) - { - this->sendStatus(clientSocket, rp.timeout, error_code); - - break; - } - - runApplication(clientSocket, *app_sets, rp); - - for (auto const &it : rp.incoming_files) - { - remove(it.second.getName().c_str() ); - } - - if (EXIT_SUCCESS == rp.app_exit_code) - { - this->getConnectionParams(rp); - - this->xSendfile(clientSocket, rp); - } - else - { - rp.connection_params = CONNECTION_CLOSED; - } - - rp.clear(); - } - while (isConnectionKeepAlive(rp) ); - - if (false == isConnectionUpgrade(rp) ) - { - clientSocket.close(); - } - - return rp.app_exit_code; - } - - bool Server::getRequest(const SocketAdapter &clientSocket, std::vector &buf, std::string &str_buf, struct request_parameters &rp) - { - // Получить данные запроса от клиента - const long recv_size = clientSocket.nonblock_recv(buf, rp.timeout); - - if (recv_size < 0 && str_buf.empty() ) - { - return false; - } - - if (recv_size > 0) // Если данные были получены - { - str_buf.append(buf.cbegin(), buf.cbegin() + recv_size); - } - - return true; - } - - int Server::getRequestHeaders(std::string &str_buf, struct request_parameters &rp) const - { - // Если запрос пустой - if (str_buf.empty() ) - { - // HTTP 400 Bad Request - return 400; - } - - // Поиск конца заголовков (пустая строка) - size_t headers_end = str_buf.find("\r\n\r\n"); - - // Если найден конец заголовков - if (std::string::npos == headers_end) - { - // HTTP 400 Bad Request - return 400; - } - - headers_end += 2; - - size_t str_cur = 0; - // Поиск конца первого заголовка - size_t str_end = str_buf.find("\r\n"); - - // Если не найден конец заголовка - if (std::string::npos == str_end) - { - // HTTP 400 Bad Request - return 400; - } - - // Установка конца строки (для поиска) - str_buf[str_end] = '\0'; - - // Разделить метод запроса и параметры запроса - size_t delimiter = str_buf.find(' ', str_cur); - - // Получить метод запроса (GET, POST, PUT, DELETE, ...) - rp.method = str_buf.substr(str_cur, delimiter - str_cur); - // Сохранить метод и параметры запроса - rp.incoming_headers[rp.method] = str_buf.substr(delimiter + 1, str_end - delimiter - 1); - - delimiter += 1; - // Найти окончание URI - size_t uri_end = str_buf.find(' ', delimiter); - - // Если окончание не найдено - if (std::string::npos == uri_end) - { - uri_end = str_end; - // то версия протокола HTTP - 0.9 - rp.version = "0.9"; - } - else // Если окончание найдено - { - str_buf[uri_end] = '\0'; - const size_t ver_beg = uri_end + 6; // Пропустить "HTTP/" - - if (ver_beg < str_end) - { - // Получить версию протокола HTTP - rp.version = str_buf.substr(ver_beg, str_end - ver_beg); - } - } - - // Поиск именованных параметров запросов (переменных ?) - const size_t params_pos = str_buf.find('?', delimiter); - - // Сохранить полную ссылку URI (без параметров) - rp.uri_reference = (std::string::npos == params_pos) ? str_buf.substr(delimiter) : str_buf.substr(delimiter, params_pos - delimiter); - - if (std::string::npos != params_pos) - { - // Извлекаем параметры запроса из URI - if (false == parseIncomingVars(rp.incoming_params, str_buf.substr(params_pos + 1, uri_end) ) ) - { - // HTTP 400 Bad Request - return 400; - } - } - - // Переход к обработке следующего заголовка - str_cur = str_end + 2; - // Поиск конца заголовка - str_end = str_buf.find("\r\n", str_cur); - // Установка конца заголовка - str_buf[str_end] = '\0'; - - // Цикл извлечения заголовков запроса - for (; str_cur != headers_end; str_end = str_buf.find("\r\n", str_cur), str_buf[str_end] = '\0') - { - // Поиск разделителя названия заголовка и его значения - delimiter = str_buf.find(':', str_cur); - - // Если разделитель найден в текущей строке - if (delimiter < str_end) - { - std::string header_name = str_buf.substr(str_cur, delimiter - str_cur); - std::string header_value = str_buf.substr(delimiter + 1, str_end - delimiter - 1); - - // Удалить лишние пробелы в начале и в конце строки - Utils::trim(header_value); - - // Сохранить заголовок и его значение - rp.incoming_headers.emplace(std::move(header_name), std::move(header_value) ); - } - - // Перейти к следующей строке - str_cur = str_end + 2; - } - - str_buf.erase(str_buf.begin(), str_buf.begin() + headers_end + 2); - - return 0; - } - - void Server::runApplication(const SocketAdapter &clientSocket, const ServerApplicationSettings &appSets, struct request_parameters &rp) - { - Utils::raw_pair *raw_pair_params = nullptr; - Utils::raw_pair *raw_pair_headers = nullptr; - Utils::raw_pair *raw_pair_data = nullptr; - Utils::raw_fileinfo *raw_fileinfo_files = nullptr; - - Utils::stlToRawPairs(&raw_pair_params, rp.incoming_params); - Utils::stlToRawPairs(&raw_pair_headers, rp.incoming_headers); - Utils::stlToRawPairs(&raw_pair_data, rp.incoming_data); - Utils::filesIncomingToRawFilesInfo(&raw_fileinfo_files, rp.incoming_files); - - server_request request { - clientSocket.get_handle(), - clientSocket.get_tls_session(), - rp.method.c_str(), - rp.uri_reference.c_str(), - appSets.root_dir.c_str(), - rp.incoming_params.size(), - raw_pair_params, - rp.incoming_headers.size(), - raw_pair_headers, - rp.incoming_data.size(), - raw_pair_data, - rp.incoming_files.size(), - raw_fileinfo_files - }; - - server_response response { - clientSocket.get_handle(), 0, nullptr - }; - - try - { - // Launch application - rp.app_exit_code = appSets.application_call(&request, &response); - } - catch (...) - { - rp.app_exit_code = EXIT_FAILURE; - } - - if (EXIT_SUCCESS == rp.app_exit_code) - { - Utils::rawPairsToStl(rp.outgoing_headers, response.headers, response.headers_count); - } - - // Очистить заголовки сформированные приложением - try - { - appSets.application_clear(response.headers, response.headers_count); - } - catch (...) {} - - Utils::destroyRawPairs(raw_pair_params, rp.incoming_params.size() ); - Utils::destroyRawPairs(raw_pair_headers, rp.incoming_headers.size() ); - Utils::destroyRawPairs(raw_pair_data, rp.incoming_data.size() ); - Utils::destroyRawFilesInfo(raw_fileinfo_files, rp.incoming_files.size() ); - } - - int Server::getRequestData(const SocketAdapter &clientSocket, std::string &str_buf, const ServerApplicationSettings &appSets, struct request_parameters &rp) const - { - // Определить вариант данных запроса (заодно проверить, есть ли данные) - auto const it = rp.incoming_headers.find("Content-Type"); - - if (rp.incoming_headers.cend() == it) - { - return 0; - } - - // Параметры - std::unordered_map content_params; - - // Получить значение заголовка - const std::string &header_value = it->second; - - // Определить, содержит ли тип данных запроса дополнительные параметры - size_t delimiter = header_value.find(';'); - - std::string data_variant_name; // Название варианта данных запроса - - // Если есть дополнительные параметры - извлекаем их - if (std::string::npos != delimiter) - { - data_variant_name = header_value.substr(0, delimiter); - Utils::trim(data_variant_name); - - for (size_t str_param_cur = delimiter + 1, str_param_end = 0; std::string::npos != str_param_end; str_param_cur = str_param_end + 1) - { - str_param_end = header_value.find(';', str_param_cur); - delimiter = header_value.find('=', str_param_cur); - - if (delimiter >= str_param_end) - { - std::string param_name = header_value.substr(str_param_cur, std::string::npos != str_param_end ? str_param_end - str_param_cur : std::string::npos); - Utils::trim(param_name); - content_params.emplace(std::move(param_name), ""); - } - else - { - std::string param_name = header_value.substr(str_param_cur, delimiter - str_param_cur); - Utils::trim(param_name); - - ++delimiter; - - std::string param_value = header_value.substr(delimiter, std::string::npos != str_param_end ? str_param_end - delimiter : std::string::npos); - Utils::trim(param_value); - - content_params.emplace(std::move(param_name), std::move(param_value) ); - } - } - } - else - { - data_variant_name = header_value; - } - - // Поиск варианта данных по имени типа - auto variant = this->variants.find(data_variant_name); - - // Если сервер не поддерживает формат полученных данных - if (this->variants.cend() == variant) - { - // HTTP 400 Bad Request - return 400; - } - - DataVariantAbstract *data_variant = variant->second; - - // Получить длину запроса в байтах - size_t data_length = 0; - - auto const it_len = rp.incoming_headers.find("Content-Length"); - - if (rp.incoming_headers.cend() != it_len) - { - data_length = std::strtoull(it_len->second.c_str(), nullptr, 10); - } - - // Если размер запроса превышает лимит (если лимит был установлен) - if (data_length > appSets.request_max_size && 0 != appSets.request_max_size) - { - // HTTP 413 Request Entity Too Large - return 413; - } - - // Сколько осталось получить данных - size_t left_bytes = 0; - - std::string data_buf; - - if (data_length >= str_buf.length() ) - { - left_bytes = data_length - str_buf.length(); - - data_buf.swap(str_buf); - } - else - { - data_buf.assign(str_buf.cbegin(), str_buf.cbegin() + data_length); - str_buf.erase(str_buf.begin(), str_buf.begin() + data_length); - } - - // Разобрать данные на составляющие - if (false == data_variant->parse(clientSocket, data_buf, left_bytes, content_params, rp) ) - { - for (auto const &it : rp.incoming_files) - { - remove(it.second.getName().c_str() ); - } - - // HTTP 400 Bad Request - return 400; - } - - if (false == data_buf.empty() ) - { - str_buf.swap(data_buf); - } - - return 0; - } - - const ServerApplicationSettings *Server::getApplicationSettings(const struct request_parameters &rp) const - { - // Получить доменное имя (или адрес) назначения запроса - auto const it_host = rp.incoming_headers.find("Host"); - - // Если имя задано - продолжить обработку запроса - if (rp.incoming_headers.cend() != it_host) - { - const std::string &host_header = it_host->second; - - // Поиск разделителя, за которым помещается номер порта, если указан - const size_t delimiter = host_header.find(':'); - - // Получить имя (или адрес) - const std::string host = host_header.substr(0, delimiter); - - // Получить номер порта - const int port = (std::string::npos != delimiter) ? std::strtol(host_header.substr(delimiter + 1).c_str(), nullptr, 10) : 80; - - // Поиск настроек приложения по имени - const ServerApplicationSettings *app_sets = this->apps_tree.find(host); - - // Если приложение найдено - if (app_sets && (app_sets->ports.cend() != app_sets->ports.find(port) || app_sets->tls_ports.cend() != app_sets->tls_ports.find(port) ) ) - { - return app_sets; - } - } - - return nullptr; - } - - void Server::xSendfile(const SocketAdapter &clientSocket, struct request_parameters &rp) const - { - auto const it_x_sendfile = rp.outgoing_headers.find("X-Sendfile"); - - if (rp.outgoing_headers.cend() != it_x_sendfile) - { - const std::string connection_header = isConnectionKeepAlive(rp) ? "Connection: Keep-Alive\r\nKeep-Alive: timeout=5; max=" + std::to_string(rp.keep_alive_count) + "\r\n" : "Connection: Close\r\n"; - - const bool headers_only = ("head" == rp.method); - - this->transferFile(clientSocket, it_x_sendfile->second, connection_header, headers_only, rp); - } - } - - void Server::getConnectionParams(struct request_parameters &rp) - { - rp.connection_params = CONNECTION_CLOSED; - - auto const it_in_connection = rp.incoming_headers.find("Connection"); - auto const it_out_connection = rp.outgoing_headers.find("Connection"); - - if (rp.incoming_headers.cend() != it_in_connection && rp.outgoing_headers.cend() != it_out_connection) - { - const std::locale loc; - - std::string connection_in = it_in_connection->second; - Utils::toLower(connection_in, loc); - - std::string connection_out = it_out_connection->second; - Utils::toLower(connection_out, loc); - - auto const incoming_params = Utils::explode(connection_in, ','); - - auto const it = std::find(incoming_params.cbegin(), incoming_params.cend(), connection_out); - - if (incoming_params.cend() != it) - { - const std::string &inc = *it; - - if ("keep-alive" == inc) - { - --rp.keep_alive_count; - - if (0 < rp.keep_alive_count) - { - rp.connection_params |= CONNECTION_KEEP_ALIVE; - } - } - else if ("upgrade" == inc) - { - rp.connection_params |= CONNECTION_UPGRADE; - } - } - } - } - - bool Server::isConnectionKeepAlive(const struct request_parameters &rp) - { - return (rp.connection_params & CONNECTION_KEEP_ALIVE) == CONNECTION_KEEP_ALIVE; - } - - bool Server::isConnectionUpgrade(const struct request_parameters &rp) - { - return (rp.connection_params & CONNECTION_UPGRADE) == CONNECTION_UPGRADE; - } - - /** - * Метод для обработки запросов (запускается в отдельном потоке) - * извлекает сокет клиенты из очереди и передаёт его на обслуживание - */ - void Server::threadRequestCycle(std::queue > &sockets, Event &eventThreadCycle) const - { - while (true) - { - Socket sock; - struct sockaddr_in addr; - - eventThreadCycle.wait(); - - if (false == this->process_flag) - { - break; - } - - this->sockets_queue_mtx.lock(); - - if (sockets.size() ) - { - std::tie(sock, addr) = sockets.front(); - - sockets.pop(); - } - - if (sockets.empty() ) - { - eventThreadCycle.reset(); - - this->eventNotFullQueue->notify(); - } - - this->sockets_queue_mtx.unlock(); - - if (sock.is_open() ) - { - ++this->threads_working_count; - - struct ::sockaddr_in sock_addr; - ::socklen_t sock_addr_len = sizeof(sock_addr); - - ::getsockname(sock.get_handle(), reinterpret_cast(&sock_addr), &sock_addr_len); - - const int port = ntohs(sock_addr.sin_port); - - auto const it = this->tls_data.find(port); - - if (this->tls_data.cend() != it) // if TLS connection - { - const std::tuple &data = it->second; - - SocketAdapterTls socket_adapter( - sock, - std::get(data), - std::get(data) - ); - - if (socket_adapter.handshake() ) - { - this->threadRequestProc(socket_adapter, addr); - } - } - else - { - SocketAdapterDefault socket_adapter(sock); - - this->threadRequestProc(socket_adapter, addr); - } - - --this->threads_working_count; - } - } - } - - /** - * Цикл управления количеством рабочих потоков - */ - int Server::cycleQueue(std::queue > &sockets) - { - auto const it_option = this->settings.find("threads_max_count"); - - size_t threads_max_count = 0; - - if (this->settings.cend() != it_option) - { - const std::string &option = it_option->second; - - threads_max_count = std::strtoull(option.c_str(), nullptr, 10); - } - - if (0 == threads_max_count) - { - threads_max_count = std::thread::hardware_concurrency(); - - if (0 == threads_max_count) - { - threads_max_count = 1; - } - - threads_max_count *= 2; - } - - this->threads_working_count = 0; - - Event eventThreadCycle(false, true); - - std::function > &, Event &)> serverThreadRequestCycle = std::mem_fn(&Server::threadRequestCycle); - - std::vector active_threads; - active_threads.reserve(threads_max_count); - - // For update applications modules - do - { - if (this->eventUpdateModule->notifed() ) - { - updateModules(); - } - - // Cycle creation threads applications requests - do - { - while (this->threads_working_count == active_threads.size() && active_threads.size() < threads_max_count && sockets.empty() == false) - { - active_threads.emplace_back(serverThreadRequestCycle, this, std::ref(sockets), std::ref(eventThreadCycle) ); - } - - size_t notify_count = active_threads.size() - this->threads_working_count; - - if (notify_count > sockets.size() ) - { - notify_count = sockets.size(); - } - - eventThreadCycle.notify(notify_count); - - this->eventProcessQueue->wait(); - } - while (this->process_flag); - - // Data clear - - eventThreadCycle.notify(); - - if (false == active_threads.empty() ) - { - // Join threads (wait completion) - for (auto &th : active_threads) - { - th.join(); - } - - active_threads.clear(); - } - - this->eventNotFullQueue->notify(); - } - while (this->eventUpdateModule->notifed() ); - - if (false == this->server_sockets.empty() ) - { - for (Socket &s : this->server_sockets) - { - s.close(); - } - - this->server_sockets.clear(); - } - - return 0; - } - - Server::Server(): eventNotFullQueue(nullptr), eventProcessQueue(nullptr), eventUpdateModule(nullptr) - { - - } - - void Server::addDataVariant(DataVariantAbstract *dataVariant) - { - this->variants.emplace(dataVariant->getName(), dataVariant); - } - - bool Server::updateModule(Module &module, std::unordered_set &applications, const size_t moduleIndex) - { - std::unordered_set same; - - for (auto &app : applications) - { - if (app->module_index == moduleIndex) - { - same.emplace(app); - - try - { - if (app->application_final) - { - app->application_final(); - } - } - catch (std::exception &exc) - { - std::cout << "Warning: the error of the application's finalize '" << app->server_module << "':" << exc.what() << std::endl; - } - - app->application_call = std::function(); - app->application_clear = std::function(); - app->application_init = std::function(); - app->application_final = std::function(); - } - } - - module.close(); - - auto const app = *(same.cbegin() ); - - const std::string &module_name = app->server_module; - - #ifdef WIN32 - std::ifstream src(app->server_module_update, std::ifstream::binary); - - if ( ! src) - { - std::cout << "Error: the file '" << app->server_module_update << "' cannot be open;" << std::endl; - return false; - } - - std::ofstream dst(module_name, std::ofstream::binary | std::ofstream::trunc); - - if ( ! dst) - { - std::cout << "Error: the file '" << module_name << "' cannot be open;" << std::endl; - return false; - } - - // Copy (rewrite) file - dst << src.rdbuf(); - - src.close(); - dst.close(); - - // Open updated module - module.open(module_name); - - #elif POSIX - // HACK: for posix system — load new version shared library - - const size_t dir_pos = module_name.rfind('/'); - const size_t ext_pos = module_name.rfind('.'); - - std::string module_name_temp; - - if (std::string::npos != ext_pos && (std::string::npos == dir_pos || dir_pos < ext_pos) ) - { - module_name_temp = module_name.substr(0, ext_pos) + '-' + Utils::getUniqueName() + module_name.substr(ext_pos); - } - else - { - module_name_temp = module_name + '-' + Utils::getUniqueName(); - } - - std::ifstream src(app->server_module_update, std::ifstream::binary); - - if ( ! src) - { - std::cout << "Error: the file '" << app->server_module_update << "' cannot be open;" << std::endl; - return false; - } - - std::ofstream dst(module_name_temp, std::ofstream::binary | std::ofstream::trunc); - - if ( ! dst) - { - std::cout << "Error: the file '" << module_name << "' cannot be open;" << std::endl; - return false; - } - - // Copy (rewrite) file - dst << src.rdbuf(); - - src.close(); - dst.close(); - - // Open updated module - module.open(module_name_temp); - - if (0 != remove(module_name.c_str() ) ) - { - std::cout << "Error: the file '" << module_name << "' could not be removed;" << std::endl; - return false; - } - - if (0 != rename(module_name_temp.c_str(), module_name.c_str() ) ) - { - std::cout << "Error: the file '" << module_name_temp << "' could not be renamed;" << std::endl; - return false; - } - #else - #error "Undefine platform" - #endif - - if (false == module.is_open() ) - { - std::cout << "Error: application's module '" << module_name << "' cloud not be opened;" << std::endl; - return false; - } - - void *(*addr)(void *) = nullptr; - - if (false == module.find("application_call", &addr) ) - { - std::cout << "Error: the function 'application_call' was not found in the module '" << module_name << "';" << std::endl; - return false; - } - - std::function app_call = reinterpret_cast(addr); - - if ( ! app_call) - { - std::cout << "Error: invalid function 'application_call' is in the module '" << module_name << "';" << std::endl; - return false; - } - - if (false == module.find("application_clear", &addr) ) - { - std::cout << "Error: the function 'application_clear' was not found in the module '" << module_name << "';" << std::endl; - return false; - } - - std::function app_clear = reinterpret_cast(addr); - - std::function app_init = std::function(); - - if (module.find("application_init", &addr) ) - { - app_init = reinterpret_cast(addr); - } - - std::function app_final = std::function(); - - if (module.find("application_final", &addr) ) - { - app_final = reinterpret_cast(addr); - } - - for (auto &app : same) - { - app->application_call = app_call; - app->application_clear = app_clear; - app->application_init = app_init; - app->application_final = app_final; - - try - { - if (app->application_init) - { - app->application_init(); - } - } - catch (std::exception &exc) - { - std::cout << "Warning: the error of the application's initializing '" << module_name << "':" << exc.what() << std::endl; - } - } - - return true; - } - - void Server::updateModules() - { - // Applications settings list - std::unordered_set applications; - // Get full applications settings list - this->apps_tree.collectApplicationSettings(applications); - - std::unordered_set updated; - - for (auto const &app : applications) - { - const size_t module_index = app->module_index; - - // If module is not updated (not checked) - if (updated.cend() == updated.find(module_index) ) - { - if (false == app->server_module_update.empty() && app->server_module_update != app->server_module) - { - size_t module_size_new = 0; - time_t module_time_new = 0; - - if (System::getFileSizeAndTimeGmt(app->server_module_update, &module_size_new, &module_time_new) ) - { - size_t module_size_cur = 0; - time_t module_time_cur = 0; - - Module &module = this->modules[module_index]; - - if (System::getFileSizeAndTimeGmt(app->server_module, &module_size_cur, &module_time_cur) ) - { - if (module_size_cur != module_size_new || module_time_cur < module_time_new) - { - this->updateModule(module, applications, module_index); - } - } - } - } - - updated.emplace(module_index); - } - } - - std::cout << "Notice: applications' modules have been updated;" << std::endl; - - this->process_flag = true; - this->eventUpdateModule->reset(); - } - - bool Server::init() - { - ConfigParser conf_parser; - - if (Socket::Startup() && conf_parser.loadConfig("main.conf", this->settings, this->mimes_types, this->modules, this->apps_tree) ) - { - this->addDataVariant(new DataVariantFormUrlencoded() ); - this->addDataVariant(new DataVariantMultipartFormData() ); - this->addDataVariant(new DataVariantTextPlain() ); - - ::gnutls_global_init(); - - return true; - } - - return false; - } - - void Server::clear() - { - Socket::Cleanup(); - - if (this->eventNotFullQueue) - { - delete this->eventNotFullQueue; - this->eventNotFullQueue = nullptr; - } - - if (this->eventProcessQueue) - { - delete this->eventProcessQueue; - this->eventProcessQueue = nullptr; - } - - if (this->eventUpdateModule) - { - delete this->eventUpdateModule; - this->eventUpdateModule = nullptr; - } - - if (false == this->variants.empty() ) - { - for (auto &variant : this->variants) - { - delete variant.second; - } - - this->variants.clear(); - } - - if (false == this->tls_data.empty() ) - { - for (auto &pair : this->tls_data) - { - std::tuple &data = pair.second; - - ::gnutls_certificate_free_credentials(std::get<0>(data) ); - ::gnutls_priority_deinit(std::get<1>(data) ); - } - } - - if (false == this->apps_tree.empty() ) - { - std::unordered_set applications; - this->apps_tree.collectApplicationSettings(applications); - - for (auto &app : applications) - { - try - { - if (app->application_final) - { - app->application_final(); - } - } - catch (std::exception &exc) - { - std::cout << "Warning: the error of the application's finalize '" << app->server_module << "':" << exc.what() << std::endl; - } - - delete app; - } - - applications.clear(); - this->apps_tree.clear(); - } - - if (false == this->modules.empty() ) - { - for (auto &module : this->modules) - { - module.close(); - } - - this->modules.empty(); - } - - if (false == this->settings.empty() ) - { - this->settings.clear(); - } - - ::gnutls_global_deinit(); - } - - bool Server::tlsInit(const ServerApplicationSettings &app, std::tuple &data) - { - ::gnutls_certificate_credentials_t x509_cred; - - int ret = ::gnutls_certificate_allocate_credentials(&x509_cred); - - if (ret < 0) - { - std::cout << "Error [tls]: certificate credentials has not been allocated;" << std::endl; - - return false; - } - - if (false == app.chain_file.empty() ) - { - ret = ::gnutls_certificate_set_x509_trust_file(x509_cred, app.chain_file.c_str(), GNUTLS_X509_FMT_PEM); - - if (ret < 0) - { - std::cout << "Warning [tls]: (CA) chain file has not been accepted;" << std::endl; - } - } - - if (false == app.crl_file.empty() ) - { - ret = ::gnutls_certificate_set_x509_crl_file(x509_cred, app.crl_file.c_str(), GNUTLS_X509_FMT_PEM); - - if (ret < 0) - { - std::cout << "Warning [tls]: (CLR) clr file has not been accepted;" << std::endl; - } - } - - ret = ::gnutls_certificate_set_x509_key_file(x509_cred, app.cert_file.c_str(), app.key_file.c_str(), GNUTLS_X509_FMT_PEM); - - if (ret < 0) - { - std::cout << "Error [tls]: (CERT) cert file or/and (KEY) key file has not been accepted;" << std::endl; - - return false; - } - - if (false == app.stapling_file.empty() ) - { - ret = ::gnutls_certificate_set_ocsp_status_request_file(x509_cred, app.stapling_file.c_str(), 0); - - if (ret < 0) - { - std::cout << "Warning [tls]: (OCSP) stapling file has not been accepted;" << std::endl; - } - } - - ::gnutls_dh_params_t dh_params; - - ::gnutls_dh_params_init(&dh_params); - - if (app.dh_file.empty() ) - { - const unsigned int bits = ::gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_HIGH); - - ret = ::gnutls_dh_params_generate2(dh_params, bits); - } - else - { - std::ifstream dh_file(app.dh_file); - - if (dh_file) - { - const size_t max_file_size = 1024 * 1024; - - std::vector buf(max_file_size); - - dh_file.read(buf.data(), buf.size() ); - - gnutls_datum_t datum { - reinterpret_cast(buf.data() ), - static_cast(dh_file.gcount() ) - }; - - ret = ::gnutls_dh_params_import_pkcs3(dh_params, &datum, GNUTLS_X509_FMT_PEM); - } - else - { - ret = -1; - - std::cout << "Error [tls]: DH params file has not been opened;" << std::endl;; - } - - dh_file.close(); - } - - if (ret < 0) - { - ::gnutls_certificate_free_credentials(x509_cred); - - std::cout << "Error [tls]: DH params were not loaded;" << std::endl; - - return false; - } - - ::gnutls_certificate_set_dh_params(x509_cred, dh_params); - - ::gnutls_priority_t priority_cache; - - ret = ::gnutls_priority_init(&priority_cache, "NORMAL", nullptr); - - if (ret < 0) - { - ::gnutls_certificate_free_credentials(x509_cred); - - std::cout << "Error [tls]: priority cache cannot be init;" << std::endl; - - return false; - } - - data = std::tuple{x509_cred, priority_cache}; - - return true; - } - - bool Server::tryBindPort(const int port, std::unordered_set &ports) - { - // Only unique ports - if (ports.cend() != ports.find(port) ) - { - return false; - } - - Socket sock; - - if (false == sock.open() ) - { - std::cout << "Error: the socket cannot be open; errno " << Socket::getLastError() << ";" << std::endl; - return false; - } - - if (false == sock.bind(port) ) - { - std::cout << "Error: the socket " << port << " cannot be bind; errno " << Socket::getLastError() << ";" << std::endl; - return false; - } - - if (false == sock.listen() ) - { - std::cout << "Error: the socket " << port << " cannot be listen; errno " << Socket::getLastError() << ";" << std::endl; - return false; - } - - sock.nonblock(true); - - this->server_sockets.emplace_back(std::move(sock) ); - - ports.emplace(port); - - return true; - } - - void Server::initAppsPorts() - { - // Applications settings list - std::unordered_set applications; - // Get full applications settings list - this->apps_tree.collectApplicationSettings(applications); - - // Bind ports set - std::unordered_set ports; - - // Open applications sockets - for (auto const &app : applications) - { - const std::unordered_set &tls = app->tls_ports; - - if (false == tls.empty() ) - { - std::tuple data; - - if (Server::tlsInit(*app, data) ) - { - for (const int port : tls) - { - if (this->tryBindPort(port, ports) ) - { - this->tls_data.emplace(port, data); - } - } - } - } - - const std::unordered_set &list = app->ports; - - for (const int port : list) - { - this->tryBindPort(port, ports); - } - } - } - - int Server::run() - { - if (false == this->init() ) - { - return 0x10; - } - - this->initAppsPorts(); - - if (this->server_sockets.empty() ) - { - std::cout << "Error: any socket was not open;" << std::endl; - this->clear(); - return 0x20; - } - - SocketList sockets_list; - - sockets_list.create(this->server_sockets.size() ); - - for (auto const &sock : this->server_sockets) - { - sockets_list.addSocket(sock); - } - - std::cout << "Log: launch server's cycle;" << std::endl << std::endl; - - const size_t queue_max_length = 1024; - this->eventNotFullQueue = new Event(true, true); - this->eventProcessQueue = new Event(); - this->eventUpdateModule = new Event(false, true); - - std::queue > sockets; - - this->process_flag = true; - - std::function > &)> serverCycleQueue = std::mem_fn(&Server::cycleQueue); - std::thread threadQueue(serverCycleQueue, this, std::ref(sockets) ); - - std::vector accept_sockets; - std::vector accept_sockets_address; - - // Cycle for receiving new connections - do - { - if (sockets_list.accept(accept_sockets, accept_sockets_address) ) - { - this->sockets_queue_mtx.lock(); - - for (size_t i = 0; i < accept_sockets.size(); ++i) - { - const Socket &sock = accept_sockets[i]; - - if (sock.is_open() ) - { - sock.nonblock(true); - sock.tcp_nodelay(true); - - sockets.emplace( - std::tuple { - sock, - accept_sockets_address[i] - } - ); - } - } - - this->sockets_queue_mtx.unlock(); - - this->eventProcessQueue->notify(); - - if (sockets.size() >= queue_max_length) - { - this->eventNotFullQueue->reset(); - } - - accept_sockets.clear(); - accept_sockets_address.clear(); - - this->eventNotFullQueue->wait(); - } - } - while (this->process_flag || this->eventUpdateModule->notifed() ); - - this->eventProcessQueue->notify(); - - threadQueue.join(); - - sockets_list.destroy(); - - this->clear(); - - std::cout << "Log: complete server's cycle;" << std::endl; - - return EXIT_SUCCESS; - } - - void Server::stopProcess() - { - this->process_flag = false; - - if (this->eventNotFullQueue) - { - this->eventNotFullQueue->notify(); - } - - this->setProcessQueue(); - } - - void Server::unsetProcess() - { - this->process_flag = false; - } - - void Server::setRestart() - { - this->restart_flag = true; - } - - void Server::setUpdateModule() - { - if (this->eventUpdateModule) - { - this->eventUpdateModule->notify(); - } - } - - void Server::setProcessQueue() - { - if (this->eventProcessQueue) - { - this->eventProcessQueue->notify(); - } - } - - bool Server::get_start_args(const int argc, const char *argv[], struct server_start_args *st) - { - for (int i = 1; i < argc; ++i) - { - if (0 == ::strcmp(argv[i], "--start") ) - { - - } - else if (0 == ::strcmp(argv[i], "--force") ) - { - st->force = true; - } - else if (argv[i] == ::strstr(argv[i], "--config-path=") ) - { - st->config_path = std::string(argv[i] + sizeof("--config-path=") - 1); - } - else if (argv[i] == ::strstr(argv[i], "--server-name=") ) - { - st->server_name = std::string(argv[i] + sizeof("--server-name=") - 1); - } - else - { - std::cout << "The argument '" << argv[i] << "' can't be applied with --start;" << std::endl; - - return false; - } - } - - if (st->server_name.empty() ) - { - st->server_name = argv[0]; - } - - System::filterSharedMemoryName(st->server_name); - - return true; - } - - int Server::command_start(const int argc, const char *argv[]) - { - struct server_start_args st = {}; - - if (false == Server::get_start_args(argc, argv, &st) ) - { - return 0x1; - } - - if (false == st.config_path.empty() ) - { - if (false == System::changeCurrentDirectory(st.config_path) ) - { - std::cout << "Configuration path '" << st.config_path << "' has not been found;" << std::endl; - - return 0x2; - } - } - - if (st.force) - { - SharedMemory::destroy(st.server_name); - GlobalMutex::destory(st.server_name); - } - - GlobalMutex glob_mtx; - SharedMemory glob_mem; - - bool is_exists = false; - - if (glob_mtx.open(st.server_name) ) - { - glob_mtx.lock(); - - if (glob_mem.open(st.server_name) ) - { - System::native_processid_type pid = 0; - - if (glob_mem.read(&pid, sizeof(pid) ) ) - { - is_exists = System::isProcessExists(pid); - } - } - - glob_mtx.unlock(); - } - - if (is_exists) - { - std::cout << "The server instance with the name '" << st.server_name << "' is already running;" << std::endl; - - return 0x3; - } - - if (false == glob_mtx.open(st.server_name) ) - { - if (false == glob_mtx.create(st.server_name) ) - { - std::cout << "The global mutex could not been created;" << std::endl; - - return 0x4; - } - } - - glob_mtx.lock(); - - if (false == glob_mem.open(st.server_name) ) - { - if (false == glob_mem.create(st.server_name, sizeof(System::native_processid_type) ) ) - { - glob_mtx.unlock(); - - std::cout << "The shared memory could not been allocated;" << std::endl; - - return 0x5; - } - } - - System::native_processid_type pid = System::getProcessId(); - - if (false == glob_mem.write(&pid, sizeof(pid) ) ) - { - glob_mem.destroy(); - glob_mtx.unlock(); - - std::cout << "Writing data to the shared memory has failed;" << std::endl; - - return 0x6; - } - - glob_mtx.unlock(); - - int code = EXIT_FAILURE; - - do - { - this->process_flag = false; - this->restart_flag = false; - - code = this->run(); - } - while (this->process_flag || this->restart_flag); - - glob_mem.destroy(); - glob_mtx.destory(); - - return code; - } - - System::native_processid_type Server::getServerProcessId(const std::string &serverName) - { - System::native_processid_type pid = 0; - - GlobalMutex glob_mtx; - - if (glob_mtx.open(serverName) ) - { - SharedMemory glob_mem; - - glob_mtx.lock(); - - if (glob_mem.open(serverName) ) - { - glob_mem.read(&pid, sizeof(pid) ); - } - - glob_mtx.unlock(); - } - - return pid; - } - - int Server::command_help(const int argc, const char *argv[]) const - { - std::cout << std::left << "Available arguments:" << std::endl - << std::setw(4) << ' ' << std::setw(26) << "--start" << "Start the http server" << std::endl - << std::setw(8) << ' ' << std::setw(22) << "[options]" << std::endl - << std::setw(8) << ' ' << std::setw(22) << "--force" << "Forcibly start the http server (ignore the existing instance)" << std::endl - << std::setw(8) << ' ' << std::setw(22) << "--config-path=" << "The path to the directory with configuration files" << std::endl - << std::endl - << std::setw(4) << ' ' << std::setw(26) << "--restart" << "Restart the http server" << std::endl - << std::setw(4) << ' ' << std::setw(26) << "--update-module" << "Update the applications modules" << std::endl - << std::setw(4) << ' ' << std::setw(26) << "--kill" << "Shutdown the http server" << std::endl - << std::setw(4) << ' ' << std::setw(26) << "--help" << "This help" << std::endl - << std::endl<< "Optional arguments:" << std::endl - << std::setw(4) << ' ' << std::setw(26) << "--server-name=" << "The name of the server instance" << std::endl; - - return EXIT_SUCCESS; - } - - static std::string get_server_name(const int argc, const char *argv[]) - { - std::string server_name; - - for (int i = 1; i < argc; ++i) - { - if (argv[i] == ::strstr(argv[i], "--server-name=") ) - { - server_name = std::string(argv[i] + sizeof("--server-name=") - 1); - break; - } - } - - if (server_name.empty() ) - { - server_name = argv[0]; - } - - System::filterSharedMemoryName(server_name); - - return server_name; - } - - int Server::command_restart(const int argc, const char *argv[]) const - { - const System::native_processid_type pid = Server::getServerProcessId(get_server_name(argc, argv) ); - - if (1 < pid && System::sendSignal(pid, SIGUSR1) ) - { - return EXIT_SUCCESS; - } - - return EXIT_FAILURE; - } - - int Server::command_terminate(const int argc, const char *argv[]) const - { - const System::native_processid_type pid = Server::getServerProcessId(get_server_name(argc, argv) ); - - if (1 < pid && System::sendSignal(pid, SIGTERM) ) - { - return EXIT_SUCCESS; - } - - return EXIT_FAILURE; - } - - int Server::command_update_module(const int argc, const char *argv[]) const - { - const System::native_processid_type pid = Server::getServerProcessId(get_server_name(argc, argv) ); - - if (1 < pid && System::sendSignal(pid, SIGUSR2) ) - { - return EXIT_SUCCESS; - } - - return EXIT_FAILURE; - } -}; \ No newline at end of file diff --git a/src/Server.h b/src/Server.h deleted file mode 100644 index 54f1d2d..0000000 --- a/src/Server.h +++ /dev/null @@ -1,148 +0,0 @@ -#pragma once - -#include "SocketList.h" -#include "DataVariantAbstract.h" -#include "ServerApplicationsTree.h" -#include "ServerApplicationDefaultSettings.h" -#include "Module.h" -#include "Event.h" -#include "SocketAdapter.h" -#include "ServerStructuresArguments.h" - -#include -#include -#include -#include -#include -#include - -#include - -namespace HttpServer -{ - class Server - { - protected: - std::unordered_map variants; - - std::unordered_map settings; - std::unordered_map mimes_types; - - std::unordered_map > tls_data; - - std::vector modules; - - ServerApplicationsTree apps_tree; - - std::vector server_sockets; - - Event *eventNotFullQueue; - Event *eventProcessQueue; - Event *eventUpdateModule; - - mutable std::atomic_size_t threads_working_count; - mutable std::mutex sockets_queue_mtx; - - // Флаг, означающий - активированы ли главные циклы сервера - // (с помощью этого флага можно деактивировать циклы, чтобы завершить работу сервера) - sig_atomic_t process_flag; - sig_atomic_t restart_flag; - - protected: - static const int CONNECTION_CLOSED = 0; - static const int CONNECTION_KEEP_ALIVE = 1; - static const int CONNECTION_UPGRADE = 2; - - protected: - int cycleQueue(std::queue > &sockets); - - int threadRequestProc(SocketAdapter &clientSocket, const struct sockaddr_in &clientAddr) const; - - static bool getRequest(const SocketAdapter &clientSocket, std::vector &buf, std::string &str_buf, struct request_parameters &rp); - - int getRequestHeaders(std::string &str_buf, struct request_parameters &rp) const; - - static void runApplication(const SocketAdapter &clientSocket, const ServerApplicationSettings &appSets, struct request_parameters &rp); - - int getRequestData(const SocketAdapter &clientSocket, std::string &str_buf, const ServerApplicationSettings &appSets, struct request_parameters &rp) const; - - const ServerApplicationSettings *getApplicationSettings(const struct request_parameters &rp) const; - - static void getConnectionParams(struct request_parameters &rp); - - void xSendfile(const SocketAdapter &clientSocket, struct request_parameters &rp) const; - - static bool isConnectionKeepAlive(const struct request_parameters &rp); - static bool isConnectionUpgrade(const struct request_parameters &rp); - - void threadRequestCycle(std::queue > &sockets, Event &eventThreadCycle) const; - - std::string getMimeTypeByFileName(const std::string &fileName) const; - - std::vector > getRanges( - const std::string &rangeHeader, - const size_t posSymEqual, - const size_t fileSize, - std::string &resultRangeHeader, - size_t &contentLength - ) const; - - int transferFilePart( - const SocketAdapter &clientSocket, - const std::chrono::milliseconds &timeout, - const std::string &fileName, - const time_t fileTime, - const size_t fileSize, - const std::string &rangeHeader, - const std::string &connectionHeader, - const std::string &dateHeader, - const bool headersOnly - ) const; - - int transferFile( - const SocketAdapter &clientSocket, - const std::string &fileName, - const std::string &connectionHeader, - const bool headersOnly, - struct request_parameters &rp - ) const; - - static bool parseIncomingVars(std::unordered_multimap ¶ms, const std::string &uriParams); - static void sendStatus(const SocketAdapter &clientSocket, const std::chrono::milliseconds &timeout, const size_t statusCode); - - static bool tlsInit(const ServerApplicationSettings &app, std::tuple &data); - - bool tryBindPort(const int port, std::unordered_set &ports); - void initAppsPorts(); - - bool init(); - int run(); - void clear(); - - static System::native_processid_type getServerProcessId(const std::string &serverName); - - void updateModules(); - bool updateModule(Module &module, std::unordered_set &applications, const size_t moduleIndex); - - private: - void addDataVariant(DataVariantAbstract *dataVariant); - - static bool get_start_args(const int argc, const char *argv[], struct server_start_args *st); - - public: - Server(); - ~Server() = default; - - void stopProcess(); - void unsetProcess(); - void setRestart(); - void setUpdateModule(); - void setProcessQueue(); - - int command_help(const int argc, const char *argv[]) const; - int command_start(const int argc, const char *argv[]); - int command_restart(const int argc, const char *argv[]) const; - int command_terminate(const int argc, const char *argv[]) const; - int command_update_module(const int argc, const char *argv[]) const; - }; -}; \ No newline at end of file diff --git a/src/ServerApplicationDefaultSettings.h b/src/ServerApplicationDefaultSettings.h deleted file mode 100644 index c0d59d1..0000000 --- a/src/ServerApplicationDefaultSettings.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -namespace HttpServer -{ - struct ServerApplicationDefaultSettings - { - std::string temp_dir; - size_t request_max_size; - }; -}; \ No newline at end of file diff --git a/src/ServerRequest.h b/src/ServerRequest.h deleted file mode 100644 index dc80893..0000000 --- a/src/ServerRequest.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "RawData.h" -#include "SocketAdapterTls.h" -#include "FileIncoming.h" - -#include - -namespace HttpServer -{ - struct server_request - { - const System::native_socket_type socket; - const ::gnutls_session_t tls_session; - const char *method; - const char *uri_reference; - const char *document_root; - const size_t params_count; - const Utils::raw_pair *params; - const size_t headers_count; - const Utils::raw_pair *headers; - const size_t data_count; - const Utils::raw_pair *data; - const size_t files_count; - const Utils::raw_fileinfo *files; - }; - - /** - * Структура запроса (входные данные) - * - * @member const SocketAdapter &socket - сокет клиента - * @member const std::string method - метод применяемый к ресурсу - * @member const std::string uri_reference - ссылка на ресурс - * @member const std::string document_root - корневая директория приложения - * @member const std::unordered_multimap params - параметры ресурса - * @member const std::unordered_map headers - заголовки запроса - * @member const std::unordered_multimap data - входящие данные запроса - * @member const std::unordered_multimap files - входящие файлы запроса - * @member const std::unordered_multimap cookies - входящие куки запроса - */ - struct ServerRequest - { - const SocketAdapter &socket; - const std::string method; - const std::string uri_reference; - const std::string document_root; - const std::unordered_multimap params; - const std::unordered_map headers; - const std::unordered_multimap data; - const std::unordered_multimap files; - const std::unordered_multimap cookies; - }; -}; diff --git a/src/ServerResponse.h b/src/ServerResponse.h deleted file mode 100644 index 7573e3a..0000000 --- a/src/ServerResponse.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "RawData.h" -#include "SocketAdapter.h" - -#include -#include - -namespace HttpServer -{ - struct server_response - { - System::native_socket_type socket; - size_t headers_count; - Utils::raw_pair *headers; - }; - - struct ServerResponse - { - SocketAdapter &socket; - std::map headers; - }; -}; diff --git a/src/SharedMemory.h b/src/SharedMemory.h deleted file mode 100644 index 8ea5b24..0000000 --- a/src/SharedMemory.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#ifdef WIN32 - #include - #include -#endif - -#include - -namespace HttpServer -{ - class SharedMemory - { - private: - #ifdef WIN32 - ::HANDLE shm_desc; - #elif POSIX - int shm_desc; - #else - #error "Undefine platform" - #endif - - std::string shm_name; - - public: - SharedMemory(); - ~SharedMemory(); - - bool create(const std::string &memName, const size_t memSize); - bool open(const std::string &memName); - - bool is_open() const; - - bool write(const void *data, const size_t size, const size_t offset = 0) const; - bool read(void *dest, const size_t size, const size_t offset = 0) const; - - bool close(); - bool destroy(); - - static bool destroy(const std::string &memName); - }; -}; \ No newline at end of file diff --git a/src/SignalHandlers.cpp b/src/SignalHandlers.cpp index cdc4bd4..cad0155 100644 --- a/src/SignalHandlers.cpp +++ b/src/SignalHandlers.cpp @@ -1,6 +1,6 @@  #include "SignalHandlers.h" -#include "System.h" +#include "system/System.h" #ifdef WIN32 #include @@ -17,47 +17,36 @@ static HttpServer::Server *globalServerPtr = nullptr; /** * Terminate signal */ -static void handlerSigTerm(const int) -{ - if (globalServerPtr) - { - globalServerPtr->stopProcess(); +static void handlerSigTerm(const int) noexcept { + if (globalServerPtr) { + globalServerPtr->stop(); } } /** * Interrupt signal */ -static void handlerSigInt(const int) -{ - if (globalServerPtr) - { - globalServerPtr->stopProcess(); +static void handlerSigInt(const int) noexcept { + if (globalServerPtr) { + globalServerPtr->stop(); } } /** * Signal to restart */ -static void handlerSigUsr1(const int) -{ - if (globalServerPtr) - { - globalServerPtr->setRestart(); - globalServerPtr->stopProcess(); +static void handlerSigUsr1(const int) noexcept { + if (globalServerPtr) { + globalServerPtr->restart(); } } /** * Signal to update modules */ -static void handlerSigUsr2(const int) -{ - if (globalServerPtr) - { - globalServerPtr->setUpdateModule(); - globalServerPtr->unsetProcess(); - globalServerPtr->setProcessQueue(); +static void handlerSigUsr2(const int) noexcept { + if (globalServerPtr) { + globalServerPtr->update(); } } @@ -67,55 +56,52 @@ static void handlerSigUsr2(const int) * It doesn't work in case the program was launched and was * attempted to finish under different remote sessions. */ -static ::LRESULT CALLBACK WndProc(const ::HWND hWnd, const ::UINT message, const ::WPARAM wParam, const ::LPARAM lParam) -{ +static ::LRESULT CALLBACK WndProc( + const ::HWND hWnd, + const ::UINT message, + const ::WPARAM wParam, + const ::LPARAM lParam +) noexcept { switch (message) { - case SIGTERM: - { + case SIGTERM: { handlerSigTerm(message); ::PostMessage(hWnd, WM_QUIT, 0, 0); // Fuck ::PostQuitMessage(0); break; } - case SIGINT: - { + case SIGINT: { handlerSigInt(message); ::PostMessage(hWnd, WM_QUIT, 0, 0); // Fuck ::PostQuitMessage(0); break; } - case SIGUSR1: - { + case SIGUSR1: { handlerSigUsr1(message); break; } - case SIGUSR2: - { + case SIGUSR2: { handlerSigUsr2(message); break; } // Cases WM_QUERYENDSESSION and WM_ENDSESSION run before shutting down the system (or ending user session) - case WM_QUERYENDSESSION: - { + case WM_QUERYENDSESSION: { handlerSigTerm(message); break; } - case WM_ENDSESSION: - { - ::HANDLE hThread = ::OpenThread(SYNCHRONIZE, false, gMainThreadId); + case WM_ENDSESSION: { + const ::HANDLE hThread = ::OpenThread(SYNCHRONIZE, false, gMainThreadId); ::WaitForSingleObject(hThread, INFINITE); ::CloseHandle(hThread); break; } - default: - { + default: { return ::DefWindowProc(hWnd, message, wParam, lParam); } } @@ -123,21 +109,33 @@ static ::LRESULT CALLBACK WndProc(const ::HWND hWnd, const ::UINT message, const return 0; } -static ::WPARAM mainMessageLoop(const ::HINSTANCE hInstance, HttpServer::Event * const eventWindowCreation) -{ - const ::HWND hWnd = ::CreateWindow(myWndClassName, nullptr, 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, nullptr, nullptr, hInstance, nullptr); +static ::WPARAM mainMessageLoop( + const ::HINSTANCE hInstance, + Utils::Event * const eventWindowCreation +) noexcept { + const ::HWND hWnd = ::CreateWindow( + myWndClassName, + nullptr, + 0, + CW_USEDEFAULT, + CW_USEDEFAULT, + 0, + 0, + nullptr, + nullptr, + hInstance, + nullptr + ); eventWindowCreation->notify(); // After this action, eventWindowCreation will be destroyed (in the other thread) - if (0 == hWnd) - { + if (0 == hWnd) { return 0; } ::MSG msg; - while (::GetMessage(&msg, hWnd, 0, 0) ) - { + while (::GetMessage(&msg, hWnd, 0, 0) ) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } @@ -146,7 +144,7 @@ static ::WPARAM mainMessageLoop(const ::HINSTANCE hInstance, HttpServer::Event * } #ifdef _CONSOLE -static ::BOOL consoleSignalHandler(const ::DWORD ctrlType) +static ::BOOL consoleSignalHandler(const ::DWORD ctrlType) noexcept { switch (ctrlType) { @@ -155,18 +153,18 @@ static ::BOOL consoleSignalHandler(const ::DWORD ctrlType) // @see my function WndProc -> cases WM_QUERYENDSESSION and WM_ENDSESSION. Only they happen in this program, because the library user32.dll is connected. // @prooflink: https://msdn.microsoft.com/library/windows/desktop/ms686016(v=vs.85).aspx case CTRL_LOGOFF_EVENT: - case CTRL_SHUTDOWN_EVENT: - { + case CTRL_SHUTDOWN_EVENT: { handlerSigTerm(ctrlType); - ::HANDLE hThread = ::OpenThread(SYNCHRONIZE, false, gMainThreadId); + const ::HANDLE hThread = ::OpenThread(SYNCHRONIZE, false, gMainThreadId); ::WaitForSingleObject(hThread, INFINITE); ::CloseHandle(hThread); return true; } - case CTRL_C_EVENT: + case CTRL_C_EVENT: { handlerSigInt(ctrlType); return true; + } default: return false; @@ -175,7 +173,7 @@ static ::BOOL consoleSignalHandler(const ::DWORD ctrlType) #endif // _CONSOLE #endif // WIN32 -bool bindSignalHandlers(HttpServer::Server *server) +bool bindSignalHandlers(HttpServer::Server *server) noexcept { globalServerPtr = server; @@ -190,19 +188,18 @@ bool bindSignalHandlers(HttpServer::Server *server) const ::HINSTANCE hInstance = ::GetModuleHandle(nullptr); - ::WNDCLASSEX wcex = {}; + ::WNDCLASSEX wcex {}; wcex.cbSize = sizeof(::WNDCLASSEX); wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.lpszClassName = myWndClassName; - if (0 == ::RegisterClassEx(&wcex) ) - { + if (::RegisterClassEx(&wcex) == 0) { return false; } - HttpServer::Event eventWindowCreation; + Utils::Event eventWindowCreation; gMainThreadId = ::GetCurrentThreadId(); gThreadMessageLoop = std::thread(mainMessageLoop, hInstance, &eventWindowCreation); @@ -211,7 +208,7 @@ bool bindSignalHandlers(HttpServer::Server *server) #elif POSIX - struct ::sigaction act = {}; + struct ::sigaction act {}; act.sa_handler = handlerSigInt; ::sigaction(SIGINT, &act, nullptr); @@ -228,16 +225,16 @@ bool bindSignalHandlers(HttpServer::Server *server) ::signal(SIGPIPE, SIG_IGN); #else - #error "Undefine platform" + #error "Undefined platform" #endif return true; } -void stopSignalHandlers() +void stopSignalHandlers() noexcept { #ifdef WIN32 System::sendSignal(::GetCurrentProcessId(), SIGINT); gThreadMessageLoop.join(); #endif -} \ No newline at end of file +} diff --git a/src/SignalHandlers.h b/src/SignalHandlers.h index 17bcc1e..3db4da3 100644 --- a/src/SignalHandlers.h +++ b/src/SignalHandlers.h @@ -1,7 +1,7 @@ #pragma once -#include "Server.h" +#include "server/Server.h" -bool bindSignalHandlers(HttpServer::Server *server); +bool bindSignalHandlers(HttpServer::Server *server) noexcept; -void stopSignalHandlers(); \ No newline at end of file +void stopSignalHandlers() noexcept; diff --git a/src/Socket.cpp b/src/Socket.cpp deleted file mode 100644 index 158e2d0..0000000 --- a/src/Socket.cpp +++ /dev/null @@ -1,446 +0,0 @@ - -#include "Socket.h" - -#ifdef POSIX - #include - #include - #include - #include - #include - #include - #include -#endif - -namespace HttpServer -{ - bool Socket::Startup() - { - #ifdef WIN32 - unsigned short version = MAKEWORD(2, 2); - ::WSADATA wsaData = {0}; - return 0 == ::WSAStartup(version, &wsaData); - #elif POSIX - return true; - #else - #error "Undefine platform" - #endif - } - - bool Socket::Cleanup() - { - #ifdef WIN32 - return 0 == ::WSACleanup(); - #elif POSIX - return true; - #else - #error "Undefine platform" - #endif - } - - int Socket::getLastError() - { - #ifdef WIN32 - return ::WSAGetLastError(); - #elif POSIX - return errno; - #else - #error "Undefine platform" - #endif - } - - Socket::Socket(): socket_handle(~0) - { - - } - - Socket::Socket(const System::native_socket_type fd) : socket_handle(fd) - { - - } - - Socket::Socket(const Socket &obj) : socket_handle(obj.socket_handle) - { - - } - - Socket::Socket(Socket &&obj) : socket_handle(obj.socket_handle) - { - obj.socket_handle = ~0; - } - - bool Socket::open() - { - this->close(); - - this->socket_handle = ::socket(AF_INET, SOCK_STREAM, 0); - - return this->is_open(); - } - - bool Socket::close() - { - if (this->is_open() ) - { - #ifdef WIN32 - const int result = ::closesocket(this->socket_handle); - #elif POSIX - const int result = ::close(this->socket_handle); - #else - #error "Undefine platform" - #endif - - if (0 == result) - { - this->socket_handle = ~0; - - return true; - } - } - - return false; - } - - bool Socket::is_open() const - { - #ifdef WIN32 - return INVALID_SOCKET != this->socket_handle; - #elif POSIX - return ~0 != this->socket_handle; - #else - #error "Undefine platform" - #endif - } - - System::native_socket_type Socket::get_handle() const - { - return this->socket_handle; - } - - bool Socket::bind(const int port) const - { - const ::sockaddr_in sock_addr = { - AF_INET, - htons(port), - ::htonl(INADDR_ANY), - 0 - }; - - return 0 == ::bind(this->socket_handle, reinterpret_cast(&sock_addr), sizeof(sockaddr_in) ); - } - - bool Socket::listen() const - { - return 0 == ::listen(this->socket_handle, SOMAXCONN); - } - - Socket Socket::accept() const - { - #ifdef WIN32 - System::native_socket_type client_socket = ::accept(this->socket_handle, static_cast(nullptr), static_cast(nullptr) ); - #elif POSIX - System::native_socket_type client_socket = ::accept(this->socket_handle, static_cast(nullptr), static_cast(nullptr) ); - #else - #error "Undefine platform" - #endif - return Socket(client_socket); - } - - Socket Socket::nonblock_accept() const - { - System::native_socket_type client_socket = ~0; - #ifdef WIN32 - WSAPOLLFD event = { - this->socket_handle, - POLLRDNORM, - 0 - }; - - if (1 == ::WSAPoll(&event, 1, ~0) && event.revents & POLLRDNORM) - { - client_socket = ::accept(this->socket_handle, static_cast(nullptr), static_cast(nullptr) ); - } - #elif POSIX - struct ::pollfd event = { - this->socket_handle, - POLLIN, - 0 - }; - - if (1 == ::poll(&event, 1, ~0) && event.revents & POLLIN) - { - client_socket = ::accept(this->socket_handle, static_cast(nullptr), static_cast(nullptr) ); - } - #else - #error "Undefine platform" - #endif - return Socket(client_socket); - } - - Socket Socket::nonblock_accept(const std::chrono::milliseconds &timeout) const - { - System::native_socket_type client_socket = ~0; - #ifdef WIN32 - WSAPOLLFD event = { - this->socket_handle, - POLLRDNORM, - 0 - }; - - if (1 == ::WSAPoll(&event, 1, static_cast<::INT>(timeout.count() ) ) && event.revents & POLLRDNORM) - { - client_socket = ::accept(this->socket_handle, static_cast(nullptr), static_cast(nullptr) ); - } - #elif POSIX - struct ::pollfd event = { - this->socket_handle, - POLLIN, - 0 - }; - - if (1 == ::poll(&event, 1, timeout.count() ) && event.revents & POLLIN) - { - client_socket = ::accept(this->socket_handle, static_cast(nullptr), static_cast(nullptr) ); - } - #else - #error "Undefine platform" - #endif - return Socket(client_socket); - } - - bool Socket::shutdown() const - { - if (is_open() ) - { - #ifdef WIN32 - return 0 == ::shutdown(this->socket_handle, SD_BOTH); - #elif POSIX - return 0 == ::shutdown(this->socket_handle, SHUT_RDWR); - #else - #error "Undefine platform" - #endif - } - - return false; - } - - bool Socket::nonblock(const bool isNonBlock) const - { - #ifdef WIN32 - unsigned long value = isNonBlock; - return 0 == ::ioctlsocket(this->socket_handle, FIONBIO, &value); - #elif POSIX - return ~0 != ::fcntl(this->socket_handle, F_SETFL, isNonBlock ? O_NONBLOCK : O_SYNC); - #else - #error "Undefine platform" - #endif - } - -/* - bool Socket::is_nonblock() const - { - #ifdef WIN32 - - #elif POSIX - const int flags = ::fcntl(socket_handle, F_GETFL, 0); - return (flags != ~0) && (flags & O_NONBLOCK); - #else - #error "Undefine platform" - #endif - } -*/ - - bool Socket::tcp_nodelay(const bool nodelay) const - { - #ifdef WIN32 - int flags = nodelay ? 1 : 0; - return 0 == setsockopt(this->socket_handle, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&flags), sizeof(flags) ); - #elif POSIX - int flags = nodelay ? 1 : 0; - return 0 == setsockopt(this->socket_handle, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags) ); - #else - #error "Undefine platform" - #endif - } - - long Socket::recv(std::vector &buf) const - { - #ifdef WIN32 - return ::recv(this->socket_handle, buf.data(), static_cast(buf.size() ), 0); - #elif POSIX - return ::recv(this->socket_handle, buf.data(), buf.size(), 0); - #else - #error "Undefine platform" - #endif - } - - long Socket::nonblock_recv(std::vector &buf, const std::chrono::milliseconds &timeout) const - { - long recv_len = ~0; - #ifdef WIN32 - WSAPOLLFD event = { - this->socket_handle, - POLLRDNORM, - 0 - }; - - if (1 == ::WSAPoll(&event, 1, static_cast<::INT>(timeout.count() ) ) && event.revents & POLLRDNORM) - { - recv_len = ::recv(this->socket_handle, buf.data(), static_cast(buf.size() ), 0); - } - #elif POSIX - struct ::pollfd event = { - this->socket_handle, - POLLIN, - 0 - }; - - if (1 == ::poll(&event, 1, timeout.count() ) && event.revents & POLLIN) - { - recv_len = ::recv(this->socket_handle, buf.data(), buf.size(), 0); - } - #else - #error "Undefine platform" - #endif - return recv_len; - } - - static long send_all(const System::native_socket_type socket_handle, const void *data, const size_t length) - { - size_t total = 0; - - while (total < length) - { - const long send_size = ::send(socket_handle, reinterpret_cast(data) + total, length - total, 0); - - if (send_size < 0) - { - return send_size; - } - - total += send_size; - } - - return static_cast(total); - } - - long Socket::send(const std::string &buf) const - { - return send_all(this->socket_handle, buf.data(), buf.length() ); - } - - long Socket::send(const std::vector &buf, const size_t length) const - { - return send_all(this->socket_handle, buf.data(), length); - } - - static long nonblock_send_all(const System::native_socket_type socket_handle, const void *data, const size_t length, const std::chrono::milliseconds &timeout) - { - size_t total = 0; - - #ifdef WIN32 - WSAPOLLFD event = { - socket_handle, - POLLWRNORM, - 0 - }; - - while (total < length) - { - if (1 == ::WSAPoll(&event, 1, static_cast<::INT>(timeout.count() ) ) && event.revents & POLLWRNORM) - { - const long send_size = ::send(socket_handle, reinterpret_cast(data) + total, static_cast(length - total), 0); - - if (send_size < 0) - { - return send_size; - } - - total += send_size; - } - else - { - return -1; - } - } - - #elif POSIX - struct ::pollfd event = { - socket_handle, - POLLOUT, - 0 - }; - - while (total < length) - { - if (1 == ::poll(&event, 1, timeout.count() ) && event.revents & POLLOUT) - { - const long send_size = ::send(socket_handle, reinterpret_cast(data) + total, length - total, 0); - - if (send_size < 0) - { - return send_size; - } - - total += send_size; - } - else - { - return -1; - } - } - #else - #error "Undefine platform" - #endif - - return static_cast(total); - } - - long Socket::nonblock_send(const std::string &buf, const std::chrono::milliseconds &timeout) const - { - return nonblock_send_all(this->socket_handle, buf.data(), buf.length(), timeout); - } - - long Socket::nonblock_send(const std::vector &buf, const size_t length, const std::chrono::milliseconds &timeout) const - { - return nonblock_send_all(this->socket_handle, buf.data(), length, timeout); - } - - void Socket::nonblock_send_sync() const - { - #ifdef WIN32 - WSAPOLLFD event = { - this->socket_handle, - POLLWRNORM, - 0 - }; - - ::WSAPoll(&event, 1, ~0); - #elif POSIX - struct ::pollfd event = { - this->socket_handle, - POLLOUT, - 0 - }; - - ::poll(&event, 1, ~0); - #else - #error "Undefine platform" - #endif - } - - Socket &Socket::operator=(const Socket &obj) - { - this->socket_handle = obj.socket_handle; - return *this; - } - - bool Socket::operator ==(const Socket &obj) const - { - return this->socket_handle == obj.socket_handle; - } - - bool Socket::operator !=(const Socket &obj) const - { - return this->socket_handle != obj.socket_handle; - } -}; \ No newline at end of file diff --git a/src/Socket.h b/src/Socket.h deleted file mode 100644 index 738abc8..0000000 --- a/src/Socket.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include "System.h" - -#include -#include -#include - -namespace HttpServer -{ - class Socket - { - protected: - System::native_socket_type socket_handle; - - public: - bool static Startup(); - bool static Cleanup(); - int static getLastError(); - - public: - Socket(); - Socket(const System::native_socket_type fd); - Socket(const Socket &obj); - Socket(Socket &&obj); - - ~Socket() = default; - - bool open(); - bool close(); - - bool is_open() const; - System::native_socket_type get_handle() const; - - bool bind(const int port) const; - bool listen() const; - - Socket accept() const; - Socket nonblock_accept() const; - Socket nonblock_accept(const std::chrono::milliseconds &timeout) const; - - bool shutdown() const; - - bool nonblock(const bool isNonBlock = true) const; - // bool is_nonblock() const; - bool tcp_nodelay(const bool nodelay = true) const; - - long recv(std::vector &buf) const; - long nonblock_recv(std::vector &buf, const std::chrono::milliseconds &timeout) const; - - long send(const std::string &buf) const; - long send(const std::vector &buf, const size_t length) const; - - long nonblock_send(const std::string &buf, const std::chrono::milliseconds &timeout) const; - long nonblock_send(const std::vector &buf, const size_t length, const std::chrono::milliseconds &timeout) const; - - void nonblock_send_sync() const; - - Socket &operator =(const Socket &obj); - - bool operator ==(const Socket &obj) const; - bool operator !=(const Socket &obj) const; - }; -}; - -namespace std -{ - // Hash for Socket - template<> struct hash - { - std::size_t operator()(const HttpServer::Socket &obj) const - { - return std::hash{}(obj.get_handle() ); - } - }; -}; \ No newline at end of file diff --git a/src/SocketAdapter.cpp b/src/SocketAdapter.cpp deleted file mode 100644 index 5354cb0..0000000 --- a/src/SocketAdapter.cpp +++ /dev/null @@ -1,15 +0,0 @@ - -#include "SocketAdapter.h" - -namespace HttpServer -{ - bool SocketAdapter::operator ==(const SocketAdapter &obj) const - { - return this->get_handle() == obj.get_handle(); - } - - bool SocketAdapter::operator !=(const SocketAdapter &obj) const - { - return this->get_handle() != obj.get_handle(); - } -}; \ No newline at end of file diff --git a/src/SocketAdapter.h b/src/SocketAdapter.h deleted file mode 100644 index 690e668..0000000 --- a/src/SocketAdapter.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "System.h" - -#include -#include -#include - -#include - -namespace HttpServer -{ - class SocketAdapter - { - public: - virtual ~SocketAdapter() = default; - - virtual System::native_socket_type get_handle() const = 0; - virtual ::gnutls_session_t get_tls_session() const = 0; - virtual SocketAdapter *copy() const = 0; - - virtual long nonblock_recv(std::vector &buf, const std::chrono::milliseconds &timeout) const = 0; - - virtual long nonblock_send(const std::string &buf, const std::chrono::milliseconds &timeout) const = 0; - virtual long nonblock_send(const std::vector &buf, const size_t length, const std::chrono::milliseconds &timeout) const = 0; - - virtual void close() = 0; - - bool operator ==(const SocketAdapter &obj) const; - bool operator !=(const SocketAdapter &obj) const; - }; -}; \ No newline at end of file diff --git a/src/SocketAdapterDefault.cpp b/src/SocketAdapterDefault.cpp deleted file mode 100644 index 12fffd4..0000000 --- a/src/SocketAdapterDefault.cpp +++ /dev/null @@ -1,49 +0,0 @@ - -#include "SocketAdapterDefault.h" - -namespace HttpServer -{ - SocketAdapterDefault::SocketAdapterDefault(const Socket &_sock) : sock(_sock) - { - - } - - System::native_socket_type SocketAdapterDefault::get_handle() const - { - return sock.get_handle(); - } - - ::gnutls_session_t SocketAdapterDefault::get_tls_session() const - { - return 0; - } - - SocketAdapter *SocketAdapterDefault::copy() const - { - return new SocketAdapterDefault(this->sock); - } - - long SocketAdapterDefault::nonblock_recv(std::vector &buf, const std::chrono::milliseconds &timeout) const - { - return sock.nonblock_recv(buf, timeout); - } - - long SocketAdapterDefault::nonblock_send(const std::string &buf, const std::chrono::milliseconds &timeout) const - { - return sock.nonblock_send(buf, timeout); - } - - long SocketAdapterDefault::nonblock_send(const std::vector &buf, const size_t length, const std::chrono::milliseconds &timeout) const - { - return sock.nonblock_send(buf, length, timeout); - } - - void SocketAdapterDefault::close() - { - // Wait for send all data to client - sock.nonblock_send_sync(); - - sock.shutdown(); - sock.close(); - } -}; \ No newline at end of file diff --git a/src/SocketAdapterDefault.h b/src/SocketAdapterDefault.h deleted file mode 100644 index e3272be..0000000 --- a/src/SocketAdapterDefault.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "SocketAdapter.h" -#include "Socket.h" - -namespace HttpServer -{ - class SocketAdapterDefault : public SocketAdapter - { - private: - Socket sock; - - public: - SocketAdapterDefault() = delete; - SocketAdapterDefault(const Socket &_sock); - - virtual System::native_socket_type get_handle() const override; - virtual ::gnutls_session_t get_tls_session() const override; - virtual SocketAdapter *copy() const override; - - virtual long nonblock_recv(std::vector &buf, const std::chrono::milliseconds &timeout) const override; - - virtual long nonblock_send(const std::string &buf, const std::chrono::milliseconds &timeout) const override; - virtual long nonblock_send(const std::vector &buf, const size_t length, const std::chrono::milliseconds &timeout) const override; - - virtual void close() override; - }; -}; \ No newline at end of file diff --git a/src/SocketAdapterTls.cpp b/src/SocketAdapterTls.cpp deleted file mode 100644 index 2860db7..0000000 --- a/src/SocketAdapterTls.cpp +++ /dev/null @@ -1,122 +0,0 @@ - -#include "SocketAdapterTls.h" - -namespace HttpServer -{ - SocketAdapterTls::SocketAdapterTls(const Socket &sock, ::gnutls_priority_t priority_cache, ::gnutls_certificate_credentials_t x509_cred) - { - ::gnutls_init(&this->session, GNUTLS_SERVER); - ::gnutls_priority_set(this->session, priority_cache); - ::gnutls_credentials_set(this->session, GNUTLS_CRD_CERTIFICATE, x509_cred); - - ::gnutls_certificate_server_set_request(this->session, GNUTLS_CERT_IGNORE); - - ::gnutls_transport_set_int2(this->session, sock.get_handle(), sock.get_handle() ); - } - - SocketAdapterTls::SocketAdapterTls(const ::gnutls_session_t _session) : session(_session) - { - - } - - bool SocketAdapterTls::handshake() - { - int ret; - - do - { - ret = ::gnutls_handshake(this->session); - } - while (ret < 0 && ::gnutls_error_is_fatal(ret) == 0); - - if (ret < 0) - { - Socket sock(this->get_handle() ); - - sock.close(); - ::gnutls_deinit(this->session); - - return false; - } - - return true; - } - - long SocketAdapterTls::nonblock_send_all(const void *buf, const size_t length, const std::chrono::milliseconds &timeout) const - { - size_t record_size = ::gnutls_record_get_max_size(this->session); - - if (0 == record_size) - { - return -1; - } - - size_t total = 0; - - while (total < length) - { - ::gnutls_record_set_timeout(this->session, static_cast(timeout.count() ) ); - - if (record_size > length - total) - { - record_size = length - total; - } - - const long send_size = ::gnutls_record_send(this->session, reinterpret_cast(buf) + total, record_size); - - if (send_size < 0) - { - return send_size; - } - - total += send_size; - } - - return static_cast(total); - } - - System::native_socket_type SocketAdapterTls::get_handle() const - { - return static_cast(::gnutls_transport_get_int(this->session) ); - } - - ::gnutls_session_t SocketAdapterTls::get_tls_session() const - { - return this->session; - } - - SocketAdapter *SocketAdapterTls::copy() const - { - return new SocketAdapterTls(this->session); - } - - long SocketAdapterTls::nonblock_recv(std::vector &buf, const std::chrono::milliseconds &timeout) const - { - ::gnutls_record_set_timeout(this->session, static_cast(timeout.count() ) ); - return ::gnutls_record_recv(this->session, buf.data(), buf.size() ); - } - - long SocketAdapterTls::nonblock_send(const std::string &buf, const std::chrono::milliseconds &timeout) const - { - return this->nonblock_send_all(buf.data(), buf.length(), timeout); - } - - long SocketAdapterTls::nonblock_send(const std::vector &buf, const size_t length, const std::chrono::milliseconds &timeout) const - { - return this->nonblock_send_all(buf.data(), length, timeout); - } - - void SocketAdapterTls::close() - { - Socket sock(this->get_handle() ); - - // Wait for send all data to client - sock.nonblock_send_sync(); - - ::gnutls_bye(this->session, GNUTLS_SHUT_RDWR); - - sock.close(); - - ::gnutls_deinit(this->session); - } -}; \ No newline at end of file diff --git a/src/SocketAdapterTls.h b/src/SocketAdapterTls.h deleted file mode 100644 index de57147..0000000 --- a/src/SocketAdapterTls.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "SocketAdapter.h" -#include "Socket.h" - -namespace HttpServer -{ - class SocketAdapterTls : public SocketAdapter - { - private: - ::gnutls_session_t session; - - public: - SocketAdapterTls() = delete; - SocketAdapterTls(const Socket &sock, ::gnutls_priority_t priority_cache, ::gnutls_certificate_credentials_t x509_cred); - SocketAdapterTls(const ::gnutls_session_t _session); - - bool handshake(); - long nonblock_send_all(const void *buf, const size_t length, const std::chrono::milliseconds &timeout) const; - - virtual System::native_socket_type get_handle() const override; - virtual ::gnutls_session_t get_tls_session() const override; - virtual SocketAdapter *copy() const override; - - virtual long nonblock_recv(std::vector &buf, const std::chrono::milliseconds &timeout) const override; - - virtual long nonblock_send(const std::string &buf, const std::chrono::milliseconds &timeout) const override; - virtual long nonblock_send(const std::vector &buf, const size_t length, const std::chrono::milliseconds &timeout) const override; - - virtual void close() override; - }; -}; \ No newline at end of file diff --git a/src/SocketList.cpp b/src/SocketList.cpp deleted file mode 100644 index e016ed8..0000000 --- a/src/SocketList.cpp +++ /dev/null @@ -1,364 +0,0 @@ - -#include "SocketList.h" - -#ifdef POSIX - #include - #include - #include -#endif - -namespace HttpServer -{ - SocketList::SocketList() - { - #ifdef WIN32 - this->obj_list = INVALID_HANDLE_VALUE; - #elif POSIX - this->obj_list = ~0; - #else - #error "Undefine platform" - #endif - } - - bool SocketList::create(const size_t startListSize) - { - destroy(); - - #ifdef WIN32 - this->obj_list = (HANDLE) 1; - - if (startListSize > 0) - { - this->poll_events.reserve(startListSize); - } - - return true; - #elif POSIX - this->obj_list = ::epoll_create(startListSize); - - if (this->obj_list == ~0) - { - return false; - } - - if (startListSize > 0) - { - this->epoll_events.reserve(startListSize); - } - - return true; - #else - #error "Undefine platform" - #endif - } - - void SocketList::destroy() - { - if (is_created() ) - { - #ifdef WIN32 - this->obj_list = INVALID_HANDLE_VALUE; - this->poll_events.clear(); - #elif POSIX - ::close(this->obj_list); - this->obj_list = ~0; - this->epoll_events.clear(); - #else - #error "Undefine platform" - #endif - } - } - - bool SocketList::is_created() const - { - #ifdef WIN32 - return this->obj_list != INVALID_HANDLE_VALUE; - #elif POSIX - return this->obj_list != ~0; - #else - #error "Undefine platform" - #endif - } - - bool SocketList::addSocket(const Socket &sock) - { - if (false == is_created() ) - { - return false; - } - - #ifdef WIN32 - WSAPOLLFD event = { - sock.get_handle(), - POLLRDNORM, - 0 - }; - - poll_events.emplace_back(event); - - return true; - #elif POSIX - struct ::epoll_event event = { - EPOLLIN | EPOLLET | EPOLLRDHUP, - reinterpret_cast(sock.get_handle() ) - }; - - const int result = ::epoll_ctl(this->obj_list, EPOLL_CTL_ADD, sock.get_handle(), &event); - - if (result == ~0) - { - return false; - } - - this->epoll_events.emplace_back(); - - return true; - #else - #error "Undefine platform" - #endif - } - - bool SocketList::removeSocket(const Socket &sock) - { - if (false == is_created() ) - { - return false; - } - - #ifdef WIN32 - for (size_t i = 0; i < poll_events.size(); ++i) - { - if (sock.get_handle() == poll_events[i].fd) - { - poll_events.erase(poll_events.begin() + i); - return true; - } - } - - return false; - #elif POSIX - const int result = ::epoll_ctl(this->obj_list, EPOLL_CTL_DEL, sock.get_handle(), nullptr); - - if (result == ~0) - { - return false; - } - - this->epoll_events.pop_back(); - - return true; - #else - #error "Undefine platform" - #endif - } - - bool SocketList::accept(std::vector &sockets) const - { - if (is_created() ) - { - #ifdef WIN32 - const int count = ::WSAPoll(this->poll_events.data(), static_cast<::ULONG>(this->poll_events.size() ), ~0); - - if (SOCKET_ERROR == count) - { - return false; - } - - for (size_t i = 0; i < this->poll_events.size(); ++i) - { - const WSAPOLLFD &event = this->poll_events[i]; - - if (event.revents & POLLRDNORM) - { - System::native_socket_type client_socket = ~0; - - do - { - client_socket = ::accept(event.fd, static_cast(nullptr), static_cast(nullptr) ); - - if (~0 != client_socket) - { - sockets.emplace_back(Socket(client_socket) ); - } - } - while (~0 != client_socket); - } - } - - return false == sockets.empty(); - #elif POSIX - const int count = ::epoll_wait(this->obj_list, this->epoll_events.data(), this->epoll_events.size(), ~0); - - if (count == ~0) - { - return false; - } - - for (int i = 0; i < count; ++i) - { - const epoll_event &event = this->epoll_events[i]; - - if (event.events & EPOLLIN) - { - System::native_socket_type client_socket = ~0; - - do - { - client_socket = ::accept(event.data.fd, static_cast(nullptr), static_cast(nullptr) ); - - if (~0 != client_socket) - { - sockets.emplace_back(Socket(client_socket) ); - } - } - while (~0 != client_socket); - } - } - - return false == sockets.empty(); - #else - #error "Undefine platform" - #endif - } - - return false; - } - - bool SocketList::accept(std::vector &sockets, std::vector &socketsAddress) const - { - if (is_created() ) - { - #ifdef WIN32 - const int count = ::WSAPoll(this->poll_events.data(), static_cast<::ULONG>(this->poll_events.size() ), ~0); - - if (SOCKET_ERROR == count) - { - return false; - } - - for (size_t i = 0; i < this->poll_events.size(); ++i) - { - const WSAPOLLFD &event = this->poll_events[i]; - - if (event.revents & POLLRDNORM) - { - System::native_socket_type client_socket = ~0; - - do - { - struct ::sockaddr_in client_addr = {}; - socklen_t client_addr_len = sizeof(client_addr); - - client_socket = ::accept(event.fd, reinterpret_cast(&client_addr), &client_addr_len); - - if (~0 != client_socket) - { - sockets.emplace_back(Socket(client_socket) ); - socketsAddress.emplace_back(client_addr); - } - } - while (~0 != client_socket); - } - } - - return false == sockets.empty(); - #elif POSIX - const int count = ::epoll_wait(this->obj_list, this->epoll_events.data(), this->epoll_events.size(), ~0); - - if (count == ~0) - { - return false; - } - - for (int i = 0; i < count; ++i) - { - const epoll_event &event = this->epoll_events[i]; - - if (event.events & EPOLLIN) - { - System::native_socket_type client_socket = ~0; - - do - { - struct ::sockaddr_in client_addr = {}; - socklen_t client_addr_len = sizeof(client_addr); - - client_socket = ::accept(event.data.fd, reinterpret_cast(&client_addr), &client_addr_len); - - if (~0 != client_socket) - { - sockets.emplace_back(Socket(client_socket) ); - socketsAddress.emplace_back(client_addr); - } - } - while (~0 != client_socket); - } - } - - return false == sockets.empty(); - #else - #error "Undefine platform" - #endif - } - - return false; - } - - bool SocketList::recv(std::vector &sockets, std::vector &disconnected, std::chrono::milliseconds timeout) const - { - if (false == is_created() ) - { - return false; - } - - #ifdef WIN32 - const int count = ::WSAPoll(this->poll_events.data(), static_cast<::ULONG>(this->poll_events.size() ), static_cast<::INT>(timeout.count() ) ); - - if (SOCKET_ERROR == count) - { - return false; - } - - for (size_t i = 0; i < this->poll_events.size(); ++i) - { - const WSAPOLLFD &event = this->poll_events[i]; - - if (event.revents & POLLRDNORM) - { - sockets.emplace_back(Socket(event.fd) ); - } - else if (event.revents & POLLHUP) - { - disconnected.emplace_back(Socket(event.fd) ); - } - } - - return false == sockets.empty() || false == disconnected.empty(); - #elif POSIX - const int count = ::epoll_wait(this->obj_list, this->epoll_events.data(), this->epoll_events.size(), timeout.count() ); - - if (count == ~0) - { - return false; - } - - for (int i = 0; i < count; ++i) - { - const epoll_event &event = this->epoll_events[i]; - - if (event.events & EPOLLIN) - { - sockets.emplace_back(Socket(event.data.fd) ); - } - else if (event.events & EPOLLRDHUP) - { - disconnected.emplace_back(Socket(event.data.fd) ); - } - } - - return false == sockets.empty() || false == disconnected.empty(); - #else - #error "Undefine platform" - #endif - } -}; \ No newline at end of file diff --git a/src/SocketList.h b/src/SocketList.h deleted file mode 100644 index 06c8d56..0000000 --- a/src/SocketList.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "Socket.h" - -#ifdef POSIX - #include - #include -#endif - -namespace HttpServer -{ - class SocketList - { - protected: - #ifdef WIN32 - HANDLE obj_list; - mutable std::vector poll_events; - #elif POSIX - int obj_list; - mutable std::vector epoll_events; - #else - #error "Undefine platform" - #endif - - public: - SocketList(); - - bool create(const size_t startListSize = 1); - void destroy(); - - bool is_created() const; - - bool addSocket(const Socket &sock); - bool removeSocket(const Socket &sock); - - bool accept(std::vector &sockets) const; - bool accept(std::vector &sockets, std::vector &socketsAddress) const; - - bool recv(std::vector &sockets, std::vector &errors, std::chrono::milliseconds timeout = std::chrono::milliseconds(~0) ) const; - }; -}; \ No newline at end of file diff --git a/src/Utils.cpp b/src/Utils.cpp deleted file mode 100644 index 525a216..0000000 --- a/src/Utils.cpp +++ /dev/null @@ -1,459 +0,0 @@ - -#include "Utils.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace Utils -{ - void toLower(std::string &str, const std::locale &loc) - { - for (auto &c : str) - { - c = std::tolower(c, loc); - } - } - - void trim(std::string &str) - { - static const std::array whitespace { " \t\n\v\f\r" }; - - const size_t last = str.find_last_not_of(whitespace.data() ); - - if (std::string::npos == last) - { - return str.clear(); - } - - str.assign(str.cbegin() + str.find_first_not_of(whitespace.data() ), str.cbegin() + last + 1); - } - - std::vector explode(const std::string &str, const char sep) - { - std::vector values; - - for (size_t pos = 0; std::string::npos != pos;) - { - size_t delimiter = str.find(sep, pos); - - std::string value = str.substr(pos, delimiter); - trim(value); - - values.emplace_back(std::move(value) ); - - pos = delimiter; - - if (std::string::npos != pos) - { - ++pos; - } - } - - return values; - } - - std::string encodeHtmlSymbols(const std::string &str) - { - std::string buf; - buf.reserve(str.length() ); - - for (size_t pos = 0; pos < str.length(); ++pos) - { - switch (str[pos]) - { - case '&': buf.append("&"); break; - case '\"': buf.append("""); break; - case '\'': buf.append("'"); break; - case '<': buf.append("<"); break; - case '>': buf.append(">"); break; - default: buf.push_back(str[pos]); break; - } - } - - return buf; - } - - std::string binToHexString(const void *binData, const size_t dataSize) - { - std::string str(dataSize * 2, 0); - - const uint8_t *bin = reinterpret_cast(binData); - - static const std::array hexDigits { "0123456789abcdef" }; - - for (size_t i = dataSize - 1; std::numeric_limits::max() != i; --i) - { - str[i * 2 + 0] = hexDigits[bin[i] >> 4]; - str[i * 2 + 1] = hexDigits[bin[i] & 0x0F]; - } - - return str; - } - - static unsigned char hexStringToBinEncodeSymbol(const char c) - { - if (c >= '0' && c <= '9') - { - return c - 0x30; - } - else if (c >= 'a' && c <= 'f') - { - return c - 0x57; - } - else if (c >= 'A' && c <= 'F') - { - return c - 0x37; - } - - return 0; - } - - std::string hexStringToBin(const std::string &hexStr) - { - std::string bin(hexStr.length() / 2, 0); - - for (size_t i = 0; i < bin.length(); ++i) - { - const char a = hexStr[i * 2 + 0]; - const char b = hexStr[i * 2 + 1]; - - bin[i] = ( - (hexStringToBinEncodeSymbol(a) << 4) | hexStringToBinEncodeSymbol(b) - ); - } - - return bin; - } - - unsigned long long htonll(const unsigned long long src) - { - enum Endianness { - INIT = 0, - LITE = 1, - BIGE = 2 - }; - - static int endian = Endianness::INIT; - - union { - unsigned long long ull; - unsigned char c[8]; - } x; - - if (endian == Endianness::INIT) - { - x.ull = 0x01; - endian = (x.c[7] == 0x01ULL) ? Endianness::BIGE : Endianness::LITE; - } - - if (endian == Endianness::BIGE) - { - return src; - } - - x.ull = src; - - unsigned char c; - - c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c; - c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c; - c = x.c[2]; x.c[2] = x.c[5]; x.c[5] = c; - c = x.c[3]; x.c[3] = x.c[4]; x.c[4] = c; - - return x.ull; - } - - std::string getUniqueName() - { - size_t time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); - - time = htonll(time); - - return binToHexString(&time, sizeof(time) ); - } - - char *stlStringToPChar(const std::string &str) - { - const size_t length = str.length(); - char *s = nullptr; - - if (length) - { - s = new char[length + 1]; - #ifdef WIN32 - ::strcpy_s(s, length + 1, str.c_str() ); - #elif POSIX - ::strcpy(s, str.c_str() ); - s[length] = '\0'; - #else - #error "Undefine platform" - #endif - } - - return s; - } - - void filesIncomingToRawFilesInfo(Utils::raw_fileinfo *raw[], const std::unordered_multimap &map) - { - if (raw && map.size() ) - { - raw_fileinfo *arr = new raw_fileinfo[map.size()]; - - *raw = arr; - - size_t i = 0; - - for (auto it = map.cbegin(); map.cend() != it; ++it, ++i) - { - arr[i].key = stlStringToPChar(it->first); - - const HttpServer::FileIncoming &file = it->second; - - arr[i].file_name = stlStringToPChar(file.getName() ); - arr[i].file_type = stlStringToPChar(file.getType() ); - arr[i].file_size = file.getSize(); - } - } - } - - void rawFilesInfoToFilesIncoming(std::unordered_multimap &map, const Utils::raw_fileinfo raw[], const size_t count) - { - for (size_t i = 0; i < count; ++i) - { - map.emplace(raw[i].key ? raw[i].key : "", HttpServer::FileIncoming(raw[i].file_name, raw[i].file_type, raw[i].file_size) ); - } - } - - void destroyRawPairs(Utils::raw_pair raw[], const size_t count) - { - if (raw) - { - for (size_t i = 0; i < count; ++i) - { - raw_pair &cur = raw[i]; - - delete[] cur.key; - delete[] cur.value; - } - - delete[] raw; - } - } - - void destroyRawFilesInfo(Utils::raw_fileinfo raw[], const size_t count) - { - if (raw) - { - for (size_t i = 0; i < count; ++i) - { - raw_fileinfo &cur = raw[i]; - - delete[] cur.key; - delete[] cur.file_name; - delete[] cur.file_type; - } - - delete[] raw; - } - } - - time_t stringTimeToTimestamp(const std::string &strTime) - { - static const std::unordered_map map_months { - {"Jan", 0}, {"Feb", 1}, {"Mar", 2}, {"Apr", 3}, {"May", 4}, {"Jun", 5}, {"Jul", 6}, {"Aug", 7}, {"Sep", 8}, {"Oct", 9}, {"Nov", 10}, {"Dec", 11} - }; - - if (strTime.length() > 64) - { - return ~0; - } - - const size_t str_mon_length = 64; - std::vector s_mon(str_mon_length); - - struct ::tm tc = {}; - - // Parse RFC 822 - #ifdef WIN32 - if (~0 != ::sscanf_s(strTime.c_str(), "%*s %d %3s %d %d:%d:%d", &tc.tm_mday, s_mon.data(), static_cast(s_mon.size() ), &tc.tm_year, &tc.tm_hour, &tc.tm_min, &tc.tm_sec) ) - #else - if (~0 != ::sscanf(strTime.c_str(), "%*s %d %3s %d %d:%d:%d", &tc.tm_mday, s_mon.data(), &tc.tm_year, &tc.tm_hour, &tc.tm_min, &tc.tm_sec) ) - #endif - { - tc.tm_year -= 1900; - - auto const it_mon = map_months.find(s_mon.data() ); - - if (map_months.cend() != it_mon) - { - tc.tm_mon = it_mon->second; - } - } - - tc.tm_isdst = -1; - - return ::mktime(&tc); - } - - std::string getDatetimeAsString(const ::time_t tTime, const bool isGmtTime) - { - std::array buf; - - ::time_t cur_time = tTime; - - if (tTime == ~0) - { - ::time(&cur_time); - } - - #ifdef WIN32 - struct ::tm stm = {}; - - isGmtTime ? - ::localtime_s(&stm, &cur_time) : - ::gmtime_s(&stm, &cur_time); - - // RFC 822 - ::strftime(buf.data(), buf.size(), "%a, %d %b %Y %H:%M:%S GMT", &stm); - #else - struct ::tm stm = {}; - - isGmtTime ? - ::localtime_r(&cur_time, &stm) : - ::gmtime_r(&cur_time, &stm); - - // RFC 822 - ::strftime(buf.data(), buf.size(), "%a, %d %b %G %H:%M:%S GMT", &stm); - #endif - - return std::string(buf.data() ); - } - - size_t getNumberLength(const size_t number) - { - size_t length = 0; - - size_t n = number; - - do - { - ++length; - n /= 10; - } - while (n); - - return length; - } - - bool parseCookies(const std::string &cookieHeader, std::unordered_multimap &cookies) - { - if (cookieHeader.empty() ) - { - return true; - } - - for (size_t cur_pos = 0, next_value; std::string::npos != cur_pos; cur_pos = next_value) - { - next_value = cookieHeader.find(';', cur_pos); - - size_t delimiter = cookieHeader.find('=', cur_pos); - - if (std::string::npos == delimiter || delimiter > next_value) - { - return false; - } - - std::string key = cookieHeader.substr(cur_pos, delimiter - cur_pos); - trim(key); - key = urlDecode(key); - - ++delimiter; - - std::string value = cookieHeader.substr(delimiter, std::string::npos != next_value ? next_value - delimiter : next_value); - trim(value); - value = urlDecode(value); - - cookies.emplace(std::move(key), std::move(value) ); - - if (std::string::npos != next_value) - { - ++next_value; - } - } - - return true; - } - - static inline bool isCharUrlAllowed(const char c) - { - return c == '-' || c == '_' || c == '.' || c == '~'; - } - - std::string urlEncode(const std::string &str) - { - std::string encoded; - - static const std::array hexDigits { "0123456789ABCDEF" }; - - for (size_t i = 0; i < str.length(); ++i) - { - const unsigned char c = str[i]; - - if (std::isalnum(c) || isCharUrlAllowed(c) ) - { - encoded.push_back(c); - } - else if (' ' == c) - { - encoded.push_back('+'); - } - else - { - const uint8_t a = c >> 4; - const uint8_t b = c & 0x0F; - - encoded.push_back('%'); - encoded.push_back(hexDigits[a]); - encoded.push_back(hexDigits[b]); - } - } - - return encoded; - } - - std::string urlDecode(const std::string &str) - { - std::string decoded; - - for (size_t i = 0; i < str.length(); ++i) - { - unsigned char c = str[i]; - - if ('%' == c) - { - if (i + 2 < str.length() ) - { - const char a = str[++i]; - const char b = str[++i]; - - c = ( - (hexStringToBinEncodeSymbol(a) << 4) | hexStringToBinEncodeSymbol(b) - ); - } - } - else if ('+' == c) - { - c = ' '; - } - - decoded.push_back(c); - } - - return decoded; - } -}; \ No newline at end of file diff --git a/src/Utils.h b/src/Utils.h deleted file mode 100644 index b2e15c1..0000000 --- a/src/Utils.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include "RawData.h" -#include "FileIncoming.h" - -#include -#include -#include - -namespace Utils -{ - void toLower(std::string &str, const std::locale &loc); - - void trim(std::string &str); - - std::vector explode(const std::string &str, const char sep); - - std::string encodeHtmlSymbols(const std::string &str); - - std::string binToHexString(const void *bin, const size_t size); - - std::string hexStringToBin(const std::string &hexStr); - - unsigned long long htonll(const unsigned long long src); - - std::string getUniqueName(); - - char *stlStringToPChar(const std::string &str); - - template - void stlToRawPairs(Utils::raw_pair *raw[], const T &stl) - { - if (raw && stl.size() ) - { - raw_pair *arr = new raw_pair[stl.size()]; - - *raw = arr; - - size_t i = 0; - - for (auto it = stl.cbegin(); stl.cend() != it; ++it, ++i) - { - arr[i].key = stlStringToPChar(it->first); - arr[i].value = stlStringToPChar(it->second); - } - } - } - - template - void rawPairsToStl(T &stl, const Utils::raw_pair raw[], const size_t count) - { - for (size_t i = 0; i < count; ++i) - { - stl.emplace(raw[i].key ? raw[i].key : "", raw[i].value ? raw[i].value : ""); - } - } - - void filesIncomingToRawFilesInfo(Utils::raw_fileinfo *raw[], const std::unordered_multimap &map); - void rawFilesInfoToFilesIncoming(std::unordered_multimap &map, const Utils::raw_fileinfo raw[], const size_t count); - - void destroyRawPairs(Utils::raw_pair raw[], const size_t count); - void destroyRawFilesInfo(Utils::raw_fileinfo raw[], const size_t count); - - time_t stringTimeToTimestamp(const std::string &strTime); - - std::string getDatetimeAsString(const ::time_t tTime = ~0, const bool isGmtTime = false); - - size_t getNumberLength(const size_t number); - - bool parseCookies(const std::string &cookieHeader, std::unordered_multimap &cookies); - - std::string urlEncode(const std::string &str); - std::string urlDecode(const std::string &str); -}; \ No newline at end of file diff --git a/src/server/Request.cpp b/src/server/Request.cpp new file mode 100644 index 0000000..17ca269 --- /dev/null +++ b/src/server/Request.cpp @@ -0,0 +1,20 @@ + +#include "Request.h" + +namespace HttpServer +{ + void Request::clear() noexcept + { + incoming_headers.clear(); + incoming_data.clear(); + incoming_files.clear(); + + outgoing_headers.clear(); + + host.clear(); + path.clear(); + method.clear(); + + // timeout = std::chrono::milliseconds::zero(); + } +} diff --git a/src/server/Request.h b/src/server/Request.h new file mode 100644 index 0000000..abc8de0 --- /dev/null +++ b/src/server/Request.h @@ -0,0 +1,42 @@ +#pragma once + +#include "../transfer/AppRequest.h" +#include "../transfer/ProtocolVariant.h" + +#include +#include + +namespace HttpServer +{ + enum ConnectionParams { + CONNECTION_CLOSE = 0, + CONNECTION_REUSE = 1, + CONNECTION_LEAVE_OPEN = 2 + }; + + struct Request : public Transfer::request_data + { + std::unordered_map outgoing_headers; + + std::string host; + std::string path; + std::string method; + + std::chrono::milliseconds timeout; + + size_t keep_alive_count; + int connection_params; + int app_exit_code; + + Transfer::ProtocolVariant protocol_variant; + // void *protocol_data; + + public: + void clear() noexcept; + }; + + struct DataTransfer { + size_t full_size; + size_t send_total; + }; +} diff --git a/src/server/Server.cpp b/src/server/Server.cpp new file mode 100644 index 0000000..ca069e9 --- /dev/null +++ b/src/server/Server.cpp @@ -0,0 +1,1101 @@ + +#include "Server.h" + +#include "config/ConfigParser.h" +#include "data-variant/FormUrlencoded.h" +#include "data-variant/MultipartFormData.h" +#include "data-variant/TextPlain.h" +#include "protocol/ServerHttp1.h" +#include "protocol/ServerHttp2.h" +#include "protocol/ServerHttp2Stream.h" + +#include "ServerStructuresArguments.h" + +#include "../socket/List.h" +#include "../socket/AdapterDefault.h" +#include "../socket/AdapterTls.h" +#include "../system/GlobalMutex.h" +#include "../system/SharedMemory.h" +#include "../system/System.h" +#include "../utils/Utils.h" + +#include +#include +#include +#include +#include + +namespace HttpServer +{ + static std::unique_ptr getProtocolVariant( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls, + SocketsQueue &sockets, + Http2::IncStream *stream + ) { + std::unique_ptr prot; + + // If request is HTTP/2 Stream + if (stream) { + prot.reset(new ServerHttp2Stream(sock, settings, controls, stream) ); + return prot; + } + + if (sock.get_tls_session() != nullptr) + { + ::gnutls_datum_t datum; + + const int ret = ::gnutls_alpn_get_selected_protocol(sock.get_tls_session(), &datum); + + if (GNUTLS_E_SUCCESS == ret) + { + const std::string protocol(reinterpret_cast(datum.data), datum.size); + + if ("h2" == protocol) { + prot.reset(new ServerHttp2(sock, settings, controls, sockets) ); + } + else if ("http/1.1" == protocol) { + prot.reset(new ServerHttp1(sock, settings, controls) ); + } + } + else if (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE == ret) { + prot.reset(new ServerHttp1(sock, settings, controls) ); + } + + return prot; + } + + prot.reset(new ServerHttp1(sock, settings, controls) ); + + return prot; + } + + /** + * Метод для обработки запроса + */ + void Server::threadRequestProc( + Socket::Adapter &sock, + SocketsQueue &sockets, + Http2::IncStream *stream + ) const { + std::unique_ptr prot = getProtocolVariant( + sock, + this->settings, + this->controls, + sockets, + stream + ); + + if (prot) { + // Check if switching protocol + for (ServerProtocol *ret = nullptr; ; ) { + ret = prot->process(); + + if (prot.get() == ret) { + break; + } + + prot.reset(ret); + } + + prot->close(); + } + } + + /** + * Метод для обработки запросов (запускается в отдельном потоке) + * извлекает сокет клиенты из очереди и передаёт его на обслуживание + */ + void Server::threadRequestCycle( + SocketsQueue &sockets, + Utils::Event &eventThreadCycle + ) const { + while (true) + { + Socket::Socket sock; + Http2::IncStream *stream = nullptr; + + eventThreadCycle.wait(); + + if (this->controls.process_flag == false) { + break; + } + + sockets.lock(); + + if (sockets.size() ) { + std::tie(sock, stream) = sockets.front(); +/* + sockaddr_in addr {}; + socklen_t addr_size = sizeof(sockaddr_in); + ::getpeername( + sock.get_handle(), + reinterpret_cast(&addr), + &addr_size + ); +*/ + sockets.pop(); + } + + if (sockets.empty() ) { + eventThreadCycle.reset(); + this->controls.eventNotFullQueue->notify(); + } + + sockets.unlock(); + + if (sock.is_open() ) + { + ++this->threads_working_count; + + ::sockaddr_in sock_addr {}; + ::socklen_t sock_addr_len = sizeof(sock_addr); + + ::getsockname( + sock.get_handle(), + reinterpret_cast(&sock_addr), + &sock_addr_len + ); + + const int port = ntohs(sock_addr.sin_port); + + auto const it = this->tls_data.find(port); + + if (this->tls_data.cend() != it) // if TLS connection + { + if (stream) { + Socket::AdapterTls socket_adapter( + reinterpret_cast(stream->reserved) + ); + + this->threadRequestProc( + socket_adapter, + sockets, + stream + ); + } else { + const std::tuple &data = it->second; + + Socket::AdapterTls socket_adapter( + sock, + std::get(data), + std::get(data) + ); + + if (socket_adapter.handshake() ) { + this->threadRequestProc( + socket_adapter, + sockets, + nullptr + ); + } + } + } else { + Socket::AdapterDefault socket_adapter(sock); + + this->threadRequestProc( + socket_adapter, + sockets, + stream + ); + } + + --this->threads_working_count; + } + } + } + + /** + * Цикл управления количеством рабочих потоков + */ + int Server::cycleQueue(SocketsQueue &sockets) + { + auto const it_option = this->settings.global.find("threads_max_count"); + + size_t threads_max_count = 0; + + if (this->settings.global.cend() != it_option) { + const std::string &option = it_option->second; + threads_max_count = std::strtoull(option.c_str(), nullptr, 10); + } + + if (0 == threads_max_count) { + threads_max_count = std::thread::hardware_concurrency(); + + if (0 == threads_max_count) { + threads_max_count = 1; + } + + threads_max_count *= 2; + } + + this->threads_working_count = 0; + + Utils::Event eventThreadCycle(false, true); + + std::function serverThreadRequestCycle = std::mem_fn(&Server::threadRequestCycle); + + std::vector active_threads; + active_threads.reserve(threads_max_count); + + // For update applications modules + do { + if (this->controls.eventUpdateModule->notifed() ) { + updateModules(); + } + + // Cycle creation threads applications requests + do { + while ( + this->threads_working_count == active_threads.size() && + active_threads.size() < threads_max_count && + sockets.empty() == false + ) { + active_threads.emplace_back( + serverThreadRequestCycle, + this, + std::ref(sockets), + std::ref(eventThreadCycle) + ); + } + + size_t notify_count = active_threads.size() - this->threads_working_count; + + if (notify_count > sockets.size() ) { + notify_count = sockets.size(); + } + + eventThreadCycle.notify(notify_count); + + this->controls.eventProcessQueue->wait(); + } + while (this->controls.process_flag); + + // Data clear + + eventThreadCycle.notify(); + + if (active_threads.empty() == false) { + // Join threads (wait completion) + for (auto &th : active_threads) { + th.join(); + } + + active_threads.clear(); + } + + this->controls.eventNotFullQueue->notify(); + } + while (this->controls.eventUpdateModule->notifed() ); + + return 0; + } + + bool Server::updateModule( + System::Module &module, + std::unordered_set &applications, + const size_t moduleIndex + ) { + std::unordered_set same; + + for (auto &app : applications) + { + if (app->module_index == moduleIndex) + { + same.emplace(app); + + try { + if (app->application_final) { + const std::string root = app->root_dir; + app->application_final(root.data() ); + } + } + catch (const std::exception &exc) { + std::cout << "Warning: an exception was thrown when the application '" + << app->server_module << "' was finishes: " << exc.what() << std::endl; + } + + app->application_call = std::function(); + app->application_clear = std::function(); + app->application_init = std::function(); + app->application_final = std::function(); + } + } + + module.close(); + + auto const app = *(same.cbegin() ); + + const std::string &module_name = app->server_module; + + #ifdef WIN32 + std::ifstream src(app->server_module_update, std::ifstream::binary); + + if ( ! src) { + std::cout << "Error: file '" << app->server_module_update << "' cannot be open;" << std::endl; + return false; + } + + std::ofstream dst(module_name, std::ofstream::binary | std::ofstream::trunc); + + if ( ! dst) { + std::cout << "Error: file '" << module_name << "' cannot be open;" << std::endl; + return false; + } + + // Copy (rewrite) file + dst << src.rdbuf(); + + src.close(); + dst.close(); + + // Open updated module + module.open(module_name); + + #elif POSIX + // HACK: for posix system — load new version shared library + + const size_t dir_pos = module_name.rfind('/'); + const size_t ext_pos = module_name.rfind('.'); + + std::string module_name_temp; + + if (std::string::npos != ext_pos && (std::string::npos == dir_pos || dir_pos < ext_pos) ) { + module_name_temp = module_name.substr(0, ext_pos) + '-' + Utils::getUniqueName() + module_name.substr(ext_pos); + } else { + module_name_temp = module_name + '-' + Utils::getUniqueName(); + } + + std::ifstream src(app->server_module_update, std::ifstream::binary); + + if ( ! src) { + std::cout << "Error: file '" << app->server_module_update << "' cannot be open;" << std::endl; + return false; + } + + std::ofstream dst(module_name_temp, std::ofstream::binary | std::ofstream::trunc); + + if ( ! dst) { + std::cout << "Error: file '" << module_name << "' cannot be open;" << std::endl; + return false; + } + + // Copy (rewrite) file + dst << src.rdbuf(); + + src.close(); + dst.close(); + + // Open updated module + module.open(module_name_temp); + + if (std::remove(module_name.c_str() ) != 0) { + std::cout << "Error: file '" << module_name << "' could not be removed;" << std::endl; + return false; + } + + if (std::rename(module_name_temp.c_str(), module_name.c_str() ) != 0) { + std::cout << "Error: file '" << module_name_temp << "' could not be renamed;" << std::endl; + return false; + } + #else + #error "Undefined platform" + #endif + + if (module.is_open() == false) { + std::cout << "Error: the application module '" << module_name << "' can not be opened;" << std::endl; + return false; + } + + void *(*addr)(void *) = nullptr; + + if (module.find("application_call", &addr) == false) { + std::cout << "Error: function 'application_call' was not found in the module '" << module_name << "';" << std::endl; + return false; + } + + std::function app_call = reinterpret_cast(addr); + + if ( ! app_call) { + std::cout << "Error: invalid function 'application_call' is in the module '" << module_name << "';" << std::endl; + return false; + } + + if (module.find("application_clear", &addr) == false) { + std::cout << "Error: function 'application_clear' was not found in the module '" << module_name << "';" << std::endl; + return false; + } + + std::function app_clear = reinterpret_cast(addr); + + std::function app_init = std::function(); + + if (module.find("application_init", &addr) ) { + app_init = reinterpret_cast(addr); + } + + std::function app_final = std::function(); + + if (module.find("application_final", &addr) ) { + app_final = reinterpret_cast(addr); + } + + for (auto &app : same) + { + app->application_call = app_call; + app->application_clear = app_clear; + app->application_init = app_init; + app->application_final = app_final; + + try { + if (app->application_init) { + const std::string root = app->root_dir; + app->application_init(root.data() ); + } + } + catch (const std::exception &exc) { + std::cout << "Warning: an exception was thrown when the application '" << module_name << "' was initialized: " << exc.what() << std::endl; + } + } + + return true; + } + + void Server::updateModules() + { + // Applications settings list + std::unordered_set applications; + // Get full applications settings list + this->settings.apps_tree.collectApplicationSettings(applications); + + std::unordered_set updated; + + for (auto const &app : applications) + { + const size_t module_index = app->module_index; + + // If module is not updated (not checked) + if (updated.cend() == updated.find(module_index) ) + { + if (app->server_module_update.empty() == false && app->server_module_update != app->server_module) + { + size_t module_size_new = 0; + time_t module_time_new = 0; + + if (System::getFileSizeAndTimeGmt(app->server_module_update, &module_size_new, &module_time_new) ) + { + size_t module_size_cur = 0; + time_t module_time_cur = 0; + + System::Module &module = this->modules[module_index]; + + if (System::getFileSizeAndTimeGmt(app->server_module, &module_size_cur, &module_time_cur) ) { + if (module_size_cur != module_size_new || module_time_cur < module_time_new) { + this->updateModule(module, applications, module_index); + } + } + } + } + + updated.emplace(module_index); + } + } + + std::cout << "Notice: application modules have been updated;" << std::endl; + + this->controls.setProcess(); + this->controls.eventUpdateModule->reset(); + } + + bool Server::init() + { + ConfigParser conf_parser; + + if (Socket::Socket::Startup() && conf_parser.loadConfig("main.conf", this->settings, this->modules) ) + { + this->settings.addDataVariant(new DataVariant::FormUrlencoded() ); + this->settings.addDataVariant(new DataVariant::MultipartFormData() ); + this->settings.addDataVariant(new DataVariant::TextPlain() ); + + ::gnutls_global_init(); + + return true; + } + + return false; + } + + void Server::clear() + { + this->controls.clear(); + + if (this->tls_data.empty() == false) { + for (auto &pair : this->tls_data) { + std::tuple &data = pair.second; + + ::gnutls_certificate_free_credentials(std::get(data) ); + ::gnutls_priority_deinit(std::get(data) ); + } + } + + this->settings.clear(); + + if (this->modules.empty() == false) { + for (auto &module : this->modules) { + module.close(); + } + + this->modules.empty(); + } + + ::gnutls_global_deinit(); + + Socket::Socket::Cleanup(); + } + + static bool tlsInit( + const ServerApplicationSettings &app, + std::tuple &data + ) { + ::gnutls_certificate_credentials_t x509_cred; + + int ret = ::gnutls_certificate_allocate_credentials(&x509_cred); + + if (ret < 0) { + std::cout << "Error [tls]: certificate credentials has not been allocated; code: " << ret << std::endl; + return false; + } + + if (app.chain_file.empty() == false) + { + ret = ::gnutls_certificate_set_x509_trust_file( + x509_cred, + app.chain_file.c_str(), + GNUTLS_X509_FMT_PEM + ); + + if (ret < 0) { + std::cout << "Warning [tls]: (CA) chain file has not been accepted; code: " << ret << std::endl; + } + } + + if (app.crl_file.empty() == false) + { + ret = ::gnutls_certificate_set_x509_crl_file( + x509_cred, + app.crl_file.c_str(), + GNUTLS_X509_FMT_PEM + ); + + if (ret < 0) { + std::cout << "Warning [tls]: (CLR) clr file has not been accepted; code: " << ret << std::endl; + } + } + + ret = ::gnutls_certificate_set_x509_key_file( + x509_cred, + app.cert_file.c_str(), + app.key_file.c_str(), + GNUTLS_X509_FMT_PEM + ); + + if (ret < 0) { + std::cout << "Error [tls]: (CERT) cert file or/and (KEY) key file has not been accepted; code: " << ret << std::endl; + return false; + } + + if (app.stapling_file.empty() == false) + { + ret = ::gnutls_certificate_set_ocsp_status_request_file( + x509_cred, + app.stapling_file.c_str(), + 0 + ); + + if (ret < 0) { + std::cout << "Warning [tls]: (OCSP) stapling file has not been accepted; code: " << ret << std::endl; + } + } + + ::gnutls_dh_params_t dh_params; + + ::gnutls_dh_params_init(&dh_params); + + if (app.dh_file.empty() ) { + const unsigned int bits = ::gnutls_sec_param_to_pk_bits( + GNUTLS_PK_DH, GNUTLS_SEC_PARAM_HIGH + ); + + ret = ::gnutls_dh_params_generate2( + dh_params, + bits + ); + } else { + std::ifstream dh_file(app.dh_file); + + if (dh_file) { + const size_t max_file_size = 1024 * 1024; + + std::vector buf(max_file_size); + + dh_file.read( + buf.data(), + std::streamsize(buf.size()) + ); + + gnutls_datum_t datum { + reinterpret_cast(buf.data() ), + static_cast(dh_file.gcount() ) + }; + + ret = ::gnutls_dh_params_import_pkcs3( + dh_params, + &datum, + GNUTLS_X509_FMT_PEM + ); + } else { + ret = -1; + std::cout << "Error [tls]: DH params file has not been opened;" << std::endl;; + } + + dh_file.close(); + } + + if (ret < 0) { + ::gnutls_certificate_free_credentials(x509_cred); + std::cout << "Error [tls]: DH params were not loaded; code: " << ret << std::endl; + return false; + } + + ::gnutls_certificate_set_dh_params(x509_cred, dh_params); + + ::gnutls_priority_t priority_cache; + + ret = ::gnutls_priority_init(&priority_cache, "NORMAL", nullptr); + + if (ret < 0) { + ::gnutls_certificate_free_credentials(x509_cred); + std::cout << "Error [tls]: priority cache cannot be init; code: " << ret << std::endl; + return false; + } + + data = std::tuple { + x509_cred, + priority_cache + }; + + return true; + } + + bool Server::tryBindPort( + const int port, + std::unordered_set &ports + ) { + // Only unique ports + if (ports.cend() != ports.find(port) ) { + return false; + } + + Socket::Socket sock; + + if (sock.open() == false) { + std::cout << "Error: socket cannot be open; errno " << Socket::Socket::getLastError() << ";" << std::endl; + return false; + } + + if (sock.bind(port) == false) { + std::cout << "Error: port " << port << " cannot be bind; errno " << Socket::Socket::getLastError() << ";" << std::endl; + return false; + } + + if (sock.listen() == false) { + std::cout << "Error: socket " << port << " cannot be listen; errno " << Socket::Socket::getLastError() << ";" << std::endl; + return false; + } + + sock.nonblock(true); + + this->liseners.emplace_back(std::move(sock) ); + + ports.emplace(port); + + return true; + } + + void Server::initAppsPorts() + { + // Applications settings list + std::unordered_set applications; + // Get full applications settings list + this->settings.apps_tree.collectApplicationSettings(applications); + + // Bind ports set + std::unordered_set ports; + + // Open applications sockets + for (auto const &app : applications) + { + const std::unordered_set &tls = app->tls_ports; + + if (tls.empty() == false) { + std::tuple data; + + if (tlsInit(*app, data) ) { + for (const int port : tls) { + if (this->tryBindPort(port, ports) ) { + this->tls_data.emplace(port, data); + } + } + } + } + + const std::unordered_set &list = app->ports; + + for (const int port : list) { + this->tryBindPort(port, ports); + } + } + } + + int Server::run() + { + if (this->init() == false) { + return 0x10; + } + + this->initAppsPorts(); + + if (this->liseners.empty() ) { + std::cout << "Error: any socket was not open;" << std::endl; + this->clear(); + return 0x20; + } + + Socket::List sockets_list; + + sockets_list.create(this->liseners.size() ); + + for (auto const &sock : this->liseners) { + sockets_list.addSocket(sock); + } + + std::cout << "Log: server started work;" << std::endl << std::endl; + + constexpr size_t queue_max_length = 1024; + this->controls.eventNotFullQueue = new Utils::Event(true, true); + this->controls.eventProcessQueue = new Utils::Event(); + this->controls.eventUpdateModule = new Utils::Event(false, true); + + SocketsQueue sockets; + + this->controls.setProcess(); + + std::function serverCycleQueue = std::mem_fn(&Server::cycleQueue); + std::thread threadQueue(serverCycleQueue, this, std::ref(sockets) ); + + std::vector accept_sockets; + + // Cycle for receiving new connections + do { + if (sockets_list.accept(accept_sockets) ) { + sockets.lock(); + + for (size_t i = 0; i < accept_sockets.size(); ++i) { + const Socket::Socket &sock = accept_sockets[i]; + + if (sock.is_open() ) { + sock.nonblock(true); + sock.tcp_nodelay(true); + + sockets.emplace( + std::tuple { + sock, + nullptr + } + ); + } + } + + sockets.unlock(); + + this->controls.eventProcessQueue->notify(); + + if (sockets.size() >= queue_max_length) { + this->controls.eventNotFullQueue->reset(); + } + + accept_sockets.clear(); + + this->controls.eventNotFullQueue->wait(); + } + } + while (this->controls.process_flag || this->controls.eventUpdateModule->notifed() ); + + this->controls.eventProcessQueue->notify(); + + threadQueue.join(); + + sockets_list.destroy(); + + if (this->liseners.empty() == false) { + for (Socket::Socket &sock : this->liseners) { + sock.close(); + } + + this->liseners.clear(); + } + + this->clear(); + + std::cout << "Log: server work completed;" << std::endl; + + return EXIT_SUCCESS; + } + + bool Server::get_start_args( + const int argc, + const char *argv[], + struct server_start_args *st + ) { + for (int i = 1; i < argc; ++i) + { + if (0 == ::strcmp(argv[i], "--start") ) { + + } + else if (0 == ::strcmp(argv[i], "--force") ) { + st->force = true; + } + else if (argv[i] == ::strstr(argv[i], "--config-path=") ) { + st->config_path = std::string(argv[i] + sizeof("--config-path=") - 1); + } + else if (argv[i] == ::strstr(argv[i], "--server-name=") ) { + st->server_name = std::string(argv[i] + sizeof("--server-name=") - 1); + } + else + { + std::cout << "Argument '" << argv[i] << "' can't be applied with --start;" << std::endl; + return false; + } + } + + if (st->server_name.empty() ) { + st->server_name = argv[0]; + } + + System::filterSharedMemoryName(st->server_name); + + return true; + } + + int Server::command_start(const int argc, const char *argv[]) + { + struct server_start_args st = {}; + + if (Server::get_start_args(argc, argv, &st) == false) { + return 0x1; + } + + if (st.config_path.empty() == false) { + if (System::changeCurrentDirectory(st.config_path) == false) { + std::cout << "Configuration path '" << st.config_path << "' has not been found;" << std::endl; + return 0x2; + } + } + + if (st.force) { + System::SharedMemory::destroy(st.server_name); + System::GlobalMutex::destory(st.server_name); + } + + System::GlobalMutex glob_mtx; + System::SharedMemory glob_mem; + + bool is_exists = false; + + if (glob_mtx.open(st.server_name) ) { + glob_mtx.lock(); + + if (glob_mem.open(st.server_name) ) { + System::native_processid_type pid = 0; + + if (glob_mem.read(&pid, sizeof(pid) ) ) { + is_exists = System::isProcessExists(pid); + } + } + + glob_mtx.unlock(); + } + + if (is_exists) { + std::cout << "Server instance with the name '" << st.server_name << "' is already running;" << std::endl; + return 0x3; + } + + if (glob_mtx.open(st.server_name) == false) { + if (glob_mtx.create(st.server_name) == false) { + std::cout << "Global mutex could not been created;" << std::endl; + return 0x4; + } + } + + glob_mtx.lock(); + + if (glob_mem.open(st.server_name) == false) { + if (glob_mem.create(st.server_name, sizeof(System::native_processid_type) ) == false) { + glob_mtx.unlock(); + std::cout << "Shared memory could not been allocated;" << std::endl; + return 0x5; + } + } + + System::native_processid_type pid = System::getProcessId(); + + if (glob_mem.write(&pid, sizeof(pid) ) == false) { + glob_mem.destroy(); + glob_mtx.unlock(); + std::cout << "Writing data to shared memory has failed;" << std::endl; + return 0x6; + } + + glob_mtx.unlock(); + + int code = EXIT_FAILURE; + + do { + this->controls.setProcess(false); + this->controls.setRestart(false); + + code = this->run(); + } + while (this->controls.process_flag || this->controls.restart_flag); + + glob_mem.destroy(); + glob_mtx.destory(); + + return code; + } + + static void close_liseners(std::vector &liseners) { + for (auto &sock : liseners) { + sock.close(); + } + } + + void Server::stop() { + this->controls.stopProcess(); + + close_liseners(this->liseners); + } + + void Server::restart() { + this->controls.setRestart(); + this->controls.stopProcess(); + + close_liseners(this->liseners); + } + + void Server::update() { + this->controls.setUpdateModule(); + this->controls.setProcess(false); + this->controls.setProcessQueue(); + } + + System::native_processid_type Server::getServerProcessId(const std::string &serverName) + { + System::native_processid_type pid = 0; + + System::GlobalMutex glob_mtx; + + if (glob_mtx.open(serverName) ) { + System::SharedMemory glob_mem; + + glob_mtx.lock(); + + if (glob_mem.open(serverName) ) { + glob_mem.read(&pid, sizeof(pid) ); + } + + glob_mtx.unlock(); + } + + return pid; + } + + int Server::command_help(const int argc, const char *argv[]) const + { + std::cout << std::left << "Available arguments:" << std::endl + << std::setw(4) << ' ' << std::setw(26) << "--start" << "Start http server" << std::endl + << std::setw(8) << ' ' << std::setw(22) << "[options]" << std::endl + << std::setw(8) << ' ' << std::setw(22) << "--force" << "Forcibly start http server (ignore existing instance)" << std::endl + << std::setw(8) << ' ' << std::setw(22) << "--config-path=" << "Path to directory with configuration files" << std::endl + << std::endl + << std::setw(4) << ' ' << std::setw(26) << "--restart" << "Restart http server" << std::endl + << std::setw(4) << ' ' << std::setw(26) << "--update-module" << "Update applications modules" << std::endl + << std::setw(4) << ' ' << std::setw(26) << "--kill" << "Shutdown http server" << std::endl + << std::setw(4) << ' ' << std::setw(26) << "--help" << "This help" << std::endl + << std::endl<< "Optional arguments:" << std::endl + << std::setw(4) << ' ' << std::setw(26) << "--server-name=" << "Name of server instance" << std::endl; + + return EXIT_SUCCESS; + } + + static std::string get_server_name(const int argc, const char *argv[]) + { + std::string server_name; + + for (int i = 1; i < argc; ++i) { + if (argv[i] == ::strstr(argv[i], "--server-name=") ) { + server_name = std::string(argv[i] + sizeof("--server-name=") - 1); + break; + } + } + + if (server_name.empty() ) { + server_name = argv[0]; + } + + System::filterSharedMemoryName(server_name); + + return server_name; + } + + int Server::command_restart(const int argc, const char *argv[]) const + { + const System::native_processid_type pid = Server::getServerProcessId(get_server_name(argc, argv) ); + + if (1 < pid && System::sendSignal(pid, SIGUSR1) ) { + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; + } + + int Server::command_terminate(const int argc, const char *argv[]) const + { + const System::native_processid_type pid = Server::getServerProcessId(get_server_name(argc, argv) ); + + if (1 < pid && System::sendSignal(pid, SIGTERM) ) { + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; + } + + int Server::command_update_module(const int argc, const char *argv[]) const + { + const System::native_processid_type pid = Server::getServerProcessId(get_server_name(argc, argv) ); + + if (1 < pid && System::sendSignal(pid, SIGUSR2) ) { + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; + } +} diff --git a/src/server/Server.h b/src/server/Server.h new file mode 100644 index 0000000..f8ca243 --- /dev/null +++ b/src/server/Server.h @@ -0,0 +1,86 @@ +#pragma once + +#include "../socket/Adapter.h" +#include "../system/Module.h" + +#include "ServerControls.h" +#include "ServerSettings.h" +#include "SocketsQueue.h" + +#include + +namespace HttpServer +{ + class Server + { + protected: + ServerSettings settings; + + std::unordered_map > tls_data; + // std::unordered_map > > http_streams; + + std::vector modules; + std::vector liseners; + + mutable std::atomic_size_t threads_working_count; + System::CachePadding padding_1; + + public: + mutable ServerControls controls; + + protected: + int cycleQueue(SocketsQueue &sockets); + + void threadRequestProc( + Socket::Adapter &sock, + SocketsQueue &sockets, + Http2::IncStream *stream + ) const; + + void threadRequestCycle( + SocketsQueue &sockets, + Utils::Event &eventThreadCycle + ) const; + + bool tryBindPort( + const int port, + std::unordered_set &ports + ); + + void initAppsPorts(); + + bool init(); + int run(); + void clear(); + + static System::native_processid_type getServerProcessId(const std::string &serverName); + + void updateModules(); + + bool updateModule( + System::Module &module, + std::unordered_set &applications, + const size_t moduleIndex + ); + + private: + static bool get_start_args( + const int argc, + const char *argv[], + struct server_start_args *st + ); + + public: + Server() = default; + + void stop(); + void restart(); + void update(); + + int command_help(const int argc, const char *argv[]) const; + int command_start(const int argc, const char *argv[]); + int command_restart(const int argc, const char *argv[]) const; + int command_terminate(const int argc, const char *argv[]) const; + int command_update_module(const int argc, const char *argv[]) const; + }; +} diff --git a/src/ServerApplicationSettings.h b/src/server/ServerApplicationSettings.h similarity index 61% rename from src/ServerApplicationSettings.h rename to src/server/ServerApplicationSettings.h index 7d61acc..ca70896 100644 --- a/src/ServerApplicationSettings.h +++ b/src/server/ServerApplicationSettings.h @@ -1,8 +1,7 @@ #pragma once -#include "ServerRequest.h" -#include "ServerResponse.h" -#include "RawData.h" +#include "../transfer/AppRequest.h" +#include "../transfer/AppResponse.h" #include #include @@ -30,9 +29,9 @@ namespace HttpServer std::string stapling_file; std::string dh_file; - std::function application_call; - std::function application_clear; - std::function application_init; - std::function application_final; + std::function application_call; + std::function application_clear; + std::function application_init; + std::function application_final; }; -}; \ No newline at end of file +} diff --git a/src/ServerApplicationsTree.cpp b/src/server/ServerApplicationsTree.cpp similarity index 69% rename from src/ServerApplicationsTree.cpp rename to src/server/ServerApplicationsTree.cpp index e548925..3e73586 100644 --- a/src/ServerApplicationsTree.cpp +++ b/src/server/ServerApplicationsTree.cpp @@ -3,14 +3,17 @@ namespace HttpServer { - ServerApplicationsTree::ServerApplicationsTree(): app_sets(nullptr) + ServerApplicationsTree::ServerApplicationsTree() noexcept : app_sets(nullptr) { } - ServerApplicationsTree::~ServerApplicationsTree() - { - clear(); + ServerApplicationsTree::~ServerApplicationsTree() noexcept { + this->clear(); + } + + bool ServerApplicationsTree::empty() const noexcept { + return this->list.empty(); } void ServerApplicationsTree::addApplication(const std::string &name, ServerApplicationSettings *sets) @@ -27,8 +30,7 @@ namespace HttpServer { std::string part = name.substr(cur_pos, delimiter - cur_pos); - if ("" == part) - { + if (part.empty() ) { part = "*"; } @@ -40,37 +42,29 @@ namespace HttpServer // Emplace last part std::string part = name.substr(cur_pos); name_parts.emplace_back(std::move(part) ); - } - else - { + } else { name_parts.emplace_back(name); } - addApplication(name_parts, sets); + this->addApplication(name_parts, sets); } void ServerApplicationsTree::addApplication(std::vector &nameParts, ServerApplicationSettings *sets) { - if (nameParts.empty() ) - { - app_sets = sets; - } - else - { + if (nameParts.empty() ) { + this->app_sets = sets; + } else { std::string &part = nameParts.back(); - auto it = list.find(part); + auto it = this->list.find(part); ServerApplicationsTree *sub; - if (list.cend() != it) - { + if (this->list.cend() != it) { sub = it->second; - } - else - { + } else { sub = new ServerApplicationsTree(); - list.emplace(std::move(part), sub); + this->list.emplace(std::move(part), sub); } nameParts.pop_back(); @@ -99,40 +93,31 @@ namespace HttpServer std::string part = name.substr(cur_pos); name_parts.emplace_back(std::move(part) ); - } - else - { + } else { name_parts.emplace_back(name); } - return find(name_parts); + return this->find(name_parts); } const ServerApplicationSettings *ServerApplicationsTree::find(std::vector &nameParts) const { - if (nameParts.empty() ) - { - return app_sets; - } - else - { + if (nameParts.empty() ) { + return this->app_sets; + } else { const std::string part = std::move(nameParts.back() ); nameParts.pop_back(); - auto it = list.find(part); + auto it = this->list.find(part); - if (list.cend() == it) - { - it = list.find("*"); + if (this->list.cend() == it) { + it = this->list.find("*"); - if (list.end() != it) - { - return app_sets; + if (this->list.end() != it) { + return this->app_sets; } - } - else - { + } else { return it->second->find(nameParts); } @@ -142,12 +127,11 @@ namespace HttpServer void ServerApplicationsTree::collectApplicationSettings(std::unordered_set &set) const { - for (auto &node : list) + for (auto &node : this->list) { const ServerApplicationsTree *tree = node.second; - if (nullptr != tree->app_sets) - { + if (nullptr != tree->app_sets) { set.emplace(tree->app_sets); } @@ -155,16 +139,15 @@ namespace HttpServer } } - void ServerApplicationsTree::clear() + void ServerApplicationsTree::clear() noexcept { - if (false == list.empty() ) + if (this->list.empty() == false) { - for (auto &it : list) - { + for (auto &it : this->list) { delete it.second; } - list.clear(); + this->list.clear(); } } -}; \ No newline at end of file +} diff --git a/src/ServerApplicationsTree.h b/src/server/ServerApplicationsTree.h similarity index 84% rename from src/ServerApplicationsTree.h rename to src/server/ServerApplicationsTree.h index 84ce430..6f4b54f 100644 --- a/src/ServerApplicationsTree.h +++ b/src/server/ServerApplicationsTree.h @@ -16,10 +16,10 @@ namespace HttpServer ServerApplicationSettings *app_sets; public: - ServerApplicationsTree(); - ~ServerApplicationsTree(); + ServerApplicationsTree() noexcept; + ~ServerApplicationsTree() noexcept; - inline bool empty() const { return list.empty(); } + bool empty() const noexcept; void addApplication(const std::string &name, ServerApplicationSettings *sets); void addApplication(std::vector &nameParts, ServerApplicationSettings *sets); @@ -29,6 +29,6 @@ namespace HttpServer void collectApplicationSettings(std::unordered_set &set) const; - void clear(); + void clear() noexcept; }; -}; \ No newline at end of file +} diff --git a/src/server/ServerControls.cpp b/src/server/ServerControls.cpp new file mode 100644 index 0000000..23fd576 --- /dev/null +++ b/src/server/ServerControls.cpp @@ -0,0 +1,66 @@ + +#include "ServerControls.h" + +namespace HttpServer +{ + ServerControls::ServerControls() + : eventProcessQueue(nullptr), + eventNotFullQueue(nullptr), + eventUpdateModule(nullptr) + { + + } + + ServerControls::~ServerControls() { + this->clear(); + } + + void ServerControls::clear() + { + if (this->eventNotFullQueue) { + delete this->eventNotFullQueue; + this->eventNotFullQueue = nullptr; + } + + if (this->eventProcessQueue) { + delete this->eventProcessQueue; + this->eventProcessQueue = nullptr; + } + + if (this->eventUpdateModule) { + delete this->eventUpdateModule; + this->eventUpdateModule = nullptr; + } + } + + void ServerControls::setProcess(const bool flag) { + this->process_flag = flag; + } + + void ServerControls::stopProcess() + { + this->process_flag = false; + + if (this->eventNotFullQueue) { + this->eventNotFullQueue->notify(); + } + + this->setProcessQueue(); + } + + void ServerControls::setRestart(const bool flag) { + this->restart_flag = flag; + } + + void ServerControls::setUpdateModule() { + if (this->eventUpdateModule) { + this->eventUpdateModule->notify(); + } + } + + void ServerControls::setProcessQueue() { + if (this->eventProcessQueue) { + this->eventProcessQueue->notify(); + } + } +} diff --git a/src/server/ServerControls.h b/src/server/ServerControls.h new file mode 100644 index 0000000..b438111 --- /dev/null +++ b/src/server/ServerControls.h @@ -0,0 +1,37 @@ +#pragma once + +#include "../utils/Event.h" +#include "../system/Cache.h" + +#include +#include + +namespace HttpServer +{ + class ServerControls + { + public: + Utils::Event *eventProcessQueue; + Utils::Event *eventNotFullQueue; + Utils::Event *eventUpdateModule; + + System::CachePaddingSize padding_1; + + // Флаг, означающий - активированы ли главные циклы сервера + // (с помощью этого флага можно деактивировать циклы, чтобы завершить работу сервера) + sig_atomic_t process_flag; + sig_atomic_t restart_flag; + + public: + ServerControls(); + ~ServerControls(); + + void clear(); + + void setProcess(const bool flag = true); + void stopProcess(); + void setRestart(const bool flag = true); + void setUpdateModule(); + void setProcessQueue(); + }; +} diff --git a/src/server/ServerSettings.cpp b/src/server/ServerSettings.cpp new file mode 100644 index 0000000..4370d1e --- /dev/null +++ b/src/server/ServerSettings.cpp @@ -0,0 +1,58 @@ + +#include "ServerSettings.h" + +#include + +namespace HttpServer +{ + ServerSettings::~ServerSettings() { + this->clear(); + } + + void ServerSettings::addDataVariant(DataVariant::Abstract *dataVariant) { + this->variants.emplace( + dataVariant->getName(), + dataVariant + ); + } + + void ServerSettings::clear() + { + if (this->variants.empty() == false) + { + for (auto &variant : this->variants) { + delete variant.second; + } + + this->variants.clear(); + } + + if (this->apps_tree.empty() == false) + { + std::unordered_set applications; + this->apps_tree.collectApplicationSettings(applications); + + for (auto &app : applications) + { + try { + if (app->application_final) { + const std::string root = app->root_dir; + app->application_final(root.data() ); + } + } + catch (const std::exception &exc) { + std::cout << "Warning: an exception was thrown when the application '" << app->server_module << "' was finishes: " << exc.what() << std::endl; + } + + delete app; + } + + applications.clear(); + this->apps_tree.clear(); + } + + if (this->global.empty() == false) { + this->global.clear(); + } + } +} diff --git a/src/server/ServerSettings.h b/src/server/ServerSettings.h new file mode 100644 index 0000000..221a466 --- /dev/null +++ b/src/server/ServerSettings.h @@ -0,0 +1,25 @@ +#pragma once + +#include "data-variant/Abstract.h" + +#include "ServerApplicationsTree.h" + +namespace HttpServer +{ + class ServerSettings + { + public: + std::unordered_map global; + std::unordered_map variants; + std::unordered_map mimes_types; + ServerApplicationsTree apps_tree; + + public: + ServerSettings() = default; + ~ServerSettings(); + + void addDataVariant(DataVariant::Abstract *dataVariant); + + void clear(); + }; +} diff --git a/src/ServerStructuresArguments.h b/src/server/ServerStructuresArguments.h similarity index 98% rename from src/ServerStructuresArguments.h rename to src/server/ServerStructuresArguments.h index 6877ab0..d00e8be 100644 --- a/src/ServerStructuresArguments.h +++ b/src/server/ServerStructuresArguments.h @@ -10,4 +10,4 @@ namespace HttpServer std::string config_path; bool force; }; -}; \ No newline at end of file +} diff --git a/src/server/SocketsQueue.h b/src/server/SocketsQueue.h new file mode 100644 index 0000000..2473ee4 --- /dev/null +++ b/src/server/SocketsQueue.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../socket/Socket.h" +#include "../system/Cache.h" + +#include "../transfer/http2/Http2.h" + +#include +#include + +namespace HttpServer +{ + class SocketsQueue : + public std::queue >, + System::CachePadding > >, + public std::mutex + { + + }; +} diff --git a/src/server/config/ConfigParser.cpp b/src/server/config/ConfigParser.cpp new file mode 100644 index 0000000..c00ea41 --- /dev/null +++ b/src/server/config/ConfigParser.cpp @@ -0,0 +1,750 @@ + +#include "ConfigParser.h" + +#include "../ServerApplicationSettings.h" +#include "../../utils/Utils.h" + +#include +#include + +namespace HttpServer +{ + enum class ConfigMimeState { + NONE, + TYPE + }; + + enum class ConfigState { + NONE, + PARAM, + BLOCK + }; + + constexpr long FILESIZE = 2 * 1024 * 1024; + + /** + * Config - include file + */ + bool ConfigParser::includeConfigFile( + const std::string &fileName, + std::string &strBuf, + const std::size_t offset + ) { + std::ifstream file(fileName); + + if ( ! file) { + file.close(); + std::cout << "Error: file " << fileName << " cannot be open;" << std::endl; + return false; + } + + file.seekg(0, std::ifstream::end); + std::streamsize file_size = file.tellg(); + file.seekg(0, std::ifstream::beg); + + constexpr std::streamsize file_size_max = FILESIZE; + + if (file_size_max < file_size) { + file.close(); + std::cout << "Error: file " << fileName << " is too large; max include file size = " << file_size_max << " bytes;" << std::endl; + return false; + } + + if (file_size) { + std::vector buf(file_size); + file.read(buf.data(), file_size); + + strBuf.insert( + strBuf.begin() + long(offset), + buf.cbegin(), + buf.cend() + ); + } + + file.close(); + + return true; + } + + /** + * Config - add application + */ + bool ConfigParser::addApplication( + const std::unordered_multimap &app, + const ServerApplicationDefaultSettings &defaults, + std::vector &modules, + ServerApplicationsTree &apps_tree + ) { + auto const it_name = app.find("server_name"); + + if (app.cend() == it_name) { + std::cout << "Error: application parameter 'server_name' has not been specified;" << std::endl; + return false; + } + + std::vector names; + + const std::string whitespace(" \t\n\v\f\r"); + + const std::string &app_name = it_name->second; + + size_t delimiter = app_name.find_first_of(whitespace); + + if (delimiter) { + size_t cur_pos = 0; + + while (std::string::npos != delimiter) { + std::string name = app_name.substr( + cur_pos, + delimiter - cur_pos + ); + + names.emplace_back(std::move(name) ); + + cur_pos = app_name.find_first_not_of( + whitespace, + delimiter + 1 + ); + + delimiter = app_name.find_first_of( + whitespace, + cur_pos + ); + } + + std::string name = app_name.substr(cur_pos); + names.emplace_back(std::move(name) ); + } + + auto const range_port = app.equal_range("listen"); + + if (range_port.first == range_port.second) { + std::cout << "Error: application port is not set;" << std::endl; + return false; + } + + std::unordered_set ports; + std::unordered_set tls_ports; + + for (auto it = range_port.first; it != range_port.second; ++it) + { + const std::string &lis = it->second; + + const bool is_tls = std::string::npos != lis.find("tls"); + + const std::vector list = Utils::explode(lis, ' '); + + for (auto const &value : list) + { + const int port = std::atoi(value.c_str()); + + if (port) { + if (is_tls) { + tls_ports.emplace(port); + } else { + ports.emplace(port); + } + } + } + } + + std::string cert_file; + std::string key_file; + std::string chain_file; + std::string crl_file; + std::string stapling_file; + std::string dh_file; + + if (tls_ports.empty() == false) + { + auto const it_ca_file = app.find("tls_certificate_chain"); + + if (app.cend() != it_ca_file) { + chain_file = it_ca_file->second; + } + + auto const it_crl_file = app.find("tls_certificate_crl"); + + if (app.cend() != it_crl_file) { + crl_file = it_crl_file->second; + } + + auto const it_stapling_file = app.find("tls_stapling_file"); + + if (app.cend() != it_stapling_file) { + stapling_file = it_stapling_file->second; + } + + auto const it_dh_params_file = app.find("tls_dh_params_file"); + + if (app.cend() != it_dh_params_file) { + dh_file = it_dh_params_file->second; + } + + auto const it_cert_file = app.find("tls_certificate"); + + if (app.cend() == it_cert_file) { + std::cout << "Error: tls certificate file \"CERT\" has not been specified in configuration file;" << std::endl; + tls_ports.clear(); + } else { + cert_file = it_cert_file->second; + } + + auto const it_key_file = app.find("tls_certificate_key"); + + if (app.cend() == it_key_file) { + std::cout << "Error: tls certificate key file \"KEY\" has not been specified in configuration file;" << std::endl; + tls_ports.clear(); + } else { + key_file = it_key_file->second; + } + } + + auto const it_root_dir = app.find("root_dir"); + + if (app.cend() == it_root_dir || it_root_dir->second.empty() ) { + std::cout << "Error: application parameter 'root_dir' has not been specified;" << std::endl; + return false; + } + + auto const it_module = app.find("server_module"); + + if (app.cend() == it_module) { + std::cout << "Error: application parameter 'server_module' has not been specified;" << std::endl; + return false; + } + + // TODO: get module realpath + + System::Module module(it_module->second); + + if (module.is_open() == false) { + std::cout << "Error: module '" << it_module->second << "' cannot be open;" << std::endl; + return false; + } + + void *(*addr)(void *) = nullptr; + + if (module.find("application_call", &addr) == false) { + std::cout << "Error: function 'application_call' not found in module '" << it_module->second << "';" << std::endl; + return false; + } + + std::function app_call = reinterpret_cast(addr); + + if ( ! app_call) { + std::cout << "Error: invalid function 'application_call' in module '" << it_module->second << "';" << std::endl; + return false; + } + + if (module.find("application_clear", &addr) == false) { + std::cout << "Error: function 'application_clear' not found in module '" << it_module->second << "';" << std::endl; + return false; + } + + std::function app_clear = reinterpret_cast(addr); + + std::function app_init = std::function(); + + if (module.find("application_init", &addr) ) { + app_init = reinterpret_cast(addr); + } + + std::function app_final = std::function(); + + if (module.find("application_final", &addr) ) { + app_final = reinterpret_cast(addr); + } + + std::string root_dir = it_root_dir->second; + + #ifdef WIN32 + if (root_dir.back() == '\\') { + root_dir.pop_back(); + } + #endif + + // Remove back slash from root_dir + if (root_dir.back() == '/') { + root_dir.pop_back(); + } + + bool success = true; + + try { + if (app_init) { + const std::string &root = root_dir; + success = app_init(root.data() ); + } + } + catch (const std::exception &exc) { + std::cout << "Warning: an exception was thrown when the application '" << it_module->second << "' was initialized: " << exc.what() << std::endl; + success = false; + } + + if (false == success) { + std::cout << "Warning: error when initializing application '" << it_module->second << "';" << std::endl; + return false; + } + + auto const it_temp_dir = app.find("temp_dir"); + + std::string temp_dir = app.cend() != it_temp_dir + ? it_temp_dir->second + : defaults.temp_dir; + + auto const it_request_max_size = app.find("request_max_size"); + + const size_t request_max_size = app.cend() != it_request_max_size + ? std::strtoull(it_request_max_size->second.c_str(), nullptr, 10) + : defaults.request_max_size; + + auto const it_module_update = app.find("server_module_update"); + + std::string module_update = app.cend() != it_module_update + ? it_module_update->second + : std::string(); + + // Calculate module index + size_t module_index = std::numeric_limits::max(); + + for (size_t i = 0; i < modules.size(); ++i) { + if (modules[i] == module) { + module_index = i; + break; + } + } + + if (std::numeric_limits::max() == module_index) { + module_index = modules.size(); + modules.emplace_back(std::move(module) ); + } + + // Create application settings struct + ServerApplicationSettings *settings = new ServerApplicationSettings { + std::move(ports), + std::move(tls_ports), + + std::move(root_dir), + std::move(temp_dir), + request_max_size, + + module_index, + it_module->second, + std::move(module_update), + + std::move(cert_file), + std::move(key_file), + std::move(chain_file), + std::move(crl_file), + std::move(stapling_file), + std::move(dh_file), + + std::move(app_call), + std::move(app_clear), + std::move(app_init), + std::move(app_final) + }; + + // Add application names in tree + if (names.empty() ) { + apps_tree.addApplication(app_name, settings); + } else { + for (auto const &name : names) { + apps_tree.addApplication(name, settings); + } + } + + return true; + } + + /** + * @brief ConfigParser::parseMimes + * @param fileName + * @param mimes_types + * @return bool + */ + bool ConfigParser::parseMimes( + const std::string &fileName, + std::unordered_map &mimes_types + ) { + std::ifstream file(fileName); + + if ( ! file) { + file.close(); + std::cout << "Error: " << fileName << " - cannot be open;" << std::endl; + return false; + } + + file.seekg(0, std::ifstream::end); + std::streamsize file_size = file.tellg(); + file.seekg(0, std::ifstream::beg); + + const std::streamsize file_size_max = FILESIZE; + + if (file_size_max < file_size) { + file.close(); + std::cout << "Error: " << fileName << " - is too large; max file size = " << file_size_max << " bytes;" << std::endl; + return false; + } + + const std::string whitespace(" \t\v\f\r"); + + std::vector buf(file_size); + + file.read(buf.data(), file_size); + + const std::string str_buf(buf.cbegin(), buf.cend() ); + + size_t delimiter = 0; + size_t cur_pos = 0; + size_t end_pos = str_buf.find('\n', cur_pos); + + ConfigMimeState state = ConfigMimeState::NONE; + + while (std::string::npos != end_pos) + { + switch (state) + { + case ConfigMimeState::NONE: + { + cur_pos = str_buf.find_first_not_of(whitespace, cur_pos); + delimiter = str_buf.find_first_of(whitespace, cur_pos); + + if (delimiter >= end_pos) { + cur_pos = end_pos + 1; + end_pos = str_buf.find('\n', cur_pos); + break; + } + + if ('#' != str_buf[cur_pos]) { + state = ConfigMimeState::TYPE; + } + + break; + } + + case ConfigMimeState::TYPE: + { + state = ConfigMimeState::NONE; + + std::string mime_type = str_buf.substr(cur_pos, delimiter - cur_pos); + + delimiter = str_buf.find_first_not_of(whitespace, delimiter); + + if (delimiter >= end_pos) { + cur_pos = end_pos + 1; + end_pos = str_buf.find('\n', cur_pos); + break; + } + + std::string ext = str_buf.substr(delimiter, end_pos - delimiter); + + delimiter = ext.find_first_of(whitespace); + + if (std::string::npos != delimiter) + { + for (size_t ext_pos = 0; std::string::npos != ext_pos; ) + { + std::string ext_unit = ext.substr(ext_pos, + std::string::npos == delimiter + ? std::string::npos + : delimiter - ext_pos + ); + + if (ext_unit.empty() == false) { + mimes_types.emplace( + std::move(ext_unit), + mime_type + ); + } + + ext_pos = ext.find_first_not_of(whitespace, delimiter); + + delimiter = ext.find_first_of(whitespace, ext_pos); + } + } + else + { + mimes_types.emplace( + std::move(ext), + std::move(mime_type) + ); + } + + cur_pos = end_pos + 1; + end_pos = str_buf.find('\n', cur_pos); + + break; + } + + default: + break; + } + } + + return true; + } + + static size_t findBlockEnd(const std::string &str_buf, size_t str_pos) + { + size_t pos = str_buf.find('}', str_pos); + + while (std::string::npos != pos) + { + size_t begin_line = str_buf.rfind('\n', pos); + + if (std::string::npos == begin_line) { + begin_line = 0; + } + + begin_line = str_buf.find_first_not_of("\r\n", begin_line); + + if ('#' != str_buf[begin_line]) { + break; + } + + str_pos = str_buf.find_first_of("\r\n", pos); + + pos = str_buf.find('}', str_pos); + } + + return pos; + } + + /** + * Config - parse + */ + bool ConfigParser::loadConfig( + const std::string &conf_file_name, + ServerSettings &settings, + std::vector &modules + ) { + std::string str_buf; + + if (includeConfigFile(conf_file_name, str_buf) == false) { + return false; + } + + std::unordered_map &global = settings.global; + std::unordered_map &mimes_types = settings.mimes_types; + ServerApplicationsTree &apps_tree = settings.apps_tree; + + std::vector > applications; + + const std::string whitespace(" \t\n\v\f\r"); + + size_t cur_pos = 0; + size_t end_pos = 0; + size_t block_pos = 0; + + ConfigState state = ConfigState::NONE; + + while (std::string::npos != end_pos) + { + switch (state) + { + case ConfigState::NONE: + { + end_pos = str_buf.find(';', cur_pos); + block_pos = str_buf.find('{', cur_pos); + + if (end_pos < block_pos) { + state = ConfigState::PARAM; + } + else if (std::string::npos != block_pos) { + state = ConfigState::BLOCK; + } + + break; + } + + case ConfigState::PARAM: + { + state = ConfigState::NONE; + + cur_pos = str_buf.find_first_not_of(whitespace, cur_pos); + end_pos = str_buf.find(';', cur_pos); + size_t delimiter = str_buf.find_first_of(whitespace, cur_pos); + + if (delimiter < end_pos) + { + std::string param_name = str_buf.substr(cur_pos, delimiter - cur_pos); + + if ('#' != param_name.front() ) + { + cur_pos = str_buf.find_first_not_of(whitespace, delimiter + 1); + delimiter = str_buf.find_last_not_of(whitespace, end_pos); + + std::string param_value = str_buf.substr(cur_pos, delimiter - cur_pos); + + if ("include" == param_name) { + this->includeConfigFile( + param_value, + str_buf, + end_pos + 1 + ); + } else { + global.emplace( + std::move(param_name), + std::move(param_value) + ); + } + } else { + // if comment line + end_pos = str_buf.find_first_of("\r\n", cur_pos); + } + } + + cur_pos = std::string::npos == end_pos + ? std::string::npos + : end_pos + 1; + + break; + } + + case ConfigState::BLOCK: + { + state = ConfigState::NONE; + + cur_pos = str_buf.find_first_not_of(whitespace, cur_pos); + end_pos = str_buf.find(';', cur_pos); + size_t delimiter = str_buf.find_first_of(whitespace, cur_pos); + + const std::string block_type_name = str_buf.substr(cur_pos, delimiter - cur_pos); + + if ('#' != block_type_name.front() ) + { + delimiter = str_buf.find_first_not_of(whitespace, delimiter); + + cur_pos = block_pos + 1; + size_t block_end = findBlockEnd(str_buf, cur_pos); + + if (std::string::npos == block_end) + { + std::cout << "Error: symbol '}' after '" << block_type_name << "' has not been found;" << std::endl + << "Parsing config aborted;" << std::endl; + + return false; + } + else if (delimiter == block_pos) + { + if ("server" == block_type_name) + { + std::unordered_multimap app; + + end_pos = str_buf.find(';', cur_pos); + + while (block_end > end_pos) + { + cur_pos = str_buf.find_first_not_of(whitespace, cur_pos); + delimiter = str_buf.find_first_of(whitespace, cur_pos); + + if (delimiter < end_pos) + { + std::string param_name = str_buf.substr(cur_pos, delimiter - cur_pos); + + if ('#' != param_name.front() ) + { + cur_pos = str_buf.find_first_not_of(whitespace, delimiter + 1); + delimiter = str_buf.find_last_not_of(whitespace, end_pos); + + std::string param_value = str_buf.substr(cur_pos, delimiter - cur_pos); + + if ("include" == param_name) { + cur_pos = end_pos + 1; + this->includeConfigFile(param_value, str_buf, cur_pos); + block_end = findBlockEnd(str_buf, cur_pos); + } else { + app.emplace( + std::move(param_name), + std::move(param_value) + ); + } + } else { + // if comment line + end_pos = str_buf.find_first_of("\r\n", cur_pos); + } + } + + cur_pos = std::string::npos == end_pos + ? std::string::npos + : end_pos + 1; + + end_pos = str_buf.find(';', cur_pos); + } + + applications.emplace_back(std::move(app) ); + } + else + { + std::cout << "Warning: " << block_type_name << " - unknown block type;" << std::endl; + } + } + else + { + std::cout << "Warning: after " << block_type_name << " expected '{' ;" << std::endl; + } + + cur_pos = std::string::npos == block_end + ? std::string::npos + : block_end + 1; + } + else // if comment line + { + cur_pos = str_buf.find_first_of("\r\n", cur_pos); + } + + break; + } + + default: + break; + } + } + + auto const it_mimes = global.find("mimes"); + + if (global.cend() != it_mimes) { + this->parseMimes(it_mimes->second, mimes_types); + } else { + std::cout << "Warning: mime types file is not set in configuration;" << std::endl; + } + + if (applications.empty() == false) + { + auto const it_default_temp_dir = global.find("default_temp_dir"); + + const std::string default_temp_dir = global.cend() != it_default_temp_dir + ? it_default_temp_dir->second + : System::getTempDir(); + + auto const it_default_request_max_size = global.find("request_max_size"); + + const size_t default_request_max_size = global.cend() != it_default_request_max_size + ? std::strtoull(it_default_request_max_size->second.c_str(), nullptr, 10) + : 0; + + ServerApplicationDefaultSettings defaults { + default_temp_dir, + default_request_max_size + }; + + for (auto const &app : applications) { + this->addApplication(app, defaults, modules, apps_tree); + } + } + + if (apps_tree.empty() ) { + std::cout << "Notice: server does not contain applications;" << std::endl; + } + + return true; + } +} diff --git a/src/server/config/ConfigParser.h b/src/server/config/ConfigParser.h new file mode 100644 index 0000000..f994a00 --- /dev/null +++ b/src/server/config/ConfigParser.h @@ -0,0 +1,43 @@ +#pragma once + +#include "../ServerSettings.h" +#include "../../system/Module.h" + +#include + +namespace HttpServer +{ + class ConfigParser + { + private: + struct ServerApplicationDefaultSettings { + std::string temp_dir; + size_t request_max_size; + }; + + static bool includeConfigFile( + const std::string &fileName, + std::string &strBuf, + const size_t offset = 0 + ); + + static bool addApplication( + const std::unordered_multimap &app, + const ServerApplicationDefaultSettings &defaults, + std::vector &modules, + ServerApplicationsTree &apps_tree + ); + + static bool parseMimes( + const std::string &fileName, + std::unordered_map &mimes_types + ); + + public: + bool loadConfig( + const std::string &conf, + ServerSettings &settings, + std::vector &modules + ); + }; +} diff --git a/src/server/data-variant/Abstract.cpp b/src/server/data-variant/Abstract.cpp new file mode 100644 index 0000000..37d9744 --- /dev/null +++ b/src/server/data-variant/Abstract.cpp @@ -0,0 +1,18 @@ + +#include "Abstract.h" + +namespace DataVariant +{ + const std::string &Abstract::getName() const noexcept { + return this->data_variant_name; + } + + void *Abstract::createStateStruct( + const Transfer::request_data *rd, + const std::unordered_map &contentParams + ) const { + return nullptr; + } + + void Abstract::destroyStateStruct(void *st) const noexcept {} +} diff --git a/src/server/data-variant/Abstract.h b/src/server/data-variant/Abstract.h new file mode 100644 index 0000000..f9f239c --- /dev/null +++ b/src/server/data-variant/Abstract.h @@ -0,0 +1,46 @@ +#pragma once + +#include "../../socket/Adapter.h" +#include "../Request.h" + +namespace DataVariant +{ + struct DataReceiver; + + class Abstract + { + protected: + std::string data_variant_name; + + public: + const std::string &getName() const noexcept; + + public: + /** + * Virtual destructor + */ + virtual ~Abstract() noexcept = default; + + virtual void *createStateStruct( + const Transfer::request_data *rd, + const std::unordered_map &contentParams + ) const; + + virtual bool parse( + const std::string &buf, + Transfer::request_data *rd, DataReceiver *dr + ) const = 0; + + virtual void destroyStateStruct(void *st) const noexcept; + }; + + struct DataReceiver + { + const Abstract *data_variant; + void *ss; + size_t full_size; + size_t recv_total; + size_t left; + void *reserved; + }; +} diff --git a/src/server/data-variant/FormUrlencoded.cpp b/src/server/data-variant/FormUrlencoded.cpp new file mode 100644 index 0000000..28adff0 --- /dev/null +++ b/src/server/data-variant/FormUrlencoded.cpp @@ -0,0 +1,83 @@ + +#include "FormUrlencoded.h" + +#include "../../utils/Utils.h" + +namespace DataVariant +{ + FormUrlencoded::FormUrlencoded() noexcept { + this->data_variant_name = "application/x-www-form-urlencoded"; + } + + bool FormUrlencoded::parse(const std::string &buf, Transfer::request_data *rd, DataReceiver *dr) const + { + if (buf.empty() ) { + return 0 == dr->full_size || dr->full_size != dr->recv_total; + } + + for ( + size_t var_pos = 0, var_end = 0; + std::string::npos != var_end; + var_pos = var_end + 1 + ) { + // Search next parameter + var_end = buf.find('&', var_pos); + + if (std::string::npos == var_end) { + if (dr->full_size != dr->recv_total) { + dr->left = buf.size() - var_pos; + return true; + } + } + + // Search parameter value + size_t delimiter = buf.find('=', var_pos); + + if (delimiter >= var_end) { + // Get parameter name + std::string var_name = Utils::urlDecode( + buf.substr( + var_pos, + std::string::npos != var_end + ? var_end - var_pos + : std::string::npos + ) + ); + + // Store parameter with empty value + rd->incoming_data.emplace( + std::move(var_name), + std::string() + ); + } else { + // Get parameter name + std::string var_name = Utils::urlDecode( + buf.substr( + var_pos, + delimiter - var_pos + ) + ); + + ++delimiter; + + // Get parameter value + std::string var_value = Utils::urlDecode( + buf.substr( + delimiter, + std::string::npos != var_end + ? var_end - delimiter + : std::string::npos + ) + ); + + // Store parameter and value + rd->incoming_data.emplace( + std::move(var_name), + std::move(var_value) + ); + } + } + + return true; + } +} diff --git a/src/server/data-variant/FormUrlencoded.h b/src/server/data-variant/FormUrlencoded.h new file mode 100644 index 0000000..141c525 --- /dev/null +++ b/src/server/data-variant/FormUrlencoded.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Abstract.h" + +namespace DataVariant +{ + class FormUrlencoded: public Abstract + { + public: + FormUrlencoded() noexcept; + + public: + virtual bool parse(const std::string &buf, Transfer::request_data *rd, DataReceiver *dr) const override; + }; +} diff --git a/src/server/data-variant/MultipartFormData.cpp b/src/server/data-variant/MultipartFormData.cpp new file mode 100644 index 0000000..56ec8cc --- /dev/null +++ b/src/server/data-variant/MultipartFormData.cpp @@ -0,0 +1,433 @@ + +#include "../../utils/Utils.h" +#include "MultipartFormData.h" + +#include + +namespace DataVariant +{ + MultipartFormData::MultipartFormData() noexcept { + this->data_variant_name = "multipart/form-data"; + } + + enum class ParsingState : uint8_t { + INITIALIZATION = 0, + FIND_DATA_BLOCK, + GET_DATA_BLOCK_TYPE, + COPY_DATA_BLOCK, + SAVE_DATA_BLOCK, + }; + + enum class BlockType : uint8_t { + UNKNOWN = 0, + DATA, + FILE, + }; + + struct StateMultipartFormData { + std::string block_name; + std::string block_value; + std::string file_tmp_name; + std::string file_name; + std::string file_type; + std::ofstream file; + std::string boundary; + ParsingState state; + BlockType block_type; + }; + + void *MultipartFormData::createStateStruct( + const Transfer::request_data *rd, + const std::unordered_map &contentParams + ) const { + std::string boundary; + + auto const it = contentParams.find("boundary"); + + if (contentParams.cend() != it) { + boundary = it->second; + } + + return new StateMultipartFormData { + std::string(), std::string(), std::string(), std::string(), std::string(), std::ofstream(), + std::move(boundary), ParsingState::INITIALIZATION, BlockType::UNKNOWN + }; + } + + void MultipartFormData::destroyStateStruct(void *st) const noexcept { + delete reinterpret_cast(st); + } + + static std::unordered_map parseHeader(const std::string &buf, size_t cur, const size_t end) + { + const std::string nl("\r\n"); + + std::unordered_map headers; + + for ( + size_t line_end = buf.find(nl.data(), cur); + cur < end; + line_end = buf.find(nl.data(), cur) + ) { + size_t delimiter = buf.find(':', cur); + + if (std::string::npos == delimiter || delimiter > line_end) + { + std::string header_name = buf.substr(cur, line_end - cur); + Utils::trim(header_name); + Utils::toLower(header_name); + + headers.emplace( + std::move(header_name), + std::string() + ); + } else { + std::string header_name = buf.substr(cur, delimiter - cur); + Utils::trim(header_name); + Utils::toLower(header_name); + + ++delimiter; + + std::string header_value = buf.substr(delimiter, line_end - delimiter); + Utils::trim(header_value); + + headers.emplace( + std::move(header_name), + std::move(header_value) + ); + } + + // Перейти к следующему заголовку + cur = line_end + nl.length(); + } + + return headers; + } + + static std::unordered_map parseMainHeaderParams(const std::string &header) + { + // Разобрать значение заголовка данных на параметры + std::unordered_map header_params; + + size_t delimiter = header.find(';'); + + if (std::string::npos == delimiter) { + return header_params; + } + + std::string content_disposition(header.substr(0, delimiter) ); + Utils::trim(content_disposition); + + // Проверить соответствие указанного формата + if ("form-data" != content_disposition) { + return header_params; + } + + // Получить параметры блока данных + for (size_t cur = delimiter + 1, end; std::string::npos != cur; cur = end) + { + end = header.find(';', cur); + delimiter = header.find('=', cur); + + if (std::string::npos == delimiter || delimiter > end) + { + std::string param_name = header.substr( + cur, + std::string::npos != end + ? end - cur + : std::string::npos + ); + + Utils::trim(param_name); + Utils::toLower(param_name); + + header_params.emplace( + std::move(param_name), + std::string() + ); + } else { + std::string param_name = header.substr(cur, delimiter - cur); + Utils::trim(param_name); + Utils::toLower(param_name); + + ++delimiter; + + delimiter = header.find('"', delimiter); + + if (std::string::npos == delimiter) + { + end = header.find(';', cur); + + std::string param_value = header.substr( + delimiter, + std::string::npos != end + ? end - delimiter + : std::string::npos + ); + + Utils::trim(param_value); + + header_params.emplace( + std::move(param_name), + std::move(param_value) + ); + } else { + ++delimiter; + + cur = header.find('"', delimiter); + end = header.find(';', cur); + + std::string param_value = header.substr( + delimiter, + std::string::npos != cur + ? cur - delimiter + : std::string::npos + ); + + header_params.emplace( + std::move(param_name), + std::move(param_value) + ); + } + } + + if (std::string::npos != end) { + ++end; + } + } + + return header_params; + } + + bool MultipartFormData::parse( + const std::string &buf, + Transfer::request_data *rd, + DataReceiver *dr + ) const { + StateMultipartFormData *ss = reinterpret_cast(dr->ss); + + size_t cur = 0; + + while (cur < buf.size() ) + { + switch (ss->state) + { + case ParsingState::INITIALIZATION: + { + if (ss->boundary.empty() ) { + return false; + } + + const std::string data_end("--" + ss->boundary + "--\r\n"); + + if (buf.size() < data_end.length() ) { + dr->left = buf.size(); + return dr->full_size != dr->recv_total; + } + + if (0 == buf.find(data_end) ) { + return dr->full_size == data_end.length() && dr->full_size == dr->recv_total; + } + + const std::string first_block("--" + ss->boundary); + + cur = buf.find(first_block); + + if (0 != cur) { + return false; + } + + cur += first_block.length() + 2; + + ss->state = ParsingState::GET_DATA_BLOCK_TYPE; + + break; + } + + case ParsingState::FIND_DATA_BLOCK: + { + dr->left = buf.size() - cur; + + const std::string data_end("\r\n--" + ss->boundary + "--\r\n"); + + if (data_end.length() > dr->left) { + return dr->full_size != dr->recv_total; + } + + const size_t end = buf.find(data_end, cur); + + if (end == cur) { + dr->left -= data_end.length(); + return dr->full_size == dr->recv_total; + } + + const std::string block_delimiter("\r\n--" + ss->boundary); + + cur = buf.find(block_delimiter, cur); + + if (std::string::npos == cur) { + return dr->full_size != dr->recv_total; + } + + cur += block_delimiter.length() + 2; + + ss->state = ParsingState::GET_DATA_BLOCK_TYPE; + + // break; + } + + case ParsingState::GET_DATA_BLOCK_TYPE: + { + const size_t end = buf.find("\r\n\r\n", cur); + + if (std::string::npos == end) { + dr->left = buf.size() - cur; + return dr->full_size != dr->recv_total; + } + + // Разобрать заголовки блока данных + const std::unordered_map headers = parseHeader(buf, cur, end); + + // Определить параметры блока данных + auto const it = headers.find("content-disposition"); + + // Если заголовок не определён + if (headers.cend() == it) { + return false; + } + + const std::unordered_map header_params = parseMainHeaderParams(it->second); + + // Поиск имени блока данных + auto const it_name = header_params.find("name"); + + if (header_params.cend() == it_name) { + return false; + } + + ss->block_name = it_name->second; + + auto const it_filename = header_params.find("filename"); + + ss->block_type = header_params.cend() == it_filename ? BlockType::DATA : BlockType::FILE; + + if (BlockType::FILE == ss->block_type) + { + ss->file_name = it_filename->second; + + // Найти тип файла + auto const it_filetype = headers.find("content-type"); + + if (headers.cend() != it_filetype) { + ss->file_type = it_filetype->second; + } + + // Сгенерировать уникальное имя + ss->file_tmp_name = System::getTempDir() + Utils::getUniqueName(); + + // Создать файл + ss->file.open(ss->file_tmp_name, std::ofstream::trunc | std::ofstream::binary); + + if (ss->file.is_open() == false) { + return false; + } + } + + // Перейти к данным + cur = end + 4; + + ss->state = ParsingState::COPY_DATA_BLOCK; + + // break; + } + + case ParsingState::COPY_DATA_BLOCK: + { + const std::string block_delimiter("\r\n--" + ss->boundary); + + // Поиск конца блока данных (возможное начало следующего блока) + const size_t block_end = buf.find(block_delimiter, cur); + + const size_t end = (std::string::npos == block_end) ? buf.size() - block_delimiter.length() : block_end; + + switch (ss->block_type) + { + case BlockType::DATA: { + ss->block_value.append( + buf.cbegin() + long(cur), + buf.cbegin() + long(end) + ); + + break; + } + + case BlockType::FILE: { + ss->file.write( + &buf[cur], + std::streamsize(end - cur) + ); + + break; + } + + default: + return false; + } + + if (std::string::npos == block_end) { + dr->left = buf.size() - end; + return dr->full_size != dr->recv_total; + } + + cur = end; + + ss->state = ParsingState::SAVE_DATA_BLOCK; + + // break; + } + + case ParsingState::SAVE_DATA_BLOCK: + { + switch (ss->block_type) + { + case BlockType::DATA: { + rd->incoming_data.emplace( + std::move(ss->block_name), + std::move(ss->block_value) + ); + + break; + } + + case BlockType::FILE: { + rd->incoming_files.emplace( + std::move(ss->block_name), + Transfer::FileIncoming( + std::move(ss->file_tmp_name), + std::move(ss->file_name), + std::move(ss->file_type), + size_t(ss->file.tellp()) + ) + ); + + ss->file.close(); + break; + } + + default: + return false; + } + + ss->state = ParsingState::FIND_DATA_BLOCK; + + break; + } + + default: + return false; + } + } + + return dr->full_size != dr->recv_total; + } +} diff --git a/src/server/data-variant/MultipartFormData.h b/src/server/data-variant/MultipartFormData.h new file mode 100644 index 0000000..1a45fc0 --- /dev/null +++ b/src/server/data-variant/MultipartFormData.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Abstract.h" + +namespace DataVariant +{ + class MultipartFormData: public Abstract + { + public: + MultipartFormData() noexcept; + + public: + virtual void *createStateStruct( + const Transfer::request_data *rd, + const std::unordered_map &contentParams + ) const override; + + virtual bool parse( + const std::string &buf, + Transfer::request_data *rd, + DataReceiver *dr + ) const override; + + virtual void destroyStateStruct(void *st) const noexcept override; + }; +} diff --git a/src/server/data-variant/TextPlain.cpp b/src/server/data-variant/TextPlain.cpp new file mode 100644 index 0000000..ecb5efa --- /dev/null +++ b/src/server/data-variant/TextPlain.cpp @@ -0,0 +1,73 @@ + +#include "TextPlain.h" + +namespace DataVariant +{ + TextPlain::TextPlain() noexcept { + this->data_variant_name = "text/plain"; + } + + bool TextPlain::parse(const std::string &buf, Transfer::request_data *rd, DataReceiver *dr) const + { + if (buf.empty() ) { + return 0 == dr->full_size || dr->full_size != dr->recv_total; + } + + for ( + size_t var_pos = 0, var_end = 0; + std::string::npos != var_end; + var_pos = var_end + 1 + ) { + // Поиск следующего параметра + var_end = buf.find('&', var_pos); + + if (std::string::npos == var_end) { + if (dr->full_size != dr->recv_total) { + dr->left = buf.size() - var_pos; + return true; + } + } + + // Поиск значения параметра + size_t delimiter = buf.find('=', var_pos); + + if (delimiter >= var_end) + { + // Получить имя параметра + std::string var_name = buf.substr( + var_pos, + std::string::npos != var_end + ? var_end - var_pos + : std::string::npos + ); + + // Сохранить параметр с пустым значением + rd->incoming_data.emplace( + std::move(var_name), + std::string() + ); + } else { + // Получить имя параметра + std::string var_name = buf.substr(var_pos, delimiter - var_pos); + + ++delimiter; + + // Получить значение параметра + std::string var_value = buf.substr( + delimiter, + std::string::npos != var_end + ? var_end - delimiter + : std::string::npos + ); + + // Сохранить параметр и значение + rd->incoming_data.emplace( + std::move(var_name), + std::move(var_value) + ); + } + } + + return true; + } +} diff --git a/src/server/data-variant/TextPlain.h b/src/server/data-variant/TextPlain.h new file mode 100644 index 0000000..d6ba101 --- /dev/null +++ b/src/server/data-variant/TextPlain.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Abstract.h" + +namespace DataVariant +{ + class TextPlain: public Abstract + { + public: + TextPlain() noexcept; + + public: + virtual bool parse(const std::string &buf, Transfer::request_data *rd, DataReceiver *dr) const override; + }; +} diff --git a/src/server/protocol/ServerHttp1.cpp b/src/server/protocol/ServerHttp1.cpp new file mode 100644 index 0000000..8bb5f17 --- /dev/null +++ b/src/server/protocol/ServerHttp1.cpp @@ -0,0 +1,658 @@ + +#include "ServerHttp1.h" + +#include "extensions/Sendfile.h" +#include "ServerWebSocket.h" + +#include "../../utils/Utils.h" + +#include + +namespace HttpServer +{ + ServerHttp1::ServerHttp1( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls + ) noexcept + : ServerProtocol(sock, settings, controls) + { + + } + + bool ServerHttp1::sendHeaders( + const Http::StatusCode status, + std::vector > &headers, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const { + static const std::unordered_map status_list { + { 200, "OK" }, + { 206, "Partial Content" }, + { 304, "Not Modified" }, + { 400, "Bad Request" }, + { 404, "Not Found" }, + { 413, "Request Entity Too Large" }, + { 416, "Requested Range Not Satisfiable" }, + { 500, "Internal Server Error" }, + }; + + std::string str = "HTTP/1.1 " + std::to_string(static_cast(status) ); + + auto const it = status_list.find(static_cast(status) ); + + if (status_list.cend() != it) { + const std::string &status = it->second; + str += ' ' + status; + } + + str += "\r\n"; + + for (auto const &header : headers) { + str += header.first + ": " + header.second + "\r\n"; + } + + str += "\r\n"; + + return this->sock.nonblock_send(str, timeout) > 0; // >= 0 + } + + long ServerHttp1::sendData( + const void *src, + size_t size, + const std::chrono::milliseconds &timeout, + DataTransfer *dt + ) const { + const long send_size = this->sock.nonblock_send( + src, + size, + timeout + ); + + if (send_size > 0) { + dt->send_total += static_cast(send_size); + } + + return send_size; + } + + void ServerHttp1::close() { + this->sock.close(); + } + + bool ServerHttp1::packRequestParameters( + std::vector &buf, + const struct Request &req, + const std::string &rootDir + ) const { + Utils::packNumber(buf, static_cast(Transfer::ProtocolVariant::HTTP_1) ); + Utils::packString(buf, rootDir); + Utils::packString(buf, req.host); + Utils::packString(buf, req.path); + Utils::packString(buf, req.method); + Utils::packContainer(buf, req.incoming_headers); + Utils::packContainer(buf, req.incoming_data); + Utils::packFilesIncoming(buf, req.incoming_files); + + return true; + } + + void ServerHttp1::unpackResponseParameters( + struct Request &req, + const void *src + ) const { + Utils::unpackContainer( + req.outgoing_headers, + reinterpret_cast(src) + ); + } + + static bool getRequest( + const Socket::Adapter &sock, + struct Request &req, + std::vector &buf, + std::string &str_buf + ) { + // Получить данные запроса от клиента + const long recv_size = sock.nonblock_recv( + buf, + req.timeout + ); + + if (recv_size < 0 && str_buf.empty() ) { + return false; + } + + if (recv_size > 0) { // Если данные были получены + str_buf.append( + buf.cbegin(), + buf.cbegin() + recv_size + ); + } + + return true; + } + + static Http::StatusCode getRequestHeaders( + struct Request &req, + std::string &str_buf + ) { + // Если запрос пустой + if (str_buf.empty() ) { + return Http::StatusCode::BAD_REQUEST; + } + + // Поиск конца заголовков (пустая строка) + size_t headers_end = str_buf.find("\r\n\r\n"); + + // Если найден конец заголовков + if (std::string::npos == headers_end) { + return Http::StatusCode::BAD_REQUEST; + } + + headers_end += 2; + + size_t str_cur = 0; + // Поиск конца первого заголовка + size_t str_end = str_buf.find("\r\n"); + + // Если не найден конец заголовка + if (std::string::npos == str_end) { + return Http::StatusCode::BAD_REQUEST; + } + + // Установка конца строки (для поиска) + str_buf[str_end] = '\0'; + + // Разделить метод запроса и параметры запроса + size_t delimiter = str_buf.find(' ', str_cur); + + // Получить метод запроса (GET, POST, PUT, DELETE, ...) + req.method = str_buf.substr( + str_cur, + delimiter - str_cur + ); + + Utils::toLower(req.method); + + // Сохранить метод и параметры запроса + // rp.incoming_headers[rp.method] = str_buf.substr(delimiter + 1, str_end - delimiter - 1); + + delimiter += 1; + // Найти окончание URI + size_t uri_end = str_buf.find(' ', delimiter); + + // Если окончание не найдено + if (std::string::npos == uri_end) { + uri_end = str_end; + // то версия протокола HTTP - 0.9 + // const std::string version = "0.9"; + } else { + // Если окончание найдено + str_buf[uri_end] = '\0'; + const size_t ver_beg = uri_end + 6; // Пропустить "HTTP/" + + if (ver_beg < str_end) { + // Получить версию протокола HTTP + // const std::string version = str_buf.substr(ver_beg, str_end - ver_beg); + } + } + + // Сохранить полную ссылку URI + req.path = str_buf.substr(delimiter, uri_end - delimiter); + + // Переход к обработке следующего заголовка + str_cur = str_end + 2; + // Поиск конца заголовка + str_end = str_buf.find("\r\n", str_cur); + // Установка конца заголовка + str_buf[str_end] = '\0'; + + // Цикл извлечения заголовков запроса + for ( + ; + str_cur != headers_end; + str_end = str_buf.find("\r\n", str_cur), str_buf[str_end] = '\0' + ) { + // Поиск разделителя названия заголовка и его значения + delimiter = str_buf.find(':', str_cur); + + // Если разделитель найден в текущей строке + if (delimiter < str_end) + { + std::string header_name = str_buf.substr( + str_cur, + delimiter - str_cur + ); + + Utils::toLower(header_name); + + std::string header_value = str_buf.substr( + delimiter + 1, + str_end - delimiter - 1 + ); + + // Удалить лишние пробелы в начале и в конце строки + Utils::trim(header_value); + + // Сохранить заголовок и его значение + req.incoming_headers.emplace( + std::move(header_name), + std::move(header_value) + ); + } + + // Перейти к следующей строке + str_cur = str_end + 2; + } + + str_buf.erase(0, headers_end + 2); + + return Http::StatusCode::EMPTY; + } + + const ServerApplicationSettings * + ServerHttp1::getApplicationSettings( + struct Request &req, + const bool isSecureConnection + ) const { + // Получить доменное имя (или адрес) назначения запроса + auto const it_host = req.incoming_headers.find("host"); + + // Если имя задано - продолжить обработку запроса + if (req.incoming_headers.cend() != it_host) + { + const std::string &host_header = it_host->second; + + // Поиск разделителя, за которым помещается номер порта, если указан + const size_t delimiter = host_header.find(':'); + + // Получить имя (или адрес) + req.host = host_header.substr(0, delimiter); + + const int default_port = isSecureConnection ? 443 : 80; + + // Получить номер порта + const int port = (std::string::npos != delimiter) + ? std::atoi(host_header.substr(delimiter + 1).c_str()) + : default_port; + + // Поиск настроек приложения по имени + const ServerApplicationSettings *app_sets = this->settings.apps_tree.find(req.host); + + // Если приложение найдено + if (app_sets && ( + app_sets->ports.cend() != app_sets->ports.find(port) || + app_sets->tls_ports.cend() != app_sets->tls_ports.find(port) + ) + ) { + return app_sets; + } + } + + return nullptr; + } + + Http::StatusCode ServerHttp1::getRequestData( + struct Request &req, + std::string &str_buf, + const ServerApplicationSettings &appSets + ) const { + // Определить вариант данных запроса (заодно проверить, есть ли данные) + auto const it = req.incoming_headers.find("content-type"); + + if (req.incoming_headers.cend() == it) { + return Http::StatusCode::EMPTY; + } + + // Получить значение заголовка + const std::string &header_value = it->second; + + std::string data_variant_name; // Название варианта данных запроса + + std::unordered_map content_params; + + // Определить, содержит ли тип данных запроса дополнительные параметры + size_t delimiter = header_value.find(';'); + + // Если есть дополнительные параметры - извлекаем их + if (std::string::npos != delimiter) + { + data_variant_name = header_value.substr(0, delimiter); + Utils::trim(data_variant_name); + + for ( + size_t str_param_cur = delimiter + 1, str_param_end = 0; + std::string::npos != str_param_end; + str_param_cur = str_param_end + 1 + ) { + str_param_end = header_value.find(';', str_param_cur); + delimiter = header_value.find('=', str_param_cur); + + if (delimiter >= str_param_end) + { + std::string param_name = header_value.substr( + str_param_cur, + std::string::npos != str_param_end + ? str_param_end - str_param_cur + : std::string::npos + ); + + Utils::trim(param_name); + + content_params.emplace( + std::move(param_name), + std::string() + ); + } + else + { + std::string param_name = header_value.substr( + str_param_cur, + delimiter - str_param_cur + ); + + Utils::trim(param_name); + + ++delimiter; + + std::string param_value = header_value.substr( + delimiter, + std::string::npos != str_param_end + ? str_param_end - delimiter + : std::string::npos + ); + + Utils::trim(param_value); + + content_params.emplace( + std::move(param_name), + std::move(param_value) + ); + } + } + } else { + data_variant_name = header_value; + } + + // Поиск варианта данных по имени типа + auto const variant = this->settings.variants.find(data_variant_name); + + // Если сервер не поддерживает формат полученных данных + if (this->settings.variants.cend() == variant) { + return Http::StatusCode::BAD_REQUEST; + } + + const DataVariant::Abstract *data_variant = variant->second; + + // Получить длину запроса в байтах + size_t data_length = 0; + + auto const it_len = req.incoming_headers.find("content-length"); + + if (req.incoming_headers.cend() != it_len) { + data_length = std::strtoull( + it_len->second.c_str(), + nullptr, + 10 + ); + } + + // Если размер запроса превышает лимит (если лимит был установлен) + if (data_length > appSets.request_max_size && 0 != appSets.request_max_size) { + return Http::StatusCode::REQUEST_ENTITY_TOO_LARGE; + } + + Transfer::request_data *rd = static_cast(&req); + + DataVariant::DataReceiver dr { + data_variant, + data_variant->createStateStruct(rd, content_params), + data_length, + 0, 0, nullptr, + }; + + std::string data_buf; + + if (str_buf.length() <= data_length) { + dr.recv_total = str_buf.length(); + data_buf.swap(str_buf); + } else { + data_buf.assign( + str_buf, 0, data_length + ); + + str_buf.erase(0, data_length); + + dr.recv_total = data_buf.size(); + } + + bool result = data_variant->parse(data_buf, rd, &dr); + + while (result && dr.full_size > dr.recv_total) + { + std::vector buf( + dr.full_size - dr.recv_total >= 512 * 1024 + ? 512 * 1024 + : dr.full_size - dr.recv_total + ); + + long recv_size = this->sock.nonblock_recv( + buf.data(), + buf.size(), + req.timeout + ); + + if (recv_size <= 0) { + result = false; + break; + } + + dr.recv_total += static_cast(recv_size); + + data_buf.erase( + 0, data_buf.length() - dr.left + ); + + data_buf.append( + buf.data(), + 0, static_cast(recv_size) + ); + + dr.left = 0; + + result = data_variant->parse(data_buf, rd, &dr); + } + + data_variant->destroyStateStruct(dr.ss); + + if (false == result) { + for (auto const &it : req.incoming_files) { + std::remove(it.second.getTmpName().c_str() ); + } + + return Http::StatusCode::BAD_REQUEST; + } + + if (dr.left) { + str_buf.assign( + data_buf, + data_buf.length() - dr.left, + data_buf.length() + ); + } + + return Http::StatusCode::EMPTY; + } + + static void sendStatus( + const Socket::Adapter &sock, + const struct Request &req, + const Http::StatusCode statusCode + ) { + static const std::unordered_map status_list { + { 400, "Bad Request" }, + { 404, "Not Found" }, + { 413, "Request Entity Too Large" }, + }; + + auto const it = status_list.find(static_cast(statusCode) ); + + if (status_list.cend() != it) { + const std::string &status = it->second; + + std::string headers("HTTP/1.1 " + std::to_string(static_cast(statusCode) ) + ' ' + status + "\r\n\r\n"); + + sock.nonblock_send(headers, req.timeout); + } + } + + static void getConnectionParams( + struct Request &req, + const bool isSecureConnection + ) { + auto const it_in_connection = req.incoming_headers.find("connection"); + auto const it_out_connection = req.outgoing_headers.find("connection"); + + if ( + req.incoming_headers.cend() != it_in_connection && + req.outgoing_headers.cend() != it_out_connection + ) { + const std::string connection_in = Utils::getLowerString(it_in_connection->second); + const std::string connection_out = Utils::getLowerString(it_out_connection->second); + + auto const incoming_params = Utils::explode(connection_in, ','); + + auto const it = std::find( + incoming_params.cbegin(), + incoming_params.cend(), + connection_out + ); + + if (incoming_params.cend() != it) + { + const std::string &inc = *it; + + if ("keep-alive" == inc) + { + --req.keep_alive_count; + + if (0 < req.keep_alive_count) { + req.connection_params |= ConnectionParams::CONNECTION_REUSE; + } + } + else if ("upgrade" == inc) + { + auto const it_out_upgrade = req.outgoing_headers.find("upgrade"); + + if (req.outgoing_headers.cend() != it_out_upgrade) + { + const std::string upgrade = Utils::getLowerString(it_out_upgrade->second); + + if ("h2" == upgrade) { + if (isSecureConnection) { + req.protocol_variant = Transfer::ProtocolVariant::HTTP_2; + req.connection_params |= ConnectionParams::CONNECTION_REUSE; + } + } + else if ("h2c" == upgrade) { + if (false == isSecureConnection) { + req.protocol_variant = Transfer::ProtocolVariant::HTTP_2; + req.connection_params |= ConnectionParams::CONNECTION_REUSE; + } + } + else if ("websocket" == upgrade) { + req.connection_params |= ConnectionParams::CONNECTION_LEAVE_OPEN; + } + } + } + } + } + } + + void ServerHttp1::useHttp1Protocol( + struct Request &req, + std::vector &buf, + std::string &str_buf + ) const { + if (getRequest(this->sock, req, buf, str_buf) == false) { + return; + } + + Http::StatusCode error_code = getRequestHeaders(req, str_buf); + + if (error_code != Http::StatusCode::EMPTY) { + sendStatus(this->sock, req, error_code); + return; + } + + const ServerApplicationSettings *app_sets = this->getApplicationSettings( + req, + this->sock.get_tls_session() != nullptr + ); + + // Если приложение не найдено + if (nullptr == app_sets) { + sendStatus(this->sock, req, Http::StatusCode::NOT_FOUND); + return; + } + + error_code = this->getRequestData(req, str_buf, *app_sets); + + if (error_code != Http::StatusCode::EMPTY) { + sendStatus(this->sock, req, error_code); + return; + } + + this->runApplication(req, *app_sets); + + for (auto const &it : req.incoming_files) { + std::remove(it.second.getTmpName().c_str() ); + } + + if (EXIT_SUCCESS == req.app_exit_code) { + getConnectionParams( + req, + this->sock.get_tls_session() != nullptr + ); + + Sendfile::xSendfile( + std::ref(*this), + req, + this->settings.mimes_types + ); + } + } + + static bool isConnectionLeaveOpen(const struct Request &req) { + return (req.connection_params & ConnectionParams::CONNECTION_LEAVE_OPEN) == ConnectionParams::CONNECTION_LEAVE_OPEN; + } + + ServerProtocol *ServerHttp1::process() + { + struct Request req; + req.timeout = std::chrono::milliseconds(5000); + req.protocol_variant = Transfer::ProtocolVariant::HTTP_1; + req.keep_alive_count = 100; + + std::vector buf(4096); + std::string str_buf; + + do { + // Подготовить параметры для получения данных + req.connection_params = ConnectionParams::CONNECTION_CLOSE; + req.app_exit_code = EXIT_FAILURE; + + this->useHttp1Protocol(req, buf, str_buf); + + req.clear(); + } + while (Sendfile::isConnectionReuse(req) ); + + if (isConnectionLeaveOpen(req) ) { + return new ServerWebSocket(*this); + } + + return this; + } +} diff --git a/src/server/protocol/ServerHttp1.h b/src/server/protocol/ServerHttp1.h new file mode 100644 index 0000000..258e2c8 --- /dev/null +++ b/src/server/protocol/ServerHttp1.h @@ -0,0 +1,64 @@ +#pragma once + +#include "ServerProtocol.h" +#include "../../transfer/HttpStatusCode.h" + +namespace HttpServer +{ + class ServerHttp1 : public ServerProtocol + { + private: + const ServerApplicationSettings *getApplicationSettings( + struct Request &rp, + const bool isSecureConnection + ) const; + + Http::StatusCode getRequestData( + struct Request &rp, + std::string &str_buf, + const ServerApplicationSettings &appSets + ) const; + + protected: + void useHttp1Protocol( + struct Request &rp, + std::vector &buf, + std::string &str_buf + ) const; + + public: + ServerHttp1( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls + ) noexcept; + + virtual bool sendHeaders( + const Http::StatusCode status, + std::vector > &headers, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const override; + + virtual long sendData( + const void *src, + size_t size, + const std::chrono::milliseconds &timeout, + DataTransfer *dt + ) const override; + + virtual bool packRequestParameters( + std::vector &buf, + const struct Request &rp, + const std::string &rootDir + ) const override; + + virtual void unpackResponseParameters( + struct Request &req, + const void *src + ) const override; + + virtual ServerProtocol *process() override; + virtual void close() override; + }; +} diff --git a/src/server/protocol/ServerHttp2.cpp b/src/server/protocol/ServerHttp2.cpp new file mode 100644 index 0000000..35632ad --- /dev/null +++ b/src/server/protocol/ServerHttp2.cpp @@ -0,0 +1,837 @@ + +#include "ServerHttp2.h" +#include "../../utils/Utils.h" +#include "../../transfer/http2/HPack.h" + +#include + +namespace HttpServer +{ + ServerHttp2::ServerHttp2( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls, + SocketsQueue &sockets + ) noexcept + : ServerHttp2Protocol(sock, settings, controls, nullptr), sockets(sockets) + { + + } + + void ServerHttp2::close() { + this->sock.close(); + } + + static uint8_t *setHttp2FrameHeader( + uint8_t *addr, + const uint32_t frameSize, + const Http2::FrameType frameType, + const Http2::FrameFlag frameFlags, + const uint32_t streamId + ) noexcept { + Utils::hton24(addr, frameSize); + *(addr + 3) = static_cast(frameType); + *(addr + 4) = static_cast(frameFlags); + *reinterpret_cast(addr + 5) = ::htonl(streamId); + + return (addr + Http2::FRAME_HEADER_SIZE); + } + + static Http2::IncStream &getStreamData( + std::unordered_map &streams, + const uint32_t streamId, + Http2::ConnectionData &conn + ) noexcept { + auto it = streams.find(streamId); + + if (streams.end() != it) { + return it->second; + } + + return streams.emplace(streamId, Http2::IncStream(streamId, conn) ).first->second; + } + + static void sendWindowUpdate( + const Socket::Adapter &sock, + const std::chrono::milliseconds &timeout, + const Http2::IncStream &stream, + const uint32_t size + ) noexcept { + std::array buf; + uint8_t *addr = buf.data(); + + addr = setHttp2FrameHeader( + addr, + sizeof(uint32_t), + Http2::FrameType::WINDOW_UPDATE, + Http2::FrameFlag::EMPTY, + stream.stream_id + ); + + *reinterpret_cast(addr) = ::htonl(size); + + const std::unique_lock lock(stream.conn.sync.mtx); + + sock.nonblock_send(buf.data(), buf.size(), timeout); + } + + static Http2::ErrorCode parseHttp2Data( + Http2::FrameMeta &meta, + Http2::IncStream &stream, + const uint8_t *src, + const uint8_t *end + ) { + if (0 == meta.stream_id) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + + if (Http2::StreamState::OPEN != stream.state) { + return Http2::ErrorCode::STREAM_CLOSED; + } + + if (stream.window_size_inc <= 0) { + return Http2::ErrorCode::FLOW_CONTROL_ERROR; + } + + uint8_t padding = 0; + + if (meta.flags & Http2::FrameFlag::PADDED) + { + padding = *src; + + if (padding >= meta.length) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + + src += sizeof(uint8_t); + } + + Http2::ErrorCode error_code = Http2::ErrorCode::NO_ERROR; + + if (stream.reserved) + { + Transfer::request_data *rd = static_cast(&stream); + + DataVariant::DataReceiver *dr = reinterpret_cast(stream.reserved); + + std::string &buf = *reinterpret_cast(dr->reserved); + + buf.append(src, end - padding); + + dr->recv_total += size_t(end - src) - padding; + + if (dr->data_variant->parse(buf, rd, dr) ) { + buf.erase( + 0, buf.length() - dr->left + ); + } else { + error_code = Http2::ErrorCode::PROTOCOL_ERROR; + } + } else { + error_code = Http2::ErrorCode::PROTOCOL_ERROR; + } + + if (meta.flags & Http2::FrameFlag::END_STREAM) { + stream.state = Http2::StreamState::HALF_CLOSED; + + ServerProtocol::destroyDataReceiver(stream.reserved); + stream.reserved = nullptr; + } + + return error_code; + } + + static Http2::ErrorCode parseHttp2Headers( + Http2::FrameMeta &meta, + Http2::IncStream &stream, + const uint8_t *src, + const uint8_t *end + ) { + stream.state = (meta.flags & Http2::FrameFlag::END_STREAM) + ? Http2::StreamState::HALF_CLOSED + : Http2::StreamState::OPEN; + + uint8_t padding = 0; + + if (meta.flags & Http2::FrameFlag::PADDED) + { + padding = *src; + + if (padding >= meta.length) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + + src += sizeof(uint8_t); + } + + if (meta.flags & Http2::FrameFlag::PRIORITY) + { + // Stream id + const uint32_t depend_stream_id = ::ntohl( + *reinterpret_cast(src) + ) & ~(uint32_t(1) << 31); + + src += sizeof(uint32_t); + + // Priority weight + stream.priority = *src; + + src += sizeof(uint8_t); + } + + if (HPack::unpack(src, size_t(end - src) - padding, stream) == false) { + return Http2::ErrorCode::COMPRESSION_ERROR; + } + + return Http2::ErrorCode::NO_ERROR; + } + + static Http2::ErrorCode parseHttp2rstStream( + Http2::FrameMeta &meta, + Http2::IncStream &stream, + const uint8_t *src, + const uint8_t *end + ) { + if (Http2::StreamState::IDLE == stream.state) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + + stream.state = Http2::StreamState::CLOSED; + + if (0 == meta.stream_id) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + + if (sizeof(uint32_t) != meta.length) { + return Http2::ErrorCode::FRAME_SIZE_ERROR; + } + + const Http2::ErrorCode error_code = static_cast( + ::ntohl(*reinterpret_cast(src) ) + ); + + if (Http2::ErrorCode::NO_ERROR != error_code) { + // DEBUG + } + + return Http2::ErrorCode::NO_ERROR; + } + + static Http2::ErrorCode parseHttp2Settings( + Http2::FrameMeta &meta, + Http2::IncStream &stream, + const uint8_t *src, + const uint8_t *end + ) { + if (0 != meta.stream_id) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + + if (meta.length % (sizeof(uint16_t) + sizeof(uint32_t) ) ) { + return Http2::ErrorCode::FRAME_SIZE_ERROR; + } + + if (Http2::StreamState::OPEN != stream.state) { + stream.state = Http2::StreamState::OPEN; + } + + Http2::ConnectionSettings &settings = stream.conn.client_settings; + + while (src != end) + { + const Http2::ConnectionSetting setting = static_cast( + ntohs(*reinterpret_cast(src) ) + ); + + src += sizeof(uint16_t); + + const uint32_t value = ::ntohl(*reinterpret_cast(src) ); + + src += sizeof(uint32_t); + + switch (setting) + { + case Http2::ConnectionSetting::SETTINGS_HEADER_TABLE_SIZE: { + settings.header_table_size = value; + break; + } + + case Http2::ConnectionSetting::SETTINGS_ENABLE_PUSH: { + if (value > 1) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + + settings.enable_push = value; + + break; + } + + case Http2::ConnectionSetting::SETTINGS_MAX_CONCURRENT_STREAMS: { + settings.max_concurrent_streams = value; + break; + } + + case Http2::ConnectionSetting::SETTINGS_INITIAL_WINDOW_SIZE: { + if (value >= uint32_t(1) << 31) { + return Http2::ErrorCode::FLOW_CONTROL_ERROR; + } + + settings.initial_window_size = value; + + break; + } + + case Http2::ConnectionSetting::SETTINGS_MAX_FRAME_SIZE: { + if (value < (1 << 14) || value >= (1 << 24) ) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + + settings.max_frame_size = value; + + break; + } + + case Http2::ConnectionSetting::SETTINGS_MAX_HEADER_LIST_SIZE: { + settings.max_header_list_size = value; + break; + } + + default: + break; + } + } + + return Http2::ErrorCode::NO_ERROR; + } + + static Http2::ErrorCode parseHttp2GoAway( + Http2::FrameMeta &meta, + Http2::IncStream &stream, + const uint8_t *src, + const uint8_t *end + ) { + if (0 != meta.stream_id) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + + stream.state = Http2::StreamState::CLOSED; + + const uint32_t last_stream_id = ::ntohl( + *reinterpret_cast(src) + ); + + if (last_stream_id > 0) { + + } + + src += sizeof(uint32_t); + + const Http2::ErrorCode error_code = static_cast( + ::ntohl(*reinterpret_cast(src) ) + ); + + if (Http2::ErrorCode::NO_ERROR != error_code) { + + } + + return Http2::ErrorCode::NO_ERROR; + } + + static void ping( + const Socket::Adapter &sock, + const std::chrono::milliseconds &timeout, + Http2::ConnectionData &conn, + const uint64_t pingData + ) { + constexpr uint32_t frame_size = sizeof(uint64_t); + + std::array buf; + uint8_t *addr = buf.data(); + + constexpr uint32_t stream_id = 0; + + addr = setHttp2FrameHeader( + addr, + frame_size, + Http2::FrameType::PING, + Http2::FrameFlag::ACK, + stream_id + ); + + *reinterpret_cast(addr) = pingData; + + const std::unique_lock lock(conn.sync.mtx); + + sock.nonblock_send(buf.data(), buf.size(), timeout); + } + + static Http2::ErrorCode parseHttp2Ping( + Http2::FrameMeta &meta + ) { + if (0 != meta.stream_id) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + + if (sizeof(uint64_t) != meta.length) { + return Http2::ErrorCode::FRAME_SIZE_ERROR; + } + + return Http2::ErrorCode::NO_ERROR; + } + + static Http2::ErrorCode parseHttp2WindowUpdate( + Http2::FrameMeta &meta, + Http2::IncStream &stream, + const uint8_t *src, + const uint8_t *end + ) { + if (Http2::StreamState::RESERVED == stream.state) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + else if (Http2::StreamState::OPEN != stream.state) { + return Http2::ErrorCode::NO_ERROR; + } + + if (sizeof(uint32_t) != meta.length) { + return Http2::ErrorCode::FRAME_SIZE_ERROR; + } + + const uint32_t window_size_increment = ::ntohl( + *reinterpret_cast(src) + ); + + if (0 == window_size_increment) { + return Http2::ErrorCode::PROTOCOL_ERROR; + } + else if (window_size_increment >= uint32_t(1) << 31) { + return Http2::ErrorCode::FLOW_CONTROL_ERROR; + } + + if (0 == meta.stream_id) { + // TODO: update all streams + stream.window_size_out += window_size_increment; + } else { + stream.window_size_out += window_size_increment; + } + + return Http2::ErrorCode::NO_ERROR; + } + + static void rstStream( + const Socket::Adapter &sock, + const std::chrono::milliseconds &timeout, + Http2::IncStream &stream, + const Http2::ErrorCode errorCode + ) { + constexpr uint32_t frame_size = sizeof(uint32_t); + + std::array buf; + uint8_t *addr = buf.data(); + + addr = setHttp2FrameHeader( + addr, + frame_size, + Http2::FrameType::RST_STREAM, + Http2::FrameFlag::EMPTY, + stream.stream_id + ); + + *reinterpret_cast(addr) = ::htonl( + static_cast(errorCode) + ); + + const std::unique_lock lock(stream.conn.sync.mtx); + + sock.nonblock_send(buf.data(), buf.size(), timeout); + } + + static void sendSettings( + const Socket::Adapter &sock, + const std::chrono::milliseconds &timeout, + Http2::ConnectionData &conn, + const uint8_t *src, + const uint8_t *end + ) { + const uint32_t frame_size = uint32_t(end - src); + + std::vector buf(Http2::FRAME_HEADER_SIZE + frame_size); + + uint8_t *addr = buf.data(); + + constexpr uint32_t stream_id = 0; + + addr = setHttp2FrameHeader( + addr, + frame_size, + Http2::FrameType::SETTINGS, + Http2::FrameFlag::EMPTY, + stream_id + ); + + std::copy(src, end, addr); + + const std::unique_lock lock(conn.sync.mtx); + + sock.nonblock_send(buf.data(), buf.size(), timeout); + } + + static void goAway( + const Socket::Adapter &sock, + const std::chrono::milliseconds &timeout, + Http2::ConnectionData &conn, + const uint32_t lastStreamId, + const Http2::ErrorCode errorCode + ) { + constexpr uint32_t frame_size = sizeof(uint32_t) * 2; + + std::array buf; + + uint8_t *addr = buf.data(); + + addr = setHttp2FrameHeader( + addr, + frame_size, + Http2::FrameType::RST_STREAM, + Http2::FrameFlag::EMPTY, + 0 + ); + + *reinterpret_cast(addr) = ::htonl(lastStreamId); + + *reinterpret_cast(addr + sizeof(uint32_t) ) = ::htonl( + static_cast(errorCode) + ); + + const std::unique_lock lock(conn.sync.mtx); + + sock.nonblock_send(buf.data(), buf.size(), timeout); + } + + static bool getClientPreface( + const Socket::Adapter &sock, + const std::chrono::milliseconds &timeout + ) { + std::array buf; + + const long read_size = sock.nonblock_recv( + buf.data(), + buf.size(), + timeout + ); + + if (buf.size() != read_size) { + return false; + } + + static constexpr char client_preface_data[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; + + const uint64_t *left = reinterpret_cast(client_preface_data); + const uint64_t *right = reinterpret_cast(buf.data() ); + + uint64_t compare = 0; + + compare |= left[0] ^ right[0]; + compare |= left[1] ^ right[1]; + compare |= left[2] ^ right[2]; + + return 0 == compare; + } + + static void sendEmptySettings( + const Socket::Adapter &sock, + const std::chrono::milliseconds &timeout, + Http2::ConnectionData &conn, + const Http2::FrameFlag flags + ) { + constexpr uint32_t frame_size = 0; + + std::array buf; + uint8_t *addr = buf.data(); + + constexpr uint32_t stream_id = 0; + + addr = setHttp2FrameHeader( + addr, + frame_size, + Http2::FrameType::SETTINGS, + flags, + stream_id + ); + + const std::unique_lock lock(conn.sync.mtx); + + sock.nonblock_send(buf.data(), buf.size(), timeout); + } + + static bool getNextHttp2FrameMeta( + const Socket::Adapter &sock, + const std::chrono::milliseconds &timeout, + std::vector &buf, + Http2::FrameMeta &meta, + long &read_size + ) { + const long length = long( + meta.length + Http2::FRAME_HEADER_SIZE + ); + + if (read_size <= length) { + if (read_size == length) { + read_size = 0; + } + + read_size = sock.nonblock_recv( + buf.data() + read_size, + buf.size() - size_t(read_size), + timeout + ); + + if (read_size < long(Http2::FRAME_HEADER_SIZE) ) { + return false; + } + } else { + std::copy( + buf.data() + length, + buf.data() + read_size, + buf.data() + ); + + read_size -= length; + } + + const uint8_t *addr = reinterpret_cast(buf.data() ); + + meta.length = Utils::ntoh24(addr); + meta.type = static_cast(*(addr + 3) ); + meta.flags = static_cast(*(addr + 4) ); + meta.stream_id = ::ntohl(*reinterpret_cast(addr + 5) ); + + return true; + } + + ServerProtocol *ServerHttp2::process() + { + struct Request req; + req.timeout = std::chrono::milliseconds(15000); + req.protocol_variant = Transfer::ProtocolVariant::HTTP_2; + + Http2::ConnectionData conn; + + sendEmptySettings( + this->sock, + req.timeout, + conn, + Http2::FrameFlag::EMPTY + ); + + if (getClientPreface(this->sock, req.timeout) == false) { + constexpr uint32_t last_stream_id = 0; + + goAway( + this->sock, + req.timeout, + conn, + last_stream_id, + Http2::ErrorCode::PROTOCOL_ERROR + ); + + return this; + } + + conn.client_settings = Http2::ConnectionSettings::defaultSettings(); + conn.server_settings = Http2::ConnectionSettings::defaultSettings(); + + std::vector buf(conn.server_settings.max_frame_size); + + std::unordered_map streams { + { 0, Http2::IncStream(0, conn) } + }; + + Http2::IncStream &primary = streams.find(0)->second; + this->stream = &primary; + + size_t streams_process_count = 0; + uint32_t last_stream_id = 0; + + Http2::FrameMeta meta {}; + long read_size = 0; + + do { + if (getNextHttp2FrameMeta(this->sock, req.timeout, buf, meta, read_size) == false) { + break; + } + + const uint8_t *addr = reinterpret_cast( + buf.data() + ) + Http2::FRAME_HEADER_SIZE; + + const uint8_t *end = addr + meta.length; + + if (meta.stream_id > last_stream_id) { + last_stream_id = meta.stream_id; + } + + Http2::IncStream &stream = getStreamData(streams, meta.stream_id, conn); + + if (Http2::StreamState::CLOSED == stream.state) { + rstStream(this->sock, req.timeout, stream, Http2::ErrorCode::STREAM_CLOSED); + continue; + } + + if (meta.type != Http2::FrameType::CONTINUATION) { + stream.frame_type = meta.type; + } + + Http2::ErrorCode result = Http2::ErrorCode::NO_ERROR; + + switch (stream.frame_type) + { + case Http2::FrameType::DATA: + { + result = parseHttp2Data(meta, stream, addr, end); + + stream.window_size_inc -= meta.length; + + if (stream.reserved) + { + DataVariant::DataReceiver *dr = reinterpret_cast(stream.reserved); + + if (stream.window_size_inc - long(conn.server_settings.max_frame_size) <= 0) + { + size_t update_size = conn.server_settings.initial_window_size + + (dr->full_size - dr->recv_total) - size_t(stream.window_size_inc); + + if (update_size > Http2::MAX_WINDOW_UPDATE) { + update_size = Http2::MAX_WINDOW_UPDATE; + } + + sendWindowUpdate(this->sock, req.timeout, stream, uint32_t(update_size) ); + sendWindowUpdate(this->sock, req.timeout, primary, uint32_t(update_size) ); + + stream.window_size_inc += update_size; + } + } + + break; + } + + case Http2::FrameType::HEADERS: + { + result = parseHttp2Headers(meta, stream, addr, end); + + if (meta.flags & Http2::FrameFlag::END_HEADERS) + { + Transfer::request_data *rd = static_cast(&stream); + + stream.reserved = createDataReceiver(rd, this->settings.variants); + + if (stream.reserved) { + DataVariant::DataReceiver *dr = reinterpret_cast(stream.reserved); + dr->reserved = new std::string(); + } + } + + break; + } + + case Http2::FrameType::PRIORITY: + result = Http2::ErrorCode::NO_ERROR; + break; + + case Http2::FrameType::RST_STREAM: + result = parseHttp2rstStream(meta, stream, addr, end); + break; + + case Http2::FrameType::SETTINGS: + { + result = parseHttp2Settings(meta, stream, addr, end); + + if (Http2::ErrorCode::NO_ERROR == result && (meta.flags & Http2::FrameFlag::ACK) == false) + { + conn.decoding_dynamic_table.changeHeaderTableSize(conn.client_settings.header_table_size); + conn.decoding_dynamic_table.changeMaxHeaderListSize(conn.client_settings.max_header_list_size); + + sendEmptySettings(this->sock, req.timeout, conn, Http2::FrameFlag::ACK); + } + + break; + } + + case Http2::FrameType::PUSH_PROMISE: + result = Http2::ErrorCode::NO_ERROR; + break; + + case Http2::FrameType::PING: + { + result = parseHttp2Ping(meta); + + if (Http2::ErrorCode::NO_ERROR == result && (meta.flags & Http2::FrameFlag::ACK) == false) + { + const uint64_t ping_data = *reinterpret_cast(addr); + ping(this->sock, req.timeout, conn, ping_data); + } + + break; + } + + case Http2::FrameType::GOAWAY: + result = parseHttp2GoAway(meta, stream, addr, end); + break; + + case Http2::FrameType::WINDOW_UPDATE: + result = parseHttp2WindowUpdate(meta, stream, addr, end); + break; + + default: + result = Http2::ErrorCode::PROTOCOL_ERROR; + break; + } + + if (result != Http2::ErrorCode::NO_ERROR) + { + stream.state = Http2::StreamState::CLOSED; + + rstStream(this->sock, req.timeout, stream, result); + + // TODO: remove closed stream(s) from unordered map + } + else if ( (meta.flags & Http2::FrameFlag::END_STREAM) && meta.stream_id != 0) + { + stream.reserved = this->sock.get_tls_session(); + + sockets.lock(); + + sockets.emplace( + std::tuple { + Socket::Socket(this->sock.get_handle() ), + &stream + } + ); + + sockets.unlock(); + + this->controls.eventProcessQueue->notify(); + + ++streams_process_count; + } + } + while (Http2::StreamState::CLOSED != primary.state); + + while (conn.sync.completed.load() < streams_process_count) { + conn.sync.event.wait(); + } + + goAway( + this->sock, + req.timeout, + conn, + last_stream_id, + Http2::ErrorCode::NO_ERROR + ); + + for (auto &pair : streams) { + destroyDataReceiver(pair.second.reserved); + } + + return this; + } +} diff --git a/src/server/protocol/ServerHttp2.h b/src/server/protocol/ServerHttp2.h new file mode 100644 index 0000000..7871d3e --- /dev/null +++ b/src/server/protocol/ServerHttp2.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../SocketsQueue.h" + +#include "ServerHttp2Protocol.h" +#include "../../transfer/HttpStatusCode.h" + +namespace HttpServer +{ + class ServerHttp2 : public ServerHttp2Protocol + { + protected: + SocketsQueue &sockets; + + public: + ServerHttp2( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls, + SocketsQueue &sockets + ) noexcept; + + virtual ServerProtocol *process() override; + virtual void close() override; + }; +} diff --git a/src/server/protocol/ServerHttp2Protocol.cpp b/src/server/protocol/ServerHttp2Protocol.cpp new file mode 100644 index 0000000..857c216 --- /dev/null +++ b/src/server/protocol/ServerHttp2Protocol.cpp @@ -0,0 +1,227 @@ + +#include "ServerHttp2Protocol.h" +#include "../../utils/Utils.h" +#include "../../transfer/http2/HPack.h" + +#include + +namespace HttpServer +{ + ServerHttp2Protocol::ServerHttp2Protocol( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls, + Http2::IncStream *stream + ) noexcept + : ServerProtocol(sock, settings, controls), stream(stream) + { + + } + + uint8_t ServerHttp2Protocol::getPaddingSize(const size_t dataSize) + { + if (0 == dataSize) { + return 0; + } + + std::random_device rd; + + uint8_t padding = uint8_t(rd()); + + while (dataSize <= padding) { + padding /= 2; + } + + return padding; + } + + bool ServerHttp2Protocol::sendHeaders( + const Http::StatusCode status, + std::vector > &headers, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const { + headers.emplace( + headers.begin(), + ":status", + std::to_string(static_cast(status)) + ); + + std::vector buf; + buf.reserve(4096); + buf.resize(Http2::FRAME_HEADER_SIZE); + + HPack::pack(buf, headers, this->stream->conn.encoding_dynamic_table); + + const uint32_t frame_size = uint32_t( + buf.size() - Http2::FRAME_HEADER_SIZE + ); + + Http2::FrameFlag flags = Http2::FrameFlag::END_HEADERS; + + if (endStream) { + flags |= Http2::FrameFlag::END_STREAM; + } + + this->stream->setHttp2FrameHeader( + reinterpret_cast(buf.data() ), + frame_size, + Http2::FrameType::HEADERS, + flags + ); + + const std::unique_lock lock(this->stream->conn.sync.mtx); + + return this->sock.nonblock_send(buf.data(), buf.size(), timeout) > 0; + } + + long ServerHttp2Protocol::sendData( + const void *src, + size_t size, + const std::chrono::milliseconds &timeout, + DataTransfer *dt + ) const { + const uint8_t *data = reinterpret_cast(src); + + const Http2::ConnectionSettings &setting = this->stream->conn.client_settings; + + std::vector buf; + buf.reserve(setting.max_frame_size + Http2::FRAME_HEADER_SIZE); + + long send_size = 0; + + while (size != 0) + { + // TODO: test with data_size == 1 (padding length == 0) + size_t data_size = setting.max_frame_size < size + ? setting.max_frame_size + : size; + + const uint8_t padding = getPaddingSize(data_size); + const uint16_t padding_size = padding + sizeof(uint8_t); + + if (padding_size) { + if (data_size + padding_size > setting.max_frame_size) { + data_size = setting.max_frame_size - padding_size; + } + } + + const uint32_t frame_size = static_cast( + data_size + padding_size + ); + + buf.resize(frame_size + Http2::FRAME_HEADER_SIZE); + + /* if (this->stream->window_size_out - frame_size <= 0) + { + size_t update_size = (dt->full_size - dt->send_total) - this->stream->window_size_out; + + if (update_size > Http2::MAX_WINDOW_UPDATE) { + update_size = Http2::MAX_WINDOW_UPDATE; + } + + sendWindowUpdate(this->sock, rp, uint32_t(update_size) ); + + this->stream->window_size_out += update_size; + }*/ + + Http2::FrameFlag flags = Http2::FrameFlag::EMPTY; + + if (dt->send_total + data_size >= dt->full_size) { + flags |= Http2::FrameFlag::END_STREAM; + } + + size_t cur = Http2::FRAME_HEADER_SIZE; + + if (padding_size) { + flags |= Http2::FrameFlag::PADDED; + + buf[cur] = padding; + + ++cur; + } + + this->stream->setHttp2FrameHeader( + buf.data(), + frame_size, + Http2::FrameType::DATA, + flags + ); + + std::copy( + data, + data + data_size, + buf.data() + cur + ); + + if (padding) { + std::fill( + buf.end() - padding, + buf.end(), + 0 + ); + } + + this->stream->lock(); + + const long sended = this->sock.nonblock_send( + buf.data(), + buf.size(), + timeout + ); + + this->stream->unlock(); + + if (sended <= 0) { + send_size = sended; + break; + } + + data += data_size; + send_size += long(data_size); + dt->send_total += data_size; + // stream->window_size_out -= frame_size; + + size -= data_size; + } + + return send_size; + } + + bool ServerHttp2Protocol::packRequestParameters( + std::vector &buf, + const struct Request &req, + const std::string &rootDir + ) const { + Utils::packNumber(buf, static_cast(Transfer::ProtocolVariant::HTTP_2) ); + Utils::packString(buf, rootDir); + Utils::packString(buf, req.host); + Utils::packString(buf, req.path); + Utils::packString(buf, req.method); + + Utils::packNumber(buf, this->stream->stream_id); + Utils::packNumber(buf, this->stream->conn.client_settings.header_table_size); + Utils::packNumber(buf, this->stream->conn.client_settings.enable_push); + Utils::packNumber(buf, this->stream->conn.client_settings.max_concurrent_streams); + Utils::packNumber(buf, this->stream->conn.client_settings.initial_window_size); + Utils::packNumber(buf, this->stream->conn.client_settings.max_frame_size); + Utils::packNumber(buf, this->stream->conn.client_settings.max_header_list_size); + Utils::packContainer(buf, this->stream->conn.encoding_dynamic_table.getList() ); + Utils::packPointer(buf, &this->stream->conn.sync.mtx); + Utils::packContainer(buf, req.incoming_headers); + Utils::packContainer(buf, req.incoming_data); + Utils::packFilesIncoming(buf, req.incoming_files); + + return true; + } + + void ServerHttp2Protocol::unpackResponseParameters( + struct Request &req, + const void *src + ) const { + Utils::unpackContainer( + req.outgoing_headers, + reinterpret_cast(src) + ); + } +} diff --git a/src/server/protocol/ServerHttp2Protocol.h b/src/server/protocol/ServerHttp2Protocol.h new file mode 100644 index 0000000..99adc42 --- /dev/null +++ b/src/server/protocol/ServerHttp2Protocol.h @@ -0,0 +1,48 @@ +#pragma once + +#include "ServerProtocol.h" +#include "../../transfer/http2/Http2.h" + +namespace HttpServer +{ + class ServerHttp2Protocol : public ServerProtocol + { + protected: + Http2::IncStream *stream; + + protected: + static uint8_t getPaddingSize(const size_t dataSize); + + public: + ServerHttp2Protocol( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls, Http2::IncStream *stream + ) noexcept; + + virtual bool sendHeaders( + const Http::StatusCode status, + std::vector > &headers, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const override; + + virtual long sendData( + const void *src, + size_t size, + const std::chrono::milliseconds &timeout, + DataTransfer *dt + ) const override; + + virtual bool packRequestParameters( + std::vector &buf, + const struct Request &rp, + const std::string &rootDir + ) const override; + + virtual void unpackResponseParameters( + struct Request &rp, + const void *src + ) const override; + }; +} diff --git a/src/server/protocol/ServerHttp2Stream.cpp b/src/server/protocol/ServerHttp2Stream.cpp new file mode 100644 index 0000000..346e0f1 --- /dev/null +++ b/src/server/protocol/ServerHttp2Stream.cpp @@ -0,0 +1,120 @@ + +#include "ServerHttp2Stream.h" + +#include "extensions/Sendfile.h" + +namespace HttpServer +{ + ServerHttp2Stream::ServerHttp2Stream( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls, + Http2::IncStream *stream + ) noexcept + : ServerHttp2Protocol(sock, settings, controls, stream) + { + + } + + void ServerHttp2Stream::close() { + this->stream->close(); + } + + ServerProtocol *ServerHttp2Stream::process() + { + struct Request req; + + req.timeout = std::chrono::milliseconds(5000); + req.protocol_variant = Transfer::ProtocolVariant::HTTP_2; + + req.incoming_headers = std::move(this->stream->incoming_headers); + req.incoming_data = std::move(this->stream->incoming_data); + req.incoming_files = std::move(this->stream->incoming_files); + + auto const &headers = req.incoming_headers; + + auto const it_scheme = headers.find(":scheme"); + + if (headers.cend() == it_scheme) { + return this; + } + + const std::string &scheme = it_scheme->second; + + const int default_port = (scheme == "https") + ? 443 + : (scheme == "http") + ? 80 : 0; + + auto const it_host = headers.find(":authority"); + + if (headers.cend() == it_host) { + return this; + } + + const std::string &host_header = it_host->second; + + // Поиск разделителя, за которым помещается номер порта, если указан + const size_t delimiter = host_header.find(':'); + + // Получить имя (или адрес) + req.host = host_header.substr(0, delimiter); + + // Получить номер порта + const int port = std::string::npos != delimiter + ? std::atoi(host_header.substr(delimiter + 1).c_str()) + : default_port; + + const ServerApplicationSettings *app_sets = this->settings.apps_tree.find(req.host); + + // Если приложение найдено + if (nullptr == app_sets || ( + app_sets->ports.cend() == app_sets->ports.find(port) && + app_sets->tls_ports.cend() == app_sets->tls_ports.find(port) + ) + ) { + return this; + } + + auto const it_method = headers.find(":method"); + + if (headers.cend() == it_method) { + return this; + } + + req.method = it_method->second; + + auto const it_path = headers.find(":path"); + + if (headers.cend() == it_path) { + return this; + } + + req.path = it_path->second; + + req.app_exit_code = EXIT_FAILURE; + + this->runApplication(req, *app_sets); + + for (auto const &it : req.incoming_files) { + std::remove(it.second.getTmpName().c_str() ); + } + + if (EXIT_SUCCESS == req.app_exit_code) { + // Http2::OutStream out(*stream); + + // auto tmp = req.protocol_data; + // req.protocol_data = &out; + + Sendfile::xSendfile( + std::ref(*this), + req, + this->settings.mimes_types + ); + + // req.protocol_data = tmp; + } + + return this; + } +} diff --git a/src/server/protocol/ServerHttp2Stream.h b/src/server/protocol/ServerHttp2Stream.h new file mode 100644 index 0000000..1181983 --- /dev/null +++ b/src/server/protocol/ServerHttp2Stream.h @@ -0,0 +1,21 @@ +#pragma once + +#include "ServerHttp2Protocol.h" +#include "../../transfer/http2/Http2.h" + +namespace HttpServer +{ + class ServerHttp2Stream : public ServerHttp2Protocol + { + public: + ServerHttp2Stream( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls, + Http2::IncStream *stream + ) noexcept; + + virtual ServerProtocol *process() override; + virtual void close() override; + }; +} diff --git a/src/server/protocol/ServerProtocol.cpp b/src/server/protocol/ServerProtocol.cpp new file mode 100644 index 0000000..4f3010d --- /dev/null +++ b/src/server/protocol/ServerProtocol.cpp @@ -0,0 +1,193 @@ + +#include "ServerProtocol.h" +#include "../../utils/Utils.h" + +#include + +namespace HttpServer +{ + ServerProtocol::ServerProtocol( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls + ) noexcept + : sock(sock), settings(settings), controls(controls) + { + + } + + ServerProtocol::ServerProtocol(const ServerProtocol &prot) noexcept + : sock(prot.sock), settings(prot.settings), controls(prot.controls) + { + + } + + DataVariant::DataReceiver * + ServerProtocol::createDataReceiver( + const Transfer::request_data *rd, + const std::unordered_map &variants + ) { + auto const it = rd->incoming_headers.find("content-type"); + + if (rd->incoming_headers.cend() == it) { + return nullptr; + } + + // Получить значение заголовка + const std::string &header_value = it->second; + + std::string data_variant_name; // Название варианта данных запроса + + std::unordered_map content_params; + + // Определить, содержит ли тип данных запроса дополнительные параметры + size_t delimiter = header_value.find(';'); + + // Если есть дополнительные параметры - извлекаем их + if (std::string::npos != delimiter) { + data_variant_name = header_value.substr(0, delimiter); + Utils::trim(data_variant_name); + + for ( + size_t str_param_cur = delimiter + 1, str_param_end = 0; + std::string::npos != str_param_end; + str_param_cur = str_param_end + 1 + ) { + str_param_end = header_value.find(';', str_param_cur); + delimiter = header_value.find('=', str_param_cur); + + if (delimiter >= str_param_end) { + std::string param_name = header_value.substr( + str_param_cur, + std::string::npos != str_param_end + ? str_param_end - str_param_cur + : std::string::npos + ); + + Utils::trim(param_name); + + content_params.emplace( + std::move(param_name), + std::string() + ); + } else { + std::string param_name = header_value.substr( + str_param_cur, + delimiter - str_param_cur + ); + + Utils::trim(param_name); + + ++delimiter; + + std::string param_value = header_value.substr( + delimiter, + std::string::npos != str_param_end + ? str_param_end - delimiter + : std::string::npos + ); + + Utils::trim(param_value); + + content_params.emplace( + std::move(param_name), + std::move(param_value) + ); + } + } + } else { + data_variant_name = header_value; + } + + auto const variant = variants.find(data_variant_name); + + if (variants.cend() == variant) { + return nullptr; + } + + const DataVariant::Abstract *data_variant = variant->second; + + size_t data_length = 0; + + auto const it_len = rd->incoming_headers.find("content-length"); + + if (rd->incoming_headers.cend() != it_len) { + data_length = std::strtoull( + it_len->second.c_str(), + nullptr, + 10 + ); + } + + return new DataVariant::DataReceiver { + data_variant, + data_variant->createStateStruct(rd, content_params), + data_length, + 0, 0, nullptr, + }; + } + + void ServerProtocol::destroyDataReceiver(void *src) + { + DataVariant::DataReceiver *dr = reinterpret_cast(src); + + if (dr) { + dr->data_variant->destroyStateStruct(dr->ss); + + if (dr->reserved) { + delete reinterpret_cast(dr->reserved); + dr->reserved = nullptr; + } + } + + delete dr; + } + + void ServerProtocol::runApplication( + struct Request &req, + const ServerApplicationSettings &appSets + ) const { + std::vector buf; + buf.reserve(4096); + + if (this->packRequestParameters(buf, req, appSets.root_dir) == false) { + return; + } + + Transfer::app_request request { + this->sock.get_handle(), + this->sock.get_tls_session(), + buf.data() + }; + + Transfer::app_response response { + nullptr, 0 + }; + + try { + // Launch application + req.app_exit_code = appSets.application_call(&request, &response); + } + catch (const std::exception &exc) { + // Exception output + std::cout << "Exception when application_call: " << exc.what() << std::endl; + } + + if (response.response_data && response.data_size) + { + if (EXIT_SUCCESS == req.app_exit_code) { + this->unpackResponseParameters(req, response.response_data); + } + + // Clear outgoing data of application + try { + appSets.application_clear(response.response_data, response.data_size); + } + catch (const std::exception &exc) { + // Exception output + std::cout << "Exception when application_clear: " << exc.what() << std::endl; + } + } + } +} diff --git a/src/server/protocol/ServerProtocol.h b/src/server/protocol/ServerProtocol.h new file mode 100644 index 0000000..e53fea6 --- /dev/null +++ b/src/server/protocol/ServerProtocol.h @@ -0,0 +1,71 @@ +#pragma once + +#include "../Request.h" +#include "../ServerControls.h" +#include "../ServerSettings.h" + +#include "../../transfer/HttpStatusCode.h" + +namespace HttpServer +{ + class ServerProtocol + { + protected: + Socket::Adapter &sock; + const ServerSettings &settings; + ServerControls &controls; + + public: + ServerProtocol( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls + ) noexcept; + + ServerProtocol(const ServerProtocol &prot) noexcept; + virtual ~ServerProtocol() noexcept = default; + + static DataVariant::DataReceiver * + createDataReceiver( + const Transfer::request_data *rd, + const std::unordered_map &variants + ); + + static void destroyDataReceiver(void *src); + + protected: + void runApplication( + struct Request &req, + const ServerApplicationSettings &appSets + ) const; + + public: + virtual bool sendHeaders( + const Http::StatusCode status, + std::vector > &headers, + const std::chrono::milliseconds &timeout, + const bool endStream = true + ) const = 0; + + virtual long sendData( + const void *src, + size_t size, + const std::chrono::milliseconds &timeout, + DataTransfer *dt + ) const = 0; + + virtual bool packRequestParameters( + std::vector &buf, + const struct Request &req, + const std::string &rootDir + ) const = 0; + + virtual void unpackResponseParameters( + struct Request &req, + const void *src + ) const = 0; + + virtual ServerProtocol *process() = 0; + virtual void close() = 0; + }; +} diff --git a/src/server/protocol/ServerWebSocket.cpp b/src/server/protocol/ServerWebSocket.cpp new file mode 100644 index 0000000..2951e73 --- /dev/null +++ b/src/server/protocol/ServerWebSocket.cpp @@ -0,0 +1,60 @@ + +#include "ServerWebSocket.h" + +namespace HttpServer +{ + ServerWebSocket::ServerWebSocket( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls + ) noexcept + : ServerProtocol(sock, settings, controls) + { + + } + + ServerWebSocket::ServerWebSocket(const ServerProtocol &prot) noexcept + : ServerProtocol(prot) + { + + } + + bool ServerWebSocket::sendHeaders( + const Http::StatusCode status, + std::vector > &headers, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const { + return false; + } + + long ServerWebSocket::sendData( + const void *src, + size_t size, + const std::chrono::milliseconds &timeout, + DataTransfer *dt + ) const { + return 0; + } + + bool ServerWebSocket::packRequestParameters( + std::vector &buf, + const struct Request &rp, + const std::string &rootDir + ) const { + return false; + } + + void ServerWebSocket::unpackResponseParameters( + struct Request &req, + const void *src + ) const { + + } + + ServerProtocol *ServerWebSocket::process() { + return this; + } + + void ServerWebSocket::close() {} +} diff --git a/src/server/protocol/ServerWebSocket.h b/src/server/protocol/ServerWebSocket.h new file mode 100644 index 0000000..5745f7e --- /dev/null +++ b/src/server/protocol/ServerWebSocket.h @@ -0,0 +1,46 @@ +#pragma once + +#include "ServerProtocol.h" + +namespace HttpServer +{ + class ServerWebSocket : public ServerProtocol + { + public: + ServerWebSocket( + Socket::Adapter &sock, + const ServerSettings &settings, + ServerControls &controls + ) noexcept; + + ServerWebSocket(const ServerProtocol &prot) noexcept; + + virtual bool sendHeaders( + const Http::StatusCode status, + std::vector > &headers, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const override; + + virtual long sendData( + const void *src, + size_t size, + const std::chrono::milliseconds &timeout, + DataTransfer *dt + ) const override; + + virtual bool packRequestParameters( + std::vector &buf, + const struct Request &rp, + const std::string &rootDir + ) const override; + + virtual void unpackResponseParameters( + struct Request &req, + const void *src + ) const override; + + virtual ServerProtocol *process() override; + virtual void close() override; + }; +} diff --git a/src/server/protocol/extensions/Sendfile.cpp b/src/server/protocol/extensions/Sendfile.cpp new file mode 100644 index 0000000..dc46186 --- /dev/null +++ b/src/server/protocol/extensions/Sendfile.cpp @@ -0,0 +1,468 @@ + +#include "Sendfile.h" +#include "../../../utils/Utils.h" + +#include + +namespace HttpServer +{ + static std::string getMimeTypeByFileName( + const std::string &fileName, + const std::unordered_map &mimesTypes + ) noexcept { + const size_t ext_pos = fileName.rfind('.'); + + const std::string file_ext = std::string::npos != ext_pos + ? Utils::getLowerString(fileName.substr(ext_pos + 1) ) + : std::string(); + + auto const it_mime = mimesTypes.find(file_ext); + + return mimesTypes.cend() == it_mime + ? std::string("application/octet-stream") + : it_mime->second; + } + + static std::vector > getRanges( + const std::string &rangeHeader, + const size_t posSymEqual, + const size_t fileSize, + std::string *resultRangeHeader, + size_t *contentLength + ) noexcept { + std::vector > ranges; + + *contentLength = 0; + + size_t delimiter = posSymEqual; // rangeHeader.find('='); + + const std::string range_unit_name( + rangeHeader.cbegin(), + rangeHeader.cbegin() + delimiter + ); + + static const std::unordered_map ranges_units { + { "bytes", 1 } + }; + + auto const it_unit = ranges_units.find(range_unit_name); + + if (ranges_units.cend() == it_unit) { + return ranges; + } + + const size_t range_unit = it_unit->second; + + for (size_t str_pos; std::string::npos != delimiter; ) + { + str_pos = delimiter + 1; + delimiter = rangeHeader.find(',', str_pos); + + const size_t range_pos = rangeHeader.find('-', str_pos); + + if (range_pos < delimiter) + { + const std::string range_begin_str( + rangeHeader.cbegin() + str_pos, + rangeHeader.cbegin() + range_pos + ); + + const std::string range_end_str( + rangeHeader.cbegin() + range_pos + 1, + std::string::npos == delimiter + ? rangeHeader.cend() + : rangeHeader.cbegin() + delimiter + ); + + if (range_begin_str.empty() == false) + { + const size_t range_begin = std::strtoull( + range_begin_str.c_str(), nullptr, 10 + ) * range_unit; + + if (range_begin < fileSize) + { + if (range_end_str.empty() == false) + { + size_t range_end = std::strtoull( + range_end_str.c_str(), nullptr, 10 + ) * range_unit; + + if (range_end >= range_begin) + { + if (range_end > fileSize) { + range_end = fileSize; + } + + const size_t length = range_end - range_begin + 1; + + *contentLength += length; + + *resultRangeHeader += std::to_string(range_begin) + '-' + std::to_string(range_end) + ','; + + ranges.emplace_back(std::tuple { + range_begin, length + }); + } + } + else // if range_end_str empty + { + const size_t length = fileSize - range_begin; + + *contentLength += length; + + *resultRangeHeader += std::to_string(range_begin) + '-' + std::to_string(fileSize - 1) + ','; + + ranges.emplace_back(std::tuple { + range_begin, length + }); + } + } + } + else if (range_end_str.empty() == false) // if range_begin_str empty + { + size_t range_end = std::strtoull( + range_end_str.c_str(), nullptr, 10 + ) * range_unit; + + const size_t length = range_end < fileSize + ? fileSize - range_end + : fileSize; + + const size_t range_begin = fileSize - length; + + range_end = fileSize - range_begin - 1; + + *contentLength += length; + + *resultRangeHeader += std::to_string(range_begin) + '-' + std::to_string(range_end) + ','; + + ranges.emplace_back(std::tuple { + range_begin, length + }); + } + } + } + + if (ranges.empty() == false) { + (*resultRangeHeader).back() = '/'; + *resultRangeHeader = "bytes " + *resultRangeHeader + std::to_string(fileSize); + } + + return ranges; + } + + int Sendfile::transferFilePart( + const ServerProtocol &prot, + const struct Request &req, + const std::string &fileName, + const std::unordered_map &mimesTypes, + const time_t fileTime, + const size_t fileSize, + const std::string &rangeHeader, + std::vector > &additionalHeaders, + const bool headersOnly + ) noexcept { + const size_t pos_sym_equal = rangeHeader.find('='); + + if (std::string::npos == pos_sym_equal) { + prot.sendHeaders( + Http::StatusCode::BAD_REQUEST, + additionalHeaders, + req.timeout + ); + + return 1; + } + + std::string content_range_header; + + size_t content_length; + + const std::vector > ranges = getRanges( + rangeHeader, + pos_sym_equal, + fileSize, + &content_range_header, + &content_length + ); + + if (0 == content_length) { + prot.sendHeaders( + Http::StatusCode::REQUESTED_RANGE_NOT_SATISFIABLE, + additionalHeaders, + req.timeout + ); + + return 2; + } + + // Ranges transfer + std::ifstream file(fileName, std::ifstream::binary); + + if ( ! file) { + file.close(); + + prot.sendHeaders( + Http::StatusCode::INTERNAL_SERVER_ERROR, + additionalHeaders, + req.timeout + ); + + return 3; + } + + const std::string file_mime_type = getMimeTypeByFileName(fileName, mimesTypes); + + additionalHeaders.emplace_back("content-type", file_mime_type); + additionalHeaders.emplace_back("content-length", std::to_string(content_length) ); + additionalHeaders.emplace_back("accept-ranges", "bytes"); + additionalHeaders.emplace_back("content-range", content_range_header); + additionalHeaders.emplace_back("last-modified", Utils::getDatetimeAsString(fileTime, true) ); + + // Отправить заголовки (206 Partial Content) + if ( + prot.sendHeaders( + Http::StatusCode::PARTIAL_CONTENT, + additionalHeaders, + req.timeout, + headersOnly + ) == false + ) { + file.close(); + return 4; + } + + if (false == headersOnly) + { + size_t position, length; + + std::vector buf; + + DataTransfer dt { + content_length, + 0 + }; + + for (auto const &range : ranges) + { + std::tie(position, length) = range; + + buf.resize( + length < 512 * 1024 + ? length + : 512 * 1024 + ); + + file.seekg(position, file.beg); + + size_t send_size_left = length; + + long send_size; + + do { + if (send_size_left < 512 * 1024) { + buf.resize(send_size_left); + } + + file.read(buf.data(), buf.size() ); + + auto const read_size = file.gcount(); + + send_size = prot.sendData( + buf.data(), + read_size, + req.timeout, + &dt + ); + + send_size_left -= send_size; + } + while ( + false == file.eof() && + false == file.fail() && + send_size > 0 && + send_size_left + ); + } + } + + file.close(); + + return 0; + } + + /** + * Передача файла (или его части) + */ + int Sendfile::transferFile( + const ServerProtocol &prot, + const struct Request &req, + const std::string &fileName, + const std::unordered_map &mimesTypes, + std::vector > &additionalHeaders, + const bool headersOnly + ) noexcept { + // Get current time in GMT + additionalHeaders.emplace_back("date", Utils::getDatetimeAsString() ); + + time_t file_time; + size_t file_size; + + // Получить размер файла и дату последнего изменения + if (System::getFileSizeAndTimeGmt(fileName, &file_size, &file_time) == false) { + prot.sendHeaders( + Http::StatusCode::NOT_FOUND, + additionalHeaders, + req.timeout + ); + + return 1; + } + + // Check for If-Modified header + auto const it_modified = req.incoming_headers.find("if-modified-since"); + + // Если найден заголовок проверки изменения файла (проверить, изменялся ли файл) + if (req.incoming_headers.cend() != it_modified) { + const time_t time_in_request = Utils::rfc822DatetimeToTimestamp(it_modified->second); + + if (file_time == time_in_request) { + prot.sendHeaders( + Http::StatusCode::NOT_MODIFIED, + additionalHeaders, + req.timeout + ); + + return 2; + } + } + + auto const it_range = req.incoming_headers.find("range"); + + // Range transfer + if (req.incoming_headers.cend() != it_range) { + return transferFilePart( + prot, + req, + fileName, + mimesTypes, + file_time, + file_size, + it_range->second, + additionalHeaders, + headersOnly + ); + } + + // File transfer + std::ifstream file(fileName, std::ifstream::binary); + + if ( ! file) { + file.close(); + + prot.sendHeaders( + Http::StatusCode::INTERNAL_SERVER_ERROR, + additionalHeaders, + req.timeout + ); + + return 3; + } + + const std::string file_mime_type = getMimeTypeByFileName(fileName, mimesTypes); + + additionalHeaders.emplace_back("content-type", file_mime_type); + additionalHeaders.emplace_back("content-length", std::to_string(file_size) ); + additionalHeaders.emplace_back("accept-ranges", "bytes"); + additionalHeaders.emplace_back("last-modified", Utils::getDatetimeAsString(file_time, true) ); + + // Отправить заголовки (200 OK) + if ( + prot.sendHeaders( + Http::StatusCode::OK, + additionalHeaders, + req.timeout, + headersOnly || 0 == file_size + ) == false + ) { + file.close(); + return 4; + } + + // Отправить файл + if (false == headersOnly && file_size) + { + std::vector buf( + file_size < 512 * 1024 + ? file_size + : 512 * 1024 + ); + + long send_size; + + DataTransfer dt { + file_size, + 0 + }; + + do { + file.read(buf.data(), buf.size() ); + + send_size = prot.sendData( + buf.data(), + file.gcount(), + req.timeout, + &dt + ); + } + while ( + false == file.eof() && + false == file.fail() && + send_size > 0 && + (dt.full_size - dt.send_total) + ); + } + + file.close(); + + return 0; + } + + bool Sendfile::isConnectionReuse(const struct Request &req) noexcept { + return (req.connection_params & ConnectionParams::CONNECTION_REUSE) == ConnectionParams::CONNECTION_REUSE; + } + + void Sendfile::xSendfile( + const ServerProtocol &prot, + struct Request &req, + const std::unordered_map &mimesTypes + ) noexcept { + auto const it_x_sendfile = req.outgoing_headers.find("x-sendfile"); + + if (req.outgoing_headers.cend() != it_x_sendfile) + { + std::vector > additional_headers; + + if (Transfer::ProtocolVariant::HTTP_1 == req.protocol_variant) { + if (isConnectionReuse(req) ) { + additional_headers.emplace_back("connection", "keep-alive"); + additional_headers.emplace_back("keep-alive", "timeout=5; max=" + std::to_string(req.keep_alive_count) ); + } else { + additional_headers.emplace_back("connection", "close"); + } + } + + const bool headers_only = ("head" == req.method); + + transferFile( + prot, + req, + it_x_sendfile->second, + mimesTypes, + additional_headers, + headers_only + ); + } + } +} diff --git a/src/server/protocol/extensions/Sendfile.h b/src/server/protocol/extensions/Sendfile.h new file mode 100644 index 0000000..23793d0 --- /dev/null +++ b/src/server/protocol/extensions/Sendfile.h @@ -0,0 +1,41 @@ +#pragma once + +#include "../ServerProtocol.h" +#include "../../Request.h" + +namespace HttpServer +{ + class Sendfile + { + private: + static int transferFilePart( + const ServerProtocol &prot, + const struct Request &rp, + const std::string &fileName, + const std::unordered_map &mimesTypes, + const time_t fileTime, + const size_t fileSize, + const std::string &rangeHeader, + std::vector > &additionalHeaders, + const bool headersOnly + ) noexcept; + + static int transferFile( + const ServerProtocol &prot, + const struct Request &req, + const std::string &fileName, + const std::unordered_map &mimesTypes, + std::vector > &additionalHeaders, + const bool headersOnly + ) noexcept; + + public: + static bool isConnectionReuse(const struct Request &req) noexcept; + + static void xSendfile( + const ServerProtocol &prot, + struct Request &req, + const std::unordered_map &mimesTypes + ) noexcept; + }; +} diff --git a/src/socket/Adapter.cpp b/src/socket/Adapter.cpp new file mode 100644 index 0000000..c99bee6 --- /dev/null +++ b/src/socket/Adapter.cpp @@ -0,0 +1,35 @@ + +#include "Adapter.h" + +namespace Socket +{ + long Adapter::nonblock_recv( + std::vector &buf, + const std::chrono::milliseconds &timeout + ) const noexcept { + return this->nonblock_recv( + buf.data(), + buf.size(), + timeout + ); + } + + long Adapter::nonblock_send( + const std::string &buf, + const std::chrono::milliseconds &timeout + ) const noexcept { + return this->nonblock_send( + buf.data(), + buf.length(), + timeout + ); + } + + bool Adapter::operator ==(const Adapter &obj) const noexcept { + return this->get_handle() == obj.get_handle(); + } + + bool Adapter::operator !=(const Adapter &obj) const noexcept { + return this->get_handle() != obj.get_handle(); + } +} diff --git a/src/socket/Adapter.h b/src/socket/Adapter.h new file mode 100644 index 0000000..2787176 --- /dev/null +++ b/src/socket/Adapter.h @@ -0,0 +1,49 @@ +#pragma once + +#include "../system/System.h" + +#include +#include +#include + +#include + +namespace Socket +{ + class Adapter + { + public: + virtual ~Adapter() noexcept = default; + + virtual System::native_socket_type get_handle() const noexcept = 0; + virtual ::gnutls_session_t get_tls_session() const noexcept = 0; + virtual Adapter *copy() const noexcept = 0; + + long nonblock_recv( + std::vector &buf, + const std::chrono::milliseconds &timeout + ) const noexcept; + + virtual long nonblock_recv( + void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept = 0; + + long nonblock_send( + const std::string &buf, + const std::chrono::milliseconds &timeout + ) const noexcept; + + virtual long nonblock_send( + const void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept = 0; + + virtual void close() noexcept = 0; + + bool operator ==(const Adapter &obj) const noexcept; + bool operator !=(const Adapter &obj) const noexcept; + }; +} diff --git a/src/socket/AdapterDefault.cpp b/src/socket/AdapterDefault.cpp new file mode 100644 index 0000000..2940daf --- /dev/null +++ b/src/socket/AdapterDefault.cpp @@ -0,0 +1,43 @@ + +#include "AdapterDefault.h" + +namespace Socket +{ + AdapterDefault::AdapterDefault(const Socket &sock) noexcept : sock(sock) {} + + System::native_socket_type AdapterDefault::get_handle() const noexcept { + return sock.get_handle(); + } + + ::gnutls_session_t AdapterDefault::get_tls_session() const noexcept { + return nullptr; + } + + Adapter *AdapterDefault::copy() const noexcept { + return new AdapterDefault(this->sock); + } + + long AdapterDefault::nonblock_recv( + void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept { + return sock.nonblock_recv(buf, length, timeout); + } + + long AdapterDefault::nonblock_send( + const void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept { + return sock.nonblock_send(buf, length, timeout); + } + + void AdapterDefault::close() noexcept { + // Wait for send all data to client + sock.nonblock_send_sync(); + + sock.shutdown(); + sock.close(); + } +} diff --git a/src/socket/AdapterDefault.h b/src/socket/AdapterDefault.h new file mode 100644 index 0000000..db8b191 --- /dev/null +++ b/src/socket/AdapterDefault.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Adapter.h" +#include "Socket.h" + +namespace Socket +{ + class AdapterDefault : public Adapter + { + private: + Socket sock; + + public: + AdapterDefault() = delete; + AdapterDefault(const Socket &_sock) noexcept; + + virtual System::native_socket_type get_handle() const noexcept override; + virtual ::gnutls_session_t get_tls_session() const noexcept override; + virtual Adapter *copy() const noexcept override; + + virtual long nonblock_recv( + void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept override; + + virtual long nonblock_send( + const void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept override; + + virtual void close() noexcept override; + }; +} diff --git a/src/socket/AdapterTls.cpp b/src/socket/AdapterTls.cpp new file mode 100644 index 0000000..0126830 --- /dev/null +++ b/src/socket/AdapterTls.cpp @@ -0,0 +1,163 @@ + +#include "AdapterTls.h" + +namespace Socket +{ + AdapterTls::AdapterTls( + const Socket &sock, + ::gnutls_priority_t priority_cache, + ::gnutls_certificate_credentials_t x509_cred + ) noexcept { + // https://leandromoreira.com.br/2015/10/12/how-to-optimize-nginx-configuration-for-http2-tls-ssl/ + // https://www.gnutls.org/manual/html_node/False-Start.html + + #ifdef GNUTLS_ENABLE_FALSE_START + constexpr int flags = GNUTLS_SERVER | GNUTLS_ENABLE_FALSE_START; + #else + constexpr int flags = GNUTLS_SERVER; + #endif + + ::gnutls_init(&this->session, flags); + ::gnutls_priority_set(this->session, priority_cache); + ::gnutls_credentials_set(this->session, GNUTLS_CRD_CERTIFICATE, x509_cred); + + ::gnutls_certificate_server_set_request(this->session, GNUTLS_CERT_IGNORE); + + ::gnutls_transport_set_int2(this->session, sock.get_handle(), sock.get_handle() ); + + char h2[] = "h2"; + char http11[] = "http/1.1"; + + const ::gnutls_datum_t protocols[] { + { reinterpret_cast(h2), sizeof(h2) - 1 }, + { reinterpret_cast(http11), sizeof(http11) - 1 }, + }; + + ::gnutls_alpn_set_protocols(this->session, protocols, sizeof(protocols) / sizeof(::gnutls_datum_t), 0); + } + + AdapterTls::AdapterTls(const ::gnutls_session_t session) noexcept : session(session) {} + + bool AdapterTls::handshake() noexcept + { + int ret; + + ::gnutls_handshake_set_timeout(this->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + + do { + ret = ::gnutls_handshake(this->session); + } + while (ret < 0 && ::gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) { + Socket sock(this->get_handle() ); + + sock.close(); + ::gnutls_deinit(this->session); + + return false; + } + + return true; + } + + long AdapterTls::nonblock_send_all( + const void *buf, const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept { + size_t record_size = length; + + if (0 == record_size) { + return -1; + } + + Socket sock(this->get_handle() ); + + size_t total = 0; + + while (total < length) { + if (record_size > length - total) { + record_size = length - total; + } + + long send_size = 0; + + do { + sock.nonblock_send_sync(); + + send_size = ::gnutls_record_send( + this->session, + reinterpret_cast(buf) + total, + record_size + ); + } + while (GNUTLS_E_AGAIN == send_size); + + if (send_size < 0) { + return send_size; + } + + total += long(send_size); + } + + return long(total); + } + + System::native_socket_type AdapterTls::get_handle() const noexcept { + return static_cast( + ::gnutls_transport_get_int(this->session) + ); + } + + ::gnutls_session_t AdapterTls::get_tls_session() const noexcept { + return this->session; + } + + Adapter *AdapterTls::copy() const noexcept { + return new AdapterTls(this->session); + } + + long AdapterTls::nonblock_recv( + void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept { + Socket sock(this->get_handle() ); + + long result; + + do { + if (sock.nonblock_recv_sync(timeout) == false) { + // Timeout + result = -1; + break; + } + + result = ::gnutls_record_recv(this->session, buf, length); + } + while (GNUTLS_E_AGAIN == result || GNUTLS_E_INTERRUPTED == result); + + return result; + } + + long AdapterTls::nonblock_send( + const void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept { + return this->nonblock_send_all(buf, length, timeout); + } + + void AdapterTls::close() noexcept { + Socket sock(this->get_handle() ); + + // Wait for send all data to client + sock.nonblock_send_sync(); + + ::gnutls_bye(this->session, GNUTLS_SHUT_RDWR); + + sock.close(); + + ::gnutls_deinit(this->session); + } +} diff --git a/src/socket/AdapterTls.h b/src/socket/AdapterTls.h new file mode 100644 index 0000000..04aab40 --- /dev/null +++ b/src/socket/AdapterTls.h @@ -0,0 +1,50 @@ +#pragma once + +#include "Adapter.h" +#include "Socket.h" + +namespace Socket +{ + class AdapterTls : public Adapter + { + private: + ::gnutls_session_t session; + + public: + AdapterTls() = delete; + + AdapterTls( + const Socket &sock, + ::gnutls_priority_t priority_cache, + ::gnutls_certificate_credentials_t x509_cred + ) noexcept; + + AdapterTls(const ::gnutls_session_t session) noexcept; + + bool handshake() noexcept; + + long nonblock_send_all( + const void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept; + + virtual System::native_socket_type get_handle() const noexcept override; + virtual ::gnutls_session_t get_tls_session() const noexcept override; + virtual Adapter *copy() const noexcept override; + + virtual long nonblock_recv( + void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept override; + + virtual long nonblock_send( + const void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept override; + + virtual void close() noexcept override; + }; +} diff --git a/src/socket/List.cpp b/src/socket/List.cpp new file mode 100644 index 0000000..eab986d --- /dev/null +++ b/src/socket/List.cpp @@ -0,0 +1,408 @@ + +#include "List.h" + +#ifdef POSIX + #include + #include + #include +#endif + +namespace Socket +{ + List::List() noexcept + { + #ifdef WIN32 + this->obj_list = INVALID_HANDLE_VALUE; + #elif POSIX + this->obj_list = ~0; + #else + #error "Undefined platform" + #endif + } + + List::List(List &&obj) noexcept + : obj_list(obj.obj_list) + { + #ifdef WIN32 + obj.obj_list = INVALID_HANDLE_VALUE; + obj.poll_events.swap(this->poll_events); + #elif POSIX + obj.obj_list = ~0; + obj.epoll_events.swap(this->epoll_events); + #else + #error "Undefined platform" + #endif + } + + List::~List() noexcept { + this->destroy(); + } + + bool List::create(const size_t startListSize) + { + this->destroy(); + + #ifdef WIN32 + this->obj_list = (HANDLE) 1; + + if (startListSize > 0) { + this->poll_events.reserve(startListSize); + } + + return true; + #elif POSIX + this->obj_list = ::epoll_create(startListSize); + + if (this->obj_list == ~0) { + return false; + } + + if (startListSize > 0) { + this->epoll_events.reserve(startListSize); + } + + return true; + #else + #error "Undefined platform" + #endif + } + + void List::destroy() noexcept + { + if (this->is_created() ) + { + #ifdef WIN32 + this->obj_list = INVALID_HANDLE_VALUE; + this->poll_events.clear(); + #elif POSIX + ::close(this->obj_list); + this->obj_list = ~0; + this->epoll_events.clear(); + #else + #error "Undefined platform" + #endif + } + } + + bool List::is_created() const noexcept + { + #ifdef WIN32 + return this->obj_list != INVALID_HANDLE_VALUE; + #elif POSIX + return this->obj_list != ~0; + #else + #error "Undefined platform" + #endif + } + + bool List::addSocket(const Socket &sock) noexcept + { + if (this->is_created() == false) { + return false; + } + + #ifdef WIN32 + WSAPOLLFD event { + sock.get_handle(), + POLLRDNORM, + 0 + }; + + poll_events.emplace_back(event); + + return true; + #elif POSIX + struct ::epoll_event event { + EPOLLIN | EPOLLET | EPOLLRDHUP, + reinterpret_cast(sock.get_handle() ) + }; + + const int result = ::epoll_ctl( + this->obj_list, + EPOLL_CTL_ADD, + sock.get_handle(), + &event + ); + + if (result == ~0) { + return false; + } + + this->epoll_events.emplace_back(); + + return true; + #else + #error "Undefined platform" + #endif + } + + bool List::removeSocket(const Socket &sock) noexcept + { + if (this->is_created() == false) { + return false; + } + + #ifdef WIN32 + for (size_t i = 0; i < poll_events.size(); ++i) { + if (sock.get_handle() == poll_events[i].fd) { + poll_events.erase(poll_events.begin() + i); + return true; + } + } + + return false; + #elif POSIX + const int result = ::epoll_ctl( + this->obj_list, + EPOLL_CTL_DEL, + sock.get_handle(), + nullptr + ); + + if (result == ~0) { + return false; + } + + this->epoll_events.pop_back(); + + return true; + #else + #error "Undefined platform" + #endif + } + + bool List::accept(std::vector &sockets) const noexcept + { + if (this->is_created() ) + { + #ifdef WIN32 + const int count = ::WSAPoll( + this->poll_events.data(), + static_cast<::ULONG>(this->poll_events.size() ), + ~0 + ); + + if (SOCKET_ERROR == count) { + return false; + } + + for (size_t i = 0; i < this->poll_events.size(); ++i) + { + const WSAPOLLFD &event = this->poll_events[i]; + + if (event.revents & POLLRDNORM) + { + System::native_socket_type client_socket = ~0; + + do { + client_socket = ::accept( + event.fd, + static_cast(nullptr), + static_cast(nullptr) + ); + + if (~0 != client_socket) { + sockets.emplace_back(Socket(client_socket) ); + } + } + while (~0 != client_socket); + } + } + + return false == sockets.empty(); + #elif POSIX + const int count = ::epoll_wait( + this->obj_list, + this->epoll_events.data(), + this->epoll_events.size(), + ~0 + ); + + if (count == ~0) { + return false; + } + + for (int i = 0; i < count; ++i) + { + const epoll_event &event = this->epoll_events[i]; + + if (event.events & EPOLLIN) + { + System::native_socket_type client_socket = ~0; + + do { + client_socket = ::accept( + event.data.fd, + static_cast(nullptr), + static_cast(nullptr) + ); + + if (~0 != client_socket) { + sockets.emplace_back(Socket(client_socket) ); + } + } + while (~0 != client_socket); + } + } + + return false == sockets.empty(); + #else + #error "Undefined platform" + #endif + } + + return false; + } + + bool List::accept( + std::vector &sockets, + std::vector &socketsAddress + ) const noexcept { + if (this->is_created() ) + { + #ifdef WIN32 + const int count = ::WSAPoll( + this->poll_events.data(), + static_cast<::ULONG>(this->poll_events.size() ), + ~0 + ); + + if (SOCKET_ERROR == count) { + return false; + } + + for (auto const &event : this->poll_events) + { + if (event.revents & POLLRDNORM) + { + System::native_socket_type client_socket = ~0; + + do { + ::sockaddr_in client_addr {}; + ::socklen_t client_addr_len = sizeof(client_addr); + + client_socket = ::accept( + event.fd, + reinterpret_cast<::sockaddr *>(&client_addr), + &client_addr_len + ); + + if (~0 != client_socket) { + sockets.emplace_back(Socket(client_socket) ); + socketsAddress.emplace_back(client_addr); + } + } + while (~0 != client_socket); + } + } + + return false == sockets.empty(); + #elif POSIX + const int count = ::epoll_wait( + this->obj_list, + this->epoll_events.data(), + this->epoll_events.size(), + ~0 + ); + + if (count == ~0) { + return false; + } + + for (int i = 0; i < count; ++i) + { + const epoll_event &event = this->epoll_events[i]; + + if (event.events & EPOLLIN) + { + System::native_socket_type client_socket = ~0; + + do { + ::sockaddr_in client_addr {}; + socklen_t client_addr_len = sizeof(client_addr); + + client_socket = ::accept( + event.data.fd, + reinterpret_cast<::sockaddr *>(&client_addr), + &client_addr_len + ); + + if (~0 != client_socket) { + sockets.emplace_back(Socket(client_socket) ); + socketsAddress.emplace_back(client_addr); + } + } + while (~0 != client_socket); + } + } + + return false == sockets.empty(); + #else + #error "Undefined platform" + #endif + } + + return false; + } + + bool List::recv( + std::vector &sockets, + std::vector &disconnected, + std::chrono::milliseconds timeout + ) const noexcept { + if (this->is_created() == false) { + return false; + } + + #ifdef WIN32 + const int count = ::WSAPoll( + this->poll_events.data(), + static_cast<::ULONG>(this->poll_events.size() ), + static_cast<::INT>(timeout.count() ) + ); + + if (SOCKET_ERROR == count) { + return false; + } + + for (auto const &event : this->poll_events) + { + if (event.revents & POLLRDNORM) { + sockets.emplace_back(Socket(event.fd) ); + } + else if (event.revents & POLLHUP) { + disconnected.emplace_back(Socket(event.fd) ); + } + } + + return false == sockets.empty() || false == disconnected.empty(); + #elif POSIX + const int count = ::epoll_wait( + this->obj_list, + this->epoll_events.data(), + this->epoll_events.size(), + timeout.count() + ); + + if (count == ~0) { + return false; + } + + for (int i = 0; i < count; ++i) + { + const epoll_event &event = this->epoll_events[i]; + + if (event.events & EPOLLIN) { + sockets.emplace_back(Socket(event.data.fd) ); + } + else if (event.events & EPOLLRDHUP) { + disconnected.emplace_back(Socket(event.data.fd) ); + } + } + + return false == sockets.empty() || false == disconnected.empty(); + #else + #error "Undefined platform" + #endif + } +} diff --git a/src/socket/List.h b/src/socket/List.h new file mode 100644 index 0000000..cdd2747 --- /dev/null +++ b/src/socket/List.h @@ -0,0 +1,53 @@ +#pragma once + +#include "Socket.h" + +#ifdef POSIX + #include + #include +#endif + +namespace Socket +{ + class List + { + protected: + #ifdef WIN32 + HANDLE obj_list; + mutable std::vector poll_events; + #elif POSIX + int obj_list; + mutable std::vector epoll_events; + #else + #error "Undefined platform" + #endif + + public: + List() noexcept; + List(List &&obj) noexcept; + ~List() noexcept; + + bool create(const size_t startListSize = 1); + void destroy() noexcept; + + bool is_created() const noexcept; + + bool addSocket(const Socket &sock) noexcept; + bool removeSocket(const Socket &sock) noexcept; + + bool accept( + std::vector &sockets + ) const noexcept; + + bool accept( + std::vector &sockets, + std::vector &socketsAddress + ) const noexcept; + + bool recv( + std::vector &sockets, + std::vector &errors, + std::chrono::milliseconds timeout = std::chrono::milliseconds(~0) + ) const noexcept; + }; +} diff --git a/src/socket/Socket.cpp b/src/socket/Socket.cpp new file mode 100644 index 0000000..05263db --- /dev/null +++ b/src/socket/Socket.cpp @@ -0,0 +1,556 @@ + +#include "Socket.h" + +#ifdef POSIX + #include + #include + #include + #include + #include + #include + #include +#endif + +namespace Socket +{ + bool Socket::Startup() noexcept + { + #ifdef WIN32 + unsigned short version = MAKEWORD(2, 2); + ::WSADATA wsaData = {0}; + return 0 == ::WSAStartup(version, &wsaData); + #elif POSIX + return true; + #else + #error "Undefined platform" + #endif + } + + bool Socket::Cleanup() noexcept + { + #ifdef WIN32 + return 0 == ::WSACleanup(); + #elif POSIX + return true; + #else + #error "Undefined platform" + #endif + } + + int Socket::getLastError() noexcept + { + #ifdef WIN32 + return ::WSAGetLastError(); + #elif POSIX + return errno; + #else + #error "Undefined platform" + #endif + } + + Socket::Socket() noexcept : socket_handle(~0) {} + + Socket::Socket(const System::native_socket_type fd) noexcept : socket_handle(fd) {} + + Socket::Socket(const Socket &obj) noexcept : socket_handle(obj.socket_handle) {} + + Socket::Socket(Socket &&obj) noexcept + : socket_handle(obj.socket_handle) + { + obj.socket_handle = ~0; + } + + bool Socket::open() noexcept { + this->close(); + this->socket_handle = ::socket(AF_INET, SOCK_STREAM, 0); + return this->is_open(); + } + + bool Socket::close() noexcept + { + if (this->is_open() ) + { + #ifdef WIN32 + const int result = ::closesocket(this->socket_handle); + #elif POSIX + const int result = ::close(this->socket_handle); + #else + #error "Undefined platform" + #endif + + if (0 == result) { + this->socket_handle = ~0; + return true; + } + } + + return false; + } + + bool Socket::is_open() const noexcept + { + #ifdef WIN32 + return INVALID_SOCKET != this->socket_handle; + #elif POSIX + return ~0 != this->socket_handle; + #else + #error "Undefined platform" + #endif + } + + System::native_socket_type Socket::get_handle() const noexcept { + return this->socket_handle; + } + + bool Socket::bind(const int port) const noexcept { + // GCC bug with global namespace: https://bbs.archlinux.org/viewtopic.php?id=53751 + auto const net_port = htons(static_cast(port)); + auto const net_addr = ::htonl(INADDR_ANY); + + const ::sockaddr_in sock_addr { + AF_INET, + net_port, + { net_addr }, + { 0 } // sin_zero + }; + + return 0 == ::bind( + this->socket_handle, + reinterpret_cast(&sock_addr), + sizeof(::sockaddr_in) + ); + } + + bool Socket::listen() const noexcept { + return 0 == ::listen(this->socket_handle, SOMAXCONN); + } + + Socket Socket::accept() const noexcept + { + #ifdef WIN32 + System::native_socket_type client_socket = ::accept( + this->socket_handle, + static_cast(nullptr), + static_cast(nullptr) + ); + #elif POSIX + System::native_socket_type client_socket = ::accept( + this->socket_handle, + static_cast(nullptr), + static_cast(nullptr) + ); + #else + #error "Undefined platform" + #endif + return Socket(client_socket); + } + + Socket Socket::nonblock_accept() const noexcept + { + System::native_socket_type client_socket = ~0; + #ifdef WIN32 + WSAPOLLFD event { + this->socket_handle, + POLLRDNORM, + 0 + }; + + if (1 == ::WSAPoll(&event, 1, ~0) && event.revents & POLLRDNORM) { + client_socket = ::accept( + this->socket_handle, + static_cast(nullptr), + static_cast(nullptr) + ); + } + #elif POSIX + struct ::pollfd event { + this->socket_handle, + POLLIN, + 0 + }; + + if (1 == ::poll(&event, 1, ~0) && event.revents & POLLIN) { + client_socket = ::accept( + this->socket_handle, + static_cast(nullptr), + static_cast(nullptr) + ); + } + #else + #error "Undefined platform" + #endif + return Socket(client_socket); + } + + Socket Socket::nonblock_accept(const std::chrono::milliseconds &timeout) const noexcept + { + System::native_socket_type client_socket = ~0; + #ifdef WIN32 + WSAPOLLFD event { + this->socket_handle, + POLLRDNORM, + 0 + }; + + if (1 == ::WSAPoll(&event, 1, int(timeout.count()) ) && event.revents & POLLRDNORM) { + client_socket = ::accept( + this->socket_handle, + static_cast(nullptr), + static_cast(nullptr) + ); + } + #elif POSIX + struct ::pollfd event { + this->socket_handle, + POLLIN, + 0 + }; + + if (1 == ::poll(&event, 1, int(timeout.count()) ) && event.revents & POLLIN) { + client_socket = ::accept( + this->socket_handle, + static_cast(nullptr), + static_cast(nullptr) + ); + } + #else + #error "Undefined platform" + #endif + return Socket(client_socket); + } + + bool Socket::shutdown() const noexcept + { + if (is_open() ) + { + #ifdef WIN32 + return 0 == ::shutdown(this->socket_handle, SD_BOTH); + #elif POSIX + return 0 == ::shutdown(this->socket_handle, SHUT_RDWR); + #else + #error "Undefined platform" + #endif + } + + return false; + } + + bool Socket::nonblock(const bool isNonBlock) const noexcept + { + #ifdef WIN32 + unsigned long value = isNonBlock; + + return 0 == ::ioctlsocket( + this->socket_handle, + FIONBIO, + &value + ); + #elif POSIX + return ~0 != ::fcntl( + this->socket_handle, + F_SETFL, + isNonBlock + ? O_NONBLOCK + : O_SYNC + ); + #else + #error "Undefined platform" + #endif + } + +/* + bool Socket::is_nonblock() const noexcept + { + #ifdef WIN32 + + #elif POSIX + const int flags = ::fcntl(socket_handle, F_GETFL, 0); + return (flags != ~0) && (flags & O_NONBLOCK); + #else + #error "Undefined platform" + #endif + } +*/ + + bool Socket::tcp_nodelay(const bool nodelay) const noexcept + { + #ifdef WIN32 + int flags = nodelay ? 1 : 0; + + return 0 == ::setsockopt( + this->socket_handle, + IPPROTO_TCP, + TCP_NODELAY, + reinterpret_cast(&flags), + sizeof(flags) + ); + #elif POSIX + int flags = nodelay ? 1 : 0; + + return 0 == ::setsockopt( + this->socket_handle, + IPPROTO_TCP, + TCP_NODELAY, + &flags, + sizeof(flags) + ); + #else + #error "Undefined platform" + #endif + } + + long Socket::recv( + std::vector &buf + ) const noexcept { + return this->recv(buf.data(), buf.size() ); + } + + long Socket::recv( + void *buf, + const size_t length + ) const noexcept { + #ifdef WIN32 + return ::recv( + this->socket_handle, + reinterpret_cast(buf), + static_cast(length), + 0 + ); + #elif POSIX + return ::recv(this->socket_handle, buf, length, 0); + #else + #error "Undefined platform" + #endif + } + + long Socket::nonblock_recv( + std::vector &buf, + const std::chrono::milliseconds &timeout + ) const noexcept { + return this->nonblock_recv( + buf.data(), + buf.size(), + timeout + ); + } + + long Socket::nonblock_recv( + void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept { + long recv_len = ~0; + #ifdef WIN32 + WSAPOLLFD event { + this->socket_handle, + POLLRDNORM, + 0 + }; + + if (1 == ::WSAPoll(&event, 1, int(timeout.count()) ) && event.revents & POLLRDNORM) { + recv_len = ::recv( + this->socket_handle, + reinterpret_cast(buf), + static_cast(length), + 0 + ); + } + #elif POSIX + struct ::pollfd event { + this->socket_handle, + POLLIN, + 0 + }; + + if (1 == ::poll(&event, 1, int(timeout.count()) ) && event.revents & POLLIN) { + recv_len = ::recv(this->socket_handle, buf, length, 0); + } + #else + #error "Undefined platform" + #endif + return recv_len; + } + + bool Socket::nonblock_recv_sync( + const std::chrono::milliseconds &timeout + ) const noexcept { + #ifdef WIN32 + WSAPOLLFD event { + this->socket_handle, + POLLIN, + 0 + }; + + return ::WSAPoll(&event, 1, int(timeout.count()) ) == 1; + #elif POSIX + struct ::pollfd event { + this->socket_handle, + POLLIN, + 0 + }; + + return ::poll(&event, 1, int(timeout.count()) ) == 1; + #else + #error "Undefined platform" + #endif + } + + static long send_all( + const System::native_socket_type socket_handle, + const void *data, + const size_t length + ) noexcept { + size_t total = 0; + + while (total < length) { + const long send_size = ::send( + socket_handle, + reinterpret_cast(data) + total, + length - total, + 0 + ); + + if (send_size < 0) { + return send_size; + } + + total += size_t(send_size); + } + + return static_cast(total); + } + + long Socket::send(const std::string &buf) const noexcept { + return this->send(buf.data(), buf.length() ); + } + + long Socket::send(const void *buf, const size_t length) const noexcept { + return send_all(this->socket_handle, buf, length); + } + + static long nonblock_send_all( + const System::native_socket_type socket_handle, + const void *data, + const size_t length, + const std::chrono::milliseconds &timeout + ) noexcept { + size_t total = 0; + + #ifdef WIN32 + WSAPOLLFD event = { + socket_handle, + POLLOUT, + 0 + }; + + while (total < length) { + if (1 == ::WSAPoll(&event, 1, int(timeout.count()) ) && event.revents & POLLOUT) { + const long send_size = ::send( + socket_handle, + reinterpret_cast(data) + total, + static_cast(length - total), + 0 + ); + + if (send_size < 0) { + return send_size; + } + + total += size_t(send_size); + } else { + return -1; + } + } + + #elif POSIX + struct ::pollfd event = { + socket_handle, + POLLOUT, + 0 + }; + + while (total < length) { + if (1 == ::poll(&event, 1, int(timeout.count()) ) && event.revents & POLLOUT) { + const long send_size = ::send( + socket_handle, + reinterpret_cast(data) + total, + length - total, + 0 + ); + + if (send_size < 0) { + return send_size; + } + + total += size_t(send_size); + } else { + return -1; + } + } + #else + #error "Undefined platform" + #endif + + return long(total); + } + + long Socket::nonblock_send( + const std::string &buf, + const std::chrono::milliseconds &timeout + ) const noexcept { + return this->nonblock_send( + buf.data(), + buf.length(), + timeout + ); + } + + long Socket::nonblock_send( + const void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept { + return nonblock_send_all( + this->socket_handle, + buf, + length, + timeout + ); + } + + void Socket::nonblock_send_sync() const noexcept + { + #ifdef WIN32 + WSAPOLLFD event { + this->socket_handle, + POLLOUT, + 0 + }; + + ::WSAPoll(&event, 1, ~0); + #elif POSIX + struct ::pollfd event { + this->socket_handle, + POLLOUT, + 0 + }; + + ::poll(&event, 1, ~0); + #else + #error "Undefined platform" + #endif + } + + Socket &Socket::operator=(const Socket &obj) noexcept { + this->socket_handle = obj.socket_handle; + return *this; + } + + bool Socket::operator ==(const Socket &obj) const noexcept { + return this->socket_handle == obj.socket_handle; + } + + bool Socket::operator !=(const Socket &obj) const noexcept { + return this->socket_handle != obj.socket_handle; + } +} diff --git a/src/socket/Socket.h b/src/socket/Socket.h new file mode 100644 index 0000000..0d5b711 --- /dev/null +++ b/src/socket/Socket.h @@ -0,0 +1,106 @@ +#pragma once + +#include "../system/System.h" + +#include +#include +#include + +namespace Socket +{ + class Socket + { + protected: + System::native_socket_type socket_handle; + + public: + bool static Startup() noexcept; + bool static Cleanup() noexcept; + int static getLastError() noexcept; + + public: + Socket() noexcept; + Socket(const System::native_socket_type fd) noexcept; + Socket(const Socket &obj) noexcept; + Socket(Socket &&obj) noexcept; + + ~Socket() noexcept = default; + + bool open() noexcept; + bool close() noexcept; + + bool is_open() const noexcept; + System::native_socket_type get_handle() const noexcept; + + bool bind(const int port) const noexcept; + bool listen() const noexcept; + + Socket accept() const noexcept; + Socket nonblock_accept() const noexcept; + + Socket nonblock_accept( + const std::chrono::milliseconds &timeout + ) const noexcept; + + bool shutdown() const noexcept; + + bool nonblock(const bool isNonBlock = true) const noexcept; + // bool is_nonblock() const noexcept; + bool tcp_nodelay(const bool nodelay = true) const noexcept; + + long recv( + std::vector &buf + ) const noexcept; + + long recv( + void *buf, + const size_t length + ) const noexcept; + + long nonblock_recv( + std::vector &buf, + const std::chrono::milliseconds &timeout + ) const noexcept; + + long nonblock_recv( + void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept; + + bool nonblock_recv_sync( + const std::chrono::milliseconds &timeout + ) const noexcept; + + long send(const std::string &buf) const noexcept; + long send(const void *buf, const size_t length) const noexcept; + + long nonblock_send( + const std::string &buf, + const std::chrono::milliseconds &timeout + ) const noexcept; + + long nonblock_send( + const void *buf, + const size_t length, + const std::chrono::milliseconds &timeout + ) const noexcept; + + void nonblock_send_sync() const noexcept; + + Socket &operator =(const Socket &obj) noexcept; + + bool operator ==(const Socket &obj) const noexcept; + bool operator !=(const Socket &obj) const noexcept; + }; +} + +namespace std +{ + // Hash for Socket + template<> struct hash { + std::size_t operator()(const Socket::Socket &obj) const noexcept { + return std::hash{}(obj.get_handle() ); + } + }; +} diff --git a/src/system/Cache.h b/src/system/Cache.h new file mode 100644 index 0000000..f8998e2 --- /dev/null +++ b/src/system/Cache.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace System +{ + constexpr int cacheLine = 64; + + template + struct CachePadding + { + private: + static constexpr int padding_size = cacheLine - sizeof(T) % cacheLine; + + uint8_t padding[padding_size > 0 ? padding_size : sizeof(void *)]; + }; + + template + struct CachePaddingSize + { + private: + uint8_t padding[cacheLine - T % cacheLine]; + }; +} diff --git a/src/GlobalMutex.cpp b/src/system/GlobalMutex.cpp similarity index 63% rename from src/GlobalMutex.cpp rename to src/system/GlobalMutex.cpp index 68a0c3a..9a5477e 100644 --- a/src/GlobalMutex.cpp +++ b/src/system/GlobalMutex.cpp @@ -11,14 +11,15 @@ #include #endif -namespace HttpServer +namespace System { - GlobalMutex::GlobalMutex() : mtx_desc(nullptr) + GlobalMutex::GlobalMutex() noexcept + : mtx_desc(nullptr) { } - GlobalMutex::~GlobalMutex() + GlobalMutex::~GlobalMutex() noexcept { this->close(); } @@ -38,24 +39,31 @@ namespace HttpServer const std::string &mutex_name = this->mtx_name; #endif - this->mtx_desc = ::CreateMutex(nullptr, false, mutex_name.c_str() ); + this->mtx_desc = ::CreateMutex( + nullptr, + false, + mutex_name.c_str() + ); - if (nullptr == this->mtx_desc) - { + if (nullptr == this->mtx_desc) { return false; } #elif POSIX - ::sem_t *sem = ::sem_open(mutexName.c_str(), O_CREAT, 0666, 1); - - if (SEM_FAILED == sem) - { + ::sem_t *sem = ::sem_open( + mutexName.c_str(), + O_CREAT, + 0666, + 1 + ); + + if (SEM_FAILED == sem) { return false; } this->mtx_desc = sem; this->mtx_name = mutexName; #else - #error "Undefine platform" + #error "Undefined platform" #endif return true; @@ -73,13 +81,17 @@ namespace HttpServer const std::string &mutex_name = mtx_name; #endif - ::HANDLE hMutex = ::OpenMutex(DELETE, true, mutex_name.c_str() ); + const ::HANDLE hMutex = ::OpenMutex( + DELETE, + true, + mutex_name.c_str() + ); return 0 != ::CloseHandle(hMutex); #elif POSIX return 0 == ::sem_unlink(mutexName.c_str() ); #else - #error "Undefine platform" + #error "Undefined platform" #endif } @@ -93,21 +105,29 @@ namespace HttpServer const std::string &mutex_name = this->mtx_name; #endif - ::HANDLE hMutex = ::OpenMutex(DELETE, true, mutex_name.c_str() ); + const ::HANDLE hMutex = ::OpenMutex( + DELETE, + true, + mutex_name.c_str() + ); - const bool ret = (0 != ::CloseHandle(hMutex) ); + const bool ret = ( + 0 != ::CloseHandle(hMutex) + ); this->close(); return ret; #elif POSIX - const int ret = ::sem_unlink(this->mtx_name.c_str() ); + const int ret = ::sem_unlink( + this->mtx_name.c_str() + ); - this->close(); + this->close(); - return 0 == ret; + return 0 == ret; #else - #error "Undefine platform" + #error "Undefined platform" #endif } @@ -126,23 +146,28 @@ namespace HttpServer const std::string &mutex_name = this->mtx_name; #endif - this->mtx_desc = ::OpenMutex(SYNCHRONIZE, false, mutex_name.c_str() ); + this->mtx_desc = ::OpenMutex( + SYNCHRONIZE, + false, + mutex_name.c_str() + ); - if (nullptr == this->mtx_desc) - { + if (nullptr == this->mtx_desc) { return false; } #elif POSIX - ::sem_t *sem = ::sem_open(mutexName.c_str(), 0); + ::sem_t *sem = ::sem_open( + mutexName.c_str(), + 0 + ); - if (SEM_FAILED == sem) - { + if (SEM_FAILED == sem) { return false; } this->mtx_desc = sem; #else - #error "Undefine platform" + #error "Undefined platform" #endif this->mtx_name = mutexName; @@ -150,7 +175,7 @@ namespace HttpServer return true; } - bool GlobalMutex::close() + bool GlobalMutex::close() noexcept { if (this->mtx_desc) { @@ -161,7 +186,7 @@ namespace HttpServer ::sem_close(this->mtx_desc); this->mtx_desc = nullptr; #else - #error "Undefine platform" + #error "Undefined platform" #endif this->mtx_name.clear(); @@ -172,47 +197,53 @@ namespace HttpServer return false; } - bool GlobalMutex::is_open() const + bool GlobalMutex::is_open() const noexcept { #ifdef WIN32 return nullptr != this->mtx_desc; #elif POSIX return nullptr != this->mtx_desc; #else - #error "Undefine platform" + #error "Undefined platform" #endif } - bool GlobalMutex::lock() const + bool GlobalMutex::lock() const noexcept { #ifdef WIN32 - return WAIT_OBJECT_0 == ::WaitForSingleObject(this->mtx_desc, INFINITE); + return WAIT_OBJECT_0 == ::WaitForSingleObject( + this->mtx_desc, + INFINITE + ); #elif POSIX return 0 == ::sem_wait(this->mtx_desc); #else - #error "Undefine platform" + #error "Undefined platform" #endif } - bool GlobalMutex::try_lock() const + bool GlobalMutex::try_lock() const noexcept { #ifdef WIN32 - return WAIT_OBJECT_0 == ::WaitForSingleObject(this->mtx_desc, 0); + return WAIT_OBJECT_0 == ::WaitForSingleObject( + this->mtx_desc, + 0 + ); #elif POSIX return 0 == ::sem_trywait(this->mtx_desc); #else - #error "Undefine platform" + #error "Undefined platform" #endif } - bool GlobalMutex::unlock() const + bool GlobalMutex::unlock() const noexcept { #ifdef WIN32 return 0 != ::ReleaseMutex(this->mtx_desc); #elif POSIX return 0 == ::sem_post(this->mtx_desc); #else - #error "Undefine platform" + #error "Undefined platform" #endif } -}; \ No newline at end of file +} diff --git a/src/GlobalMutex.h b/src/system/GlobalMutex.h similarity index 63% rename from src/GlobalMutex.h rename to src/system/GlobalMutex.h index 42cee55..d6bc0b0 100644 --- a/src/GlobalMutex.h +++ b/src/system/GlobalMutex.h @@ -9,7 +9,7 @@ #include -namespace HttpServer +namespace System { class GlobalMutex { @@ -19,14 +19,14 @@ namespace HttpServer #elif POSIX ::sem_t *mtx_desc; #else - #error "Undefine platform" + #error "Undefined platform" #endif std::string mtx_name; public: - GlobalMutex(); - ~GlobalMutex(); + GlobalMutex() noexcept; + ~GlobalMutex() noexcept; bool create(const std::string &mutexName); bool destory(); @@ -34,12 +34,12 @@ namespace HttpServer static bool destory(const std::string &mutexName); bool open(const std::string &mutexName); - bool close(); + bool close() noexcept; - bool is_open() const; + bool is_open() const noexcept; - bool lock() const; - bool try_lock() const; - bool unlock() const; + bool lock() const noexcept; + bool try_lock() const noexcept; + bool unlock() const noexcept; }; -}; \ No newline at end of file +} diff --git a/src/system/Module.cpp b/src/system/Module.cpp new file mode 100644 index 0000000..ef7f512 --- /dev/null +++ b/src/system/Module.cpp @@ -0,0 +1,228 @@ + +#include "Module.h" + +#ifdef WIN32 + #include + + #ifdef UNICODE + #include + #endif +#elif POSIX + #include + #include +#endif + +namespace System +{ + Module::Module() noexcept + : lib_handle(nullptr) + { + + } + + Module::Module(const std::string &libPath) + : lib_handle(nullptr) + { + open(libPath); + } + + Module::Module(const Module &obj) noexcept + : lib_handle(obj.lib_handle) + { + + } + + Module::Module(Module &&obj) noexcept + : lib_handle(obj.lib_handle) + { + obj.lib_handle = nullptr; + } + + bool Module::is_open() const noexcept { + return nullptr != this->lib_handle; + } + + bool Module::open(const std::string &libPath) + { + if (this->is_open() ) { + this->close(); + } + + #ifdef WIN32 + const size_t pos_slash = libPath.rfind('\\'); + const size_t pos_slash_back = libPath.rfind('/'); + + const size_t pos = ( + pos_slash != std::string::npos && + pos_slash > pos_slash_back + ? pos_slash + : pos_slash_back != std::string::npos + ? pos_slash_back + : std::string::npos + ); + + ::DLL_DIRECTORY_COOKIE cookie = nullptr; + + if (std::string::npos != pos) { + const std::wstring directory( + libPath.cbegin(), + libPath.cbegin() + pos + 1 + ); + + cookie = ::AddDllDirectory( + directory.data() + ); + } + + #ifdef UNICODE + std::wstring_convert > converter; + const std::wstring lib_path = converter.from_bytes(libPath); + #else + const std::string &lib_path = libPath; + #endif + + this->lib_handle = ::LoadLibraryEx( + lib_path.c_str(), + 0, + LOAD_LIBRARY_SEARCH_DEFAULT_DIRS + ); + + if (cookie) { + ::RemoveDllDirectory(cookie); + } + #elif POSIX + this->lib_handle = ::dlopen( + libPath.c_str(), + RTLD_NOW | RTLD_LOCAL + ); + #else + #error "Undefined platform" + #endif + + if (nullptr == this->lib_handle) + { + #ifdef POSIX + std::cout << ::dlerror() << std::endl; + #endif + return false; + } + + return true; + } + + void Module::close() noexcept + { + if (this->lib_handle) + { + #ifdef WIN32 + ::FreeLibrary(this->lib_handle); + #elif POSIX + ::dlclose(this->lib_handle); + #else + #error "Undefined platform" + #endif + + this->lib_handle = nullptr; + } + } + + bool Module::find( + const std::string &symbolName, + void *(**addr)(void *) + ) const noexcept { + if (lib_handle) { + #ifdef WIN32 + *addr = reinterpret_cast( + ::GetProcAddress( + this->lib_handle, + symbolName.c_str() + ) + ); + + return nullptr != *addr; + #elif POSIX + char *error = ::dlerror(); + + *addr = reinterpret_cast( + ::dlsym( + this->lib_handle, + symbolName.c_str() + ) + ); + + error = ::dlerror(); + + return nullptr == error; + #else + #error "Undefined platform" + #endif + } + + return false; + } + + bool Module::find( + const char *symbolName, + void *(**addr)(void *) + ) const noexcept { + if (lib_handle) + { + #ifdef WIN32 + *addr = reinterpret_cast( + ::GetProcAddress( + this->lib_handle, + symbolName + ) + ); + + return nullptr != *addr; + #elif POSIX + char *error = ::dlerror(); + + *addr = reinterpret_cast( + ::dlsym( + this->lib_handle, + symbolName + ) + ); + + error = ::dlerror(); + + return nullptr == error; + #else + #error "Undefined platform" + #endif + } + + return false; + } + + bool Module::operator ==(const Module &obj) const noexcept { + return this->lib_handle == obj.lib_handle; + } + + bool Module::operator !=(const Module &obj) const noexcept { + return this->lib_handle != obj.lib_handle; + } + + Module &Module::operator =(const Module &obj) noexcept + { + if (*this != obj) { + this->close(); + this->lib_handle = obj.lib_handle; + } + + return *this; + } + + Module &Module::operator =(Module &&obj) noexcept + { + if (*this != obj) { + this->close(); + this->lib_handle = obj.lib_handle; + obj.lib_handle = nullptr; + } + + return *this; + } +} diff --git a/src/system/Module.h b/src/system/Module.h new file mode 100644 index 0000000..d7739ed --- /dev/null +++ b/src/system/Module.h @@ -0,0 +1,52 @@ +#pragma once + +#ifdef WIN32 + #include + #include +#endif + +#include + +namespace System +{ + class Module + { + protected: + #ifdef WIN32 + ::HMODULE lib_handle; + #elif POSIX + void *lib_handle; + #else + #error "Undefined platform" + #endif + + public: + Module() noexcept; + Module(const std::string &libPath); + Module(const Module &obj) noexcept; + Module(Module &&obj) noexcept; + + ~Module() noexcept = default; + + bool is_open() const noexcept; + + bool open(const std::string &libPath); + void close() noexcept; + + bool find( + const std::string &symbolName, + void *(**addr)(void *) + ) const noexcept; + + bool find( + const char *symbolName, + void *(**addr)(void *) + ) const noexcept; + + bool operator ==(const Module &obj) const noexcept; + bool operator !=(const Module &obj) const noexcept; + + Module &operator =(const Module &obj) noexcept; + Module &operator =(Module &&obj) noexcept; + }; +} diff --git a/src/SharedMemory.cpp b/src/system/SharedMemory.cpp similarity index 56% rename from src/SharedMemory.cpp rename to src/system/SharedMemory.cpp index 878d3d4..a88b1e7 100644 --- a/src/SharedMemory.cpp +++ b/src/system/SharedMemory.cpp @@ -15,26 +15,27 @@ #include -namespace HttpServer +namespace System { - SharedMemory::SharedMemory() + SharedMemory::SharedMemory() noexcept { #ifdef WIN32 this->shm_desc = nullptr; #elif POSIX this->shm_desc = -1; #else - #error "Undefine platform" + #error "Undefined platform" #endif } - SharedMemory::~SharedMemory() - { + SharedMemory::~SharedMemory() noexcept { this->close(); } - bool SharedMemory::create(const std::string &memName, const size_t memSize) - { + bool SharedMemory::create( + const std::string &memName, + const size_t memSize + ) { this->close(); #ifdef WIN32 @@ -57,31 +58,31 @@ namespace HttpServer memory_name.c_str() ); - if (nullptr == this->shm_desc) - { + if (nullptr == this->shm_desc) { return false; } #elif POSIX - this->shm_desc = ::shm_open(memName.c_str(), O_CREAT | O_RDWR, 0666); + this->shm_desc = ::shm_open( + memName.c_str(), + O_CREAT | O_RDWR, + 0666 + ); - if (-1 == this->shm_desc) - { + if (-1 == this->shm_desc) { return false; } - if (-1 == ::ftruncate64(this->shm_desc, memSize) ) - { + if (::ftruncate64(this->shm_desc, long(memSize)) == -1) { this->destroy(memName); - return false; } this->shm_name = memName; #else - #error "Undefine platform" + #error "Undefined platform" #endif return true; @@ -102,10 +103,13 @@ namespace HttpServer const std::string &memory_name = this->shm_name; #endif - this->shm_desc = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, false, memory_name.c_str() ); + this->shm_desc = ::OpenFileMapping( + FILE_MAP_ALL_ACCESS, + false, + memory_name.c_str() + ); - if (nullptr == this->shm_desc) - { + if (nullptr == this->shm_desc) { return false; } @@ -113,41 +117,55 @@ namespace HttpServer #elif POSIX - this->shm_desc = ::shm_open(memName.c_str(), O_RDWR, 0666); + this->shm_desc = ::shm_open( + memName.c_str(), + O_RDWR, + 0666 + ); - if (-1 == this->shm_desc) - { + if (-1 == this->shm_desc) { return false; } this->shm_name = memName; #else - #error "Undefine platform" + #error "Undefined platform" #endif return true; } - bool SharedMemory::is_open() const + bool SharedMemory::is_open() const noexcept { #ifdef WIN32 return nullptr != this->shm_desc; #elif POSIX return -1 != this->shm_desc; #else - #error "Undefine platform" + #error "Undefined platform" #endif } - bool SharedMemory::write(const void *src, const size_t size, const size_t offset) const - { + bool SharedMemory::write( + const void *src, + const size_t size, + const size_t offset + ) const noexcept { #ifdef WIN32 - void * const addr = ::MapViewOfFile(this->shm_desc, FILE_MAP_WRITE, 0, static_cast<::DWORD>(offset), size); + ::ULARGE_INTEGER off; + off.QuadPart = ::ULONGLONG(offset); - if (nullptr == addr) - { + void * const addr = ::MapViewOfFile( + this->shm_desc, + FILE_MAP_WRITE, + off.HighPart, + off.LowPart, + size + ); + + if (nullptr == addr) { return false; } @@ -157,32 +175,49 @@ namespace HttpServer #elif POSIX - void * const addr = ::mmap(0, size, PROT_WRITE, MAP_SHARED, this->shm_desc, offset); + void * const addr = ::mmap( + nullptr, + size, + PROT_WRITE, + MAP_SHARED, + this->shm_desc, + long(offset) + ); - if (reinterpret_cast(-1) == addr) - { + if (reinterpret_cast(-1) == addr) { return false; } - ::memcpy(addr, src, size); + std::memcpy(addr, src, size); ::munmap(addr, size); #else - #error "Undefine platform" + #error "Undefined platform" #endif return true; } - bool SharedMemory::read(void *dest, const size_t size, const size_t offset) const - { + bool SharedMemory::read( + void *dest, + const size_t size, + const size_t offset + ) const noexcept { #ifdef WIN32 - void * const addr = ::MapViewOfFile(this->shm_desc, FILE_MAP_READ, 0, static_cast<::DWORD>(offset), size); + ::ULARGE_INTEGER off; + off.QuadPart = ::ULONGLONG(offset); - if (nullptr == addr) - { + void * const addr = ::MapViewOfFile( + this->shm_desc, + FILE_MAP_READ, + off.HighPart, + off.LowPart, + size + ); + + if (nullptr == addr) { return false; } @@ -192,25 +227,31 @@ namespace HttpServer #elif POSIX - void * const addr = ::mmap(0, size, PROT_READ, MAP_SHARED, this->shm_desc, offset); + void * const addr = ::mmap( + nullptr, + size, + PROT_READ, + MAP_SHARED, + this->shm_desc, + long(offset) + ); - if (reinterpret_cast(-1) == addr) - { + if (reinterpret_cast(-1) == addr) { return false; } - ::memcpy(dest, addr, size); + std::memcpy(dest, addr, size); ::munmap(addr, size); #else - #error "Undefine platform" + #error "Undefined platform" #endif return true; } - bool SharedMemory::close() + bool SharedMemory::close() noexcept { if (this->is_open() ) { @@ -221,7 +262,7 @@ namespace HttpServer ::close(this->shm_desc); this->shm_desc = ~0; #else - #error "Undefine platform" + #error "Undefined platform" #endif this->shm_name.clear(); @@ -242,21 +283,29 @@ namespace HttpServer const std::string &memory_name = this->shm_name; #endif - ::HANDLE hMemory = ::OpenFileMapping(DELETE, false, memory_name.c_str() ); + const ::HANDLE hMemory = ::OpenFileMapping( + DELETE, + false, + memory_name.c_str() + ); - const bool ret = (0 != ::CloseHandle(hMemory) ); + const bool ret = ( + 0 != ::CloseHandle(hMemory) + ); this->close(); return ret; #elif POSIX - const int ret = ::shm_unlink(this->shm_name.c_str() ); + const int ret = ::shm_unlink( + this->shm_name.c_str() + ); this->close(); return 0 == ret; #else - #error "Undefine platform" + #error "Undefined platform" #endif } @@ -272,13 +321,17 @@ namespace HttpServer const std::string &memory_name = shm_name; #endif - ::HANDLE hMemory = ::OpenFileMapping(DELETE, false, memory_name.c_str() ); + const ::HANDLE hMemory = ::OpenFileMapping( + DELETE, + false, + memory_name.c_str() + ); return 0 != ::CloseHandle(hMemory); #elif POSIX return 0 == ::shm_unlink(memName.c_str() ); #else - #error "Undefine platform" + #error "Undefined platform" #endif } -}; \ No newline at end of file +} diff --git a/src/system/SharedMemory.h b/src/system/SharedMemory.h new file mode 100644 index 0000000..b9a84e9 --- /dev/null +++ b/src/system/SharedMemory.h @@ -0,0 +1,55 @@ +#pragma once + +#ifdef WIN32 + #include + #include +#endif + +#include + +namespace System +{ + class SharedMemory + { + private: + #ifdef WIN32 + ::HANDLE shm_desc; + #elif POSIX + int shm_desc; + #else + #error "Undefined platform" + #endif + + std::string shm_name; + + public: + SharedMemory() noexcept; + ~SharedMemory() noexcept; + + bool create( + const std::string &memName, + const size_t memSize + ); + + bool open(const std::string &memName); + + bool is_open() const noexcept; + + bool write( + const void *data, + const size_t size, + const size_t offset = 0 + ) const noexcept; + + bool read( + void *dest, + const size_t size, + const size_t offset = 0 + ) const noexcept; + + bool close() noexcept; + bool destroy(); + + static bool destroy(const std::string &memName); + }; +} diff --git a/src/System.cpp b/src/system/System.cpp similarity index 69% rename from src/System.cpp rename to src/system/System.cpp index c903837..ebb5935 100644 --- a/src/System.cpp +++ b/src/system/System.cpp @@ -1,4 +1,4 @@ - + #include "System.h" #include @@ -20,8 +20,7 @@ namespace System { #ifdef WIN32 - struct EnumData - { + struct EnumData { native_processid_type process_id; ::HWND hWnd; }; @@ -34,16 +33,14 @@ namespace System ::GetWindowThreadProcessId(hWnd, &process_id); - if (process_id == ed.process_id && ::GetConsoleWindow() != hWnd) + if (process_id == ed.process_id && ::GetConsoleWindow() != hWnd) { std::array<::TCHAR, 257> class_name; - ::GetClassName(hWnd, class_name.data(), static_cast(class_name.size() - 1) ); + ::GetClassName(hWnd, class_name.data(), int(class_name.size() - 1) ); - if (0 == ::_tcscmp(class_name.data(), myWndClassName) ) - { + if (0 == ::_tcscmp(class_name.data(), myWndClassName) ) { ed.hWnd = hWnd; - return false; } } @@ -52,14 +49,14 @@ namespace System } #endif - native_processid_type getProcessId() + native_processid_type getProcessId() noexcept { #ifdef WIN32 return ::GetCurrentProcessId(); #elif POSIX return ::getpid(); #else - #error "Undefine platform" + #error "Undefined platform" #endif } @@ -76,11 +73,11 @@ namespace System #elif POSIX return 0 == ::chdir(dir.c_str() ); #else - #error "Undefine platform" + #error "Undefined platform" #endif } - bool isProcessExists(const native_processid_type pid) + bool isProcessExists(const native_processid_type pid) noexcept { #ifdef WIN32 HANDLE hProcess = ::OpenProcess(SYNCHRONIZE, false, pid); @@ -89,19 +86,18 @@ namespace System #elif POSIX return 0 == ::kill(pid, 0); #else - #error "Undefine platform" + #error "Undefined platform" #endif } - bool sendSignal(const native_processid_type pid, const int signal) + bool sendSignal(const native_processid_type pid, const int signal) noexcept { #ifdef WIN32 EnumData ed = {pid, 0}; ::EnumWindows(EnumProc, reinterpret_cast<::LPARAM>(&ed) ); - if (0 == ed.hWnd) - { + if (0 == ed.hWnd) { return false; } @@ -109,18 +105,18 @@ namespace System #elif POSIX return 0 == ::kill(pid, signal); #else - #error "Undefine platform" + #error "Undefined platform" #endif } - bool isDoneThread(const std::thread::native_handle_type handle) + bool isDoneThread(const std::thread::native_handle_type handle) noexcept { #ifdef WIN32 return WAIT_OBJECT_0 == ::WaitForSingleObject(handle, 0); #elif POSIX return 0 != ::pthread_kill(handle, 0); #else - #error "Undefine platform" + #error "Undefined platform" #endif } @@ -138,23 +134,21 @@ namespace System return std::string(buf.cbegin(), buf.cbegin() + len); #endif #elif POSIX - const char *buf = ::getenv("TMPDIR"); + const char *buf = ::getenv("TMPDIR"); - if (nullptr == buf) - { + if (nullptr == buf) { return std::string("/tmp/"); } std::string str(buf); - if ('/' != str.back() ) - { + if (str.back() != '/') { str.push_back('/'); } return str; #else - #error "Undefine platform" + #error "Undefined platform" #endif } @@ -170,8 +164,7 @@ namespace System const ::DWORD attrib = ::GetFileAttributes(file_name.c_str() ); - if (INVALID_FILE_ATTRIBUTES == attrib) - { + if (INVALID_FILE_ATTRIBUTES == attrib) { return false; } @@ -179,19 +172,21 @@ namespace System #elif POSIX struct ::stat attrib; - if (-1 == ::stat(fileName.c_str(), &attrib) ) - { + if (-1 == ::stat(fileName.c_str(), &attrib) ) { return false; } return S_ISREG(attrib.st_mode); #else - #error "Undefine platform" + #error "Undefined platform" #endif } - bool getFileSizeAndTimeGmt(const std::string &filePath, size_t *fileSize, time_t *fileTime) - { + bool getFileSizeAndTimeGmt( + const std::string &filePath, + size_t *fileSize, + time_t *fileTime + ) { #ifdef WIN32 #ifdef UNICODE std::wstring_convert> converter; @@ -200,27 +195,37 @@ namespace System const std::string &file_path = filePath; #endif - const ::HANDLE hFile = ::CreateFile(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); + const ::HANDLE hFile = ::CreateFile( + file_path.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + 0, + nullptr + ); - if (INVALID_HANDLE_VALUE == hFile) - { + if (INVALID_HANDLE_VALUE == hFile) { return false; } - if (false == ::GetFileSizeEx(hFile, reinterpret_cast<::PLARGE_INTEGER>(fileSize) ) ) - { + if (::GetFileSizeEx(hFile, reinterpret_cast<::PLARGE_INTEGER>(fileSize) ) == false) { ::CloseHandle(hFile); return false; } ::FILETIME ftWrite; - ::BOOL result = ::GetFileTime(hFile, nullptr, nullptr, &ftWrite); + ::BOOL result = ::GetFileTime( + hFile, + nullptr, + nullptr, + &ftWrite + ); ::CloseHandle(hFile); - if (false == result) - { + if (false == result) { return false; } @@ -228,7 +233,7 @@ namespace System ::FileTimeToSystemTime(&ftWrite, &stUtc); - struct ::tm tm_time { + std::tm tm_time { stUtc.wSecond, stUtc.wMinute, stUtc.wHour, @@ -237,31 +242,30 @@ namespace System stUtc.wYear - 1900, 0, 0, - -1 + -1 }; - *fileTime = ::mktime(&tm_time); + *fileTime = std::mktime(&tm_time); return true; #elif POSIX - struct ::stat attrib; + struct ::stat attrib {}; - if (-1 == ::stat(filePath.c_str(), &attrib) ) - { + if (-1 == ::stat(filePath.c_str(), &attrib) ) { return false; } - *fileSize = attrib.st_size; + *fileSize = static_cast(attrib.st_size); + + std::tm clock {}; - struct ::tm clock = {}; - ::gmtime_r(&(attrib.st_mtime), &clock); - *fileTime = ::mktime(&clock); + *fileTime = std::mktime(&clock); return true; #else - #error "Undefine platform" + #error "Undefined platform" #endif } @@ -279,12 +283,14 @@ namespace System const size_t pos = memory_name.rfind(file_ext); - if (pos == memory_name.length() - file_ext.length() ) - { - memory_name.erase(memory_name.begin() + pos, memory_name.end() ); + if (pos == memory_name.length() - file_ext.length() ) { + memory_name.erase( + pos, + memory_name.length() + ); } - ::TCHAR buf[MAX_PATH + 1] = {}; + ::TCHAR buf[MAX_PATH + 1] {}; ::GetFullPathName(memory_name.c_str(), MAX_PATH, buf, nullptr); #ifdef UNICODE @@ -293,28 +299,23 @@ namespace System memName = buf; #endif - for (size_t i = 1; i < memName.length(); ++i) - { - if ('/' == memName[i] || '\\' == memName[i]) - { + for (size_t i = 1; i < memName.length(); ++i) { + if ('/' == memName[i] || '\\' == memName[i]) { memName[i] = '-'; } } #elif POSIX - if ('/' != memName.front() ) - { + if (memName.front() != '/') { memName = '/' + memName; } - for (size_t i = 1; i < memName.length(); ++i) - { - if ('/' == memName[i] || '\\' == memName[i]) - { + for (size_t i = 1; i < memName.length(); ++i) { + if ('/' == memName[i] || '\\' == memName[i]) { memName[i] = '-'; } } #else - #error "Undefine platform" + #error "Undefined platform" #endif } -}; \ No newline at end of file +} diff --git a/src/System.h b/src/system/System.h similarity index 60% rename from src/System.h rename to src/system/System.h index 6d5e109..8b637d4 100644 --- a/src/System.h +++ b/src/system/System.h @@ -1,31 +1,35 @@ -#pragma once +#pragma once #ifdef WIN32 #include + #ifndef ssize_t + typedef long ssize_t; + #endif + ::TCHAR myWndClassName[]; - #ifdef SIGTERM - #undef SIGTERM - #define SIGTERM (WM_USER + 15) - #endif + #ifdef SIGTERM + #undef SIGTERM + #define SIGTERM (WM_USER + 15) + #endif - #ifdef SIGINT - #undef SIGINT - #define SIGINT (WM_USER + 2) - #endif + #ifdef SIGINT + #undef SIGINT + #define SIGINT (WM_USER + 2) + #endif - #ifndef SIGUSR1 - #define SIGUSR1 (WM_USER + 10) - #endif + #ifndef SIGUSR1 + #define SIGUSR1 (WM_USER + 10) + #endif - #ifndef SIGUSR2 - #define SIGUSR2 (WM_USER + 12) - #endif + #ifndef SIGUSR2 + #define SIGUSR2 (WM_USER + 12) + #endif #elif POSIX #include #else - #error "Undefine platform" + #error "Undefined platform" #endif #include @@ -39,7 +43,7 @@ namespace System #elif POSIX typedef int native_socket_type; #else - #error "Undefine platform" + #error "Undefined platform" #endif #ifdef WIN32 @@ -47,18 +51,18 @@ namespace System #elif POSIX typedef ::pid_t native_processid_type; #else - #error "Undefine platform" + #error "Undefined platform" #endif - native_processid_type getProcessId(); + native_processid_type getProcessId() noexcept; bool changeCurrentDirectory(const std::string &dir); - bool isProcessExists(const native_processid_type pid); + bool isProcessExists(const native_processid_type pid) noexcept; - bool sendSignal(const native_processid_type pid, const int signal); + bool sendSignal(const native_processid_type pid, const int signal) noexcept; - bool isDoneThread(const std::thread::native_handle_type handle); + bool isDoneThread(const std::thread::native_handle_type handle) noexcept; std::string getTempDir(); @@ -67,4 +71,4 @@ namespace System bool getFileSizeAndTimeGmt(const std::string &filePath, size_t *fileSize, time_t *fileTime); void filterSharedMemoryName(std::string &memName); -}; \ No newline at end of file +} diff --git a/src/transfer/AppRequest.h b/src/transfer/AppRequest.h new file mode 100644 index 0000000..52d894e --- /dev/null +++ b/src/transfer/AppRequest.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../socket/AdapterTls.h" +#include "FileIncoming.h" + +#include + +namespace Transfer +{ + struct request_data { + std::unordered_multimap incoming_headers; + std::unordered_multimap incoming_data; + std::unordered_multimap incoming_files; + }; + + struct app_request { + const System::native_socket_type socket; + const ::gnutls_session_t tls_session; + const void * const request_data; + }; +} diff --git a/src/transfer/AppResponse.h b/src/transfer/AppResponse.h new file mode 100644 index 0000000..e059e55 --- /dev/null +++ b/src/transfer/AppResponse.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace Transfer +{ + struct app_response { + void *response_data; + size_t data_size; + }; +} diff --git a/src/transfer/FileIncoming.cpp b/src/transfer/FileIncoming.cpp new file mode 100644 index 0000000..07fb877 --- /dev/null +++ b/src/transfer/FileIncoming.cpp @@ -0,0 +1,121 @@ + +#include "FileIncoming.h" +#include "../utils/Utils.h" + +#include + +namespace Transfer +{ + FileIncoming::FileIncoming( + std::string &&fileTmpName, + std::string &&fileName, + std::string &&fileType, + const size_t fileSize + ) noexcept + : file_tmp_name(std::move(fileTmpName) ), + file_name(std::move(fileName) ), + file_type(std::move(fileType) ), + file_size(fileSize) + { + + } + + FileIncoming::FileIncoming(const FileIncoming &obj) + : file_tmp_name(obj.file_tmp_name), + file_name(obj.file_name), + file_type(obj.file_type), + file_size(obj.file_size) + { + + } + + FileIncoming::FileIncoming(FileIncoming &&obj) noexcept + : file_tmp_name(std::move(obj.file_tmp_name) ), + file_name(std::move(obj.file_name) ), + file_type(std::move(obj.file_type) ), + file_size(obj.file_size) + { + obj.file_size = 0; + } + + const std::string &FileIncoming::getTmpName() const noexcept { + return this->file_tmp_name; + } + + const std::string &FileIncoming::getName() const noexcept { + return this->file_name; + } + + const std::string &FileIncoming::getType() const noexcept { + return this->file_type; + } + + size_t FileIncoming::getSize() const noexcept { + return this->file_size; + } + + bool FileIncoming::isExists() const noexcept { + std::ifstream file(this->file_tmp_name, std::ifstream::binary); + const bool is_exists = file.good(); + file.close(); + return is_exists; + } +} + +namespace Utils +{ + void packFilesIncoming( + std::vector &buf, + const std::unordered_multimap &map + ) { + packNumber(buf, map.size() ); + + for (auto it = map.cbegin(); map.cend() != it; ++it) { + packString(buf, it->first); + + const Transfer::FileIncoming &file = it->second; + + packString(buf, file.getTmpName() ); + packString(buf, file.getName() ); + packString(buf, file.getType() ); + packNumber(buf, file.getSize() ); + } + } + + const uint8_t *unpackFilesIncoming( + std::unordered_multimap &map, + const uint8_t *src + ) { + size_t count; + src = unpackNumber(&count, src); + + for (size_t i = 0; i < count; ++i) { + std::string key; + src = unpackString(key, src); + + std::string file_tmp_name; + src = unpackString(file_tmp_name, src); + + std::string file_name; + src = unpackString(file_name, src); + + std::string file_type; + src = unpackString(file_type, src); + + size_t file_size; + src = unpackNumber(&file_size, src); + + map.emplace( + std::move(key), + Transfer::FileIncoming( + std::move(file_tmp_name), + std::move(file_name), + std::move(file_type), + file_size + ) + ); + } + + return src; + } +} diff --git a/src/transfer/FileIncoming.h b/src/transfer/FileIncoming.h new file mode 100644 index 0000000..448c8c8 --- /dev/null +++ b/src/transfer/FileIncoming.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include + +namespace Transfer +{ + class FileIncoming + { + protected: + std::string file_tmp_name; + std::string file_name; + std::string file_type; + size_t file_size; + + public: + FileIncoming() = default; + + FileIncoming( + std::string &&fileTmpName, + std::string &&fileName, + std::string &&fileType, + const size_t fileSize + ) noexcept; + + FileIncoming(const FileIncoming &obj); + FileIncoming(FileIncoming &&obj) noexcept; + + ~FileIncoming() noexcept = default; + + FileIncoming &operator =(const FileIncoming &) = default; + + const std::string &getTmpName() const noexcept; + const std::string &getName() const noexcept; + const std::string &getType() const noexcept; + size_t getSize() const noexcept; + + bool isExists() const noexcept; + }; +} + +namespace Utils +{ + void packFilesIncoming( + std::vector &buf, + const std::unordered_multimap &map + ); + + const uint8_t *unpackFilesIncoming( + std::unordered_multimap &map, + const uint8_t *src + ); +} diff --git a/src/transfer/HttpStatusCode.h b/src/transfer/HttpStatusCode.h new file mode 100644 index 0000000..abb0c85 --- /dev/null +++ b/src/transfer/HttpStatusCode.h @@ -0,0 +1,51 @@ +#pragma once + +namespace Http +{ + enum class StatusCode { + EMPTY = 0, + + CONTINUE = 100, + SWITCHING_PROTOCOLS = 101, + PROCESSING = 102, + + OK = 200, + CREATED = 201, + ACCEPTED = 202, + NON_AUTHORITATIVE_INFORMATION = 203, + NO_CONTENT = 204, + RESET_CONTENT = 205, + PARTIAL_CONTENT = 206, + MULTI_STATUS = 207, + + MULTIPLE_CHOISES = 300, + MOVED_PERMANENTLY = 301, + MOVED_TEMPORARILY = 302, + FOUND = 302, + SEE_OTHER = 303, + NOT_MODIFIED = 304, + USE_PROXY = 305, + TEMPORARY_REDIRECT = 307, + + BAD_REQUEST = 400, + UNAUTHORIZED = 401, + PAYMENT_REQUIRED = 402, + FORBIDDEN = 403, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + NOT_ACCEPTABLE = 406, + PROXY_AUTHENTICATION_REQUIRED = 407, + REQUEST_TIMEOUT = 408, + CONFLICT = 409, + GONE = 410, + REQUEST_ENTITY_TOO_LARGE = 413, + REQUESTED_RANGE_NOT_SATISFIABLE = 416, + + INTERNAL_SERVER_ERROR = 500, + NOT_IMPLEMENTED = 501, + BAD_GATEWAY = 502, + SERVICE_UNAVAILABLE = 503, + GATEWAY_TIMEOUT = 504, + HTTP_VERSION_NOT_SUPPORTED = 505, + }; +} diff --git a/src/transfer/ProtocolVariant.h b/src/transfer/ProtocolVariant.h new file mode 100644 index 0000000..093a02a --- /dev/null +++ b/src/transfer/ProtocolVariant.h @@ -0,0 +1,10 @@ +#pragma once + +namespace Transfer +{ + enum class ProtocolVariant { + UNKNOWN = 0, + HTTP_1, + HTTP_2 + }; +} diff --git a/src/transfer/http2/HPack.cpp b/src/transfer/http2/HPack.cpp new file mode 100644 index 0000000..b8d0128 --- /dev/null +++ b/src/transfer/http2/HPack.cpp @@ -0,0 +1,6062 @@ +/** + * This code getting from https://github.com/tatsuhiro-t/go-http2-hpack + */ + +#include "HPack.h" + +#include +#include +#include +#include + +namespace HPack +{ + struct HuffmanSymbol { + int nbits; + uint32_t code; + }; + + static const HuffmanSymbol huffmanSymbolTable[] { + {13, 0x1ff8}, + {23, 0x7fffd8}, + {28, 0xfffffe2}, + {28, 0xfffffe3}, + {28, 0xfffffe4}, + {28, 0xfffffe5}, + {28, 0xfffffe6}, + {28, 0xfffffe7}, + {28, 0xfffffe8}, + {24, 0xffffea}, + {30, 0x3ffffffc}, + {28, 0xfffffe9}, + {28, 0xfffffea}, + {30, 0x3ffffffd}, + {28, 0xfffffeb}, + {28, 0xfffffec}, + {28, 0xfffffed}, + {28, 0xfffffee}, + {28, 0xfffffef}, + {28, 0xffffff0}, + {28, 0xffffff1}, + {28, 0xffffff2}, + {30, 0x3ffffffe}, + {28, 0xffffff3}, + {28, 0xffffff4}, + {28, 0xffffff5}, + {28, 0xffffff6}, + {28, 0xffffff7}, + {28, 0xffffff8}, + {28, 0xffffff9}, + {28, 0xffffffa}, + {28, 0xffffffb}, + {6, 0x14}, + {10, 0x3f8}, + {10, 0x3f9}, + {12, 0xffa}, + {13, 0x1ff9}, + {6, 0x15}, + {8, 0xf8}, + {11, 0x7fa}, + {10, 0x3fa}, + {10, 0x3fb}, + {8, 0xf9}, + {11, 0x7fb}, + {8, 0xfa}, + {6, 0x16}, + {6, 0x17}, + {6, 0x18}, + {5, 0x0}, + {5, 0x1}, + {5, 0x2}, + {6, 0x19}, + {6, 0x1a}, + {6, 0x1b}, + {6, 0x1c}, + {6, 0x1d}, + {6, 0x1e}, + {6, 0x1f}, + {7, 0x5c}, + {8, 0xfb}, + {15, 0x7ffc}, + {6, 0x20}, + {12, 0xffb}, + {10, 0x3fc}, + {13, 0x1ffa}, + {6, 0x21}, + {7, 0x5d}, + {7, 0x5e}, + {7, 0x5f}, + {7, 0x60}, + {7, 0x61}, + {7, 0x62}, + {7, 0x63}, + {7, 0x64}, + {7, 0x65}, + {7, 0x66}, + {7, 0x67}, + {7, 0x68}, + {7, 0x69}, + {7, 0x6a}, + {7, 0x6b}, + {7, 0x6c}, + {7, 0x6d}, + {7, 0x6e}, + {7, 0x6f}, + {7, 0x70}, + {7, 0x71}, + {7, 0x72}, + {8, 0xfc}, + {7, 0x73}, + {8, 0xfd}, + {13, 0x1ffb}, + {19, 0x7fff0}, + {13, 0x1ffc}, + {14, 0x3ffc}, + {6, 0x22}, + {15, 0x7ffd}, + {5, 0x3}, + {6, 0x23}, + {5, 0x4}, + {6, 0x24}, + {5, 0x5}, + {6, 0x25}, + {6, 0x26}, + {6, 0x27}, + {5, 0x6}, + {7, 0x74}, + {7, 0x75}, + {6, 0x28}, + {6, 0x29}, + {6, 0x2a}, + {5, 0x7}, + {6, 0x2b}, + {7, 0x76}, + {6, 0x2c}, + {5, 0x8}, + {5, 0x9}, + {6, 0x2d}, + {7, 0x77}, + {7, 0x78}, + {7, 0x79}, + {7, 0x7a}, + {7, 0x7b}, + {15, 0x7ffe}, + {11, 0x7fc}, + {14, 0x3ffd}, + {13, 0x1ffd}, + {28, 0xffffffc}, + {20, 0xfffe6}, + {22, 0x3fffd2}, + {20, 0xfffe7}, + {20, 0xfffe8}, + {22, 0x3fffd3}, + {22, 0x3fffd4}, + {22, 0x3fffd5}, + {23, 0x7fffd9}, + {22, 0x3fffd6}, + {23, 0x7fffda}, + {23, 0x7fffdb}, + {23, 0x7fffdc}, + {23, 0x7fffdd}, + {23, 0x7fffde}, + {24, 0xffffeb}, + {23, 0x7fffdf}, + {24, 0xffffec}, + {24, 0xffffed}, + {22, 0x3fffd7}, + {23, 0x7fffe0}, + {24, 0xffffee}, + {23, 0x7fffe1}, + {23, 0x7fffe2}, + {23, 0x7fffe3}, + {23, 0x7fffe4}, + {21, 0x1fffdc}, + {22, 0x3fffd8}, + {23, 0x7fffe5}, + {22, 0x3fffd9}, + {23, 0x7fffe6}, + {23, 0x7fffe7}, + {24, 0xffffef}, + {22, 0x3fffda}, + {21, 0x1fffdd}, + {20, 0xfffe9}, + {22, 0x3fffdb}, + {22, 0x3fffdc}, + {23, 0x7fffe8}, + {23, 0x7fffe9}, + {21, 0x1fffde}, + {23, 0x7fffea}, + {22, 0x3fffdd}, + {22, 0x3fffde}, + {24, 0xfffff0}, + {21, 0x1fffdf}, + {22, 0x3fffdf}, + {23, 0x7fffeb}, + {23, 0x7fffec}, + {21, 0x1fffe0}, + {21, 0x1fffe1}, + {22, 0x3fffe0}, + {21, 0x1fffe2}, + {23, 0x7fffed}, + {22, 0x3fffe1}, + {23, 0x7fffee}, + {23, 0x7fffef}, + {20, 0xfffea}, + {22, 0x3fffe2}, + {22, 0x3fffe3}, + {22, 0x3fffe4}, + {23, 0x7ffff0}, + {22, 0x3fffe5}, + {22, 0x3fffe6}, + {23, 0x7ffff1}, + {26, 0x3ffffe0}, + {26, 0x3ffffe1}, + {20, 0xfffeb}, + {19, 0x7fff1}, + {22, 0x3fffe7}, + {23, 0x7ffff2}, + {22, 0x3fffe8}, + {25, 0x1ffffec}, + {26, 0x3ffffe2}, + {26, 0x3ffffe3}, + {26, 0x3ffffe4}, + {27, 0x7ffffde}, + {27, 0x7ffffdf}, + {26, 0x3ffffe5}, + {24, 0xfffff1}, + {25, 0x1ffffed}, + {19, 0x7fff2}, + {21, 0x1fffe3}, + {26, 0x3ffffe6}, + {27, 0x7ffffe0}, + {27, 0x7ffffe1}, + {26, 0x3ffffe7}, + {27, 0x7ffffe2}, + {24, 0xfffff2}, + {21, 0x1fffe4}, + {21, 0x1fffe5}, + {26, 0x3ffffe8}, + {26, 0x3ffffe9}, + {28, 0xffffffd}, + {27, 0x7ffffe3}, + {27, 0x7ffffe4}, + {27, 0x7ffffe5}, + {20, 0xfffec}, + {24, 0xfffff3}, + {20, 0xfffed}, + {21, 0x1fffe6}, + {22, 0x3fffe9}, + {21, 0x1fffe7}, + {21, 0x1fffe8}, + {23, 0x7ffff3}, + {22, 0x3fffea}, + {22, 0x3fffeb}, + {25, 0x1ffffee}, + {25, 0x1ffffef}, + {24, 0xfffff4}, + {24, 0xfffff5}, + {26, 0x3ffffea}, + {23, 0x7ffff4}, + {26, 0x3ffffeb}, + {27, 0x7ffffe6}, + {26, 0x3ffffec}, + {26, 0x3ffffed}, + {27, 0x7ffffe7}, + {27, 0x7ffffe8}, + {27, 0x7ffffe9}, + {27, 0x7ffffea}, + {27, 0x7ffffeb}, + {28, 0xffffffe}, + {27, 0x7ffffec}, + {27, 0x7ffffed}, + {27, 0x7ffffee}, + {27, 0x7ffffef}, + {27, 0x7fffff0}, + {26, 0x3ffffee}, + {30, 0x3fffffff}, + }; + + struct HuffmanDecodeNode { + uint8_t state; + uint8_t flags; + uint8_t symbol; + }; + + static const HuffmanDecodeNode huffmanDecodeTable[][16] { + { + {4, 0x00, 0}, + {5, 0x00, 0}, + {7, 0x00, 0}, + {8, 0x00, 0}, + {11, 0x00, 0}, + {12, 0x00, 0}, + {16, 0x00, 0}, + {19, 0x00, 0}, + {25, 0x00, 0}, + {28, 0x00, 0}, + {32, 0x00, 0}, + {35, 0x00, 0}, + {42, 0x00, 0}, + {49, 0x00, 0}, + {57, 0x00, 0}, + {64, 0x01, 0}, + }, + + { + {0, 0x03, 48}, + {0, 0x03, 49}, + {0, 0x03, 50}, + {0, 0x03, 97}, + {0, 0x03, 99}, + {0, 0x03, 101}, + {0, 0x03, 105}, + {0, 0x03, 111}, + {0, 0x03, 115}, + {0, 0x03, 116}, + {13, 0x00, 0}, + {14, 0x00, 0}, + {17, 0x00, 0}, + {18, 0x00, 0}, + {20, 0x00, 0}, + {21, 0x00, 0}, + }, + + { + {1, 0x02, 48}, + {22, 0x03, 48}, + {1, 0x02, 49}, + {22, 0x03, 49}, + {1, 0x02, 50}, + {22, 0x03, 50}, + {1, 0x02, 97}, + {22, 0x03, 97}, + {1, 0x02, 99}, + {22, 0x03, 99}, + {1, 0x02, 101}, + {22, 0x03, 101}, + {1, 0x02, 105}, + {22, 0x03, 105}, + {1, 0x02, 111}, + {22, 0x03, 111}, + }, + + { + {2, 0x02, 48}, + {9, 0x02, 48}, + {23, 0x02, 48}, + {40, 0x03, 48}, + {2, 0x02, 49}, + {9, 0x02, 49}, + {23, 0x02, 49}, + {40, 0x03, 49}, + {2, 0x02, 50}, + {9, 0x02, 50}, + {23, 0x02, 50}, + {40, 0x03, 50}, + {2, 0x02, 97}, + {9, 0x02, 97}, + {23, 0x02, 97}, + {40, 0x03, 97}, + }, + + { + {3, 0x02, 48}, + {6, 0x02, 48}, + {10, 0x02, 48}, + {15, 0x02, 48}, + {24, 0x02, 48}, + {31, 0x02, 48}, + {41, 0x02, 48}, + {56, 0x03, 48}, + {3, 0x02, 49}, + {6, 0x02, 49}, + {10, 0x02, 49}, + {15, 0x02, 49}, + {24, 0x02, 49}, + {31, 0x02, 49}, + {41, 0x02, 49}, + {56, 0x03, 49}, + }, + + { + {3, 0x02, 50}, + {6, 0x02, 50}, + {10, 0x02, 50}, + {15, 0x02, 50}, + {24, 0x02, 50}, + {31, 0x02, 50}, + {41, 0x02, 50}, + {56, 0x03, 50}, + {3, 0x02, 97}, + {6, 0x02, 97}, + {10, 0x02, 97}, + {15, 0x02, 97}, + {24, 0x02, 97}, + {31, 0x02, 97}, + {41, 0x02, 97}, + {56, 0x03, 97}, + }, + + { + {2, 0x02, 99}, + {9, 0x02, 99}, + {23, 0x02, 99}, + {40, 0x03, 99}, + {2, 0x02, 101}, + {9, 0x02, 101}, + {23, 0x02, 101}, + {40, 0x03, 101}, + {2, 0x02, 105}, + {9, 0x02, 105}, + {23, 0x02, 105}, + {40, 0x03, 105}, + {2, 0x02, 111}, + {9, 0x02, 111}, + {23, 0x02, 111}, + {40, 0x03, 111}, + }, + + { + {3, 0x02, 99}, + {6, 0x02, 99}, + {10, 0x02, 99}, + {15, 0x02, 99}, + {24, 0x02, 99}, + {31, 0x02, 99}, + {41, 0x02, 99}, + {56, 0x03, 99}, + {3, 0x02, 101}, + {6, 0x02, 101}, + {10, 0x02, 101}, + {15, 0x02, 101}, + {24, 0x02, 101}, + {31, 0x02, 101}, + {41, 0x02, 101}, + {56, 0x03, 101}, + }, + + { + {3, 0x02, 105}, + {6, 0x02, 105}, + {10, 0x02, 105}, + {15, 0x02, 105}, + {24, 0x02, 105}, + {31, 0x02, 105}, + {41, 0x02, 105}, + {56, 0x03, 105}, + {3, 0x02, 111}, + {6, 0x02, 111}, + {10, 0x02, 111}, + {15, 0x02, 111}, + {24, 0x02, 111}, + {31, 0x02, 111}, + {41, 0x02, 111}, + {56, 0x03, 111}, + }, + + { + {1, 0x02, 115}, + {22, 0x03, 115}, + {1, 0x02, 116}, + {22, 0x03, 116}, + {0, 0x03, 32}, + {0, 0x03, 37}, + {0, 0x03, 45}, + {0, 0x03, 46}, + {0, 0x03, 47}, + {0, 0x03, 51}, + {0, 0x03, 52}, + {0, 0x03, 53}, + {0, 0x03, 54}, + {0, 0x03, 55}, + {0, 0x03, 56}, + {0, 0x03, 57}, + }, + + { + {2, 0x02, 115}, + {9, 0x02, 115}, + {23, 0x02, 115}, + {40, 0x03, 115}, + {2, 0x02, 116}, + {9, 0x02, 116}, + {23, 0x02, 116}, + {40, 0x03, 116}, + {1, 0x02, 32}, + {22, 0x03, 32}, + {1, 0x02, 37}, + {22, 0x03, 37}, + {1, 0x02, 45}, + {22, 0x03, 45}, + {1, 0x02, 46}, + {22, 0x03, 46}, + }, + + { + {3, 0x02, 115}, + {6, 0x02, 115}, + {10, 0x02, 115}, + {15, 0x02, 115}, + {24, 0x02, 115}, + {31, 0x02, 115}, + {41, 0x02, 115}, + {56, 0x03, 115}, + {3, 0x02, 116}, + {6, 0x02, 116}, + {10, 0x02, 116}, + {15, 0x02, 116}, + {24, 0x02, 116}, + {31, 0x02, 116}, + {41, 0x02, 116}, + {56, 0x03, 116}, + }, + + { + {2, 0x02, 32}, + {9, 0x02, 32}, + {23, 0x02, 32}, + {40, 0x03, 32}, + {2, 0x02, 37}, + {9, 0x02, 37}, + {23, 0x02, 37}, + {40, 0x03, 37}, + {2, 0x02, 45}, + {9, 0x02, 45}, + {23, 0x02, 45}, + {40, 0x03, 45}, + {2, 0x02, 46}, + {9, 0x02, 46}, + {23, 0x02, 46}, + {40, 0x03, 46}, + }, + + { + {3, 0x02, 32}, + {6, 0x02, 32}, + {10, 0x02, 32}, + {15, 0x02, 32}, + {24, 0x02, 32}, + {31, 0x02, 32}, + {41, 0x02, 32}, + {56, 0x03, 32}, + {3, 0x02, 37}, + {6, 0x02, 37}, + {10, 0x02, 37}, + {15, 0x02, 37}, + {24, 0x02, 37}, + {31, 0x02, 37}, + {41, 0x02, 37}, + {56, 0x03, 37}, + }, + + { + {3, 0x02, 45}, + {6, 0x02, 45}, + {10, 0x02, 45}, + {15, 0x02, 45}, + {24, 0x02, 45}, + {31, 0x02, 45}, + {41, 0x02, 45}, + {56, 0x03, 45}, + {3, 0x02, 46}, + {6, 0x02, 46}, + {10, 0x02, 46}, + {15, 0x02, 46}, + {24, 0x02, 46}, + {31, 0x02, 46}, + {41, 0x02, 46}, + {56, 0x03, 46}, + }, + + { + {1, 0x02, 47}, + {22, 0x03, 47}, + {1, 0x02, 51}, + {22, 0x03, 51}, + {1, 0x02, 52}, + {22, 0x03, 52}, + {1, 0x02, 53}, + {22, 0x03, 53}, + {1, 0x02, 54}, + {22, 0x03, 54}, + {1, 0x02, 55}, + {22, 0x03, 55}, + {1, 0x02, 56}, + {22, 0x03, 56}, + {1, 0x02, 57}, + {22, 0x03, 57}, + }, + + { + {2, 0x02, 47}, + {9, 0x02, 47}, + {23, 0x02, 47}, + {40, 0x03, 47}, + {2, 0x02, 51}, + {9, 0x02, 51}, + {23, 0x02, 51}, + {40, 0x03, 51}, + {2, 0x02, 52}, + {9, 0x02, 52}, + {23, 0x02, 52}, + {40, 0x03, 52}, + {2, 0x02, 53}, + {9, 0x02, 53}, + {23, 0x02, 53}, + {40, 0x03, 53}, + }, + + { + {3, 0x02, 47}, + {6, 0x02, 47}, + {10, 0x02, 47}, + {15, 0x02, 47}, + {24, 0x02, 47}, + {31, 0x02, 47}, + {41, 0x02, 47}, + {56, 0x03, 47}, + {3, 0x02, 51}, + {6, 0x02, 51}, + {10, 0x02, 51}, + {15, 0x02, 51}, + {24, 0x02, 51}, + {31, 0x02, 51}, + {41, 0x02, 51}, + {56, 0x03, 51}, + }, + + { + {3, 0x02, 52}, + {6, 0x02, 52}, + {10, 0x02, 52}, + {15, 0x02, 52}, + {24, 0x02, 52}, + {31, 0x02, 52}, + {41, 0x02, 52}, + {56, 0x03, 52}, + {3, 0x02, 53}, + {6, 0x02, 53}, + {10, 0x02, 53}, + {15, 0x02, 53}, + {24, 0x02, 53}, + {31, 0x02, 53}, + {41, 0x02, 53}, + {56, 0x03, 53}, + }, + + { + {2, 0x02, 54}, + {9, 0x02, 54}, + {23, 0x02, 54}, + {40, 0x03, 54}, + {2, 0x02, 55}, + {9, 0x02, 55}, + {23, 0x02, 55}, + {40, 0x03, 55}, + {2, 0x02, 56}, + {9, 0x02, 56}, + {23, 0x02, 56}, + {40, 0x03, 56}, + {2, 0x02, 57}, + {9, 0x02, 57}, + {23, 0x02, 57}, + {40, 0x03, 57}, + }, + + { + {3, 0x02, 54}, + {6, 0x02, 54}, + {10, 0x02, 54}, + {15, 0x02, 54}, + {24, 0x02, 54}, + {31, 0x02, 54}, + {41, 0x02, 54}, + {56, 0x03, 54}, + {3, 0x02, 55}, + {6, 0x02, 55}, + {10, 0x02, 55}, + {15, 0x02, 55}, + {24, 0x02, 55}, + {31, 0x02, 55}, + {41, 0x02, 55}, + {56, 0x03, 55}, + }, + + { + {3, 0x02, 56}, + {6, 0x02, 56}, + {10, 0x02, 56}, + {15, 0x02, 56}, + {24, 0x02, 56}, + {31, 0x02, 56}, + {41, 0x02, 56}, + {56, 0x03, 56}, + {3, 0x02, 57}, + {6, 0x02, 57}, + {10, 0x02, 57}, + {15, 0x02, 57}, + {24, 0x02, 57}, + {31, 0x02, 57}, + {41, 0x02, 57}, + {56, 0x03, 57}, + }, + + { + {26, 0x00, 0}, + {27, 0x00, 0}, + {29, 0x00, 0}, + {30, 0x00, 0}, + {33, 0x00, 0}, + {34, 0x00, 0}, + {36, 0x00, 0}, + {37, 0x00, 0}, + {43, 0x00, 0}, + {46, 0x00, 0}, + {50, 0x00, 0}, + {53, 0x00, 0}, + {58, 0x00, 0}, + {61, 0x00, 0}, + {65, 0x00, 0}, + {68, 0x01, 0}, + }, + + { + {0, 0x03, 61}, + {0, 0x03, 65}, + {0, 0x03, 95}, + {0, 0x03, 98}, + {0, 0x03, 100}, + {0, 0x03, 102}, + {0, 0x03, 103}, + {0, 0x03, 104}, + {0, 0x03, 108}, + {0, 0x03, 109}, + {0, 0x03, 110}, + {0, 0x03, 112}, + {0, 0x03, 114}, + {0, 0x03, 117}, + {38, 0x00, 0}, + {39, 0x00, 0}, + }, + + { + {1, 0x02, 61}, + {22, 0x03, 61}, + {1, 0x02, 65}, + {22, 0x03, 65}, + {1, 0x02, 95}, + {22, 0x03, 95}, + {1, 0x02, 98}, + {22, 0x03, 98}, + {1, 0x02, 100}, + {22, 0x03, 100}, + {1, 0x02, 102}, + {22, 0x03, 102}, + {1, 0x02, 103}, + {22, 0x03, 103}, + {1, 0x02, 104}, + {22, 0x03, 104}, + }, + + { + {2, 0x02, 61}, + {9, 0x02, 61}, + {23, 0x02, 61}, + {40, 0x03, 61}, + {2, 0x02, 65}, + {9, 0x02, 65}, + {23, 0x02, 65}, + {40, 0x03, 65}, + {2, 0x02, 95}, + {9, 0x02, 95}, + {23, 0x02, 95}, + {40, 0x03, 95}, + {2, 0x02, 98}, + {9, 0x02, 98}, + {23, 0x02, 98}, + {40, 0x03, 98}, + }, + + { + {3, 0x02, 61}, + {6, 0x02, 61}, + {10, 0x02, 61}, + {15, 0x02, 61}, + {24, 0x02, 61}, + {31, 0x02, 61}, + {41, 0x02, 61}, + {56, 0x03, 61}, + {3, 0x02, 65}, + {6, 0x02, 65}, + {10, 0x02, 65}, + {15, 0x02, 65}, + {24, 0x02, 65}, + {31, 0x02, 65}, + {41, 0x02, 65}, + {56, 0x03, 65}, + }, + + { + {3, 0x02, 95}, + {6, 0x02, 95}, + {10, 0x02, 95}, + {15, 0x02, 95}, + {24, 0x02, 95}, + {31, 0x02, 95}, + {41, 0x02, 95}, + {56, 0x03, 95}, + {3, 0x02, 98}, + {6, 0x02, 98}, + {10, 0x02, 98}, + {15, 0x02, 98}, + {24, 0x02, 98}, + {31, 0x02, 98}, + {41, 0x02, 98}, + {56, 0x03, 98}, + }, + + { + {2, 0x02, 100}, + {9, 0x02, 100}, + {23, 0x02, 100}, + {40, 0x03, 100}, + {2, 0x02, 102}, + {9, 0x02, 102}, + {23, 0x02, 102}, + {40, 0x03, 102}, + {2, 0x02, 103}, + {9, 0x02, 103}, + {23, 0x02, 103}, + {40, 0x03, 103}, + {2, 0x02, 104}, + {9, 0x02, 104}, + {23, 0x02, 104}, + {40, 0x03, 104}, + }, + + { + {3, 0x02, 100}, + {6, 0x02, 100}, + {10, 0x02, 100}, + {15, 0x02, 100}, + {24, 0x02, 100}, + {31, 0x02, 100}, + {41, 0x02, 100}, + {56, 0x03, 100}, + {3, 0x02, 102}, + {6, 0x02, 102}, + {10, 0x02, 102}, + {15, 0x02, 102}, + {24, 0x02, 102}, + {31, 0x02, 102}, + {41, 0x02, 102}, + {56, 0x03, 102}, + }, + + { + {3, 0x02, 103}, + {6, 0x02, 103}, + {10, 0x02, 103}, + {15, 0x02, 103}, + {24, 0x02, 103}, + {31, 0x02, 103}, + {41, 0x02, 103}, + {56, 0x03, 103}, + {3, 0x02, 104}, + {6, 0x02, 104}, + {10, 0x02, 104}, + {15, 0x02, 104}, + {24, 0x02, 104}, + {31, 0x02, 104}, + {41, 0x02, 104}, + {56, 0x03, 104}, + }, + + { + {1, 0x02, 108}, + {22, 0x03, 108}, + {1, 0x02, 109}, + {22, 0x03, 109}, + {1, 0x02, 110}, + {22, 0x03, 110}, + {1, 0x02, 112}, + {22, 0x03, 112}, + {1, 0x02, 114}, + {22, 0x03, 114}, + {1, 0x02, 117}, + {22, 0x03, 117}, + {0, 0x03, 58}, + {0, 0x03, 66}, + {0, 0x03, 67}, + {0, 0x03, 68}, + }, + + { + {2, 0x02, 108}, + {9, 0x02, 108}, + {23, 0x02, 108}, + {40, 0x03, 108}, + {2, 0x02, 109}, + {9, 0x02, 109}, + {23, 0x02, 109}, + {40, 0x03, 109}, + {2, 0x02, 110}, + {9, 0x02, 110}, + {23, 0x02, 110}, + {40, 0x03, 110}, + {2, 0x02, 112}, + {9, 0x02, 112}, + {23, 0x02, 112}, + {40, 0x03, 112}, + }, + + { + {3, 0x02, 108}, + {6, 0x02, 108}, + {10, 0x02, 108}, + {15, 0x02, 108}, + {24, 0x02, 108}, + {31, 0x02, 108}, + {41, 0x02, 108}, + {56, 0x03, 108}, + {3, 0x02, 109}, + {6, 0x02, 109}, + {10, 0x02, 109}, + {15, 0x02, 109}, + {24, 0x02, 109}, + {31, 0x02, 109}, + {41, 0x02, 109}, + {56, 0x03, 109}, + }, + + { + {3, 0x02, 110}, + {6, 0x02, 110}, + {10, 0x02, 110}, + {15, 0x02, 110}, + {24, 0x02, 110}, + {31, 0x02, 110}, + {41, 0x02, 110}, + {56, 0x03, 110}, + {3, 0x02, 112}, + {6, 0x02, 112}, + {10, 0x02, 112}, + {15, 0x02, 112}, + {24, 0x02, 112}, + {31, 0x02, 112}, + {41, 0x02, 112}, + {56, 0x03, 112}, + }, + + { + {2, 0x02, 114}, + {9, 0x02, 114}, + {23, 0x02, 114}, + {40, 0x03, 114}, + {2, 0x02, 117}, + {9, 0x02, 117}, + {23, 0x02, 117}, + {40, 0x03, 117}, + {1, 0x02, 58}, + {22, 0x03, 58}, + {1, 0x02, 66}, + {22, 0x03, 66}, + {1, 0x02, 67}, + {22, 0x03, 67}, + {1, 0x02, 68}, + {22, 0x03, 68}, + }, + + { + {3, 0x02, 114}, + {6, 0x02, 114}, + {10, 0x02, 114}, + {15, 0x02, 114}, + {24, 0x02, 114}, + {31, 0x02, 114}, + {41, 0x02, 114}, + {56, 0x03, 114}, + {3, 0x02, 117}, + {6, 0x02, 117}, + {10, 0x02, 117}, + {15, 0x02, 117}, + {24, 0x02, 117}, + {31, 0x02, 117}, + {41, 0x02, 117}, + {56, 0x03, 117}, + }, + + { + {2, 0x02, 58}, + {9, 0x02, 58}, + {23, 0x02, 58}, + {40, 0x03, 58}, + {2, 0x02, 66}, + {9, 0x02, 66}, + {23, 0x02, 66}, + {40, 0x03, 66}, + {2, 0x02, 67}, + {9, 0x02, 67}, + {23, 0x02, 67}, + {40, 0x03, 67}, + {2, 0x02, 68}, + {9, 0x02, 68}, + {23, 0x02, 68}, + {40, 0x03, 68}, + }, + + { + {3, 0x02, 58}, + {6, 0x02, 58}, + {10, 0x02, 58}, + {15, 0x02, 58}, + {24, 0x02, 58}, + {31, 0x02, 58}, + {41, 0x02, 58}, + {56, 0x03, 58}, + {3, 0x02, 66}, + {6, 0x02, 66}, + {10, 0x02, 66}, + {15, 0x02, 66}, + {24, 0x02, 66}, + {31, 0x02, 66}, + {41, 0x02, 66}, + {56, 0x03, 66}, + }, + + { + {3, 0x02, 67}, + {6, 0x02, 67}, + {10, 0x02, 67}, + {15, 0x02, 67}, + {24, 0x02, 67}, + {31, 0x02, 67}, + {41, 0x02, 67}, + {56, 0x03, 67}, + {3, 0x02, 68}, + {6, 0x02, 68}, + {10, 0x02, 68}, + {15, 0x02, 68}, + {24, 0x02, 68}, + {31, 0x02, 68}, + {41, 0x02, 68}, + {56, 0x03, 68}, + }, + + { + {44, 0x00, 0}, + {45, 0x00, 0}, + {47, 0x00, 0}, + {48, 0x00, 0}, + {51, 0x00, 0}, + {52, 0x00, 0}, + {54, 0x00, 0}, + {55, 0x00, 0}, + {59, 0x00, 0}, + {60, 0x00, 0}, + {62, 0x00, 0}, + {63, 0x00, 0}, + {66, 0x00, 0}, + {67, 0x00, 0}, + {69, 0x00, 0}, + {72, 0x01, 0}, + }, + + { + {0, 0x03, 69}, + {0, 0x03, 70}, + {0, 0x03, 71}, + {0, 0x03, 72}, + {0, 0x03, 73}, + {0, 0x03, 74}, + {0, 0x03, 75}, + {0, 0x03, 76}, + {0, 0x03, 77}, + {0, 0x03, 78}, + {0, 0x03, 79}, + {0, 0x03, 80}, + {0, 0x03, 81}, + {0, 0x03, 82}, + {0, 0x03, 83}, + {0, 0x03, 84}, + }, + + { + {1, 0x02, 69}, + {22, 0x03, 69}, + {1, 0x02, 70}, + {22, 0x03, 70}, + {1, 0x02, 71}, + {22, 0x03, 71}, + {1, 0x02, 72}, + {22, 0x03, 72}, + {1, 0x02, 73}, + {22, 0x03, 73}, + {1, 0x02, 74}, + {22, 0x03, 74}, + {1, 0x02, 75}, + {22, 0x03, 75}, + {1, 0x02, 76}, + {22, 0x03, 76}, + }, + + { + {2, 0x02, 69}, + {9, 0x02, 69}, + {23, 0x02, 69}, + {40, 0x03, 69}, + {2, 0x02, 70}, + {9, 0x02, 70}, + {23, 0x02, 70}, + {40, 0x03, 70}, + {2, 0x02, 71}, + {9, 0x02, 71}, + {23, 0x02, 71}, + {40, 0x03, 71}, + {2, 0x02, 72}, + {9, 0x02, 72}, + {23, 0x02, 72}, + {40, 0x03, 72}, + }, + + { + {3, 0x02, 69}, + {6, 0x02, 69}, + {10, 0x02, 69}, + {15, 0x02, 69}, + {24, 0x02, 69}, + {31, 0x02, 69}, + {41, 0x02, 69}, + {56, 0x03, 69}, + {3, 0x02, 70}, + {6, 0x02, 70}, + {10, 0x02, 70}, + {15, 0x02, 70}, + {24, 0x02, 70}, + {31, 0x02, 70}, + {41, 0x02, 70}, + {56, 0x03, 70}, + }, + + { + {3, 0x02, 71}, + {6, 0x02, 71}, + {10, 0x02, 71}, + {15, 0x02, 71}, + {24, 0x02, 71}, + {31, 0x02, 71}, + {41, 0x02, 71}, + {56, 0x03, 71}, + {3, 0x02, 72}, + {6, 0x02, 72}, + {10, 0x02, 72}, + {15, 0x02, 72}, + {24, 0x02, 72}, + {31, 0x02, 72}, + {41, 0x02, 72}, + {56, 0x03, 72}, + }, + + { + {2, 0x02, 73}, + {9, 0x02, 73}, + {23, 0x02, 73}, + {40, 0x03, 73}, + {2, 0x02, 74}, + {9, 0x02, 74}, + {23, 0x02, 74}, + {40, 0x03, 74}, + {2, 0x02, 75}, + {9, 0x02, 75}, + {23, 0x02, 75}, + {40, 0x03, 75}, + {2, 0x02, 76}, + {9, 0x02, 76}, + {23, 0x02, 76}, + {40, 0x03, 76}, + }, + + { + {3, 0x02, 73}, + {6, 0x02, 73}, + {10, 0x02, 73}, + {15, 0x02, 73}, + {24, 0x02, 73}, + {31, 0x02, 73}, + {41, 0x02, 73}, + {56, 0x03, 73}, + {3, 0x02, 74}, + {6, 0x02, 74}, + {10, 0x02, 74}, + {15, 0x02, 74}, + {24, 0x02, 74}, + {31, 0x02, 74}, + {41, 0x02, 74}, + {56, 0x03, 74}, + }, + + { + {3, 0x02, 75}, + {6, 0x02, 75}, + {10, 0x02, 75}, + {15, 0x02, 75}, + {24, 0x02, 75}, + {31, 0x02, 75}, + {41, 0x02, 75}, + {56, 0x03, 75}, + {3, 0x02, 76}, + {6, 0x02, 76}, + {10, 0x02, 76}, + {15, 0x02, 76}, + {24, 0x02, 76}, + {31, 0x02, 76}, + {41, 0x02, 76}, + {56, 0x03, 76}, + }, + + { + {1, 0x02, 77}, + {22, 0x03, 77}, + {1, 0x02, 78}, + {22, 0x03, 78}, + {1, 0x02, 79}, + {22, 0x03, 79}, + {1, 0x02, 80}, + {22, 0x03, 80}, + {1, 0x02, 81}, + {22, 0x03, 81}, + {1, 0x02, 82}, + {22, 0x03, 82}, + {1, 0x02, 83}, + {22, 0x03, 83}, + {1, 0x02, 84}, + {22, 0x03, 84}, + }, + + { + {2, 0x02, 77}, + {9, 0x02, 77}, + {23, 0x02, 77}, + {40, 0x03, 77}, + {2, 0x02, 78}, + {9, 0x02, 78}, + {23, 0x02, 78}, + {40, 0x03, 78}, + {2, 0x02, 79}, + {9, 0x02, 79}, + {23, 0x02, 79}, + {40, 0x03, 79}, + {2, 0x02, 80}, + {9, 0x02, 80}, + {23, 0x02, 80}, + {40, 0x03, 80}, + }, + + { + {3, 0x02, 77}, + {6, 0x02, 77}, + {10, 0x02, 77}, + {15, 0x02, 77}, + {24, 0x02, 77}, + {31, 0x02, 77}, + {41, 0x02, 77}, + {56, 0x03, 77}, + {3, 0x02, 78}, + {6, 0x02, 78}, + {10, 0x02, 78}, + {15, 0x02, 78}, + {24, 0x02, 78}, + {31, 0x02, 78}, + {41, 0x02, 78}, + {56, 0x03, 78}, + }, + + { + {3, 0x02, 79}, + {6, 0x02, 79}, + {10, 0x02, 79}, + {15, 0x02, 79}, + {24, 0x02, 79}, + {31, 0x02, 79}, + {41, 0x02, 79}, + {56, 0x03, 79}, + {3, 0x02, 80}, + {6, 0x02, 80}, + {10, 0x02, 80}, + {15, 0x02, 80}, + {24, 0x02, 80}, + {31, 0x02, 80}, + {41, 0x02, 80}, + {56, 0x03, 80}, + }, + + { + {2, 0x02, 81}, + {9, 0x02, 81}, + {23, 0x02, 81}, + {40, 0x03, 81}, + {2, 0x02, 82}, + {9, 0x02, 82}, + {23, 0x02, 82}, + {40, 0x03, 82}, + {2, 0x02, 83}, + {9, 0x02, 83}, + {23, 0x02, 83}, + {40, 0x03, 83}, + {2, 0x02, 84}, + {9, 0x02, 84}, + {23, 0x02, 84}, + {40, 0x03, 84}, + }, + + { + {3, 0x02, 81}, + {6, 0x02, 81}, + {10, 0x02, 81}, + {15, 0x02, 81}, + {24, 0x02, 81}, + {31, 0x02, 81}, + {41, 0x02, 81}, + {56, 0x03, 81}, + {3, 0x02, 82}, + {6, 0x02, 82}, + {10, 0x02, 82}, + {15, 0x02, 82}, + {24, 0x02, 82}, + {31, 0x02, 82}, + {41, 0x02, 82}, + {56, 0x03, 82}, + }, + + { + {3, 0x02, 83}, + {6, 0x02, 83}, + {10, 0x02, 83}, + {15, 0x02, 83}, + {24, 0x02, 83}, + {31, 0x02, 83}, + {41, 0x02, 83}, + {56, 0x03, 83}, + {3, 0x02, 84}, + {6, 0x02, 84}, + {10, 0x02, 84}, + {15, 0x02, 84}, + {24, 0x02, 84}, + {31, 0x02, 84}, + {41, 0x02, 84}, + {56, 0x03, 84}, + }, + + { + {0, 0x03, 85}, + {0, 0x03, 86}, + {0, 0x03, 87}, + {0, 0x03, 89}, + {0, 0x03, 106}, + {0, 0x03, 107}, + {0, 0x03, 113}, + {0, 0x03, 118}, + {0, 0x03, 119}, + {0, 0x03, 120}, + {0, 0x03, 121}, + {0, 0x03, 122}, + {70, 0x00, 0}, + {71, 0x00, 0}, + {73, 0x00, 0}, + {74, 0x01, 0}, + }, + + { + {1, 0x02, 85}, + {22, 0x03, 85}, + {1, 0x02, 86}, + {22, 0x03, 86}, + {1, 0x02, 87}, + {22, 0x03, 87}, + {1, 0x02, 89}, + {22, 0x03, 89}, + {1, 0x02, 106}, + {22, 0x03, 106}, + {1, 0x02, 107}, + {22, 0x03, 107}, + {1, 0x02, 113}, + {22, 0x03, 113}, + {1, 0x02, 118}, + {22, 0x03, 118}, + }, + + { + {2, 0x02, 85}, + {9, 0x02, 85}, + {23, 0x02, 85}, + {40, 0x03, 85}, + {2, 0x02, 86}, + {9, 0x02, 86}, + {23, 0x02, 86}, + {40, 0x03, 86}, + {2, 0x02, 87}, + {9, 0x02, 87}, + {23, 0x02, 87}, + {40, 0x03, 87}, + {2, 0x02, 89}, + {9, 0x02, 89}, + {23, 0x02, 89}, + {40, 0x03, 89}, + }, + + { + {3, 0x02, 85}, + {6, 0x02, 85}, + {10, 0x02, 85}, + {15, 0x02, 85}, + {24, 0x02, 85}, + {31, 0x02, 85}, + {41, 0x02, 85}, + {56, 0x03, 85}, + {3, 0x02, 86}, + {6, 0x02, 86}, + {10, 0x02, 86}, + {15, 0x02, 86}, + {24, 0x02, 86}, + {31, 0x02, 86}, + {41, 0x02, 86}, + {56, 0x03, 86}, + }, + + { + {3, 0x02, 87}, + {6, 0x02, 87}, + {10, 0x02, 87}, + {15, 0x02, 87}, + {24, 0x02, 87}, + {31, 0x02, 87}, + {41, 0x02, 87}, + {56, 0x03, 87}, + {3, 0x02, 89}, + {6, 0x02, 89}, + {10, 0x02, 89}, + {15, 0x02, 89}, + {24, 0x02, 89}, + {31, 0x02, 89}, + {41, 0x02, 89}, + {56, 0x03, 89}, + }, + + { + {2, 0x02, 106}, + {9, 0x02, 106}, + {23, 0x02, 106}, + {40, 0x03, 106}, + {2, 0x02, 107}, + {9, 0x02, 107}, + {23, 0x02, 107}, + {40, 0x03, 107}, + {2, 0x02, 113}, + {9, 0x02, 113}, + {23, 0x02, 113}, + {40, 0x03, 113}, + {2, 0x02, 118}, + {9, 0x02, 118}, + {23, 0x02, 118}, + {40, 0x03, 118}, + }, + + { + {3, 0x02, 106}, + {6, 0x02, 106}, + {10, 0x02, 106}, + {15, 0x02, 106}, + {24, 0x02, 106}, + {31, 0x02, 106}, + {41, 0x02, 106}, + {56, 0x03, 106}, + {3, 0x02, 107}, + {6, 0x02, 107}, + {10, 0x02, 107}, + {15, 0x02, 107}, + {24, 0x02, 107}, + {31, 0x02, 107}, + {41, 0x02, 107}, + {56, 0x03, 107}, + }, + + { + {3, 0x02, 113}, + {6, 0x02, 113}, + {10, 0x02, 113}, + {15, 0x02, 113}, + {24, 0x02, 113}, + {31, 0x02, 113}, + {41, 0x02, 113}, + {56, 0x03, 113}, + {3, 0x02, 118}, + {6, 0x02, 118}, + {10, 0x02, 118}, + {15, 0x02, 118}, + {24, 0x02, 118}, + {31, 0x02, 118}, + {41, 0x02, 118}, + {56, 0x03, 118}, + }, + + { + {1, 0x02, 119}, + {22, 0x03, 119}, + {1, 0x02, 120}, + {22, 0x03, 120}, + {1, 0x02, 121}, + {22, 0x03, 121}, + {1, 0x02, 122}, + {22, 0x03, 122}, + {0, 0x03, 38}, + {0, 0x03, 42}, + {0, 0x03, 44}, + {0, 0x03, 59}, + {0, 0x03, 88}, + {0, 0x03, 90}, + {75, 0x00, 0}, + {78, 0x00, 0}, + }, + + { + {2, 0x02, 119}, + {9, 0x02, 119}, + {23, 0x02, 119}, + {40, 0x03, 119}, + {2, 0x02, 120}, + {9, 0x02, 120}, + {23, 0x02, 120}, + {40, 0x03, 120}, + {2, 0x02, 121}, + {9, 0x02, 121}, + {23, 0x02, 121}, + {40, 0x03, 121}, + {2, 0x02, 122}, + {9, 0x02, 122}, + {23, 0x02, 122}, + {40, 0x03, 122}, + }, + + { + {3, 0x02, 119}, + {6, 0x02, 119}, + {10, 0x02, 119}, + {15, 0x02, 119}, + {24, 0x02, 119}, + {31, 0x02, 119}, + {41, 0x02, 119}, + {56, 0x03, 119}, + {3, 0x02, 120}, + {6, 0x02, 120}, + {10, 0x02, 120}, + {15, 0x02, 120}, + {24, 0x02, 120}, + {31, 0x02, 120}, + {41, 0x02, 120}, + {56, 0x03, 120}, + }, + + { + {3, 0x02, 121}, + {6, 0x02, 121}, + {10, 0x02, 121}, + {15, 0x02, 121}, + {24, 0x02, 121}, + {31, 0x02, 121}, + {41, 0x02, 121}, + {56, 0x03, 121}, + {3, 0x02, 122}, + {6, 0x02, 122}, + {10, 0x02, 122}, + {15, 0x02, 122}, + {24, 0x02, 122}, + {31, 0x02, 122}, + {41, 0x02, 122}, + {56, 0x03, 122}, + }, + + { + {1, 0x02, 38}, + {22, 0x03, 38}, + {1, 0x02, 42}, + {22, 0x03, 42}, + {1, 0x02, 44}, + {22, 0x03, 44}, + {1, 0x02, 59}, + {22, 0x03, 59}, + {1, 0x02, 88}, + {22, 0x03, 88}, + {1, 0x02, 90}, + {22, 0x03, 90}, + {76, 0x00, 0}, + {77, 0x00, 0}, + {79, 0x00, 0}, + {81, 0x00, 0}, + }, + + { + {2, 0x02, 38}, + {9, 0x02, 38}, + {23, 0x02, 38}, + {40, 0x03, 38}, + {2, 0x02, 42}, + {9, 0x02, 42}, + {23, 0x02, 42}, + {40, 0x03, 42}, + {2, 0x02, 44}, + {9, 0x02, 44}, + {23, 0x02, 44}, + {40, 0x03, 44}, + {2, 0x02, 59}, + {9, 0x02, 59}, + {23, 0x02, 59}, + {40, 0x03, 59}, + }, + + { + {3, 0x02, 38}, + {6, 0x02, 38}, + {10, 0x02, 38}, + {15, 0x02, 38}, + {24, 0x02, 38}, + {31, 0x02, 38}, + {41, 0x02, 38}, + {56, 0x03, 38}, + {3, 0x02, 42}, + {6, 0x02, 42}, + {10, 0x02, 42}, + {15, 0x02, 42}, + {24, 0x02, 42}, + {31, 0x02, 42}, + {41, 0x02, 42}, + {56, 0x03, 42}, + }, + + { + {3, 0x02, 44}, + {6, 0x02, 44}, + {10, 0x02, 44}, + {15, 0x02, 44}, + {24, 0x02, 44}, + {31, 0x02, 44}, + {41, 0x02, 44}, + {56, 0x03, 44}, + {3, 0x02, 59}, + {6, 0x02, 59}, + {10, 0x02, 59}, + {15, 0x02, 59}, + {24, 0x02, 59}, + {31, 0x02, 59}, + {41, 0x02, 59}, + {56, 0x03, 59}, + }, + + { + {2, 0x02, 88}, + {9, 0x02, 88}, + {23, 0x02, 88}, + {40, 0x03, 88}, + {2, 0x02, 90}, + {9, 0x02, 90}, + {23, 0x02, 90}, + {40, 0x03, 90}, + {0, 0x03, 33}, + {0, 0x03, 34}, + {0, 0x03, 40}, + {0, 0x03, 41}, + {0, 0x03, 63}, + {80, 0x00, 0}, + {82, 0x00, 0}, + {84, 0x00, 0}, + }, + + { + {3, 0x02, 88}, + {6, 0x02, 88}, + {10, 0x02, 88}, + {15, 0x02, 88}, + {24, 0x02, 88}, + {31, 0x02, 88}, + {41, 0x02, 88}, + {56, 0x03, 88}, + {3, 0x02, 90}, + {6, 0x02, 90}, + {10, 0x02, 90}, + {15, 0x02, 90}, + {24, 0x02, 90}, + {31, 0x02, 90}, + {41, 0x02, 90}, + {56, 0x03, 90}, + }, + + { + {1, 0x02, 33}, + {22, 0x03, 33}, + {1, 0x02, 34}, + {22, 0x03, 34}, + {1, 0x02, 40}, + {22, 0x03, 40}, + {1, 0x02, 41}, + {22, 0x03, 41}, + {1, 0x02, 63}, + {22, 0x03, 63}, + {0, 0x03, 39}, + {0, 0x03, 43}, + {0, 0x03, 124}, + {83, 0x00, 0}, + {85, 0x00, 0}, + {88, 0x00, 0}, + }, + + { + {2, 0x02, 33}, + {9, 0x02, 33}, + {23, 0x02, 33}, + {40, 0x03, 33}, + {2, 0x02, 34}, + {9, 0x02, 34}, + {23, 0x02, 34}, + {40, 0x03, 34}, + {2, 0x02, 40}, + {9, 0x02, 40}, + {23, 0x02, 40}, + {40, 0x03, 40}, + {2, 0x02, 41}, + {9, 0x02, 41}, + {23, 0x02, 41}, + {40, 0x03, 41}, + }, + + { + {3, 0x02, 33}, + {6, 0x02, 33}, + {10, 0x02, 33}, + {15, 0x02, 33}, + {24, 0x02, 33}, + {31, 0x02, 33}, + {41, 0x02, 33}, + {56, 0x03, 33}, + {3, 0x02, 34}, + {6, 0x02, 34}, + {10, 0x02, 34}, + {15, 0x02, 34}, + {24, 0x02, 34}, + {31, 0x02, 34}, + {41, 0x02, 34}, + {56, 0x03, 34}, + }, + + { + {3, 0x02, 40}, + {6, 0x02, 40}, + {10, 0x02, 40}, + {15, 0x02, 40}, + {24, 0x02, 40}, + {31, 0x02, 40}, + {41, 0x02, 40}, + {56, 0x03, 40}, + {3, 0x02, 41}, + {6, 0x02, 41}, + {10, 0x02, 41}, + {15, 0x02, 41}, + {24, 0x02, 41}, + {31, 0x02, 41}, + {41, 0x02, 41}, + {56, 0x03, 41}, + }, + + { + {2, 0x02, 63}, + {9, 0x02, 63}, + {23, 0x02, 63}, + {40, 0x03, 63}, + {1, 0x02, 39}, + {22, 0x03, 39}, + {1, 0x02, 43}, + {22, 0x03, 43}, + {1, 0x02, 124}, + {22, 0x03, 124}, + {0, 0x03, 35}, + {0, 0x03, 62}, + {86, 0x00, 0}, + {87, 0x00, 0}, + {89, 0x00, 0}, + {90, 0x00, 0}, + }, + + { + {3, 0x02, 63}, + {6, 0x02, 63}, + {10, 0x02, 63}, + {15, 0x02, 63}, + {24, 0x02, 63}, + {31, 0x02, 63}, + {41, 0x02, 63}, + {56, 0x03, 63}, + {2, 0x02, 39}, + {9, 0x02, 39}, + {23, 0x02, 39}, + {40, 0x03, 39}, + {2, 0x02, 43}, + {9, 0x02, 43}, + {23, 0x02, 43}, + {40, 0x03, 43}, + }, + + { + {3, 0x02, 39}, + {6, 0x02, 39}, + {10, 0x02, 39}, + {15, 0x02, 39}, + {24, 0x02, 39}, + {31, 0x02, 39}, + {41, 0x02, 39}, + {56, 0x03, 39}, + {3, 0x02, 43}, + {6, 0x02, 43}, + {10, 0x02, 43}, + {15, 0x02, 43}, + {24, 0x02, 43}, + {31, 0x02, 43}, + {41, 0x02, 43}, + {56, 0x03, 43}, + }, + + { + {2, 0x02, 124}, + {9, 0x02, 124}, + {23, 0x02, 124}, + {40, 0x03, 124}, + {1, 0x02, 35}, + {22, 0x03, 35}, + {1, 0x02, 62}, + {22, 0x03, 62}, + {0, 0x03, 0}, + {0, 0x03, 36}, + {0, 0x03, 64}, + {0, 0x03, 91}, + {0, 0x03, 93}, + {0, 0x03, 126}, + {91, 0x00, 0}, + {92, 0x00, 0}, + }, + + { + {3, 0x02, 124}, + {6, 0x02, 124}, + {10, 0x02, 124}, + {15, 0x02, 124}, + {24, 0x02, 124}, + {31, 0x02, 124}, + {41, 0x02, 124}, + {56, 0x03, 124}, + {2, 0x02, 35}, + {9, 0x02, 35}, + {23, 0x02, 35}, + {40, 0x03, 35}, + {2, 0x02, 62}, + {9, 0x02, 62}, + {23, 0x02, 62}, + {40, 0x03, 62}, + }, + + { + {3, 0x02, 35}, + {6, 0x02, 35}, + {10, 0x02, 35}, + {15, 0x02, 35}, + {24, 0x02, 35}, + {31, 0x02, 35}, + {41, 0x02, 35}, + {56, 0x03, 35}, + {3, 0x02, 62}, + {6, 0x02, 62}, + {10, 0x02, 62}, + {15, 0x02, 62}, + {24, 0x02, 62}, + {31, 0x02, 62}, + {41, 0x02, 62}, + {56, 0x03, 62}, + }, + + { + {1, 0x02, 0}, + {22, 0x03, 0}, + {1, 0x02, 36}, + {22, 0x03, 36}, + {1, 0x02, 64}, + {22, 0x03, 64}, + {1, 0x02, 91}, + {22, 0x03, 91}, + {1, 0x02, 93}, + {22, 0x03, 93}, + {1, 0x02, 126}, + {22, 0x03, 126}, + {0, 0x03, 94}, + {0, 0x03, 125}, + {93, 0x00, 0}, + {94, 0x00, 0}, + }, + + { + {2, 0x02, 0}, + {9, 0x02, 0}, + {23, 0x02, 0}, + {40, 0x03, 0}, + {2, 0x02, 36}, + {9, 0x02, 36}, + {23, 0x02, 36}, + {40, 0x03, 36}, + {2, 0x02, 64}, + {9, 0x02, 64}, + {23, 0x02, 64}, + {40, 0x03, 64}, + {2, 0x02, 91}, + {9, 0x02, 91}, + {23, 0x02, 91}, + {40, 0x03, 91}, + }, + + { + {3, 0x02, 0}, + {6, 0x02, 0}, + {10, 0x02, 0}, + {15, 0x02, 0}, + {24, 0x02, 0}, + {31, 0x02, 0}, + {41, 0x02, 0}, + {56, 0x03, 0}, + {3, 0x02, 36}, + {6, 0x02, 36}, + {10, 0x02, 36}, + {15, 0x02, 36}, + {24, 0x02, 36}, + {31, 0x02, 36}, + {41, 0x02, 36}, + {56, 0x03, 36}, + }, + + { + {3, 0x02, 64}, + {6, 0x02, 64}, + {10, 0x02, 64}, + {15, 0x02, 64}, + {24, 0x02, 64}, + {31, 0x02, 64}, + {41, 0x02, 64}, + {56, 0x03, 64}, + {3, 0x02, 91}, + {6, 0x02, 91}, + {10, 0x02, 91}, + {15, 0x02, 91}, + {24, 0x02, 91}, + {31, 0x02, 91}, + {41, 0x02, 91}, + {56, 0x03, 91}, + }, + + { + {2, 0x02, 93}, + {9, 0x02, 93}, + {23, 0x02, 93}, + {40, 0x03, 93}, + {2, 0x02, 126}, + {9, 0x02, 126}, + {23, 0x02, 126}, + {40, 0x03, 126}, + {1, 0x02, 94}, + {22, 0x03, 94}, + {1, 0x02, 125}, + {22, 0x03, 125}, + {0, 0x03, 60}, + {0, 0x03, 96}, + {0, 0x03, 123}, + {95, 0x00, 0}, + }, + + { + {3, 0x02, 93}, + {6, 0x02, 93}, + {10, 0x02, 93}, + {15, 0x02, 93}, + {24, 0x02, 93}, + {31, 0x02, 93}, + {41, 0x02, 93}, + {56, 0x03, 93}, + {3, 0x02, 126}, + {6, 0x02, 126}, + {10, 0x02, 126}, + {15, 0x02, 126}, + {24, 0x02, 126}, + {31, 0x02, 126}, + {41, 0x02, 126}, + {56, 0x03, 126}, + }, + + { + {2, 0x02, 94}, + {9, 0x02, 94}, + {23, 0x02, 94}, + {40, 0x03, 94}, + {2, 0x02, 125}, + {9, 0x02, 125}, + {23, 0x02, 125}, + {40, 0x03, 125}, + {1, 0x02, 60}, + {22, 0x03, 60}, + {1, 0x02, 96}, + {22, 0x03, 96}, + {1, 0x02, 123}, + {22, 0x03, 123}, + {96, 0x00, 0}, + {110, 0x00, 0}, + }, + + { + {3, 0x02, 94}, + {6, 0x02, 94}, + {10, 0x02, 94}, + {15, 0x02, 94}, + {24, 0x02, 94}, + {31, 0x02, 94}, + {41, 0x02, 94}, + {56, 0x03, 94}, + {3, 0x02, 125}, + {6, 0x02, 125}, + {10, 0x02, 125}, + {15, 0x02, 125}, + {24, 0x02, 125}, + {31, 0x02, 125}, + {41, 0x02, 125}, + {56, 0x03, 125}, + }, + + { + {2, 0x02, 60}, + {9, 0x02, 60}, + {23, 0x02, 60}, + {40, 0x03, 60}, + {2, 0x02, 96}, + {9, 0x02, 96}, + {23, 0x02, 96}, + {40, 0x03, 96}, + {2, 0x02, 123}, + {9, 0x02, 123}, + {23, 0x02, 123}, + {40, 0x03, 123}, + {97, 0x00, 0}, + {101, 0x00, 0}, + {111, 0x00, 0}, + {133, 0x00, 0}, + }, + + { + {3, 0x02, 60}, + {6, 0x02, 60}, + {10, 0x02, 60}, + {15, 0x02, 60}, + {24, 0x02, 60}, + {31, 0x02, 60}, + {41, 0x02, 60}, + {56, 0x03, 60}, + {3, 0x02, 96}, + {6, 0x02, 96}, + {10, 0x02, 96}, + {15, 0x02, 96}, + {24, 0x02, 96}, + {31, 0x02, 96}, + {41, 0x02, 96}, + {56, 0x03, 96}, + }, + + { + {3, 0x02, 123}, + {6, 0x02, 123}, + {10, 0x02, 123}, + {15, 0x02, 123}, + {24, 0x02, 123}, + {31, 0x02, 123}, + {41, 0x02, 123}, + {56, 0x03, 123}, + {98, 0x00, 0}, + {99, 0x00, 0}, + {102, 0x00, 0}, + {105, 0x00, 0}, + {112, 0x00, 0}, + {119, 0x00, 0}, + {134, 0x00, 0}, + {153, 0x00, 0}, + }, + + { + {0, 0x03, 92}, + {0, 0x03, 195}, + {0, 0x03, 208}, + {100, 0x00, 0}, + {103, 0x00, 0}, + {104, 0x00, 0}, + {106, 0x00, 0}, + {107, 0x00, 0}, + {113, 0x00, 0}, + {116, 0x00, 0}, + {120, 0x00, 0}, + {126, 0x00, 0}, + {135, 0x00, 0}, + {142, 0x00, 0}, + {154, 0x00, 0}, + {169, 0x00, 0}, + }, + + { + {1, 0x02, 92}, + {22, 0x03, 92}, + {1, 0x02, 195}, + {22, 0x03, 195}, + {1, 0x02, 208}, + {22, 0x03, 208}, + {0, 0x03, 128}, + {0, 0x03, 130}, + {0, 0x03, 131}, + {0, 0x03, 162}, + {0, 0x03, 184}, + {0, 0x03, 194}, + {0, 0x03, 224}, + {0, 0x03, 226}, + {108, 0x00, 0}, + {109, 0x00, 0}, + }, + + { + {2, 0x02, 92}, + {9, 0x02, 92}, + {23, 0x02, 92}, + {40, 0x03, 92}, + {2, 0x02, 195}, + {9, 0x02, 195}, + {23, 0x02, 195}, + {40, 0x03, 195}, + {2, 0x02, 208}, + {9, 0x02, 208}, + {23, 0x02, 208}, + {40, 0x03, 208}, + {1, 0x02, 128}, + {22, 0x03, 128}, + {1, 0x02, 130}, + {22, 0x03, 130}, + }, + + { + {3, 0x02, 92}, + {6, 0x02, 92}, + {10, 0x02, 92}, + {15, 0x02, 92}, + {24, 0x02, 92}, + {31, 0x02, 92}, + {41, 0x02, 92}, + {56, 0x03, 92}, + {3, 0x02, 195}, + {6, 0x02, 195}, + {10, 0x02, 195}, + {15, 0x02, 195}, + {24, 0x02, 195}, + {31, 0x02, 195}, + {41, 0x02, 195}, + {56, 0x03, 195}, + }, + + { + {3, 0x02, 208}, + {6, 0x02, 208}, + {10, 0x02, 208}, + {15, 0x02, 208}, + {24, 0x02, 208}, + {31, 0x02, 208}, + {41, 0x02, 208}, + {56, 0x03, 208}, + {2, 0x02, 128}, + {9, 0x02, 128}, + {23, 0x02, 128}, + {40, 0x03, 128}, + {2, 0x02, 130}, + {9, 0x02, 130}, + {23, 0x02, 130}, + {40, 0x03, 130}, + }, + + { + {3, 0x02, 128}, + {6, 0x02, 128}, + {10, 0x02, 128}, + {15, 0x02, 128}, + {24, 0x02, 128}, + {31, 0x02, 128}, + {41, 0x02, 128}, + {56, 0x03, 128}, + {3, 0x02, 130}, + {6, 0x02, 130}, + {10, 0x02, 130}, + {15, 0x02, 130}, + {24, 0x02, 130}, + {31, 0x02, 130}, + {41, 0x02, 130}, + {56, 0x03, 130}, + }, + + { + {1, 0x02, 131}, + {22, 0x03, 131}, + {1, 0x02, 162}, + {22, 0x03, 162}, + {1, 0x02, 184}, + {22, 0x03, 184}, + {1, 0x02, 194}, + {22, 0x03, 194}, + {1, 0x02, 224}, + {22, 0x03, 224}, + {1, 0x02, 226}, + {22, 0x03, 226}, + {0, 0x03, 153}, + {0, 0x03, 161}, + {0, 0x03, 167}, + {0, 0x03, 172}, + }, + + { + {2, 0x02, 131}, + {9, 0x02, 131}, + {23, 0x02, 131}, + {40, 0x03, 131}, + {2, 0x02, 162}, + {9, 0x02, 162}, + {23, 0x02, 162}, + {40, 0x03, 162}, + {2, 0x02, 184}, + {9, 0x02, 184}, + {23, 0x02, 184}, + {40, 0x03, 184}, + {2, 0x02, 194}, + {9, 0x02, 194}, + {23, 0x02, 194}, + {40, 0x03, 194}, + }, + + { + {3, 0x02, 131}, + {6, 0x02, 131}, + {10, 0x02, 131}, + {15, 0x02, 131}, + {24, 0x02, 131}, + {31, 0x02, 131}, + {41, 0x02, 131}, + {56, 0x03, 131}, + {3, 0x02, 162}, + {6, 0x02, 162}, + {10, 0x02, 162}, + {15, 0x02, 162}, + {24, 0x02, 162}, + {31, 0x02, 162}, + {41, 0x02, 162}, + {56, 0x03, 162}, + }, + + { + {3, 0x02, 184}, + {6, 0x02, 184}, + {10, 0x02, 184}, + {15, 0x02, 184}, + {24, 0x02, 184}, + {31, 0x02, 184}, + {41, 0x02, 184}, + {56, 0x03, 184}, + {3, 0x02, 194}, + {6, 0x02, 194}, + {10, 0x02, 194}, + {15, 0x02, 194}, + {24, 0x02, 194}, + {31, 0x02, 194}, + {41, 0x02, 194}, + {56, 0x03, 194}, + }, + + { + {2, 0x02, 224}, + {9, 0x02, 224}, + {23, 0x02, 224}, + {40, 0x03, 224}, + {2, 0x02, 226}, + {9, 0x02, 226}, + {23, 0x02, 226}, + {40, 0x03, 226}, + {1, 0x02, 153}, + {22, 0x03, 153}, + {1, 0x02, 161}, + {22, 0x03, 161}, + {1, 0x02, 167}, + {22, 0x03, 167}, + {1, 0x02, 172}, + {22, 0x03, 172}, + }, + + { + {3, 0x02, 224}, + {6, 0x02, 224}, + {10, 0x02, 224}, + {15, 0x02, 224}, + {24, 0x02, 224}, + {31, 0x02, 224}, + {41, 0x02, 224}, + {56, 0x03, 224}, + {3, 0x02, 226}, + {6, 0x02, 226}, + {10, 0x02, 226}, + {15, 0x02, 226}, + {24, 0x02, 226}, + {31, 0x02, 226}, + {41, 0x02, 226}, + {56, 0x03, 226}, + }, + + { + {2, 0x02, 153}, + {9, 0x02, 153}, + {23, 0x02, 153}, + {40, 0x03, 153}, + {2, 0x02, 161}, + {9, 0x02, 161}, + {23, 0x02, 161}, + {40, 0x03, 161}, + {2, 0x02, 167}, + {9, 0x02, 167}, + {23, 0x02, 167}, + {40, 0x03, 167}, + {2, 0x02, 172}, + {9, 0x02, 172}, + {23, 0x02, 172}, + {40, 0x03, 172}, + }, + + { + {3, 0x02, 153}, + {6, 0x02, 153}, + {10, 0x02, 153}, + {15, 0x02, 153}, + {24, 0x02, 153}, + {31, 0x02, 153}, + {41, 0x02, 153}, + {56, 0x03, 153}, + {3, 0x02, 161}, + {6, 0x02, 161}, + {10, 0x02, 161}, + {15, 0x02, 161}, + {24, 0x02, 161}, + {31, 0x02, 161}, + {41, 0x02, 161}, + {56, 0x03, 161}, + }, + + { + {3, 0x02, 167}, + {6, 0x02, 167}, + {10, 0x02, 167}, + {15, 0x02, 167}, + {24, 0x02, 167}, + {31, 0x02, 167}, + {41, 0x02, 167}, + {56, 0x03, 167}, + {3, 0x02, 172}, + {6, 0x02, 172}, + {10, 0x02, 172}, + {15, 0x02, 172}, + {24, 0x02, 172}, + {31, 0x02, 172}, + {41, 0x02, 172}, + {56, 0x03, 172}, + }, + + { + {114, 0x00, 0}, + {115, 0x00, 0}, + {117, 0x00, 0}, + {118, 0x00, 0}, + {121, 0x00, 0}, + {123, 0x00, 0}, + {127, 0x00, 0}, + {130, 0x00, 0}, + {136, 0x00, 0}, + {139, 0x00, 0}, + {143, 0x00, 0}, + {146, 0x00, 0}, + {155, 0x00, 0}, + {162, 0x00, 0}, + {170, 0x00, 0}, + {180, 0x00, 0}, + }, + + { + {0, 0x03, 176}, + {0, 0x03, 177}, + {0, 0x03, 179}, + {0, 0x03, 209}, + {0, 0x03, 216}, + {0, 0x03, 217}, + {0, 0x03, 227}, + {0, 0x03, 229}, + {0, 0x03, 230}, + {122, 0x00, 0}, + {124, 0x00, 0}, + {125, 0x00, 0}, + {128, 0x00, 0}, + {129, 0x00, 0}, + {131, 0x00, 0}, + {132, 0x00, 0}, + }, + + { + {1, 0x02, 176}, + {22, 0x03, 176}, + {1, 0x02, 177}, + {22, 0x03, 177}, + {1, 0x02, 179}, + {22, 0x03, 179}, + {1, 0x02, 209}, + {22, 0x03, 209}, + {1, 0x02, 216}, + {22, 0x03, 216}, + {1, 0x02, 217}, + {22, 0x03, 217}, + {1, 0x02, 227}, + {22, 0x03, 227}, + {1, 0x02, 229}, + {22, 0x03, 229}, + }, + + { + {2, 0x02, 176}, + {9, 0x02, 176}, + {23, 0x02, 176}, + {40, 0x03, 176}, + {2, 0x02, 177}, + {9, 0x02, 177}, + {23, 0x02, 177}, + {40, 0x03, 177}, + {2, 0x02, 179}, + {9, 0x02, 179}, + {23, 0x02, 179}, + {40, 0x03, 179}, + {2, 0x02, 209}, + {9, 0x02, 209}, + {23, 0x02, 209}, + {40, 0x03, 209}, + }, + + { + {3, 0x02, 176}, + {6, 0x02, 176}, + {10, 0x02, 176}, + {15, 0x02, 176}, + {24, 0x02, 176}, + {31, 0x02, 176}, + {41, 0x02, 176}, + {56, 0x03, 176}, + {3, 0x02, 177}, + {6, 0x02, 177}, + {10, 0x02, 177}, + {15, 0x02, 177}, + {24, 0x02, 177}, + {31, 0x02, 177}, + {41, 0x02, 177}, + {56, 0x03, 177}, + }, + + { + {3, 0x02, 179}, + {6, 0x02, 179}, + {10, 0x02, 179}, + {15, 0x02, 179}, + {24, 0x02, 179}, + {31, 0x02, 179}, + {41, 0x02, 179}, + {56, 0x03, 179}, + {3, 0x02, 209}, + {6, 0x02, 209}, + {10, 0x02, 209}, + {15, 0x02, 209}, + {24, 0x02, 209}, + {31, 0x02, 209}, + {41, 0x02, 209}, + {56, 0x03, 209}, + }, + + { + {2, 0x02, 216}, + {9, 0x02, 216}, + {23, 0x02, 216}, + {40, 0x03, 216}, + {2, 0x02, 217}, + {9, 0x02, 217}, + {23, 0x02, 217}, + {40, 0x03, 217}, + {2, 0x02, 227}, + {9, 0x02, 227}, + {23, 0x02, 227}, + {40, 0x03, 227}, + {2, 0x02, 229}, + {9, 0x02, 229}, + {23, 0x02, 229}, + {40, 0x03, 229}, + }, + + { + {3, 0x02, 216}, + {6, 0x02, 216}, + {10, 0x02, 216}, + {15, 0x02, 216}, + {24, 0x02, 216}, + {31, 0x02, 216}, + {41, 0x02, 216}, + {56, 0x03, 216}, + {3, 0x02, 217}, + {6, 0x02, 217}, + {10, 0x02, 217}, + {15, 0x02, 217}, + {24, 0x02, 217}, + {31, 0x02, 217}, + {41, 0x02, 217}, + {56, 0x03, 217}, + }, + + { + {3, 0x02, 227}, + {6, 0x02, 227}, + {10, 0x02, 227}, + {15, 0x02, 227}, + {24, 0x02, 227}, + {31, 0x02, 227}, + {41, 0x02, 227}, + {56, 0x03, 227}, + {3, 0x02, 229}, + {6, 0x02, 229}, + {10, 0x02, 229}, + {15, 0x02, 229}, + {24, 0x02, 229}, + {31, 0x02, 229}, + {41, 0x02, 229}, + {56, 0x03, 229}, + }, + + { + {1, 0x02, 230}, + {22, 0x03, 230}, + {0, 0x03, 129}, + {0, 0x03, 132}, + {0, 0x03, 133}, + {0, 0x03, 134}, + {0, 0x03, 136}, + {0, 0x03, 146}, + {0, 0x03, 154}, + {0, 0x03, 156}, + {0, 0x03, 160}, + {0, 0x03, 163}, + {0, 0x03, 164}, + {0, 0x03, 169}, + {0, 0x03, 170}, + {0, 0x03, 173}, + }, + + { + {2, 0x02, 230}, + {9, 0x02, 230}, + {23, 0x02, 230}, + {40, 0x03, 230}, + {1, 0x02, 129}, + {22, 0x03, 129}, + {1, 0x02, 132}, + {22, 0x03, 132}, + {1, 0x02, 133}, + {22, 0x03, 133}, + {1, 0x02, 134}, + {22, 0x03, 134}, + {1, 0x02, 136}, + {22, 0x03, 136}, + {1, 0x02, 146}, + {22, 0x03, 146}, + }, + + { + {3, 0x02, 230}, + {6, 0x02, 230}, + {10, 0x02, 230}, + {15, 0x02, 230}, + {24, 0x02, 230}, + {31, 0x02, 230}, + {41, 0x02, 230}, + {56, 0x03, 230}, + {2, 0x02, 129}, + {9, 0x02, 129}, + {23, 0x02, 129}, + {40, 0x03, 129}, + {2, 0x02, 132}, + {9, 0x02, 132}, + {23, 0x02, 132}, + {40, 0x03, 132}, + }, + + { + {3, 0x02, 129}, + {6, 0x02, 129}, + {10, 0x02, 129}, + {15, 0x02, 129}, + {24, 0x02, 129}, + {31, 0x02, 129}, + {41, 0x02, 129}, + {56, 0x03, 129}, + {3, 0x02, 132}, + {6, 0x02, 132}, + {10, 0x02, 132}, + {15, 0x02, 132}, + {24, 0x02, 132}, + {31, 0x02, 132}, + {41, 0x02, 132}, + {56, 0x03, 132}, + }, + + { + {2, 0x02, 133}, + {9, 0x02, 133}, + {23, 0x02, 133}, + {40, 0x03, 133}, + {2, 0x02, 134}, + {9, 0x02, 134}, + {23, 0x02, 134}, + {40, 0x03, 134}, + {2, 0x02, 136}, + {9, 0x02, 136}, + {23, 0x02, 136}, + {40, 0x03, 136}, + {2, 0x02, 146}, + {9, 0x02, 146}, + {23, 0x02, 146}, + {40, 0x03, 146}, + }, + + { + {3, 0x02, 133}, + {6, 0x02, 133}, + {10, 0x02, 133}, + {15, 0x02, 133}, + {24, 0x02, 133}, + {31, 0x02, 133}, + {41, 0x02, 133}, + {56, 0x03, 133}, + {3, 0x02, 134}, + {6, 0x02, 134}, + {10, 0x02, 134}, + {15, 0x02, 134}, + {24, 0x02, 134}, + {31, 0x02, 134}, + {41, 0x02, 134}, + {56, 0x03, 134}, + }, + + { + {3, 0x02, 136}, + {6, 0x02, 136}, + {10, 0x02, 136}, + {15, 0x02, 136}, + {24, 0x02, 136}, + {31, 0x02, 136}, + {41, 0x02, 136}, + {56, 0x03, 136}, + {3, 0x02, 146}, + {6, 0x02, 146}, + {10, 0x02, 146}, + {15, 0x02, 146}, + {24, 0x02, 146}, + {31, 0x02, 146}, + {41, 0x02, 146}, + {56, 0x03, 146}, + }, + + { + {1, 0x02, 154}, + {22, 0x03, 154}, + {1, 0x02, 156}, + {22, 0x03, 156}, + {1, 0x02, 160}, + {22, 0x03, 160}, + {1, 0x02, 163}, + {22, 0x03, 163}, + {1, 0x02, 164}, + {22, 0x03, 164}, + {1, 0x02, 169}, + {22, 0x03, 169}, + {1, 0x02, 170}, + {22, 0x03, 170}, + {1, 0x02, 173}, + {22, 0x03, 173}, + }, + + { + {2, 0x02, 154}, + {9, 0x02, 154}, + {23, 0x02, 154}, + {40, 0x03, 154}, + {2, 0x02, 156}, + {9, 0x02, 156}, + {23, 0x02, 156}, + {40, 0x03, 156}, + {2, 0x02, 160}, + {9, 0x02, 160}, + {23, 0x02, 160}, + {40, 0x03, 160}, + {2, 0x02, 163}, + {9, 0x02, 163}, + {23, 0x02, 163}, + {40, 0x03, 163}, + }, + + { + {3, 0x02, 154}, + {6, 0x02, 154}, + {10, 0x02, 154}, + {15, 0x02, 154}, + {24, 0x02, 154}, + {31, 0x02, 154}, + {41, 0x02, 154}, + {56, 0x03, 154}, + {3, 0x02, 156}, + {6, 0x02, 156}, + {10, 0x02, 156}, + {15, 0x02, 156}, + {24, 0x02, 156}, + {31, 0x02, 156}, + {41, 0x02, 156}, + {56, 0x03, 156}, + }, + + { + {3, 0x02, 160}, + {6, 0x02, 160}, + {10, 0x02, 160}, + {15, 0x02, 160}, + {24, 0x02, 160}, + {31, 0x02, 160}, + {41, 0x02, 160}, + {56, 0x03, 160}, + {3, 0x02, 163}, + {6, 0x02, 163}, + {10, 0x02, 163}, + {15, 0x02, 163}, + {24, 0x02, 163}, + {31, 0x02, 163}, + {41, 0x02, 163}, + {56, 0x03, 163}, + }, + + { + {2, 0x02, 164}, + {9, 0x02, 164}, + {23, 0x02, 164}, + {40, 0x03, 164}, + {2, 0x02, 169}, + {9, 0x02, 169}, + {23, 0x02, 169}, + {40, 0x03, 169}, + {2, 0x02, 170}, + {9, 0x02, 170}, + {23, 0x02, 170}, + {40, 0x03, 170}, + {2, 0x02, 173}, + {9, 0x02, 173}, + {23, 0x02, 173}, + {40, 0x03, 173}, + }, + + { + {3, 0x02, 164}, + {6, 0x02, 164}, + {10, 0x02, 164}, + {15, 0x02, 164}, + {24, 0x02, 164}, + {31, 0x02, 164}, + {41, 0x02, 164}, + {56, 0x03, 164}, + {3, 0x02, 169}, + {6, 0x02, 169}, + {10, 0x02, 169}, + {15, 0x02, 169}, + {24, 0x02, 169}, + {31, 0x02, 169}, + {41, 0x02, 169}, + {56, 0x03, 169}, + }, + + { + {3, 0x02, 170}, + {6, 0x02, 170}, + {10, 0x02, 170}, + {15, 0x02, 170}, + {24, 0x02, 170}, + {31, 0x02, 170}, + {41, 0x02, 170}, + {56, 0x03, 170}, + {3, 0x02, 173}, + {6, 0x02, 173}, + {10, 0x02, 173}, + {15, 0x02, 173}, + {24, 0x02, 173}, + {31, 0x02, 173}, + {41, 0x02, 173}, + {56, 0x03, 173}, + }, + + { + {137, 0x00, 0}, + {138, 0x00, 0}, + {140, 0x00, 0}, + {141, 0x00, 0}, + {144, 0x00, 0}, + {145, 0x00, 0}, + {147, 0x00, 0}, + {150, 0x00, 0}, + {156, 0x00, 0}, + {159, 0x00, 0}, + {163, 0x00, 0}, + {166, 0x00, 0}, + {171, 0x00, 0}, + {174, 0x00, 0}, + {181, 0x00, 0}, + {190, 0x00, 0}, + }, + + { + {0, 0x03, 178}, + {0, 0x03, 181}, + {0, 0x03, 185}, + {0, 0x03, 186}, + {0, 0x03, 187}, + {0, 0x03, 189}, + {0, 0x03, 190}, + {0, 0x03, 196}, + {0, 0x03, 198}, + {0, 0x03, 228}, + {0, 0x03, 232}, + {0, 0x03, 233}, + {148, 0x00, 0}, + {149, 0x00, 0}, + {151, 0x00, 0}, + {152, 0x00, 0}, + }, + + { + {1, 0x02, 178}, + {22, 0x03, 178}, + {1, 0x02, 181}, + {22, 0x03, 181}, + {1, 0x02, 185}, + {22, 0x03, 185}, + {1, 0x02, 186}, + {22, 0x03, 186}, + {1, 0x02, 187}, + {22, 0x03, 187}, + {1, 0x02, 189}, + {22, 0x03, 189}, + {1, 0x02, 190}, + {22, 0x03, 190}, + {1, 0x02, 196}, + {22, 0x03, 196}, + }, + + { + {2, 0x02, 178}, + {9, 0x02, 178}, + {23, 0x02, 178}, + {40, 0x03, 178}, + {2, 0x02, 181}, + {9, 0x02, 181}, + {23, 0x02, 181}, + {40, 0x03, 181}, + {2, 0x02, 185}, + {9, 0x02, 185}, + {23, 0x02, 185}, + {40, 0x03, 185}, + {2, 0x02, 186}, + {9, 0x02, 186}, + {23, 0x02, 186}, + {40, 0x03, 186}, + }, + + { + {3, 0x02, 178}, + {6, 0x02, 178}, + {10, 0x02, 178}, + {15, 0x02, 178}, + {24, 0x02, 178}, + {31, 0x02, 178}, + {41, 0x02, 178}, + {56, 0x03, 178}, + {3, 0x02, 181}, + {6, 0x02, 181}, + {10, 0x02, 181}, + {15, 0x02, 181}, + {24, 0x02, 181}, + {31, 0x02, 181}, + {41, 0x02, 181}, + {56, 0x03, 181}, + }, + + { + {3, 0x02, 185}, + {6, 0x02, 185}, + {10, 0x02, 185}, + {15, 0x02, 185}, + {24, 0x02, 185}, + {31, 0x02, 185}, + {41, 0x02, 185}, + {56, 0x03, 185}, + {3, 0x02, 186}, + {6, 0x02, 186}, + {10, 0x02, 186}, + {15, 0x02, 186}, + {24, 0x02, 186}, + {31, 0x02, 186}, + {41, 0x02, 186}, + {56, 0x03, 186}, + }, + + { + {2, 0x02, 187}, + {9, 0x02, 187}, + {23, 0x02, 187}, + {40, 0x03, 187}, + {2, 0x02, 189}, + {9, 0x02, 189}, + {23, 0x02, 189}, + {40, 0x03, 189}, + {2, 0x02, 190}, + {9, 0x02, 190}, + {23, 0x02, 190}, + {40, 0x03, 190}, + {2, 0x02, 196}, + {9, 0x02, 196}, + {23, 0x02, 196}, + {40, 0x03, 196}, + }, + + { + {3, 0x02, 187}, + {6, 0x02, 187}, + {10, 0x02, 187}, + {15, 0x02, 187}, + {24, 0x02, 187}, + {31, 0x02, 187}, + {41, 0x02, 187}, + {56, 0x03, 187}, + {3, 0x02, 189}, + {6, 0x02, 189}, + {10, 0x02, 189}, + {15, 0x02, 189}, + {24, 0x02, 189}, + {31, 0x02, 189}, + {41, 0x02, 189}, + {56, 0x03, 189}, + }, + + { + {3, 0x02, 190}, + {6, 0x02, 190}, + {10, 0x02, 190}, + {15, 0x02, 190}, + {24, 0x02, 190}, + {31, 0x02, 190}, + {41, 0x02, 190}, + {56, 0x03, 190}, + {3, 0x02, 196}, + {6, 0x02, 196}, + {10, 0x02, 196}, + {15, 0x02, 196}, + {24, 0x02, 196}, + {31, 0x02, 196}, + {41, 0x02, 196}, + {56, 0x03, 196}, + }, + + { + {1, 0x02, 198}, + {22, 0x03, 198}, + {1, 0x02, 228}, + {22, 0x03, 228}, + {1, 0x02, 232}, + {22, 0x03, 232}, + {1, 0x02, 233}, + {22, 0x03, 233}, + {0, 0x03, 1}, + {0, 0x03, 135}, + {0, 0x03, 137}, + {0, 0x03, 138}, + {0, 0x03, 139}, + {0, 0x03, 140}, + {0, 0x03, 141}, + {0, 0x03, 143}, + }, + + { + {2, 0x02, 198}, + {9, 0x02, 198}, + {23, 0x02, 198}, + {40, 0x03, 198}, + {2, 0x02, 228}, + {9, 0x02, 228}, + {23, 0x02, 228}, + {40, 0x03, 228}, + {2, 0x02, 232}, + {9, 0x02, 232}, + {23, 0x02, 232}, + {40, 0x03, 232}, + {2, 0x02, 233}, + {9, 0x02, 233}, + {23, 0x02, 233}, + {40, 0x03, 233}, + }, + + { + {3, 0x02, 198}, + {6, 0x02, 198}, + {10, 0x02, 198}, + {15, 0x02, 198}, + {24, 0x02, 198}, + {31, 0x02, 198}, + {41, 0x02, 198}, + {56, 0x03, 198}, + {3, 0x02, 228}, + {6, 0x02, 228}, + {10, 0x02, 228}, + {15, 0x02, 228}, + {24, 0x02, 228}, + {31, 0x02, 228}, + {41, 0x02, 228}, + {56, 0x03, 228}, + }, + + { + {3, 0x02, 232}, + {6, 0x02, 232}, + {10, 0x02, 232}, + {15, 0x02, 232}, + {24, 0x02, 232}, + {31, 0x02, 232}, + {41, 0x02, 232}, + {56, 0x03, 232}, + {3, 0x02, 233}, + {6, 0x02, 233}, + {10, 0x02, 233}, + {15, 0x02, 233}, + {24, 0x02, 233}, + {31, 0x02, 233}, + {41, 0x02, 233}, + {56, 0x03, 233}, + }, + + { + {1, 0x02, 1}, + {22, 0x03, 1}, + {1, 0x02, 135}, + {22, 0x03, 135}, + {1, 0x02, 137}, + {22, 0x03, 137}, + {1, 0x02, 138}, + {22, 0x03, 138}, + {1, 0x02, 139}, + {22, 0x03, 139}, + {1, 0x02, 140}, + {22, 0x03, 140}, + {1, 0x02, 141}, + {22, 0x03, 141}, + {1, 0x02, 143}, + {22, 0x03, 143}, + }, + + { + {2, 0x02, 1}, + {9, 0x02, 1}, + {23, 0x02, 1}, + {40, 0x03, 1}, + {2, 0x02, 135}, + {9, 0x02, 135}, + {23, 0x02, 135}, + {40, 0x03, 135}, + {2, 0x02, 137}, + {9, 0x02, 137}, + {23, 0x02, 137}, + {40, 0x03, 137}, + {2, 0x02, 138}, + {9, 0x02, 138}, + {23, 0x02, 138}, + {40, 0x03, 138}, + }, + + { + {3, 0x02, 1}, + {6, 0x02, 1}, + {10, 0x02, 1}, + {15, 0x02, 1}, + {24, 0x02, 1}, + {31, 0x02, 1}, + {41, 0x02, 1}, + {56, 0x03, 1}, + {3, 0x02, 135}, + {6, 0x02, 135}, + {10, 0x02, 135}, + {15, 0x02, 135}, + {24, 0x02, 135}, + {31, 0x02, 135}, + {41, 0x02, 135}, + {56, 0x03, 135}, + }, + + { + {3, 0x02, 137}, + {6, 0x02, 137}, + {10, 0x02, 137}, + {15, 0x02, 137}, + {24, 0x02, 137}, + {31, 0x02, 137}, + {41, 0x02, 137}, + {56, 0x03, 137}, + {3, 0x02, 138}, + {6, 0x02, 138}, + {10, 0x02, 138}, + {15, 0x02, 138}, + {24, 0x02, 138}, + {31, 0x02, 138}, + {41, 0x02, 138}, + {56, 0x03, 138}, + }, + + { + {2, 0x02, 139}, + {9, 0x02, 139}, + {23, 0x02, 139}, + {40, 0x03, 139}, + {2, 0x02, 140}, + {9, 0x02, 140}, + {23, 0x02, 140}, + {40, 0x03, 140}, + {2, 0x02, 141}, + {9, 0x02, 141}, + {23, 0x02, 141}, + {40, 0x03, 141}, + {2, 0x02, 143}, + {9, 0x02, 143}, + {23, 0x02, 143}, + {40, 0x03, 143}, + }, + + { + {3, 0x02, 139}, + {6, 0x02, 139}, + {10, 0x02, 139}, + {15, 0x02, 139}, + {24, 0x02, 139}, + {31, 0x02, 139}, + {41, 0x02, 139}, + {56, 0x03, 139}, + {3, 0x02, 140}, + {6, 0x02, 140}, + {10, 0x02, 140}, + {15, 0x02, 140}, + {24, 0x02, 140}, + {31, 0x02, 140}, + {41, 0x02, 140}, + {56, 0x03, 140}, + }, + + { + {3, 0x02, 141}, + {6, 0x02, 141}, + {10, 0x02, 141}, + {15, 0x02, 141}, + {24, 0x02, 141}, + {31, 0x02, 141}, + {41, 0x02, 141}, + {56, 0x03, 141}, + {3, 0x02, 143}, + {6, 0x02, 143}, + {10, 0x02, 143}, + {15, 0x02, 143}, + {24, 0x02, 143}, + {31, 0x02, 143}, + {41, 0x02, 143}, + {56, 0x03, 143}, + }, + + { + {157, 0x00, 0}, + {158, 0x00, 0}, + {160, 0x00, 0}, + {161, 0x00, 0}, + {164, 0x00, 0}, + {165, 0x00, 0}, + {167, 0x00, 0}, + {168, 0x00, 0}, + {172, 0x00, 0}, + {173, 0x00, 0}, + {175, 0x00, 0}, + {177, 0x00, 0}, + {182, 0x00, 0}, + {185, 0x00, 0}, + {191, 0x00, 0}, + {207, 0x00, 0}, + }, + + { + {0, 0x03, 147}, + {0, 0x03, 149}, + {0, 0x03, 150}, + {0, 0x03, 151}, + {0, 0x03, 152}, + {0, 0x03, 155}, + {0, 0x03, 157}, + {0, 0x03, 158}, + {0, 0x03, 165}, + {0, 0x03, 166}, + {0, 0x03, 168}, + {0, 0x03, 174}, + {0, 0x03, 175}, + {0, 0x03, 180}, + {0, 0x03, 182}, + {0, 0x03, 183}, + }, + + { + {1, 0x02, 147}, + {22, 0x03, 147}, + {1, 0x02, 149}, + {22, 0x03, 149}, + {1, 0x02, 150}, + {22, 0x03, 150}, + {1, 0x02, 151}, + {22, 0x03, 151}, + {1, 0x02, 152}, + {22, 0x03, 152}, + {1, 0x02, 155}, + {22, 0x03, 155}, + {1, 0x02, 157}, + {22, 0x03, 157}, + {1, 0x02, 158}, + {22, 0x03, 158}, + }, + + { + {2, 0x02, 147}, + {9, 0x02, 147}, + {23, 0x02, 147}, + {40, 0x03, 147}, + {2, 0x02, 149}, + {9, 0x02, 149}, + {23, 0x02, 149}, + {40, 0x03, 149}, + {2, 0x02, 150}, + {9, 0x02, 150}, + {23, 0x02, 150}, + {40, 0x03, 150}, + {2, 0x02, 151}, + {9, 0x02, 151}, + {23, 0x02, 151}, + {40, 0x03, 151}, + }, + + { + {3, 0x02, 147}, + {6, 0x02, 147}, + {10, 0x02, 147}, + {15, 0x02, 147}, + {24, 0x02, 147}, + {31, 0x02, 147}, + {41, 0x02, 147}, + {56, 0x03, 147}, + {3, 0x02, 149}, + {6, 0x02, 149}, + {10, 0x02, 149}, + {15, 0x02, 149}, + {24, 0x02, 149}, + {31, 0x02, 149}, + {41, 0x02, 149}, + {56, 0x03, 149}, + }, + + { + {3, 0x02, 150}, + {6, 0x02, 150}, + {10, 0x02, 150}, + {15, 0x02, 150}, + {24, 0x02, 150}, + {31, 0x02, 150}, + {41, 0x02, 150}, + {56, 0x03, 150}, + {3, 0x02, 151}, + {6, 0x02, 151}, + {10, 0x02, 151}, + {15, 0x02, 151}, + {24, 0x02, 151}, + {31, 0x02, 151}, + {41, 0x02, 151}, + {56, 0x03, 151}, + }, + + { + {2, 0x02, 152}, + {9, 0x02, 152}, + {23, 0x02, 152}, + {40, 0x03, 152}, + {2, 0x02, 155}, + {9, 0x02, 155}, + {23, 0x02, 155}, + {40, 0x03, 155}, + {2, 0x02, 157}, + {9, 0x02, 157}, + {23, 0x02, 157}, + {40, 0x03, 157}, + {2, 0x02, 158}, + {9, 0x02, 158}, + {23, 0x02, 158}, + {40, 0x03, 158}, + }, + + { + {3, 0x02, 152}, + {6, 0x02, 152}, + {10, 0x02, 152}, + {15, 0x02, 152}, + {24, 0x02, 152}, + {31, 0x02, 152}, + {41, 0x02, 152}, + {56, 0x03, 152}, + {3, 0x02, 155}, + {6, 0x02, 155}, + {10, 0x02, 155}, + {15, 0x02, 155}, + {24, 0x02, 155}, + {31, 0x02, 155}, + {41, 0x02, 155}, + {56, 0x03, 155}, + }, + + { + {3, 0x02, 157}, + {6, 0x02, 157}, + {10, 0x02, 157}, + {15, 0x02, 157}, + {24, 0x02, 157}, + {31, 0x02, 157}, + {41, 0x02, 157}, + {56, 0x03, 157}, + {3, 0x02, 158}, + {6, 0x02, 158}, + {10, 0x02, 158}, + {15, 0x02, 158}, + {24, 0x02, 158}, + {31, 0x02, 158}, + {41, 0x02, 158}, + {56, 0x03, 158}, + }, + + { + {1, 0x02, 165}, + {22, 0x03, 165}, + {1, 0x02, 166}, + {22, 0x03, 166}, + {1, 0x02, 168}, + {22, 0x03, 168}, + {1, 0x02, 174}, + {22, 0x03, 174}, + {1, 0x02, 175}, + {22, 0x03, 175}, + {1, 0x02, 180}, + {22, 0x03, 180}, + {1, 0x02, 182}, + {22, 0x03, 182}, + {1, 0x02, 183}, + {22, 0x03, 183}, + }, + + { + {2, 0x02, 165}, + {9, 0x02, 165}, + {23, 0x02, 165}, + {40, 0x03, 165}, + {2, 0x02, 166}, + {9, 0x02, 166}, + {23, 0x02, 166}, + {40, 0x03, 166}, + {2, 0x02, 168}, + {9, 0x02, 168}, + {23, 0x02, 168}, + {40, 0x03, 168}, + {2, 0x02, 174}, + {9, 0x02, 174}, + {23, 0x02, 174}, + {40, 0x03, 174}, + }, + + { + {3, 0x02, 165}, + {6, 0x02, 165}, + {10, 0x02, 165}, + {15, 0x02, 165}, + {24, 0x02, 165}, + {31, 0x02, 165}, + {41, 0x02, 165}, + {56, 0x03, 165}, + {3, 0x02, 166}, + {6, 0x02, 166}, + {10, 0x02, 166}, + {15, 0x02, 166}, + {24, 0x02, 166}, + {31, 0x02, 166}, + {41, 0x02, 166}, + {56, 0x03, 166}, + }, + + { + {3, 0x02, 168}, + {6, 0x02, 168}, + {10, 0x02, 168}, + {15, 0x02, 168}, + {24, 0x02, 168}, + {31, 0x02, 168}, + {41, 0x02, 168}, + {56, 0x03, 168}, + {3, 0x02, 174}, + {6, 0x02, 174}, + {10, 0x02, 174}, + {15, 0x02, 174}, + {24, 0x02, 174}, + {31, 0x02, 174}, + {41, 0x02, 174}, + {56, 0x03, 174}, + }, + + { + {2, 0x02, 175}, + {9, 0x02, 175}, + {23, 0x02, 175}, + {40, 0x03, 175}, + {2, 0x02, 180}, + {9, 0x02, 180}, + {23, 0x02, 180}, + {40, 0x03, 180}, + {2, 0x02, 182}, + {9, 0x02, 182}, + {23, 0x02, 182}, + {40, 0x03, 182}, + {2, 0x02, 183}, + {9, 0x02, 183}, + {23, 0x02, 183}, + {40, 0x03, 183}, + }, + + { + {3, 0x02, 175}, + {6, 0x02, 175}, + {10, 0x02, 175}, + {15, 0x02, 175}, + {24, 0x02, 175}, + {31, 0x02, 175}, + {41, 0x02, 175}, + {56, 0x03, 175}, + {3, 0x02, 180}, + {6, 0x02, 180}, + {10, 0x02, 180}, + {15, 0x02, 180}, + {24, 0x02, 180}, + {31, 0x02, 180}, + {41, 0x02, 180}, + {56, 0x03, 180}, + }, + + { + {3, 0x02, 182}, + {6, 0x02, 182}, + {10, 0x02, 182}, + {15, 0x02, 182}, + {24, 0x02, 182}, + {31, 0x02, 182}, + {41, 0x02, 182}, + {56, 0x03, 182}, + {3, 0x02, 183}, + {6, 0x02, 183}, + {10, 0x02, 183}, + {15, 0x02, 183}, + {24, 0x02, 183}, + {31, 0x02, 183}, + {41, 0x02, 183}, + {56, 0x03, 183}, + }, + + { + {0, 0x03, 188}, + {0, 0x03, 191}, + {0, 0x03, 197}, + {0, 0x03, 231}, + {0, 0x03, 239}, + {176, 0x00, 0}, + {178, 0x00, 0}, + {179, 0x00, 0}, + {183, 0x00, 0}, + {184, 0x00, 0}, + {186, 0x00, 0}, + {187, 0x00, 0}, + {192, 0x00, 0}, + {199, 0x00, 0}, + {208, 0x00, 0}, + {223, 0x00, 0}, + }, + + { + {1, 0x02, 188}, + {22, 0x03, 188}, + {1, 0x02, 191}, + {22, 0x03, 191}, + {1, 0x02, 197}, + {22, 0x03, 197}, + {1, 0x02, 231}, + {22, 0x03, 231}, + {1, 0x02, 239}, + {22, 0x03, 239}, + {0, 0x03, 9}, + {0, 0x03, 142}, + {0, 0x03, 144}, + {0, 0x03, 145}, + {0, 0x03, 148}, + {0, 0x03, 159}, + }, + + { + {2, 0x02, 188}, + {9, 0x02, 188}, + {23, 0x02, 188}, + {40, 0x03, 188}, + {2, 0x02, 191}, + {9, 0x02, 191}, + {23, 0x02, 191}, + {40, 0x03, 191}, + {2, 0x02, 197}, + {9, 0x02, 197}, + {23, 0x02, 197}, + {40, 0x03, 197}, + {2, 0x02, 231}, + {9, 0x02, 231}, + {23, 0x02, 231}, + {40, 0x03, 231}, + }, + + { + {3, 0x02, 188}, + {6, 0x02, 188}, + {10, 0x02, 188}, + {15, 0x02, 188}, + {24, 0x02, 188}, + {31, 0x02, 188}, + {41, 0x02, 188}, + {56, 0x03, 188}, + {3, 0x02, 191}, + {6, 0x02, 191}, + {10, 0x02, 191}, + {15, 0x02, 191}, + {24, 0x02, 191}, + {31, 0x02, 191}, + {41, 0x02, 191}, + {56, 0x03, 191}, + }, + + { + {3, 0x02, 197}, + {6, 0x02, 197}, + {10, 0x02, 197}, + {15, 0x02, 197}, + {24, 0x02, 197}, + {31, 0x02, 197}, + {41, 0x02, 197}, + {56, 0x03, 197}, + {3, 0x02, 231}, + {6, 0x02, 231}, + {10, 0x02, 231}, + {15, 0x02, 231}, + {24, 0x02, 231}, + {31, 0x02, 231}, + {41, 0x02, 231}, + {56, 0x03, 231}, + }, + + { + {2, 0x02, 239}, + {9, 0x02, 239}, + {23, 0x02, 239}, + {40, 0x03, 239}, + {1, 0x02, 9}, + {22, 0x03, 9}, + {1, 0x02, 142}, + {22, 0x03, 142}, + {1, 0x02, 144}, + {22, 0x03, 144}, + {1, 0x02, 145}, + {22, 0x03, 145}, + {1, 0x02, 148}, + {22, 0x03, 148}, + {1, 0x02, 159}, + {22, 0x03, 159}, + }, + + { + {3, 0x02, 239}, + {6, 0x02, 239}, + {10, 0x02, 239}, + {15, 0x02, 239}, + {24, 0x02, 239}, + {31, 0x02, 239}, + {41, 0x02, 239}, + {56, 0x03, 239}, + {2, 0x02, 9}, + {9, 0x02, 9}, + {23, 0x02, 9}, + {40, 0x03, 9}, + {2, 0x02, 142}, + {9, 0x02, 142}, + {23, 0x02, 142}, + {40, 0x03, 142}, + }, + + { + {3, 0x02, 9}, + {6, 0x02, 9}, + {10, 0x02, 9}, + {15, 0x02, 9}, + {24, 0x02, 9}, + {31, 0x02, 9}, + {41, 0x02, 9}, + {56, 0x03, 9}, + {3, 0x02, 142}, + {6, 0x02, 142}, + {10, 0x02, 142}, + {15, 0x02, 142}, + {24, 0x02, 142}, + {31, 0x02, 142}, + {41, 0x02, 142}, + {56, 0x03, 142}, + }, + + { + {2, 0x02, 144}, + {9, 0x02, 144}, + {23, 0x02, 144}, + {40, 0x03, 144}, + {2, 0x02, 145}, + {9, 0x02, 145}, + {23, 0x02, 145}, + {40, 0x03, 145}, + {2, 0x02, 148}, + {9, 0x02, 148}, + {23, 0x02, 148}, + {40, 0x03, 148}, + {2, 0x02, 159}, + {9, 0x02, 159}, + {23, 0x02, 159}, + {40, 0x03, 159}, + }, + + { + {3, 0x02, 144}, + {6, 0x02, 144}, + {10, 0x02, 144}, + {15, 0x02, 144}, + {24, 0x02, 144}, + {31, 0x02, 144}, + {41, 0x02, 144}, + {56, 0x03, 144}, + {3, 0x02, 145}, + {6, 0x02, 145}, + {10, 0x02, 145}, + {15, 0x02, 145}, + {24, 0x02, 145}, + {31, 0x02, 145}, + {41, 0x02, 145}, + {56, 0x03, 145}, + }, + + { + {3, 0x02, 148}, + {6, 0x02, 148}, + {10, 0x02, 148}, + {15, 0x02, 148}, + {24, 0x02, 148}, + {31, 0x02, 148}, + {41, 0x02, 148}, + {56, 0x03, 148}, + {3, 0x02, 159}, + {6, 0x02, 159}, + {10, 0x02, 159}, + {15, 0x02, 159}, + {24, 0x02, 159}, + {31, 0x02, 159}, + {41, 0x02, 159}, + {56, 0x03, 159}, + }, + + { + {0, 0x03, 171}, + {0, 0x03, 206}, + {0, 0x03, 215}, + {0, 0x03, 225}, + {0, 0x03, 236}, + {0, 0x03, 237}, + {188, 0x00, 0}, + {189, 0x00, 0}, + {193, 0x00, 0}, + {196, 0x00, 0}, + {200, 0x00, 0}, + {203, 0x00, 0}, + {209, 0x00, 0}, + {216, 0x00, 0}, + {224, 0x00, 0}, + {238, 0x00, 0}, + }, + + { + {1, 0x02, 171}, + {22, 0x03, 171}, + {1, 0x02, 206}, + {22, 0x03, 206}, + {1, 0x02, 215}, + {22, 0x03, 215}, + {1, 0x02, 225}, + {22, 0x03, 225}, + {1, 0x02, 236}, + {22, 0x03, 236}, + {1, 0x02, 237}, + {22, 0x03, 237}, + {0, 0x03, 199}, + {0, 0x03, 207}, + {0, 0x03, 234}, + {0, 0x03, 235}, + }, + + { + {2, 0x02, 171}, + {9, 0x02, 171}, + {23, 0x02, 171}, + {40, 0x03, 171}, + {2, 0x02, 206}, + {9, 0x02, 206}, + {23, 0x02, 206}, + {40, 0x03, 206}, + {2, 0x02, 215}, + {9, 0x02, 215}, + {23, 0x02, 215}, + {40, 0x03, 215}, + {2, 0x02, 225}, + {9, 0x02, 225}, + {23, 0x02, 225}, + {40, 0x03, 225}, + }, + + { + {3, 0x02, 171}, + {6, 0x02, 171}, + {10, 0x02, 171}, + {15, 0x02, 171}, + {24, 0x02, 171}, + {31, 0x02, 171}, + {41, 0x02, 171}, + {56, 0x03, 171}, + {3, 0x02, 206}, + {6, 0x02, 206}, + {10, 0x02, 206}, + {15, 0x02, 206}, + {24, 0x02, 206}, + {31, 0x02, 206}, + {41, 0x02, 206}, + {56, 0x03, 206}, + }, + + { + {3, 0x02, 215}, + {6, 0x02, 215}, + {10, 0x02, 215}, + {15, 0x02, 215}, + {24, 0x02, 215}, + {31, 0x02, 215}, + {41, 0x02, 215}, + {56, 0x03, 215}, + {3, 0x02, 225}, + {6, 0x02, 225}, + {10, 0x02, 225}, + {15, 0x02, 225}, + {24, 0x02, 225}, + {31, 0x02, 225}, + {41, 0x02, 225}, + {56, 0x03, 225}, + }, + + { + {2, 0x02, 236}, + {9, 0x02, 236}, + {23, 0x02, 236}, + {40, 0x03, 236}, + {2, 0x02, 237}, + {9, 0x02, 237}, + {23, 0x02, 237}, + {40, 0x03, 237}, + {1, 0x02, 199}, + {22, 0x03, 199}, + {1, 0x02, 207}, + {22, 0x03, 207}, + {1, 0x02, 234}, + {22, 0x03, 234}, + {1, 0x02, 235}, + {22, 0x03, 235}, + }, + + { + {3, 0x02, 236}, + {6, 0x02, 236}, + {10, 0x02, 236}, + {15, 0x02, 236}, + {24, 0x02, 236}, + {31, 0x02, 236}, + {41, 0x02, 236}, + {56, 0x03, 236}, + {3, 0x02, 237}, + {6, 0x02, 237}, + {10, 0x02, 237}, + {15, 0x02, 237}, + {24, 0x02, 237}, + {31, 0x02, 237}, + {41, 0x02, 237}, + {56, 0x03, 237}, + }, + + { + {2, 0x02, 199}, + {9, 0x02, 199}, + {23, 0x02, 199}, + {40, 0x03, 199}, + {2, 0x02, 207}, + {9, 0x02, 207}, + {23, 0x02, 207}, + {40, 0x03, 207}, + {2, 0x02, 234}, + {9, 0x02, 234}, + {23, 0x02, 234}, + {40, 0x03, 234}, + {2, 0x02, 235}, + {9, 0x02, 235}, + {23, 0x02, 235}, + {40, 0x03, 235}, + }, + + { + {3, 0x02, 199}, + {6, 0x02, 199}, + {10, 0x02, 199}, + {15, 0x02, 199}, + {24, 0x02, 199}, + {31, 0x02, 199}, + {41, 0x02, 199}, + {56, 0x03, 199}, + {3, 0x02, 207}, + {6, 0x02, 207}, + {10, 0x02, 207}, + {15, 0x02, 207}, + {24, 0x02, 207}, + {31, 0x02, 207}, + {41, 0x02, 207}, + {56, 0x03, 207}, + }, + + { + {3, 0x02, 234}, + {6, 0x02, 234}, + {10, 0x02, 234}, + {15, 0x02, 234}, + {24, 0x02, 234}, + {31, 0x02, 234}, + {41, 0x02, 234}, + {56, 0x03, 234}, + {3, 0x02, 235}, + {6, 0x02, 235}, + {10, 0x02, 235}, + {15, 0x02, 235}, + {24, 0x02, 235}, + {31, 0x02, 235}, + {41, 0x02, 235}, + {56, 0x03, 235}, + }, + + { + {194, 0x00, 0}, + {195, 0x00, 0}, + {197, 0x00, 0}, + {198, 0x00, 0}, + {201, 0x00, 0}, + {202, 0x00, 0}, + {204, 0x00, 0}, + {205, 0x00, 0}, + {210, 0x00, 0}, + {213, 0x00, 0}, + {217, 0x00, 0}, + {220, 0x00, 0}, + {225, 0x00, 0}, + {231, 0x00, 0}, + {239, 0x00, 0}, + {246, 0x00, 0}, + }, + + { + {0, 0x03, 192}, + {0, 0x03, 193}, + {0, 0x03, 200}, + {0, 0x03, 201}, + {0, 0x03, 202}, + {0, 0x03, 205}, + {0, 0x03, 210}, + {0, 0x03, 213}, + {0, 0x03, 218}, + {0, 0x03, 219}, + {0, 0x03, 238}, + {0, 0x03, 240}, + {0, 0x03, 242}, + {0, 0x03, 243}, + {0, 0x03, 255}, + {206, 0x00, 0}, + }, + + { + {1, 0x02, 192}, + {22, 0x03, 192}, + {1, 0x02, 193}, + {22, 0x03, 193}, + {1, 0x02, 200}, + {22, 0x03, 200}, + {1, 0x02, 201}, + {22, 0x03, 201}, + {1, 0x02, 202}, + {22, 0x03, 202}, + {1, 0x02, 205}, + {22, 0x03, 205}, + {1, 0x02, 210}, + {22, 0x03, 210}, + {1, 0x02, 213}, + {22, 0x03, 213}, + }, + + { + {2, 0x02, 192}, + {9, 0x02, 192}, + {23, 0x02, 192}, + {40, 0x03, 192}, + {2, 0x02, 193}, + {9, 0x02, 193}, + {23, 0x02, 193}, + {40, 0x03, 193}, + {2, 0x02, 200}, + {9, 0x02, 200}, + {23, 0x02, 200}, + {40, 0x03, 200}, + {2, 0x02, 201}, + {9, 0x02, 201}, + {23, 0x02, 201}, + {40, 0x03, 201}, + }, + + { + {3, 0x02, 192}, + {6, 0x02, 192}, + {10, 0x02, 192}, + {15, 0x02, 192}, + {24, 0x02, 192}, + {31, 0x02, 192}, + {41, 0x02, 192}, + {56, 0x03, 192}, + {3, 0x02, 193}, + {6, 0x02, 193}, + {10, 0x02, 193}, + {15, 0x02, 193}, + {24, 0x02, 193}, + {31, 0x02, 193}, + {41, 0x02, 193}, + {56, 0x03, 193}, + }, + + { + {3, 0x02, 200}, + {6, 0x02, 200}, + {10, 0x02, 200}, + {15, 0x02, 200}, + {24, 0x02, 200}, + {31, 0x02, 200}, + {41, 0x02, 200}, + {56, 0x03, 200}, + {3, 0x02, 201}, + {6, 0x02, 201}, + {10, 0x02, 201}, + {15, 0x02, 201}, + {24, 0x02, 201}, + {31, 0x02, 201}, + {41, 0x02, 201}, + {56, 0x03, 201}, + }, + + { + {2, 0x02, 202}, + {9, 0x02, 202}, + {23, 0x02, 202}, + {40, 0x03, 202}, + {2, 0x02, 205}, + {9, 0x02, 205}, + {23, 0x02, 205}, + {40, 0x03, 205}, + {2, 0x02, 210}, + {9, 0x02, 210}, + {23, 0x02, 210}, + {40, 0x03, 210}, + {2, 0x02, 213}, + {9, 0x02, 213}, + {23, 0x02, 213}, + {40, 0x03, 213}, + }, + + { + {3, 0x02, 202}, + {6, 0x02, 202}, + {10, 0x02, 202}, + {15, 0x02, 202}, + {24, 0x02, 202}, + {31, 0x02, 202}, + {41, 0x02, 202}, + {56, 0x03, 202}, + {3, 0x02, 205}, + {6, 0x02, 205}, + {10, 0x02, 205}, + {15, 0x02, 205}, + {24, 0x02, 205}, + {31, 0x02, 205}, + {41, 0x02, 205}, + {56, 0x03, 205}, + }, + + { + {3, 0x02, 210}, + {6, 0x02, 210}, + {10, 0x02, 210}, + {15, 0x02, 210}, + {24, 0x02, 210}, + {31, 0x02, 210}, + {41, 0x02, 210}, + {56, 0x03, 210}, + {3, 0x02, 213}, + {6, 0x02, 213}, + {10, 0x02, 213}, + {15, 0x02, 213}, + {24, 0x02, 213}, + {31, 0x02, 213}, + {41, 0x02, 213}, + {56, 0x03, 213}, + }, + + { + {1, 0x02, 218}, + {22, 0x03, 218}, + {1, 0x02, 219}, + {22, 0x03, 219}, + {1, 0x02, 238}, + {22, 0x03, 238}, + {1, 0x02, 240}, + {22, 0x03, 240}, + {1, 0x02, 242}, + {22, 0x03, 242}, + {1, 0x02, 243}, + {22, 0x03, 243}, + {1, 0x02, 255}, + {22, 0x03, 255}, + {0, 0x03, 203}, + {0, 0x03, 204}, + }, + + { + {2, 0x02, 218}, + {9, 0x02, 218}, + {23, 0x02, 218}, + {40, 0x03, 218}, + {2, 0x02, 219}, + {9, 0x02, 219}, + {23, 0x02, 219}, + {40, 0x03, 219}, + {2, 0x02, 238}, + {9, 0x02, 238}, + {23, 0x02, 238}, + {40, 0x03, 238}, + {2, 0x02, 240}, + {9, 0x02, 240}, + {23, 0x02, 240}, + {40, 0x03, 240}, + }, + + { + {3, 0x02, 218}, + {6, 0x02, 218}, + {10, 0x02, 218}, + {15, 0x02, 218}, + {24, 0x02, 218}, + {31, 0x02, 218}, + {41, 0x02, 218}, + {56, 0x03, 218}, + {3, 0x02, 219}, + {6, 0x02, 219}, + {10, 0x02, 219}, + {15, 0x02, 219}, + {24, 0x02, 219}, + {31, 0x02, 219}, + {41, 0x02, 219}, + {56, 0x03, 219}, + }, + + { + {3, 0x02, 238}, + {6, 0x02, 238}, + {10, 0x02, 238}, + {15, 0x02, 238}, + {24, 0x02, 238}, + {31, 0x02, 238}, + {41, 0x02, 238}, + {56, 0x03, 238}, + {3, 0x02, 240}, + {6, 0x02, 240}, + {10, 0x02, 240}, + {15, 0x02, 240}, + {24, 0x02, 240}, + {31, 0x02, 240}, + {41, 0x02, 240}, + {56, 0x03, 240}, + }, + + { + {2, 0x02, 242}, + {9, 0x02, 242}, + {23, 0x02, 242}, + {40, 0x03, 242}, + {2, 0x02, 243}, + {9, 0x02, 243}, + {23, 0x02, 243}, + {40, 0x03, 243}, + {2, 0x02, 255}, + {9, 0x02, 255}, + {23, 0x02, 255}, + {40, 0x03, 255}, + {1, 0x02, 203}, + {22, 0x03, 203}, + {1, 0x02, 204}, + {22, 0x03, 204}, + }, + + { + {3, 0x02, 242}, + {6, 0x02, 242}, + {10, 0x02, 242}, + {15, 0x02, 242}, + {24, 0x02, 242}, + {31, 0x02, 242}, + {41, 0x02, 242}, + {56, 0x03, 242}, + {3, 0x02, 243}, + {6, 0x02, 243}, + {10, 0x02, 243}, + {15, 0x02, 243}, + {24, 0x02, 243}, + {31, 0x02, 243}, + {41, 0x02, 243}, + {56, 0x03, 243}, + }, + + { + {3, 0x02, 255}, + {6, 0x02, 255}, + {10, 0x02, 255}, + {15, 0x02, 255}, + {24, 0x02, 255}, + {31, 0x02, 255}, + {41, 0x02, 255}, + {56, 0x03, 255}, + {2, 0x02, 203}, + {9, 0x02, 203}, + {23, 0x02, 203}, + {40, 0x03, 203}, + {2, 0x02, 204}, + {9, 0x02, 204}, + {23, 0x02, 204}, + {40, 0x03, 204}, + }, + + { + {3, 0x02, 203}, + {6, 0x02, 203}, + {10, 0x02, 203}, + {15, 0x02, 203}, + {24, 0x02, 203}, + {31, 0x02, 203}, + {41, 0x02, 203}, + {56, 0x03, 203}, + {3, 0x02, 204}, + {6, 0x02, 204}, + {10, 0x02, 204}, + {15, 0x02, 204}, + {24, 0x02, 204}, + {31, 0x02, 204}, + {41, 0x02, 204}, + {56, 0x03, 204}, + }, + + { + {211, 0x00, 0}, + {212, 0x00, 0}, + {214, 0x00, 0}, + {215, 0x00, 0}, + {218, 0x00, 0}, + {219, 0x00, 0}, + {221, 0x00, 0}, + {222, 0x00, 0}, + {226, 0x00, 0}, + {228, 0x00, 0}, + {232, 0x00, 0}, + {235, 0x00, 0}, + {240, 0x00, 0}, + {243, 0x00, 0}, + {247, 0x00, 0}, + {250, 0x00, 0}, + }, + + { + {0, 0x03, 211}, + {0, 0x03, 212}, + {0, 0x03, 214}, + {0, 0x03, 221}, + {0, 0x03, 222}, + {0, 0x03, 223}, + {0, 0x03, 241}, + {0, 0x03, 244}, + {0, 0x03, 245}, + {0, 0x03, 246}, + {0, 0x03, 247}, + {0, 0x03, 248}, + {0, 0x03, 250}, + {0, 0x03, 251}, + {0, 0x03, 252}, + {0, 0x03, 253}, + }, + + { + {1, 0x02, 211}, + {22, 0x03, 211}, + {1, 0x02, 212}, + {22, 0x03, 212}, + {1, 0x02, 214}, + {22, 0x03, 214}, + {1, 0x02, 221}, + {22, 0x03, 221}, + {1, 0x02, 222}, + {22, 0x03, 222}, + {1, 0x02, 223}, + {22, 0x03, 223}, + {1, 0x02, 241}, + {22, 0x03, 241}, + {1, 0x02, 244}, + {22, 0x03, 244}, + }, + + { + {2, 0x02, 211}, + {9, 0x02, 211}, + {23, 0x02, 211}, + {40, 0x03, 211}, + {2, 0x02, 212}, + {9, 0x02, 212}, + {23, 0x02, 212}, + {40, 0x03, 212}, + {2, 0x02, 214}, + {9, 0x02, 214}, + {23, 0x02, 214}, + {40, 0x03, 214}, + {2, 0x02, 221}, + {9, 0x02, 221}, + {23, 0x02, 221}, + {40, 0x03, 221}, + }, + + { + {3, 0x02, 211}, + {6, 0x02, 211}, + {10, 0x02, 211}, + {15, 0x02, 211}, + {24, 0x02, 211}, + {31, 0x02, 211}, + {41, 0x02, 211}, + {56, 0x03, 211}, + {3, 0x02, 212}, + {6, 0x02, 212}, + {10, 0x02, 212}, + {15, 0x02, 212}, + {24, 0x02, 212}, + {31, 0x02, 212}, + {41, 0x02, 212}, + {56, 0x03, 212}, + }, + + { + {3, 0x02, 214}, + {6, 0x02, 214}, + {10, 0x02, 214}, + {15, 0x02, 214}, + {24, 0x02, 214}, + {31, 0x02, 214}, + {41, 0x02, 214}, + {56, 0x03, 214}, + {3, 0x02, 221}, + {6, 0x02, 221}, + {10, 0x02, 221}, + {15, 0x02, 221}, + {24, 0x02, 221}, + {31, 0x02, 221}, + {41, 0x02, 221}, + {56, 0x03, 221}, + }, + + { + {2, 0x02, 222}, + {9, 0x02, 222}, + {23, 0x02, 222}, + {40, 0x03, 222}, + {2, 0x02, 223}, + {9, 0x02, 223}, + {23, 0x02, 223}, + {40, 0x03, 223}, + {2, 0x02, 241}, + {9, 0x02, 241}, + {23, 0x02, 241}, + {40, 0x03, 241}, + {2, 0x02, 244}, + {9, 0x02, 244}, + {23, 0x02, 244}, + {40, 0x03, 244}, + }, + + { + {3, 0x02, 222}, + {6, 0x02, 222}, + {10, 0x02, 222}, + {15, 0x02, 222}, + {24, 0x02, 222}, + {31, 0x02, 222}, + {41, 0x02, 222}, + {56, 0x03, 222}, + {3, 0x02, 223}, + {6, 0x02, 223}, + {10, 0x02, 223}, + {15, 0x02, 223}, + {24, 0x02, 223}, + {31, 0x02, 223}, + {41, 0x02, 223}, + {56, 0x03, 223}, + }, + + { + {3, 0x02, 241}, + {6, 0x02, 241}, + {10, 0x02, 241}, + {15, 0x02, 241}, + {24, 0x02, 241}, + {31, 0x02, 241}, + {41, 0x02, 241}, + {56, 0x03, 241}, + {3, 0x02, 244}, + {6, 0x02, 244}, + {10, 0x02, 244}, + {15, 0x02, 244}, + {24, 0x02, 244}, + {31, 0x02, 244}, + {41, 0x02, 244}, + {56, 0x03, 244}, + }, + + { + {1, 0x02, 245}, + {22, 0x03, 245}, + {1, 0x02, 246}, + {22, 0x03, 246}, + {1, 0x02, 247}, + {22, 0x03, 247}, + {1, 0x02, 248}, + {22, 0x03, 248}, + {1, 0x02, 250}, + {22, 0x03, 250}, + {1, 0x02, 251}, + {22, 0x03, 251}, + {1, 0x02, 252}, + {22, 0x03, 252}, + {1, 0x02, 253}, + {22, 0x03, 253}, + }, + + { + {2, 0x02, 245}, + {9, 0x02, 245}, + {23, 0x02, 245}, + {40, 0x03, 245}, + {2, 0x02, 246}, + {9, 0x02, 246}, + {23, 0x02, 246}, + {40, 0x03, 246}, + {2, 0x02, 247}, + {9, 0x02, 247}, + {23, 0x02, 247}, + {40, 0x03, 247}, + {2, 0x02, 248}, + {9, 0x02, 248}, + {23, 0x02, 248}, + {40, 0x03, 248}, + }, + + { + {3, 0x02, 245}, + {6, 0x02, 245}, + {10, 0x02, 245}, + {15, 0x02, 245}, + {24, 0x02, 245}, + {31, 0x02, 245}, + {41, 0x02, 245}, + {56, 0x03, 245}, + {3, 0x02, 246}, + {6, 0x02, 246}, + {10, 0x02, 246}, + {15, 0x02, 246}, + {24, 0x02, 246}, + {31, 0x02, 246}, + {41, 0x02, 246}, + {56, 0x03, 246}, + }, + + { + {3, 0x02, 247}, + {6, 0x02, 247}, + {10, 0x02, 247}, + {15, 0x02, 247}, + {24, 0x02, 247}, + {31, 0x02, 247}, + {41, 0x02, 247}, + {56, 0x03, 247}, + {3, 0x02, 248}, + {6, 0x02, 248}, + {10, 0x02, 248}, + {15, 0x02, 248}, + {24, 0x02, 248}, + {31, 0x02, 248}, + {41, 0x02, 248}, + {56, 0x03, 248}, + }, + + { + {2, 0x02, 250}, + {9, 0x02, 250}, + {23, 0x02, 250}, + {40, 0x03, 250}, + {2, 0x02, 251}, + {9, 0x02, 251}, + {23, 0x02, 251}, + {40, 0x03, 251}, + {2, 0x02, 252}, + {9, 0x02, 252}, + {23, 0x02, 252}, + {40, 0x03, 252}, + {2, 0x02, 253}, + {9, 0x02, 253}, + {23, 0x02, 253}, + {40, 0x03, 253}, + }, + + { + {3, 0x02, 250}, + {6, 0x02, 250}, + {10, 0x02, 250}, + {15, 0x02, 250}, + {24, 0x02, 250}, + {31, 0x02, 250}, + {41, 0x02, 250}, + {56, 0x03, 250}, + {3, 0x02, 251}, + {6, 0x02, 251}, + {10, 0x02, 251}, + {15, 0x02, 251}, + {24, 0x02, 251}, + {31, 0x02, 251}, + {41, 0x02, 251}, + {56, 0x03, 251}, + }, + + { + {3, 0x02, 252}, + {6, 0x02, 252}, + {10, 0x02, 252}, + {15, 0x02, 252}, + {24, 0x02, 252}, + {31, 0x02, 252}, + {41, 0x02, 252}, + {56, 0x03, 252}, + {3, 0x02, 253}, + {6, 0x02, 253}, + {10, 0x02, 253}, + {15, 0x02, 253}, + {24, 0x02, 253}, + {31, 0x02, 253}, + {41, 0x02, 253}, + {56, 0x03, 253}, + }, + + { + {0, 0x03, 254}, + {227, 0x00, 0}, + {229, 0x00, 0}, + {230, 0x00, 0}, + {233, 0x00, 0}, + {234, 0x00, 0}, + {236, 0x00, 0}, + {237, 0x00, 0}, + {241, 0x00, 0}, + {242, 0x00, 0}, + {244, 0x00, 0}, + {245, 0x00, 0}, + {248, 0x00, 0}, + {249, 0x00, 0}, + {251, 0x00, 0}, + {252, 0x00, 0}, + }, + + { + {1, 0x02, 254}, + {22, 0x03, 254}, + {0, 0x03, 2}, + {0, 0x03, 3}, + {0, 0x03, 4}, + {0, 0x03, 5}, + {0, 0x03, 6}, + {0, 0x03, 7}, + {0, 0x03, 8}, + {0, 0x03, 11}, + {0, 0x03, 12}, + {0, 0x03, 14}, + {0, 0x03, 15}, + {0, 0x03, 16}, + {0, 0x03, 17}, + {0, 0x03, 18}, + }, + + { + {2, 0x02, 254}, + {9, 0x02, 254}, + {23, 0x02, 254}, + {40, 0x03, 254}, + {1, 0x02, 2}, + {22, 0x03, 2}, + {1, 0x02, 3}, + {22, 0x03, 3}, + {1, 0x02, 4}, + {22, 0x03, 4}, + {1, 0x02, 5}, + {22, 0x03, 5}, + {1, 0x02, 6}, + {22, 0x03, 6}, + {1, 0x02, 7}, + {22, 0x03, 7}, + }, + + { + {3, 0x02, 254}, + {6, 0x02, 254}, + {10, 0x02, 254}, + {15, 0x02, 254}, + {24, 0x02, 254}, + {31, 0x02, 254}, + {41, 0x02, 254}, + {56, 0x03, 254}, + {2, 0x02, 2}, + {9, 0x02, 2}, + {23, 0x02, 2}, + {40, 0x03, 2}, + {2, 0x02, 3}, + {9, 0x02, 3}, + {23, 0x02, 3}, + {40, 0x03, 3}, + }, + + { + {3, 0x02, 2}, + {6, 0x02, 2}, + {10, 0x02, 2}, + {15, 0x02, 2}, + {24, 0x02, 2}, + {31, 0x02, 2}, + {41, 0x02, 2}, + {56, 0x03, 2}, + {3, 0x02, 3}, + {6, 0x02, 3}, + {10, 0x02, 3}, + {15, 0x02, 3}, + {24, 0x02, 3}, + {31, 0x02, 3}, + {41, 0x02, 3}, + {56, 0x03, 3}, + }, + + { + {2, 0x02, 4}, + {9, 0x02, 4}, + {23, 0x02, 4}, + {40, 0x03, 4}, + {2, 0x02, 5}, + {9, 0x02, 5}, + {23, 0x02, 5}, + {40, 0x03, 5}, + {2, 0x02, 6}, + {9, 0x02, 6}, + {23, 0x02, 6}, + {40, 0x03, 6}, + {2, 0x02, 7}, + {9, 0x02, 7}, + {23, 0x02, 7}, + {40, 0x03, 7}, + }, + + { + {3, 0x02, 4}, + {6, 0x02, 4}, + {10, 0x02, 4}, + {15, 0x02, 4}, + {24, 0x02, 4}, + {31, 0x02, 4}, + {41, 0x02, 4}, + {56, 0x03, 4}, + {3, 0x02, 5}, + {6, 0x02, 5}, + {10, 0x02, 5}, + {15, 0x02, 5}, + {24, 0x02, 5}, + {31, 0x02, 5}, + {41, 0x02, 5}, + {56, 0x03, 5}, + }, + + { + {3, 0x02, 6}, + {6, 0x02, 6}, + {10, 0x02, 6}, + {15, 0x02, 6}, + {24, 0x02, 6}, + {31, 0x02, 6}, + {41, 0x02, 6}, + {56, 0x03, 6}, + {3, 0x02, 7}, + {6, 0x02, 7}, + {10, 0x02, 7}, + {15, 0x02, 7}, + {24, 0x02, 7}, + {31, 0x02, 7}, + {41, 0x02, 7}, + {56, 0x03, 7}, + }, + + { + {1, 0x02, 8}, + {22, 0x03, 8}, + {1, 0x02, 11}, + {22, 0x03, 11}, + {1, 0x02, 12}, + {22, 0x03, 12}, + {1, 0x02, 14}, + {22, 0x03, 14}, + {1, 0x02, 15}, + {22, 0x03, 15}, + {1, 0x02, 16}, + {22, 0x03, 16}, + {1, 0x02, 17}, + {22, 0x03, 17}, + {1, 0x02, 18}, + {22, 0x03, 18}, + }, + + { + {2, 0x02, 8}, + {9, 0x02, 8}, + {23, 0x02, 8}, + {40, 0x03, 8}, + {2, 0x02, 11}, + {9, 0x02, 11}, + {23, 0x02, 11}, + {40, 0x03, 11}, + {2, 0x02, 12}, + {9, 0x02, 12}, + {23, 0x02, 12}, + {40, 0x03, 12}, + {2, 0x02, 14}, + {9, 0x02, 14}, + {23, 0x02, 14}, + {40, 0x03, 14}, + }, + + { + {3, 0x02, 8}, + {6, 0x02, 8}, + {10, 0x02, 8}, + {15, 0x02, 8}, + {24, 0x02, 8}, + {31, 0x02, 8}, + {41, 0x02, 8}, + {56, 0x03, 8}, + {3, 0x02, 11}, + {6, 0x02, 11}, + {10, 0x02, 11}, + {15, 0x02, 11}, + {24, 0x02, 11}, + {31, 0x02, 11}, + {41, 0x02, 11}, + {56, 0x03, 11}, + }, + + { + {3, 0x02, 12}, + {6, 0x02, 12}, + {10, 0x02, 12}, + {15, 0x02, 12}, + {24, 0x02, 12}, + {31, 0x02, 12}, + {41, 0x02, 12}, + {56, 0x03, 12}, + {3, 0x02, 14}, + {6, 0x02, 14}, + {10, 0x02, 14}, + {15, 0x02, 14}, + {24, 0x02, 14}, + {31, 0x02, 14}, + {41, 0x02, 14}, + {56, 0x03, 14}, + }, + + { + {2, 0x02, 15}, + {9, 0x02, 15}, + {23, 0x02, 15}, + {40, 0x03, 15}, + {2, 0x02, 16}, + {9, 0x02, 16}, + {23, 0x02, 16}, + {40, 0x03, 16}, + {2, 0x02, 17}, + {9, 0x02, 17}, + {23, 0x02, 17}, + {40, 0x03, 17}, + {2, 0x02, 18}, + {9, 0x02, 18}, + {23, 0x02, 18}, + {40, 0x03, 18}, + }, + + { + {3, 0x02, 15}, + {6, 0x02, 15}, + {10, 0x02, 15}, + {15, 0x02, 15}, + {24, 0x02, 15}, + {31, 0x02, 15}, + {41, 0x02, 15}, + {56, 0x03, 15}, + {3, 0x02, 16}, + {6, 0x02, 16}, + {10, 0x02, 16}, + {15, 0x02, 16}, + {24, 0x02, 16}, + {31, 0x02, 16}, + {41, 0x02, 16}, + {56, 0x03, 16}, + }, + + { + {3, 0x02, 17}, + {6, 0x02, 17}, + {10, 0x02, 17}, + {15, 0x02, 17}, + {24, 0x02, 17}, + {31, 0x02, 17}, + {41, 0x02, 17}, + {56, 0x03, 17}, + {3, 0x02, 18}, + {6, 0x02, 18}, + {10, 0x02, 18}, + {15, 0x02, 18}, + {24, 0x02, 18}, + {31, 0x02, 18}, + {41, 0x02, 18}, + {56, 0x03, 18}, + }, + + { + {0, 0x03, 19}, + {0, 0x03, 20}, + {0, 0x03, 21}, + {0, 0x03, 23}, + {0, 0x03, 24}, + {0, 0x03, 25}, + {0, 0x03, 26}, + {0, 0x03, 27}, + {0, 0x03, 28}, + {0, 0x03, 29}, + {0, 0x03, 30}, + {0, 0x03, 31}, + {0, 0x03, 127}, + {0, 0x03, 220}, + {0, 0x03, 249}, + {253, 0x00, 0}, + }, + + { + {1, 0x02, 19}, + {22, 0x03, 19}, + {1, 0x02, 20}, + {22, 0x03, 20}, + {1, 0x02, 21}, + {22, 0x03, 21}, + {1, 0x02, 23}, + {22, 0x03, 23}, + {1, 0x02, 24}, + {22, 0x03, 24}, + {1, 0x02, 25}, + {22, 0x03, 25}, + {1, 0x02, 26}, + {22, 0x03, 26}, + {1, 0x02, 27}, + {22, 0x03, 27}, + }, + + { + {2, 0x02, 19}, + {9, 0x02, 19}, + {23, 0x02, 19}, + {40, 0x03, 19}, + {2, 0x02, 20}, + {9, 0x02, 20}, + {23, 0x02, 20}, + {40, 0x03, 20}, + {2, 0x02, 21}, + {9, 0x02, 21}, + {23, 0x02, 21}, + {40, 0x03, 21}, + {2, 0x02, 23}, + {9, 0x02, 23}, + {23, 0x02, 23}, + {40, 0x03, 23}, + }, + + { + {3, 0x02, 19}, + {6, 0x02, 19}, + {10, 0x02, 19}, + {15, 0x02, 19}, + {24, 0x02, 19}, + {31, 0x02, 19}, + {41, 0x02, 19}, + {56, 0x03, 19}, + {3, 0x02, 20}, + {6, 0x02, 20}, + {10, 0x02, 20}, + {15, 0x02, 20}, + {24, 0x02, 20}, + {31, 0x02, 20}, + {41, 0x02, 20}, + {56, 0x03, 20}, + }, + + { + {3, 0x02, 21}, + {6, 0x02, 21}, + {10, 0x02, 21}, + {15, 0x02, 21}, + {24, 0x02, 21}, + {31, 0x02, 21}, + {41, 0x02, 21}, + {56, 0x03, 21}, + {3, 0x02, 23}, + {6, 0x02, 23}, + {10, 0x02, 23}, + {15, 0x02, 23}, + {24, 0x02, 23}, + {31, 0x02, 23}, + {41, 0x02, 23}, + {56, 0x03, 23}, + }, + + { + {2, 0x02, 24}, + {9, 0x02, 24}, + {23, 0x02, 24}, + {40, 0x03, 24}, + {2, 0x02, 25}, + {9, 0x02, 25}, + {23, 0x02, 25}, + {40, 0x03, 25}, + {2, 0x02, 26}, + {9, 0x02, 26}, + {23, 0x02, 26}, + {40, 0x03, 26}, + {2, 0x02, 27}, + {9, 0x02, 27}, + {23, 0x02, 27}, + {40, 0x03, 27}, + }, + + { + {3, 0x02, 24}, + {6, 0x02, 24}, + {10, 0x02, 24}, + {15, 0x02, 24}, + {24, 0x02, 24}, + {31, 0x02, 24}, + {41, 0x02, 24}, + {56, 0x03, 24}, + {3, 0x02, 25}, + {6, 0x02, 25}, + {10, 0x02, 25}, + {15, 0x02, 25}, + {24, 0x02, 25}, + {31, 0x02, 25}, + {41, 0x02, 25}, + {56, 0x03, 25}, + }, + + { + {3, 0x02, 26}, + {6, 0x02, 26}, + {10, 0x02, 26}, + {15, 0x02, 26}, + {24, 0x02, 26}, + {31, 0x02, 26}, + {41, 0x02, 26}, + {56, 0x03, 26}, + {3, 0x02, 27}, + {6, 0x02, 27}, + {10, 0x02, 27}, + {15, 0x02, 27}, + {24, 0x02, 27}, + {31, 0x02, 27}, + {41, 0x02, 27}, + {56, 0x03, 27}, + }, + + { + {1, 0x02, 28}, + {22, 0x03, 28}, + {1, 0x02, 29}, + {22, 0x03, 29}, + {1, 0x02, 30}, + {22, 0x03, 30}, + {1, 0x02, 31}, + {22, 0x03, 31}, + {1, 0x02, 127}, + {22, 0x03, 127}, + {1, 0x02, 220}, + {22, 0x03, 220}, + {1, 0x02, 249}, + {22, 0x03, 249}, + {254, 0x00, 0}, + {255, 0x00, 0}, + }, + + { + {2, 0x02, 28}, + {9, 0x02, 28}, + {23, 0x02, 28}, + {40, 0x03, 28}, + {2, 0x02, 29}, + {9, 0x02, 29}, + {23, 0x02, 29}, + {40, 0x03, 29}, + {2, 0x02, 30}, + {9, 0x02, 30}, + {23, 0x02, 30}, + {40, 0x03, 30}, + {2, 0x02, 31}, + {9, 0x02, 31}, + {23, 0x02, 31}, + {40, 0x03, 31}, + }, + + { + {3, 0x02, 28}, + {6, 0x02, 28}, + {10, 0x02, 28}, + {15, 0x02, 28}, + {24, 0x02, 28}, + {31, 0x02, 28}, + {41, 0x02, 28}, + {56, 0x03, 28}, + {3, 0x02, 29}, + {6, 0x02, 29}, + {10, 0x02, 29}, + {15, 0x02, 29}, + {24, 0x02, 29}, + {31, 0x02, 29}, + {41, 0x02, 29}, + {56, 0x03, 29}, + }, + + { + {3, 0x02, 30}, + {6, 0x02, 30}, + {10, 0x02, 30}, + {15, 0x02, 30}, + {24, 0x02, 30}, + {31, 0x02, 30}, + {41, 0x02, 30}, + {56, 0x03, 30}, + {3, 0x02, 31}, + {6, 0x02, 31}, + {10, 0x02, 31}, + {15, 0x02, 31}, + {24, 0x02, 31}, + {31, 0x02, 31}, + {41, 0x02, 31}, + {56, 0x03, 31}, + }, + + { + {2, 0x02, 127}, + {9, 0x02, 127}, + {23, 0x02, 127}, + {40, 0x03, 127}, + {2, 0x02, 220}, + {9, 0x02, 220}, + {23, 0x02, 220}, + {40, 0x03, 220}, + {2, 0x02, 249}, + {9, 0x02, 249}, + {23, 0x02, 249}, + {40, 0x03, 249}, + {0, 0x03, 10}, + {0, 0x03, 13}, + {0, 0x03, 22}, + {0, 0x04, 0}, + }, + + { + {3, 0x02, 127}, + {6, 0x02, 127}, + {10, 0x02, 127}, + {15, 0x02, 127}, + {24, 0x02, 127}, + {31, 0x02, 127}, + {41, 0x02, 127}, + {56, 0x03, 127}, + {3, 0x02, 220}, + {6, 0x02, 220}, + {10, 0x02, 220}, + {15, 0x02, 220}, + {24, 0x02, 220}, + {31, 0x02, 220}, + {41, 0x02, 220}, + {56, 0x03, 220}, + }, + + { + {3, 0x02, 249}, + {6, 0x02, 249}, + {10, 0x02, 249}, + {15, 0x02, 249}, + {24, 0x02, 249}, + {31, 0x02, 249}, + {41, 0x02, 249}, + {56, 0x03, 249}, + {1, 0x02, 10}, + {22, 0x03, 10}, + {1, 0x02, 13}, + {22, 0x03, 13}, + {1, 0x02, 22}, + {22, 0x03, 22}, + {0, 0x04, 0}, + {0, 0x04, 0}, + }, + + { + {2, 0x02, 10}, + {9, 0x02, 10}, + {23, 0x02, 10}, + {40, 0x03, 10}, + {2, 0x02, 13}, + {9, 0x02, 13}, + {23, 0x02, 13}, + {40, 0x03, 13}, + {2, 0x02, 22}, + {9, 0x02, 22}, + {23, 0x02, 22}, + {40, 0x03, 22}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + }, + + { + {3, 0x02, 10}, + {6, 0x02, 10}, + {10, 0x02, 10}, + {15, 0x02, 10}, + {24, 0x02, 10}, + {31, 0x02, 10}, + {41, 0x02, 10}, + {56, 0x03, 10}, + {3, 0x02, 13}, + {6, 0x02, 13}, + {10, 0x02, 13}, + {15, 0x02, 13}, + {24, 0x02, 13}, + {31, 0x02, 13}, + {41, 0x02, 13}, + {56, 0x03, 13}, + }, + + { + {3, 0x02, 22}, + {6, 0x02, 22}, + {10, 0x02, 22}, + {15, 0x02, 22}, + {24, 0x02, 22}, + {31, 0x02, 22}, + {41, 0x02, 22}, + {56, 0x03, 22}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + }, + }; + + static const std::pair staticTable[] { + {":authority", ""}, + {":method", "GET"}, + {":method", "POST"}, + {":path", "/"}, + {":path", "/index.html"}, + {":scheme", "http"}, + {":scheme", "https"}, + {":status", "200"}, + {":status", "204"}, + {":status", "206"}, + {":status", "304"}, + {":status", "400"}, + {":status", "404"}, + {":status", "500"}, + {"accept-charset", ""}, + {"accept-encoding", "gzip, deflate"}, + {"accept-language", ""}, + {"accept-ranges", ""}, + {"accept", ""}, + {"access-control-allow-origin", ""}, + {"age", ""}, + {"allow", ""}, + {"authorization", ""}, + {"cache-control", ""}, + {"content-disposition", ""}, + {"content-encoding", ""}, + {"content-language", ""}, + {"content-length", ""}, + {"content-location", ""}, + {"content-range", ""}, + {"content-type", ""}, + {"cookie", ""}, + {"date", ""}, + {"etag", ""}, + {"expect", ""}, + {"expires", ""}, + {"from", ""}, + {"host", ""}, + {"if-match", ""}, + {"if-modified-since", ""}, + {"if-none-match", ""}, + {"if-range", ""}, + {"if-unmodified-since", ""}, + {"last-modified", ""}, + {"link", ""}, + {"location", ""}, + {"max-forwards", ""}, + {"proxy-authenticate", ""}, + {"proxy-authorization", ""}, + {"range", ""}, + {"referer", ""}, + {"refresh", ""}, + {"retry-after", ""}, + {"server", ""}, + {"set-cookie", ""}, + {"strict-transport-security", ""}, + {"transfer-encoding", ""}, + {"user-agent", ""}, + {"vary", ""}, + {"via", ""}, + {"www-authenticate", ""}, + }; + + static constexpr size_t getStaticTableSize() noexcept { + return sizeof(staticTable) / sizeof(*staticTable); + } + + static size_t huffmanEncodeLength( + const void *dest, + const size_t length + ) noexcept { + const uint8_t *data = reinterpret_cast(dest); + + size_t n = 0; + + for (size_t i = 0; i < length; ++i) { + n += size_t(huffmanSymbolTable[data[i] ].nbits); + } + + return (n + 7) / 8; + } + + static uint8_t huffmanEncodeSymbol( + std::vector &dest, + uint8_t rembits, + const HuffmanSymbol &sym + ) { + uint8_t nbits = uint8_t(sym.nbits); + + for (;;) { + if (rembits > nbits) { + dest.back() |= uint8_t(sym.code << (rembits - nbits) ); + rembits -= nbits; + break; + } + + dest.back() |= uint8_t(sym.code >> (nbits - rembits) ); + + nbits -= rembits; + rembits = 8; + + if (0 == nbits) { + break; + } + + dest.emplace_back(0); + } + + return rembits; + } + + static void encode( + std::vector &dest, + const void* src, + const size_t srcSize + ) { + const uint8_t *data = reinterpret_cast(src); + + uint8_t rembits = 8; + + for (size_t i = 0; i < srcSize; ++i) { + const HuffmanSymbol &sym = huffmanSymbolTable[data[i] ]; + + if (8 == rembits) { + dest.emplace_back(0); + } + + rembits = huffmanEncodeSymbol(dest, rembits, sym); + } + + if (rembits < 8) { + const HuffmanSymbol &sym = huffmanSymbolTable[256]; + dest.back() |= uint8_t(sym.code >> (sym.nbits - rembits) ); + } + } + + enum HuffmanDecode { + ACCEPT = 0x1, + SYMBOL = 0x2, + FAIL = 0x4 + }; + + static bool decode( + std::vector &dest, + const void* src, + const size_t srcSize + ) { + const uint8_t *data = reinterpret_cast(src); + const uint8_t *end = data + srcSize; + + uint8_t state = 0; + bool accept = true; + + for (; data < end; ++data) + { + uint8_t c = *data; + uint8_t x = c >> 4; + + for (auto i = 0; i < 2; ++i) + { + const HuffmanDecodeNode &node = huffmanDecodeTable[state][x]; + + if (node.flags & HuffmanDecode::FAIL) { + return false; + } + + if (node.flags & HuffmanDecode::SYMBOL) { + dest.emplace_back(node.symbol); + } + + state = node.state; + accept = (node.flags & HuffmanDecode::ACCEPT); + + x = c & 0xf; + } + } + + return accept; + } + + static void packInteger( + std::vector &dest, + uint64_t num, + const uint8_t prefix + ) { + const uint64_t k = (1 << prefix) - 1; + + if (num < k) { + dest.emplace_back(num); + return; + } + + dest.emplace_back(k); + + num -= k; + + for (;;) { + if (num < 128) { + dest.emplace_back(num); + break; + } + + dest.emplace_back(0x80 | (num & 0x7f) ); + + num >>= 7; + + if (0 == num) { + break; + } + } + } + + static void packIndex( + std::vector &dest, + const size_t index + ) { + const size_t head = dest.size(); + packInteger(dest, index, 7); + dest[head] |= 0x80; + } + + static std::tuple findHeaderInTable( + const std::pair &header, + const Http2::DynamicTable &dynamicTable + ) { + std::tuple result { + 0, false + }; + + size_t &index = std::get(result); + bool &is_full_match = std::get(result); + + for (size_t i = 0; i < getStaticTableSize(); ++i) { + auto const &pair = staticTable[i]; + + if (pair.first == header.first) { + index = i + 1; + + if (pair.second == header.second) { + is_full_match = true; + return result; + } + } + else if (0 != index) { + break; + } + } + + for (size_t i = 0; i < dynamicTable.size(); ++i) { + auto const &pair = dynamicTable[i]; + + if (pair.first == header.first) { + index = i + getStaticTableSize() + 1; + + if (pair.second == header.second) { + is_full_match = true; + break; + } + } + } + + return result; + } + + static uint8_t packFirstByte(const bool indexing) noexcept + { + if (indexing) { + return 0x40; + } + + /* if (never_indexing) { + return 0x10; + }*/ + + return 0; + } + + static void packString(std::vector &dest, const std::string &str) + { + const size_t huffman_length = huffmanEncodeLength(str.data(), str.length() ); + + if (huffman_length < str.length() ) { + const size_t head = dest.size(); + + packInteger(dest, huffman_length, 7); + encode(dest, str.data(), str.length() ); + + dest[head] |= 0x80; + } else { + packInteger(dest, str.length(), 7); + + dest.insert( + dest.end(), + str.cbegin(), + str.cend() + ); + } + } + + static void packFullHeader( + std::vector &dest, + const std::pair &header, + const bool indexing + ) { + dest.emplace_back(packFirstByte(indexing) ); + packString(dest, header.first); + packString(dest, header.second); + } + + static void packHeaderValue( + std::vector &dest, + const size_t keyIndex, + const std::pair &header, + const bool indexing + ) { + const size_t head = dest.size(); + + const uint8_t prefix = indexing ? 6 : 4; + + packInteger(dest, keyIndex, prefix); + + dest[head] |= packFirstByte(indexing); + + packString(dest, header.second); + } + + static bool shouldIndexing(const std::pair &header) + { + /* const std::string &key = header.first; + + if ("content-length" == key || "set-cookie" == key) { + return true; + }*/ + + return false; + } + + static void packHeader( + std::vector &dest, + const std::pair &header, + Http2::DynamicTable &dynamicTable + ) { + size_t index; + bool is_full_match; + + std::tie(index, is_full_match) = findHeaderInTable(header, dynamicTable); + + if (is_full_match) { + packIndex(dest, index); + return; + } + + const bool indexing = shouldIndexing(header); + + if (indexing) { + dynamicTable.addHeader(header); + } + + if (0 == index) { + packFullHeader(dest, header, indexing); + } else { + packHeaderValue(dest, index, header, indexing); + } + } +/* + static void packTableSize(std::vector &dest, const size_t tableSize) + { + const size_t head = dest.size(); + + packInteger(dest, tableSize, 5); + + dest[head] |= 0x20; + } +*/ + void pack( + std::vector &dest, + const std::vector > &headers, + Http2::DynamicTable &dynamicTable + ) { + for (auto const &header : headers) { + packHeader(dest, header, dynamicTable); + } + } + + enum class HuffmanDecodeOpcode { + NONE, + INDEXED, + NOT_INDEXED, + INDEXED_KEY, + }; + + enum class HuffmanDecodeState { + OPCODE, + READ_TABLE_SIZE, + READ_INDEX, + CHECK_KEY_LENGTH, + READ_KEY_LENGTH, + READ_KEY_HUFFMAN, + READ_KEY, + CHECK_VALUE_LENGTH, + READ_VALUE_LENGTH, + READ_VALUE_HUFFMAN, + READ_VALUE, + }; + + static size_t getMaxTableIndex(const Http2::IncStream &stream) noexcept { + return getStaticTableSize() + stream.conn.decoding_dynamic_table.size(); + } + + static const std::pair *getHeaderFromTable( + const size_t index, + const Http2::IncStream &stream + ) noexcept { + if (getStaticTableSize() >= index) { + return &staticTable[index - 1]; + } + else if (stream.conn.decoding_dynamic_table.size() + getStaticTableSize() >= index) { + return &stream.conn.decoding_dynamic_table[index - getStaticTableSize() - 1]; + } + + return nullptr; + } + + static const std::pair *unpackIndexed( + const size_t index, + const Http2::IncStream &stream + ) noexcept { + return getHeaderFromTable(index, stream); + } + + static bool checkHuffmanEncoded(const uint8_t c) noexcept { + return c & (1 << 7); + } + + static std::tuple unpackHuffman( + std::vector &dest, + const uint8_t *data, + const size_t dataSize, + const uint64_t left + ) { + std::tuple result { + left, false + }; + + if (dataSize < left) { + std::get(result) = dataSize; + } + + std::get(result) = decode( + dest, + data, + std::get(result) + ); + + return result; + } + + static uint64_t unpackString( + std::vector &dest, + const uint8_t *data, + const size_t dataSize, + uint64_t left + ) { + if (dataSize < left) { + left = dataSize; + } + + dest.insert( + dest.end(), + data, + data + left + ); + + return left; + } + + static std::tuple + unpackInteger( + const uint8_t *data, + const size_t dataSize, + const uint64_t initial, + const uint8_t initialShift, + const uint8_t prefix + ) { + std::tuple result { + initial, 0, initialShift, true + }; + + uint64_t &num = std::get<0>(result); + uint64_t &nread = std::get<1>(result); + uint8_t &shift = std::get(result); + + const uint8_t k = uint8_t(1 << prefix) - 1; + + if (0 == num) + { + const uint8_t c = data[0]; + + ++nread; + + if (k != (c & k) ) { + num = c & k; + return result; + } + + num = k; + + if (nread == dataSize) { + return result; + } + } + + for (; nread < dataSize; ++nread) + { + const uint8_t c = data[nread]; + + uint32_t add = c & 0x7f; + + if (add > (std::numeric_limits::max() >> shift) ) { + std::get(result) = false; + return result; + } + + add <<= shift; + + if (std::numeric_limits::max() - add < num) { + std::get(result) = false; + return result; + } + + num += add; + + if (0 == (c & (1 << 7) ) ) { + break; + } + + shift += 7; + } + + if (nread != dataSize) { + ++nread; + } + + return result; + } + + class HuffmanDecoder + { + public: + std::vector buf; + + size_t key_index = 0; + size_t key_length = 0; + + HuffmanDecodeOpcode opcode = HuffmanDecodeOpcode::NONE; + HuffmanDecodeState state = HuffmanDecodeState::OPCODE; + + uint64_t left = 0; + uint8_t shift = 0; + + bool index_required = false; + bool never_indexed = false; + bool huffman_encoded = false; + + public: + std::pair unpackFullHeader(Http2::IncStream &stream) + { + std::pair header { + std::string(this->buf.cbegin(), this->buf.cbegin() + long(this->key_length)), + std::string(this->buf.cbegin() + long(this->key_length), this->buf.cend() ) + }; + + this->buf.clear(); + + if (this->index_required) { + stream.conn.decoding_dynamic_table.addHeader(header); + } + + return header; + } + + std::pair unpackHeaderValue(Http2::IncStream &stream) + { + auto entry = getHeaderFromTable(this->key_index, stream); + + if (nullptr == entry) { + return std::pair(); + } + + std::pair header { + entry->first, + std::string(this->buf.cbegin(), this->buf.cend() ) + }; + + this->key_index = 0; + this->buf.clear(); + + if (this->index_required) { + stream.conn.decoding_dynamic_table.addHeader(header); + } + + return header; + } + }; + + bool unpack( + const void *src, + const size_t srcSize, + Http2::IncStream &stream + ) { + const uint8_t *data = reinterpret_cast(src); + + size_t cur = 0; + + HuffmanDecoder dec; + + while (cur < srcSize) + { + switch (dec.state) + { + case HuffmanDecodeState::OPCODE: + { + const uint8_t c = data[cur]; + + if (0x20 == (c & 0xe0) ) { + dec.opcode = HuffmanDecodeOpcode::INDEXED; + dec.state = HuffmanDecodeState::READ_TABLE_SIZE; + } + else if (c & 0x80) { + dec.opcode = HuffmanDecodeOpcode::INDEXED; + dec.state = HuffmanDecodeState::READ_INDEX; + } else { + if (0 == c || 0x40 == c || 0x10 == c) { + dec.opcode = HuffmanDecodeOpcode::NOT_INDEXED; + dec.state = HuffmanDecodeState::CHECK_KEY_LENGTH; + + ++cur; + } else { + dec.opcode = HuffmanDecodeOpcode::INDEXED_KEY; + dec.state = HuffmanDecodeState::READ_INDEX; + } + + dec.index_required = (c & 0x40); + dec.never_indexed = (c & 0xf0) == 0x10; + } + + dec.left = dec.shift = 0; + + break; + } + + case HuffmanDecodeState::READ_TABLE_SIZE: + { + uint64_t nread; + bool success; + + std::tie(dec.left, nread, dec.shift, success) = unpackInteger( + data + cur, + srcSize - cur, + dec.left, + dec.shift, + 5 + ); + + cur += nread; + + if (false == success) { + return false; + } + + if (dec.left > stream.conn.client_settings.header_table_size) { + // TODO: invalid max table size error code + return false; + } + + stream.conn.decoding_dynamic_table.changeHeaderTableSize( + static_cast(dec.left) + ); + + dec.state = HuffmanDecodeState::OPCODE; + + break; + } + + case HuffmanDecodeState::READ_INDEX: + { + uint8_t prefixlen; + + if (HuffmanDecodeOpcode::INDEXED == dec.opcode) { + prefixlen = 7; + } + else if (dec.index_required) { + prefixlen = 6; + } else { + prefixlen = 4; + } + + uint64_t nread; + bool success; + + std::tie(dec.left, nread, dec.shift, success) = unpackInteger( + data + cur, + srcSize - cur, + dec.left, + dec.shift, + prefixlen + ); + + cur += nread; + + if (false == success || 0 == dec.left || dec.left > getMaxTableIndex(stream) ) { + return false; + } + + if (HuffmanDecodeOpcode::INDEXED == dec.opcode) { + stream.incoming_headers.emplace(*unpackIndexed(dec.left, stream) ); + dec.state = HuffmanDecodeState::OPCODE; + } else { + dec.key_index = dec.left; + dec.state = HuffmanDecodeState::CHECK_VALUE_LENGTH; + } + + break; + } + + case HuffmanDecodeState::CHECK_KEY_LENGTH: + { + dec.huffman_encoded = checkHuffmanEncoded(data[cur]); + dec.state = HuffmanDecodeState::READ_KEY_LENGTH; + dec.left = dec.shift = 0; + break; + } + + case HuffmanDecodeState::READ_KEY_LENGTH: + { + uint64_t nread; + bool success; + + std::tie(dec.left, nread, dec.shift, success) = unpackInteger( + data + cur, + srcSize - cur, + dec.left, + dec.shift, + 7 + ); + + cur += nread; + + if (false == success) { + return false; + } + + dec.state = dec.huffman_encoded + ? HuffmanDecodeState::READ_KEY_HUFFMAN + : HuffmanDecodeState::READ_KEY; + + break; + } + + case HuffmanDecodeState::READ_KEY_HUFFMAN: + { + uint64_t nread; + bool success; + + std::tie(nread, success) = unpackHuffman( + dec.buf, + data + cur, + srcSize - cur, + dec.left + ); + + cur += nread; + dec.left -= nread; + + if (false == success || dec.left > 0) { + return false; + } + + dec.key_length = dec.buf.size(); + + dec.state = HuffmanDecodeState::CHECK_VALUE_LENGTH; + + break; + } + + case HuffmanDecodeState::READ_KEY: + { + const uint64_t nread = unpackString( + dec.buf, + data + cur, + srcSize - cur, + dec.left + ); + + cur += nread; + dec.left -= nread; + + if (dec.left > 0) { + return false; + } + + dec.key_length = dec.buf.size(); + + dec.state = HuffmanDecodeState::CHECK_VALUE_LENGTH; + + break; + } + + case HuffmanDecodeState::CHECK_VALUE_LENGTH: + { + dec.huffman_encoded = checkHuffmanEncoded(data[cur]); + dec.state = HuffmanDecodeState::READ_VALUE_LENGTH; + dec.left = dec.shift = 0; + break; + } + + case HuffmanDecodeState::READ_VALUE_LENGTH: + { + uint64_t nread; + bool success; + + std::tie(dec.left, nread, dec.shift, success) = unpackInteger( + data + cur, + srcSize - cur, + dec.left, + dec.shift, + 7 + ); + + cur += nread; + + if (false == success) { + return false; + } + + if (0 == dec.left) + { + HuffmanDecodeOpcode::NOT_INDEXED == dec.opcode + ? stream.incoming_headers.emplace(dec.unpackFullHeader(stream) ) + : stream.incoming_headers.emplace(dec.unpackHeaderValue(stream) ); + + dec.state = HuffmanDecodeState::OPCODE; + + break; + } + + dec.state = dec.huffman_encoded + ? HuffmanDecodeState::READ_VALUE_HUFFMAN + : HuffmanDecodeState::READ_VALUE; + + break; + } + + case HuffmanDecodeState::READ_VALUE_HUFFMAN: + { + uint64_t nread; + bool success; + + std::tie(nread, success) = unpackHuffman( + dec.buf, + data + cur, + srcSize - cur, + dec.left + ); + + cur += nread; + dec.left -= nread; + + if (false == success || dec.left > 0) { + return false; + } + + HuffmanDecodeOpcode::NOT_INDEXED == dec.opcode + ? stream.incoming_headers.emplace(dec.unpackFullHeader(stream) ) + : stream.incoming_headers.emplace(dec.unpackHeaderValue(stream) ); + + dec.state = HuffmanDecodeState::OPCODE; + + break; + } + + case HuffmanDecodeState::READ_VALUE: + { + const uint64_t nread = unpackString( + dec.buf, + data + cur, + srcSize - cur, + dec.left + ); + + cur += nread; + dec.left -= nread; + + if (dec.left > 0) { + return false; + } + + HuffmanDecodeOpcode::NOT_INDEXED == dec.opcode + ? stream.incoming_headers.emplace(dec.unpackFullHeader(stream) ) + : stream.incoming_headers.emplace(dec.unpackHeaderValue(stream) ); + + dec.state = HuffmanDecodeState::OPCODE; + + break; + } + } + } + + return true; + } +} diff --git a/src/transfer/http2/HPack.h b/src/transfer/http2/HPack.h new file mode 100644 index 0000000..c030a7f --- /dev/null +++ b/src/transfer/http2/HPack.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Http2.h" + +#include +#include +#include + +namespace HPack +{ + void pack( + std::vector &dest, + const std::vector > &headers, + Http2::DynamicTable &dynamicTable + ); + + // TODO: replace IncStream to DynamicTable if possible + bool unpack( + const void *src, + const size_t srcSize, + Http2::IncStream &stream + ); +} diff --git a/src/transfer/http2/Http2.cpp b/src/transfer/http2/Http2.cpp new file mode 100644 index 0000000..71c60c9 --- /dev/null +++ b/src/transfer/http2/Http2.cpp @@ -0,0 +1,245 @@ + +#include "Http2.h" +#include "../../utils/Utils.h" + +namespace Http2 +{ + bool operator &( + const FrameFlag left, + const FrameFlag right + ) noexcept { + return static_cast(left) & static_cast(right); + } + + FrameFlag operator |( + const FrameFlag left, + const FrameFlag right + ) noexcept { + return static_cast( + static_cast(left) | static_cast(right) + ); + } + + FrameFlag operator |=( + FrameFlag &left, + const FrameFlag right + ) noexcept { + return static_cast( + *reinterpret_cast(&left) |= static_cast(right) + ); + } + + ConnectionSettings ConnectionSettings::defaultSettings() noexcept { + return ConnectionSettings { + 4096, + 1, + 0, + (1 << 16) - 1, // 65535 + 1 << 14, // 16384 + 0 + }; + } + + DynamicTable::DynamicTable() noexcept + : header_table_size(0), + max_header_list_size(0), + cur_header_list_size(0) + { + + } + + size_t DynamicTable::size() const noexcept { + return this->list.size(); + } + + DynamicTable::DynamicTable( + const uint32_t headerTableSize, + const uint32_t maxHeaderListSize, + std::deque > &&list + ) noexcept + : list(std::move(list) ), + header_table_size(headerTableSize), + max_header_list_size(maxHeaderListSize), + cur_header_list_size(0) + { + for (auto const &pair : list) { + this->cur_header_list_size += pair.first.length() + pair.second.length(); + } + } + + void DynamicTable::addHeader(const std::pair &header) + { + this->cur_header_list_size += header.first.length() + header.second.length(); + + this->list.emplace_front(header); + + while ( + this->list.size() > this->header_table_size || ( + this->max_header_list_size != 0 && + this->cur_header_list_size > this->max_header_list_size + ) + ) { + auto const &pair = this->list.back(); + + this->cur_header_list_size -= pair.first.length() + pair.second.length(); + + this->list.pop_back(); + } + } + + void DynamicTable::addHeader(std::pair &&header) + { + this->cur_header_list_size += header.first.length() + header.second.length(); + + this->list.emplace_front(std::move(header) ); + + while ( + this->list.size() > this->header_table_size || ( + this->max_header_list_size != 0 && + this->cur_header_list_size > this->max_header_list_size + ) + ) { + auto const &pair = this->list.back(); + + this->cur_header_list_size -= pair.first.length() + pair.second.length(); + + this->list.pop_back(); + } + } + + void DynamicTable::changeHeaderTableSize(const uint32_t headerTableSize) + { + this->header_table_size = headerTableSize; + + while (this->list.size() > this->header_table_size) { + auto const &pair = this->list.back(); + + this->cur_header_list_size -= pair.first.length() + pair.second.length(); + + this->list.pop_back(); + } + } + + void DynamicTable::changeMaxHeaderListSize(const uint32_t maxHeaderListSize) + { + this->max_header_list_size = maxHeaderListSize; + + while ( + this->max_header_list_size != 0 && + this->cur_header_list_size > this->max_header_list_size + ) { + auto const &pair = this->list.back(); + + this->cur_header_list_size -= pair.first.length() + pair.second.length(); + + this->list.pop_back(); + } + } + + const std::pair & + DynamicTable::operator[](const size_t index) const noexcept { + return this->list[index]; + } + + std::pair & + DynamicTable::operator[](const size_t index) noexcept { + return this->list[index]; + } + + const std::deque > &DynamicTable::getList() const noexcept { + return this->list; + } + + IncStream::IncStream(const uint32_t streamId, ConnectionData &conn) noexcept + : conn(conn), + window_size_inc(int32_t(conn.server_settings.initial_window_size)), + window_size_out(int32_t(conn.client_settings.initial_window_size)), + stream_id(streamId), + state(StreamState::IDLE), + priority(0), + reserved(nullptr) + { + + } + + uint8_t *IncStream::setHttp2FrameHeader( + uint8_t *addr, + const uint32_t frameSize, + const Http2::FrameType frameType, + const Http2::FrameFlag frameFlags + ) noexcept { + Utils::hton24(addr, frameSize); + *(addr + 3) = static_cast(frameType); + *(addr + 4) = static_cast(frameFlags); + *reinterpret_cast(addr + 5) = ::htonl(this->stream_id); + + return (addr + Http2::FRAME_HEADER_SIZE); + } + + void IncStream::lock() { + this->conn.sync.mtx.lock(); + } + + void IncStream::unlock() noexcept { + this->conn.sync.mtx.unlock(); + } + + void IncStream::close() noexcept { + this->incoming_headers.clear(); + this->incoming_data.clear(); + this->incoming_files.clear(); + this->reserved = nullptr; + + ++this->conn.sync.completed; + this->conn.sync.event.notify(); + + // this->state = StreamState::CLOSED; + } + + OutStream::OutStream( + const uint32_t streamId, + const ConnectionSettings &settings, + DynamicTable &&dynamic_table, + std::mutex *mtx + ) noexcept + : stream_id(streamId), + window_size_out(int32_t(settings.initial_window_size)), + settings(settings), + dynamic_table(std::move(dynamic_table) ), + mtx(mtx) + { + + } + + OutStream::OutStream(const IncStream &stream) + : stream_id(stream.stream_id), + window_size_out(stream.window_size_out), + settings(stream.conn.client_settings), + dynamic_table(stream.conn.encoding_dynamic_table), + mtx(&stream.conn.sync.mtx) + { + + } + + uint8_t *OutStream::setHttp2FrameHeader( + uint8_t *addr, + const uint32_t frameSize, + const Http2::FrameType frameType, + const Http2::FrameFlag frameFlags + ) noexcept { + Utils::hton24(addr, frameSize); + *(addr + 3) = static_cast(frameType); + *(addr + 4) = static_cast(frameFlags); + *reinterpret_cast(addr + 5) = ::htonl(this->stream_id); + + return (addr + Http2::FRAME_HEADER_SIZE); + } + + void OutStream::lock() { + this->mtx->lock(); + } + + void OutStream::unlock() noexcept { + this->mtx->unlock(); + } +} diff --git a/src/transfer/http2/Http2.h b/src/transfer/http2/Http2.h new file mode 100644 index 0000000..f4927f2 --- /dev/null +++ b/src/transfer/http2/Http2.h @@ -0,0 +1,220 @@ +#pragma once + +#include "../../utils/Event.h" +#include "../AppRequest.h" + +#include +#include +#include +#include +#include + +#ifdef WIN32 + #undef NO_ERROR +#elif POSIX + #include +#endif + +namespace Http2 +{ + enum class ErrorCode : uint32_t { + NO_ERROR = 0x0, + PROTOCOL_ERROR, + INTERNAL_ERROR, + FLOW_CONTROL_ERROR, + SETTINGS_TIMEOUT, + STREAM_CLOSED, + FRAME_SIZE_ERROR, + REFUSED_STREAM, + CANCEL, + COMPRESSION_ERROR, + CONNECT_ERROR, + ENHANCE_YOUR_CALM, + INADEQUATE_SECURITY, + HTTP_1_1_REQUIRED + }; + + enum class FrameType : uint8_t { + DATA = 0x0, + HEADERS, + PRIORITY, + RST_STREAM, + SETTINGS, + PUSH_PROMISE, + PING, + GOAWAY, + WINDOW_UPDATE, + CONTINUATION + }; + + enum class FrameFlag : uint8_t { + EMPTY = 0x0, + ACK = 0x1, + END_STREAM = 0x1, + END_HEADERS = 0x4, + PADDED = 0x8, + PRIORITY = 0x20 + }; + + bool operator &(const FrameFlag left, const FrameFlag right) noexcept; + FrameFlag operator |(const FrameFlag left, const FrameFlag right) noexcept; + FrameFlag operator |=(FrameFlag &left, const FrameFlag right) noexcept; + + struct FrameMeta { + uint32_t stream_id; + uint32_t length; + FrameType type; + FrameFlag flags; + }; + + constexpr uint32_t FRAME_HEADER_SIZE = 9; + constexpr uint32_t MAX_WINDOW_UPDATE = (uint32_t(1) << 31) - 1; + + enum class ConnectionSetting : uint16_t { + SETTINGS_HEADER_TABLE_SIZE = 0x1, + SETTINGS_ENABLE_PUSH = 0x2, + SETTINGS_MAX_CONCURRENT_STREAMS = 0x3, + SETTINGS_INITIAL_WINDOW_SIZE = 0x4, + SETTINGS_MAX_FRAME_SIZE = 0x5, + SETTINGS_MAX_HEADER_LIST_SIZE = 0x6 + }; + + struct ConnectionSettings { + uint32_t header_table_size; + uint32_t enable_push; + uint32_t max_concurrent_streams; + uint32_t initial_window_size; + uint32_t max_frame_size; + uint32_t max_header_list_size; + + static ConnectionSettings defaultSettings() noexcept; + }; + + enum class StreamState : uint8_t { + IDLE, + RESERVED, + OPEN, + HALF_CLOSED, + CLOSED + }; + + struct ConnectionSync { + Utils::Event event; + std::mutex mtx; + std::atomic completed {}; + }; + + class DynamicTable + { + private: + std::deque > list; + + uint32_t header_table_size; + uint32_t max_header_list_size; + + uint32_t cur_header_list_size; + + public: + DynamicTable() noexcept; + + DynamicTable( + const uint32_t headerTableSize, + const uint32_t maxHeaderListSize, + std::deque > &&list + ) noexcept; + + size_t size() const noexcept; + + void addHeader(const std::pair &header); + void addHeader(std::pair &&header); + + void changeHeaderTableSize(const uint32_t headerTableSize); + void changeMaxHeaderListSize(const uint32_t maxHeaderListSize); + + const std::pair & + operator[](const size_t index) const noexcept; + + std::pair & + operator[](const size_t index) noexcept; + + const std::deque > & + getList() const noexcept; + }; + + struct ConnectionData { + DynamicTable decoding_dynamic_table; + DynamicTable encoding_dynamic_table; + + ConnectionSettings client_settings; + ConnectionSettings server_settings; + + ConnectionSync sync; + }; + + class IncStream : public Transfer::request_data + { + public: + ConnectionData &conn; + + int32_t window_size_inc; + int32_t window_size_out; + + uint32_t stream_id; + + StreamState state; + uint8_t priority; + + FrameType frame_type; + + void *reserved; + + public: + IncStream( + const uint32_t streamId, + ConnectionData &conn + ) noexcept; + + uint8_t *setHttp2FrameHeader( + uint8_t *addr, + const uint32_t frameSize, + const Http2::FrameType frameType, + const Http2::FrameFlag frameFlags + ) noexcept; + + void lock(); + void unlock() noexcept; + + void close() noexcept; + }; + + struct OutStream + { + uint32_t stream_id; + int32_t window_size_out; + + ConnectionSettings settings; + DynamicTable dynamic_table; + + std::mutex *mtx; + + public: + OutStream( + const uint32_t streamId, + const ConnectionSettings &settings, + DynamicTable &&dynamic_table, + std::mutex *mtx + ) noexcept; + + OutStream(const IncStream &stream); + + uint8_t *setHttp2FrameHeader( + uint8_t *addr, + const uint32_t frameSize, + const Http2::FrameType frameType, + const Http2::FrameFlag frameFlags + ) noexcept; + + void lock(); + void unlock() noexcept; + }; +} diff --git a/src/utils/Event.cpp b/src/utils/Event.cpp new file mode 100644 index 0000000..3c9fd01 --- /dev/null +++ b/src/utils/Event.cpp @@ -0,0 +1,91 @@ + +#include "Event.h" + +namespace Utils +{ + Event::Event(const bool signaled, const bool manually) noexcept + : signaled(signaled), manually(manually) + { + + } + + void Event::wait() + { + if (this->signaled.load() == false) { + std::unique_lock lck(this->mtx); + + do { + this->cv.wait(lck); + } + while (this->signaled.load() == false); + } + + if (false == this->manually) { + this->signaled.store(false); + } + } + + bool Event::wait_for(const std::chrono::milliseconds &ms) + { + bool is_timeout = false; + + if (this->signaled.load() == false) { + std::unique_lock lck(this->mtx); + + is_timeout = false == this->cv.wait_for(lck, ms, [this] { + return this->notifed(); + }); + } + + if (false == this->manually) { + this->signaled.store(false); + } + + return is_timeout; + } + + bool Event::wait_until( + const std::chrono::high_resolution_clock::time_point &tp + ) { + bool is_timeout = false; + + if (this->signaled.load() == false) { + std::unique_lock lck(this->mtx); + + do { + if (std::cv_status::timeout == this->cv.wait_until(lck, tp) ) { + is_timeout = true; + break; + } + } + while (this->signaled.load() == false); + } + + if (false == this->manually) { + this->signaled.store(false); + } + + return is_timeout; + } + + void Event::notify() noexcept { + this->signaled.store(true); + this->cv.notify_all(); + } + + void Event::notify(const size_t threadsCount) noexcept { + this->signaled.store(true); + + for (size_t i = 0; i < threadsCount; ++i) { + this->cv.notify_one(); + } + } + + void Event::reset() noexcept { + this->signaled.store(false); + } + + bool Event::notifed() const noexcept { + return this->signaled.load(); + } +} diff --git a/src/Event.h b/src/utils/Event.h similarity index 59% rename from src/Event.h rename to src/utils/Event.h index c9f1174..b9d20ad 100644 --- a/src/Event.h +++ b/src/utils/Event.h @@ -4,7 +4,7 @@ #include #include -namespace HttpServer +namespace Utils { class Event { @@ -15,19 +15,19 @@ namespace HttpServer bool manually; public: - Event(const bool _signaled = false, const bool _manualy = false); - ~Event() = default; + Event(const bool signaled = false, const bool manualy = false) noexcept; + ~Event() noexcept = default; public: void wait(); bool wait_for(const std::chrono::milliseconds &ms); bool wait_until(const std::chrono::high_resolution_clock::time_point &tp); - void notify(); - void notify(const size_t threadsCount); + void notify() noexcept; + void notify(const size_t threadsCount) noexcept; - void reset(); + void reset() noexcept; - bool notifed() const; + bool notifed() const noexcept; }; -}; \ No newline at end of file +} diff --git a/src/utils/Utils.cpp b/src/utils/Utils.cpp new file mode 100644 index 0000000..aacd218 --- /dev/null +++ b/src/utils/Utils.cpp @@ -0,0 +1,815 @@ + +#include "Utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Utils +{ + void toLower(std::string &str) noexcept { + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + } + + std::string getLowerString(const std::string &str) { + std::string copy = str; + toLower(copy); + return copy; + } + + void trim(std::string &str) + { + static const std::array whitespace { " \t\n\v\f\r" }; + + const size_t last = str.find_last_not_of(whitespace.data() ); + + if (std::string::npos == last) { + return str.clear(); + } + + str.assign( + str, + str.find_first_not_of(whitespace.data() ), + last + 1 + ); + } + + std::string getTrimmedString(const std::string &str) { + std::string copy = str; + trim(copy); + return copy; + } + + std::vector explode(const std::string &str, const char sep) + { + std::vector values; + + for (size_t pos = 0; std::string::npos != pos;) + { + const size_t delimiter = str.find(sep, pos); + + std::string value = str.substr(pos, delimiter - pos); + trim(value); + + values.emplace_back(std::move(value) ); + + pos = delimiter; + + if (std::string::npos != pos) { + ++pos; + } + } + + return values; + } + + std::string encodeHtmlSymbols(const std::string &str) + { + std::string buf; + buf.reserve(str.length() ); + + for (size_t pos = 0; pos < str.length(); ++pos) { + switch (str[pos]) + { + case '&': buf.append("&"); break; + case '\"': buf.append("""); break; + case '\'': buf.append("'"); break; + case '<': buf.append("<"); break; + case '>': buf.append(">"); break; + default: buf.push_back(str[pos]); break; + } + } + + return buf; + } + + std::string binToHexString(const void *binData, const size_t dataSize) + { + std::string str(dataSize * 2, 0); + + const uint8_t *bin = reinterpret_cast(binData); + + static const std::array hexDigits { "0123456789abcdef" }; + + for (size_t i = dataSize - 1; std::numeric_limits::max() != i; --i) { + str[i * 2 + 0] = hexDigits[bin[i] >> 4]; + str[i * 2 + 1] = hexDigits[bin[i] & 0x0F]; + } + + return str; + } + + static unsigned char hexStringToBinEncodeSymbol(const char c) noexcept + { + if (c >= '0' && c <= '9') { + return static_cast(c - 0x30); + } + else if (c >= 'a' && c <= 'f') { + return static_cast(c - 0x57); + } + else if (c >= 'A' && c <= 'F') { + return static_cast(c - 0x37); + } + + return 0; + } + + std::string hexStringToBin(const std::string &hexStr) + { + std::string bin(hexStr.length() / 2, 0); + + for (size_t i = 0; i < bin.length(); ++i) { + const char a = hexStr[i * 2 + 0]; + const char b = hexStr[i * 2 + 1]; + + bin[i] = char( + (hexStringToBinEncodeSymbol(a) << 4) | hexStringToBinEncodeSymbol(b) + ); + } + + return bin; + } + + enum Endianness { + INIT = 0, + LITE = 1, + BIGE = 2 + }; + + uint64_t hton64(const uint64_t host64) noexcept + { + static Endianness endian = Endianness::INIT; + + union { + uint64_t ull; + unsigned char c[sizeof(uint64_t)]; + } x; + + if (endian == Endianness::INIT) { + x.ull = 0x01; + endian = (x.c[7] == 0x01ULL) ? Endianness::BIGE : Endianness::LITE; + } + + if (endian == Endianness::BIGE) { + return host64; + } + + x.ull = host64; + + unsigned char c; + + c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c; + c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c; + c = x.c[2]; x.c[2] = x.c[5]; x.c[5] = c; + c = x.c[3]; x.c[3] = x.c[4]; x.c[4] = c; + + return x.ull; + } + + uint64_t ntoh64(const uint64_t net64) noexcept { + return hton64(net64); + } + + void hton24(void *dest, const uint32_t src) noexcept + { + static Endianness endian = Endianness::INIT; + + union { + uint32_t ui; + uint8_t c[sizeof(uint32_t)]; + } x; + + if (endian == Endianness::INIT) { + x.ui = 0x01; + endian = (x.c[3] == 0x01) ? Endianness::BIGE : Endianness::LITE; + } + + x.ui = src; + + if (endian == Endianness::BIGE) { + x.ui <<= 8; + } else { + uint8_t c = x.c[0]; + x.c[0] = x.c[2]; + x.c[2] = c; + } + + std::copy(x.c, x.c + 3, reinterpret_cast(dest) ); + } + + uint32_t ntoh24(const void *src24) noexcept + { + static Endianness endian = Endianness::INIT; + + union { + uint32_t ui; + uint8_t c[sizeof(uint32_t)]; + } x; + + if (endian == Endianness::INIT) { + x.ui = 0x01; + endian = (x.c[3] == 0x01) ? Endianness::BIGE : Endianness::LITE; + } + + if (endian == Endianness::BIGE) { + return *reinterpret_cast(src24) >> 8; + } + + const uint8_t *addr = reinterpret_cast(src24); + + x.ui = 0; + + x.c[0] = addr[2]; + x.c[1] = addr[1]; + x.c[2] = addr[0]; + + return x.ui; + } + + std::string getUniqueName() { + size_t time = size_t( + std::chrono::high_resolution_clock::now().time_since_epoch().count() + ); + + time = hton64(time); + + return binToHexString(&time, sizeof(time) ); + } + + constexpr uint8_t PACK_NUMBER_SIZE_BYTE = 252; + constexpr uint8_t PACK_NUMBER_SIZE_16 = 253; + constexpr uint8_t PACK_NUMBER_SIZE_32 = 254; + constexpr uint8_t PACK_NUMBER_SIZE_MAX = 255; + + size_t getPackNumberSize(const size_t number) noexcept + { + if (number <= PACK_NUMBER_SIZE_BYTE) { + return sizeof(uint8_t); + } + else if (number <= std::numeric_limits::max() ) { + return sizeof(uint8_t) + sizeof(uint16_t); + } + else if (number <= std::numeric_limits::max() ) { + return sizeof(uint8_t) + sizeof(uint32_t); + } + + return sizeof(uint8_t) + sizeof(size_t); + } + + size_t getPackStringSize(const std::string &str) noexcept { + return getPackNumberSize(str.length() ) + str.length(); + } + + uint8_t *packPointer(uint8_t *dest, void *pointer) noexcept { + *reinterpret_cast(dest) = pointer; + return dest + sizeof(void *); + } + + uint8_t *packNumber(uint8_t *dest, const size_t number) noexcept + { + if (number <= PACK_NUMBER_SIZE_BYTE) { + *dest = static_cast(number); + + dest += sizeof(uint8_t); + } + else if (number <= std::numeric_limits::max() ) { + *dest = PACK_NUMBER_SIZE_16; + + dest += sizeof(uint8_t); + + *reinterpret_cast(dest) = static_cast(number); + + dest += sizeof(uint16_t); + } + else if (number <= std::numeric_limits::max() ) { + *dest = PACK_NUMBER_SIZE_32; + + dest += sizeof(uint8_t); + + *reinterpret_cast(dest) = static_cast(number); + + dest += sizeof(uint32_t); + } else { + *dest = PACK_NUMBER_SIZE_MAX; + + dest += sizeof(uint8_t); + + *reinterpret_cast(dest) = number; + + dest += sizeof(size_t); + } + + return dest; + } + + uint8_t *packString(uint8_t *dest, const std::string &str) noexcept { + dest = packNumber(dest, str.length() ); + std::memcpy(dest, str.data(), str.length() ); + return dest + str.length(); + } + + void packPointer(std::vector &buf, void *pointer) { + buf.resize(buf.size() + sizeof(void *) ); + uint8_t *dest = reinterpret_cast(buf.data() + buf.size() - sizeof(void *) ); + *reinterpret_cast(dest) = pointer; + } + + void packNumber(std::vector &buf, const size_t number) + { + if (number <= PACK_NUMBER_SIZE_BYTE) { + buf.emplace_back(number); + } + else if (number <= std::numeric_limits::max() ) { + buf.emplace_back(PACK_NUMBER_SIZE_16); + + buf.resize(buf.size() + sizeof(uint16_t) ); + + *reinterpret_cast(buf.data() + buf.size() - sizeof(uint16_t) ) = static_cast(number); + } + else if (number <= std::numeric_limits::max() ) { + buf.emplace_back(PACK_NUMBER_SIZE_32); + + buf.resize(buf.size() + sizeof(uint32_t) ); + + *reinterpret_cast(buf.data() + buf.size() - sizeof(uint32_t) ) = static_cast(number); + } else { + buf.emplace_back(PACK_NUMBER_SIZE_MAX); + + buf.resize(buf.size() + sizeof(size_t) ); + + *reinterpret_cast(buf.data() + buf.size() - sizeof(size_t) ) = number; + } + } + + void packString(std::vector &buf, const std::string &str) + { + packNumber(buf, str.length() ); + + if (str.length() ) { + buf.insert( + buf.end(), + str.cbegin(), + str.cend() + ); + } + } + + const uint8_t *unpackPointer(void **pointer, const uint8_t *src) noexcept { + *pointer = *reinterpret_cast( + const_cast( + static_cast(src) + ) + ); + + return src + sizeof(void *); + } + + const uint8_t *unpackNumber(size_t *number, const uint8_t *src) noexcept + { + *number = *src; + + src += sizeof(uint8_t); + + if (*number <= PACK_NUMBER_SIZE_BYTE) { + + } + else if (*number == PACK_NUMBER_SIZE_16) { + *number = *reinterpret_cast(src); + src += sizeof(uint16_t); + } + else if (*number == PACK_NUMBER_SIZE_32) { + *number = *reinterpret_cast(src); + src += sizeof(uint32_t); + } else { + *number = *reinterpret_cast(src); + src += sizeof(size_t); + } + + return src; + } + + const uint8_t *unpackString(std::string &str, const uint8_t *src) + { + size_t length; + src = unpackNumber(&length, src); + + str.assign(src, src + length); + + return src + length; + } + + static const std::unordered_map map_days { + {"Mon", 0}, {"Tue", 1}, {"Wed", 2}, {"Thu", 3}, {"Fri", 4}, {"Sat", 5}, {"Sun", 6} + }; + + static const std::unordered_map map_months { + {"Jan", 0}, {"Feb", 1}, {"Mar", 2}, {"Apr", 3}, {"May", 4}, {"Jun", 5}, {"Jul", 6}, {"Aug", 7}, {"Sep", 8}, {"Oct", 9}, {"Nov", 10}, {"Dec", 11} + }; + + static const std::unordered_map map_zones { + {"GMT", 0}, {"UT", 0}, + {"EST", -5 * 3600}, {"EDT", -4 * 3600}, {"CST", -6 * 3600}, {"CDT", -5 * 3600}, {"MST", -7 * 3600}, {"MDT", -6 * 3600}, {"PST", -8 * 3600}, {"PDT", -7 * 3600}, + {"Z", 0}, {"A", -1 * 3600}, {"M", -12 * 3600}, {"N", 1 * 3600}, {"Y", 12 * 3600} + }; + + /** + * Parse RFC 882 (ddd, dd MMM yyyy HH:mm:ss K) + */ + time_t rfc822DatetimeToTimestamp(const std::string &strTime) { + std::tm tc {}; + + // Parse RFC 882 (ddd, dd MMM yyyy HH:mm:ss K) + + size_t pos = strTime.find_first_not_of(' '); + size_t delimiter = strTime.find(',', pos); + + if (std::string::npos == delimiter || delimiter - pos != 3) { + return ~0; + } + + const std::string day = strTime.substr(pos, delimiter - pos); + + auto const it_day = map_days.find(day); + + if (map_days.cend() != it_day) { + tc.tm_wday = it_day->second; + } else { + return ~0; + } + + pos = strTime.find_first_not_of(' ', delimiter + 1); + delimiter = strTime.find_first_of(' ', pos); + + if (std::string::npos == delimiter) { + return ~0; + } + + tc.tm_mday = std::atoi(strTime.data() + pos); + + pos = strTime.find_first_not_of(' ', delimiter + 1); + delimiter = strTime.find_first_of(' ', pos); + + if (std::string::npos == delimiter || delimiter - pos != 3) { + return ~0; + } + + const std::string month = strTime.substr(pos, delimiter - pos); + + auto const it_mon = map_months.find(month); + + if (map_months.cend() != it_mon) { + tc.tm_mon = it_mon->second; + } else { + return ~0; + } + + pos = strTime.find_first_not_of(' ', delimiter + 1); + delimiter = strTime.find_first_of(' ', pos); + + if (std::string::npos == delimiter) { + return ~0; + } + + tc.tm_year = std::atoi(strTime.data() + pos) - 1900; + + pos = strTime.find_first_not_of(' ', delimiter + 1); + delimiter = strTime.find_first_of(':', pos); + + if (std::string::npos == delimiter) { + return ~0; + } + + tc.tm_hour = std::atoi(strTime.data() + pos); + + pos = strTime.find_first_not_of(' ', delimiter + 1); + delimiter = strTime.find_first_of(':', pos); + + if (std::string::npos == delimiter) { + return ~0; + } + + tc.tm_min = std::atoi(strTime.data() + pos); + + pos = strTime.find_first_not_of(' ', delimiter + 1); + delimiter = strTime.find_first_of(' ', pos); + + if (std::string::npos == delimiter) { + return ~0; + } + + tc.tm_sec = std::atoi(strTime.data() + pos); + + pos = strTime.find_first_not_of(' ', delimiter + 1); + delimiter = strTime.find_first_of(' ', pos); + + if (std::string::npos == delimiter) { + delimiter = strTime.length(); + } + + if (std::string::npos == pos || delimiter - pos > 5) { + return ~0; + } + + const std::string zone = strTime.substr(pos, delimiter - pos); + + auto const it_zone = map_zones.find(zone); + + int timezone = 0; + + if (map_zones.cend() != it_zone) { + timezone = it_zone->second; + } + else if (zone.length() == 5 && ('+' == zone.front() || '-' == zone.front() ) ) + { + std::array hours; + std::array minutes; + + zone.copy(hours.data(), 2, 1); + zone.copy(minutes.data(), 2, 3); + + timezone = std::atoi(hours.data()) * 3600; + timezone += std::atoi(minutes.data()) * 60; + + if (zone.front() == '-') { + timezone *= -1; + } + } else { + return ~0; + } + + tc.tm_isdst = -1; + + return std::mktime(&tc) - timezone; + } + + static time_t localToGmt(const time_t timestamp) + { + #ifdef WIN32 + std::tm stm {}; + + ::gmtime_s(&stm, ×tamp); + + return std::mktime(&stm); + #else + std::tm stm {}; + + ::gmtime_r(×tamp, &stm); + + return std::mktime(&stm); + #endif + } + + /** + * Convert c-string (__DATE__ " " __TIME__) to std::time_t + */ + time_t predefinedDatetimeToTimestamp(const char *strTime) + { + std::tm tc {}; + + const char *ptrStr = std::strchr(strTime, ' '); + + if (nullptr == ptrStr) { + return ~0; + } + + const std::string month(strTime, ptrStr); + + auto const it_mon = map_months.find(month); + + if (map_months.cend() != it_mon) { + tc.tm_mon = it_mon->second; + } else { + return ~0; + } + + ++ptrStr; + + // Fix for MS __DATE__ + if (' ' == *ptrStr) { + ++ptrStr; + } + + strTime = std::strchr(ptrStr, ' '); + + if (nullptr == strTime) { + return ~0; + } + + tc.tm_mday = std::atoi(ptrStr); + + ++strTime; + + ptrStr = std::strchr(strTime, ' '); + + if (nullptr == ptrStr) { + return ~0; + } + + tc.tm_year = std::atoi(strTime) - 1900; + + ++ptrStr; + + strTime = std::strchr(ptrStr, ':'); + + if (nullptr == strTime) { + return ~0; + } + + tc.tm_hour = std::atoi(ptrStr); + + ++strTime; + + ptrStr = std::strchr(strTime, ':'); + + if (nullptr == ptrStr) { + return ~0; + } + + tc.tm_min = std::atoi(strTime); + + ++ptrStr; + + tc.tm_sec = std::atoi(ptrStr); + + return localToGmt(std::mktime(&tc) ); + } + + /** + * Convert std::time_t to RFC822 std::string + */ + std::string getDatetimeAsString(time_t tTime, const bool isGmtTime) + { + std::array buf; + + if (tTime == ~0) { + std::time(&tTime); + } + + #ifdef WIN32 + std::tm stm {}; + + isGmtTime + ? ::localtime_s(&stm, &tTime) + : ::gmtime_s(&stm, &tTime); + + auto const len = std::strftime( + buf.data(), + buf.size(), + "%a, %d %b %Y %H:%M:%S GMT", // RFC 822 + &stm + ); + #else + std::tm stm {}; + + isGmtTime + ? ::localtime_r(&tTime, &stm) + : ::gmtime_r(&tTime, &stm); + + auto const len = std::strftime( + buf.data(), + buf.size(), + "%a, %d %b %G %H:%M:%S GMT", // RFC 822 + &stm + ); + #endif + + return std::string(buf.data(), buf.data() + len); + } + + std::string predefinedDatetimeToRfc822(const char *strTime) { + const std::time_t time = predefinedDatetimeToTimestamp(strTime); + return getDatetimeAsString(time, false); + } + + size_t getNumberLength(size_t number) noexcept + { + size_t length = 0; + + do { + ++length; + number /= 10; + } + while (number); + + return length; + } + + bool parseCookies( + const std::string &cookieHeader, + std::unordered_multimap &cookies + ) { + if (cookieHeader.empty() ) { + return true; + } + + for ( + size_t cur_pos = 0, next_value; + std::string::npos != cur_pos; + cur_pos = next_value + ) { + next_value = cookieHeader.find(';', cur_pos); + + size_t delimiter = cookieHeader.find('=', cur_pos); + + if (std::string::npos == delimiter || delimiter > next_value) { + return false; + } + + std::string key = cookieHeader.substr( + cur_pos, + delimiter - cur_pos + ); + + trim(key); + key = urlDecode(key); + + ++delimiter; + + std::string value = cookieHeader.substr( + delimiter, + std::string::npos != next_value + ? next_value - delimiter + : next_value + ); + + trim(value); + value = urlDecode(value); + + cookies.emplace( + std::move(key), + std::move(value) + ); + + if (std::string::npos != next_value) { + ++next_value; + } + } + + return true; + } + + static inline bool isCharUrlAllowed(const char c) noexcept { + return c == '-' || c == '_' || c == '.' || c == '~'; + } + + std::string urlEncode(const std::string &str) + { + std::string encoded; + + static const std::array hexDigits { "0123456789ABCDEF" }; + + for (size_t i = 0; i < str.length(); ++i) + { + const unsigned char c = static_cast(str[i]); + + if (std::isalnum(c) || isCharUrlAllowed(char(c) ) ) { + encoded.push_back(char(c) ); + } + else if (' ' == c) { + encoded.push_back('+'); + } else { + const uint8_t a = c >> 4; + const uint8_t b = c & 0x0F; + + encoded.push_back('%'); + encoded.push_back(hexDigits[a]); + encoded.push_back(hexDigits[b]); + } + } + + return encoded; + } + + std::string urlDecode(const std::string &str) + { + std::string decoded; + + for (size_t i = 0; i < str.length(); ++i) + { + unsigned char c = static_cast(str[i]); + + if ('%' == c) { + if (i + 2 < str.length() ) { + const char a = str[++i]; + const char b = str[++i]; + + c = static_cast( + (hexStringToBinEncodeSymbol(a) << 4) | hexStringToBinEncodeSymbol(b) + ); + } + } + else if ('+' == c) { + c = ' '; + } + + decoded.push_back(char(c) ); + } + + return decoded; + } +} diff --git a/src/utils/Utils.h b/src/utils/Utils.h new file mode 100644 index 0000000..551e975 --- /dev/null +++ b/src/utils/Utils.h @@ -0,0 +1,138 @@ +#pragma once + +#include +#include +#include + +namespace Utils +{ + void toLower(std::string &str) noexcept; + std::string getLowerString(const std::string &str); + + void trim(std::string &str); + std::string getTrimmedString(const std::string &str); + + std::vector explode(const std::string &str, const char sep); + + std::string encodeHtmlSymbols(const std::string &str); + + std::string binToHexString(const void *bin, const size_t size); + + std::string hexStringToBin(const std::string &hexStr); + + uint64_t hton64(const uint64_t host64) noexcept; + uint64_t ntoh64(const uint64_t net64) noexcept; + + void hton24(void *dest, const uint32_t host32) noexcept; + uint32_t ntoh24(const void *src24) noexcept; + + std::string getUniqueName(); + + size_t getPackNumberSize(const size_t number) noexcept; + size_t getPackStringSize(const std::string &str) noexcept; + + template + size_t getPackContainerSize(const T &container) noexcept + { + size_t full_size = getPackNumberSize(container.size() ); + + for (auto const &pair : container) { + full_size += getPackStringSize(pair.first); + full_size += getPackStringSize(pair.second); + } + + return full_size; + } + + uint8_t *packPointer(uint8_t *dest, void *pointer) noexcept; + uint8_t *packNumber(uint8_t *dest, const size_t number) noexcept; + uint8_t *packString(uint8_t *dest, const std::string &str) noexcept; + + template + uint8_t *packContainer(void *dest, const T &container) noexcept + { + uint8_t *addr = reinterpret_cast(dest); + + addr = packNumber(addr, container.size() ); + + for (auto const &pair : container) { + addr = packString(addr, pair.first); + addr = packString(addr, pair.second); + } + + return addr; + } + + void packPointer(std::vector &buf, void *pointer); + void packNumber(std::vector &buf, const size_t number); + void packString(std::vector &buf, const std::string &str); + + template + void packContainer(std::vector &buf, const T &container) + { + packNumber(buf, container.size() ); + + for (auto const &pair : container) { + packString(buf, pair.first); + packString(buf, pair.second); + } + } + + const uint8_t *unpackPointer(void **pointer, const uint8_t *src) noexcept; + const uint8_t *unpackNumber(size_t *number, const uint8_t *src) noexcept; + const uint8_t *unpackString(std::string &str, const uint8_t *src); + + template + const uint8_t *unpackContainer(T &container, const uint8_t *src) + { + size_t count; + src = unpackNumber(&count, src); + + for (size_t i = 0; i < count; ++i) { + std::string key; + src = unpackString(key, src); + + std::string value; + src = unpackString(value, src); + + container.emplace(std::move(key), std::move(value) ); + } + + return src; + } + + template + const uint8_t *unpackVector(T &vector, const uint8_t *src) + { + size_t count; + src = unpackNumber(&count, src); + + for (size_t i = 0; i < count; ++i) { + std::string key; + src = unpackString(key, src); + + std::string value; + src = unpackString(value, src); + + vector.emplace_back(std::move(key), std::move(value) ); + } + + return src; + } + + time_t rfc822DatetimeToTimestamp(const std::string &strTime); + time_t predefinedDatetimeToTimestamp(const char *strTime); + + std::string getDatetimeAsString(time_t tTime = ~0, const bool isGmtTime = false); + std::string predefinedDatetimeToRfc822(const char *strTime); + + size_t getNumberLength(size_t number) noexcept; + + bool parseCookies( + const std::string &cookieHeader, + std::unordered_multimap &cookies + ); + + std::string urlEncode(const std::string &str); + std::string urlDecode(const std::string &str); +}