diff options
author | Sergiu Giurgiu <sgiurgiu11@gmail.com> | 2016-12-04 14:03:20 -0500 |
---|---|---|
committer | Sergiu Giurgiu <sgiurgiu11@gmail.com> | 2016-12-04 14:03:20 -0500 |
commit | c63113f8250c3ef0eb4dfc25aeab7e2e7c475fc4 (patch) | |
tree | 72fcad77e8393bd236ed684efaed97ee22721e7a /amalgamate | |
parent | 4e39b23e455e455f1878b3e68d729a1737f3e431 (diff) | |
download | crow-c63113f8250c3ef0eb4dfc25aeab7e2e7c475fc4.tar.gz crow-c63113f8250c3ef0eb4dfc25aeab7e2e7c475fc4.zip |
Removed strict-aliasing warning
Diffstat (limited to 'amalgamate')
-rw-r--r-- | amalgamate/crow_all.h | 4589 |
1 files changed, 2298 insertions, 2291 deletions
diff --git a/amalgamate/crow_all.h b/amalgamate/crow_all.h index 41b06ff..b2bf8d0 100644 --- a/amalgamate/crow_all.h +++ b/amalgamate/crow_all.h @@ -1,348 +1,3 @@ -#pragma once - -#include <stdio.h> -#include <string.h> -#include <string> -#include <vector> -#include <iostream> - -// ---------------------------------------------------------------------------- -// 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 <ctype.h>, 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<qs_kv_size; i++) qs_kv[i] = NULL; - - // find the beginning of the k/v substrings or the fragment - substr_ptr = qs + strcspn(qs, "?#"); - 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' ) { break; } - substr_ptr += j + 1; - i++; - } - i++; // x &'s -> 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<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, int qs_kv_size, int nth = 0) -{ - int 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 NULL; -} - - -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, "&=#"); - strncpy(val, qs, (val_len-1)<(i+1) ? (val_len-1) : (i+1)); - 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<char*> get_list (const std::string& name) const - { - std::vector<char*> 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; - } - - - private: - std::string url_; - std::vector<char*> key_value_pairs_; - }; - -} // end namespace - - - /* merged revision: 5b951d74bd66ec9d38448e0a85b1cf8b85d97db3 */ /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * @@ -3023,6 +2678,599 @@ namespace crow +#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 +*/ +#define CROW_LOG_LEVEL 1 + + +// 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 <cstdint> +#include <stdexcept> +#include <tuple> +#include <type_traits> +#include <cstring> +#include <functional> +#include <string> + + + + +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, "<int>", 0, 5); + } + + constexpr bool is_uint(const_str s, unsigned i) + { + return is_equ_n(s, i, "<uint>", 0, 6); + } + + constexpr bool is_float(const_str s, unsigned i) + { + return is_equ_n(s, i, "<float>", 0, 7) || + is_equ_n(s, i, "<double>", 0, 8); + } + + constexpr bool is_str(const_str s, unsigned i) + { + return is_equ_n(s, i, "<str>", 0, 5) || + is_equ_n(s, i, "<string>", 0, 8); + } + + constexpr bool is_path(const_str s, unsigned i) + { + return is_equ_n(s, i, "<path>", 0, 6); + } +#endif + template <typename T> + struct parameter_tag + { + static const int value = 0; + }; +#define CROW_INTERNAL_PARAMETER_TAG(t, i) \ +template <> \ +struct parameter_tag<t> \ +{ \ + 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 <typename ... Args> + struct compute_parameter_tag_from_args_list; + + template <> + struct compute_parameter_tag_from_args_list<> + { + static const int value = 0; + }; + + template <typename Arg, typename ... Args> + struct compute_parameter_tag_from_args_list<Arg, Args...> + { + static const int sub_value = + compute_parameter_tag_from_args_list<Args...>::value; + static const int value = + parameter_tag<typename std::decay<Arg>::type>::value + ? sub_value* 6 + parameter_tag<typename std::decay<Arg>::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, "<int>", 5) == 0 + ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 1 : + std::strncmp(s+p, "<uint>", 6) == 0 + ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 2 : + (std::strncmp(s+p, "<float>", 7) == 0 || + std::strncmp(s+p, "<double>", 8) == 0) + ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 3 : + (std::strncmp(s+p, "<str>", 5) == 0 || + std::strncmp(s+p, "<string>", 8) == 0) + ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 4 : + std::strncmp(s+p, "<path>", 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 <typename ... T> + struct S + { + template <typename U> + using push = S<U, T...>; + template <typename U> + using push_back = S<T..., U>; + template <template<typename ... Args> class U> + using rebind = U<T...>; + }; +template <typename F, typename Set> + struct CallHelper; + template <typename F, typename ...Args> + struct CallHelper<F, S<Args...>> + { + template <typename F1, typename ...Args1, typename = + decltype(std::declval<F1>()(std::declval<Args1>()...)) + > + static char __test(int); + + template <typename ...> + static int __test(...); + + static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char); + }; + + + template <int N> + 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 <uint64_t Tag> + struct arguments + { + using subarguments = typename arguments<Tag/6>::type; + using type = + typename subarguments::template push<typename single_tag_to_type<Tag%6>::type>; + }; + + template <> + struct arguments<0> + { + using type = S<>; + }; + + template <typename ... T> + struct last_element_type + { + using type = typename std::tuple_element<sizeof...(T)-1, std::tuple<T...>>::type; + }; + + + template <> + struct last_element_type<> + { + }; + + + // from http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth + template<class T> using Invoke = typename T::type; + + template<unsigned...> struct seq{ using type = seq; }; + + template<class S1, class S2> struct concat; + + template<unsigned... I1, unsigned... I2> + struct concat<seq<I1...>, seq<I2...>> + : seq<I1..., (sizeof...(I1)+I2)...>{}; + + template<class S1, class S2> + using Concat = Invoke<concat<S1, S2>>; + + template<unsigned N> struct gen_seq; + template<unsigned N> using GenSeq = Invoke<gen_seq<N>>; + + template<unsigned N> + struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{}; + + template<> struct gen_seq<0> : seq<>{}; + template<> struct gen_seq<1> : seq<0>{}; + + template <typename Seq, typename Tuple> + struct pop_back_helper; + + template <unsigned ... N, typename Tuple> + struct pop_back_helper<seq<N...>, Tuple> + { + template <template <typename ... Args> class U> + using rebind = U<typename std::tuple_element<N, Tuple>::type...>; + }; + + template <typename ... T> + struct pop_back //: public pop_back_helper<typename gen_seq<sizeof...(T)-1>::type, std::tuple<T...>> + { + template <template <typename ... Args> class U> + using rebind = typename pop_back_helper<typename gen_seq<sizeof...(T)-1>::type, std::tuple<T...>>::template rebind<U>; + }; + + template <> + struct pop_back<> + { + template <template <typename ... Args> class U> + using rebind = U<>; + }; + + // from http://stackoverflow.com/questions/2118541/check-if-c0x-parameter-pack-contains-a-type + template < typename Tp, typename... List > + struct contains : std::true_type {}; + + template < typename Tp, typename Head, typename... Rest > + struct contains<Tp, Head, Rest...> + : std::conditional< std::is_same<Tp, Head>::value, + std::true_type, + contains<Tp, Rest...> + >::type {}; + + template < typename Tp > + struct contains<Tp> : std::false_type {}; + + template <typename T> + struct empty_context + { + }; + + template <typename T> + struct promote + { + using type = T; + }; + +#define CROW_INTERNAL_PROMOTE_TYPE(t1, t2) \ + template<> \ + struct promote<t1> \ + { \ + using type = t2; \ + } + + CROW_INTERNAL_PROMOTE_TYPE(char, int64_t); + CROW_INTERNAL_PROMOTE_TYPE(short, int64_t); + CROW_INTERNAL_PROMOTE_TYPE(int, int64_t); + CROW_INTERNAL_PROMOTE_TYPE(long, int64_t); + CROW_INTERNAL_PROMOTE_TYPE(long long, int64_t); + CROW_INTERNAL_PROMOTE_TYPE(unsigned char, uint64_t); + CROW_INTERNAL_PROMOTE_TYPE(unsigned short, uint64_t); + CROW_INTERNAL_PROMOTE_TYPE(unsigned int, uint64_t); + CROW_INTERNAL_PROMOTE_TYPE(unsigned long, uint64_t); + CROW_INTERNAL_PROMOTE_TYPE(unsigned long long, uint64_t); + CROW_INTERNAL_PROMOTE_TYPE(float, double); +#undef CROW_INTERNAL_PROMOTE_TYPE + + template <typename T> + using promote_t = typename promote<T>::type; + + } // namespace black_magic + + namespace detail + { + + template <class T, std::size_t N, class... Args> + struct get_index_of_element_from_tuple_by_type_impl + { + static constexpr auto value = N; + }; + + template <class T, std::size_t N, class... Args> + struct get_index_of_element_from_tuple_by_type_impl<T, N, T, Args...> + { + static constexpr auto value = N; + }; + + template <class T, std::size_t N, class U, class... Args> + struct get_index_of_element_from_tuple_by_type_impl<T, N, U, Args...> + { + static constexpr auto value = get_index_of_element_from_tuple_by_type_impl<T, N + 1, Args...>::value; + }; + + } // namespace detail + + namespace utility + { + template <class T, class... Args> + T& get_element_by_type(std::tuple<Args...>& t) + { + return std::get<detail::get_index_of_element_from_tuple_by_type_impl<T, 0, Args...>::value>(t); + } + + template<typename T> + struct function_traits; + +#ifndef CROW_MSVC_WORKAROUND + template<typename T> + struct function_traits : public function_traits<decltype(&T::operator())> + { + using parent_t = function_traits<decltype(&T::operator())>; + static const size_t arity = parent_t::arity; + using result_type = typename parent_t::result_type; + template <size_t i> + using arg = typename parent_t::template arg<i>; + + }; +#endif + + template<typename ClassType, typename R, typename ...Args> + struct function_traits<R(ClassType::*)(Args...) const> + { + static const size_t arity = sizeof...(Args); + + typedef R result_type; + + template <size_t i> + using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; + }; + + template<typename ClassType, typename R, typename ...Args> + struct function_traits<R(ClassType::*)(Args...)> + { + static const size_t arity = sizeof...(Args); + + typedef R result_type; + + template <size_t i> + using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; + }; + + template<typename R, typename ...Args> + struct function_traits<std::function<R(Args...)>> + { + static const size_t arity = sizeof...(Args); + + typedef R result_type; + + template <size_t i> + using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; + }; + + inline static std::string base64encode(const char* data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") + { + std::string ret; + ret.resize((size+2) / 3 * 4); + auto it = ret.begin(); + while(size >= 3) + { + *it++ = key[(((unsigned char)*data)&0xFC)>>2]; + unsigned char h = (((unsigned char)*data++) & 0x03) << 4; + *it++ = key[h|((((unsigned char)*data)&0xF0)>>4)]; + h = (((unsigned char)*data++) & 0x0F) << 2; + *it++ = key[h|((((unsigned char)*data)&0xC0)>>6)]; + *it++ = key[((unsigned char)*data++)&0x3F]; + + size -= 3; + } + if (size == 1) + { + *it++ = key[(((unsigned char)*data)&0xFC)>>2]; + unsigned char h = (((unsigned char)*data++) & 0x03) << 4; + *it++ = key[h]; + *it++ = '='; + *it++ = '='; + } + else if (size == 2) + { + *it++ = key[(((unsigned char)*data)&0xFC)>>2]; + unsigned char h = (((unsigned char)*data++) & 0x03) << 4; + *it++ = key[h|((((unsigned char)*data)&0xF0)>>4)]; + h = (((unsigned char)*data++) & 0x0F) << 2; + *it++ = key[h]; + *it++ = '='; + } + return ret; + } + + inline static std::string base64encode_urlsafe(const char* data, size_t size) + { + return base64encode(data, size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"); + } + + + } // namespace utility +} + + + /* * * TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based @@ -3223,47 +3471,6 @@ namespace sha1 #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 -*/ -#define CROW_LOG_LEVEL 1 - - -// 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 <boost/asio.hpp> #ifdef CROW_ENABLE_SSL #include <boost/asio/ssl.hpp> @@ -3381,6 +3588,500 @@ namespace crow #pragma once +#include <stdio.h> +#include <string.h> +#include <string> +#include <vector> +#include <iostream> + +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 <ctype.h>, 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<qs_kv_size; i++) qs_kv[i] = NULL; + + // find the beginning of the k/v substrings or the fragment + substr_ptr = qs + strcspn(qs, "?#"); + 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' ) { break; } + substr_ptr += j + 1; + i++; + } + i++; // x &'s -> 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<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, int qs_kv_size, int nth = 0) +{ + int 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 NULL; +} + + +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, "&=#"); + strncpy(val, qs, (val_len-1)<(i+1) ? (val_len-1) : (i+1)); + 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<char*> get_list (const std::string& name) const + { + std::vector<char*> 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; + } + + + private: + std::string url_; + std::vector<char*> key_value_pairs_; + }; + +} // end namespace + + + +#pragma once + +#include <string> +#include <cstdio> +#include <cstdlib> +#include <ctime> +#include <iostream> +#include <sstream> + + + + +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; + +#ifdef _MSC_VER + 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 <typename T> + 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 + //#define CROW_JSON_NO_ERROR_CHECK #include <string> @@ -5400,152 +6101,6 @@ namespace crow #pragma once -#include <string> -#include <cstdio> -#include <cstdlib> -#include <ctime> -#include <iostream> -#include <sstream> - - - - -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; - -#ifdef _MSC_VER - 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 <typename T> - 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 <boost/asio.hpp> #include <deque> #include <functional> @@ -5631,700 +6186,1217 @@ namespace crow #pragma once -#include <cstdint> -#include <stdexcept> -#include <tuple> -#include <type_traits> -#include <cstring> -#include <functional> +#include <vector> #include <string> - +#include <stdexcept> +#include <iostream> namespace crow { - namespace black_magic + enum class HTTPMethod { -#ifndef CROW_MSVC_WORKAROUND - struct OutOfRange - { - OutOfRange(unsigned /*pos*/, unsigned /*length*/) {} - }; - constexpr unsigned requires_in_range( unsigned i, unsigned len ) +#ifndef DELETE + DELETE = 0, + GET, + HEAD, + POST, + PUT, + CONNECT, + OPTIONS, + TRACE, +#endif + + Delete = 0, + Get, + Head, + Post, + Put, + Connect, + Options, + Trace, + }; + + inline std::string method_name(HTTPMethod method) + { + switch(method) { - return i >= len ? throw OutOfRange(i, len) : i; + case HTTPMethod::Delete: + return "DELETE"; + case HTTPMethod::Get: + return "GET"; + case HTTPMethod::Head: + return "HEAD"; + case HTTPMethod::Post: + return "POST"; + case HTTPMethod::Put: + return "PUT"; + case HTTPMethod::Connect: + return "CONNECT"; + case HTTPMethod::Options: + return "OPTIONS"; + case HTTPMethod::Trace: + return "TRACE"; } + return "invalid"; + } - class const_str + enum class ParamType + { + INT, + UINT, + DOUBLE, + STRING, + PATH, + + MAX + }; + + struct routing_params + { + std::vector<int64_t> int_params; + std::vector<uint64_t> uint_params; + std::vector<double> double_params; + std::vector<std::string> string_params; + + void debug_print() const { - const char * const begin_; - unsigned size_; + std::cerr << "routing_params" << std::endl; + for(auto i:int_params) + std::cerr<<i <<", " ; + std::cerr<<std::endl; + for(auto i:uint_params) + std::cerr<<i <<", " ; + std::cerr<<std::endl; + for(auto i:double_params) + std::cerr<<i <<", " ; + std::cerr<<std::endl; + for(auto& i:string_params) + std::cerr<<i <<", " ; + std::cerr<<std::endl; + } - 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]; - } + template <typename T> + T get(unsigned) const; - constexpr operator const char *() const { - return begin_; - } + }; - constexpr const char* begin() const { return begin_; } - constexpr const char* end() const { return begin_ + size_; } + template<> + inline int64_t routing_params::get<int64_t>(unsigned index) const + { + return int_params[index]; + } - constexpr unsigned size() const { - return size_; - } - }; + template<> + inline uint64_t routing_params::get<uint64_t>(unsigned index) const + { + return uint_params[index]; + } - constexpr unsigned find_closing_tag(const_str s, unsigned p) + template<> + inline double routing_params::get<double>(unsigned index) const + { + return double_params[index]; + } + + template<> + inline std::string routing_params::get<std::string>(unsigned index) const + { + return string_params[index]; + } +} + +#ifndef CROW_MSVC_WORKAROUND +constexpr crow::HTTPMethod operator "" _method(const char* str, size_t /*len*/) +{ + return + crow::black_magic::is_equ_p(str, "GET", 3) ? crow::HTTPMethod::Get : + crow::black_magic::is_equ_p(str, "DELETE", 6) ? crow::HTTPMethod::Delete : + crow::black_magic::is_equ_p(str, "HEAD", 4) ? crow::HTTPMethod::Head : + crow::black_magic::is_equ_p(str, "POST", 4) ? crow::HTTPMethod::Post : + crow::black_magic::is_equ_p(str, "PUT", 3) ? crow::HTTPMethod::Put : + crow::black_magic::is_equ_p(str, "OPTIONS", 7) ? crow::HTTPMethod::Options : + crow::black_magic::is_equ_p(str, "CONNECT", 7) ? crow::HTTPMethod::Connect : + crow::black_magic::is_equ_p(str, "TRACE", 5) ? crow::HTTPMethod::Trace : + throw std::runtime_error("invalid http method"); +} +#endif + + + +#pragma once + +#include <boost/asio.hpp> + + + + + + + + +namespace crow +{ + template <typename T> + inline const std::string& get_header_value(const T& headers, const std::string& key) + { + if (headers.count(key)) { - return s[p] == '>' ? p : find_closing_tag(s, p+1); + return headers.find(key)->second; } + static std::string empty; + return empty; + } - constexpr bool is_valid(const_str s, unsigned i = 0, int f = 0) + struct DetachHelper; + + struct request + { + HTTPMethod method; + std::string raw_url; + std::string url; + query_string url_params; + ci_map headers; + std::string body; + + void* middleware_context{}; + boost::asio::io_service* io_service{}; + + request() + : method(HTTPMethod::Get) { - 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) + request(HTTPMethod method, std::string raw_url, std::string url, query_string url_params, ci_map headers, std::string body) + : method(method), raw_url(std::move(raw_url)), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body)) { - 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) + void add_header(std::string key, std::string value) { - 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); + headers.emplace(std::move(key), std::move(value)); } - constexpr bool is_int(const_str s, unsigned i) + const std::string& get_header_value(const std::string& key) const { - return is_equ_n(s, i, "<int>", 0, 5); + return crow::get_header_value(headers, key); } - constexpr bool is_uint(const_str s, unsigned i) + template<typename CompletionHandler> + void post(CompletionHandler handler) { - return is_equ_n(s, i, "<uint>", 0, 6); + io_service->post(handler); } - constexpr bool is_float(const_str s, unsigned i) + template<typename CompletionHandler> + void dispatch(CompletionHandler handler) { - return is_equ_n(s, i, "<float>", 0, 7) || - is_equ_n(s, i, "<double>", 0, 8); + io_service->dispatch(handler); } - constexpr bool is_str(const_str s, unsigned i) + }; +} + + + +#pragma once + +#include <string> +#include <unordered_map> +#include <boost/algorithm/string.hpp> +#include <boost/tokenizer.hpp> +#include <algorithm> + + + + + + +namespace crow +{ + template <typename Handler> + struct HTTPParser : public http_parser + { + static int on_message_begin(http_parser* self_) { - return is_equ_n(s, i, "<str>", 0, 5) || - is_equ_n(s, i, "<string>", 0, 8); + HTTPParser* self = static_cast<HTTPParser*>(self_); + self->clear(); + return 0; + } + static int on_url(http_parser* self_, const char* at, size_t length) + { + HTTPParser* self = static_cast<HTTPParser*>(self_); + self->raw_url.insert(self->raw_url.end(), at, at+length); + return 0; + } + static int on_header_field(http_parser* self_, const char* at, size_t length) + { + HTTPParser* self = static_cast<HTTPParser*>(self_); + switch (self->header_building_state) + { + case 0: + if (!self->header_value.empty()) + { + self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); + } + self->header_field.assign(at, at+length); + self->header_building_state = 1; + break; + case 1: + self->header_field.insert(self->header_field.end(), at, at+length); + break; + } + return 0; } + static int on_header_value(http_parser* self_, const char* at, size_t length) + { + HTTPParser* self = static_cast<HTTPParser*>(self_); + switch (self->header_building_state) + { + case 0: + self->header_value.insert(self->header_value.end(), at, at+length); + break; + case 1: + self->header_building_state = 0; + self->header_value.assign(at, at+length); + break; + } + return 0; + } + static int on_headers_complete(http_parser* self_) + { + HTTPParser* self = static_cast<HTTPParser*>(self_); + if (!self->header_field.empty()) + { + self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); + } + self->process_header(); + return 0; + } + static int on_body(http_parser* self_, const char* at, size_t length) + { + HTTPParser* self = static_cast<HTTPParser*>(self_); + self->body.insert(self->body.end(), at, at+length); + return 0; + } + static int on_message_complete(http_parser* self_) + { + HTTPParser* self = static_cast<HTTPParser*>(self_); - constexpr bool is_path(const_str s, unsigned i) + // url params + self->url = self->raw_url.substr(0, self->raw_url.find("?")); + self->url_params = query_string(self->raw_url); + + self->process_message(); + return 0; + } + HTTPParser(Handler* handler) : + handler_(handler) { - return is_equ_n(s, i, "<path>", 0, 6); + http_parser_init(this, HTTP_REQUEST); } -#endif - template <typename T> - struct parameter_tag + + // return false on error + bool feed(const char* buffer, int length) { - static const int value = 0; - }; -#define CROW_INTERNAL_PARAMETER_TAG(t, i) \ -template <> \ -struct parameter_tag<t> \ -{ \ - 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 <typename ... Args> - struct compute_parameter_tag_from_args_list; + const static http_parser_settings settings_{ + on_message_begin, + on_url, + nullptr, + on_header_field, + on_header_value, + on_headers_complete, + on_body, + on_message_complete, + }; - template <> - struct compute_parameter_tag_from_args_list<> + int nparsed = http_parser_execute(this, &settings_, buffer, length); + return nparsed == length; + } + + bool done() { - static const int value = 0; - }; + return feed(nullptr, 0); + } - template <typename Arg, typename ... Args> - struct compute_parameter_tag_from_args_list<Arg, Args...> + void clear() { - static const int sub_value = - compute_parameter_tag_from_args_list<Args...>::value; - static const int value = - parameter_tag<typename std::decay<Arg>::type>::value - ? sub_value* 6 + parameter_tag<typename std::decay<Arg>::type>::value - : sub_value; - }; + url.clear(); + raw_url.clear(); + header_building_state = 0; + header_field.clear(); + header_value.clear(); + headers.clear(); + url_params.clear(); + body.clear(); + } - static inline bool is_parameter_tag_compatible(uint64_t a, uint64_t b) + void process_header() { - 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); + handler_->handle_header(); } - static inline unsigned find_closing_tag_runtime(const char* s, unsigned p) + void process_message() { - return - s[p] == 0 - ? throw std::runtime_error("unmatched tag <") : - s[p] == '>' - ? p : find_closing_tag_runtime(s, p + 1); + handler_->handle(); } - - static inline uint64_t get_parameter_tag_runtime(const char* s, unsigned p = 0) + + request to_request() const { - return - s[p] == 0 - ? 0 : - s[p] == '<' ? ( - std::strncmp(s+p, "<int>", 5) == 0 - ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 1 : - std::strncmp(s+p, "<uint>", 6) == 0 - ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 2 : - (std::strncmp(s+p, "<float>", 7) == 0 || - std::strncmp(s+p, "<double>", 8) == 0) - ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 3 : - (std::strncmp(s+p, "<str>", 5) == 0 || - std::strncmp(s+p, "<string>", 8) == 0) - ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 4 : - std::strncmp(s+p, "<path>", 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); + return request{(HTTPMethod)method, std::move(raw_url), std::move(url), std::move(url_params), std::move(headers), std::move(body)}; } -#ifndef CROW_MSVC_WORKAROUND - constexpr uint64_t get_parameter_tag(const_str s, unsigned p = 0) + + bool is_upgrade() const + { + return upgrade; + } + + bool check_version(int major, int minor) const { - 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); + return http_major == major && http_minor == minor; } -#endif - template <typename ... T> - struct S + std::string raw_url; + std::string url; + + int header_building_state = 0; + std::string header_field; + std::string header_value; + ci_map headers; + query_string url_params; + std::string body; + + Handler* handler_; + }; +} + + + +#pragma once +#include <string> +#include <unordered_map> + + + + + + + + +namespace crow +{ + template <typename Adaptor, typename Handler, typename ... Middlewares> + class Connection; + struct response + { + template <typename Adaptor, typename Handler, typename ... Middlewares> + friend class crow::Connection; + + int code{200}; + std::string body; + json::wvalue json_value; + + // `headers' stores HTTP headers. + ci_map headers; + + void set_header(std::string key, std::string value) { - template <typename U> - using push = S<U, T...>; - template <typename U> - using push_back = S<T..., U>; - template <template<typename ... Args> class U> - using rebind = U<T...>; - }; -template <typename F, typename Set> - struct CallHelper; - template <typename F, typename ...Args> - struct CallHelper<F, S<Args...>> + headers.erase(key); + headers.emplace(std::move(key), std::move(value)); + } + void add_header(std::string key, std::string value) { - template <typename F1, typename ...Args1, typename = - decltype(std::declval<F1>()(std::declval<Args1>()...)) - > - static char __test(int); + headers.emplace(std::move(key), std::move(value)); + } - template <typename ...> - static int __test(...); + const std::string& get_header_value(const std::string& key) + { + return crow::get_header_value(headers, key); + } - static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char); - }; + response() {} + explicit response(int code) : code(code) {} + response(std::string body) : body(std::move(body)) {} + response(json::wvalue&& json_value) : json_value(std::move(json_value)) + { + json_mode(); + } + response(int code, std::string body) : code(code), body(std::move(body)) {} + response(const json::wvalue& json_value) : body(json::dump(json_value)) + { + json_mode(); + } + response(int code, const json::wvalue& json_value) : code(code), body(json::dump(json_value)) + { + json_mode(); + } - template <int N> - struct single_tag_to_type + response(response&& r) { - }; + *this = std::move(r); + } - template <> - struct single_tag_to_type<1> + response& operator = (const response& r) = delete; + + response& operator = (response&& r) noexcept { - using type = int64_t; - }; + body = std::move(r.body); + json_value = std::move(r.json_value); + code = r.code; + headers = std::move(r.headers); + completed_ = r.completed_; + return *this; + } - template <> - struct single_tag_to_type<2> + bool is_completed() const noexcept { - using type = uint64_t; - }; + return completed_; + } - template <> - struct single_tag_to_type<3> + void clear() { - using type = double; - }; + body.clear(); + json_value.clear(); + code = 200; + headers.clear(); + completed_ = false; + } - template <> - struct single_tag_to_type<4> + void write(const std::string& body_part) { - using type = std::string; - }; + body += body_part; + } - template <> - struct single_tag_to_type<5> + void end() { - using type = std::string; - }; + if (!completed_) + { + completed_ = true; + if (complete_request_handler_) + { + complete_request_handler_(); + } + } + } - template <uint64_t Tag> - struct arguments + void end(const std::string& body_part) { - using subarguments = typename arguments<Tag/6>::type; - using type = - typename subarguments::template push<typename single_tag_to_type<Tag%6>::type>; - }; + body += body_part; + end(); + } - template <> - struct arguments<0> + bool is_alive() { - using type = S<>; + return is_alive_helper_ && is_alive_helper_(); + } + + private: + bool completed_{}; + std::function<void()> complete_request_handler_; + std::function<bool()> is_alive_helper_; + + //In case of a JSON object, set the Content-Type header + void json_mode() + { + set_header("Content-Type", "application/json"); + } + }; +} + + + +#pragma once + + + + + + + + +namespace crow +{ + namespace detail + { + template <typename ... Middlewares> + struct partial_context + : public black_magic::pop_back<Middlewares...>::template rebind<partial_context> + , public black_magic::last_element_type<Middlewares...>::type::context + { + using parent_context = typename black_magic::pop_back<Middlewares...>::template rebind<::crow::detail::partial_context>; + template <int N> + using partial = typename std::conditional<N == sizeof...(Middlewares)-1, partial_context, typename parent_context::template partial<N>>::type; + + template <typename T> + typename T::context& get() + { + return static_cast<typename T::context&>(*this); + } }; - template <typename ... T> - struct last_element_type + template <> + struct partial_context<> { - using type = typename std::tuple_element<sizeof...(T)-1, std::tuple<T...>>::type; + template <int> + using partial = partial_context; }; + template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares> + bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx); - template <> - struct last_element_type<> + template <typename ... Middlewares> + struct context : private partial_context<Middlewares...> + //struct context : private Middlewares::context... // simple but less type-safe { + template <int N, typename Context, typename Container> + friend typename std::enable_if<(N==0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res); + template <int N, typename Context, typename Container> + friend typename std::enable_if<(N>0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res); + + template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares2> + friend bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx); + + template <typename T> + typename T::context& get() + { + return static_cast<typename T::context&>(*this); + } + + template <int N> + using partial = typename partial_context<Middlewares...>::template partial<N>; }; + } +} - // from http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth - template<class T> using Invoke = typename T::type; - template<unsigned...> struct seq{ using type = seq; }; +#pragma once +#include <boost/asio.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/array.hpp> +#include <atomic> +#include <chrono> +#include <vector> - template<class S1, class S2> struct concat; - template<unsigned... I1, unsigned... I2> - struct concat<seq<I1...>, seq<I2...>> - : seq<I1..., (sizeof...(I1)+I2)...>{}; - template<class S1, class S2> - using Concat = Invoke<concat<S1, S2>>; - template<unsigned N> struct gen_seq; - template<unsigned N> using GenSeq = Invoke<gen_seq<N>>; - template<unsigned N> - struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{}; - template<> struct gen_seq<0> : seq<>{}; - template<> struct gen_seq<1> : seq<0>{}; - template <typename Seq, typename Tuple> - struct pop_back_helper; - template <unsigned ... N, typename Tuple> - struct pop_back_helper<seq<N...>, Tuple> + + + + + + + + + + + +namespace crow +{ + using namespace boost; + using tcp = asio::ip::tcp; + + namespace detail + { + template <typename MW> + struct check_before_handle_arity_3_const { - template <template <typename ... Args> class U> - using rebind = U<typename std::tuple_element<N, Tuple>::type...>; + template <typename T, + void (T::*)(request&, response&, typename MW::context&) const = &T::before_handle + > + struct get + { }; }; - template <typename ... T> - struct pop_back //: public pop_back_helper<typename gen_seq<sizeof...(T)-1>::type, std::tuple<T...>> + template <typename MW> + struct check_before_handle_arity_3 { - template <template <typename ... Args> class U> - using rebind = typename pop_back_helper<typename gen_seq<sizeof...(T)-1>::type, std::tuple<T...>>::template rebind<U>; + template <typename T, + void (T::*)(request&, response&, typename MW::context&) = &T::before_handle + > + struct get + { }; }; - template <> - struct pop_back<> + template <typename MW> + struct check_after_handle_arity_3_const { - template <template <typename ... Args> class U> - using rebind = U<>; + template <typename T, + void (T::*)(request&, response&, typename MW::context&) const = &T::after_handle + > + struct get + { }; }; - // from http://stackoverflow.com/questions/2118541/check-if-c0x-parameter-pack-contains-a-type - template < typename Tp, typename... List > - struct contains : std::true_type {}; - - template < typename Tp, typename Head, typename... Rest > - struct contains<Tp, Head, Rest...> - : std::conditional< std::is_same<Tp, Head>::value, - std::true_type, - contains<Tp, Rest...> - >::type {}; - - template < typename Tp > - struct contains<Tp> : std::false_type {}; + template <typename MW> + struct check_after_handle_arity_3 + { + template <typename T, + void (T::*)(request&, response&, typename MW::context&) = &T::after_handle + > + struct get + { }; + }; template <typename T> - struct empty_context + struct is_before_handle_arity_3_impl { + template <typename C> + static std::true_type f(typename check_before_handle_arity_3_const<T>::template get<C>*); + + template <typename C> + static std::true_type f(typename check_before_handle_arity_3<T>::template get<C>*); + + template <typename C> + static std::false_type f(...); + + public: + static const bool value = decltype(f<T>(nullptr))::value; }; template <typename T> - struct promote + struct is_after_handle_arity_3_impl { - using type = T; - }; + template <typename C> + static std::true_type f(typename check_after_handle_arity_3_const<T>::template get<C>*); -#define CROW_INTERNAL_PROMOTE_TYPE(t1, t2) \ - template<> \ - struct promote<t1> \ - { \ - using type = t2; \ - } + template <typename C> + static std::true_type f(typename check_after_handle_arity_3<T>::template get<C>*); - CROW_INTERNAL_PROMOTE_TYPE(char, int64_t); - CROW_INTERNAL_PROMOTE_TYPE(short, int64_t); - CROW_INTERNAL_PROMOTE_TYPE(int, int64_t); - CROW_INTERNAL_PROMOTE_TYPE(long, int64_t); - CROW_INTERNAL_PROMOTE_TYPE(long long, int64_t); - CROW_INTERNAL_PROMOTE_TYPE(unsigned char, uint64_t); - CROW_INTERNAL_PROMOTE_TYPE(unsigned short, uint64_t); - CROW_INTERNAL_PROMOTE_TYPE(unsigned int, uint64_t); - CROW_INTERNAL_PROMOTE_TYPE(unsigned long, uint64_t); - CROW_INTERNAL_PROMOTE_TYPE(unsigned long long, uint64_t); - CROW_INTERNAL_PROMOTE_TYPE(float, double); -#undef CROW_INTERNAL_PROMOTE_TYPE + template <typename C> + static std::false_type f(...); - template <typename T> - using promote_t = typename promote<T>::type; + public: + static const bool value = decltype(f<T>(nullptr))::value; + }; - } // namespace black_magic + template <typename MW, typename Context, typename ParentContext> + typename std::enable_if<!is_before_handle_arity_3_impl<MW>::value>::type + before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) + { + mw.before_handle(req, res, ctx.template get<MW>(), ctx); + } - namespace detail - { + template <typename MW, typename Context, typename ParentContext> + typename std::enable_if<is_before_handle_arity_3_impl<MW>::value>::type + before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) + { + mw.before_handle(req, res, ctx.template get<MW>()); + } - template <class T, std::size_t N, class... Args> - struct get_index_of_element_from_tuple_by_type_impl + template <typename MW, typename Context, typename ParentContext> + typename std::enable_if<!is_after_handle_arity_3_impl<MW>::value>::type + after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) { - static constexpr auto value = N; - }; + mw.after_handle(req, res, ctx.template get<MW>(), ctx); + } - template <class T, std::size_t N, class... Args> - struct get_index_of_element_from_tuple_by_type_impl<T, N, T, Args...> + template <typename MW, typename Context, typename ParentContext> + typename std::enable_if<is_after_handle_arity_3_impl<MW>::value>::type + after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) { - static constexpr auto value = N; - }; + mw.after_handle(req, res, ctx.template get<MW>()); + } - template <class T, std::size_t N, class U, class... Args> - struct get_index_of_element_from_tuple_by_type_impl<T, N, U, Args...> + template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares> + bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx) { - static constexpr auto value = get_index_of_element_from_tuple_by_type_impl<T, N + 1, Args...>::value; - }; + using parent_context_t = typename Context::template partial<N-1>; + before_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - } // namespace detail + if (res.is_completed()) + { + after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); + return true; + } - namespace utility - { - template <class T, class... Args> - T& get_element_by_type(std::tuple<Args...>& t) + if (middleware_call_helper<N+1, Context, Container, Middlewares...>(middlewares, req, res, ctx)) + { + after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); + return true; + } + + return false; + } + + template <int N, typename Context, typename Container> + bool middleware_call_helper(Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/) { - return std::get<detail::get_index_of_element_from_tuple_by_type_impl<T, 0, Args...>::value>(t); + return false; } - template<typename T> - struct function_traits; + template <int N, typename Context, typename Container> + typename std::enable_if<(N<0)>::type + after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/) + { + } -#ifndef CROW_MSVC_WORKAROUND - template<typename T> - struct function_traits : public function_traits<decltype(&T::operator())> + template <int N, typename Context, typename Container> + typename std::enable_if<(N==0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res) { - using parent_t = function_traits<decltype(&T::operator())>; - static const size_t arity = parent_t::arity; - using result_type = typename parent_t::result_type; - template <size_t i> - using arg = typename parent_t::template arg<i>; + using parent_context_t = typename Context::template partial<N-1>; + using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type; + after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); + } + + template <int N, typename Context, typename Container> + typename std::enable_if<(N>0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res) + { + using parent_context_t = typename Context::template partial<N-1>; + using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type; + after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); + after_handlers_call_helper<N-1, Context, Container>(middlewares, ctx, req, res); + } + } + +#ifdef CROW_ENABLE_DEBUG + static int connectionCount; +#endif + template <typename Adaptor, typename Handler, typename ... Middlewares> + class Connection + { + public: + Connection( + boost::asio::io_service& io_service, + Handler* handler, + const std::string& server_name, + std::tuple<Middlewares...>* middlewares, + std::function<std::string()>& get_cached_date_str_f, + detail::dumb_timer_queue& timer_queue, + typename Adaptor::context* adaptor_ctx_ + ) + : adaptor_(io_service, adaptor_ctx_), + handler_(handler), + parser_(this), + server_name_(server_name), + middlewares_(middlewares), + get_cached_date_str(get_cached_date_str_f), + timer_queue(timer_queue) + { +#ifdef CROW_ENABLE_DEBUG + connectionCount ++; + CROW_LOG_DEBUG << "Connection open, total " << connectionCount << ", " << this; +#endif + } - }; + ~Connection() + { + res.complete_request_handler_ = nullptr; + cancel_deadline_timer(); +#ifdef CROW_ENABLE_DEBUG + connectionCount --; + CROW_LOG_DEBUG << "Connection closed, total " << connectionCount << ", " << this; #endif + } - template<typename ClassType, typename R, typename ...Args> - struct function_traits<R(ClassType::*)(Args...) const> + decltype(std::declval<Adaptor>().raw_socket())& socket() { - static const size_t arity = sizeof...(Args); + return adaptor_.raw_socket(); + } - typedef R result_type; + void start() + { + adaptor_.start([this](const boost::system::error_code& ec) { + if (!ec) + { + start_deadline(); - template <size_t i> - using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; - }; + do_read(); + } + else + { + check_destroy(); + } + }); + } - template<typename ClassType, typename R, typename ...Args> - struct function_traits<R(ClassType::*)(Args...)> + void handle_header() { - static const size_t arity = sizeof...(Args); + // HTTP 1.1 Expect: 100-continue + if (parser_.check_version(1, 1) && parser_.headers.count("expect") && get_header_value(parser_.headers, "expect") == "100-continue") + { + buffers_.clear(); + static std::string expect_100_continue = "HTTP/1.1 100 Continue\r\n\r\n"; + buffers_.emplace_back(expect_100_continue.data(), expect_100_continue.size()); + do_write(); + } + } - typedef R result_type; + void handle() + { + cancel_deadline_timer(); + bool is_invalid_request = false; + add_keep_alive_ = false; - template <size_t i> - using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; - }; + req_ = std::move(parser_.to_request()); + request& req = req_; - template<typename R, typename ...Args> - struct function_traits<std::function<R(Args...)>> - { - static const size_t arity = sizeof...(Args); + if (parser_.check_version(1, 0)) + { + // HTTP/1.0 + if (req.headers.count("connection")) + { + if (boost::iequals(req.get_header_value("connection"),"Keep-Alive")) + add_keep_alive_ = true; + } + else + close_connection_ = true; + } + else if (parser_.check_version(1, 1)) + { + // HTTP/1.1 + if (req.headers.count("connection")) + { + if (req.get_header_value("connection") == "close") + close_connection_ = true; + else if (boost::iequals(req.get_header_value("connection"),"Keep-Alive")) + add_keep_alive_ = true; + } + if (!req.headers.count("host")) + { + is_invalid_request = true; + res = response(400); + } + if (parser_.is_upgrade()) + { + if (req.get_header_value("upgrade") == "h2c") + { + // TODO HTTP/2 + // currently, ignore upgrade header + } + else + { + close_connection_ = true; + handler_->handle_upgrade(req, res, std::move(adaptor_)); + return; + } + } + } - typedef R result_type; + CROW_LOG_INFO << "Request: " << boost::lexical_cast<std::string>(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << parser_.http_major << "." << parser_.http_minor << ' ' + << method_name(req.method) << " " << req.url; - template <size_t i> - using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; - }; - inline static std::string base64encode(const char* data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") - { - std::string ret; - ret.resize((size+2) / 3 * 4); - auto it = ret.begin(); - while(size >= 3) + need_to_call_after_handlers_ = false; + if (!is_invalid_request) { - *it++ = key[(((unsigned char)*data)&0xFC)>>2]; - unsigned char h = (((unsigned char)*data++) & 0x03) << 4; - *it++ = key[h|((((unsigned char)*data)&0xF0)>>4)]; - h = (((unsigned char)*data++) & 0x0F) << 2; - *it++ = key[h|((((unsigned char)*data)&0xC0)>>6)]; - *it++ = key[((unsigned char)*data++)&0x3F]; + res.complete_request_handler_ = []{}; + res.is_alive_helper_ = [this]()->bool{ return adaptor_.is_open(); }; - size -= 3; - } - if (size == 1) - { - *it++ = key[(((unsigned char)*data)&0xFC)>>2]; - unsigned char h = (((unsigned char)*data++) & 0x03) << 4; - *it++ = key[h]; - *it++ = '='; - *it++ = '='; + ctx_ = detail::context<Middlewares...>(); + req.middleware_context = (void*)&ctx_; + req.io_service = &adaptor_.get_io_service(); + detail::middleware_call_helper<0, decltype(ctx_), decltype(*middlewares_), Middlewares...>(*middlewares_, req, res, ctx_); + + if (!res.completed_) + { + res.complete_request_handler_ = [this]{ this->complete_request(); }; + need_to_call_after_handlers_ = true; + handler_->handle(req, res); + if (add_keep_alive_) + res.set_header("connection", "Keep-Alive"); + } + else + { + complete_request(); + } } - else if (size == 2) + else { - *it++ = key[(((unsigned char)*data)&0xFC)>>2]; - unsigned char h = (((unsigned char)*data++) & 0x03) << 4; - *it++ = key[h|((((unsigned char)*data)&0xF0)>>4)]; - h = (((unsigned char)*data++) & 0x0F) << 2; - *it++ = key[h]; - *it++ = '='; + complete_request(); } - return ret; } - inline static std::string base64encode_urlsafe(const char* data, size_t size) + void complete_request() { - return base64encode(data, size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"); - } + CROW_LOG_INFO << "Response: " << this << ' ' << req_.raw_url << ' ' << res.code << ' ' << close_connection_; + if (need_to_call_after_handlers_) + { + need_to_call_after_handlers_ = false; - } // namespace utility -} + // call all after_handler of middlewares + detail::after_handlers_call_helper< + ((int)sizeof...(Middlewares)-1), + decltype(ctx_), + decltype(*middlewares_)> + (*middlewares_, ctx_, req_, res); + } + //auto self = this->shared_from_this(); + res.complete_request_handler_ = nullptr; + + if (!adaptor_.is_open()) + { + //CROW_LOG_DEBUG << this << " delete (socket is closed) " << is_reading << ' ' << is_writing; + //delete this; + return; + } + static std::unordered_map<int, std::string> statusCodes = { + {200, "HTTP/1.1 200 OK\r\n"}, + {201, "HTTP/1.1 201 Created\r\n"}, + {202, "HTTP/1.1 202 Accepted\r\n"}, + {204, "HTTP/1.1 204 No Content\r\n"}, -#pragma once + {300, "HTTP/1.1 300 Multiple Choices\r\n"}, + {301, "HTTP/1.1 301 Moved Permanently\r\n"}, + {302, "HTTP/1.1 302 Moved Temporarily\r\n"}, + {304, "HTTP/1.1 304 Not Modified\r\n"}, -#include <vector> -#include <string> -#include <stdexcept> -#include <iostream> + {400, "HTTP/1.1 400 Bad Request\r\n"}, + {401, "HTTP/1.1 401 Unauthorized\r\n"}, + {403, "HTTP/1.1 403 Forbidden\r\n"}, + {404, "HTTP/1.1 404 Not Found\r\n"}, + {500, "HTTP/1.1 500 Internal Server Error\r\n"}, + {501, "HTTP/1.1 501 Not Implemented\r\n"}, + {502, "HTTP/1.1 502 Bad Gateway\r\n"}, + {503, "HTTP/1.1 503 Service Unavailable\r\n"}, + }; + static std::string seperator = ": "; + static std::string crlf = "\r\n"; -namespace crow -{ - enum class HTTPMethod - { -#ifndef DELETE - DELETE = 0, - GET, - HEAD, - POST, - PUT, - CONNECT, - OPTIONS, - TRACE, -#endif + buffers_.clear(); + buffers_.reserve(4*(res.headers.size()+5)+3); - Delete = 0, - Get, - Head, - Post, - Put, - Connect, - Options, - Trace, - }; + if (res.body.empty() && res.json_value.t() == json::type::Object) + { + res.body = json::dump(res.json_value); + } - inline std::string method_name(HTTPMethod method) - { - switch(method) + if (!statusCodes.count(res.code)) + res.code = 500; + { + auto& status = statusCodes.find(res.code)->second; + buffers_.emplace_back(status.data(), status.size()); + } + + if (res.code >= 400 && res.body.empty()) + res.body = statusCodes[res.code].substr(9); + + for(auto& kv : res.headers) + { + buffers_.emplace_back(kv.first.data(), kv.first.size()); + buffers_.emplace_back(seperator.data(), seperator.size()); + buffers_.emplace_back(kv.second.data(), kv.second.size()); + buffers_.emplace_back(crlf.data(), crlf.size()); + + } + + if (!res.headers.count("content-length")) + { + content_length_ = std::to_string(res.body.size()); + static std::string content_length_tag = "Content-Length: "; + buffers_.emplace_back(content_length_tag.data(), content_length_tag.size()); + buffers_.emplace_back(content_length_.data(), content_length_.size()); + buffers_.emplace_back(crlf.data(), crlf.size()); + } + if (!res.headers.count("server")) + { + static std::string server_tag = "Server: "; + buffers_.emplace_back(server_tag.data(), server_tag.size()); + buffers_.emplace_back(server_name_.data(), server_name_.size()); + buffers_.emplace_back(crlf.data(), crlf.size()); + } + if (!res.headers.count("date")) + { + static std::string date_tag = "Date: "; + date_str_ = get_cached_date_str(); + buffers_.emplace_back(date_tag.data(), date_tag.size()); + buffers_.emplace_back(date_str_.data(), date_str_.size()); + buffers_.emplace_back(crlf.data(), crlf.size()); + } + if (add_keep_alive_) + { + static std::string keep_alive_tag = "Connection: Keep-Alive"; + buffers_.emplace_back(keep_alive_tag.data(), keep_alive_tag.size()); + buffers_.emplace_back(crlf.data(), crlf.size()); + } + + buffers_.emplace_back(crlf.data(), crlf.size()); + res_body_copy_.swap(res.body); + buffers_.emplace_back(res_body_copy_.data(), res_body_copy_.size()); + + do_write(); + + if (need_to_start_read_after_complete_) + { + need_to_start_read_after_complete_ = false; + start_deadline(); + do_read(); + } + } + + private: + void do_read() { - case HTTPMethod::Delete: - return "DELETE"; - case HTTPMethod::Get: - return "GET"; - case HTTPMethod::Head: - return "HEAD"; - case HTTPMethod::Post: - return "POST"; - case HTTPMethod::Put: - return "PUT"; - case HTTPMethod::Connect: - return "CONNECT"; - case HTTPMethod::Options: - return "OPTIONS"; - case HTTPMethod::Trace: - return "TRACE"; + //auto self = this->shared_from_this(); + is_reading = true; + adaptor_.socket().async_read_some(boost::asio::buffer(buffer_), + [this](const boost::system::error_code& ec, std::size_t bytes_transferred) + { + bool error_while_reading = true; + if (!ec) + { + bool ret = parser_.feed(buffer_.data(), bytes_transferred); + if (ret && adaptor_.is_open()) + { + error_while_reading = false; + } + } + + if (error_while_reading) + { + cancel_deadline_timer(); + parser_.done(); + adaptor_.close(); + is_reading = false; + CROW_LOG_DEBUG << this << " from read(1)"; + check_destroy(); + } + else if (close_connection_) + { + cancel_deadline_timer(); + parser_.done(); + is_reading = false; + check_destroy(); + // adaptor will close after write + } + else if (!need_to_call_after_handlers_) + { + start_deadline(); + do_read(); + } + else + { + // res will be completed later by user + need_to_start_read_after_complete_ = true; + } + }); } - return "invalid"; - } - enum class ParamType - { - INT, - UINT, - DOUBLE, - STRING, - PATH, + void do_write() + { + //auto self = this->shared_from_this(); + is_writing = true; + boost::asio::async_write(adaptor_.socket(), buffers_, + [&](const boost::system::error_code& ec, std::size_t /*bytes_transferred*/) + { + is_writing = false; + res.clear(); + res_body_copy_.clear(); + if (!ec) + { + if (close_connection_) + { + adaptor_.close(); + CROW_LOG_DEBUG << this << " from write(1)"; + check_destroy(); + } + } + else + { + CROW_LOG_DEBUG << this << " from write(2)"; + check_destroy(); + } + }); + } - MAX - }; + void check_destroy() + { + CROW_LOG_DEBUG << this << " is_reading " << is_reading << " is_writing " << is_writing; + if (!is_reading && !is_writing) + { + CROW_LOG_DEBUG << this << " delete (idle) "; + delete this; + } + } - struct routing_params - { - std::vector<int64_t> int_params; - std::vector<uint64_t> uint_params; - std::vector<double> double_params; - std::vector<std::string> string_params; + void cancel_deadline_timer() + { + CROW_LOG_DEBUG << this << " timer cancelled: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second; + timer_queue.cancel(timer_cancel_key_); + } - void debug_print() const + void start_deadline(/*int timeout = 5*/) { - std::cerr << "routing_params" << std::endl; - for(auto i:int_params) - std::cerr<<i <<", " ; - std::cerr<<std::endl; - for(auto i:uint_params) - std::cerr<<i <<", " ; - std::cerr<<std::endl; - for(auto i:double_params) - std::cerr<<i <<", " ; - std::cerr<<std::endl; - for(auto& i:string_params) - std::cerr<<i <<", " ; - std::cerr<<std::endl; + cancel_deadline_timer(); + + timer_cancel_key_ = timer_queue.add([this] + { + if (!adaptor_.is_open()) + { + return; + } + adaptor_.close(); + }); + CROW_LOG_DEBUG << this << " timer added: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second; } - template <typename T> - T get(unsigned) const; + private: + Adaptor adaptor_; + Handler* handler_; - }; + boost::array<char, 4096> buffer_; - template<> - inline int64_t routing_params::get<int64_t>(unsigned index) const - { - return int_params[index]; - } + HTTPParser<Connection> parser_; + request req_; + response res; - template<> - inline uint64_t routing_params::get<uint64_t>(unsigned index) const - { - return uint_params[index]; - } + bool close_connection_ = false; - template<> - inline double routing_params::get<double>(unsigned index) const - { - return double_params[index]; - } + const std::string& server_name_; + std::vector<boost::asio::const_buffer> buffers_; - template<> - inline std::string routing_params::get<std::string>(unsigned index) const - { - return string_params[index]; - } -} + std::string content_length_; + std::string date_str_; + std::string res_body_copy_; + + //boost::asio::deadline_timer deadline_; + detail::dumb_timer_queue::key timer_cancel_key_; + + bool is_reading{}; + bool is_writing{}; + bool need_to_call_after_handlers_{}; + bool need_to_start_read_after_complete_{}; + bool add_keep_alive_{}; + + std::tuple<Middlewares...>* middlewares_; + detail::context<Middlewares...> ctx_; + + std::function<std::string()>& get_cached_date_str; + detail::dumb_timer_queue& timer_queue; + }; -#ifndef CROW_MSVC_WORKAROUND -constexpr crow::HTTPMethod operator "" _method(const char* str, size_t /*len*/) -{ - return - crow::black_magic::is_equ_p(str, "GET", 3) ? crow::HTTPMethod::Get : - crow::black_magic::is_equ_p(str, "DELETE", 6) ? crow::HTTPMethod::Delete : - crow::black_magic::is_equ_p(str, "HEAD", 4) ? crow::HTTPMethod::Head : - crow::black_magic::is_equ_p(str, "POST", 4) ? crow::HTTPMethod::Post : - crow::black_magic::is_equ_p(str, "PUT", 3) ? crow::HTTPMethod::Put : - crow::black_magic::is_equ_p(str, "OPTIONS", 7) ? crow::HTTPMethod::Options : - crow::black_magic::is_equ_p(str, "CONNECT", 7) ? crow::HTTPMethod::Connect : - crow::black_magic::is_equ_p(str, "TRACE", 5) ? crow::HTTPMethod::Trace : - throw std::runtime_error("invalid http method"); } -#endif #pragma once +#include <chrono> +#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/asio.hpp> +#ifdef CROW_ENABLE_SSL +#include <boost/asio/ssl.hpp> +#endif +#include <cstdint> +#include <atomic> +#include <future> +#include <vector> + +#include <memory> @@ -6335,63 +7407,210 @@ constexpr crow::HTTPMethod operator "" _method(const char* str, size_t /*len*/) namespace crow { - template <typename T> - inline const std::string& get_header_value(const T& headers, const std::string& key) + using namespace boost; + using tcp = asio::ip::tcp; + + template <typename Handler, typename Adaptor = SocketAdaptor, typename ... Middlewares> + class Server { - if (headers.count(key)) + public: + Server(Handler* handler, std::string bindaddr, uint16_t port, std::tuple<Middlewares...>* middlewares = nullptr, uint16_t concurrency = 1, typename Adaptor::context* adaptor_ctx = nullptr) + : acceptor_(io_service_, tcp::endpoint(boost::asio::ip::address::from_string(bindaddr), port)), + signals_(io_service_, SIGINT, SIGTERM), + tick_timer_(io_service_), + handler_(handler), + concurrency_(concurrency), + port_(port), + bindaddr_(bindaddr), + middlewares_(middlewares), + adaptor_ctx_(adaptor_ctx) { - return headers.find(key)->second; } - static std::string empty; - return empty; - } - - struct DetachHelper; - - struct request - { - HTTPMethod method; - std::string raw_url; - std::string url; - query_string url_params; - ci_map headers; - std::string body; - void* middleware_context{}; - boost::asio::io_service* io_service{}; - - request() - : method(HTTPMethod::Get) + void set_tick_function(std::chrono::milliseconds d, std::function<void()> f) { + tick_interval_ = d; + tick_function_ = f; } - request(HTTPMethod method, std::string raw_url, std::string url, query_string url_params, ci_map headers, std::string body) - : method(method), raw_url(std::move(raw_url)), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body)) + void on_tick() { + tick_function_(); + tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count())); + tick_timer_.async_wait([this](const boost::system::error_code& ec) + { + if (ec) + return; + on_tick(); + }); } - void add_header(std::string key, std::string value) + void run() { - headers.emplace(std::move(key), std::move(value)); + if (concurrency_ < 0) + concurrency_ = 1; + + for(int i = 0; i < concurrency_; i++) + io_service_pool_.emplace_back(new boost::asio::io_service()); + get_cached_date_str_pool_.resize(concurrency_); + timer_queue_pool_.resize(concurrency_); + + std::vector<std::future<void>> v; + std::atomic<int> init_count(0); + for(uint16_t i = 0; i < concurrency_; i ++) + v.push_back( + std::async(std::launch::async, [this, i, &init_count]{ + + // thread local date string get function + auto last = std::chrono::steady_clock::now(); + + std::string date_str; + auto update_date_str = [&] + { + auto last_time_t = time(0); + tm my_tm; + +#ifdef _MSC_VER + gmtime_s(&my_tm, &last_time_t); +#else + gmtime_r(&last_time_t, &my_tm); +#endif + date_str.resize(100); + size_t date_str_sz = strftime(&date_str[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &my_tm); + date_str.resize(date_str_sz); + }; + update_date_str(); + get_cached_date_str_pool_[i] = [&]()->std::string + { + if (std::chrono::steady_clock::now() - last >= std::chrono::seconds(1)) + { + last = std::chrono::steady_clock::now(); + update_date_str(); + } + return date_str; + }; + + // initializing timer queue + detail::dumb_timer_queue timer_queue; + timer_queue_pool_[i] = &timer_queue; + + timer_queue.set_io_service(*io_service_pool_[i]); + boost::asio::deadline_timer timer(*io_service_pool_[i]); + timer.expires_from_now(boost::posix_time::seconds(1)); + + std::function<void(const boost::system::error_code& ec)> handler; + handler = [&](const boost::system::error_code& ec){ + if (ec) + return; + timer_queue.process(); + timer.expires_from_now(boost::posix_time::seconds(1)); + timer.async_wait(handler); + }; + timer.async_wait(handler); + + init_count ++; + try + { + io_service_pool_[i]->run(); + } catch(std::exception& e) + { + CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what(); + } + })); + + if (tick_function_ && tick_interval_.count() > 0) + { + tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count())); + tick_timer_.async_wait([this](const boost::system::error_code& ec) + { + if (ec) + return; + on_tick(); + }); + } + + CROW_LOG_INFO << server_name_ << " server is running, local port " << port_; + + signals_.async_wait( + [&](const boost::system::error_code& /*error*/, int /*signal_number*/){ + stop(); + }); + + while(concurrency_ != init_count) + std::this_thread::yield(); + + do_accept(); + + std::thread([this]{ + io_service_.run(); + CROW_LOG_INFO << "Exiting."; + }).join(); } - const std::string& get_header_value(const std::string& key) const + void stop() { - return crow::get_header_value(headers, key); + io_service_.stop(); + for(auto& io_service:io_service_pool_) + io_service->stop(); } - template<typename CompletionHandler> - void post(CompletionHandler handler) + private: + asio::io_service& pick_io_service() { - io_service->post(handler); + // TODO load balancing + roundrobin_index_++; + if (roundrobin_index_ >= io_service_pool_.size()) + roundrobin_index_ = 0; + return *io_service_pool_[roundrobin_index_]; } - template<typename CompletionHandler> - void dispatch(CompletionHandler handler) + void do_accept() { - io_service->dispatch(handler); + asio::io_service& is = pick_io_service(); + auto p = new Connection<Adaptor, Handler, Middlewares...>( + is, handler_, server_name_, middlewares_, + get_cached_date_str_pool_[roundrobin_index_], *timer_queue_pool_[roundrobin_index_], + adaptor_ctx_); + acceptor_.async_accept(p->socket(), + [this, p, &is](boost::system::error_code ec) + { + if (!ec) + { + is.post([p] + { + p->start(); + }); + } + do_accept(); + }); } + private: + asio::io_service io_service_; + std::vector<std::unique_ptr<asio::io_service>> io_service_pool_; + std::vector<detail::dumb_timer_queue*> timer_queue_pool_; + std::vector<std::function<std::string()>> get_cached_date_str_pool_; + tcp::acceptor acceptor_; + boost::asio::signal_set signals_; + boost::asio::deadline_timer tick_timer_; + + Handler* handler_; + uint16_t concurrency_{1}; + std::string server_name_ = "Crow/0.1"; + uint16_t port_; + std::string bindaddr_; + unsigned int roundrobin_index_{}; + + std::chrono::milliseconds tick_interval_; + std::function<void()> tick_function_; + + std::tuple<Middlewares...>* middlewares_; + +#ifdef CROW_ENABLE_SSL + bool use_ssl_{false}; + boost::asio::ssl::context ssl_context_{boost::asio::ssl::context::sslv23}; +#endif + typename Adaptor::context* adaptor_ctx_; }; } @@ -6614,10 +7833,14 @@ namespace crow case WebSocketReadState::Len16: { remaining_length_ = 0; - boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length_, 2), - [this](const boost::system::error_code& ec, std::size_t bytes_transferred) + uint16_t remaining_length16_ = 0; + boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length16_, 2), + [this,&remaining_length16_](const boost::system::error_code& ec, std::size_t bytes_transferred) { is_reading = false; + remaining_length16_ = ntohs(remaining_length16_); + remaining_length_ = remaining_length16_; + remaining_length_ = ntohs(*(uint16_t*)&remaining_length_); #ifdef CROW_ENABLE_DEBUG if (!ec && bytes_transferred != 2) @@ -6893,314 +8116,6 @@ namespace crow #pragma once - -#include <string> -#include <unordered_map> -#include <boost/algorithm/string.hpp> -#include <boost/tokenizer.hpp> -#include <algorithm> - - - - - - -namespace crow -{ - template <typename Handler> - struct HTTPParser : public http_parser - { - static int on_message_begin(http_parser* self_) - { - HTTPParser* self = static_cast<HTTPParser*>(self_); - self->clear(); - return 0; - } - static int on_url(http_parser* self_, const char* at, size_t length) - { - HTTPParser* self = static_cast<HTTPParser*>(self_); - self->raw_url.insert(self->raw_url.end(), at, at+length); - return 0; - } - static int on_header_field(http_parser* self_, const char* at, size_t length) - { - HTTPParser* self = static_cast<HTTPParser*>(self_); - switch (self->header_building_state) - { - case 0: - if (!self->header_value.empty()) - { - self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); - } - self->header_field.assign(at, at+length); - self->header_building_state = 1; - break; - case 1: - self->header_field.insert(self->header_field.end(), at, at+length); - break; - } - return 0; - } - static int on_header_value(http_parser* self_, const char* at, size_t length) - { - HTTPParser* self = static_cast<HTTPParser*>(self_); - switch (self->header_building_state) - { - case 0: - self->header_value.insert(self->header_value.end(), at, at+length); - break; - case 1: - self->header_building_state = 0; - self->header_value.assign(at, at+length); - break; - } - return 0; - } - static int on_headers_complete(http_parser* self_) - { - HTTPParser* self = static_cast<HTTPParser*>(self_); - if (!self->header_field.empty()) - { - self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); - } - self->process_header(); - return 0; - } - static int on_body(http_parser* self_, const char* at, size_t length) - { - HTTPParser* self = static_cast<HTTPParser*>(self_); - self->body.insert(self->body.end(), at, at+length); - return 0; - } - static int on_message_complete(http_parser* self_) - { - HTTPParser* self = static_cast<HTTPParser*>(self_); - - // url params - self->url = self->raw_url.substr(0, self->raw_url.find("?")); - self->url_params = query_string(self->raw_url); - - self->process_message(); - return 0; - } - HTTPParser(Handler* handler) : - handler_(handler) - { - http_parser_init(this, HTTP_REQUEST); - } - - // return false on error - bool feed(const char* buffer, int length) - { - const static http_parser_settings settings_{ - on_message_begin, - on_url, - nullptr, - on_header_field, - on_header_value, - on_headers_complete, - on_body, - on_message_complete, - }; - - int nparsed = http_parser_execute(this, &settings_, buffer, length); - return nparsed == length; - } - - bool done() - { - return feed(nullptr, 0); - } - - void clear() - { - url.clear(); - raw_url.clear(); - header_building_state = 0; - header_field.clear(); - header_value.clear(); - headers.clear(); - url_params.clear(); - body.clear(); - } - - void process_header() - { - handler_->handle_header(); - } - - void process_message() - { - handler_->handle(); - } - - request to_request() const - { - return request{(HTTPMethod)method, std::move(raw_url), std::move(url), std::move(url_params), std::move(headers), std::move(body)}; - } - - bool is_upgrade() const - { - return upgrade; - } - - bool check_version(int major, int minor) const - { - return http_major == major && http_minor == minor; - } - - std::string raw_url; - std::string url; - - int header_building_state = 0; - std::string header_field; - std::string header_value; - ci_map headers; - query_string url_params; - std::string body; - - Handler* handler_; - }; -} - - - -#pragma once -#include <string> -#include <unordered_map> - - - - - - - - -namespace crow -{ - template <typename Adaptor, typename Handler, typename ... Middlewares> - class Connection; - struct response - { - template <typename Adaptor, typename Handler, typename ... Middlewares> - friend class crow::Connection; - - int code{200}; - std::string body; - json::wvalue json_value; - - // `headers' stores HTTP headers. - ci_map headers; - - void set_header(std::string key, std::string value) - { - headers.erase(key); - headers.emplace(std::move(key), std::move(value)); - } - void add_header(std::string key, std::string value) - { - headers.emplace(std::move(key), std::move(value)); - } - - const std::string& get_header_value(const std::string& key) - { - return crow::get_header_value(headers, key); - } - - - response() {} - explicit response(int code) : code(code) {} - response(std::string body) : body(std::move(body)) {} - response(json::wvalue&& json_value) : json_value(std::move(json_value)) - { - json_mode(); - } - response(int code, std::string body) : code(code), body(std::move(body)) {} - response(const json::wvalue& json_value) : body(json::dump(json_value)) - { - json_mode(); - } - response(int code, const json::wvalue& json_value) : code(code), body(json::dump(json_value)) - { - json_mode(); - } - - response(response&& r) - { - *this = std::move(r); - } - - response& operator = (const response& r) = delete; - - response& operator = (response&& r) noexcept - { - body = std::move(r.body); - json_value = std::move(r.json_value); - code = r.code; - headers = std::move(r.headers); - completed_ = r.completed_; - return *this; - } - - bool is_completed() const noexcept - { - return completed_; - } - - void clear() - { - body.clear(); - json_value.clear(); - code = 200; - headers.clear(); - completed_ = false; - } - - void write(const std::string& body_part) - { - body += body_part; - } - - void end() - { - if (!completed_) - { - completed_ = true; - - if (complete_request_handler_) - { - complete_request_handler_(); - } - } - } - - void end(const std::string& body_part) - { - body += body_part; - end(); - } - - bool is_alive() - { - return is_alive_helper_ && is_alive_helper_(); - } - - private: - bool completed_{}; - std::function<void()> complete_request_handler_; - std::function<bool()> is_alive_helper_; - - //In case of a JSON object, set the Content-Type header - void json_mode() - { - set_header("Content-Type", "application/json"); - } - }; -} - - - -#pragma once #include <boost/algorithm/string/trim.hpp> @@ -8446,914 +9361,6 @@ public: #pragma once - - - - - - - -namespace crow -{ - namespace detail - { - template <typename ... Middlewares> - struct partial_context - : public black_magic::pop_back<Middlewares...>::template rebind<partial_context> - , public black_magic::last_element_type<Middlewares...>::type::context - { - using parent_context = typename black_magic::pop_back<Middlewares...>::template rebind<::crow::detail::partial_context>; - template <int N> - using partial = typename std::conditional<N == sizeof...(Middlewares)-1, partial_context, typename parent_context::template partial<N>>::type; - - template <typename T> - typename T::context& get() - { - return static_cast<typename T::context&>(*this); - } - }; - - template <> - struct partial_context<> - { - template <int> - using partial = partial_context; - }; - - template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares> - bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx); - - template <typename ... Middlewares> - struct context : private partial_context<Middlewares...> - //struct context : private Middlewares::context... // simple but less type-safe - { - template <int N, typename Context, typename Container> - friend typename std::enable_if<(N==0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res); - template <int N, typename Context, typename Container> - friend typename std::enable_if<(N>0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res); - - template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares2> - friend bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx); - - template <typename T> - typename T::context& get() - { - return static_cast<typename T::context&>(*this); - } - - template <int N> - using partial = typename partial_context<Middlewares...>::template partial<N>; - }; - } -} - - - -#pragma once -#include <boost/asio.hpp> -#include <boost/algorithm/string/predicate.hpp> -#include <boost/lexical_cast.hpp> -#include <boost/array.hpp> -#include <atomic> -#include <chrono> -#include <vector> - - - - - - - - - - - - - - - - - - - -namespace crow -{ - using namespace boost; - using tcp = asio::ip::tcp; - - namespace detail - { - template <typename MW> - struct check_before_handle_arity_3_const - { - template <typename T, - void (T::*)(request&, response&, typename MW::context&) const = &T::before_handle - > - struct get - { }; - }; - - template <typename MW> - struct check_before_handle_arity_3 - { - template <typename T, - void (T::*)(request&, response&, typename MW::context&) = &T::before_handle - > - struct get - { }; - }; - - template <typename MW> - struct check_after_handle_arity_3_const - { - template <typename T, - void (T::*)(request&, response&, typename MW::context&) const = &T::after_handle - > - struct get - { }; - }; - - template <typename MW> - struct check_after_handle_arity_3 - { - template <typename T, - void (T::*)(request&, response&, typename MW::context&) = &T::after_handle - > - struct get - { }; - }; - - template <typename T> - struct is_before_handle_arity_3_impl - { - template <typename C> - static std::true_type f(typename check_before_handle_arity_3_const<T>::template get<C>*); - - template <typename C> - static std::true_type f(typename check_before_handle_arity_3<T>::template get<C>*); - - template <typename C> - static std::false_type f(...); - - public: - static const bool value = decltype(f<T>(nullptr))::value; - }; - - template <typename T> - struct is_after_handle_arity_3_impl - { - template <typename C> - static std::true_type f(typename check_after_handle_arity_3_const<T>::template get<C>*); - - template <typename C> - static std::true_type f(typename check_after_handle_arity_3<T>::template get<C>*); - - template <typename C> - static std::false_type f(...); - - public: - static const bool value = decltype(f<T>(nullptr))::value; - }; - - template <typename MW, typename Context, typename ParentContext> - typename std::enable_if<!is_before_handle_arity_3_impl<MW>::value>::type - before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.before_handle(req, res, ctx.template get<MW>(), ctx); - } - - template <typename MW, typename Context, typename ParentContext> - typename std::enable_if<is_before_handle_arity_3_impl<MW>::value>::type - before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.before_handle(req, res, ctx.template get<MW>()); - } - - template <typename MW, typename Context, typename ParentContext> - typename std::enable_if<!is_after_handle_arity_3_impl<MW>::value>::type - after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.after_handle(req, res, ctx.template get<MW>(), ctx); - } - - template <typename MW, typename Context, typename ParentContext> - typename std::enable_if<is_after_handle_arity_3_impl<MW>::value>::type - after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.after_handle(req, res, ctx.template get<MW>()); - } - - template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares> - bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx) - { - using parent_context_t = typename Context::template partial<N-1>; - before_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - - if (res.is_completed()) - { - after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - return true; - } - - if (middleware_call_helper<N+1, Context, Container, Middlewares...>(middlewares, req, res, ctx)) - { - after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - return true; - } - - return false; - } - - template <int N, typename Context, typename Container> - bool middleware_call_helper(Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/) - { - return false; - } - - template <int N, typename Context, typename Container> - typename std::enable_if<(N<0)>::type - after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/) - { - } - - template <int N, typename Context, typename Container> - typename std::enable_if<(N==0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res) - { - using parent_context_t = typename Context::template partial<N-1>; - using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type; - after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - } - - template <int N, typename Context, typename Container> - typename std::enable_if<(N>0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res) - { - using parent_context_t = typename Context::template partial<N-1>; - using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type; - after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - after_handlers_call_helper<N-1, Context, Container>(middlewares, ctx, req, res); - } - } - -#ifdef CROW_ENABLE_DEBUG - static int connectionCount; -#endif - template <typename Adaptor, typename Handler, typename ... Middlewares> - class Connection - { - public: - Connection( - boost::asio::io_service& io_service, - Handler* handler, - const std::string& server_name, - std::tuple<Middlewares...>* middlewares, - std::function<std::string()>& get_cached_date_str_f, - detail::dumb_timer_queue& timer_queue, - typename Adaptor::context* adaptor_ctx_ - ) - : adaptor_(io_service, adaptor_ctx_), - handler_(handler), - parser_(this), - server_name_(server_name), - middlewares_(middlewares), - get_cached_date_str(get_cached_date_str_f), - timer_queue(timer_queue) - { -#ifdef CROW_ENABLE_DEBUG - connectionCount ++; - CROW_LOG_DEBUG << "Connection open, total " << connectionCount << ", " << this; -#endif - } - - ~Connection() - { - res.complete_request_handler_ = nullptr; - cancel_deadline_timer(); -#ifdef CROW_ENABLE_DEBUG - connectionCount --; - CROW_LOG_DEBUG << "Connection closed, total " << connectionCount << ", " << this; -#endif - } - - decltype(std::declval<Adaptor>().raw_socket())& socket() - { - return adaptor_.raw_socket(); - } - - void start() - { - adaptor_.start([this](const boost::system::error_code& ec) { - if (!ec) - { - start_deadline(); - - do_read(); - } - else - { - check_destroy(); - } - }); - } - - void handle_header() - { - // HTTP 1.1 Expect: 100-continue - if (parser_.check_version(1, 1) && parser_.headers.count("expect") && get_header_value(parser_.headers, "expect") == "100-continue") - { - buffers_.clear(); - static std::string expect_100_continue = "HTTP/1.1 100 Continue\r\n\r\n"; - buffers_.emplace_back(expect_100_continue.data(), expect_100_continue.size()); - do_write(); - } - } - - void handle() - { - cancel_deadline_timer(); - bool is_invalid_request = false; - add_keep_alive_ = false; - - req_ = std::move(parser_.to_request()); - request& req = req_; - - if (parser_.check_version(1, 0)) - { - // HTTP/1.0 - if (req.headers.count("connection")) - { - if (boost::iequals(req.get_header_value("connection"),"Keep-Alive")) - add_keep_alive_ = true; - } - else - close_connection_ = true; - } - else if (parser_.check_version(1, 1)) - { - // HTTP/1.1 - if (req.headers.count("connection")) - { - if (req.get_header_value("connection") == "close") - close_connection_ = true; - else if (boost::iequals(req.get_header_value("connection"),"Keep-Alive")) - add_keep_alive_ = true; - } - if (!req.headers.count("host")) - { - is_invalid_request = true; - res = response(400); - } - if (parser_.is_upgrade()) - { - if (req.get_header_value("upgrade") == "h2c") - { - // TODO HTTP/2 - // currently, ignore upgrade header - } - else - { - close_connection_ = true; - handler_->handle_upgrade(req, res, std::move(adaptor_)); - return; - } - } - } - - CROW_LOG_INFO << "Request: " << boost::lexical_cast<std::string>(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << parser_.http_major << "." << parser_.http_minor << ' ' - << method_name(req.method) << " " << req.url; - - - need_to_call_after_handlers_ = false; - if (!is_invalid_request) - { - res.complete_request_handler_ = []{}; - res.is_alive_helper_ = [this]()->bool{ return adaptor_.is_open(); }; - - ctx_ = detail::context<Middlewares...>(); - req.middleware_context = (void*)&ctx_; - req.io_service = &adaptor_.get_io_service(); - detail::middleware_call_helper<0, decltype(ctx_), decltype(*middlewares_), Middlewares...>(*middlewares_, req, res, ctx_); - - if (!res.completed_) - { - res.complete_request_handler_ = [this]{ this->complete_request(); }; - need_to_call_after_handlers_ = true; - handler_->handle(req, res); - if (add_keep_alive_) - res.set_header("connection", "Keep-Alive"); - } - else - { - complete_request(); - } - } - else - { - complete_request(); - } - } - - void complete_request() - { - CROW_LOG_INFO << "Response: " << this << ' ' << req_.raw_url << ' ' << res.code << ' ' << close_connection_; - - if (need_to_call_after_handlers_) - { - need_to_call_after_handlers_ = false; - - // call all after_handler of middlewares - detail::after_handlers_call_helper< - ((int)sizeof...(Middlewares)-1), - decltype(ctx_), - decltype(*middlewares_)> - (*middlewares_, ctx_, req_, res); - } - - //auto self = this->shared_from_this(); - res.complete_request_handler_ = nullptr; - - if (!adaptor_.is_open()) - { - //CROW_LOG_DEBUG << this << " delete (socket is closed) " << is_reading << ' ' << is_writing; - //delete this; - return; - } - - static std::unordered_map<int, std::string> statusCodes = { - {200, "HTTP/1.1 200 OK\r\n"}, - {201, "HTTP/1.1 201 Created\r\n"}, - {202, "HTTP/1.1 202 Accepted\r\n"}, - {204, "HTTP/1.1 204 No Content\r\n"}, - - {300, "HTTP/1.1 300 Multiple Choices\r\n"}, - {301, "HTTP/1.1 301 Moved Permanently\r\n"}, - {302, "HTTP/1.1 302 Moved Temporarily\r\n"}, - {304, "HTTP/1.1 304 Not Modified\r\n"}, - - {400, "HTTP/1.1 400 Bad Request\r\n"}, - {401, "HTTP/1.1 401 Unauthorized\r\n"}, - {403, "HTTP/1.1 403 Forbidden\r\n"}, - {404, "HTTP/1.1 404 Not Found\r\n"}, - - {500, "HTTP/1.1 500 Internal Server Error\r\n"}, - {501, "HTTP/1.1 501 Not Implemented\r\n"}, - {502, "HTTP/1.1 502 Bad Gateway\r\n"}, - {503, "HTTP/1.1 503 Service Unavailable\r\n"}, - }; - - static std::string seperator = ": "; - static std::string crlf = "\r\n"; - - buffers_.clear(); - buffers_.reserve(4*(res.headers.size()+5)+3); - - if (res.body.empty() && res.json_value.t() == json::type::Object) - { - res.body = json::dump(res.json_value); - } - - if (!statusCodes.count(res.code)) - res.code = 500; - { - auto& status = statusCodes.find(res.code)->second; - buffers_.emplace_back(status.data(), status.size()); - } - - if (res.code >= 400 && res.body.empty()) - res.body = statusCodes[res.code].substr(9); - - for(auto& kv : res.headers) - { - buffers_.emplace_back(kv.first.data(), kv.first.size()); - buffers_.emplace_back(seperator.data(), seperator.size()); - buffers_.emplace_back(kv.second.data(), kv.second.size()); - buffers_.emplace_back(crlf.data(), crlf.size()); - - } - - if (!res.headers.count("content-length")) - { - content_length_ = std::to_string(res.body.size()); - static std::string content_length_tag = "Content-Length: "; - buffers_.emplace_back(content_length_tag.data(), content_length_tag.size()); - buffers_.emplace_back(content_length_.data(), content_length_.size()); - buffers_.emplace_back(crlf.data(), crlf.size()); - } - if (!res.headers.count("server")) - { - static std::string server_tag = "Server: "; - buffers_.emplace_back(server_tag.data(), server_tag.size()); - buffers_.emplace_back(server_name_.data(), server_name_.size()); - buffers_.emplace_back(crlf.data(), crlf.size()); - } - if (!res.headers.count("date")) - { - static std::string date_tag = "Date: "; - date_str_ = get_cached_date_str(); - buffers_.emplace_back(date_tag.data(), date_tag.size()); - buffers_.emplace_back(date_str_.data(), date_str_.size()); - buffers_.emplace_back(crlf.data(), crlf.size()); - } - if (add_keep_alive_) - { - static std::string keep_alive_tag = "Connection: Keep-Alive"; - buffers_.emplace_back(keep_alive_tag.data(), keep_alive_tag.size()); - buffers_.emplace_back(crlf.data(), crlf.size()); - } - - buffers_.emplace_back(crlf.data(), crlf.size()); - res_body_copy_.swap(res.body); - buffers_.emplace_back(res_body_copy_.data(), res_body_copy_.size()); - - do_write(); - - if (need_to_start_read_after_complete_) - { - need_to_start_read_after_complete_ = false; - start_deadline(); - do_read(); - } - } - - private: - void do_read() - { - //auto self = this->shared_from_this(); - is_reading = true; - adaptor_.socket().async_read_some(boost::asio::buffer(buffer_), - [this](const boost::system::error_code& ec, std::size_t bytes_transferred) - { - bool error_while_reading = true; - if (!ec) - { - bool ret = parser_.feed(buffer_.data(), bytes_transferred); - if (ret && adaptor_.is_open()) - { - error_while_reading = false; - } - } - - if (error_while_reading) - { - cancel_deadline_timer(); - parser_.done(); - adaptor_.close(); - is_reading = false; - CROW_LOG_DEBUG << this << " from read(1)"; - check_destroy(); - } - else if (close_connection_) - { - cancel_deadline_timer(); - parser_.done(); - is_reading = false; - check_destroy(); - // adaptor will close after write - } - else if (!need_to_call_after_handlers_) - { - start_deadline(); - do_read(); - } - else - { - // res will be completed later by user - need_to_start_read_after_complete_ = true; - } - }); - } - - void do_write() - { - //auto self = this->shared_from_this(); - is_writing = true; - boost::asio::async_write(adaptor_.socket(), buffers_, - [&](const boost::system::error_code& ec, std::size_t /*bytes_transferred*/) - { - is_writing = false; - res.clear(); - res_body_copy_.clear(); - if (!ec) - { - if (close_connection_) - { - adaptor_.close(); - CROW_LOG_DEBUG << this << " from write(1)"; - check_destroy(); - } - } - else - { - CROW_LOG_DEBUG << this << " from write(2)"; - check_destroy(); - } - }); - } - - void check_destroy() - { - CROW_LOG_DEBUG << this << " is_reading " << is_reading << " is_writing " << is_writing; - if (!is_reading && !is_writing) - { - CROW_LOG_DEBUG << this << " delete (idle) "; - delete this; - } - } - - void cancel_deadline_timer() - { - CROW_LOG_DEBUG << this << " timer cancelled: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second; - timer_queue.cancel(timer_cancel_key_); - } - - void start_deadline(/*int timeout = 5*/) - { - cancel_deadline_timer(); - - timer_cancel_key_ = timer_queue.add([this] - { - if (!adaptor_.is_open()) - { - return; - } - adaptor_.close(); - }); - CROW_LOG_DEBUG << this << " timer added: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second; - } - - private: - Adaptor adaptor_; - Handler* handler_; - - boost::array<char, 4096> buffer_; - - HTTPParser<Connection> parser_; - request req_; - response res; - - bool close_connection_ = false; - - const std::string& server_name_; - std::vector<boost::asio::const_buffer> buffers_; - - std::string content_length_; - std::string date_str_; - std::string res_body_copy_; - - //boost::asio::deadline_timer deadline_; - detail::dumb_timer_queue::key timer_cancel_key_; - - bool is_reading{}; - bool is_writing{}; - bool need_to_call_after_handlers_{}; - bool need_to_start_read_after_complete_{}; - bool add_keep_alive_{}; - - std::tuple<Middlewares...>* middlewares_; - detail::context<Middlewares...> ctx_; - - std::function<std::string()>& get_cached_date_str; - detail::dumb_timer_queue& timer_queue; - }; - -} - - - -#pragma once - -#include <chrono> -#include <boost/date_time/posix_time/posix_time.hpp> -#include <boost/asio.hpp> -#ifdef CROW_ENABLE_SSL -#include <boost/asio/ssl.hpp> -#endif -#include <cstdint> -#include <atomic> -#include <future> -#include <vector> - -#include <memory> - - - - - - - - -namespace crow -{ - using namespace boost; - using tcp = asio::ip::tcp; - - template <typename Handler, typename Adaptor = SocketAdaptor, typename ... Middlewares> - class Server - { - public: - Server(Handler* handler, std::string bindaddr, uint16_t port, std::tuple<Middlewares...>* middlewares = nullptr, uint16_t concurrency = 1, typename Adaptor::context* adaptor_ctx = nullptr) - : acceptor_(io_service_, tcp::endpoint(boost::asio::ip::address::from_string(bindaddr), port)), - signals_(io_service_, SIGINT, SIGTERM), - tick_timer_(io_service_), - handler_(handler), - concurrency_(concurrency), - port_(port), - bindaddr_(bindaddr), - middlewares_(middlewares), - adaptor_ctx_(adaptor_ctx) - { - } - - void set_tick_function(std::chrono::milliseconds d, std::function<void()> f) - { - tick_interval_ = d; - tick_function_ = f; - } - - void on_tick() - { - tick_function_(); - tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count())); - tick_timer_.async_wait([this](const boost::system::error_code& ec) - { - if (ec) - return; - on_tick(); - }); - } - - void run() - { - if (concurrency_ < 0) - concurrency_ = 1; - - for(int i = 0; i < concurrency_; i++) - io_service_pool_.emplace_back(new boost::asio::io_service()); - get_cached_date_str_pool_.resize(concurrency_); - timer_queue_pool_.resize(concurrency_); - - std::vector<std::future<void>> v; - std::atomic<int> init_count(0); - for(uint16_t i = 0; i < concurrency_; i ++) - v.push_back( - std::async(std::launch::async, [this, i, &init_count]{ - - // thread local date string get function - auto last = std::chrono::steady_clock::now(); - - std::string date_str; - auto update_date_str = [&] - { - auto last_time_t = time(0); - tm my_tm; - -#ifdef _MSC_VER - gmtime_s(&my_tm, &last_time_t); -#else - gmtime_r(&last_time_t, &my_tm); -#endif - date_str.resize(100); - size_t date_str_sz = strftime(&date_str[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &my_tm); - date_str.resize(date_str_sz); - }; - update_date_str(); - get_cached_date_str_pool_[i] = [&]()->std::string - { - if (std::chrono::steady_clock::now() - last >= std::chrono::seconds(1)) - { - last = std::chrono::steady_clock::now(); - update_date_str(); - } - return date_str; - }; - - // initializing timer queue - detail::dumb_timer_queue timer_queue; - timer_queue_pool_[i] = &timer_queue; - - timer_queue.set_io_service(*io_service_pool_[i]); - boost::asio::deadline_timer timer(*io_service_pool_[i]); - timer.expires_from_now(boost::posix_time::seconds(1)); - - std::function<void(const boost::system::error_code& ec)> handler; - handler = [&](const boost::system::error_code& ec){ - if (ec) - return; - timer_queue.process(); - timer.expires_from_now(boost::posix_time::seconds(1)); - timer.async_wait(handler); - }; - timer.async_wait(handler); - - init_count ++; - try - { - io_service_pool_[i]->run(); - } catch(std::exception& e) - { - CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what(); - } - })); - - if (tick_function_ && tick_interval_.count() > 0) - { - tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count())); - tick_timer_.async_wait([this](const boost::system::error_code& ec) - { - if (ec) - return; - on_tick(); - }); - } - - CROW_LOG_INFO << server_name_ << " server is running, local port " << port_; - - signals_.async_wait( - [&](const boost::system::error_code& /*error*/, int /*signal_number*/){ - stop(); - }); - - while(concurrency_ != init_count) - std::this_thread::yield(); - - do_accept(); - - std::thread([this]{ - io_service_.run(); - CROW_LOG_INFO << "Exiting."; - }).join(); - } - - void stop() - { - io_service_.stop(); - for(auto& io_service:io_service_pool_) - io_service->stop(); - } - - private: - asio::io_service& pick_io_service() - { - // TODO load balancing - roundrobin_index_++; - if (roundrobin_index_ >= io_service_pool_.size()) - roundrobin_index_ = 0; - return *io_service_pool_[roundrobin_index_]; - } - - void do_accept() - { - asio::io_service& is = pick_io_service(); - auto p = new Connection<Adaptor, Handler, Middlewares...>( - is, handler_, server_name_, middlewares_, - get_cached_date_str_pool_[roundrobin_index_], *timer_queue_pool_[roundrobin_index_], - adaptor_ctx_); - acceptor_.async_accept(p->socket(), - [this, p, &is](boost::system::error_code ec) - { - if (!ec) - { - is.post([p] - { - p->start(); - }); - } - do_accept(); - }); - } - - private: - asio::io_service io_service_; - std::vector<std::unique_ptr<asio::io_service>> io_service_pool_; - std::vector<detail::dumb_timer_queue*> timer_queue_pool_; - std::vector<std::function<std::string()>> get_cached_date_str_pool_; - tcp::acceptor acceptor_; - boost::asio::signal_set signals_; - boost::asio::deadline_timer tick_timer_; - - Handler* handler_; - uint16_t concurrency_{1}; - std::string server_name_ = "Crow/0.1"; - uint16_t port_; - std::string bindaddr_; - unsigned int roundrobin_index_{}; - - std::chrono::milliseconds tick_interval_; - std::function<void()> tick_function_; - - std::tuple<Middlewares...>* middlewares_; - -#ifdef CROW_ENABLE_SSL - bool use_ssl_{false}; - boost::asio::ssl::context ssl_context_{boost::asio::ssl::context::sslv23}; -#endif - typename Adaptor::context* adaptor_ctx_; - }; -} - - - -#pragma once - #include <chrono> #include <string> #include <functional> |