2014-04-02 16:38:08 +00:00
# pragma once
# include <cstdint>
2014-04-02 20:31:32 +00:00
# include <utility>
# include <tuple>
2014-04-10 16:43:33 +00:00
# include <unordered_map>
2014-04-13 02:24:06 +00:00
# include <memory>
# include <boost/lexical_cast.hpp>
2014-10-23 17:45:34 +00:00
# include <vector>
2014-04-02 16:38:08 +00:00
2014-04-14 15:31:51 +00:00
# include "common.h"
2014-04-09 23:17:08 +00:00
# include "http_response.h"
2014-04-14 18:02:48 +00:00
# include "http_request.h"
# include "utility.h"
2014-07-08 09:28:38 +00:00
# include "logging.h"
2014-04-02 16:38:08 +00:00
2014-04-26 17:19:59 +00:00
namespace crow
2014-04-02 16:38:08 +00:00
{
2014-04-13 02:24:06 +00:00
class BaseRule
2014-04-02 16:38:08 +00:00
{
2014-04-09 23:17:08 +00:00
public :
2015-02-18 15:57:01 +00:00
BaseRule ( std : : string rule )
: rule_ ( std : : move ( rule ) )
{
}
2014-04-13 02:24:06 +00:00
virtual ~ BaseRule ( )
{
}
2014-04-15 13:46:28 +00:00
virtual void validate ( ) = 0 ;
2014-04-14 15:31:51 +00:00
2014-08-05 18:54:38 +00:00
virtual void handle ( const request & , response & , const routing_params & ) = 0 ;
2014-04-13 02:24:06 +00:00
2015-02-20 02:58:41 +00:00
uint32_t get_methods ( )
2014-10-07 12:51:24 +00:00
{
return methods_ ;
}
2014-04-20 08:45:10 +00:00
2014-10-07 12:51:24 +00:00
protected :
uint32_t methods_ { 1 < < ( int ) HTTPMethod : : GET } ;
2015-02-18 15:57:01 +00:00
std : : string rule_ ;
std : : string name_ ;
friend class Router ;
2015-02-20 02:58:41 +00:00
template < typename T >
friend struct RuleParameterTraits ;
2014-04-13 02:24:06 +00:00
} ;
2014-04-20 08:45:10 +00:00
2015-02-18 15:57:01 +00:00
namespace detail
{
namespace routing_handler_call_helper
2014-04-14 15:31:51 +00:00
{
2015-02-18 15:57:01 +00:00
template < typename T , int Pos >
struct call_pair
{
using type = T ;
static const int pos = Pos ;
} ;
2014-04-14 15:31:51 +00:00
2015-02-18 15:57:01 +00:00
template < typename H1 >
struct call_params
2014-04-14 15:31:51 +00:00
{
2015-02-18 15:57:01 +00:00
H1 & handler ;
const routing_params & params ;
const request & req ;
response & res ;
} ;
2014-04-14 15:31:51 +00:00
2015-02-18 15:57:01 +00:00
template < typename F , int NInt , int NUint , int NDouble , int NString , typename S1 , typename S2 >
struct call
2014-04-15 13:46:28 +00:00
{
2015-02-18 15:57:01 +00:00
} ;
2014-04-15 13:46:28 +00:00
2015-02-18 15:57:01 +00:00
template < typename F , int NInt , int NUint , int NDouble , int NString , typename . . . Args1 , typename . . . Args2 >
struct call < F , NInt , NUint , NDouble , NString , black_magic : : S < int64_t , Args1 . . . > , black_magic : : S < Args2 . . . > >
2014-04-15 13:46:28 +00:00
{
2015-02-18 15:57:01 +00:00
void operator ( ) ( F cparams )
{
using pushed = typename black_magic : : S < Args2 . . . > : : template push_back < call_pair < int64_t , NInt > > ;
call < F , NInt + 1 , NUint , NDouble , NString ,
black_magic : : S < Args1 . . . > , pushed > ( ) ( cparams ) ;
}
} ;
2014-04-15 13:46:28 +00:00
2015-02-18 15:57:01 +00:00
template < typename F , int NInt , int NUint , int NDouble , int NString , typename . . . Args1 , typename . . . Args2 >
struct call < F , NInt , NUint , NDouble , NString , black_magic : : S < uint64_t , Args1 . . . > , black_magic : : S < Args2 . . . > >
2014-04-15 13:46:28 +00:00
{
2015-02-18 15:57:01 +00:00
void operator ( ) ( F cparams )
{
using pushed = typename black_magic : : S < Args2 . . . > : : template push_back < call_pair < uint64_t , NUint > > ;
call < F , NInt , NUint + 1 , NDouble , NString ,
black_magic : : S < Args1 . . . > , pushed > ( ) ( cparams ) ;
}
} ;
2014-04-15 13:46:28 +00:00
2015-02-18 15:57:01 +00:00
template < typename F , int NInt , int NUint , int NDouble , int NString , typename . . . Args1 , typename . . . Args2 >
struct call < F , NInt , NUint , NDouble , NString , black_magic : : S < double , Args1 . . . > , black_magic : : S < Args2 . . . > >
2014-04-14 15:31:51 +00:00
{
2015-02-18 15:57:01 +00:00
void operator ( ) ( F cparams )
2014-08-05 18:54:38 +00:00
{
2015-02-18 15:57:01 +00:00
using pushed = typename black_magic : : S < Args2 . . . > : : template push_back < call_pair < double , NDouble > > ;
call < F , NInt , NUint , NDouble + 1 , NString ,
black_magic : : S < Args1 . . . > , pushed > ( ) ( cparams ) ;
2014-08-05 18:54:38 +00:00
}
2015-02-18 15:57:01 +00:00
} ;
template < typename F , int NInt , int NUint , int NDouble , int NString , typename . . . Args1 , typename . . . Args2 >
struct call < F , NInt , NUint , NDouble , NString , black_magic : : S < std : : string , Args1 . . . > , black_magic : : S < Args2 . . . > >
{
void operator ( ) ( F cparams )
2014-08-05 18:54:38 +00:00
{
2015-02-18 15:57:01 +00:00
using pushed = typename black_magic : : S < Args2 . . . > : : template push_back < call_pair < std : : string , NString > > ;
call < F , NInt , NUint , NDouble , NString + 1 ,
black_magic : : S < Args1 . . . > , pushed > ( ) ( cparams ) ;
2014-08-05 18:54:38 +00:00
}
2015-02-18 15:57:01 +00:00
} ;
template < typename F , int NInt , int NUint , int NDouble , int NString , typename . . . Args1 >
struct call < F , NInt , NUint , NDouble , NString , black_magic : : S < > , black_magic : : S < Args1 . . . > >
{
void operator ( ) ( F cparams )
2014-08-05 13:38:51 +00:00
{
2015-02-18 15:57:01 +00:00
cparams . handler (
2014-08-05 13:38:51 +00:00
cparams . req ,
2014-08-05 18:54:38 +00:00
cparams . res ,
2014-08-05 13:38:51 +00:00
cparams . params . template get < typename Args1 : : type > ( Args1 : : pos ) . . .
) ;
}
2015-02-18 15:57:01 +00:00
} ;
template < typename Func , typename . . . ArgsWrapped >
struct Wrapped
{
template < typename . . . Args >
void set ( Func f , typename std : : enable_if <
! std : : is_same < typename std : : tuple_element < 0 , std : : tuple < Args . . . , void > > : : type , const request & > : : value
, int > : : type = 0 )
{
handler_ = (
2015-02-20 02:58:41 +00:00
# ifdef CROW_CAN_USE_CPP14
2015-02-18 15:57:01 +00:00
[ f = std : : move ( f ) ]
2015-02-20 02:58:41 +00:00
# else
[ f ]
# endif
2015-02-18 15:57:01 +00:00
( const request & , response & res , Args . . . args ) {
res = response ( f ( args . . . ) ) ;
res . end ( ) ;
} ) ;
}
template < typename Req , typename . . . Args >
struct req_handler_wrapper
{
req_handler_wrapper ( Func f )
: f ( std : : move ( f ) )
{
}
void operator ( ) ( const request & req , response & res , Args . . . args )
{
res = response ( f ( req , args . . . ) ) ;
res . end ( ) ;
}
Func f ;
} ;
template < typename . . . Args >
void set ( Func f , typename std : : enable_if <
std : : is_same < typename std : : tuple_element < 0 , std : : tuple < Args . . . , void > > : : type , const request & > : : value & &
! std : : is_same < typename std : : tuple_element < 1 , std : : tuple < Args . . . , void , void > > : : type , response & > : : value
, int > : : type = 0 )
{
handler_ = req_handler_wrapper < Args . . . > ( std : : move ( f ) ) ;
/*handler_ = (
[ f = std : : move ( f ) ]
( const request & req , response & res , Args . . . args ) {
res = response ( f ( req , args . . . ) ) ;
res . end ( ) ;
} ) ; */
}
template < typename . . . Args >
void set ( Func f , typename std : : enable_if <
std : : is_same < typename std : : tuple_element < 0 , std : : tuple < Args . . . , void > > : : type , const request & > : : value & &
std : : is_same < typename std : : tuple_element < 1 , std : : tuple < Args . . . , void , void > > : : type , response & > : : value
, int > : : type = 0 )
{
handler_ = std : : move ( f ) ;
}
template < typename . . . Args >
struct handler_type_helper
{
using type = std : : function < void ( const crow : : request & , crow : : response & , Args . . . ) > ;
using args_type = black_magic : : S < typename black_magic : : promote_t < Args > . . . > ;
} ;
2014-08-16 02:55:26 +00:00
2015-02-18 15:57:01 +00:00
template < typename . . . Args >
struct handler_type_helper < const request & , Args . . . >
{
using type = std : : function < void ( const crow : : request & , crow : : response & , Args . . . ) > ;
using args_type = black_magic : : S < typename black_magic : : promote_t < Args > . . . > ;
} ;
template < typename . . . Args >
struct handler_type_helper < const request & , response & , Args . . . >
{
using type = std : : function < void ( const crow : : request & , crow : : response & , Args . . . ) > ;
using args_type = black_magic : : S < typename black_magic : : promote_t < Args > . . . > ;
} ;
typename handler_type_helper < ArgsWrapped . . . > : : type handler_ ;
void operator ( ) ( const request & req , response & res , const routing_params & params )
{
detail : : routing_handler_call_helper : : call <
detail : : routing_handler_call_helper : : call_params <
decltype ( handler_ ) > ,
0 , 0 , 0 , 0 ,
typename handler_type_helper < ArgsWrapped . . . > : : args_type ,
black_magic : : S < >
> ( ) (
detail : : routing_handler_call_helper : : call_params <
decltype ( handler_ ) >
{ handler_ , params , req , res }
) ;
}
} ;
}
}
2015-02-20 02:58:41 +00:00
template < typename T >
struct RuleParameterTraits
{
using self_t = T ;
self_t & name ( std : : string name ) noexcept
{
( ( self_t * ) this ) - > name_ = std : : move ( name ) ;
return ( self_t & ) * this ;
}
self_t & methods ( HTTPMethod method )
{
( ( self_t * ) this ) - > methods_ = 1 < < ( int ) method ;
return ( self_t & ) * this ;
}
template < typename . . . MethodArgs >
self_t & methods ( HTTPMethod method , MethodArgs . . . args_method )
{
methods ( args_method . . . ) ;
( ( self_t * ) this ) - > methods_ | = 1 < < ( int ) method ;
return ( self_t & ) * this ;
}
} ;
class DynamicRule : public BaseRule , public RuleParameterTraits < DynamicRule >
2015-02-18 15:57:01 +00:00
{
public :
2015-02-20 02:58:41 +00:00
2015-02-18 15:57:01 +00:00
DynamicRule ( std : : string rule )
: BaseRule ( std : : move ( rule ) )
{
}
void validate ( ) override
{
if ( ! erased_handler_ )
{
throw std : : runtime_error ( name_ + ( ! name_ . empty ( ) ? " : " : " " ) + " no handler for url " + rule_ ) ;
2014-04-14 15:31:51 +00:00
}
2015-02-18 15:57:01 +00:00
}
void handle ( const request & req , response & res , const routing_params & params ) override
{
erased_handler_ ( req , res , params ) ;
}
template < typename Func >
void operator ( ) ( Func f )
{
2015-02-20 02:58:41 +00:00
# ifdef CROW_MSVC_WORKAROUND
using function_t = utility : : function_traits < decltype ( & Func : : operator ( ) ) > ;
# else
2015-02-18 15:57:01 +00:00
using function_t = utility : : function_traits < Func > ;
2015-02-20 02:58:41 +00:00
# endif
2015-02-18 15:57:01 +00:00
erased_handler_ = wrap ( std : : move ( f ) , black_magic : : gen_seq < function_t : : arity > ( ) ) ;
}
// enable_if Arg1 == request && Arg2 == response
// enable_if Arg1 == request && Arg2 != resposne
// enable_if Arg1 != request
2015-03-15 08:51:55 +00:00
# ifdef CROW_MSVC_WORKAROUND
template < typename Func , size_t . . . Indices >
# else
2015-02-18 15:57:01 +00:00
template < typename Func , unsigned . . . Indices >
2015-03-15 08:51:55 +00:00
# endif
2015-02-18 15:57:01 +00:00
std : : function < void ( const request & , response & , const routing_params & ) >
wrap ( Func f , black_magic : : seq < Indices . . . > )
{
2015-02-20 02:58:41 +00:00
# ifdef CROW_MSVC_WORKAROUND
using function_t = utility : : function_traits < decltype ( & Func : : operator ( ) ) > ;
# else
2015-02-18 15:57:01 +00:00
using function_t = utility : : function_traits < Func > ;
2015-02-20 02:58:41 +00:00
# endif
2015-03-12 21:53:45 +00:00
if ( ! black_magic : : is_parameter_tag_compatible (
2015-02-19 03:16:28 +00:00
black_magic : : get_parameter_tag_runtime ( rule_ . c_str ( ) ) ,
2015-03-12 21:53:45 +00:00
black_magic : : compute_parameter_tag_from_args_list <
2015-02-19 03:16:28 +00:00
typename function_t : : template arg < Indices > . . . > : : value ) )
{
2015-03-12 21:53:45 +00:00
throw std : : runtime_error ( " route_dynamic: Handler type is mismatched with URL parameters: " + rule_ ) ;
2015-02-19 03:16:28 +00:00
}
2015-02-18 15:57:01 +00:00
auto ret = detail : : routing_handler_call_helper : : Wrapped < Func , typename function_t : : template arg < Indices > . . . > ( ) ;
ret . template set <
typename function_t : : template arg < Indices > . . .
> ( std : : move ( f ) ) ;
return ret ;
}
template < typename Func >
void operator ( ) ( std : : string name , Func & & f )
{
name_ = std : : move ( name ) ;
( * this ) . template operator ( ) < Func > ( std : : forward ( f ) ) ;
}
private :
std : : function < void ( const request & , response & , const routing_params & ) > erased_handler_ ;
} ;
template < typename . . . Args >
2015-02-20 02:58:41 +00:00
class TaggedRule : public BaseRule , public RuleParameterTraits < TaggedRule < Args . . . > >
2015-02-18 15:57:01 +00:00
{
2014-04-13 02:24:06 +00:00
public :
2014-04-26 17:19:59 +00:00
using self_t = TaggedRule < Args . . . > ;
2015-02-18 15:57:01 +00:00
2014-04-13 02:24:06 +00:00
TaggedRule ( std : : string rule )
2015-02-18 15:57:01 +00:00
: BaseRule ( std : : move ( rule ) )
2014-04-13 02:24:06 +00:00
{
}
2014-04-26 17:19:59 +00:00
2014-04-15 13:46:28 +00:00
void validate ( )
{
2015-02-18 15:57:01 +00:00
if ( ! handler_ )
2014-04-21 18:27:53 +00:00
{
throw std : : runtime_error ( name_ + ( ! name_ . empty ( ) ? " : " : " " ) + " no handler for url " + rule_ ) ;
}
2014-04-15 13:46:28 +00:00
}
2014-04-13 02:24:06 +00:00
template < typename Func >
2014-04-20 08:45:10 +00:00
typename std : : enable_if < black_magic : : CallHelper < Func , black_magic : : S < Args . . . > > : : value , void > : : type
operator ( ) ( Func & & f )
2014-04-02 20:31:32 +00:00
{
2014-04-20 08:45:10 +00:00
static_assert ( black_magic : : CallHelper < Func , black_magic : : S < Args . . . > > : : value | |
2014-08-05 13:38:51 +00:00
black_magic : : CallHelper < Func , black_magic : : S < crow : : request , Args . . . > > : : value ,
2015-03-12 21:53:45 +00:00
" Handler type is mismatched with URL parameters " ) ;
2014-04-15 17:57:18 +00:00
static_assert ( ! std : : is_same < void , decltype ( f ( std : : declval < Args > ( ) . . . ) ) > : : value ,
2014-04-26 17:19:59 +00:00
" Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue " ) ;
2014-04-20 08:45:10 +00:00
2015-02-18 15:57:01 +00:00
handler_ = [ f = std : : move ( f ) ] ( const request & , response & res , Args . . . args ) {
res = response ( f ( args . . . ) ) ;
res . end ( ) ;
2014-04-20 08:45:10 +00:00
} ;
2014-04-13 02:24:06 +00:00
}
template < typename Func >
2014-08-05 13:38:51 +00:00
typename std : : enable_if <
! black_magic : : CallHelper < Func , black_magic : : S < Args . . . > > : : value & &
black_magic : : CallHelper < Func , black_magic : : S < crow : : request , Args . . . > > : : value ,
void > : : type
2014-04-20 08:45:10 +00:00
operator ( ) ( Func & & f )
2014-04-13 02:24:06 +00:00
{
2014-04-20 08:45:10 +00:00
static_assert ( black_magic : : CallHelper < Func , black_magic : : S < Args . . . > > : : value | |
2014-08-05 13:38:51 +00:00
black_magic : : CallHelper < Func , black_magic : : S < crow : : request , Args . . . > > : : value ,
2015-03-12 21:53:45 +00:00
" Handler type is mismatched with URL parameters " ) ;
2014-04-26 17:19:59 +00:00
static_assert ( ! std : : is_same < void , decltype ( f ( std : : declval < crow : : request > ( ) , std : : declval < Args > ( ) . . . ) ) > : : value ,
" Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue " ) ;
2014-04-20 08:45:10 +00:00
2015-02-18 15:57:01 +00:00
handler_ = [ f = std : : move ( f ) ] ( const crow : : request & req , crow : : response & res , Args . . . args ) {
res = response ( f ( req , args . . . ) ) ;
res . end ( ) ;
2014-04-20 08:45:10 +00:00
} ;
2014-08-05 13:38:51 +00:00
}
template < typename Func >
typename std : : enable_if <
! black_magic : : CallHelper < Func , black_magic : : S < Args . . . > > : : value & &
! black_magic : : CallHelper < Func , black_magic : : S < crow : : request , Args . . . > > : : value ,
void > : : type
operator ( ) ( Func & & f )
{
static_assert ( black_magic : : CallHelper < Func , black_magic : : S < Args . . . > > : : value | |
black_magic : : CallHelper < Func , black_magic : : S < crow : : request , Args . . . > > : : value | |
black_magic : : CallHelper < Func , black_magic : : S < crow : : request , crow : : response & , Args . . . > > : : value
,
2015-03-12 21:53:45 +00:00
" Handler type is mismatched with URL parameters " ) ;
2014-08-05 13:38:51 +00:00
static_assert ( std : : is_same < void , decltype ( f ( std : : declval < crow : : request > ( ) , std : : declval < crow : : response & > ( ) , std : : declval < Args > ( ) . . . ) ) > : : value ,
" Handler function with response argument should have void return type " ) ;
2015-02-18 15:57:01 +00:00
handler_ = std : : move ( f ) ;
2014-04-20 08:45:10 +00:00
}
template < typename Func >
void operator ( ) ( std : : string name , Func & & f )
{
2014-04-13 02:24:06 +00:00
name_ = std : : move ( name ) ;
2014-07-08 10:14:21 +00:00
( * this ) . template operator ( ) < Func > ( std : : forward ( f ) ) ;
2014-04-09 23:17:08 +00:00
}
2014-04-02 20:31:32 +00:00
2014-08-05 18:54:38 +00:00
void handle ( const request & req , response & res , const routing_params & params ) override
{
2015-02-18 15:57:01 +00:00
detail : : routing_handler_call_helper : : call <
detail : : routing_handler_call_helper : : call_params <
decltype ( handler_ ) > ,
2014-08-05 18:54:38 +00:00
0 , 0 , 0 , 0 ,
black_magic : : S < Args . . . > ,
black_magic : : S < >
> ( ) (
2015-02-18 15:57:01 +00:00
detail : : routing_handler_call_helper : : call_params <
decltype ( handler_ ) >
{ handler_ , params , req , res }
2014-08-05 18:54:38 +00:00
) ;
2014-04-02 20:31:32 +00:00
}
2014-04-09 23:17:08 +00:00
private :
2015-02-18 15:57:01 +00:00
std : : function < void ( const crow : : request & , crow : : response & , Args . . . ) > handler_ ;
2014-04-26 17:19:59 +00:00
2014-04-09 23:17:08 +00:00
} ;
2014-04-02 16:38:08 +00:00
2015-01-19 10:03:06 +00:00
const int RULE_SPECIAL_REDIRECT_SLASH = 1 ;
2014-04-10 16:43:33 +00:00
class Trie
{
public :
struct Node
{
2014-04-14 15:31:51 +00:00
unsigned rule_index { } ;
std : : array < unsigned , ( int ) ParamType : : MAX > param_childrens { } ;
2014-04-15 17:57:18 +00:00
std : : unordered_map < std : : string , unsigned > children ;
2014-08-06 21:18:21 +00:00
bool IsSimpleNode ( ) const
{
return
! rule_index & &
std : : all_of (
std : : begin ( param_childrens ) ,
std : : end ( param_childrens ) ,
[ ] ( unsigned x ) { return ! x ; } ) ;
}
2014-04-10 16:43:33 +00:00
} ;
2014-08-06 21:18:21 +00:00
Trie ( ) : nodes_ ( 1 )
{
}
2014-04-15 13:46:28 +00:00
2014-08-06 21:18:21 +00:00
private :
void optimizeNode ( Node * node )
{
for ( auto x : node - > param_childrens )
{
if ( ! x )
continue ;
Node * child = & nodes_ [ x ] ;
optimizeNode ( child ) ;
}
if ( node - > children . empty ( ) )
return ;
bool mergeWithChild = true ;
for ( auto & kv : node - > children )
{
Node * child = & nodes_ [ kv . second ] ;
if ( ! child - > IsSimpleNode ( ) )
{
mergeWithChild = false ;
break ;
}
}
if ( mergeWithChild )
{
decltype ( node - > children ) merged ;
for ( auto & kv : node - > children )
{
Node * child = & nodes_ [ kv . second ] ;
for ( auto & child_kv : child - > children )
{
merged [ kv . first + child_kv . first ] = child_kv . second ;
}
}
node - > children = std : : move ( merged ) ;
optimizeNode ( node ) ;
}
else
{
for ( auto & kv : node - > children )
{
Node * child = & nodes_ [ kv . second ] ;
optimizeNode ( child ) ;
}
}
}
void optimize ( )
{
optimizeNode ( head ( ) ) ;
}
2014-04-15 13:46:28 +00:00
2014-08-06 21:18:21 +00:00
public :
void validate ( )
{
if ( ! head ( ) - > IsSimpleNode ( ) )
throw std : : runtime_error ( " Internal error: Trie header should be simple! " ) ;
optimize ( ) ;
}
2014-04-10 16:43:33 +00:00
2014-09-15 16:28:15 +00:00
std : : pair < unsigned , routing_params > find ( const std : : string & req_url , const Node * node = nullptr , unsigned pos = 0 , routing_params * params = nullptr ) const
2014-08-06 21:18:21 +00:00
{
routing_params empty ;
if ( params = = nullptr )
params = & empty ;
2014-04-10 16:43:33 +00:00
2014-08-06 21:18:21 +00:00
unsigned found { } ;
routing_params match_params ;
2014-04-10 16:43:33 +00:00
2014-08-06 21:18:21 +00:00
if ( node = = nullptr )
node = head ( ) ;
2014-09-15 16:28:15 +00:00
if ( pos = = req_url . size ( ) )
2014-08-06 21:18:21 +00:00
return { node - > rule_index , * params } ;
auto update_found = [ & found , & match_params ] ( std : : pair < unsigned , routing_params > & ret )
{
if ( ret . first & & ( ! found | | found > ret . first ) )
{
found = ret . first ;
match_params = std : : move ( ret . second ) ;
}
} ;
if ( node - > param_childrens [ ( int ) ParamType : : INT ] )
{
2014-09-15 16:28:15 +00:00
char c = req_url [ pos ] ;
2014-08-06 21:18:21 +00:00
if ( ( c > = ' 0 ' & & c < = ' 9 ' ) | | c = = ' + ' | | c = = ' - ' )
{
char * eptr ;
errno = 0 ;
2014-09-15 16:28:15 +00:00
long long int value = strtoll ( req_url . data ( ) + pos , & eptr , 10 ) ;
if ( errno ! = ERANGE & & eptr ! = req_url . data ( ) + pos )
2014-08-06 21:18:21 +00:00
{
params - > int_params . push_back ( value ) ;
2014-09-15 16:28:15 +00:00
auto ret = find ( req_url , & nodes_ [ node - > param_childrens [ ( int ) ParamType : : INT ] ] , eptr - req_url . data ( ) , params ) ;
2014-08-06 21:18:21 +00:00
update_found ( ret ) ;
params - > int_params . pop_back ( ) ;
}
}
}
if ( node - > param_childrens [ ( int ) ParamType : : UINT ] )
{
2014-09-15 16:28:15 +00:00
char c = req_url [ pos ] ;
2014-08-06 21:18:21 +00:00
if ( ( c > = ' 0 ' & & c < = ' 9 ' ) | | c = = ' + ' )
{
char * eptr ;
errno = 0 ;
2014-09-15 16:28:15 +00:00
unsigned long long int value = strtoull ( req_url . data ( ) + pos , & eptr , 10 ) ;
if ( errno ! = ERANGE & & eptr ! = req_url . data ( ) + pos )
2014-08-06 21:18:21 +00:00
{
params - > uint_params . push_back ( value ) ;
2014-09-15 16:28:15 +00:00
auto ret = find ( req_url , & nodes_ [ node - > param_childrens [ ( int ) ParamType : : UINT ] ] , eptr - req_url . data ( ) , params ) ;
2014-08-06 21:18:21 +00:00
update_found ( ret ) ;
params - > uint_params . pop_back ( ) ;
}
}
}
if ( node - > param_childrens [ ( int ) ParamType : : DOUBLE ] )
{
2014-09-15 16:28:15 +00:00
char c = req_url [ pos ] ;
2014-08-06 21:18:21 +00:00
if ( ( c > = ' 0 ' & & c < = ' 9 ' ) | | c = = ' + ' | | c = = ' - ' | | c = = ' . ' )
{
char * eptr ;
errno = 0 ;
2014-09-15 16:28:15 +00:00
double value = strtod ( req_url . data ( ) + pos , & eptr ) ;
if ( errno ! = ERANGE & & eptr ! = req_url . data ( ) + pos )
2014-08-06 21:18:21 +00:00
{
params - > double_params . push_back ( value ) ;
2014-09-15 16:28:15 +00:00
auto ret = find ( req_url , & nodes_ [ node - > param_childrens [ ( int ) ParamType : : DOUBLE ] ] , eptr - req_url . data ( ) , params ) ;
2014-08-06 21:18:21 +00:00
update_found ( ret ) ;
params - > double_params . pop_back ( ) ;
}
}
}
if ( node - > param_childrens [ ( int ) ParamType : : STRING ] )
{
size_t epos = pos ;
2014-09-15 16:28:15 +00:00
for ( ; epos < req_url . size ( ) ; epos + + )
2014-08-06 21:18:21 +00:00
{
2014-09-15 16:28:15 +00:00
if ( req_url [ epos ] = = ' / ' )
2014-08-06 21:18:21 +00:00
break ;
}
if ( epos ! = pos )
{
2014-09-15 16:28:15 +00:00
params - > string_params . push_back ( req_url . substr ( pos , epos - pos ) ) ;
auto ret = find ( req_url , & nodes_ [ node - > param_childrens [ ( int ) ParamType : : STRING ] ] , epos , params ) ;
2014-08-06 21:18:21 +00:00
update_found ( ret ) ;
params - > string_params . pop_back ( ) ;
}
}
if ( node - > param_childrens [ ( int ) ParamType : : PATH ] )
{
2014-09-15 16:28:15 +00:00
size_t epos = req_url . size ( ) ;
2014-08-06 21:18:21 +00:00
if ( epos ! = pos )
{
2014-09-15 16:28:15 +00:00
params - > string_params . push_back ( req_url . substr ( pos , epos - pos ) ) ;
auto ret = find ( req_url , & nodes_ [ node - > param_childrens [ ( int ) ParamType : : PATH ] ] , epos , params ) ;
2014-08-06 21:18:21 +00:00
update_found ( ret ) ;
params - > string_params . pop_back ( ) ;
}
}
2014-04-15 17:57:18 +00:00
2014-08-06 21:18:21 +00:00
for ( auto & kv : node - > children )
{
const std : : string & fragment = kv . first ;
const Node * child = & nodes_ [ kv . second ] ;
2014-04-10 16:43:33 +00:00
2014-09-15 16:28:15 +00:00
if ( req_url . compare ( pos , fragment . size ( ) , fragment ) = = 0 )
2014-08-06 21:18:21 +00:00
{
2014-09-15 16:28:15 +00:00
auto ret = find ( req_url , child , pos + fragment . size ( ) , params ) ;
2014-08-06 21:18:21 +00:00
update_found ( ret ) ;
}
}
2014-04-10 16:43:33 +00:00
2014-08-06 21:18:21 +00:00
return { found , match_params } ;
}
void add ( const std : : string & url , unsigned rule_index )
{
unsigned idx { 0 } ;
for ( unsigned i = 0 ; i < url . size ( ) ; i + + )
{
char c = url [ i ] ;
if ( c = = ' < ' )
{
static struct ParamTraits
{
ParamType type ;
std : : string name ;
} paramTraits [ ] =
{
{ ParamType : : INT , " <int> " } ,
{ ParamType : : UINT , " <uint> " } ,
{ ParamType : : DOUBLE , " <float> " } ,
{ ParamType : : DOUBLE , " <double> " } ,
{ ParamType : : STRING , " <str> " } ,
{ ParamType : : STRING , " <string> " } ,
{ ParamType : : PATH , " <path> " } ,
} ;
for ( auto & x : paramTraits )
{
if ( url . compare ( i , x . name . size ( ) , x . name ) = = 0 )
{
if ( ! nodes_ [ idx ] . param_childrens [ ( int ) x . type ] )
{
auto new_node_idx = new_node ( ) ;
nodes_ [ idx ] . param_childrens [ ( int ) x . type ] = new_node_idx ;
}
idx = nodes_ [ idx ] . param_childrens [ ( int ) x . type ] ;
i + = x . name . size ( ) ;
break ;
}
}
i - - ;
}
else
{
std : : string piece ( & c , 1 ) ;
if ( ! nodes_ [ idx ] . children . count ( piece ) )
{
auto new_node_idx = new_node ( ) ;
nodes_ [ idx ] . children . emplace ( piece , new_node_idx ) ;
}
idx = nodes_ [ idx ] . children [ piece ] ;
}
}
if ( nodes_ [ idx ] . rule_index )
throw std : : runtime_error ( " handler already exists for " + url ) ;
nodes_ [ idx ] . rule_index = rule_index ;
}
private :
void debug_node_print ( Node * n , int level )
{
for ( int i = 0 ; i < ( int ) ParamType : : MAX ; i + + )
{
if ( n - > param_childrens [ i ] )
{
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) /*<< "("<<n->param_childrens[i]<<") "*/ ;
switch ( ( ParamType ) i )
{
case ParamType : : INT :
CROW_LOG_DEBUG < < " <int> " ;
break ;
case ParamType : : UINT :
CROW_LOG_DEBUG < < " <uint> " ;
break ;
case ParamType : : DOUBLE :
CROW_LOG_DEBUG < < " <float> " ;
break ;
case ParamType : : STRING :
CROW_LOG_DEBUG < < " <str> " ;
break ;
case ParamType : : PATH :
CROW_LOG_DEBUG < < " <path> " ;
break ;
default :
CROW_LOG_DEBUG < < " <ERROR> " ;
break ;
}
debug_node_print ( & nodes_ [ n - > param_childrens [ i ] ] , level + 1 ) ;
}
}
for ( auto & kv : n - > children )
{
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) /*<< "(" << kv.second << ") "*/ < < kv . first ;
debug_node_print ( & nodes_ [ kv . second ] , level + 1 ) ;
}
}
public :
void debug_print ( )
{
debug_node_print ( head ( ) , 0 ) ;
}
2014-04-10 16:43:33 +00:00
2014-08-06 20:25:18 +00:00
private :
2014-08-06 21:18:21 +00:00
const Node * head ( ) const
{
return & nodes_ . front ( ) ;
}
Node * head ( )
{
return & nodes_ . front ( ) ;
}
unsigned new_node ( )
{
nodes_ . resize ( nodes_ . size ( ) + 1 ) ;
return nodes_ . size ( ) - 1 ;
}
2014-04-10 16:43:33 +00:00
std : : vector < Node > nodes_ ;
} ;
2014-04-02 16:38:08 +00:00
class Router
{
public :
2015-01-19 10:03:06 +00:00
Router ( ) : rules_ ( 2 )
{
}
2015-02-18 15:57:01 +00:00
DynamicRule & new_rule_dynamic ( const std : : string & rule )
{
auto ruleObject = new DynamicRule ( rule ) ;
internal_add_rule_object ( rule , ruleObject ) ;
return * ruleObject ;
}
2014-04-13 02:24:06 +00:00
template < uint64_t N >
typename black_magic : : arguments < N > : : type : : template rebind < TaggedRule > & new_rule_tagged ( const std : : string & rule )
{
using RuleT = typename black_magic : : arguments < N > : : type : : template rebind < TaggedRule > ;
auto ruleObject = new RuleT ( rule ) ;
2015-01-19 10:03:06 +00:00
2015-02-18 15:57:01 +00:00
internal_add_rule_object ( rule , ruleObject ) ;
return * ruleObject ;
}
void internal_add_rule_object ( const std : : string & rule , BaseRule * ruleObject )
{
rules_ . emplace_back ( ruleObject ) ;
2014-04-13 02:24:06 +00:00
trie_ . add ( rule , rules_ . size ( ) - 1 ) ;
2015-01-19 10:03:06 +00:00
// directory case:
// request to `/about' url matches `/about/' rule
if ( rule . size ( ) > 1 & & rule . back ( ) = = ' / ' )
{
std : : string rule_without_trailing_slash = rule ;
rule_without_trailing_slash . pop_back ( ) ;
trie_ . add ( rule_without_trailing_slash , RULE_SPECIAL_REDIRECT_SLASH ) ;
}
2014-04-13 02:24:06 +00:00
}
2014-08-06 21:18:21 +00:00
void validate ( )
{
trie_ . validate ( ) ;
for ( auto & rule : rules_ )
{
if ( rule )
rule - > validate ( ) ;
}
}
2014-04-10 16:43:33 +00:00
2014-08-06 21:18:21 +00:00
void handle ( const request & req , response & res )
{
2014-10-14 08:48:35 +00:00
auto found = trie_ . find ( req . url ) ;
2014-08-06 21:18:21 +00:00
unsigned rule_index = found . first ;
2015-01-19 10:03:06 +00:00
CROW_LOG_DEBUG < < " ??? " < < rule_index ;
2014-08-06 21:18:21 +00:00
if ( ! rule_index )
{
2014-12-11 16:38:57 +00:00
CROW_LOG_DEBUG < < " Cannot match rules " < < req . url ;
2014-08-06 21:18:21 +00:00
res = response ( 404 ) ;
2014-12-11 16:38:57 +00:00
res . end ( ) ;
2014-08-06 21:18:21 +00:00
return ;
}
if ( rule_index > = rules_ . size ( ) )
throw std : : runtime_error ( " Trie internal structure corrupted! " ) ;
2015-01-19 10:03:06 +00:00
if ( rule_index = = RULE_SPECIAL_REDIRECT_SLASH )
{
CROW_LOG_INFO < < " Redirecting to a url with trailing slash: " < < req . url ;
res = response ( 301 ) ;
// TODO absolute url building
if ( req . get_header_value ( " Host " ) . empty ( ) )
{
res . add_header ( " Location " , req . url + " / " ) ;
}
else
{
res . add_header ( " Location " , " http:// " + req . get_header_value ( " Host " ) + req . url + " / " ) ;
}
res . end ( ) ;
return ;
}
2015-02-20 02:58:41 +00:00
if ( ( rules_ [ rule_index ] - > get_methods ( ) & ( 1 < < ( uint32_t ) req . method ) ) = = 0 )
2014-10-07 12:51:24 +00:00
{
2015-02-20 02:58:41 +00:00
CROW_LOG_DEBUG < < " Rule found but method mismatch: " < < req . url < < " with " < < method_name ( req . method ) < < " ( " < < ( uint32_t ) req . method < < " ) / " < < rules_ [ rule_index ] - > get_methods ( ) ;
2014-10-07 12:51:24 +00:00
res = response ( 404 ) ;
2014-12-11 16:38:57 +00:00
res . end ( ) ;
2014-10-07 12:51:24 +00:00
return ;
}
2015-02-20 02:58:41 +00:00
CROW_LOG_DEBUG < < " Matched rule ' " < < rules_ [ rule_index ] - > rule_ < < " ' " < < ( uint32_t ) req . method < < " / " < < rules_ [ rule_index ] - > get_methods ( ) ;
2014-08-06 21:18:21 +00:00
2014-12-10 17:24:13 +00:00
// any uncaught exceptions become 500s
try
{
rules_ [ rule_index ] - > handle ( req , res , found . second ) ;
}
catch ( std : : exception & e )
{
CROW_LOG_ERROR < < " An uncaught exception occurred: " < < e . what ( ) ;
res = response ( 500 ) ;
res . end ( ) ;
return ;
}
catch ( . . . )
{
CROW_LOG_ERROR < < " An uncaught exception occurred. The type was unknown so no information was available. " ;
res = response ( 500 ) ;
res . end ( ) ;
return ;
}
2014-08-06 21:18:21 +00:00
}
void debug_print ( )
{
trie_ . debug_print ( ) ;
}
2014-04-15 17:57:18 +00:00
2014-04-02 20:31:32 +00:00
private :
2014-04-13 02:24:06 +00:00
std : : vector < std : : unique_ptr < BaseRule > > rules_ ;
2014-04-10 16:43:33 +00:00
Trie trie_ ;
2014-04-02 16:38:08 +00:00
} ;
}