From f29bb15f9d8639a417be3a506a4d60855132c0c6 Mon Sep 17 00:00:00 2001 From: yhirose Date: Sun, 22 Feb 2026 05:29:54 -0500 Subject: [PATCH] Performance improvement! --- benchmark/Makefile | 13 +- benchmark/crow/crow_all.h | 22158 ++++++++++++++++++------------------ benchmark/crow/main.cpp | 2 +- httplib.h | 40 +- 4 files changed, 10846 insertions(+), 11367 deletions(-) diff --git a/benchmark/Makefile b/benchmark/Makefile index 6dc93f0..6fdd528 100644 --- a/benchmark/Makefile +++ b/benchmark/Makefile @@ -1,4 +1,7 @@ -CXXFLAGS = -std=c++11 -O2 -I.. +CXXFLAGS = -O2 -I.. + +CPPHTTPLIB_CXXFLAGS = -std=c++11 +CROW_CXXFLAGS = -std=c++17 CPPHTTPLIB_FLAGS = -DCPPHTTPLIB_THREAD_POOL_COUNT=16 @@ -18,11 +21,11 @@ run : server @./server server : cpp-httplib/main.cpp ../httplib.h - @g++ -o $@ $(CXXFLAGS) $(CPPHTTPLIB_FLAGS) cpp-httplib/main.cpp + @g++ -o $@ $(CXXFLAGS) $(CPPHTTPLIB_CXXFLAGS) $(CPPHTTPLIB_FLAGS) cpp-httplib/main.cpp # crow bench-crow: server-crow - @echo "-------------\n Crow v1.2.0\n-------------\n" + @echo "-------------\n Crow v1.3.1\n-------------\n" @./server-crow & export PID=$$!; $(BENCH); kill $${PID} @echo "" @@ -32,8 +35,8 @@ monitor-crow: server-crow run-crow : server-crow @./server-crow -server-crow : crow/main.cpp - @g++ -o $@ $(CXXFLAGS) crow/main.cpp +server-crow : crow/main.cpp crow/crow_all.h + @g++ -o $@ $(CXXFLAGS) $(CROW_CXXFLAGS) crow/main.cpp # misc build: server server-crow diff --git a/benchmark/crow/crow_all.h b/benchmark/crow/crow_all.h index 0465f96..6de0dac 100644 --- a/benchmark/crow/crow_all.h +++ b/benchmark/crow/crow_all.h @@ -2,7 +2,7 @@ /*BSD 3-Clause License Copyright (c) 2014-2017, ipkn - 2020-2022, CrowCpp + 2020-2026, CrowCpp All rights reserved. Redistribution and use in source and binary forms, with or without @@ -30,641 +30,11 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -The Crow logo and other graphic material (excluding third party logos) used are under exclusive Copyright (c) 2021-2022, Farook Al-Sammarraie (The-EDev), All rights reserved. +The Crow logo and other graphic material (excluding third party logos) used are +under exclusive Copyright (c) 2021-2022, Farook Al-Sammarraie (The-EDev), All +rights reserved. */ #pragma once -// This file is generated from nginx/conf/mime.types using nginx_mime2cpp.py on 2021-12-03. -#include -#include - -namespace crow -{ - const std::unordered_map mime_types{ - {"shtml", "text/html"}, - {"htm", "text/html"}, - {"html", "text/html"}, - {"css", "text/css"}, - {"xml", "text/xml"}, - {"gif", "image/gif"}, - {"jpg", "image/jpeg"}, - {"jpeg", "image/jpeg"}, - {"js", "application/javascript"}, - {"atom", "application/atom+xml"}, - {"rss", "application/rss+xml"}, - {"mml", "text/mathml"}, - {"txt", "text/plain"}, - {"jad", "text/vnd.sun.j2me.app-descriptor"}, - {"wml", "text/vnd.wap.wml"}, - {"htc", "text/x-component"}, - {"avif", "image/avif"}, - {"png", "image/png"}, - {"svgz", "image/svg+xml"}, - {"svg", "image/svg+xml"}, - {"tiff", "image/tiff"}, - {"tif", "image/tiff"}, - {"wbmp", "image/vnd.wap.wbmp"}, - {"webp", "image/webp"}, - {"ico", "image/x-icon"}, - {"jng", "image/x-jng"}, - {"bmp", "image/x-ms-bmp"}, - {"woff", "font/woff"}, - {"woff2", "font/woff2"}, - {"ear", "application/java-archive"}, - {"war", "application/java-archive"}, - {"jar", "application/java-archive"}, - {"json", "application/json"}, - {"hqx", "application/mac-binhex40"}, - {"doc", "application/msword"}, - {"pdf", "application/pdf"}, - {"ai", "application/postscript"}, - {"eps", "application/postscript"}, - {"ps", "application/postscript"}, - {"rtf", "application/rtf"}, - {"m3u8", "application/vnd.apple.mpegurl"}, - {"kml", "application/vnd.google-earth.kml+xml"}, - {"kmz", "application/vnd.google-earth.kmz"}, - {"xls", "application/vnd.ms-excel"}, - {"eot", "application/vnd.ms-fontobject"}, - {"ppt", "application/vnd.ms-powerpoint"}, - {"odg", "application/vnd.oasis.opendocument.graphics"}, - {"odp", "application/vnd.oasis.opendocument.presentation"}, - {"ods", "application/vnd.oasis.opendocument.spreadsheet"}, - {"odt", "application/vnd.oasis.opendocument.text"}, - {"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, - {"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, - {"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, - {"wmlc", "application/vnd.wap.wmlc"}, - {"wasm", "application/wasm"}, - {"7z", "application/x-7z-compressed"}, - {"cco", "application/x-cocoa"}, - {"jardiff", "application/x-java-archive-diff"}, - {"jnlp", "application/x-java-jnlp-file"}, - {"run", "application/x-makeself"}, - {"pm", "application/x-perl"}, - {"pl", "application/x-perl"}, - {"pdb", "application/x-pilot"}, - {"prc", "application/x-pilot"}, - {"rar", "application/x-rar-compressed"}, - {"rpm", "application/x-redhat-package-manager"}, - {"sea", "application/x-sea"}, - {"swf", "application/x-shockwave-flash"}, - {"sit", "application/x-stuffit"}, - {"tk", "application/x-tcl"}, - {"tcl", "application/x-tcl"}, - {"crt", "application/x-x509-ca-cert"}, - {"pem", "application/x-x509-ca-cert"}, - {"der", "application/x-x509-ca-cert"}, - {"xpi", "application/x-xpinstall"}, - {"xhtml", "application/xhtml+xml"}, - {"xspf", "application/xspf+xml"}, - {"zip", "application/zip"}, - {"dll", "application/octet-stream"}, - {"exe", "application/octet-stream"}, - {"bin", "application/octet-stream"}, - {"deb", "application/octet-stream"}, - {"dmg", "application/octet-stream"}, - {"img", "application/octet-stream"}, - {"iso", "application/octet-stream"}, - {"msm", "application/octet-stream"}, - {"msp", "application/octet-stream"}, - {"msi", "application/octet-stream"}, - {"kar", "audio/midi"}, - {"midi", "audio/midi"}, - {"mid", "audio/midi"}, - {"mp3", "audio/mpeg"}, - {"ogg", "audio/ogg"}, - {"m4a", "audio/x-m4a"}, - {"ra", "audio/x-realaudio"}, - {"3gp", "video/3gpp"}, - {"3gpp", "video/3gpp"}, - {"ts", "video/mp2t"}, - {"mp4", "video/mp4"}, - {"mpg", "video/mpeg"}, - {"mpeg", "video/mpeg"}, - {"mov", "video/quicktime"}, - {"webm", "video/webm"}, - {"flv", "video/x-flv"}, - {"m4v", "video/x-m4v"}, - {"mng", "video/x-mng"}, - {"asf", "video/x-ms-asf"}, - {"asx", "video/x-ms-asf"}, - {"wmv", "video/x-ms-wmv"}, - {"avi", "video/x-msvideo"}}; -} - - -#include - -namespace crow -{ - /// An abstract class that allows any other class to be returned by a handler. - struct returnable - { - std::string content_type; - virtual std::string dump() const = 0; - - returnable(std::string ctype): - content_type{ctype} - {} - - virtual ~returnable(){}; - }; -} // namespace crow - - -#include -#include -#include -#include -#include -#include -#include - -namespace crow -{ - -// ---------------------------------------------------------------------------- -// qs_parse (modified) -// https://github.com/bartgrantham/qs_parse -// ---------------------------------------------------------------------------- -/* Similar to strncmp, but handles URL-encoding for either string */ -int qs_strncmp(const char* s, const char* qs, size_t n); - - -/* Finds the beginning of each key/value pair and stores a pointer in qs_kv. - * Also decodes the value portion of the k/v pair *in-place*. In a future - * enhancement it will also have a compile-time option of sorting qs_kv - * alphabetically by key. */ -size_t qs_parse(char* qs, char* qs_kv[], size_t qs_kv_size, bool parse_url); - - -/* Used by qs_parse to decode the value portion of a k/v pair */ -int qs_decode(char * qs); - - -/* Looks up the value according to the key on a pre-processed query string - * A future enhancement will be a compile-time option to look up the key - * in a pre-sorted qs_kv array via a binary search. */ -//char * qs_k2v(const char * key, char * qs_kv[], int qs_kv_size); - char * qs_k2v(const char * key, char * const * qs_kv, size_t qs_kv_size, int nth); - - -/* Non-destructive lookup of value, based on key. User provides the - * destinaton string and length. */ -char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len); - -// TODO: implement sorting of the qs_kv array; for now ensure it's not compiled -#undef _qsSORTING - -// isxdigit _is_ available in , but let's avoid another header instead -#define CROW_QS_ISHEX(x) ((((x)>='0'&&(x)<='9') || ((x)>='A'&&(x)<='F') || ((x)>='a'&&(x)<='f')) ? 1 : 0) -#define CROW_QS_HEX2DEC(x) (((x)>='0'&&(x)<='9') ? (x)-48 : ((x)>='A'&&(x)<='F') ? (x)-55 : ((x)>='a'&&(x)<='f') ? (x)-87 : 0) -#define CROW_QS_ISQSCHR(x) ((((x)=='=')||((x)=='#')||((x)=='&')||((x)=='\0')) ? 0 : 1) - -inline int qs_strncmp(const char * s, const char * qs, size_t n) -{ - unsigned char u1, u2, unyb, lnyb; - - while(n-- > 0) - { - u1 = static_cast(*s++); - u2 = static_cast(*qs++); - - if ( ! CROW_QS_ISQSCHR(u1) ) { u1 = '\0'; } - if ( ! CROW_QS_ISQSCHR(u2) ) { u2 = '\0'; } - - if ( u1 == '+' ) { u1 = ' '; } - if ( u1 == '%' ) // easier/safer than scanf - { - unyb = static_cast(*s++); - lnyb = static_cast(*s++); - if ( CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb) ) - u1 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb); - else - u1 = '\0'; - } - - if ( u2 == '+' ) { u2 = ' '; } - if ( u2 == '%' ) // easier/safer than scanf - { - unyb = static_cast(*qs++); - lnyb = static_cast(*qs++); - if ( CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb) ) - u2 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb); - else - u2 = '\0'; - } - - if ( u1 != u2 ) - return u1 - u2; - if ( u1 == '\0' ) - return 0; - } - if ( CROW_QS_ISQSCHR(*qs) ) - return -1; - else - return 0; -} - - -inline size_t qs_parse(char* qs, char* qs_kv[], size_t qs_kv_size, bool parse_url = true) -{ - size_t i, j; - char * substr_ptr; - - for(i=0; i means x iterations of this loop -> means *x+1* k/v pairs - substr_ptr += j + 1; - i++; - } - - // we only decode the values in place, the keys could have '='s in them - // which will hose our ability to distinguish keys from values later - for(j=0; j> qs_dict_name2kv(const char * dict_name, char * const * qs_kv, size_t qs_kv_size, int nth = 0) -{ - size_t i; - size_t name_len, skip_to_eq, skip_to_brace_open, skip_to_brace_close; - - name_len = strlen(dict_name); - -#ifdef _qsSORTING -// TODO: binary search for key in the sorted qs_kv -#else // _qsSORTING - for(i=0; i 0 && - skip_to_brace_close > 0 && - nth == 0 ) - { - auto key = std::string(qs_kv[i] + skip_to_brace_open, skip_to_brace_close - skip_to_brace_open); - auto value = std::string(qs_kv[i] + skip_to_eq); - return std::unique_ptr>(new std::pair(key, value)); - } - else - { - --nth; - } - } - } -#endif // _qsSORTING - - return nullptr; -} - - -inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len) -{ - size_t i, key_len; - const char * tmp; - - // find the beginning of the k/v substrings - if ( (tmp = strchr(qs, '?')) != NULL ) - qs = tmp + 1; - - key_len = strlen(key); - while(qs[0] != '#' && qs[0] != '\0') - { - if ( qs_strncmp(key, qs, key_len) == 0 ) - break; - qs += strcspn(qs, "&") + 1; - } - - if ( qs[0] == '\0' ) return NULL; - - qs += strcspn(qs, "=&#"); - if ( qs[0] == '=' ) - { - qs++; - i = strcspn(qs, "&=#"); -#ifdef _MSC_VER - strncpy_s(val, val_len, qs, (val_len - 1)<(i + 1) ? (val_len - 1) : (i + 1)); -#else - strncpy(val, qs, (val_len - 1)<(i + 1) ? (val_len - 1) : (i + 1)); -#endif - qs_decode(val); - } - else - { - if ( val_len > 0 ) - val[0] = '\0'; - } - - return val; -} -} -// ---------------------------------------------------------------------------- - - -namespace crow -{ - struct request; - /// A class to represent any data coming after the `?` in the request URL into key-value pairs. - class query_string - { - public: - static const int MAX_KEY_VALUE_PAIRS_COUNT = 256; - - query_string() = default; - - query_string(const query_string& qs): - url_(qs.url_) - { - for (auto p : qs.key_value_pairs_) - { - key_value_pairs_.push_back((char*)(p - qs.url_.c_str() + url_.c_str())); - } - } - - query_string& operator=(const query_string& qs) - { - url_ = qs.url_; - key_value_pairs_.clear(); - for (auto p : qs.key_value_pairs_) - { - key_value_pairs_.push_back((char*)(p - qs.url_.c_str() + url_.c_str())); - } - return *this; - } - - query_string& operator=(query_string&& qs) noexcept - { - key_value_pairs_ = std::move(qs.key_value_pairs_); - char* old_data = (char*)qs.url_.c_str(); - url_ = std::move(qs.url_); - for (auto& p : key_value_pairs_) - { - p += (char*)url_.c_str() - old_data; - } - return *this; - } - - - query_string(std::string params, bool url = true): - url_(std::move(params)) - { - if (url_.empty()) - return; - - key_value_pairs_.resize(MAX_KEY_VALUE_PAIRS_COUNT); - size_t count = qs_parse(&url_[0], &key_value_pairs_[0], MAX_KEY_VALUE_PAIRS_COUNT, url); - - key_value_pairs_.resize(count); - key_value_pairs_.shrink_to_fit(); - } - - void clear() - { - key_value_pairs_.clear(); - url_.clear(); - } - - friend std::ostream& operator<<(std::ostream& os, const query_string& qs) - { - os << "[ "; - for (size_t i = 0; i < qs.key_value_pairs_.size(); ++i) - { - if (i) - os << ", "; - os << qs.key_value_pairs_[i]; - } - os << " ]"; - return os; - } - - /// Get a value from a name, used for `?name=value`. - - /// - /// Note: this method returns the value of the first occurrence of the key only, to return all occurrences, see \ref get_list(). - char* get(const std::string& name) const - { - char* ret = qs_k2v(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size()); - return ret; - } - - /// Works similar to \ref get() except it removes the item from the query string. - char* pop(const std::string& name) - { - char* ret = get(name); - if (ret != nullptr) - { - for (unsigned int i = 0; i < key_value_pairs_.size(); i++) - { - std::string str_item(key_value_pairs_[i]); - if (str_item.substr(0, name.size() + 1) == name + '=') - { - key_value_pairs_.erase(key_value_pairs_.begin() + i); - break; - } - } - } - return ret; - } - - /// Returns a list of values, passed as `?name[]=value1&name[]=value2&...name[]=valuen` with n being the size of the list. - - /// - /// Note: Square brackets in the above example are controlled by `use_brackets` boolean (true by default). If set to false, the example becomes `?name=value1,name=value2...name=valuen` - std::vector get_list(const std::string& name, bool use_brackets = true) const - { - std::vector ret; - std::string plus = name + (use_brackets ? "[]" : ""); - char* element = nullptr; - - int count = 0; - while (1) - { - element = qs_k2v(plus.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++); - if (!element) - break; - ret.push_back(element); - } - return ret; - } - - /// Similar to \ref get_list() but it removes the - std::vector pop_list(const std::string& name, bool use_brackets = true) - { - std::vector ret = get_list(name, use_brackets); - if (!ret.empty()) - { - for (unsigned int i = 0; i < key_value_pairs_.size(); i++) - { - std::string str_item(key_value_pairs_[i]); - if ((use_brackets ? (str_item.substr(0, name.size() + 3) == name + "[]=") : (str_item.substr(0, name.size() + 1) == name + '='))) - { - key_value_pairs_.erase(key_value_pairs_.begin() + i--); - } - } - } - return ret; - } - - /// Works similar to \ref get_list() except the brackets are mandatory must not be empty. - - /// - /// For example calling `get_dict(yourname)` on `?yourname[sub1]=42&yourname[sub2]=84` would give a map containing `{sub1 : 42, sub2 : 84}`. - /// - /// if your query string has both empty brackets and ones with a key inside, use pop_list() to get all the values without a key before running this method. - std::unordered_map get_dict(const std::string& name) const - { - std::unordered_map ret; - - int count = 0; - while (1) - { - if (auto element = qs_dict_name2kv(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++)) - ret.insert(*element); - else - break; - } - return ret; - } - - /// Works the same as \ref get_dict() but removes the values from the query string. - std::unordered_map pop_dict(const std::string& name) - { - std::unordered_map ret = get_dict(name); - if (!ret.empty()) - { - for (unsigned int i = 0; i < key_value_pairs_.size(); i++) - { - std::string str_item(key_value_pairs_[i]); - if (str_item.substr(0, name.size() + 1) == name + '[') - { - key_value_pairs_.erase(key_value_pairs_.begin() + i--); - } - } - } - return ret; - } - - std::vector keys() const - { - std::vector keys; - keys.reserve(key_value_pairs_.size()); - - for (const char* const element : key_value_pairs_) - { - const char* delimiter = strchr(element, '='); - if (delimiter) - keys.emplace_back(element, delimiter); - else - keys.emplace_back(element); - } - - return keys; - } - - private: - std::string url_; - std::vector key_value_pairs_; - }; - -} // namespace crow - #ifdef CROW_ENABLE_COMPRESSION #include @@ -673,100 +43,99 @@ namespace crow // http://zlib.net/manual.html namespace crow // NOTE: Already documented in "crow/app.h" { - namespace compression - { - // Values used in the 'windowBits' parameter for deflateInit2. - enum algorithm - { - // 15 is the default value for deflate - DEFLATE = 15, - // windowBits can also be greater than 15 for optional gzip encoding. - // Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. - GZIP = 15 | 16, - }; +namespace compression { +// Values used in the 'windowBits' parameter for deflateInit2. +enum algorithm { + // 15 is the default value for deflate + DEFLATE = 15, + // windowBits can also be greater than 15 for optional gzip encoding. + // Add 16 to windowBits to write a simple gzip header and trailer around the + // compressed data instead of a zlib wrapper. + GZIP = 15 | 16, +}; - inline std::string compress_string(std::string const& str, algorithm algo) - { - std::string compressed_str; - z_stream stream{}; - // Initialize with the default values - if (::deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, algo, 8, Z_DEFAULT_STRATEGY) == Z_OK) - { - char buffer[8192]; +inline std::string compress_string(std::string const &str, algorithm algo) { + std::string compressed_str; + z_stream stream{}; + // Initialize with the default values + if (::deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, algo, 8, + Z_DEFAULT_STRATEGY) == Z_OK) { + char buffer[8192]; - stream.avail_in = str.size(); - // zlib does not take a const pointer. The data is not altered. - stream.next_in = const_cast(reinterpret_cast(str.c_str())); + stream.avail_in = str.size(); + // zlib does not take a const pointer. The data is not altered. + stream.next_in = + const_cast(reinterpret_cast(str.c_str())); - int code = Z_OK; - do - { - stream.avail_out = sizeof(buffer); - stream.next_out = reinterpret_cast(&buffer[0]); + int code = Z_OK; + do { + stream.avail_out = sizeof(buffer); + stream.next_out = reinterpret_cast(&buffer[0]); - code = ::deflate(&stream, Z_FINISH); - // Successful and non-fatal error code returned by deflate when used with Z_FINISH flush - if (code == Z_OK || code == Z_STREAM_END) - { - std::copy(&buffer[0], &buffer[sizeof(buffer) - stream.avail_out], std::back_inserter(compressed_str)); - } + code = ::deflate(&stream, Z_FINISH); + // Successful and non-fatal error code returned by deflate when used with + // Z_FINISH flush + if (code == Z_OK || code == Z_STREAM_END) { + std::copy(&buffer[0], &buffer[sizeof(buffer) - stream.avail_out], + std::back_inserter(compressed_str)); + } - } while (code == Z_OK); + } while (code == Z_OK); - if (code != Z_STREAM_END) - compressed_str.clear(); + if (code != Z_STREAM_END) compressed_str.clear(); - ::deflateEnd(&stream); - } + ::deflateEnd(&stream); + } - return compressed_str; - } + return compressed_str; +} - inline std::string decompress_string(std::string const& deflated_string) - { - std::string inflated_string; - Bytef tmp[8192]; +inline std::string decompress_string(std::string const &deflated_string) { + std::string inflated_string; + Bytef tmp[8192]; - z_stream zstream{}; - zstream.avail_in = deflated_string.size(); - // Nasty const_cast but zlib won't alter its contents - zstream.next_in = const_cast(reinterpret_cast(deflated_string.c_str())); - // Initialize with automatic header detection, for gzip support - if (::inflateInit2(&zstream, MAX_WBITS | 32) == Z_OK) - { - do - { - zstream.avail_out = sizeof(tmp); - zstream.next_out = &tmp[0]; + z_stream zstream{}; + zstream.avail_in = deflated_string.size(); + // Nasty const_cast but zlib won't alter its contents + zstream.next_in = const_cast( + reinterpret_cast(deflated_string.c_str())); + // Initialize with automatic header detection, for gzip support + if (::inflateInit2(&zstream, MAX_WBITS | 32) == Z_OK) { + do { + zstream.avail_out = sizeof(tmp); + zstream.next_out = &tmp[0]; - auto ret = ::inflate(&zstream, Z_NO_FLUSH); - if (ret == Z_OK || ret == Z_STREAM_END) - { - std::copy(&tmp[0], &tmp[sizeof(tmp) - zstream.avail_out], std::back_inserter(inflated_string)); - } - else - { - // Something went wrong with inflate; make sure we return an empty string - inflated_string.clear(); - break; - } + auto ret = ::inflate(&zstream, Z_NO_FLUSH); + if (ret == Z_OK || ret == Z_STREAM_END) { + std::copy(&tmp[0], &tmp[sizeof(tmp) - zstream.avail_out], + std::back_inserter(inflated_string)); + } else { + // Something went wrong with inflate; make sure we return an empty + // string + inflated_string.clear(); + break; + } - } while (zstream.avail_out == 0); + } while (zstream.avail_out == 0); - // Free zlib's internal memory - ::inflateEnd(&zstream); - } + // Free zlib's internal memory + ::inflateEnd(&zstream); + } - return inflated_string; - } - } // namespace compression + return inflated_string; +} +} // namespace compression } // namespace crow #endif +namespace crow { +constexpr const char VERSION[] = "master"; +} + /* * SHA1 Wikipedia Page: http://en.wikipedia.org/wiki/SHA-1 - * + * * Copyright (c) 2012-22 SAURAV MOHAPATRA * * Permission to use, copy, modify, and distribute this software for any @@ -782,12 +151,12 @@ namespace crow // NOTE: Already documented in "crow/app.h" * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/** +/** * \file TinySHA1.hpp * \author SAURAV MOHAPATRA * \date 2012-22 - * \brief TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based - * on the implementation in boost::uuid::details. + * \brief TinySHA1 - a header only implementation of the SHA1 algorithm in C++. + * Based on the implementation in boost::uuid::details. * * In this file are defined: * - sha1::SHA1 @@ -803,192 +172,779 @@ namespace crow // NOTE: Already documented in "crow/app.h" * \namespace sha1 * \brief Here is defined the SHA1 class */ -namespace sha1 -{ - /** - * \class SHA1 - * \brief A tiny SHA1 algorithm implementation used internally in the - * Crow server (specifically in crow/websocket.h). - */ - class SHA1 - { - public: - typedef uint32_t digest32_t[5]; - typedef uint8_t digest8_t[20]; - inline static uint32_t LeftRotate(uint32_t value, size_t count) { - return (value << count) ^ (value >> (32-count)); - } - SHA1(){ reset(); } - virtual ~SHA1() {} - SHA1(const SHA1& s) { *this = s; } - const SHA1& operator = (const SHA1& s) { - memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t)); - memcpy(m_block, s.m_block, 64); - m_blockByteIndex = s.m_blockByteIndex; - m_byteCount = s.m_byteCount; - return *this; - } - SHA1& reset() { - m_digest[0] = 0x67452301; - m_digest[1] = 0xEFCDAB89; - m_digest[2] = 0x98BADCFE; - m_digest[3] = 0x10325476; - m_digest[4] = 0xC3D2E1F0; - m_blockByteIndex = 0; - m_byteCount = 0; - return *this; - } - SHA1& processByte(uint8_t octet) { - this->m_block[this->m_blockByteIndex++] = octet; - ++this->m_byteCount; - if(m_blockByteIndex == 64) { - this->m_blockByteIndex = 0; - processBlock(); - } - return *this; - } - SHA1& processBlock(const void* const start, const void* const end) { - const uint8_t* begin = static_cast(start); - const uint8_t* finish = static_cast(end); - while(begin != finish) { - processByte(*begin); - begin++; - } - return *this; - } - SHA1& processBytes(const void* const data, size_t len) { - const uint8_t* block = static_cast(data); - processBlock(block, block + len); - return *this; - } - const uint32_t* getDigest(digest32_t digest) { - size_t bitCount = this->m_byteCount * 8; - processByte(0x80); - if (this->m_blockByteIndex > 56) { - while (m_blockByteIndex != 0) { - processByte(0); - } - while (m_blockByteIndex < 56) { - processByte(0); - } - } else { - while (m_blockByteIndex < 56) { - processByte(0); - } - } - processByte(0); - processByte(0); - processByte(0); - processByte(0); - processByte( static_cast((bitCount>>24) & 0xFF)); - processByte( static_cast((bitCount>>16) & 0xFF)); - processByte( static_cast((bitCount>>8 ) & 0xFF)); - processByte( static_cast((bitCount) & 0xFF)); - - memcpy(digest, m_digest, 5 * sizeof(uint32_t)); - return digest; - } - const uint8_t* getDigestBytes(digest8_t digest) { - digest32_t d32; - getDigest(d32); - size_t di = 0; - digest[di++] = ((d32[0] >> 24) & 0xFF); - digest[di++] = ((d32[0] >> 16) & 0xFF); - digest[di++] = ((d32[0] >> 8) & 0xFF); - digest[di++] = ((d32[0]) & 0xFF); - - digest[di++] = ((d32[1] >> 24) & 0xFF); - digest[di++] = ((d32[1] >> 16) & 0xFF); - digest[di++] = ((d32[1] >> 8) & 0xFF); - digest[di++] = ((d32[1]) & 0xFF); - - digest[di++] = ((d32[2] >> 24) & 0xFF); - digest[di++] = ((d32[2] >> 16) & 0xFF); - digest[di++] = ((d32[2] >> 8) & 0xFF); - digest[di++] = ((d32[2]) & 0xFF); - - digest[di++] = ((d32[3] >> 24) & 0xFF); - digest[di++] = ((d32[3] >> 16) & 0xFF); - digest[di++] = ((d32[3] >> 8) & 0xFF); - digest[di++] = ((d32[3]) & 0xFF); - - digest[di++] = ((d32[4] >> 24) & 0xFF); - digest[di++] = ((d32[4] >> 16) & 0xFF); - digest[di++] = ((d32[4] >> 8) & 0xFF); - digest[di++] = ((d32[4]) & 0xFF); - return digest; - } - - protected: - void processBlock() { - uint32_t w[80]; - for (size_t i = 0; i < 16; i++) { - w[i] = (m_block[i*4 + 0] << 24); - w[i] |= (m_block[i*4 + 1] << 16); - w[i] |= (m_block[i*4 + 2] << 8); - w[i] |= (m_block[i*4 + 3]); - } - for (size_t i = 16; i < 80; i++) { - w[i] = LeftRotate((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1); - } - - uint32_t a = m_digest[0]; - uint32_t b = m_digest[1]; - uint32_t c = m_digest[2]; - uint32_t d = m_digest[3]; - uint32_t e = m_digest[4]; - - for (std::size_t i=0; i<80; ++i) { - uint32_t f = 0; - uint32_t k = 0; - - if (i<20) { - f = (b & c) | (~b & d); - k = 0x5A827999; - } else if (i<40) { - f = b ^ c ^ d; - k = 0x6ED9EBA1; - } else if (i<60) { - f = (b & c) | (b & d) | (c & d); - k = 0x8F1BBCDC; - } else { - f = b ^ c ^ d; - k = 0xCA62C1D6; - } - uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i]; - e = d; - d = c; - c = LeftRotate(b, 30); - b = a; - a = temp; - } - - m_digest[0] += a; - m_digest[1] += b; - m_digest[2] += c; - m_digest[3] += d; - m_digest[4] += e; - } - private: - digest32_t m_digest; - uint8_t m_block[64]; - size_t m_blockByteIndex; - size_t m_byteCount; - }; -} +namespace sha1 { +/** + * \class SHA1 + * \brief A tiny SHA1 algorithm implementation used internally in the + * Crow server (specifically in crow/websocket.h). + */ +class SHA1 { +public: + typedef uint32_t digest32_t[5]; + typedef uint8_t digest8_t[20]; + inline static uint32_t LeftRotate(uint32_t value, size_t count) { + return (value << count) ^ (value >> (32 - count)); + } + SHA1() { reset(); } + virtual ~SHA1() {} + SHA1(const SHA1 &s) { *this = s; } + const SHA1 &operator=(const SHA1 &s) { + memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t)); + memcpy(m_block, s.m_block, 64); + m_blockByteIndex = s.m_blockByteIndex; + m_byteCount = s.m_byteCount; + return *this; + } + SHA1 &reset() { + m_digest[0] = 0x67452301; + m_digest[1] = 0xEFCDAB89; + m_digest[2] = 0x98BADCFE; + m_digest[3] = 0x10325476; + m_digest[4] = 0xC3D2E1F0; + m_blockByteIndex = 0; + m_byteCount = 0; + return *this; + } + SHA1 &processByte(uint8_t octet) { + this->m_block[this->m_blockByteIndex++] = octet; + ++this->m_byteCount; + if (m_blockByteIndex == 64) { + this->m_blockByteIndex = 0; + processBlock(); + } + return *this; + } + SHA1 &processBlock(const void *const start, const void *const end) { + const uint8_t *begin = static_cast(start); + const uint8_t *finish = static_cast(end); + while (begin != finish) { + processByte(*begin); + begin++; + } + return *this; + } + SHA1 &processBytes(const void *const data, size_t len) { + const uint8_t *block = static_cast(data); + processBlock(block, block + len); + return *this; + } + const uint32_t *getDigest(digest32_t digest) { + size_t bitCount = this->m_byteCount * 8; + processByte(0x80); + if (this->m_blockByteIndex > 56) { + while (m_blockByteIndex != 0) { + processByte(0); + } + while (m_blockByteIndex < 56) { + processByte(0); + } + } else { + while (m_blockByteIndex < 56) { + processByte(0); + } + } + processByte(0); + processByte(0); + processByte(0); + processByte(0); + processByte(static_cast((bitCount >> 24) & 0xFF)); + processByte(static_cast((bitCount >> 16) & 0xFF)); + processByte(static_cast((bitCount >> 8) & 0xFF)); + processByte(static_cast((bitCount) & 0xFF)); + + memcpy(digest, m_digest, 5 * sizeof(uint32_t)); + return digest; + } + const uint8_t *getDigestBytes(digest8_t digest) { + digest32_t d32; + getDigest(d32); + size_t di = 0; + digest[di++] = ((d32[0] >> 24) & 0xFF); + digest[di++] = ((d32[0] >> 16) & 0xFF); + digest[di++] = ((d32[0] >> 8) & 0xFF); + digest[di++] = ((d32[0]) & 0xFF); + + digest[di++] = ((d32[1] >> 24) & 0xFF); + digest[di++] = ((d32[1] >> 16) & 0xFF); + digest[di++] = ((d32[1] >> 8) & 0xFF); + digest[di++] = ((d32[1]) & 0xFF); + + digest[di++] = ((d32[2] >> 24) & 0xFF); + digest[di++] = ((d32[2] >> 16) & 0xFF); + digest[di++] = ((d32[2] >> 8) & 0xFF); + digest[di++] = ((d32[2]) & 0xFF); + + digest[di++] = ((d32[3] >> 24) & 0xFF); + digest[di++] = ((d32[3] >> 16) & 0xFF); + digest[di++] = ((d32[3] >> 8) & 0xFF); + digest[di++] = ((d32[3]) & 0xFF); + + digest[di++] = ((d32[4] >> 24) & 0xFF); + digest[di++] = ((d32[4] >> 16) & 0xFF); + digest[di++] = ((d32[4] >> 8) & 0xFF); + digest[di++] = ((d32[4]) & 0xFF); + return digest; + } + +protected: + void processBlock() { + uint32_t w[80]; + for (size_t i = 0; i < 16; i++) { + w[i] = (m_block[i * 4 + 0] << 24); + w[i] |= (m_block[i * 4 + 1] << 16); + w[i] |= (m_block[i * 4 + 2] << 8); + w[i] |= (m_block[i * 4 + 3]); + } + for (size_t i = 16; i < 80; i++) { + w[i] = LeftRotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); + } + + uint32_t a = m_digest[0]; + uint32_t b = m_digest[1]; + uint32_t c = m_digest[2]; + uint32_t d = m_digest[3]; + uint32_t e = m_digest[4]; + + for (std::size_t i = 0; i < 80; ++i) { + uint32_t f = 0; + uint32_t k = 0; + + if (i < 20) { + f = (b & c) | (~b & d); + k = 0x5A827999; + } else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i]; + e = d; + d = c; + c = LeftRotate(b, 30); + b = a; + a = temp; + } + + m_digest[0] += a; + m_digest[1] += b; + m_digest[2] += c; + m_digest[3] += d; + m_digest[4] += e; + } + +private: + digest32_t m_digest; + uint8_t m_block[64]; + size_t m_blockByteIndex; + size_t m_byteCount; +}; +} // namespace sha1 #endif +#include +#include +#include +#include +#include +#include +#include + +namespace crow { + +// ---------------------------------------------------------------------------- +// qs_parse (modified) +// https://github.com/bartgrantham/qs_parse +// ---------------------------------------------------------------------------- +/* Similar to strncmp, but handles URL-encoding for either string */ +int qs_strncmp(const char *s, const char *qs, size_t n); + +/* Finds the beginning of each key/value pair and stores a pointer in qs_kv. + * Also decodes the value portion of the k/v pair *in-place*. In a future + * enhancement it will also have a compile-time option of sorting qs_kv + * alphabetically by key. */ +size_t qs_parse(char *qs, char *qs_kv[], size_t qs_kv_size, bool parse_url); + +/* Used by qs_parse to decode the value portion of a k/v pair */ +int qs_decode(char *qs); + +/* Looks up the value according to the key on a pre-processed query string + * A future enhancement will be a compile-time option to look up the key + * in a pre-sorted qs_kv array via a binary search. */ +// char * qs_k2v(const char * key, char * qs_kv[], int qs_kv_size); +char *qs_k2v(const char *key, char *const *qs_kv, size_t qs_kv_size, int nth); + +/* Non-destructive lookup of value, based on key. User provides the + * destinaton string and length. */ +char *qs_scanvalue(const char *key, const char *qs, char *val, size_t val_len); + +// TODO: implement sorting of the qs_kv array; for now ensure it's not compiled +#undef _qsSORTING + +// isxdigit _is_ available in , but let's avoid another header instead +#define CROW_QS_ISHEX(x) \ + ((((x) >= '0' && (x) <= '9') || ((x) >= 'A' && (x) <= 'F') || \ + ((x) >= 'a' && (x) <= 'f')) \ + ? 1 \ + : 0) +#define CROW_QS_HEX2DEC(x) \ + (((x) >= '0' && (x) <= '9') ? (x) - 48 \ + : ((x) >= 'A' && (x) <= 'F') ? (x) - 55 \ + : ((x) >= 'a' && (x) <= 'f') ? (x) - 87 \ + : 0) +#define CROW_QS_ISQSCHR(x) \ + ((((x) == '=') || ((x) == '#') || ((x) == '&') || ((x) == '\0')) ? 0 : 1) + +inline int qs_strncmp(const char *s, const char *qs, size_t n) { + unsigned char u1, u2, unyb, lnyb; + + while (n-- > 0) { + u1 = static_cast(*s++); + u2 = static_cast(*qs++); + + if (!CROW_QS_ISQSCHR(u1)) { u1 = '\0'; } + if (!CROW_QS_ISQSCHR(u2)) { u2 = '\0'; } + + if (u1 == '+') { u1 = ' '; } + if (u1 == '%') // easier/safer than scanf + { + unyb = static_cast(*s++); + lnyb = static_cast(*s++); + if (CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb)) + u1 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb); + else + u1 = '\0'; + } + + if (u2 == '+') { u2 = ' '; } + if (u2 == '%') // easier/safer than scanf + { + unyb = static_cast(*qs++); + lnyb = static_cast(*qs++); + if (CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb)) + u2 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb); + else + u2 = '\0'; + } + + if (u1 != u2) return u1 - u2; + if (u1 == '\0') return 0; + } + if (CROW_QS_ISQSCHR(*qs)) + return -1; + else + return 0; +} + +inline size_t qs_parse(char *qs, char *qs_kv[], size_t qs_kv_size, + bool parse_url = true) { + size_t i, j; + char *substr_ptr; + + for (i = 0; i < qs_kv_size; i++) + qs_kv[i] = NULL; + + // find the beginning of the k/v substrings or the fragment + substr_ptr = parse_url ? qs + strcspn(qs, "?#") : qs; + if (parse_url) { + if (substr_ptr[0] != '\0') + substr_ptr++; + else + return 0; // no query or fragment + } + + i = 0; + while (i < qs_kv_size) { + qs_kv[i] = substr_ptr; + j = strcspn(substr_ptr, "&"); + if (substr_ptr[j] == '\0') { + i++; + break; + } // x &'s -> means x iterations of this loop -> means *x+1* k/v pairs + substr_ptr += j + 1; + i++; + } + + // we only decode the values in place, the keys could have '='s in them + // which will hose our ability to distinguish keys from values later + for (j = 0; j < i; j++) { + substr_ptr = qs_kv[j] + strcspn(qs_kv[j], "=&#"); + if (substr_ptr[0] == '&' || + substr_ptr[0] == '\0') // blank value: skip decoding + substr_ptr[0] = '\0'; + else + qs_decode(++substr_ptr); + } + +#ifdef _qsSORTING +// TODO: qsort qs_kv, using qs_strncmp() for the comparison +#endif + + return i; +} + +inline int qs_decode(char *qs) { + int i = 0, j = 0; + + while (CROW_QS_ISQSCHR(qs[j])) { + if (qs[j] == '+') { + qs[i] = ' '; + } else if (qs[j] == '%') // easier/safer than scanf + { + if (!CROW_QS_ISHEX(qs[j + 1]) || !CROW_QS_ISHEX(qs[j + 2])) { + qs[i] = '\0'; + return i; + } + qs[i] = (CROW_QS_HEX2DEC(qs[j + 1]) * 16) + CROW_QS_HEX2DEC(qs[j + 2]); + j += 2; + } else { + qs[i] = qs[j]; + } + i++; + j++; + } + qs[i] = '\0'; + + return i; +} + +inline char *qs_k2v(const char *key, char *const *qs_kv, size_t qs_kv_size, + int nth = 0) { + size_t i; + size_t key_len, skip; + + key_len = strlen(key); + +#ifdef _qsSORTING +// TODO: binary search for key in the sorted qs_kv +#else // _qsSORTING + for (i = 0; i < qs_kv_size; i++) { + // we rely on the unambiguous '=' to find the value in our k/v pair + if (qs_strncmp(key, qs_kv[i], key_len) == 0) { + skip = strcspn(qs_kv[i], "="); + if (qs_kv[i][skip] == '=') skip++; + // return (zero-char value) ? ptr to trailing '\0' : ptr to value + if (nth == 0) + return qs_kv[i] + skip; + else + --nth; + } + } +#endif // _qsSORTING + + return nullptr; +} + +inline std::unique_ptr> +qs_dict_name2kv(const char *dict_name, char *const *qs_kv, size_t qs_kv_size, + int nth = 0) { + size_t i; + size_t name_len, skip_to_eq, skip_to_brace_open, skip_to_brace_close; + + name_len = strlen(dict_name); + +#ifdef _qsSORTING +// TODO: binary search for key in the sorted qs_kv +#else // _qsSORTING + for (i = 0; i < qs_kv_size; i++) { + if (strncmp(dict_name, qs_kv[i], name_len) == 0) { + skip_to_eq = strcspn(qs_kv[i], "="); + if (qs_kv[i][skip_to_eq] == '=') skip_to_eq++; + skip_to_brace_open = strcspn(qs_kv[i], "["); + if (qs_kv[i][skip_to_brace_open] == '[') skip_to_brace_open++; + skip_to_brace_close = strcspn(qs_kv[i], "]"); + + if (skip_to_brace_open <= skip_to_brace_close && skip_to_brace_open > 0 && + skip_to_brace_close > 0 && nth == 0) { + auto key = std::string(qs_kv[i] + skip_to_brace_open, + skip_to_brace_close - skip_to_brace_open); + auto value = std::string(qs_kv[i] + skip_to_eq); + return std::unique_ptr>( + new std::pair(key, value)); + } else { + --nth; + } + } + } +#endif // _qsSORTING + + return nullptr; +} + +inline char *qs_scanvalue(const char *key, const char *qs, char *val, + size_t val_len) { + const char *tmp = strchr(qs, '?'); + + // find the beginning of the k/v substrings + if (tmp != nullptr) qs = tmp + 1; + + const size_t key_len = strlen(key); + while (*qs != '#' && *qs != '\0') { + if (qs_strncmp(key, qs, key_len) == 0) break; + qs += strcspn(qs, "&"); + if (*qs == '&') qs++; + } + + if (qs[0] == '\0') return nullptr; + + qs += strcspn(qs, "=&#"); + if (qs[0] == '=') { + qs++; + size_t i = strcspn(qs, "&=#"); +#ifdef _MSC_VER + strncpy_s(val, val_len, qs, + (val_len - 1) < (i + 1) ? (val_len - 1) : (i + 1)); +#else + strncpy(val, qs, (val_len - 1) < (i + 1) ? (val_len - 1) : (i + 1)); +#endif + qs_decode(val); + } else { + if (val_len > 0) val[0] = '\0'; + } + + return val; +} +} // namespace crow +// ---------------------------------------------------------------------------- + +namespace crow { +struct request; +/// A class to represent any data coming after the `?` in the request URL into +/// key-value pairs. +class query_string { +public: + static const int MAX_KEY_VALUE_PAIRS_COUNT = 256; + + query_string() = default; + + query_string(const query_string &qs) : url_(qs.url_) { + for (auto p : qs.key_value_pairs_) { + key_value_pairs_.push_back((char *)(p - qs.url_.c_str() + url_.c_str())); + } + } + + query_string &operator=(const query_string &qs) { + url_ = qs.url_; + key_value_pairs_.clear(); + for (auto p : qs.key_value_pairs_) { + key_value_pairs_.push_back((char *)(p - qs.url_.c_str() + url_.c_str())); + } + return *this; + } + + query_string &operator=(query_string &&qs) noexcept { + key_value_pairs_ = std::move(qs.key_value_pairs_); + char *old_data = (char *)qs.url_.c_str(); + url_ = std::move(qs.url_); + for (auto &p : key_value_pairs_) { + p += (char *)url_.c_str() - old_data; + } + return *this; + } + + query_string(std::string params, bool url = true) : url_(std::move(params)) { + if (url_.empty()) return; + + key_value_pairs_.resize(MAX_KEY_VALUE_PAIRS_COUNT); + size_t count = qs_parse(&url_[0], &key_value_pairs_[0], + MAX_KEY_VALUE_PAIRS_COUNT, url); + + key_value_pairs_.resize(count); + key_value_pairs_.shrink_to_fit(); + } + + void clear() { + key_value_pairs_.clear(); + url_.clear(); + } + + friend std::ostream &operator<<(std::ostream &os, const query_string &qs) { + os << "[ "; + for (size_t i = 0; i < qs.key_value_pairs_.size(); ++i) { + if (i) os << ", "; + os << qs.key_value_pairs_[i]; + } + os << " ]"; + return os; + } + + /// Get a value from a name, used for `?name=value`. + + /// + /// Note: this method returns the value of the first occurrence of the key + /// only, to return all occurrences, see \ref get_list(). + char *get(const std::string &name) const { + char *ret = + qs_k2v(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size()); + return ret; + } + + /// Works similar to \ref get() except it removes the item from the query + /// string. + char *pop(const std::string &name) { + char *ret = get(name); + if (ret != nullptr) { + const std::string key_name = name + '='; + for (unsigned int i = 0; i < key_value_pairs_.size(); i++) { + std::string str_item(key_value_pairs_[i]); + if (str_item.find(key_name) == 0) { + key_value_pairs_.erase(key_value_pairs_.begin() + i); + break; + } + } + } + return ret; + } + + /// Returns a list of values, passed as + /// `?name[]=value1&name[]=value2&...name[]=valuen` with n being the size of + /// the list. + + /// + /// Note: Square brackets in the above example are controlled by + /// `use_brackets` boolean (true by default). If set to false, the example + /// becomes `?name=value1,name=value2...name=valuen` + std::vector get_list(const std::string &name, + bool use_brackets = true) const { + std::vector ret; + std::string plus = name + (use_brackets ? "[]" : ""); + char *element = nullptr; + + int count = 0; + while (1) { + element = qs_k2v(plus.c_str(), key_value_pairs_.data(), + key_value_pairs_.size(), count++); + if (!element) break; + ret.push_back(element); + } + return ret; + } + + /// Similar to \ref get_list() but it removes the + std::vector pop_list(const std::string &name, + bool use_brackets = true) { + std::vector ret = get_list(name, use_brackets); + const size_t name_len = name.length(); + if (!ret.empty()) { + for (unsigned int i = 0; i < key_value_pairs_.size(); i++) { + std::string str_item(key_value_pairs_[i]); + if (str_item.find(name) == 0) { + if (use_brackets && str_item.find("[]=", name_len) == name_len) { + key_value_pairs_.erase(key_value_pairs_.begin() + i--); + } else if (!use_brackets && + str_item.find('=', name_len) == name_len) { + key_value_pairs_.erase(key_value_pairs_.begin() + i--); + } + } + } + } + return ret; + } + + /// Works similar to \ref get_list() except the brackets are mandatory must + /// not be empty. + + /// + /// For example calling `get_dict(yourname)` on + /// `?yourname[sub1]=42&yourname[sub2]=84` would give a map containing `{sub1 + /// : 42, sub2 : 84}`. + /// + /// if your query string has both empty brackets and ones with a key inside, + /// use pop_list() to get all the values without a key before running this + /// method. + std::unordered_map + get_dict(const std::string &name) const { + std::unordered_map ret; + + int count = 0; + while (1) { + if (auto element = qs_dict_name2kv(name.c_str(), key_value_pairs_.data(), + key_value_pairs_.size(), count++)) + ret.insert(*element); + else + break; + } + return ret; + } + + /// Works the same as \ref get_dict() but removes the values from the query + /// string. + std::unordered_map + pop_dict(const std::string &name) { + const std::string name_value = name + '['; + std::unordered_map ret = get_dict(name); + if (!ret.empty()) { + for (unsigned int i = 0; i < key_value_pairs_.size(); i++) { + std::string str_item(key_value_pairs_[i]); + if (str_item.find(name_value) == 0) { + key_value_pairs_.erase(key_value_pairs_.begin() + i--); + } + } + } + return ret; + } + + std::vector keys() const { + std::vector keys; + keys.reserve(key_value_pairs_.size()); + + for (const char *const element : key_value_pairs_) { + const char *delimiter = strchr(element, '='); + if (delimiter) + keys.emplace_back(element, delimiter); + else + keys.emplace_back(element); + } + + return keys; + } + +private: + std::string url_; + std::vector key_value_pairs_; +}; + +} // namespace crow + +// This file is generated from nginx/conf/mime.types using nginx_mime2cpp.py on +// 2021-12-03. +#include +#include + +namespace crow { +const std::unordered_map mime_types{ + {"gz", "application/gzip"}, + {"shtml", "text/html"}, + {"htm", "text/html"}, + {"html", "text/html"}, + {"css", "text/css"}, + {"xml", "text/xml"}, + {"gif", "image/gif"}, + {"jpg", "image/jpeg"}, + {"jpeg", "image/jpeg"}, + {"js", "application/javascript"}, + {"atom", "application/atom+xml"}, + {"rss", "application/rss+xml"}, + {"mml", "text/mathml"}, + {"txt", "text/plain"}, + {"jad", "text/vnd.sun.j2me.app-descriptor"}, + {"wml", "text/vnd.wap.wml"}, + {"htc", "text/x-component"}, + {"avif", "image/avif"}, + {"png", "image/png"}, + {"svgz", "image/svg+xml"}, + {"svg", "image/svg+xml"}, + {"tiff", "image/tiff"}, + {"tif", "image/tiff"}, + {"wbmp", "image/vnd.wap.wbmp"}, + {"webp", "image/webp"}, + {"ico", "image/x-icon"}, + {"jng", "image/x-jng"}, + {"bmp", "image/x-ms-bmp"}, + {"woff", "font/woff"}, + {"woff2", "font/woff2"}, + {"ear", "application/java-archive"}, + {"war", "application/java-archive"}, + {"jar", "application/java-archive"}, + {"json", "application/json"}, + {"hqx", "application/mac-binhex40"}, + {"doc", "application/msword"}, + {"pdf", "application/pdf"}, + {"ai", "application/postscript"}, + {"eps", "application/postscript"}, + {"ps", "application/postscript"}, + {"rtf", "application/rtf"}, + {"m3u8", "application/vnd.apple.mpegurl"}, + {"kml", "application/vnd.google-earth.kml+xml"}, + {"kmz", "application/vnd.google-earth.kmz"}, + {"xls", "application/vnd.ms-excel"}, + {"eot", "application/vnd.ms-fontobject"}, + {"ppt", "application/vnd.ms-powerpoint"}, + {"odg", "application/vnd.oasis.opendocument.graphics"}, + {"odp", "application/vnd.oasis.opendocument.presentation"}, + {"ods", "application/vnd.oasis.opendocument.spreadsheet"}, + {"odt", "application/vnd.oasis.opendocument.text"}, + {"pptx", "application/" + "vnd.openxmlformats-officedocument.presentationml.presentation"}, + {"xlsx", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, + {"docx", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, + {"wmlc", "application/vnd.wap.wmlc"}, + {"wasm", "application/wasm"}, + {"7z", "application/x-7z-compressed"}, + {"cco", "application/x-cocoa"}, + {"jardiff", "application/x-java-archive-diff"}, + {"jnlp", "application/x-java-jnlp-file"}, + {"run", "application/x-makeself"}, + {"pm", "application/x-perl"}, + {"pl", "application/x-perl"}, + {"pdb", "application/x-pilot"}, + {"prc", "application/x-pilot"}, + {"rar", "application/x-rar-compressed"}, + {"rpm", "application/x-redhat-package-manager"}, + {"sea", "application/x-sea"}, + {"swf", "application/x-shockwave-flash"}, + {"sit", "application/x-stuffit"}, + {"tk", "application/x-tcl"}, + {"tcl", "application/x-tcl"}, + {"crt", "application/x-x509-ca-cert"}, + {"pem", "application/x-x509-ca-cert"}, + {"der", "application/x-x509-ca-cert"}, + {"xpi", "application/x-xpinstall"}, + {"xhtml", "application/xhtml+xml"}, + {"xspf", "application/xspf+xml"}, + {"zip", "application/zip"}, + {"dll", "application/octet-stream"}, + {"exe", "application/octet-stream"}, + {"bin", "application/octet-stream"}, + {"deb", "application/octet-stream"}, + {"dmg", "application/octet-stream"}, + {"img", "application/octet-stream"}, + {"iso", "application/octet-stream"}, + {"msm", "application/octet-stream"}, + {"msp", "application/octet-stream"}, + {"msi", "application/octet-stream"}, + {"kar", "audio/midi"}, + {"midi", "audio/midi"}, + {"mid", "audio/midi"}, + {"mp3", "audio/mpeg"}, + {"ogg", "audio/ogg"}, + {"m4a", "audio/x-m4a"}, + {"ra", "audio/x-realaudio"}, + {"3gp", "video/3gpp"}, + {"3gpp", "video/3gpp"}, + {"ts", "video/mp2t"}, + {"mp4", "video/mp4"}, + {"mpg", "video/mpeg"}, + {"mpeg", "video/mpeg"}, + {"mov", "video/quicktime"}, + {"webm", "video/webm"}, + {"flv", "video/x-flv"}, + {"m4v", "video/x-m4v"}, + {"mng", "video/x-mng"}, + {"asf", "video/x-ms-asf"}, + {"asx", "video/x-ms-asf"}, + {"wmv", "video/x-ms-wmv"}, + {"avi", "video/x-msvideo"}}; +} + // settings for crow // TODO(ipkn) replace with runtime config. libucl? /* #ifdef - enables debug mode */ -//#define CROW_ENABLE_DEBUG +// #define CROW_ENABLE_DEBUG /* #ifdef - enables logging */ #define CROW_ENABLE_LOGGING -/* #ifdef - enforces section 5.2 and 6.1 of RFC6455 (only accepting masked messages from clients) */ -//#define CROW_ENFORCE_WS_SPEC +/* #ifdef - enforces section 5.2 and 6.1 of RFC6455 (only accepting masked + * messages from clients) */ +// #define CROW_ENFORCE_WS_SPEC /* #define - specifies log level */ /* @@ -1012,22 +968,6 @@ namespace sha1 #endif // compiler flags -#if defined(_MSVC_LANG) && _MSVC_LANG >= 201402L -#define CROW_CAN_USE_CPP14 -#endif -#if __cplusplus >= 201402L -#define CROW_CAN_USE_CPP14 -#endif - -#if defined(_MSVC_LANG) && _MSVC_LANG >= 201703L -#define CROW_CAN_USE_CPP17 -#endif -#if __cplusplus >= 201703L -#define CROW_CAN_USE_CPP17 -#if defined(__GNUC__) && __GNUC__ < 8 -#define CROW_FILESYSTEM_IS_EXPERIMENTAL -#endif -#endif #if defined(_MSC_VER) #if _MSC_VER < 1900 @@ -1037,15 +977,6 @@ namespace sha1 #endif #endif -#if defined(__GNUC__) && __GNUC__ == 8 && __GNUC_MINOR__ < 4 -#if __cplusplus > 201103L -#define CROW_GCC83_WORKAROUND -#else -#error "GCC 8.1 - 8.3 has a bug that prevents Crow from compiling with C++11. Please update GCC to > 8.3 or use C++ > 11." -#endif -#endif - - #ifdef CROW_USE_BOOST #include #include @@ -1063,195 +994,420 @@ namespace sha1 #endif #endif -#if (CROW_USE_BOOST && BOOST_VERSION >= 107000) || (ASIO_VERSION >= 101300) -#define GET_IO_SERVICE(s) ((asio::io_context&)(s).get_executor().context()) +#if (defined(CROW_USE_BOOST) && BOOST_VERSION >= 107000) || \ + (ASIO_VERSION >= 101008) +#define GET_IO_CONTEXT(s) ((asio::io_context &)(s).get_executor().context()) #else -#define GET_IO_SERVICE(s) ((s).get_io_service()) +#define GET_IO_CONTEXT(s) ((s).get_io_service()) #endif -namespace crow -{ +namespace crow { #ifdef CROW_USE_BOOST - namespace asio = boost::asio; - using error_code = boost::system::error_code; +namespace asio = boost::asio; +using error_code = boost::system::error_code; #else - using error_code = asio::error_code; +using error_code = asio::error_code; #endif - using tcp = asio::ip::tcp; +using tcp = asio::ip::tcp; +using stream_protocol = asio::local::stream_protocol; - /// A wrapper for the asio::ip::tcp::socket and asio::ssl::stream - struct SocketAdaptor - { - using context = void; - SocketAdaptor(asio::io_service& io_service, context*): - socket_(io_service) - {} +/// A wrapper for the asio::ip::tcp::socket and asio::ssl::stream +struct SocketAdaptor { + using context = void; + SocketAdaptor(asio::io_context &io_context, context *) + : socket_(io_context) {} - asio::io_service& get_io_service() - { - return GET_IO_SERVICE(socket_); - } + asio::io_context &get_io_context() { return GET_IO_CONTEXT(socket_); } - /// Get the TCP socket handling data trasfers, regardless of what layer is handling transfers on top of the socket. - tcp::socket& raw_socket() - { - return socket_; - } + /// Get the TCP socket handling data transfers, regardless of what layer is + /// handling transfers on top of the socket. + tcp::socket &raw_socket() { return socket_; } - /// Get the object handling data transfers, this can be either a TCP socket or an SSL stream (if SSL is enabled). - tcp::socket& socket() - { - return socket_; - } + /// Get the object handling data transfers, this can be either a TCP socket or + /// an SSL stream (if SSL is enabled). + tcp::socket &socket() { return socket_; } - tcp::endpoint remote_endpoint() - { - return socket_.remote_endpoint(); - } + tcp::endpoint remote_endpoint() const { return socket_.remote_endpoint(); } - bool is_open() - { - return socket_.is_open(); - } + std::string address() const { + return socket_.remote_endpoint().address().to_string(); + } - void close() - { - error_code ec; - socket_.close(ec); - } + bool is_open() const { return socket_.is_open(); } - void shutdown_readwrite() - { - error_code ec; - socket_.shutdown(asio::socket_base::shutdown_type::shutdown_both, ec); - } + void close() { + error_code ec; + socket_.close(ec); + } - void shutdown_write() - { - error_code ec; - socket_.shutdown(asio::socket_base::shutdown_type::shutdown_send, ec); - } + void shutdown_readwrite() { + error_code ec; + socket_.shutdown(asio::socket_base::shutdown_type::shutdown_both, ec); + } - void shutdown_read() - { - error_code ec; - socket_.shutdown(asio::socket_base::shutdown_type::shutdown_receive, ec); - } + void shutdown_write() { + error_code ec; + socket_.shutdown(asio::socket_base::shutdown_type::shutdown_send, ec); + } - template - void start(F f) - { - f(error_code()); - } + void shutdown_read() { + error_code ec; + socket_.shutdown(asio::socket_base::shutdown_type::shutdown_receive, ec); + } - tcp::socket socket_; - }; + template void start(F f) { f(error_code()); } + + tcp::socket socket_; +}; + +struct UnixSocketAdaptor { + using context = void; + UnixSocketAdaptor(asio::io_context &io_context, context *) + : socket_(io_context) {} + + asio::io_context &get_io_context() { return GET_IO_CONTEXT(socket_); } + + stream_protocol::socket &raw_socket() { return socket_; } + + stream_protocol::socket &socket() { return socket_; } + + stream_protocol::endpoint remote_endpoint() { + return socket_.local_endpoint(); + } + + std::string address() const { return ""; } + + bool is_open() { return socket_.is_open(); } + + void close() { + error_code ec; + socket_.close(ec); + } + + void shutdown_readwrite() { + error_code ec; + socket_.shutdown(asio::socket_base::shutdown_type::shutdown_both, ec); + } + + void shutdown_write() { + error_code ec; + socket_.shutdown(asio::socket_base::shutdown_type::shutdown_send, ec); + } + + void shutdown_read() { + error_code ec; + socket_.shutdown(asio::socket_base::shutdown_type::shutdown_receive, ec); + } + + template void start(F f) { f(error_code()); } + + stream_protocol::socket socket_; +}; #ifdef CROW_ENABLE_SSL - struct SSLAdaptor - { - using context = asio::ssl::context; - using ssl_socket_t = asio::ssl::stream; - SSLAdaptor(asio::io_service& io_service, context* ctx): - ssl_socket_(new ssl_socket_t(io_service, *ctx)) - {} +struct SSLAdaptor { + using context = asio::ssl::context; + using ssl_socket_t = asio::ssl::stream; + SSLAdaptor(asio::io_context &io_context, context *ctx) + : ssl_socket_(new ssl_socket_t(io_context, *ctx)) {} - asio::ssl::stream& socket() - { - return *ssl_socket_; - } + asio::ssl::stream &socket() { return *ssl_socket_; } - tcp::socket::lowest_layer_type& - raw_socket() - { - return ssl_socket_->lowest_layer(); - } + tcp::socket::lowest_layer_type &raw_socket() { + return ssl_socket_->lowest_layer(); + } - tcp::endpoint remote_endpoint() - { - return raw_socket().remote_endpoint(); - } + tcp::endpoint remote_endpoint() { return raw_socket().remote_endpoint(); } - bool is_open() - { - return ssl_socket_ ? raw_socket().is_open() : false; - } + std::string address() const { + return ssl_socket_->lowest_layer().remote_endpoint().address().to_string(); + } - void close() - { - if (is_open()) - { - error_code ec; - raw_socket().close(ec); - } - } + bool is_open() { return ssl_socket_ ? raw_socket().is_open() : false; } - void shutdown_readwrite() - { - if (is_open()) - { - error_code ec; - raw_socket().shutdown(asio::socket_base::shutdown_type::shutdown_both, ec); - } - } + void close() { + if (is_open()) { + error_code ec; + raw_socket().close(ec); + } + } - void shutdown_write() - { - if (is_open()) - { - error_code ec; - raw_socket().shutdown(asio::socket_base::shutdown_type::shutdown_send, ec); - } - } + void shutdown_readwrite() { + if (is_open()) { + error_code ec; + raw_socket().shutdown(asio::socket_base::shutdown_type::shutdown_both, + ec); + } + } - void shutdown_read() - { - if (is_open()) - { - error_code ec; - raw_socket().shutdown(asio::socket_base::shutdown_type::shutdown_receive, ec); - } - } + void shutdown_write() { + if (is_open()) { + error_code ec; + raw_socket().shutdown(asio::socket_base::shutdown_type::shutdown_send, + ec); + } + } - asio::io_service& get_io_service() - { - return GET_IO_SERVICE(raw_socket()); - } + void shutdown_read() { + if (is_open()) { + error_code ec; + raw_socket().shutdown(asio::socket_base::shutdown_type::shutdown_receive, + ec); + } + } - template - void start(F f) - { - ssl_socket_->async_handshake(asio::ssl::stream_base::server, - [f](const error_code& ec) { - f(ec); - }); - } + asio::io_context &get_io_context() { return GET_IO_CONTEXT(raw_socket()); } - std::unique_ptr> ssl_socket_; - }; + template void start(F f) { + ssl_socket_->async_handshake(asio::ssl::stream_base::server, + [f](const error_code &ec) { f(ec); }); + } + + std::unique_ptr> ssl_socket_; +}; #endif } // namespace crow - -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include -#include -#include -#include +#include - -#if defined(CROW_CAN_USE_CPP17) && !defined(CROW_FILESYSTEM_IS_EXPERIMENTAL) -#include +namespace crow { +enum class LogLevel { +#ifndef ERROR +#ifndef DEBUG + DEBUG = 0, + INFO, + WARNING, + ERROR, + CRITICAL, +#endif #endif -// TODO(EDev): Adding C++20's [[likely]] and [[unlikely]] attributes might be useful + Debug = 0, + Info, + Warning, + Error, + Critical, +}; + +class ILogHandler { +public: + virtual ~ILogHandler() = default; + + virtual void log(const std::string &message, LogLevel level) = 0; +}; + +class CerrLogHandler : public ILogHandler { +public: + void log(const std::string &message, LogLevel level) override { + std::string log_msg; + log_msg.reserve(message.length() + 1 + 32 + 3 + 8 + 2); + log_msg.append("(").append(timestamp()).append(") ["); + + switch (level) { + case LogLevel::Debug: log_msg.append("DEBUG "); break; + case LogLevel::Info: log_msg.append("INFO "); break; + case LogLevel::Warning: log_msg.append("WARNING "); break; + case LogLevel::Error: log_msg.append("ERROR "); break; + case LogLevel::Critical: log_msg.append("CRITICAL"); break; + } + + log_msg.append("] ").append(message); + + std::cerr << log_msg << std::endl; + } + +private: + static std::string timestamp() { + char date[32]; + time_t t = time(0); + + tm my_tm; + +#if defined(_MSC_VER) || defined(__MINGW32__) +#ifdef CROW_USE_LOCALTIMEZONE + localtime_s(&my_tm, &t); +#else + gmtime_s(&my_tm, &t); +#endif +#else +#ifdef CROW_USE_LOCALTIMEZONE + localtime_r(&t, &my_tm); +#else + gmtime_r(&t, &my_tm); +#endif +#endif + + size_t sz = strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", &my_tm); + return std::string(date, date + sz); + } +}; + +class logger { +public: + logger(LogLevel level) : level_(level) {} + ~logger() { +#ifdef CROW_ENABLE_LOGGING + if (level_ >= get_current_log_level()) { + get_handler_ref()->log(stringstream_.str(), level_); + } +#endif + } + + // + template logger &operator<<(T const &value) { +#ifdef CROW_ENABLE_LOGGING + if (level_ >= get_current_log_level()) { stringstream_ << value; } +#endif + return *this; + } + + // + static void setLogLevel(LogLevel level) { get_log_level_ref() = level; } + + static void setHandler(ILogHandler *handler) { get_handler_ref() = handler; } + + static LogLevel get_current_log_level() { return get_log_level_ref(); } + +private: + // + static LogLevel &get_log_level_ref() { + static LogLevel current_level = static_cast(CROW_LOG_LEVEL); + return current_level; + } + static ILogHandler *&get_handler_ref() { + static CerrLogHandler default_handler; + static ILogHandler *current_handler = &default_handler; + return current_handler; + } + + // + std::ostringstream stringstream_; + LogLevel level_; +}; +} // namespace crow + +#define CROW_LOG_CRITICAL \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Critical) \ + crow::logger(crow::LogLevel::Critical) +#define CROW_LOG_ERROR \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Error) \ + crow::logger(crow::LogLevel::Error) +#define CROW_LOG_WARNING \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Warning) \ + crow::logger(crow::LogLevel::Warning) +#define CROW_LOG_INFO \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Info) \ + crow::logger(crow::LogLevel::Info) +#define CROW_LOG_DEBUG \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Debug) \ + crow::logger(crow::LogLevel::Debug) + +#include + +namespace crow { +/// An abstract class that allows any other class to be returned by a handler. +struct returnable { + std::string content_type; + virtual std::string dump() const = 0; + + returnable(std::string ctype) : content_type{ctype} {} + + virtual ~returnable() {} +}; +} // namespace crow + +#ifdef CROW_USE_BOOST +#include +#ifdef CROW_ENABLE_SSL +#include +#endif +#else +#ifndef ASIO_STANDALONE +#define ASIO_STANDALONE +#endif +#include +#ifdef CROW_ENABLE_SSL +#include +#endif +#endif + +namespace crow { +#ifdef CROW_USE_BOOST +namespace asio = boost::asio; +using error_code = boost::system::error_code; +#else +using error_code = asio::error_code; +#endif +using tcp = asio::ip::tcp; +using stream_protocol = asio::local::stream_protocol; + +struct TCPAcceptor { + using endpoint = tcp::endpoint; + tcp::acceptor acceptor_; + TCPAcceptor(asio::io_context &io_context) : acceptor_(io_context) {} + + int16_t port() const { return acceptor_.local_endpoint().port(); } + std::string address() const { + return acceptor_.local_endpoint().address().to_string(); + } + std::string url_display(bool ssl_used) const { + auto address = acceptor_.local_endpoint().address(); + return (ssl_used ? "https://" : "http://") + + (address.is_v4() ? address.to_string() + : "[" + address.to_string() + "]") + + ":" + std::to_string(acceptor_.local_endpoint().port()); + } + tcp::acceptor &raw_acceptor() { return acceptor_; } + endpoint local_endpoint() const { return acceptor_.local_endpoint(); } + inline static tcp::acceptor::reuse_address reuse_address_option() { + return tcp::acceptor::reuse_address(true); + } +}; + +struct UnixSocketAcceptor { + using endpoint = stream_protocol::endpoint; + stream_protocol::acceptor acceptor_; + UnixSocketAcceptor(asio::io_context &io_context) : acceptor_(io_context) {} + + int16_t port() const { return 0; } + std::string address() const { return acceptor_.local_endpoint().path(); } + std::string url_display(bool) const { + return acceptor_.local_endpoint().path(); + } + stream_protocol::acceptor &raw_acceptor() { return acceptor_; } + endpoint local_endpoint() const { return acceptor_.local_endpoint(); } + inline static stream_protocol::acceptor::reuse_address + reuse_address_option() { + // reuse addr must be false + // (https://github.com/chriskohlhoff/asio/issues/622) + return stream_protocol::acceptor::reuse_address(false); + } +}; +} // namespace crow + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// TODO(EDev): Adding C++20's [[likely]] and [[unlikely]] attributes might be +// useful #if defined(__GNUG__) || defined(__clang__) #define CROW_LIKELY(X) __builtin_expect(!!(X), 1) #define CROW_UNLIKELY(X) __builtin_expect(!!(X), 0) @@ -1260,1090 +1416,2654 @@ namespace crow #define CROW_UNLIKELY(X) (X) #endif -namespace crow -{ - /// @cond SKIP - namespace black_magic - { +namespace crow { +/// @cond SKIP +namespace black_magic { #ifndef CROW_MSVC_WORKAROUND - /// Out of Range Exception for const_str - struct OutOfRange - { - OutOfRange(unsigned /*pos*/, unsigned /*length*/) {} - }; - /// Helper function to throw an exception if i is larger than len - constexpr unsigned requires_in_range(unsigned i, unsigned len) - { - return i >= len ? throw OutOfRange(i, len) : i; - } +/// Out of Range Exception for const_str +struct OutOfRange { + OutOfRange(unsigned /*pos*/, unsigned /*length*/) {} +}; +/// Helper function to throw an exception if i is larger than len +constexpr unsigned requires_in_range(unsigned i, unsigned len) { + return i >= len ? throw OutOfRange(i, len) : i; +} - /// A constant string implementation. - class const_str - { - const char* const begin_; - unsigned size_; +/// A constant string implementation. +class const_str { + const char *const begin_; + unsigned size_; - public: - template - constexpr const_str(const char (&arr)[N]): - begin_(arr), size_(N - 1) - { - static_assert(N >= 1, "not a string literal"); - } - constexpr char operator[](unsigned i) const - { - return requires_in_range(i, size_), begin_[i]; - } +public: + template + constexpr const_str(const char (&arr)[N]) : begin_(arr), size_(N - 1) { + static_assert(N >= 1, "not a string literal"); + } + constexpr char operator[](unsigned i) const { + return requires_in_range(i, size_), begin_[i]; + } - constexpr operator const char*() const - { - return begin_; - } + constexpr operator const char *() const { return begin_; } - constexpr const char* begin() const { return begin_; } - constexpr const char* end() const { return begin_ + size_; } + constexpr const char *begin() const { return begin_; } + constexpr const char *end() const { return begin_ + size_; } - constexpr unsigned size() const - { - return size_; - } - }; + constexpr unsigned size() const { return size_; } +}; - constexpr unsigned find_closing_tag(const_str s, unsigned p) - { - return s[p] == '>' ? p : find_closing_tag(s, p + 1); - } +constexpr unsigned find_closing_tag(const_str s, unsigned p) { + return s[p] == '>' ? p : find_closing_tag(s, p + 1); +} - /// Check that the CROW_ROUTE string is valid - constexpr bool is_valid(const_str s, unsigned i = 0, int f = 0) - { - return i == s.size() ? f == 0 : - f < 0 || f >= 2 ? false : - s[i] == '<' ? is_valid(s, i + 1, f + 1) : - s[i] == '>' ? is_valid(s, i + 1, f - 1) : - is_valid(s, i + 1, f); - } +/// Check that the CROW_ROUTE string is valid +constexpr bool is_valid(const_str s, unsigned i = 0, int f = 0) { + return i == s.size() ? f == 0 + : f < 0 || f >= 2 ? false + : s[i] == '<' ? is_valid(s, i + 1, f + 1) + : s[i] == '>' ? is_valid(s, i + 1, f - 1) + : is_valid(s, i + 1, f); +} - constexpr bool is_equ_p(const char* a, const char* b, unsigned n) - { - return *a == 0 && *b == 0 && n == 0 ? true : - (*a == 0 || *b == 0) ? false : - n == 0 ? true : - *a != *b ? false : - is_equ_p(a + 1, b + 1, n - 1); - } +constexpr bool is_equ_p(const char *a, const char *b, unsigned n) { + return *a == 0 && *b == 0 && n == 0 ? true + : (*a == 0 || *b == 0) ? false + : n == 0 ? true + : *a != *b ? false + : is_equ_p(a + 1, b + 1, n - 1); +} - constexpr bool is_equ_n(const_str a, unsigned ai, const_str b, unsigned bi, unsigned n) - { - return ai + n > a.size() || bi + n > b.size() ? false : - n == 0 ? true : - a[ai] != b[bi] ? false : - is_equ_n(a, ai + 1, b, bi + 1, n - 1); - } +constexpr bool is_equ_n(const_str a, unsigned ai, const_str b, unsigned bi, + unsigned n) { + return ai + n > a.size() || bi + n > b.size() ? false + : n == 0 ? true + : a[ai] != b[bi] ? false + : is_equ_n(a, ai + 1, b, bi + 1, n - 1); +} - constexpr bool is_int(const_str s, unsigned i) - { - return is_equ_n(s, i, "", 0, 5); - } +constexpr bool is_int(const_str s, unsigned i) { + return is_equ_n(s, i, "", 0, 5); +} - constexpr bool is_uint(const_str s, unsigned i) - { - return is_equ_n(s, i, "", 0, 6); - } +constexpr bool is_uint(const_str s, unsigned i) { + return is_equ_n(s, i, "", 0, 6); +} - constexpr bool is_float(const_str s, unsigned i) - { - return is_equ_n(s, i, "", 0, 7) || - is_equ_n(s, i, "", 0, 8); - } +constexpr bool is_float(const_str s, unsigned i) { + return is_equ_n(s, i, "", 0, 7) || is_equ_n(s, i, "", 0, 8); +} - constexpr bool is_str(const_str s, unsigned i) - { - return is_equ_n(s, i, "", 0, 5) || - is_equ_n(s, i, "", 0, 8); - } +constexpr bool is_str(const_str s, unsigned i) { + return is_equ_n(s, i, "", 0, 5) || is_equ_n(s, i, "", 0, 8); +} - constexpr bool is_path(const_str s, unsigned i) - { - return is_equ_n(s, i, "", 0, 6); - } +constexpr bool is_path(const_str s, unsigned i) { + return is_equ_n(s, i, "", 0, 6); +} #endif - template - struct parameter_tag - { - static const int value = 0; - }; -#define CROW_INTERNAL_PARAMETER_TAG(t, i) \ - template<> \ - struct parameter_tag \ - { \ - static const int value = i; \ - } - CROW_INTERNAL_PARAMETER_TAG(int, 1); - CROW_INTERNAL_PARAMETER_TAG(char, 1); - CROW_INTERNAL_PARAMETER_TAG(short, 1); - CROW_INTERNAL_PARAMETER_TAG(long, 1); - CROW_INTERNAL_PARAMETER_TAG(long long, 1); - CROW_INTERNAL_PARAMETER_TAG(unsigned int, 2); - CROW_INTERNAL_PARAMETER_TAG(unsigned char, 2); - CROW_INTERNAL_PARAMETER_TAG(unsigned short, 2); - CROW_INTERNAL_PARAMETER_TAG(unsigned long, 2); - CROW_INTERNAL_PARAMETER_TAG(unsigned long long, 2); - CROW_INTERNAL_PARAMETER_TAG(double, 3); - CROW_INTERNAL_PARAMETER_TAG(std::string, 4); +template struct parameter_tag { + static const int value = 0; +}; +#define CROW_INTERNAL_PARAMETER_TAG(t, i) \ + template <> struct parameter_tag { \ + static const int value = i; \ + } +CROW_INTERNAL_PARAMETER_TAG(int, 1); +CROW_INTERNAL_PARAMETER_TAG(char, 1); +CROW_INTERNAL_PARAMETER_TAG(short, 1); +CROW_INTERNAL_PARAMETER_TAG(long, 1); +CROW_INTERNAL_PARAMETER_TAG(long long, 1); +CROW_INTERNAL_PARAMETER_TAG(unsigned int, 2); +CROW_INTERNAL_PARAMETER_TAG(unsigned char, 2); +CROW_INTERNAL_PARAMETER_TAG(unsigned short, 2); +CROW_INTERNAL_PARAMETER_TAG(unsigned long, 2); +CROW_INTERNAL_PARAMETER_TAG(unsigned long long, 2); +CROW_INTERNAL_PARAMETER_TAG(double, 3); +CROW_INTERNAL_PARAMETER_TAG(std::string, 4); #undef CROW_INTERNAL_PARAMETER_TAG - template - struct compute_parameter_tag_from_args_list; +template struct compute_parameter_tag_from_args_list; - template<> - struct compute_parameter_tag_from_args_list<> - { - static const int value = 0; - }; +template <> struct compute_parameter_tag_from_args_list<> { + static const int value = 0; +}; - template - struct compute_parameter_tag_from_args_list - { - static const int sub_value = - compute_parameter_tag_from_args_list::value; - static const int value = - parameter_tag::type>::value ? sub_value * 6 + parameter_tag::type>::value : sub_value; - }; +template +struct compute_parameter_tag_from_args_list { + static const int sub_value = + compute_parameter_tag_from_args_list::value; + static const int value = + parameter_tag::type>::value + ? sub_value * 6 + parameter_tag::type>::value + : sub_value; +}; - static inline bool is_parameter_tag_compatible(uint64_t a, uint64_t b) - { - if (a == 0) - return b == 0; - if (b == 0) - return a == 0; - int sa = a % 6; - int sb = a % 6; - if (sa == 5) sa = 4; - if (sb == 5) sb = 4; - if (sa != sb) - return false; - return is_parameter_tag_compatible(a / 6, b / 6); - } +static inline bool is_parameter_tag_compatible(uint64_t a, uint64_t b) { + if (a == 0) return b == 0; + if (b == 0) return a == 0; + int sa = a % 6; + int sb = a % 6; + if (sa == 5) sa = 4; + if (sb == 5) sb = 4; + if (sa != sb) return false; + return is_parameter_tag_compatible(a / 6, b / 6); +} - static inline unsigned find_closing_tag_runtime(const char* s, unsigned p) - { - return s[p] == 0 ? throw std::runtime_error("unmatched tag <") : - s[p] == '>' ? p : - find_closing_tag_runtime(s, p + 1); - } +static inline unsigned find_closing_tag_runtime(const char *s, unsigned p) { + return s[p] == 0 ? throw std::runtime_error("unmatched tag <") + : s[p] == '>' ? p + : find_closing_tag_runtime(s, p + 1); +} - static inline uint64_t get_parameter_tag_runtime(const char* s, unsigned p = 0) - { - return s[p] == 0 ? 0 : - s[p] == '<' ? ( - std::strncmp(s + p, "", 5) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 1 : - std::strncmp(s + p, "", 6) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 2 : - (std::strncmp(s + p, "", 7) == 0 || - std::strncmp(s + p, "", 8) == 0) ? - get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 3 : - (std::strncmp(s + p, "", 5) == 0 || - std::strncmp(s + p, "", 8) == 0) ? - get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 4 : - std::strncmp(s + p, "", 6) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 5 : - throw std::runtime_error("invalid parameter type")) : - get_parameter_tag_runtime(s, p + 1); - } +static inline uint64_t get_parameter_tag_runtime(const char *s, + unsigned p = 0) { + return s[p] == 0 ? 0 + : s[p] == '<' + ? (std::strncmp(s + p, "", 5) == 0 + ? get_parameter_tag_runtime( + s, find_closing_tag_runtime(s, p)) * + 6 + + 1 + : std::strncmp(s + p, "", 6) == 0 + ? get_parameter_tag_runtime( + s, find_closing_tag_runtime(s, p)) * + 6 + + 2 + : (std::strncmp(s + p, "", 7) == 0 || + std::strncmp(s + p, "", 8) == 0) + ? get_parameter_tag_runtime( + s, find_closing_tag_runtime(s, p)) * + 6 + + 3 + : (std::strncmp(s + p, "", 5) == 0 || + std::strncmp(s + p, "", 8) == 0) + ? get_parameter_tag_runtime( + s, find_closing_tag_runtime(s, p)) * + 6 + + 4 + : std::strncmp(s + p, "", 6) == 0 + ? get_parameter_tag_runtime( + s, find_closing_tag_runtime(s, p)) * + 6 + + 5 + : throw std::runtime_error("invalid parameter type")) + : get_parameter_tag_runtime(s, p + 1); +} #ifndef CROW_MSVC_WORKAROUND - constexpr uint64_t get_parameter_tag(const_str s, unsigned p = 0) - { - return p == s.size() ? 0 : - s[p] == '<' ? ( - is_int(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 1 : - is_uint(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 2 : - is_float(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 3 : - is_str(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 4 : - is_path(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 5 : - throw std::runtime_error("invalid parameter type")) : - get_parameter_tag(s, p + 1); - } +constexpr uint64_t get_parameter_tag(const_str s, unsigned p = 0) { + return p == s.size() ? 0 + : s[p] == '<' + ? (is_int(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 1 + : is_uint(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 2 + : is_float(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 3 + : is_str(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 4 + : is_path(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 5 + : throw std::runtime_error("invalid parameter type")) + : get_parameter_tag(s, p + 1); +} #endif - template - struct S - { - template - using push = S; - template - using push_back = S; - template class U> - using rebind = U; - }; +template struct S { + template using push = S; + template using push_back = S; + template