#pragma once // settings for crow // TODO - replace with runtime config. libucl? /* #ifdef - enables debug mode */ //#define CROW_ENABLE_DEBUG /* #ifdef - enables logging */ #define CROW_ENABLE_LOGGING /* #ifdef - enables ssl */ //#define CROW_ENABLE_SSL /* #define - specifies log level */ /* Debug = 0 Info = 1 Warning = 2 Error = 3 Critical = 4 default to INFO */ #ifndef CROW_LOG_LEVEL #define CROW_LOG_LEVEL 1 #endif // compiler flags #if __cplusplus >= 201402L #define CROW_CAN_USE_CPP14 #endif #if defined(_MSC_VER) #if _MSC_VER < 1900 #define CROW_MSVC_WORKAROUND #define constexpr const #define noexcept throw() #endif #endif #pragma once #include #include #include #include #include #include namespace crow { enum class LogLevel { #ifndef ERROR DEBUG = 0, INFO, WARNING, ERROR, CRITICAL, #endif Debug = 0, Info, Warning, Error, Critical, }; class ILogHandler { public: virtual void log(std::string message, LogLevel level) = 0; }; class CerrLogHandler : public ILogHandler { public: void log(std::string message, LogLevel /*level*/) override { std::cerr << message; } }; class logger { private: // static std::string timestamp() { char date[32]; time_t t = time(0); tm my_tm; #if defined(_MSC_VER) or defined(__MINGW32__) gmtime_s(&my_tm, &t); #else gmtime_r(&t, &my_tm); #endif size_t sz = strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", &my_tm); return std::string(date, date+sz); } public: logger(std::string prefix, LogLevel level) : level_(level) { #ifdef CROW_ENABLE_LOGGING stringstream_ << "(" << timestamp() << ") [" << prefix << "] "; #endif } ~logger() { #ifdef CROW_ENABLE_LOGGING if(level_ >= get_current_log_level()) { stringstream_ << std::endl; 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 = (LogLevel)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_; }; } #define CROW_LOG_CRITICAL \ if (crow::logger::get_current_log_level() <= crow::LogLevel::Critical) \ crow::logger("CRITICAL", crow::LogLevel::Critical) #define CROW_LOG_ERROR \ if (crow::logger::get_current_log_level() <= crow::LogLevel::Error) \ crow::logger("ERROR ", crow::LogLevel::Error) #define CROW_LOG_WARNING \ if (crow::logger::get_current_log_level() <= crow::LogLevel::Warning) \ crow::logger("WARNING ", crow::LogLevel::Warning) #define CROW_LOG_INFO \ if (crow::logger::get_current_log_level() <= crow::LogLevel::Info) \ crow::logger("INFO ", crow::LogLevel::Info) #define CROW_LOG_DEBUG \ if (crow::logger::get_current_log_level() <= crow::LogLevel::Debug) \ crow::logger("DEBUG ", crow::LogLevel::Debug) #pragma once #include #include #include #include #include namespace crow { namespace detail { ///Fast timer queue for fixed tick value. class dumb_timer_queue { public: static int tick; using key = std::pair; void cancel(key& k) { auto self = k.first; k.first = nullptr; if (!self) return; unsigned int index = (unsigned int)(k.second - self->step_); if (index < self->dq_.size()) self->dq_[index].second = nullptr; } key add(std::function f) { dq_.emplace_back(std::chrono::steady_clock::now(), std::move(f)); int ret = step_+dq_.size()-1; CROW_LOG_DEBUG << "timer add inside: " << this << ' ' << ret ; return {this, ret}; } void process() { if (!io_service_) return; auto now = std::chrono::steady_clock::now(); while(!dq_.empty()) { auto& x = dq_.front(); if (now - x.first < std::chrono::seconds(tick)) break; if (x.second) { CROW_LOG_DEBUG << "timer call: " << this << ' ' << step_; // we know that timer handlers are very simple currenty; call here x.second(); } dq_.pop_front(); step_++; } } void set_io_service(boost::asio::io_service& io_service) { io_service_ = &io_service; } dumb_timer_queue() noexcept { } private: boost::asio::io_service* io_service_{}; std::deque>> dq_; int step_{}; }; } } /* * * TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based * on the implementation in boost::uuid::details. * * 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 * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _TINY_SHA1_HPP_ #define _TINY_SHA1_HPP_ #include #include #include #include namespace sha1 { 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; }; } #endif #pragma once #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. */ int qs_parse(char * qs, char * qs_kv[], int qs_kv_size); /* 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, int 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) { int i=0; unsigned char u1, u2, unyb, lnyb; while(n-- > 0) { u1 = (unsigned char) *s++; u2 = (unsigned char) *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 = (unsigned char) *s++; lnyb = (unsigned char) *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 = (unsigned char) *qs++; lnyb = (unsigned char) *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; i++; } if ( CROW_QS_ISQSCHR(*qs) ) return -1; else return 0; } inline int qs_parse(char * qs, char * qs_kv[], int qs_kv_size) { int i, j; char * substr_ptr; for(i=0; i means x iterations of this loop -> means *x+1* k/v pairs // 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, int qs_kv_size, int nth = 0) { int 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 boost::make_optional(std::make_pair(key, value)); } else { --nth; } } } #endif // _qsSORTING return boost::none; } 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 { class query_string { public: static const int MAX_KEY_VALUE_PAIRS_COUNT = 256; query_string() { } 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) { 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 url) : url_(std::move(url)) { if (url_.empty()) return; key_value_pairs_.resize(MAX_KEY_VALUE_PAIRS_COUNT); int count = qs_parse(&url_[0], &key_value_pairs_[0], MAX_KEY_VALUE_PAIRS_COUNT); key_value_pairs_.resize(count); } 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; } char* get (const std::string& name) const { char* ret = qs_k2v(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size()); return ret; } std::vector get_list (const std::string& name) const { std::vector ret; std::string plus = name + "[]"; 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; } 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; } private: std::string url_; std::vector key_value_pairs_; }; } // end namespace #pragma once #include #ifdef CROW_ENABLE_SSL #include #endif #if BOOST_VERSION >= 107000 #define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context()) #else #define GET_IO_SERVICE(s) ((s).get_io_service()) #endif namespace crow { using namespace boost; using tcp = asio::ip::tcp; struct SocketAdaptor { using context = void; SocketAdaptor(boost::asio::io_service& io_service, context*) : socket_(io_service) { } boost::asio::io_service& get_io_service() { return GET_IO_SERVICE(socket_); } tcp::socket& raw_socket() { return socket_; } tcp::socket& socket() { return socket_; } tcp::endpoint remote_endpoint() { return socket_.remote_endpoint(); } bool is_open() { return socket_.is_open(); } void close() { boost::system::error_code ec; socket_.close(ec); } void shutdown_readwrite() { boost::system::error_code ec; socket_.shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, ec); } void shutdown_write() { boost::system::error_code ec; socket_.shutdown(boost::asio::socket_base::shutdown_type::shutdown_send, ec); } void shutdown_read() { boost::system::error_code ec; socket_.shutdown(boost::asio::socket_base::shutdown_type::shutdown_receive, ec); } template void start(F f) { f(boost::system::error_code()); } tcp::socket socket_; }; #ifdef CROW_ENABLE_SSL struct SSLAdaptor { using context = boost::asio::ssl::context; using ssl_socket_t = boost::asio::ssl::stream; SSLAdaptor(boost::asio::io_service& io_service, context* ctx) : ssl_socket_(new ssl_socket_t(io_service, *ctx)) { } boost::asio::ssl::stream& socket() { return *ssl_socket_; } tcp::socket::lowest_layer_type& raw_socket() { return ssl_socket_->lowest_layer(); } tcp::endpoint remote_endpoint() { return raw_socket().remote_endpoint(); } bool is_open() { return raw_socket().is_open(); } void close() { boost::system::error_code ec; raw_socket().close(ec); } void shutdown_readwrite() { boost::system::error_code ec; raw_socket().shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, ec); } void shutdown_write() { boost::system::error_code ec; raw_socket().shutdown(boost::asio::socket_base::shutdown_type::shutdown_send, ec); } void shutdown_read() { boost::system::error_code ec; raw_socket().shutdown(boost::asio::socket_base::shutdown_type::shutdown_receive, ec); } boost::asio::io_service& get_io_service() { return GET_IO_SERVICE(raw_socket()); } template void start(F f) { ssl_socket_->async_handshake(boost::asio::ssl::stream_base::server, [f](const boost::system::error_code& ec) { f(ec); }); } std::unique_ptr> ssl_socket_; }; #endif } //This file is generated from nginx/conf/mime.types using nginx_mime2cpp.py #include #include namespace crow { 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"}, {"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"}, {"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"} }; } #pragma once #include #include #include namespace crow { struct ci_hash { size_t operator()(const std::string& key) const { std::size_t seed = 0; std::locale locale; for(auto c : key) { boost::hash_combine(seed, std::toupper(c, locale)); } return seed; } }; struct ci_key_eq { bool operator()(const std::string& l, const std::string& r) const { return boost::iequals(l, r); } }; using ci_map = std::unordered_multimap; } #pragma once #include #include #include #include #include #include #include namespace crow { namespace black_magic { #ifndef CROW_MSVC_WORKAROUND struct OutOfRange { OutOfRange(unsigned /*pos*/, unsigned /*length*/) {} }; constexpr unsigned requires_in_range( unsigned i, unsigned len ) { return i >= len ? throw OutOfRange(i, len) : i; } class const_str { const char * const begin_; unsigned size_; public: template< unsigned N > 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 const char* begin() const { return begin_; } constexpr const char* end() const { return begin_ + 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 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_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_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_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); } #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); #undef CROW_INTERNAL_PARAMETER_TAG 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 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 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); } #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); } #endif template struct S { template using push = S; template using push_back = S; template class U> using rebind = U; }; template struct CallHelper; template struct CallHelper> { template ()(std::declval()...)) > static char __test(int); template static int __test(...); static constexpr bool value = sizeof(__test(0)) == sizeof(char); }; template struct single_tag_to_type { }; template <> struct single_tag_to_type<1> { using type = int64_t; }; template <> struct single_tag_to_type<2> { using type = uint64_t; }; template <> struct single_tag_to_type<3> { using type = double; }; template <> struct single_tag_to_type<4> { using type = std::string; }; template <> struct single_tag_to_type<5> { using type = std::string; }; template struct arguments { using subarguments = typename arguments::type; using type = typename subarguments::template push::type>; }; template <> struct arguments<0> { using type = S<>; }; template struct last_element_type { using type = typename std::tuple_element>::type; }; template <> struct last_element_type<> { }; // from http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth template using Invoke = typename T::type; template struct seq{ using type = seq; }; template struct concat; template struct concat, seq> : seq{}; template using Concat = Invoke>; template struct gen_seq; template using GenSeq = Invoke>; template struct gen_seq : Concat, GenSeq>{}; template<> struct gen_seq<0> : seq<>{}; template<> struct gen_seq<1> : seq<0>{}; template struct pop_back_helper; template struct pop_back_helper, Tuple> { template