diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..707f4da --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build +projects/build* +projects/qt-creator/*.user diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5900f8e --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +RM = rm -rf +MKDIR = mkdir -p + +#CXX = g++ +CXXFLAGS += -fPIC -std=c++14 -c -Wall -O2 +LDFLAGS = -shared -ldl -lpthread -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)/httpserverapp.so + +.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 c55f835..bd7312e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,49 @@ -httpserverapp -============= +# httpserverapp -Sample http application on C++ +Sample application for [C++ http server](https://github.com/awwit/httpserver). -For http server on C++ -See: https://github.com/awwit/httpserver +## Dependencies + +Common: + +* [gnutls](https://www.gnutls.org/) + +Linux: `dl`, `pthread`, `gnutls` + +Windows: `ws2_32.lib`, `libgnutls.dll.a` + +## Build + +Linux: + +```sh +git clone https://github.com/awwit/httpserverapp.git +cd httpserverapp +make +``` + +or + +```sh +git clone https://github.com/awwit/httpserverapp.git +cd httpserverapp +mkdir build +cd build +qbs build -f ./../projects/qt-creator/httpserverapp.qbs release +``` + +Windows: + +```sh +git clone https://github.com/awwit/httpserverapp.git +cd httpserver +mkdir build +cd build +devenv ./../projects/msvs/httpserverapp.sln /build +``` + +# License + +The source codes are licensed under the +[MIT](https://opensource.org/licenses/MIT), +the full text of the license is located in the [LICENSE](LICENSE) file. diff --git a/httpserverapp.sln b/httpserverapp.sln deleted file mode 100644 index 467c721..0000000 --- a/httpserverapp.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{2857B73E-F847-4B02-9238-064979017E93}") = "httpserverapp", "httpserverapp\httpserverapp.cproj", "{4C15CBD7-D1C0-4869-AEE1-14CB41328514}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4C15CBD7-D1C0-4869-AEE1-14CB41328514}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C15CBD7-D1C0-4869-AEE1-14CB41328514}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C15CBD7-D1C0-4869-AEE1-14CB41328514}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C15CBD7-D1C0-4869-AEE1-14CB41328514}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = httpserverapp\httpserverapp.cproj - EndGlobalSection -EndGlobal diff --git a/httpserverapp.userprefs b/httpserverapp.userprefs deleted file mode 100644 index 5caf378..0000000 --- a/httpserverapp.userprefs +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/httpserverapp/FileIncoming.cpp b/httpserverapp/FileIncoming.cpp deleted file mode 100644 index ee590d2..0000000 --- a/httpserverapp/FileIncoming.cpp +++ /dev/null @@ -1,38 +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 &file) - : file_name(file.file_name), file_type(file.file_type), file_size(file.file_size) - { - - } - - FileIncoming::FileIncoming(FileIncoming &&file) - : file_name(file.file_name), file_type(file.file_type), file_size(file.file_size) - { - file.file_name.clear(); - file.file_type.clear(); - file.file_size = 0; - } - - bool FileIncoming::isExists() const - { - std::ifstream file(file_name, std::ifstream::binary); - - bool is_exists = file.good(); - - file.close(); - - return is_exists; - } -}; \ No newline at end of file diff --git a/httpserverapp/FileIncoming.h b/httpserverapp/FileIncoming.h deleted file mode 100644 index 75df66c..0000000 --- a/httpserverapp/FileIncoming.h +++ /dev/null @@ -1,40 +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 &, const std::string &, const size_t); - FileIncoming(const FileIncoming &); - FileIncoming(FileIncoming &&); - ~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/httpserverapp/Main.cpp b/httpserverapp/Main.cpp deleted file mode 100644 index 819645a..0000000 --- a/httpserverapp/Main.cpp +++ /dev/null @@ -1,97 +0,0 @@ - -#include "Main.h" - -#include "Test.h" - -#include - -DLLEXPORT bool application_init() -{ - return true; -} - -DLLEXPORT int application_call(HttpServer::server_request *request, HttpServer::server_response *response) -{ - std::unordered_multimap params; - std::unordered_map headers; - std::unordered_multimap data; - std::unordered_multimap files; - std::unordered_multimap cookies; - - Utils::rawPairsToStlUnorderedMultimap(params, request->params, request->params_count); - Utils::rawPairsToStlUnorderedMap(headers, request->headers, request->headers_count); - Utils::rawPairsToStlUnorderedMultimap(data, request->data, request->data_count); - Utils::rawFilesInfoToFilesIncoming(files, request->files, request->files_count); - - auto it_cookie = headers.find("Cookie"); - - if (headers.end() != it_cookie) - { - Utils::parseCookies(it_cookie->second, cookies); - } - - HttpServer::ServerRequest proc_request { - HttpServer::Socket(request->socket), - std::string(request->method), - std::string(request->uri_reference), - std::string(request->document_root), - std::move(params), - std::move(headers), - std::move(data), - std::move(files), - std::move(cookies) - }; - - HttpServer::ServerResponse proc_response { - HttpServer::Socket(request->socket), - std::map() - }; - - std::string absolute_path = proc_request.document_root + proc_request.uri_reference; - - int result = EXIT_SUCCESS; - - std::ifstream file(absolute_path, std::ifstream::binary); - - if (file) - { - auto it_connection = proc_request.headers.find("Connection"); - - if (proc_request.headers.cend() != it_connection) - { - proc_response.headers["Connection"] = it_connection->second; - } - - proc_response.headers["X-Sendfile"] = absolute_path; - } - else - { - result = test(proc_request, proc_response); - } - - file.close(); - - if (proc_response.headers.size() ) - { - Utils::raw_pair *headers; - Utils::stlMapToRawPairs(&headers, proc_response.headers); - - response->headers_count = proc_response.headers.size(); - response->headers = headers; - } - - return result; -} - -DLLEXPORT void application_clear(Utils::raw_pair headers[], const size_t headers_count) -{ - if (headers && headers_count) - { - destroyRawPairs(headers, headers_count); - } -} - -DLLEXPORT void application_final() -{ - -} \ No newline at end of file diff --git a/httpserverapp/Main.h b/httpserverapp/Main.h deleted file mode 100644 index ddc8a58..0000000 --- a/httpserverapp/Main.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "ServerRequest.h" -#include "ServerResponse.h" -#include "FileIncoming.h" -#include "Utils.h" - -#include -#include -#include - -#ifdef WIN32 - #define DLLEXPORT extern "C" __declspec(dllexport) -#else - #define DLLEXPORT extern "C" -#endif \ No newline at end of file diff --git a/httpserverapp/RawData.h b/httpserverapp/RawData.h deleted file mode 100644 index a493382..0000000 --- a/httpserverapp/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/httpserverapp/ResourceAbstract.cpp b/httpserverapp/ResourceAbstract.cpp deleted file mode 100644 index aa51959..0000000 --- a/httpserverapp/ResourceAbstract.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "ResourceAbstract.h" - -#include "ResourceMethodAbstract.h" -#include "ResourceMethodOptions.h" - -namespace HttpServer -{ - ResourceAbstract::ResourceAbstract() - { - addMethod(new ResourceMethodOptions() ); - } - - ResourceAbstract::~ResourceAbstract() - { - for (auto &method : methods) - { - delete method.second; - } - - for (auto &resource : resources) - { - delete resource.second; - } - } - - void ResourceAbstract::addMethod(ResourceMethodAbstract *method) - { - methods.emplace(method->getName(), method); - } - - void ResourceAbstract::addResource(ResourceAbstract *resource) - { - resources.emplace(resource->getName(), resource); - } -}; diff --git a/httpserverapp/ResourceAbstract.h b/httpserverapp/ResourceAbstract.h deleted file mode 100644 index 3df64af..0000000 --- a/httpserverapp/ResourceAbstract.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -namespace HttpServer -{ - class ResourceMethodAbstract; - - class ResourceAbstract - { - protected: - std::string resource_name; - - public: - std::unordered_map methods; - - std::unordered_map resources; - - private: - void addMethod(ResourceMethodAbstract *); - void addResource(ResourceAbstract *); - - public: - ResourceAbstract(); - ~ResourceAbstract(); - - inline std::string getName() const - { - return resource_name; - } - }; -}; diff --git a/httpserverapp/ResourceMethodAbstract.h b/httpserverapp/ResourceMethodAbstract.h deleted file mode 100644 index 42d87a0..0000000 --- a/httpserverapp/ResourceMethodAbstract.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "ServerRequest.h" -#include "ServerResponse.h" -#include "ResourceAbstract.h" - -#include - -namespace HttpServer -{ - class ResourceMethodAbstract - { - protected: - std::string resource_method_name; - - public: - inline std::string getName() const - { - return resource_method_name; - } - - virtual void execute(ResourceAbstract *, ServerRequest &, ServerResponse &) = 0; - }; -}; diff --git a/httpserverapp/ResourceMethodOptions.cpp b/httpserverapp/ResourceMethodOptions.cpp deleted file mode 100644 index e895240..0000000 --- a/httpserverapp/ResourceMethodOptions.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "ResourceMethodOptions.h" - -namespace HttpServer -{ - ResourceMethodOptions::ResourceMethodOptions() - { - resource_method_name = "OPTIONS"; - } - - void ResourceMethodOptions::execute(ResourceAbstract *resource, ServerRequest &request, ServerResponse &response) - { - std::string options; - - for (auto &method : resource->methods) - { - if (method.second != this) - { - options += method.first; - options.push_back(','); - } - } - - if (options.length() ) - { - options.pop_back(); - } - } -}; diff --git a/httpserverapp/ResourceMethodOptions.h b/httpserverapp/ResourceMethodOptions.h deleted file mode 100644 index 2a170a4..0000000 --- a/httpserverapp/ResourceMethodOptions.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "ResourceMethodAbstract.h" - -namespace HttpServer -{ - class ResourceMethodOptions: public ResourceMethodAbstract - { - public: - ResourceMethodOptions(); - - virtual void execute(ResourceAbstract *, ServerRequest &, ServerResponse &) override; - }; -}; diff --git a/httpserverapp/ServerRequest.h b/httpserverapp/ServerRequest.h deleted file mode 100644 index 0982cc8..0000000 --- a/httpserverapp/ServerRequest.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "RawData.h" -#include "Socket.h" -#include "FileIncoming.h" - -#include - -namespace HttpServer -{ - struct server_request - { - const System::native_socket_type socket; - 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 Socket 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 Socket 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/httpserverapp/ServerResponse.h b/httpserverapp/ServerResponse.h deleted file mode 100644 index f59400c..0000000 --- a/httpserverapp/ServerResponse.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "RawData.h" - -#include -#include - -namespace HttpServer -{ - struct server_response - { - System::native_socket_type socket; - size_t headers_count; - Utils::raw_pair *headers; - }; - - struct ServerResponse - { - Socket socket; - std::map headers; - }; -}; diff --git a/httpserverapp/Socket.cpp b/httpserverapp/Socket.cpp deleted file mode 100644 index bd79fbd..0000000 --- a/httpserverapp/Socket.cpp +++ /dev/null @@ -1,382 +0,0 @@ - -#include "Socket.h" - -namespace HttpServer -{ - int Socket::Startup() - { - #ifdef WIN32 - unsigned short version = MAKEWORD(2, 2); - ::WSADATA wsaData = {0}; - return ::WSAStartup(version, &wsaData); - #elif POSIX - return 0; - #else - #error "Undefine platform" - #endif - } - - int Socket::Cleanup() - { - #ifdef WIN32 - return ::WSACleanup(); - #elif POSIX - return 0; - #else - #error "Undefine platform" - #endif - } - - Socket::Socket(): socket_handle(~0) - { - - } - - Socket::Socket(const System::native_socket_type handle) : socket_handle(handle) - { - - } - - Socket::Socket(const Socket &obj) : socket_handle(obj.socket_handle) - { - - } - - Socket::Socket(Socket &&that) : socket_handle(that.socket_handle) - { - that.socket_handle = ~0; - } - - System::native_socket_type Socket::open() - { - close(); - - socket_handle = ::socket(AF_INET, SOCK_STREAM, 0); - - return socket_handle; - } - - int Socket::close() - { - if (is_open() ) - { - #ifdef WIN32 - int result = ::closesocket(socket_handle); - #elif POSIX - int result = ::close(socket_handle); - #else - #error "Undefine platform" - #endif - - if (0 == result) - { - socket_handle = ~0; - } - - return result; - } - - return ~0; - } - - int Socket::bind(const int port) const - { - ::sockaddr_in sock_addr = {0}; - sock_addr.sin_family = AF_INET; - sock_addr.sin_port = htons(port); - sock_addr.sin_addr.s_addr = ::htonl(INADDR_ANY); - - return ::bind(socket_handle, reinterpret_cast(&sock_addr), sizeof(sockaddr_in) ); - } - - int Socket::listen() const - { - return ::listen(socket_handle, SOMAXCONN); - } - - Socket Socket::accept() const - { - #ifdef WIN32 - System::native_socket_type client_socket = ::accept(socket_handle, static_cast(nullptr), static_cast(nullptr) ); - #elif POSIX - System::native_socket_type client_socket = ::accept(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 - ::fd_set readset; - FD_ZERO(&readset); - FD_SET(socket_handle, &readset); - - if (0 < ::select(socket_handle + 1, &readset, nullptr, nullptr, nullptr) ) - { - if (FD_ISSET(socket_handle, &readset) ) - { - client_socket = ::accept(socket_handle, static_cast(nullptr), static_cast(nullptr) ); - } - } - #elif POSIX - ::fd_set readset; - FD_ZERO(&readset); - FD_SET(socket_handle, &readset); - - if (0 < ::select(socket_handle + 1, &readset, nullptr, nullptr, nullptr) ) - { - if (FD_ISSET(socket_handle, &readset) ) - { - client_socket = ::accept(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 &timeWait) const - { - System::native_socket_type client_socket = ~0; - #ifdef WIN32 - ::fd_set readset; - FD_ZERO(&readset); - FD_SET(socket_handle, &readset); - - long seconds = timeWait.count() / 1000; - ::timeval timeout {seconds, (timeWait.count() - seconds * 1000) * 1000}; - - if (0 < ::select(socket_handle + 1, &readset, nullptr, nullptr, &timeout) ) - { - if (FD_ISSET(socket_handle, &readset) ) - { - client_socket = ::accept(socket_handle, static_cast(nullptr), static_cast(nullptr) ); - } - } - #elif POSIX - ::fd_set readset; - FD_ZERO(&readset); - FD_SET(socket_handle, &readset); - - long seconds = timeWait.count() / 1000; - ::timeval timeout {seconds, (timeWait.count() - seconds * 1000) * 1000}; - - if (0 < ::select(socket_handle + 1, &readset, nullptr, nullptr, &timeout) ) - { - if (FD_ISSET(socket_handle, &readset) ) - { - client_socket = ::accept(socket_handle, static_cast(nullptr), static_cast(nullptr) ); - } - } - #else - #error "Undefine platform" - #endif - return Socket(client_socket); - } - - int Socket::shutdown() const - { - if (is_open() ) - { - #ifdef WIN32 - return ::shutdown(socket_handle, SD_BOTH); - #elif POSIX - return ::shutdown(socket_handle, SHUT_RDWR); - #else - #error "Undefine platform" - #endif - } - - return -1; - } - - bool Socket::nonblock(const bool isNonBlock) const - { - #ifdef WIN32 - unsigned long value = isNonBlock; - return 0 == ::ioctlsocket(socket_handle, FIONBIO, &value); - #elif POSIX - return -1 != ::fcntl(socket_handle, F_SETFL, isNonBlock ? O_NONBLOCK : O_SYNC); - #else - #error "Undefine platform" - #endif - } - -/* bool Socket::is_nonblock() const - { - #ifdef WIN32 - - #elif POSIX - int flags = ::fcntl(socket_handle, F_GETFL, 0); - return (flags != -1) && (flags & O_NONBLOCK); - #else - #error "Undefine platform" - #endif - }*/ - - size_t Socket::recv(std::vector &buf) const - { - #ifdef WIN32 - return ::recv(socket_handle, buf.data(), buf.size(), 0); - #elif POSIX - return ::recv(socket_handle, buf.data(), buf.size(), MSG_NOSIGNAL); - #else - #error "Undefine platform" - #endif - } - - size_t Socket::nonblock_recv(std::vector &buf, const std::chrono::milliseconds &timeWait) const - { - #ifdef WIN32 - ::fd_set readset; - FD_ZERO(&readset); - FD_SET(socket_handle, &readset); - - long seconds = timeWait.count() / 1000; - ::timeval timeout {seconds, (timeWait.count() - seconds * 1000) * 1000}; - - if (0 < ::select(socket_handle + 1, &readset, nullptr, nullptr, &timeout) ) - { - if (FD_ISSET(socket_handle, &readset) ) - { - return ::recv(socket_handle, buf.data(), buf.size(), 0); - } - } - - return std::numeric_limits::max(); - #elif POSIX - ::fd_set readset; - FD_ZERO(&readset); - FD_SET(socket_handle, &readset); - - long seconds = timeWait.count() / 1000; - ::timeval timeout {seconds, (timeWait.count() - seconds * 1000) * 1000}; - - if (0 < ::select(socket_handle + 1, &readset, nullptr, nullptr, &timeout) ) - { - if (FD_ISSET(socket_handle, &readset) ) - { - return ::recv(socket_handle, buf.data(), buf.size(), MSG_NOSIGNAL); - } - } - - return std::numeric_limits::max(); - #else - #error "Undefine platform" - #endif - } - - size_t Socket::send(const std::string &buf) const - { - #ifdef WIN32 - return ::send(socket_handle, buf.data(), buf.length(), 0); - #elif POSIX - return ::send(socket_handle, buf.data(), buf.length(), MSG_WAITALL | MSG_NOSIGNAL); - #else - #error "Undefine platform" - #endif - } - - size_t Socket::send(const std::vector &buf, const size_t length) const - { - #ifdef WIN32 - return ::send(socket_handle, buf.data(), length, 0); - #elif POSIX - return ::send(socket_handle, buf.data(), length, MSG_WAITALL | MSG_NOSIGNAL); - #else - #error "Undefine platform" - #endif - } - - size_t Socket::nonblock_send(const std::string &buf, const std::chrono::milliseconds &timeWait) const - { - #ifdef WIN32 - ::fd_set writeset; - FD_ZERO(&writeset); - FD_SET(socket_handle, &writeset); - - long seconds = timeWait.count() / 1000; - ::timeval timeout {seconds, (timeWait.count() - seconds * 1000) * 1000}; - - if (0 < ::select(socket_handle + 1, nullptr, &writeset, nullptr, &timeout) ) - { - if (FD_ISSET(socket_handle, &writeset) ) - { - return ::send(socket_handle, buf.data(), buf.length(), 0); - } - } - - return std::numeric_limits::max(); - #elif POSIX - ::fd_set writeset; - FD_ZERO(&writeset); - FD_SET(socket_handle, &writeset); - - long seconds = timeWait.count() / 1000; - ::timeval timeout {seconds, (timeWait.count() - seconds * 1000) * 1000}; - - if (0 < ::select(socket_handle + 1, nullptr, &writeset, nullptr, &timeout) ) - { - if (FD_ISSET(socket_handle, &writeset) ) - { - return ::send(socket_handle, buf.data(), buf.length(), MSG_NOSIGNAL); - } - } - - return std::numeric_limits::max(); - #else - #error "Undefine platform" - #endif - } - - size_t Socket::nonblock_send(const std::vector &buf, const size_t length, const std::chrono::milliseconds &timeWait) const - { - #ifdef WIN32 - ::fd_set writeset; - FD_ZERO(&writeset); - FD_SET(socket_handle, &writeset); - - long seconds = timeWait.count() / 1000; - ::timeval timeout {seconds, (timeWait.count() - seconds * 1000) * 1000}; - - if (0 < ::select(socket_handle + 1, nullptr, &writeset, nullptr, &timeout) ) - { - if (FD_ISSET(socket_handle, &writeset) ) - { - return ::send(socket_handle, buf.data(), length, 0); - } - } - - return std::numeric_limits::max(); - #elif POSIX - ::fd_set writeset; - FD_ZERO(&writeset); - FD_SET(socket_handle, &writeset); - - long seconds = timeWait.count() / 1000; - ::timeval timeout {seconds, (timeWait.count() - seconds * 1000) * 1000}; - - if (0 < ::select(socket_handle + 1, nullptr, &writeset, nullptr, &timeout) ) - { - if (FD_ISSET(socket_handle, &writeset) ) - { - return ::send(socket_handle, buf.data(), length, MSG_WAITALL | MSG_NOSIGNAL); - } - } - - return std::numeric_limits::max(); - #else - #error "Undefine platform" - #endif - } - - Socket &Socket::operator=(const Socket s) - { - socket_handle = s.socket_handle; - return *this; - } -}; \ No newline at end of file diff --git a/httpserverapp/Socket.h b/httpserverapp/Socket.h deleted file mode 100644 index 4774055..0000000 --- a/httpserverapp/Socket.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#ifdef WIN32 - #include - #pragma comment(lib, "ws2_32.lib") - #undef max -#elif POSIX - #include - #include - #include - #include - #include -#else - #error "Undefine platform" -#endif - -#include "System.h" - -#include -#include - -#include -#include -#include - -namespace HttpServer -{ - class Socket - { - protected: - System::native_socket_type socket_handle; - - public: - int static Startup(); - int static Cleanup(); - - public: - Socket(); - Socket(const System::native_socket_type); - Socket(const Socket &); - Socket(Socket &&); - - ~Socket() = default; - - System::native_socket_type open(); - int close(); - - inline bool is_open() const - { - #ifdef WIN32 - return INVALID_SOCKET != socket_handle; - #elif POSIX - return ~0 != socket_handle; - #else - #error "Undefine platform" - #endif - } - - int bind(const int) const; - int listen() const; - - Socket accept() const; - Socket nonblock_accept() const; - Socket nonblock_accept(const std::chrono::milliseconds &) const; - - int shutdown() const; - - bool nonblock(const bool = true) const; - // bool is_nonblock() const; - - size_t recv(std::vector &) const; - size_t nonblock_recv(std::vector &, const std::chrono::milliseconds &) const; - - size_t send(const std::string &) const; - size_t send(const std::vector &, const size_t) const; - - size_t nonblock_send(const std::string &, const std::chrono::milliseconds &) const; - size_t nonblock_send(const std::vector &, const size_t, const std::chrono::milliseconds &) const; - - inline System::native_socket_type get_handle() const - { - return socket_handle; - } - - Socket &operator =(const Socket); - }; -}; \ No newline at end of file diff --git a/httpserverapp/System.cpp b/httpserverapp/System.cpp deleted file mode 100644 index c5f6218..0000000 --- a/httpserverapp/System.cpp +++ /dev/null @@ -1,117 +0,0 @@ - -#include "System.h" - -namespace System -{ -#ifdef WIN32 - struct EnumData - { - native_processid_type process_id; - ::HWND hWnd; - }; - - BOOL WINAPI EnumProc(::HWND hWnd, ::LPARAM lParam) - { - EnumData &ed = *reinterpret_cast(lParam); - - native_processid_type process_id = 0; - - ::GetWindowThreadProcessId(hWnd, &process_id); - - if (process_id == ed.process_id && GetConsoleWindow() != hWnd) - { - ed.hWnd = hWnd; - - return false; - } - - return true; - } -#endif - - bool sendSignal(const native_processid_type pid, const int signal) - { - #ifdef WIN32 - EnumData ed = {pid, 0}; - - ::EnumWindows(EnumProc, reinterpret_cast(&ed) ); - - if (0 == ed.hWnd) - { - return false; - } - - return 0 != ::PostMessage(ed.hWnd, signal, 0, 0); - #elif POSIX - return 0 == ::kill(pid, signal); - #else - #error "Undefine platform" - #endif - } - - bool getFileSizeAndTimeGmt(const std::string &filePath, size_t *fileSize, time_t *fileTime) - { - #ifdef WIN32 - ::HANDLE hFile = ::CreateFile(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); - - if (INVALID_HANDLE_VALUE == hFile) - { - return false; - } - - if (false == ::GetFileSizeEx(hFile, reinterpret_cast<::PLARGE_INTEGER>(fileSize) ) ) - { - return false; - } - - ::FILETIME ftWrite; - - ::BOOL result = ::GetFileTime(hFile, nullptr, nullptr, &ftWrite); - - ::CloseHandle(hFile); - - if (false == result) - { - return false; - } - - ::SYSTEMTIME stUtc; - - ::FileTimeToSystemTime(&ftWrite, &stUtc); - - struct ::tm tm_time { - stUtc.wSecond, - stUtc.wMinute, - stUtc.wHour, - stUtc.wDay, - stUtc.wMonth - 1, - stUtc.wYear - 1900, - 0, - 0, - 0 - }; - - *fileTime = ::mktime(&tm_time); - - return true; - #elif POSIX - struct ::tm *clock; - struct ::stat attrib; - - if (-1 == ::stat(filePath.c_str(), &attrib) ) - { - return false; - } - - *fileSize = attrib.st_size; - - clock = ::gmtime(&(attrib.st_mtime) ); - - *fileTime = ::mktime(clock); - - return true; - #else - #error "Undefine platform" - #endif - } -}; \ No newline at end of file diff --git a/httpserverapp/System.h b/httpserverapp/System.h deleted file mode 100644 index 29ee42b..0000000 --- a/httpserverapp/System.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#ifdef WIN32 - #include -#elif POSIX - #include - #include - #include - #include -#else - #error "Undefine platform" -#endif - -#ifndef SIGUSR1 - #define SIGUSR1 10 -#endif - -#include -#include -#include - -namespace System -{ -#ifdef WIN32 - typedef ::SOCKET native_socket_type; -#elif POSIX - typedef int native_socket_type; -#else - #error "Undefine platform" -#endif - -#ifdef WIN32 - typedef ::DWORD native_processid_type; -#elif POSIX - typedef ::pid_t native_processid_type; -#else - #error "Undefine platform" -#endif - - inline size_t getProcessorsCount() - { - #ifdef WIN32 - ::SYSTEM_INFO si = {0}; - ::GetSystemInfo(&si); - return si.dwNumberOfProcessors; - #elif POSIX - return ::get_nprocs(); - #else - #error "Undefine platform" - #endif - } - - inline native_processid_type getProcessId() - { - #ifdef WIN32 - return ::GetCurrentProcessId(); - #elif POSIX - return ::getpid(); - #else - #error "Undefine platform" - #endif - } - - bool sendSignal(const native_processid_type pid, const int signal); - - inline bool isDoneThread(const std::thread::native_handle_type handle) - { - #ifdef WIN32 - return WAIT_OBJECT_0 == ::WaitForSingleObject(handle, 0); - #elif POSIX - return 0 != ::pthread_kill(handle, 0); - #else - #error "Undefine platform" - #endif - } - - inline std::string getTempDir() - { - #ifdef WIN32 - return std::string("C:/Temp/"); // FIXME: Windows temp dir - #elif POSIX - return std::string("/tmp/"); - #else - #error "Undefine platform" - #endif - } - - bool getFileSizeAndTimeGmt(const std::string &, size_t *, time_t *); -}; \ No newline at end of file diff --git a/httpserverapp/Test.cpp b/httpserverapp/Test.cpp deleted file mode 100644 index 24bf3ab..0000000 --- a/httpserverapp/Test.cpp +++ /dev/null @@ -1,74 +0,0 @@ - -#include "Test.h" - -bool test(HttpServer::ServerRequest &request, HttpServer::ServerResponse &response) -{ - const std::unordered_map &incoming_headers = request.headers; - const std::unordered_multimap &incoming_data = request.data; - - HttpServer::Socket &socket = response.socket; - std::map &headers_outgoing = response.headers; - - std::string s; - - for (auto h = incoming_headers.cbegin(); h != incoming_headers.cend(); ++h) - { - s += h->first + ":" + h->second + "\n"; - } - - s += "\n"; - - for (auto v = incoming_data.cbegin(); v != incoming_data.cend(); ++v) - { - s += v->first + ": " + v->second + "\n"; - } - -/* s = "\ -\ -\ - \ -\ - \ -
1\ - 2\ -
3\ - 4\ -
";*/ - - headers_outgoing[""] = "HTTP/1.1 200 OK"; - headers_outgoing["Content-Type"] = "text/plain; charset=utf-8"; -// headers_outgoing["Access-Control-Allow-Origin"] = "*"; -// headers_outgoing["Content-Type"] = "text/html; charset=utf-8"; - headers_outgoing["Content-Length"] = std::to_string(s.length() ); - headers_outgoing["Connection"] = "keep-alive"; - headers_outgoing["Date"] = Utils::getDatetimeStringValue(); - - std::string headers; - - for (auto h = headers_outgoing.cbegin(); h != headers_outgoing.cend(); ++h) - { - if (h->first.length() ) - { - headers += h->first + ": "; - } - - headers += h->second + "\r\n"; - } - - headers += "\r\n"; - - headers_outgoing.clear(); - -// s = headers + s; - - std::chrono::milliseconds timeout(5000); - - socket.nonblock_send(headers, timeout); - - if ( (size_t)~0 == socket.nonblock_send(s, timeout) ) - { - socket.close(); - } - - return EXIT_SUCCESS; -} diff --git a/httpserverapp/Test.h b/httpserverapp/Test.h deleted file mode 100644 index 254aa58..0000000 --- a/httpserverapp/Test.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "ServerRequest.h" -#include "ServerResponse.h" - -#include "Utils.h" - -#include - -bool test(HttpServer::ServerRequest &, HttpServer::ServerResponse &); diff --git a/httpserverapp/Utils.cpp b/httpserverapp/Utils.cpp deleted file mode 100644 index 498bc6d..0000000 --- a/httpserverapp/Utils.cpp +++ /dev/null @@ -1,308 +0,0 @@ - -#include "Utils.h" - -#include - -namespace Utils -{ - void trim(std::string &str) - { - size_t last = str.find_last_not_of(" \t\n\v\f\r"); - - if (std::string::npos == last) - { - return str.clear(); - } - - str.assign(str.cbegin() + str.find_first_not_of(" \t\n\v\f\r"), str.cbegin() + last + 1); - } - - char *stlStringToPChar(const std::string &str) - { - 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 stlMapToRawPairs(Utils::raw_pair *raw[], const std::map &map) - { - if (raw && map.size() ) - { - raw_pair *arr = new raw_pair[map.size()]; - - *raw = arr; - - size_t i = 0; - - for (auto it = map.cbegin(); map.cend() != it; ++it, ++i) - { - arr[i].key = stlStringToPChar(it->first); - arr[i].value = stlStringToPChar(it->second); - } - } - } - - void stlUnorderedMapToRawPairs(Utils::raw_pair *raw[], const std::unordered_map &map) - { - if (raw && map.size() ) - { - raw_pair *arr = new raw_pair[map.size()]; - - *raw = arr; - - size_t i = 0; - - for (auto it = map.cbegin(); map.cend() != it; ++it, ++i) - { - arr[i].key = stlStringToPChar(it->first); - arr[i].value = stlStringToPChar(it->second); - } - } - } - - void stlUnorderedMultimapToRawPairs(Utils::raw_pair *raw[], const std::unordered_multimap &map) - { - if (raw && map.size() ) - { - raw_pair *arr = new raw_pair[map.size()]; - - *raw = arr; - - size_t i = 0; - - for (auto it = map.cbegin(); map.cend() != it; ++it, ++i) - { - arr[i].key = stlStringToPChar(it->first); - arr[i].value = stlStringToPChar(it->second); - } - } - } - - 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 rawPairsToStlMap(std::map &map, const Utils::raw_pair raw[], const size_t count) - { - for (size_t i = 0; i < count; ++i) - { - map.emplace(raw[i].key ? raw[i].key : "", raw[i].value); - } - } - - void rawPairsToStlUnorderedMap(std::unordered_map &map, const Utils::raw_pair raw[], const size_t count) - { - for (size_t i = 0; i < count; ++i) - { - map.emplace(raw[i].key, raw[i].value); - } - } - - void rawPairsToStlUnorderedMultimap(std::unordered_multimap &map, const Utils::raw_pair raw[], const size_t count) - { - for (size_t i = 0; i < count; ++i) - { - map.emplace(raw[i].key, raw[i].value); - } - } - - 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, 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_days { - {"Sun", 0}, {"Mon", 1}, {"Tue", 2}, {"Wed", 3}, {"Thu", 4}, {"Fri", 5}, {"Sat", 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} - }; - - const size_t str_mon_length = 32; - char *s_mon = new char[str_mon_length]; - ::memset(s_mon, 0, str_mon_length); - - struct ::tm tc = {0}; - - // Parse RFC 822 - #ifdef WIN32 - if (std::numeric_limits::max() != ::sscanf_s(strTime.c_str(), "%*s %d %3s %d %d:%d:%d", &tc.tm_mday, s_mon, str_mon_length, &tc.tm_year, &tc.tm_hour, &tc.tm_min, &tc.tm_sec) ) - #else - if (std::numeric_limits::max() != ::sscanf(strTime.c_str(), "%*s %d %3s %d %d:%d:%d", &tc.tm_mday, s_mon, &tc.tm_year, &tc.tm_hour, &tc.tm_min, &tc.tm_sec) ) - #endif - { - tc.tm_year -= 1900; - - auto it_mon = map_months.find(s_mon); - - if (map_months.end() != it_mon) - { - tc.tm_mon = it_mon->second; - } - } - - delete[] s_mon; - - return ::mktime(&tc); - } - - std::string getDatetimeStringValue(const ::time_t tTime, const bool isGmtTime) - { - char buf[64]; - - ::time_t cur_time = tTime; - - if (std::numeric_limits<::time_t>::max() == tTime) - { - ::time(&cur_time); - } - - #ifdef WIN32 - struct ::tm stm = {0}; - - if (isGmtTime) - { - ::localtime_s(&stm, &cur_time); - } - else - { - ::gmtime_s(&stm, &cur_time); - } - - // RFC 822 - ::strftime(buf, 64, "%a, %d %b %Y %H:%M:%S GMT", &stm); - #else - struct ::tm *ptm = isGmtTime ? localtime(&cur_time) : gmtime(&cur_time); - - // RFC 822 - ::strftime(buf, 64, "%a, %d %b %G %H:%M:%S GMT", ptm); - #endif - - return std::string(buf); - } - - 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 false; - } - - 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); - - ++delimiter; - - std::string value = cookieHeader.substr(delimiter, std::string::npos != next_value ? next_value - delimiter : next_value); - trim(value); - - cookies.emplace(std::move(key), std::move(value) ); - - if (std::string::npos != next_value) - { - ++next_value; - } - } - - return true; - } -}; \ No newline at end of file diff --git a/httpserverapp/Utils.h b/httpserverapp/Utils.h deleted file mode 100644 index 103dccb..0000000 --- a/httpserverapp/Utils.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "RawData.h" -#include "FileIncoming.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Utils -{ - inline void tolower(std::string &str, const std::locale &loc) - { - for (auto &c : str) - { - c = std::tolower(c, loc); - } - } - - void trim(std::string &); - - inline std::string getUniqueName() - { - std::stringstream s; - s << std::hex << std::chrono::high_resolution_clock::now().time_since_epoch().count(); - return s.str(); - } - - char *stlStringToPChar(const std::string &); - - void stlMapToRawPairs(Utils::raw_pair *[], const std::map &); - void stlMultimapToRawPairs(Utils::raw_pair *[], const std::multimap &); - void stlUnorderedMapToRawPairs(Utils::raw_pair *[], const std::unordered_map &); - void stlUnorderedMultimapToRawPairs(Utils::raw_pair *[], const std::unordered_multimap &); - void filesIncomingToRawFilesInfo(Utils::raw_fileinfo *[], const std::unordered_multimap &); - void rawPairsToStlMap(std::map &, const Utils::raw_pair [], const size_t); - void rawPairsToStlMultimap(std::multimap &, const Utils::raw_pair [], const size_t); - void rawPairsToStlUnorderedMap(std::unordered_map &, const Utils::raw_pair [], const size_t); - void rawPairsToStlUnorderedMultimap(std::unordered_multimap &, const Utils::raw_pair [], const size_t); - void rawFilesInfoToFilesIncoming(std::unordered_multimap &, const Utils::raw_fileinfo [], const size_t); - void destroyRawPairs(Utils::raw_pair [], const size_t); - void destroyRawFilesInfo(Utils::raw_fileinfo [], const size_t); - - time_t stringTimeToTimestamp(const std::string &); - - std::string getDatetimeStringValue(const ::time_t = std::numeric_limits<::time_t>::max(), const bool = false); - - size_t getNumberLength(const size_t number); - - bool parseCookies(const std::string &, std::unordered_multimap &); -}; diff --git a/httpserverapp/httpserverapp.cproj b/httpserverapp/httpserverapp.cproj deleted file mode 100644 index 3393b9c..0000000 --- a/httpserverapp/httpserverapp.cproj +++ /dev/null @@ -1,56 +0,0 @@ - - - - Debug - AnyCPU - {4C15CBD7-D1C0-4869-AEE1-14CB41328514} - - - - CPP - Bin - - - true - bin\Debug - httpserverapp - SharedLibrary - DEBUG MONODEVELOP POSIX - . - -std=c++11 - All - - - bin\Release - httpserverapp - SharedLibrary - 3 - MONODEVELOP POSIX - . - -std=c++11 - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/httpserverapp/httpserverapp.md.pc b/httpserverapp/httpserverapp.md.pc deleted file mode 100644 index d109504..0000000 --- a/httpserverapp/httpserverapp.md.pc +++ /dev/null @@ -1,5 +0,0 @@ -Name: httpserverapp -Description: -Version: 0.1 -Libs: -L"/media/projects/httpserverapp/httpserverapp/bin/Release" -lhttpserverapp -Cflags: -I"/media/projects/httpserverapp/httpserverapp" diff --git a/projects/msvs/httpserverapp.sln b/projects/msvs/httpserverapp.sln new file mode 100644 index 0000000..07921f1 --- /dev/null +++ b/projects/msvs/httpserverapp.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2013 for Windows Desktop +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "httpserverapp", "httpserverapp.vcxproj", "{D1565609-DDDE-4521-8AD9-0D1AD7D18525}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D1565609-DDDE-4521-8AD9-0D1AD7D18525}.Debug|Win32.ActiveCfg = Debug|x64 + {D1565609-DDDE-4521-8AD9-0D1AD7D18525}.Debug|Win32.Build.0 = Debug|x64 + {D1565609-DDDE-4521-8AD9-0D1AD7D18525}.Debug|x64.ActiveCfg = Debug|x64 + {D1565609-DDDE-4521-8AD9-0D1AD7D18525}.Debug|x64.Build.0 = Debug|x64 + {D1565609-DDDE-4521-8AD9-0D1AD7D18525}.Release|Win32.ActiveCfg = Release|Win32 + {D1565609-DDDE-4521-8AD9-0D1AD7D18525}.Release|Win32.Build.0 = Release|Win32 + {D1565609-DDDE-4521-8AD9-0D1AD7D18525}.Release|x64.ActiveCfg = Release|x64 + {D1565609-DDDE-4521-8AD9-0D1AD7D18525}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/projects/msvs/httpserverapp.vcxproj b/projects/msvs/httpserverapp.vcxproj new file mode 100644 index 0000000..cec4a76 --- /dev/null +++ b/projects/msvs/httpserverapp.vcxproj @@ -0,0 +1,222 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {D1565609-DDDE-4521-8AD9-0D1AD7D18525} + Win32Proj + httpserverapp + + + + DynamicLibrary + true + v141 + MultiByte + + + DynamicLibrary + true + v141 + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + true + ..\..\build\$(Platform)\$(Configuration)\ + ..\..\build\$(Platform)\$(Configuration)\ + D:\usr\include;$(IncludePath) + D:\usr\lib;$(LibraryPath) + + + true + ..\..\build\$(Platform)\$(Configuration)\ + ..\..\build\$(Platform)\$(Configuration)\ + D:\usr\include;$(IncludePath) + D:\usr\lib64;$(LibraryPath) + + + false + ..\..\build\$(Platform)\$(Configuration)\ + ..\..\build\$(Platform)\$(Configuration)\ + D:\usr\include;$(IncludePath) + D:\usr\lib;$(LibraryPath) + + + false + ..\..\build\$(Platform)\$(Configuration)\ + ..\..\build\$(Platform)\$(Configuration)\ + D:\usr\include;$(IncludePath) + D:\usr\lib64;$(LibraryPath) + + + + NotUsing + Level3 + Disabled + WIN32;NOMINMAX;DEBUG;%(PreprocessorDefinitions) + $(IntDir)asm\ + $(IntDir)obj\ + true + -Dssize_t=long %(AdditionalOptions) + + + Windows + true + ws2_32.lib;libgnutls.dll.a;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + WIN32;NOMINMAX;DEBUG;%(PreprocessorDefinitions) + $(IntDir)asm\ + $(IntDir)obj\ + true + -Dssize_t=long %(AdditionalOptions) + + + Windows + true + ws2_32.lib;libgnutls.dll.a;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NOMINMAX;NDEBUG;%(PreprocessorDefinitions) + $(IntDir)asm\ + $(IntDir)obj\ + true + -Dssize_t=long %(AdditionalOptions) + + + Windows + true + true + true + ws2_32.lib;libgnutls.dll.a;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NOMINMAX;NDEBUG;%(PreprocessorDefinitions) + $(IntDir)asm\ + $(IntDir)obj\ + true + -Dssize_t=long %(AdditionalOptions) + + + Windows + true + true + true + ws2_32.lib;libgnutls.dll.a;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/projects/msvs/httpserverapp.vcxproj.filters b/projects/msvs/httpserverapp.vcxproj.filters new file mode 100644 index 0000000..1e4277e --- /dev/null +++ b/projects/msvs/httpserverapp.vcxproj.filters @@ -0,0 +1,147 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 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 + + + \ No newline at end of file diff --git a/projects/msvs/httpserverapp.vcxproj.user b/projects/msvs/httpserverapp.vcxproj.user new file mode 100644 index 0000000..223f65b --- /dev/null +++ b/projects/msvs/httpserverapp.vcxproj.user @@ -0,0 +1,18 @@ + + + + + + $(ProjectDir) + WindowsLocalDebugger + + + + + $(ProjectDir) + WindowsLocalDebugger + + + true + + \ No newline at end of file diff --git a/projects/qt-creator/httpserverapp.qbs b/projects/qt-creator/httpserverapp.qbs new file mode 100644 index 0000000..56d90fc --- /dev/null +++ b/projects/qt-creator/httpserverapp.qbs @@ -0,0 +1,69 @@ +import qbs + +Project { + DynamicLibrary { + name: "httpserverapp" + + Depends { name: "cpp" } + cpp.cxxLanguageVersion: "c++14" + + cpp.defines: qbs.buildVariant == "debug" ? ["DEBUG"] : base + + cpp.dynamicLibraries: ["gnutls"] + + Properties { + condition: qbs.targetOS.contains("linux") + cpp.defines: outer.concat(["POSIX"]) + cpp.dynamicLibraries: outer.concat(["dl", "pthread"]) + } + Properties { + condition: qbs.targetOS.contains("windows") + cpp.defines: outer.concat(["WIN32", "NOMINMAX"]) + } + + files: [ + "../../src/Init.cpp", + "../../src/Init.h", + "../../src/application/Test.cpp", + "../../src/application/Test.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/WebSocket.cpp", + "../../src/server/protocol/WebSocket.h", + "../../src/utils/Event.cpp", + "../../src/utils/Event.h", + "../../src/transfer/FileIncoming.cpp", + "../../src/transfer/FileIncoming.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/server/Request.cpp", + "../../src/server/Request.h", + "../../src/server/Response.cpp", + "../../src/server/Response.h", + "../../src/transfer/ProtocolVariant.h", + "../../src/transfer/AppRequest.h", + "../../src/transfer/AppResponse.h", + "../../src/socket/Socket.cpp", + "../../src/socket/Socket.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/system/System.cpp", + "../../src/system/System.h", + "../../src/utils/Utils.cpp", + "../../src/utils/Utils.h", + ] + } +} diff --git a/src/Init.cpp b/src/Init.cpp new file mode 100644 index 0000000..0831bdb --- /dev/null +++ b/src/Init.cpp @@ -0,0 +1,365 @@ + +#include "Init.h" + +#include "socket/AdapterDefault.h" + +#include "transfer/FileIncoming.h" +#include "transfer/http2/Http2.h" +#include "utils/Utils.h" + +#include "server/protocol/ServerHttp1.h" +#include "server/protocol/ServerHttp2.h" + +#include +#include + +Socket::Adapter *createSocketAdapter( + Transfer::app_request *request, + void *addr +) { + if (request->tls_session) { + return new (addr) Socket::AdapterTls(request->tls_session); + } + + return new (addr) Socket::AdapterDefault(request->socket); +} + +void destroySocketAdapter(Socket::Adapter *adapter) { + if (adapter) { + adapter->~Adapter(); + } +} + +std::string utf8ToLocal(const std::string &u8str) +{ + std::locale loc(""); + + std::wstring_convert > conv; + std::wstring wstr = conv.from_bytes(u8str); + + std::string str(wstr.size(), 0); + + std::use_facet >(loc).narrow( + wstr.data(), + wstr.data() + wstr.size(), + '?', + &str.front() + ); + + return str; +} + +std::string getClearPath(const std::string &path) +{ + const size_t pos = path.find_first_of("?#"); + + const std::string clean = Utils::urlDecode( + std::string::npos == pos + ? path + : path.substr(0, pos) + ); + +#ifdef WIN32 + return utf8ToLocal(clean); +#else + return clean; +#endif +} + +static void getIncomingVars( + std::unordered_multimap ¶ms, + const std::string &uri +) { + const size_t start = uri.find('?'); + + if (std::string::npos == start) { + return; + } + + const size_t finish = uri.find('#'); + + if (finish < start) { + return; + } + + for ( + size_t var_pos = start + 1, var_end = 0; + std::string::npos != var_end; + var_pos = var_end + 1 + ) { + var_end = uri.find('&', var_pos); + + if (var_end > finish) { + var_end = std::string::npos; + } + + size_t delimiter = uri.find('=', var_pos); + + if (delimiter >= var_end) { + std::string var_name = Utils::urlDecode( + uri.substr( + var_pos, + std::string::npos != var_end + ? var_end - var_pos + : std::string::npos + ) + ); + + params.emplace( + std::move(var_name), + std::string() + ); + } else { + std::string var_name = Utils::urlDecode( + uri.substr( + var_pos, + delimiter - var_pos + ) + ); + + ++delimiter; + + std::string var_value = Utils::urlDecode( + uri.substr( + delimiter, + std::string::npos != var_end + ? var_end - delimiter + : std::string::npos + ) + ); + + params.emplace( + std::move(var_name), + std::move(var_value) + ); + } + } +} + +bool initServerObjects( + HttpServer::Request *procRequest, + HttpServer::Response *procResponse, + const Transfer::app_request *request, + Socket::Adapter *socket_adapter +) { + const uint8_t *src = reinterpret_cast(request->request_data); + + size_t protocol_number; + src = Utils::unpackNumber(&protocol_number, src); + Transfer::ProtocolVariant protocol_variant = static_cast(protocol_number); + HttpServer::ServerProtocol *prot = nullptr; + + std::string document_root; + std::string host; + std::string path; + std::string method; + std::unordered_multimap params; + std::unordered_multimap headers; + std::unordered_multimap data; + std::unordered_multimap files; + std::unordered_multimap cookies; + + bool success = true; + + switch (protocol_variant) + { + case Transfer::ProtocolVariant::HTTP_1: + { + src = Utils::unpackString(document_root, src); + src = Utils::unpackString(host, src); + src = Utils::unpackString(path, src); + src = Utils::unpackString(method, src); + src = Utils::unpackContainer(headers, src); + src = Utils::unpackContainer(data, src); + src = Utils::unpackFilesIncoming(files, src); + + auto const it_cookie = headers.find("cookie"); + + if (headers.cend() != it_cookie) { + Utils::parseCookies(it_cookie->second, cookies); + } + + getIncomingVars(params, path); + + prot = new HttpServer::ServerHttp1(socket_adapter); + + break; + } + + case Transfer::ProtocolVariant::HTTP_2: + { + src = Utils::unpackString(document_root, src); + src = Utils::unpackString(host, src); + src = Utils::unpackString(path, src); + src = Utils::unpackString(method, src); + + size_t number; + + src = Utils::unpackNumber(&number, src); + const uint32_t stream_id = static_cast(number); + + Http2::ConnectionSettings settings; + + src = Utils::unpackNumber(&number, src); + settings.header_table_size = static_cast(number); + src = Utils::unpackNumber(&number, src); + settings.enable_push = static_cast(number); + src = Utils::unpackNumber(&number, src); + settings.max_concurrent_streams = static_cast(number); + src = Utils::unpackNumber(&number, src); + settings.initial_window_size = static_cast(number); + src = Utils::unpackNumber(&number, src); + settings.max_frame_size = static_cast(number); + src = Utils::unpackNumber(&number, src); + settings.max_header_list_size = static_cast(number); + + std::deque > dynamic_table; + src = Utils::unpackVector(dynamic_table, src); + + std::mutex *mtx = nullptr; + src = Utils::unpackPointer(reinterpret_cast(&mtx), src); + + src = Utils::unpackContainer(headers, src); + src = Utils::unpackContainer(data, src); + src = Utils::unpackFilesIncoming(files, src); + + auto const it_cookie = headers.find("cookie"); + + if (headers.cend() != it_cookie) { + Utils::parseCookies(it_cookie->second, cookies); + } + + getIncomingVars(params, path); + + Http2::OutStream *stream = new Http2::OutStream( + stream_id, + settings, + Http2::DynamicTable( + settings.header_table_size, + settings.max_header_list_size, + std::move(dynamic_table) + ), + mtx + ); + + prot = new HttpServer::ServerHttp2(socket_adapter, stream); + + break; + } + + default: { + success = false; + break; + } + } + + *procRequest = HttpServer::Request { + prot, + std::move(document_root), + std::move(host), + std::move(path), + std::move(method), + std::move(params), + std::move(headers), + std::move(data), + std::move(files), + std::move(cookies), + protocol_variant + }; + + *procResponse = HttpServer::Response { + prot, + protocol_variant, + std::unordered_map(), + Http::StatusCode::EMPTY + }; + + return success; +} + +void freeProtocolData(HttpServer::Response *response) { + if (response) { + delete response->prot; + } +} + +bool isSwitchingProtocols( + const HttpServer::Request &request, + HttpServer::Response &response +) { + // Check for https is not set + if (request.prot->getSocket()->get_tls_session() != nullptr) { + return false; + } + + // Check for upgrade to https + /*auto const it_upgrade_insecure = request.headers.find("upgrade-insecure-requests"); + + if (request.headers.cend() != it_upgrade_insecure) { + if (it_upgrade_insecure->second == "1") { + response.status = Http::StatusCode::MOVED_TEMPORARILY; + response.headers["location"] = "https://" + request.host + request.path; + response.headers["strict-transport-security"] = "max-age=86400"; + + const std::string headers = "HTTP/1.1 307 Moved Temporarily\r\nLocation: https://" + request.host + request.path + "\r\nStrict-Transport-Security: max-age=86400\r\n\r\n"; + + response.prot->getSocket()->nonblock_send(headers, std::chrono::milliseconds(5000) ); + + return true; + } + }*/ + + // Check if switch protocol to h2c + auto const it_upgrade = request.headers.find("upgrade"); + + if (request.headers.cend() == it_upgrade) { + return false; + } + + auto const it_connection = request.headers.find("connection"); + + if (request.headers.cend() == it_connection) { + return false; + } + + std::vector list = Utils::explode(it_connection->second, ','); + + bool is_upgrade = false; + + for (auto &item : list) { + Utils::toLower(item); + + if ("upgrade" == item) { + is_upgrade = true; + break; + } + } + + if (false == is_upgrade) { + return false; + } + + const std::string &upgrade = it_upgrade->second; + + if ("h2c" != upgrade) { + return false; + } + + auto const it_settings = request.headers.find("http2-settings"); + + if (request.headers.cend() == it_settings) { + return false; + } + + response.headers["connection"] = "upgrade"; + response.headers["upgrade"] = "h2c"; + + const std::string headers = "HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nUpgrade: h2c\r\n\r\n"; + + response.prot->getSocket()->nonblock_send( + headers, + std::chrono::milliseconds(5000) + ); + + return true; +} diff --git a/src/Init.h b/src/Init.h new file mode 100644 index 0000000..b7a4c0b --- /dev/null +++ b/src/Init.h @@ -0,0 +1,29 @@ +#pragma once + +#include "server/Request.h" +#include "server/Response.h" +#include "transfer/AppRequest.h" +#include "transfer/AppResponse.h" + +Socket::Adapter *createSocketAdapter( + Transfer::app_request *request, + void *addr +); + +void destroySocketAdapter(Socket::Adapter *adapter); + +std::string getClearPath(const std::string &path); + +bool initServerObjects( + HttpServer::Request *procRequest, + HttpServer::Response *procResponse, + const Transfer::app_request *request, + Socket::Adapter *socket_adapter +); + +void freeProtocolData(HttpServer::Response *response); + +bool isSwitchingProtocols( + const HttpServer::Request &request, + HttpServer::Response &response +); diff --git a/src/Main.cpp b/src/Main.cpp new file mode 100644 index 0000000..270689e --- /dev/null +++ b/src/Main.cpp @@ -0,0 +1,80 @@ + +#include "Main.h" +#include "Init.h" + +#include "application/Test.h" + +#include "utils/Utils.h" + +EXPORT bool application_init(const char *root) +{ + return true; +} + +EXPORT int application_call( + Transfer::app_request *request, + Transfer::app_response *response +) { + // Allocate memory on the stack + uint8_t addr[sizeof(Socket::AdapterTls)]; + + // Create the socket adapter + Socket::Adapter *socket_adapter = createSocketAdapter(request, addr); + + HttpServer::Request proc_request; + HttpServer::Response proc_response; + + if (initServerObjects(&proc_request, &proc_response, request, socket_adapter) == false) { + return EXIT_FAILURE; + } + + const std::string absolute_path = proc_request.document_root + getClearPath(proc_request.path); + + int result = EXIT_SUCCESS; + + if (isSwitchingProtocols(proc_request, proc_response) ) { + + } else if ( + std::string::npos == absolute_path.find("/../") && + System::isFileExists(absolute_path) + ) { + auto it_connection = proc_request.headers.find("connection"); + + if (proc_request.headers.cend() != it_connection) { + proc_response.headers["connection"] = it_connection->second; + } + + proc_response.headers["x-sendfile"] = absolute_path; + } else { + // Call application + result = Application::test(proc_request, proc_response); + } + + destroySocketAdapter(socket_adapter); + + if (proc_response.headers.size() ) { + response->data_size = Utils::getPackContainerSize(proc_response.headers); + response->response_data = new uint8_t[response->data_size]; + + Utils::packContainer( + reinterpret_cast(response->response_data), + proc_response.headers + ); + } + + freeProtocolData(&proc_response); + + return result; +} + +EXPORT void application_clear(void *response_data, size_t response_size) +{ + if (response_data && response_size) { + delete[] reinterpret_cast(response_data); + } +} + +EXPORT void application_final(const char *root) +{ + +} diff --git a/src/Main.h b/src/Main.h new file mode 100644 index 0000000..ba712da --- /dev/null +++ b/src/Main.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef _MSC_VER + #define EXPORT extern "C" __declspec(dllexport) +#else + #define EXPORT extern "C" +#endif diff --git a/src/application/Test.cpp b/src/application/Test.cpp new file mode 100644 index 0000000..78249e0 --- /dev/null +++ b/src/application/Test.cpp @@ -0,0 +1,148 @@ + +#include "Test.h" + +#include "../utils/Utils.h" + +namespace Application +{ + bool test( + HttpServer::Request &request, + HttpServer::Response &response + ) { + // Output incoming headers + + std::string s = R"( + + + + + + + + + + +)"; + + for (auto const &pair : request.headers) + { + s += R"( + + + +)"; + } + + // Output incoming url parameters + + s += R"( + + + + + + + + + + +)"; + + for (auto const &pair : request.params) + { + s += R"( + + + +)"; + } + + // Output incoming form data + + s += R"( + + + + + + + + + + +)"; + + for (auto const &pair : request.data) + { + s += R"( + + + +)"; + } + + // Output info about incoming files + + s += R"( + + + + + + + + + + + + + +)"; + + for (auto const &pair : request.files) + { + const std::string &field_name = pair.first; + const Transfer::FileIncoming &file = pair.second; + + s += R"( + + + + + + +)"; + } + + s += R"( +
Incoming headers
Header nameHeader value
)" + pair.first + R"()" + pair.second + R"(
Incoming url parameters
Parameter nameParameter value
)" + pair.first + R"()" + pair.second + R"(
Incoming form data
Form fieldField value
)" + pair.first + R"()" + pair.second + R"(
Incoming files
Form fieldOriginal nameFile typeFile sizeUploaded name
)" + field_name + R"()" + file.getName() + R"()" + file.getType() + R"()" + std::to_string(file.getSize() ) + R"()" + file.getTmpName() + R"(
)"; + + std::unordered_map &headers = response.headers; + + // Set outgoing headers + + response.setStatusCode(Http::StatusCode::OK); + headers["content-type"] = "text/html; charset=utf-8"; + headers["accept-ranges"] = "bytes"; + headers["content-length"] = std::to_string(s.size() ); + headers["connection"] = "keep-alive"; + headers["date"] = Utils::getDatetimeAsString(); + + // Additional headers + // In additional headers may be placed values of cookies + std::vector > additional; + // additional.emplace_back("set-cookie", "key=value; expires=; path=/; domain=*"); + + // Send headers and data + + const std::chrono::milliseconds timeout(5000); + + if (response.sendHeaders(additional, timeout, s.empty() ) ) { + if (s.empty() == false) { + response.sendData(s.data(), s.size(), timeout, true); + } + } + + return EXIT_SUCCESS; + } +} diff --git a/src/application/Test.h b/src/application/Test.h new file mode 100644 index 0000000..d6a6316 --- /dev/null +++ b/src/application/Test.h @@ -0,0 +1,12 @@ +#pragma once + +#include "../server/Request.h" +#include "../server/Response.h" + +namespace Application +{ + bool test( + HttpServer::Request &, + HttpServer::Response & + ); +} diff --git a/src/server/Request.cpp b/src/server/Request.cpp new file mode 100644 index 0000000..c78934a --- /dev/null +++ b/src/server/Request.cpp @@ -0,0 +1,75 @@ + +#include "Request.h" + +namespace HttpServer +{ + std::string Request::getHeader(const std::string &key) const { + auto it = headers.find(key); + return headers.end() != it ? it->second : std::string(); + } + + bool Request::isDataExists(const std::string &key) const { + return data.cend() != data.find(key); + } + + std::string Request::getDataAsString(const std::string &key) const { + auto it = data.find(key); + return data.end() != it ? it->second : std::string(); + } + + std::vector Request::getDataAsArray(const std::string &key) const + { + std::vector arr; + + size_t count = data.count(key); + + if (count) { + auto range = data.equal_range(key); + + arr.resize(count); + + size_t i = 0; + + for (auto it = range.first; it != range.second; ++it) { + arr[i++] = it->second; + } + } + + return arr; + } + + bool Request::isFileExists(const std::string &key) const { + return this->files.cend() != this->files.find(key); + } + + Transfer::FileIncoming Request::getFile(const std::string &key) const { + auto const it = this->files.find(key); + return this->files.end() != it ? it->second : Transfer::FileIncoming(); + } + + std::vector Request::getFilesAsArray(const std::string &key) const + { + std::vector arr; + + const size_t count = this->files.count(key); + + if (count) { + auto const range = this->files.equal_range(key); + + arr.resize(count); + + size_t i = 0; + + for (auto it = range.first; it != range.second; ++it) { + arr[i++] = it->second; + } + } + + return arr; + } + + std::string Request::getCookieAsString(const std::string &cookieName) const { + auto it = cookies.find(cookieName); + return cookies.end() != it ? it->second : std::string(); + } +} diff --git a/src/server/Request.h b/src/server/Request.h new file mode 100644 index 0000000..1255c65 --- /dev/null +++ b/src/server/Request.h @@ -0,0 +1,56 @@ +#pragma once + +#include "protocol/ServerProtocol.h" +#include "../transfer/FileIncoming.h" +#include "../transfer/ProtocolVariant.h" + +#include + +namespace HttpServer +{ + /** + * Структура запроса (входные данные) + * + * @member const Socket::Adapter &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 Request + { + ServerProtocol *prot; + std::string document_root; + std::string host; + std::string path; + std::string method; + std::unordered_multimap params; + std::unordered_multimap headers; + std::unordered_multimap data; + std::unordered_multimap files; + std::unordered_multimap cookies; + + Transfer::ProtocolVariant protocol_variant; + + public: + std::string getHeader(const std::string &key) const; + + bool isDataExists(const std::string &key) const; + + std::string getDataAsString(const std::string &key) const; + + std::vector getDataAsArray(const std::string &key) const; + + bool isFileExists(const std::string &key) const; + + Transfer::FileIncoming getFile(const std::string &key) const; + + std::vector getFilesAsArray(const std::string &key) const; + + std::string getCookieAsString(const std::string &cookieName) const; + }; +} diff --git a/src/server/Response.cpp b/src/server/Response.cpp new file mode 100644 index 0000000..27ec2c6 --- /dev/null +++ b/src/server/Response.cpp @@ -0,0 +1,60 @@ + +#include "Response.h" + +#include "../transfer/http2/HPack.h" + +#include + +namespace HttpServer +{ + void Response::setStatusCode( + const Http::StatusCode status + ) noexcept { + this->status = status; + } + + bool Response::sendHeaders( + const std::vector > &additional, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const { + std::vector > headers; + + headers.reserve( + this->headers.size() + additional.size() + ); + + headers.insert( + headers.end(), + this->headers.cbegin(), + this->headers.cend() + ); + + headers.insert( + headers.end(), + additional.cbegin(), + additional.cend() + ); + + return this->prot->sendHeaders( + this->status, + headers, + timeout, + endStream + ); + } + + long Response::sendData( + const void *src, + const size_t size, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const noexcept { + return this->prot->sendData( + src, + size, + timeout, + endStream + ); + } +} diff --git a/src/server/Response.h b/src/server/Response.h new file mode 100644 index 0000000..79ad7a8 --- /dev/null +++ b/src/server/Response.h @@ -0,0 +1,35 @@ +#pragma once + +#include "protocol/ServerProtocol.h" +#include "../transfer/ProtocolVariant.h" +#include "../transfer/HttpStatusCode.h" + +#include +#include + +namespace HttpServer +{ + struct Response + { + ServerProtocol *prot; + Transfer::ProtocolVariant protocol_variant; + std::unordered_map headers; + Http::StatusCode status; + + public: + void setStatusCode(const Http::StatusCode status) noexcept; + + bool sendHeaders( + const std::vector > &additional, + const std::chrono::milliseconds &timeout, + const bool endStream = true + ) const; + + long sendData( + const void *src, + const size_t size, + const std::chrono::milliseconds &timeout, + const bool endStream = true + ) const noexcept; + }; +} diff --git a/src/server/protocol/ServerHttp1.cpp b/src/server/protocol/ServerHttp1.cpp new file mode 100644 index 0000000..350f7c3 --- /dev/null +++ b/src/server/protocol/ServerHttp1.cpp @@ -0,0 +1,41 @@ + +#include "ServerHttp1.h" + +namespace HttpServer +{ + ServerHttp1::ServerHttp1(Socket::Adapter *sock) noexcept + : ServerProtocol(sock) + { + + } + + bool ServerHttp1::sendHeaders( + const Http::StatusCode status, + std::vector > &headers, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const { + std::string out = "HTTP/1.1 " + std::to_string(static_cast(status) ) + "\r\n"; + + for (auto const &h : headers) { + out += h.first + ": " + h.second + "\r\n"; + } + + out += "\r\n"; + + return this->sock->nonblock_send(out, timeout) > 0; + } + + long ServerHttp1::sendData( + const void *src, + const size_t size, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const noexcept { + return this->sock->nonblock_send( + src, + size, + timeout + ); + } +} diff --git a/src/server/protocol/ServerHttp1.h b/src/server/protocol/ServerHttp1.h new file mode 100644 index 0000000..55b3b08 --- /dev/null +++ b/src/server/protocol/ServerHttp1.h @@ -0,0 +1,26 @@ +#pragma once + +#include "ServerProtocol.h" + +namespace HttpServer +{ + class ServerHttp1 : public ServerProtocol + { + public: + ServerHttp1(Socket::Adapter *sock) noexcept; + + virtual bool sendHeaders( + const Http::StatusCode status, + std::vector > &headers, + const std::chrono::milliseconds &timeout, + const bool endStream = true + ) const override; + + virtual long sendData( + const void *src, + const size_t size, + const std::chrono::milliseconds &timeout, + const bool endStream = true + ) const noexcept override; + }; +} diff --git a/src/server/protocol/ServerHttp2.cpp b/src/server/protocol/ServerHttp2.cpp new file mode 100644 index 0000000..fd64c82 --- /dev/null +++ b/src/server/protocol/ServerHttp2.cpp @@ -0,0 +1,239 @@ + +#include "ServerHttp2.h" + +#include "../../transfer/http2/HPack.h" + +#include +#include + +namespace HttpServer +{ + ServerHttp2::ServerHttp2( + Socket::Adapter *sock, + Http2::OutStream *stream + ) noexcept + : ServerProtocol(sock), stream(stream) + { + + } + + ServerHttp2::~ServerHttp2() noexcept { + delete this->stream; + } + + static uint8_t 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 ServerHttp2::sendHeaders( + const Http::StatusCode status, + std::vector > &headers, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const { + std::vector buf; + buf.reserve(4096); + + buf.resize( + Http2::FRAME_HEADER_SIZE + sizeof(uint8_t) + ); + + headers.emplace( + headers.begin(), + ":status", + std::to_string(static_cast(status) ) + ); + + HPack::pack(buf, headers, this->stream->dynamic_table); + + size_t data_size = ( + buf.size() - Http2::FRAME_HEADER_SIZE - sizeof(uint8_t) + ); + + const uint8_t padding = getPaddingSize(data_size); + const uint16_t padding_size = padding + sizeof(uint8_t); + + if (data_size + padding_size > this->stream->settings.max_frame_size) { + data_size = this->stream->settings.max_frame_size - padding_size; + } + + const uint32_t frame_size = static_cast( + data_size + padding_size + ); + + buf.resize( + frame_size + Http2::FRAME_HEADER_SIZE + ); + + Http2::FrameFlag flags = Http2::FrameFlag::END_HEADERS; + + if (endStream) { + flags |= Http2::FrameFlag::END_STREAM; + } + + flags |= Http2::FrameFlag::PADDED; + + buf[Http2::FRAME_HEADER_SIZE] = char(padding); + + if (padding) { + std::fill( + buf.end() - padding, + buf.end(), + 0 + ); + } + + this->stream->setHttp2FrameHeader( + reinterpret_cast(buf.data() ), + frame_size, + Http2::FrameType::HEADERS, + flags + ); + + const std::unique_lock lock(*this->stream->mtx); + + return this->sock->nonblock_send(buf.data(), buf.size(), timeout) > 0; + } + + void ServerHttp2::sendWindowUpdate( + const uint32_t size, + const std::chrono::milliseconds &timeout + ) const { + std::array buf; + uint8_t *addr = buf.data(); + + addr = this->stream->setHttp2FrameHeader( + addr, + sizeof(uint32_t), + Http2::FrameType::WINDOW_UPDATE, + Http2::FrameFlag::EMPTY + ); + + *reinterpret_cast(addr) = ::htonl(size); + + const std::unique_lock lock(*this->stream->mtx); + + this->sock->nonblock_send(buf.data(), buf.size(), timeout); + } + + long ServerHttp2::sendData( + const void *src, + const size_t size, + const std::chrono::milliseconds &timeout, + const bool endStream + ) const noexcept { + const uint8_t *data = reinterpret_cast(src); + + std::vector buf; + + buf.reserve( + this->stream->settings.max_frame_size + Http2::FRAME_HEADER_SIZE + ); + + size_t total = 0; + + while (total < size) + { + size_t data_size = (size - total < this->stream->settings.max_frame_size) + ? size - total + : this->stream->settings.max_frame_size; + + const uint8_t padding = getPaddingSize(data_size); + const uint16_t padding_size = padding + sizeof(uint8_t); + + if (data_size + padding_size > this->stream->settings.max_frame_size) { + data_size = this->stream->settings.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 - long(this->stream->settings.max_frame_size) <= 0) + { + size_t update_size = this->stream->settings.initial_window_size + + (size - total) - size_t(this->stream->window_size_out); + + if (update_size > Http2::MAX_WINDOW_UPDATE) { + update_size = Http2::MAX_WINDOW_UPDATE; + } + + this->sendWindowUpdate(uint32_t(update_size), timeout); + + this->stream->window_size_out += update_size; + } + + Http2::FrameFlag flags = Http2::FrameFlag::EMPTY; + + if (endStream && (total + data_size >= 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) { + total = 0; + break; + } + + this->stream->window_size_out -= frame_size; + + data += data_size; + total += data_size; + } + + return long(total); + } +} diff --git a/src/server/protocol/ServerHttp2.h b/src/server/protocol/ServerHttp2.h new file mode 100644 index 0000000..b7389f1 --- /dev/null +++ b/src/server/protocol/ServerHttp2.h @@ -0,0 +1,41 @@ +#pragma once + +#include "ServerProtocol.h" + +#include "../../transfer/http2/Http2.h" + +namespace HttpServer +{ + class ServerHttp2 : public ServerProtocol + { + protected: + Http2::OutStream *stream; + + public: + ServerHttp2( + Socket::Adapter *sock, + Http2::OutStream *stream + ) noexcept; + + virtual ~ServerHttp2() noexcept override; + + void sendWindowUpdate( + const uint32_t size, + const std::chrono::milliseconds &timeout + ) const; + + virtual bool sendHeaders( + const Http::StatusCode status, + std::vector > &headers, + const std::chrono::milliseconds &timeout, + const bool endStream = true + ) const override; + + virtual long sendData( + const void *src, + const size_t size, + const std::chrono::milliseconds &timeout, + const bool endStream = true + ) const noexcept override; + }; +} diff --git a/src/server/protocol/ServerProtocol.cpp b/src/server/protocol/ServerProtocol.cpp new file mode 100644 index 0000000..d5ac1dd --- /dev/null +++ b/src/server/protocol/ServerProtocol.cpp @@ -0,0 +1,19 @@ + +#include "ServerProtocol.h" + +namespace HttpServer +{ + ServerProtocol::ServerProtocol(Socket::Adapter *sock) noexcept + : sock(sock) + { + + } + + Socket::Adapter *ServerProtocol::getSocket() noexcept { + return this->sock; + } + + void ServerProtocol::close() noexcept { + this->sock->close(); + } +} diff --git a/src/server/protocol/ServerProtocol.h b/src/server/protocol/ServerProtocol.h new file mode 100644 index 0000000..f0c211c --- /dev/null +++ b/src/server/protocol/ServerProtocol.h @@ -0,0 +1,35 @@ +#pragma once + +#include "../../socket/Adapter.h" +#include "../../transfer/HttpStatusCode.h" + +namespace HttpServer +{ + class ServerProtocol + { + protected: + Socket::Adapter *sock; + + public: + ServerProtocol(Socket::Adapter *sock) noexcept; + virtual ~ServerProtocol() noexcept = default; + + Socket::Adapter *getSocket() noexcept; + + 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, + const size_t size, + const std::chrono::milliseconds &timeout, + const bool endStream = true + ) const noexcept = 0; + + virtual void close() noexcept; + }; +} diff --git a/src/server/protocol/WebSocket.cpp b/src/server/protocol/WebSocket.cpp new file mode 100644 index 0000000..58bffd4 --- /dev/null +++ b/src/server/protocol/WebSocket.cpp @@ -0,0 +1,161 @@ + +#include "WebSocket.h" + +#include "../../utils/Utils.h" +#include "../../socket/Socket.h" + +#ifdef POSIX + #include +#endif + +namespace HttpClient +{ + WebSocket::WebSocket(const WebSocket &obj) noexcept : sock(obj.sock) {} + + WebSocket::WebSocket(Socket::Adapter *adapter) noexcept : sock(adapter) {} + + std::vector WebSocket::packDataToMessageFrame(const void *data, const size_t size) + { + std::vector frame; + + if (0 == size) { + return frame; + } + + constexpr size_t header_max_size = 14; + frame.reserve(size + header_max_size); + frame.resize(header_max_size); + + frame[0] = 0x81; + + size_t cur_pos = sizeof(uint8_t) * 2; + + if (size <= 125) { + frame[1] = size; + } + else if (size <= 65536) { + frame[1] = 126; + + *reinterpret_cast(&frame[2]) = htons(size); + + cur_pos += sizeof(uint16_t); + } else { // More + frame[1] = 127; + + *reinterpret_cast(&frame[2]) = Utils::hton64(size); + + cur_pos += sizeof(uint64_t); + } + + frame.erase(frame.cbegin() + cur_pos, frame.cend() ); + + frame.insert(frame.cend(), reinterpret_cast(data), reinterpret_cast(data) + size); + + return frame; + } + + Socket::Adapter *WebSocket::getSocket() noexcept { + return this->sock; + } + + const Socket::Adapter *WebSocket::getSocket() const noexcept { + return this->sock; + } + + long WebSocket::nonblock_recv(std::vector &frame, const std::chrono::milliseconds &timeout) const + { + std::vector buf(4096); + + const long recv_size = this->sock->nonblock_recv(buf, timeout); + + if (recv_size <= 0) { + return recv_size; + } + + // See @link: http://tools.ietf.org/html/rfc6455#page-28 + + const uint8_t info_frame = buf[0]; + + if ( (info_frame & 0x08) == 0x08) { // opcode 0x08 — close connection + return -1; + } + + uint8_t info_size = buf[1]; + + const bool is_mask_set = info_size & uint8_t(1 << 7); + + info_size &= ~uint8_t(1 << 7); // Unset first bit + + size_t cur_pos = sizeof(uint8_t) * 2; + + uint64_t frame_size; + + if (info_size <= 125) { + frame_size = info_size; + } + else if (info_size == 126) { + frame_size = ntohs(*reinterpret_cast(&buf[cur_pos]) ); + cur_pos += sizeof(uint16_t); + } else { // if (info_size == 127) + frame_size = Utils::ntoh64(*reinterpret_cast(&buf[cur_pos]) ); + cur_pos += sizeof(uint64_t); + } + + if (frame_size > (buf.size() - cur_pos) ) { + return -1; // Close connection + } + + uint32_t mask; + + if (is_mask_set) { + mask = *reinterpret_cast(&buf[cur_pos]); + cur_pos += sizeof(uint32_t); + } + + frame.reserve(recv_size - cur_pos); + + frame.assign(buf.cbegin() + cur_pos, buf.cbegin() + recv_size); + + if (is_mask_set) { + const size_t aligned = frame.size() - (frame.size() % sizeof(mask)); + + uint32_t * const addr = reinterpret_cast(frame.data() ); + + for (size_t i = 0; i < aligned / sizeof(uint32_t); ++i) { + addr[i] ^= mask; + } + + for (size_t i = aligned; i < frame.size(); ++i) { + frame[i] ^= reinterpret_cast(&mask)[i % sizeof(mask)]; + } + } + + return recv_size - cur_pos; + } + + long WebSocket::nonblock_send(const void *data, const size_t length, const std::chrono::milliseconds &timeout) const + { + const std::vector frame = WebSocket::packDataToMessageFrame(data, length); + + if (frame.empty() ) { + return 0; + } + + return this->sock->nonblock_send(frame.data(), frame.size(), timeout); + } + + long WebSocket::nonblock_send(const std::string &str, const std::chrono::milliseconds &timeout) const + { + const std::vector frame = WebSocket::packDataToMessageFrame(str.data(), str.length() ); + + if (frame.empty() ) { + return 0; + } + + return this->sock->nonblock_send(frame.data(), frame.size(), timeout); + } + + void WebSocket::close() noexcept { + this->sock->close(); + } +} diff --git a/src/server/protocol/WebSocket.h b/src/server/protocol/WebSocket.h new file mode 100644 index 0000000..ad1a672 --- /dev/null +++ b/src/server/protocol/WebSocket.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../../socket/Adapter.h" + +namespace HttpClient +{ + class WebSocket + { + private: + Socket::Adapter *sock; + + public: + static std::vector packDataToMessageFrame(const void *data, const size_t size); + + public: + WebSocket() = delete; + WebSocket(const WebSocket &obj) noexcept; + WebSocket(Socket::Adapter *adapter) noexcept; + + Socket::Adapter *getSocket() noexcept; + const Socket::Adapter *getSocket() const noexcept; + + long nonblock_recv(std::vector &frame, const std::chrono::milliseconds &timeout) const; + + long nonblock_send(const void *data, const size_t length, const std::chrono::milliseconds &timeout) const; + long nonblock_send(const std::string &str, const std::chrono::milliseconds &timeout) const; + + void close() 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..40b0f92 --- /dev/null +++ b/src/socket/Socket.cpp @@ -0,0 +1,552 @@ + +#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 { + 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 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/System.cpp b/src/system/System.cpp new file mode 100644 index 0000000..ebb5935 --- /dev/null +++ b/src/system/System.cpp @@ -0,0 +1,321 @@ + +#include "System.h" + +#include + +#ifdef WIN32 + #include + ::TCHAR myWndClassName[] = TEXT("WndClassNameConstant"); + + #ifdef UNICODE + #include + #endif +#elif POSIX + #include + #include + #include + #include +#endif + +namespace System +{ +#ifdef WIN32 + struct EnumData { + native_processid_type process_id; + ::HWND hWnd; + }; + + static ::BOOL WINAPI EnumProc(const ::HWND hWnd, const ::LPARAM lParam) + { + EnumData &ed = *reinterpret_cast(lParam); + + native_processid_type process_id = 0; + + ::GetWindowThreadProcessId(hWnd, &process_id); + + if (process_id == ed.process_id && ::GetConsoleWindow() != hWnd) + { + std::array<::TCHAR, 257> class_name; + + ::GetClassName(hWnd, class_name.data(), int(class_name.size() - 1) ); + + if (0 == ::_tcscmp(class_name.data(), myWndClassName) ) { + ed.hWnd = hWnd; + return false; + } + } + + return true; + } +#endif + + native_processid_type getProcessId() noexcept + { + #ifdef WIN32 + return ::GetCurrentProcessId(); + #elif POSIX + return ::getpid(); + #else + #error "Undefined platform" + #endif + } + + bool changeCurrentDirectory(const std::string &dir) + { + #ifdef WIN32 + #ifdef UNICODE + std::wstring_convert > converter; + const std::wstring target = converter.from_bytes(dir); + #else + const std::string &target = dir; + #endif + return 0 != ::SetCurrentDirectory(target.c_str() ); + #elif POSIX + return 0 == ::chdir(dir.c_str() ); + #else + #error "Undefined platform" + #endif + } + + bool isProcessExists(const native_processid_type pid) noexcept + { + #ifdef WIN32 + HANDLE hProcess = ::OpenProcess(SYNCHRONIZE, false, pid); + ::CloseHandle(hProcess); + return 0 != hProcess; + #elif POSIX + return 0 == ::kill(pid, 0); + #else + #error "Undefined platform" + #endif + } + + 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) { + return false; + } + + return 0 != ::PostMessage(ed.hWnd, signal, 0, 0); + #elif POSIX + return 0 == ::kill(pid, signal); + #else + #error "Undefined platform" + #endif + } + + 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 "Undefined platform" + #endif + } + + std::string getTempDir() + { + #ifdef WIN32 + std::array<::TCHAR, MAX_PATH + 1> buf; + + auto const len = ::GetTempPath(static_cast<::DWORD>(buf.size() ), buf.data() ); + + #ifdef UNICODE + std::wstring_convert > converter; + return converter.to_bytes(buf.data() ); + #else + return std::string(buf.cbegin(), buf.cbegin() + len); + #endif + #elif POSIX + const char *buf = ::getenv("TMPDIR"); + + if (nullptr == buf) { + return std::string("/tmp/"); + } + + std::string str(buf); + + if (str.back() != '/') { + str.push_back('/'); + } + + return str; + #else + #error "Undefined platform" + #endif + } + + bool isFileExists(const std::string &fileName) + { + #ifdef WIN32 + #ifdef UNICODE + std::wstring_convert> converter; + const std::wstring file_name = converter.from_bytes(fileName); + #else + const std::string &file_name = fileName; + #endif + + const ::DWORD attrib = ::GetFileAttributes(file_name.c_str() ); + + if (INVALID_FILE_ATTRIBUTES == attrib) { + return false; + } + + return FILE_ATTRIBUTE_DIRECTORY != (attrib & FILE_ATTRIBUTE_DIRECTORY); + #elif POSIX + struct ::stat attrib; + + if (-1 == ::stat(fileName.c_str(), &attrib) ) { + return false; + } + + return S_ISREG(attrib.st_mode); + #else + #error "Undefined platform" + #endif + } + + bool getFileSizeAndTimeGmt( + const std::string &filePath, + size_t *fileSize, + time_t *fileTime + ) { + #ifdef WIN32 + #ifdef UNICODE + std::wstring_convert> converter; + const std::wstring file_path = converter.from_bytes(filePath); + #else + 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 + ); + + if (INVALID_HANDLE_VALUE == hFile) { + return false; + } + + if (::GetFileSizeEx(hFile, reinterpret_cast<::PLARGE_INTEGER>(fileSize) ) == false) { + ::CloseHandle(hFile); + return false; + } + + ::FILETIME ftWrite; + + ::BOOL result = ::GetFileTime( + hFile, + nullptr, + nullptr, + &ftWrite + ); + + ::CloseHandle(hFile); + + if (false == result) { + return false; + } + + ::SYSTEMTIME stUtc; + + ::FileTimeToSystemTime(&ftWrite, &stUtc); + + std::tm tm_time { + stUtc.wSecond, + stUtc.wMinute, + stUtc.wHour, + stUtc.wDay, + stUtc.wMonth - 1, + stUtc.wYear - 1900, + 0, + 0, + -1 + }; + + *fileTime = std::mktime(&tm_time); + + return true; + #elif POSIX + struct ::stat attrib {}; + + if (-1 == ::stat(filePath.c_str(), &attrib) ) { + return false; + } + + *fileSize = static_cast(attrib.st_size); + + std::tm clock {}; + + ::gmtime_r(&(attrib.st_mtime), &clock); + + *fileTime = std::mktime(&clock); + + return true; + #else + #error "Undefined platform" + #endif + } + + void filterSharedMemoryName(std::string &memName) + { + #ifdef WIN32 + #ifdef UNICODE + std::wstring_convert > converter; + std::wstring memory_name = converter.from_bytes(memName); + const std::wstring file_ext = L".exe"; + #else + std::string &memory_name = memName; + const std::string file_ext = ".exe"; + #endif + + const size_t pos = memory_name.rfind(file_ext); + + if (pos == memory_name.length() - file_ext.length() ) { + memory_name.erase( + pos, + memory_name.length() + ); + } + + ::TCHAR buf[MAX_PATH + 1] {}; + ::GetFullPathName(memory_name.c_str(), MAX_PATH, buf, nullptr); + + #ifdef UNICODE + memName = converter.to_bytes(buf); + #else + memName = buf; + #endif + + for (size_t i = 1; i < memName.length(); ++i) { + if ('/' == memName[i] || '\\' == memName[i]) { + memName[i] = '-'; + } + } + #elif POSIX + if (memName.front() != '/') { + memName = '/' + memName; + } + + for (size_t i = 1; i < memName.length(); ++i) { + if ('/' == memName[i] || '\\' == memName[i]) { + memName[i] = '-'; + } + } + #else + #error "Undefined platform" + #endif + } +} diff --git a/src/system/System.h b/src/system/System.h new file mode 100644 index 0000000..8b637d4 --- /dev/null +++ b/src/system/System.h @@ -0,0 +1,74 @@ +#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 SIGINT + #undef SIGINT + #define SIGINT (WM_USER + 2) + #endif + + #ifndef SIGUSR1 + #define SIGUSR1 (WM_USER + 10) + #endif + + #ifndef SIGUSR2 + #define SIGUSR2 (WM_USER + 12) + #endif +#elif POSIX + #include +#else + #error "Undefined platform" +#endif + +#include +#include +#include + +namespace System +{ +#ifdef WIN32 + typedef ::SOCKET native_socket_type; +#elif POSIX + typedef int native_socket_type; +#else + #error "Undefined platform" +#endif + +#ifdef WIN32 + typedef ::DWORD native_processid_type; +#elif POSIX + typedef ::pid_t native_processid_type; +#else + #error "Undefined platform" +#endif + + native_processid_type getProcessId() noexcept; + + bool changeCurrentDirectory(const std::string &dir); + + bool isProcessExists(const native_processid_type pid) noexcept; + + bool sendSignal(const native_processid_type pid, const int signal) noexcept; + + bool isDoneThread(const std::thread::native_handle_type handle) noexcept; + + std::string getTempDir(); + + bool isFileExists(const std::string &fileName); + + bool getFileSizeAndTimeGmt(const std::string &filePath, size_t *fileSize, time_t *fileTime); + + void filterSharedMemoryName(std::string &memName); +} 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/utils/Event.h b/src/utils/Event.h new file mode 100644 index 0000000..b9d20ad --- /dev/null +++ b/src/utils/Event.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +namespace Utils +{ + class Event + { + private: + std::mutex mtx; + std::condition_variable cv; + std::atomic signaled; + bool manually; + + public: + 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() noexcept; + void notify(const size_t threadsCount) noexcept; + + void reset() noexcept; + + bool notifed() const noexcept; + }; +} 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); +}