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
2016-09-21 14:11:06 +00:00
# include "crow/common.h"
# include "crow/http_response.h"
# include "crow/http_request.h"
# include "crow/utility.h"
# include "crow/logging.h"
# include "crow/websocket.h"
2021-07-07 12:51:04 +00:00
# include "crow/mustache.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
{
2020-11-18 22:13:57 +00:00
/// A base class for all rules.
/// Used to provide a common interface for code dealing with different types of rules.
/// A Rule provides a URL, allowed HTTP methods, and handlers.
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 ( )
{
}
2020-10-14 21:09:35 +00:00
2014-04-15 13:46:28 +00:00
virtual void validate ( ) = 0 ;
2016-08-28 05:46:31 +00:00
std : : unique_ptr < BaseRule > upgrade ( )
2017-12-24 15:40:39 +00:00
{
if ( rule_to_upgrade_ )
return std : : move ( rule_to_upgrade_ ) ;
return { } ;
}
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 ;
2020-10-14 21:09:35 +00:00
virtual void handle_upgrade ( const request & , response & res , SocketAdaptor & & )
2017-12-24 15:40:39 +00:00
{
res = response ( 404 ) ;
res . end ( ) ;
}
2016-08-28 05:46:31 +00:00
# ifdef CROW_ENABLE_SSL
2020-10-14 21:09:35 +00:00
virtual void handle_upgrade ( const request & , response & res , SSLAdaptor & & )
2017-12-24 15:40:39 +00:00
{
res = response ( 404 ) ;
res . end ( ) ;
}
2016-08-28 05:46:31 +00:00
# endif
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
2017-12-24 15:40:39 +00:00
template < typename F >
void foreach_method ( F f )
{
2021-02-21 00:14:30 +00:00
for ( uint32_t method = 0 , method_bit = 1 ; method < static_cast < uint32_t > ( HTTPMethod : : InternalMethodCount ) ; method + + , method_bit < < = 1 )
2017-12-24 15:40:39 +00:00
{
if ( methods_ & method_bit )
f ( method ) ;
}
}
2021-07-07 12:51:04 +00:00
std : : string custom_templates_base ;
2017-12-24 15:40:39 +00:00
const std : : string & rule ( ) { return rule_ ; }
2014-10-07 12:51:24 +00:00
protected :
2021-02-21 00:14:30 +00:00
uint32_t methods_ { 1 < < static_cast < int > ( HTTPMethod : : Get ) } ;
2015-02-18 15:57:01 +00:00
std : : string rule_ ;
std : : string name_ ;
2016-08-28 05:46:31 +00:00
2017-12-24 15:40:39 +00:00
std : : unique_ptr < BaseRule > rule_to_upgrade_ ;
2016-08-28 05:46:31 +00:00
2015-02-18 15:57:01 +00:00
friend class Router ;
2021-07-27 07:52:49 +00:00
friend class Blueprint ;
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
2020-10-14 21:09:35 +00:00
template < typename F , int NInt , int NUint , int NDouble , int NString , typename S1 , typename S2 >
2015-02-18 15:57:01 +00:00
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
2020-10-14 21:09:35 +00:00
template < typename F , int NInt , int NUint , int NDouble , int NString , typename . . . Args1 , typename . . . Args2 >
2015-02-18 15:57:01 +00:00
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
2020-10-14 21:09:35 +00:00
template < typename F , int NInt , int NUint , int NDouble , int NString , typename . . . Args1 , typename . . . Args2 >
2015-02-18 15:57:01 +00:00
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
2020-10-14 21:09:35 +00:00
template < typename F , int NInt , int NUint , int NDouble , int NString , typename . . . Args1 , typename . . . Args2 >
2015-02-18 15:57:01 +00:00
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
} ;
2020-10-14 21:09:35 +00:00
template < typename F , int NInt , int NUint , int NDouble , int NString , typename . . . Args1 , typename . . . Args2 >
2015-02-18 15:57:01 +00:00
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
} ;
2020-10-14 21:09:35 +00:00
template < typename F , int NInt , int NUint , int NDouble , int NString , typename . . . Args1 >
2015-02-18 15:57:01 +00:00
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 ,
2020-10-14 21:09:35 +00:00
cparams . params . template get < typename Args1 : : type > ( Args1 : : pos ) . . .
2014-08-05 13:38:51 +00:00
) ;
}
2015-02-18 15:57:01 +00:00
} ;
template < typename Func , typename . . . ArgsWrapped >
struct Wrapped
{
template < typename . . . Args >
2016-12-06 15:22:10 +00:00
void set_ ( Func f , typename std : : enable_if <
2015-02-18 15:57:01 +00:00
! 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 >
2016-12-06 15:22:10 +00:00
void set_ ( Func f , typename std : : enable_if <
2015-02-18 15:57:01 +00:00
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 >
2016-12-06 15:22:10 +00:00
void set_ ( Func f , typename std : : enable_if <
2015-02-18 15:57:01 +00:00
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 . . . ) > ;
2020-10-14 21:09:35 +00:00
using args_type = black_magic : : S < typename black_magic : : promote_t < Args > . . . > ;
2015-02-18 15:57:01 +00:00
} ;
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 . . . ) > ;
2020-10-14 21:09:35 +00:00
using args_type = black_magic : : S < typename black_magic : : promote_t < Args > . . . > ;
2015-02-18 15:57:01 +00:00
} ;
template < typename . . . Args >
struct handler_type_helper < const request & , response & , Args . . . >
{
using type = std : : function < void ( const crow : : request & , crow : : response & , Args . . . ) > ;
2020-10-14 21:09:35 +00:00
using args_type = black_magic : : S < typename black_magic : : promote_t < Args > . . . > ;
2015-02-18 15:57:01 +00:00
} ;
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_ ) > ,
2020-10-14 21:09:35 +00:00
0 , 0 , 0 , 0 ,
2015-02-18 15:57:01 +00:00
typename handler_type_helper < ArgsWrapped . . . > : : args_type ,
black_magic : : S < >
> ( ) (
detail : : routing_handler_call_helper : : call_params <
decltype ( handler_ ) >
{ handler_ , params , req , res }
) ;
}
} ;
}
}
2021-04-12 05:25:09 +00:00
class CatchallRule
{
public :
CatchallRule ( ) { }
template < typename Func >
typename std : : enable_if < black_magic : : CallHelper < Func , black_magic : : S < > > : : value , void > : : type
operator ( ) ( Func & & f )
{
static_assert ( ! std : : is_same < void , decltype ( f ( ) ) > : : value ,
2021-04-14 01:55:27 +00:00
" Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable " ) ;
2021-04-12 05:25:09 +00:00
handler_ = (
# ifdef CROW_CAN_USE_CPP14
[ f = std : : move ( f ) ]
# else
[ f ]
# endif
( const request & , response & res ) {
res = response ( f ( ) ) ;
res . end ( ) ;
} ) ;
}
template < typename Func >
typename std : : enable_if <
! black_magic : : CallHelper < Func , black_magic : : S < > > : : value & &
black_magic : : CallHelper < Func , black_magic : : S < crow : : request > > : : value ,
void > : : type
operator ( ) ( Func & & f )
{
static_assert ( ! std : : is_same < void , decltype ( f ( std : : declval < crow : : request > ( ) ) ) > : : value ,
2021-04-14 01:55:27 +00:00
" Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable " ) ;
2021-04-12 05:25:09 +00:00
handler_ = (
# ifdef CROW_CAN_USE_CPP14
[ f = std : : move ( f ) ]
# else
[ f ]
# endif
( const crow : : request & req , crow : : response & res ) {
res = response ( f ( req ) ) ;
res . end ( ) ;
} ) ;
}
template < typename Func >
typename std : : enable_if <
! black_magic : : CallHelper < Func , black_magic : : S < > > : : value & &
! black_magic : : CallHelper < Func , black_magic : : S < crow : : request > > : : value & &
black_magic : : CallHelper < Func , black_magic : : S < crow : : response & > > : : value ,
void > : : type
operator ( ) ( Func & & f )
{
static_assert ( std : : is_same < void , decltype ( f ( std : : declval < crow : : response & > ( ) ) ) > : : value ,
" Handler function with response argument should have void return type " ) ;
handler_ = (
# ifdef CROW_CAN_USE_CPP14
[ f = std : : move ( f ) ]
# else
[ f ]
# endif
( const crow : : request & , crow : : response & res ) {
f ( res ) ;
} ) ;
}
template < typename Func >
typename std : : enable_if <
! black_magic : : CallHelper < Func , black_magic : : S < > > : : value & &
! black_magic : : CallHelper < Func , black_magic : : S < crow : : request > > : : value & &
! black_magic : : CallHelper < Func , black_magic : : S < crow : : response & > > : : value ,
void > : : type
operator ( ) ( Func & & f )
{
static_assert ( std : : is_same < void , decltype ( f ( std : : declval < crow : : request > ( ) , std : : declval < crow : : response & > ( ) ) ) > : : value ,
" Handler function with response argument should have void return type " ) ;
handler_ = std : : move ( f ) ;
}
bool has_handler ( )
{
return ( handler_ ! = nullptr ) ;
}
protected :
friend class Router ;
private :
std : : function < void ( const crow : : request & , crow : : response & ) > handler_ ;
} ;
2020-11-18 22:13:57 +00:00
/// A rule dealing with websockets.
/// Provides the interface for the user to put in the necessary handlers for a websocket to work.
///
2017-12-24 15:40:39 +00:00
class WebSocketRule : public BaseRule
{
2016-08-28 05:46:31 +00:00
using self_t = WebSocketRule ;
2017-12-24 15:40:39 +00:00
public :
2016-08-28 05:46:31 +00:00
WebSocketRule ( std : : string rule )
: BaseRule ( std : : move ( rule ) )
{
}
2017-12-24 15:40:39 +00:00
void validate ( ) override
{
}
2016-08-28 05:46:31 +00:00
2017-12-24 15:40:39 +00:00
void handle ( const request & , response & res , const routing_params & ) override
{
res = response ( 404 ) ;
res . end ( ) ;
}
2016-08-28 05:46:31 +00:00
2020-10-14 21:09:35 +00:00
void handle_upgrade ( const request & req , response & , SocketAdaptor & & adaptor ) override
2017-12-24 15:40:39 +00:00
{
new crow : : websocket : : Connection < SocketAdaptor > ( req , std : : move ( adaptor ) , open_handler_ , message_handler_ , close_handler_ , error_handler_ , accept_handler_ ) ;
}
2016-08-28 05:46:31 +00:00
# ifdef CROW_ENABLE_SSL
void handle_upgrade ( const request & req , response & , SSLAdaptor & & adaptor ) override
2017-12-24 15:40:39 +00:00
{
new crow : : websocket : : Connection < SSLAdaptor > ( req , std : : move ( adaptor ) , open_handler_ , message_handler_ , close_handler_ , error_handler_ , accept_handler_ ) ;
}
2016-08-28 05:46:31 +00:00
# endif
2017-12-24 15:40:39 +00:00
template < typename Func >
self_t & onopen ( Func f )
{
open_handler_ = f ;
return * this ;
}
template < typename Func >
self_t & onmessage ( Func f )
{
message_handler_ = f ;
return * this ;
}
template < typename Func >
self_t & onclose ( Func f )
{
close_handler_ = f ;
return * this ;
}
template < typename Func >
self_t & onerror ( Func f )
{
error_handler_ = f ;
return * this ;
}
template < typename Func >
self_t & onaccept ( Func f )
{
accept_handler_ = f ;
return * this ;
}
protected :
std : : function < void ( crow : : websocket : : connection & ) > open_handler_ ;
std : : function < void ( crow : : websocket : : connection & , const std : : string & , bool ) > message_handler_ ;
std : : function < void ( crow : : websocket : : connection & , const std : : string & ) > close_handler_ ;
std : : function < void ( crow : : websocket : : connection & ) > error_handler_ ;
std : : function < bool ( const crow : : request & ) > accept_handler_ ;
} ;
2016-08-28 05:46:31 +00:00
2020-11-18 22:13:57 +00:00
/// Allows the user to assign parameters using functions.
///
/// `rule.name("name").methods(HTTPMethod::POST)`
2015-02-20 02:58:41 +00:00
template < typename T >
struct RuleParameterTraits
{
using self_t = T ;
2020-10-14 21:09:35 +00:00
WebSocketRule & websocket ( )
2017-12-24 15:40:39 +00:00
{
2021-02-21 00:14:30 +00:00
auto p = new WebSocketRule ( static_cast < self_t * > ( this ) - > rule_ ) ;
static_cast < self_t * > ( this ) - > rule_to_upgrade_ . reset ( p ) ;
2017-12-24 15:40:39 +00:00
return * p ;
}
2016-08-28 05:46:31 +00:00
2015-02-20 02:58:41 +00:00
self_t & name ( std : : string name ) noexcept
{
2021-02-21 00:14:30 +00:00
static_cast < self_t * > ( this ) - > name_ = std : : move ( name ) ;
return static_cast < self_t & > ( * this ) ;
2015-02-20 02:58:41 +00:00
}
self_t & methods ( HTTPMethod method )
{
2021-02-21 00:14:30 +00:00
static_cast < self_t * > ( this ) - > methods_ = 1 < < static_cast < int > ( method ) ;
return static_cast < self_t & > ( * this ) ;
2015-02-20 02:58:41 +00:00
}
template < typename . . . MethodArgs >
self_t & methods ( HTTPMethod method , MethodArgs . . . args_method )
{
methods ( args_method . . . ) ;
2021-02-21 00:14:30 +00:00
static_cast < self_t * > ( this ) - > methods_ | = 1 < < static_cast < int > ( method ) ;
return static_cast < self_t & > ( * this ) ;
2015-02-20 02:58:41 +00:00
}
2016-08-28 05:46:31 +00:00
2015-02-20 02:58:41 +00:00
} ;
2020-11-18 22:13:57 +00:00
/// A rule that can change its parameters during runtime.
2015-02-20 02:58:41 +00:00
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
{
2021-07-07 12:51:04 +00:00
if ( ! custom_templates_base . empty ( ) )
mustache : : set_base ( custom_templates_base ) ;
else if ( mustache : : detail : : get_template_base_directory_ref ( ) ! = " templates " )
mustache : : set_base ( " templates " ) ;
2015-02-18 15:57:01 +00:00
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
2020-10-14 21:09:35 +00:00
std : : function < void ( const request & , response & , const routing_params & ) >
2015-02-18 15:57:01 +00:00
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 (
2020-10-14 21:09:35 +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 > . . . > ( ) ;
2016-12-06 15:22:10 +00:00
ret . template set_ <
2015-02-18 15:57:01 +00:00
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_ ;
} ;
2020-11-18 22:13:57 +00:00
/// Default rule created when CROW_ROUTE is called.
2015-02-18 15:57:01 +00:00
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
2016-08-28 05:46:31 +00:00
void validate ( ) override
2014-04-15 13:46:28 +00:00
{
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 | |
2020-10-14 21:09:35 +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 " ) ;
2020-10-14 21:09:35 +00:00
static_assert ( ! std : : is_same < void , decltype ( f ( std : : declval < Args > ( ) . . . ) ) > : : value ,
2021-04-14 01:55:27 +00:00
" Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable " ) ;
2014-04-20 08:45:10 +00:00
2017-01-09 12:24:01 +00:00
handler_ = (
# ifdef CROW_CAN_USE_CPP14
[ f = std : : move ( f ) ]
# else
[ f ]
# endif
( const request & , response & res , Args . . . args ) {
2015-02-18 15:57:01 +00:00
res = response ( f ( args . . . ) ) ;
res . end ( ) ;
2017-01-09 12:24:01 +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 & &
2020-10-14 21:09:35 +00:00
black_magic : : CallHelper < Func , black_magic : : S < crow : : request , Args . . . > > : : value ,
2014-08-05 13:38:51 +00:00
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 | |
2020-10-14 21:09:35 +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 " ) ;
2020-10-14 21:09:35 +00:00
static_assert ( ! std : : is_same < void , decltype ( f ( std : : declval < crow : : request > ( ) , std : : declval < Args > ( ) . . . ) ) > : : value ,
2021-04-14 01:55:27 +00:00
" Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable " ) ;
2014-04-20 08:45:10 +00:00
2017-01-09 12:24:01 +00:00
handler_ = (
# ifdef CROW_CAN_USE_CPP14
[ f = std : : move ( f ) ]
# else
[ f ]
# endif
( const crow : : request & req , crow : : response & res , Args . . . args ) {
2015-02-18 15:57:01 +00:00
res = response ( f ( req , args . . . ) ) ;
res . end ( ) ;
2017-01-09 12:24:01 +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 & &
2020-12-01 06:20:46 +00:00
! black_magic : : CallHelper < Func , black_magic : : S < crow : : request , Args . . . > > : : value & &
black_magic : : CallHelper < Func , black_magic : : S < crow : : response & , Args . . . > > : : value ,
2020-12-02 10:06:29 +00:00
void > : : type
operator ( ) ( Func & & f )
{
static_assert ( black_magic : : CallHelper < Func , black_magic : : S < Args . . . > > : : value | |
black_magic : : CallHelper < Func , black_magic : : S < crow : : response & , Args . . . > > : : value
,
" Handler type is mismatched with URL parameters " ) ;
static_assert ( std : : is_same < void , decltype ( f ( std : : declval < crow : : response & > ( ) , std : : declval < Args > ( ) . . . ) ) > : : value ,
" Handler function with response argument should have void return type " ) ;
2020-12-02 11:07:43 +00:00
handler_ = (
2020-12-02 10:06:29 +00:00
# ifdef CROW_CAN_USE_CPP14
[ f = std : : move ( f ) ]
# else
[ f ]
# endif
2020-12-02 17:29:45 +00:00
( const crow : : request & , crow : : response & res , Args . . . args ) {
2020-12-02 10:06:29 +00:00
f ( res , args . . . ) ;
} ) ;
}
template < typename Func >
typename std : : enable_if <
! black_magic : : CallHelper < Func , black_magic : : S < Args . . . > > : : value & &
2020-12-01 06:20:46 +00:00
! black_magic : : CallHelper < Func , black_magic : : S < crow : : request , Args . . . > > : : value & &
2020-12-02 10:06:29 +00:00
! black_magic : : CallHelper < Func , black_magic : : S < crow : : response & , Args . . . > > : : value ,
2014-08-05 13:38:51 +00:00
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
2020-10-14 21:09:35 +00:00
,
2015-03-12 21:53:45 +00:00
" Handler type is mismatched with URL parameters " ) ;
2020-10-14 21:09:35 +00:00
static_assert ( std : : is_same < void , decltype ( f ( std : : declval < crow : : request > ( ) , std : : declval < crow : : response & > ( ) , std : : declval < Args > ( ) . . . ) ) > : : value ,
2014-08-05 13:38:51 +00:00
" 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
{
2021-07-07 12:51:04 +00:00
if ( ! custom_templates_base . empty ( ) )
mustache : : set_base ( custom_templates_base ) ;
else if ( mustache : : detail : : get_template_base_directory_ref ( ) ! = " templates " )
mustache : : set_base ( " templates " ) ;
2015-02-18 15:57:01 +00:00
detail : : routing_handler_call_helper : : call <
detail : : routing_handler_call_helper : : call_params <
2020-10-14 21:09:35 +00:00
decltype ( handler_ ) > ,
0 , 0 , 0 , 0 ,
black_magic : : S < Args . . . > ,
2014-08-05 18:54:38 +00:00
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 ;
2021-07-27 07:52:49 +00:00
2020-11-18 22:13:57 +00:00
/// A search tree.
2014-04-10 16:43:33 +00:00
class Trie
{
public :
struct Node
{
2021-07-27 07:52:49 +00:00
uint16_t rule_index { } ;
// Assign the index to the maximum 32 unsigned integer value by default so that any other number (specifically 0) is a valid BP id.
uint16_t blueprint_index { 0xFFFF } ;
2021-07-10 19:45:47 +00:00
std : : string key ;
ParamType param = ParamType : : MAX ; // MAX = No param.
std : : vector < Node * > children ;
2014-04-15 17:57:18 +00:00
2014-08-06 21:18:21 +00:00
bool IsSimpleNode ( ) const
{
2020-10-14 21:09:35 +00:00
return
2014-08-06 21:18:21 +00:00
! rule_index & &
2021-07-27 07:52:49 +00:00
blueprint_index = = 0xFFFF & &
2021-07-10 19:45:47 +00:00
children . size ( ) < 2 & &
param = = ParamType : : MAX & &
std : : all_of ( std : : begin ( children ) , std : : end ( children ) , [ ] ( Node * x ) { return x - > param = = ParamType : : MAX ; } ) ;
2014-08-06 21:18:21 +00:00
}
2014-04-10 16:43:33 +00:00
} ;
2021-07-10 19:45:47 +00:00
Trie ( )
2014-08-06 21:18:21 +00:00
{
}
2014-04-15 13:46:28 +00:00
2021-04-03 02:33:40 +00:00
///Check whether or not the trie is empty.
bool is_empty ( )
{
2021-07-10 19:45:47 +00:00
return head_ . children . empty ( ) ;
2021-04-03 02:33:40 +00:00
}
2021-07-10 19:45:47 +00:00
void optimize ( )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
for ( auto child : head_ . children )
2014-08-06 21:18:21 +00:00
{
optimizeNode ( child ) ;
}
2021-07-10 19:45:47 +00:00
}
private :
void optimizeNode ( Node * node )
{
2014-08-06 21:18:21 +00:00
if ( node - > children . empty ( ) )
return ;
2021-07-18 21:01:20 +00:00
if ( node - > IsSimpleNode ( ) )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
Node * child_temp = node - > children [ 0 ] ;
node - > key = node - > key + child_temp - > key ;
node - > rule_index = child_temp - > rule_index ;
2021-07-27 07:52:49 +00:00
node - > blueprint_index = child_temp - > blueprint_index ;
2021-07-10 19:45:47 +00:00
node - > children = std : : move ( child_temp - > children ) ;
delete ( child_temp ) ;
2014-08-06 21:18:21 +00:00
optimizeNode ( node ) ;
}
else
{
2021-07-10 19:45:47 +00:00
for ( auto & child : node - > children )
2014-08-06 21:18:21 +00:00
{
optimizeNode ( child ) ;
}
}
}
2021-07-10 19:45:47 +00:00
void debug_node_print ( Node * node , int level )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
if ( node - > param ! = ParamType : : MAX )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
switch ( node - > param )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
case ParamType : : INT :
2021-07-10 20:14:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < " <int> " ;
2021-07-10 19:45:47 +00:00
break ;
case ParamType : : UINT :
2021-07-10 20:14:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < " <uint> " ;
2021-07-10 19:45:47 +00:00
break ;
case ParamType : : DOUBLE :
2021-07-10 20:14:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < " <double> " ;
2021-07-10 19:45:47 +00:00
break ;
case ParamType : : STRING :
2021-07-10 20:14:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < " <string> " ;
2021-07-10 19:45:47 +00:00
break ;
case ParamType : : PATH :
2021-07-10 20:14:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < " <path> " ;
2021-07-10 19:45:47 +00:00
break ;
default :
2021-07-10 20:14:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < " <ERROR> " ;
2021-07-10 19:45:47 +00:00
break ;
2014-08-06 21:18:21 +00:00
}
}
else
2021-07-10 20:14:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < node - > key ;
2014-04-15 13:46:28 +00:00
2021-07-10 19:45:47 +00:00
for ( auto & child : node - > children )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
debug_node_print ( child , level + 1 ) ;
2014-08-06 21:18:21 +00:00
}
}
2021-04-03 02:33:40 +00:00
public :
2014-08-06 21:18:21 +00:00
2021-07-10 19:45:47 +00:00
void debug_print ( )
2014-08-06 21:18:21 +00:00
{
2021-07-10 20:14:38 +00:00
CROW_LOG_DEBUG < < " HEAD " ;
2021-07-10 19:45:47 +00:00
for ( auto & child : head_ . children )
debug_node_print ( child , 1 ) ;
2014-08-06 21:18:21 +00:00
}
2014-04-15 13:46:28 +00:00
2014-08-06 21:18:21 +00:00
void validate ( )
{
2021-07-10 19:45:47 +00:00
if ( ! head_ . IsSimpleNode ( ) )
2014-08-06 21:18:21 +00:00
throw std : : runtime_error ( " Internal error: Trie header should be simple! " ) ;
optimize ( ) ;
}
2014-04-10 16:43:33 +00:00
2021-07-27 07:52:49 +00:00
//Rule_index, Blueprint_index, routing_params
std : : tuple < uint16_t , std : : vector < uint16_t > , routing_params > find ( const std : : string & req_url , const Node * node = nullptr , unsigned pos = 0 , routing_params * params = nullptr , std : : vector < uint16_t > * blueprints = nullptr ) const
2014-08-06 21:18:21 +00:00
{
2021-07-27 07:52:49 +00:00
static const uint16_t idx_max { 0xFFFF } ;
2021-07-10 19:45:47 +00:00
//start params as an empty struct
2014-08-06 21:18:21 +00:00
routing_params empty ;
if ( params = = nullptr )
params = & empty ;
2021-07-27 07:52:49 +00:00
//same for blueprint vector
std : : vector < uint16_t > MT ;
if ( blueprints = = nullptr )
blueprints = & MT ;
2014-04-10 16:43:33 +00:00
2021-07-27 07:52:49 +00:00
uint16_t found { } ; //The rule index to be found
std : : vector < uint16_t > found_BP ; //The Blueprint indices to be found
2021-07-10 19:45:47 +00:00
routing_params match_params ; //supposedly the final matched parameters
2014-04-10 16:43:33 +00:00
2021-07-10 19:45:47 +00:00
//start from the head node
2014-08-06 21:18:21 +00:00
if ( node = = nullptr )
2021-07-10 19:45:47 +00:00
node = & head_ ;
2021-07-27 07:52:49 +00:00
auto update_found = [ & found , & found_BP , & match_params ] ( std : : tuple < uint16_t , std : : vector < uint16_t > , routing_params > & ret )
2014-08-06 21:18:21 +00:00
{
2021-07-27 07:52:49 +00:00
found_BP = std : : move ( std : : get < 1 > ( ret ) ) ;
if ( std : : get < 0 > ( ret ) & & ( ! found | | found > std : : get < 0 > ( ret ) ) )
2014-08-06 21:18:21 +00:00
{
2021-07-27 07:52:49 +00:00
found = std : : get < 0 > ( ret ) ;
match_params = std : : move ( std : : get < 2 > ( ret ) ) ;
2014-08-06 21:18:21 +00:00
}
} ;
2021-07-27 07:52:49 +00:00
//if the function was called on a node at the end of the string (the last recursion), return the nodes rule index, and whatever params were passed to the function
if ( pos = = req_url . size ( ) )
{
found_BP = std : : move ( * blueprints ) ;
return { node - > rule_index , * blueprints , * params } ;
}
bool found_fragment = false ;
2021-07-10 19:45:47 +00:00
for ( auto & child : node - > children )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
if ( child - > param ! = ParamType : : MAX )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
if ( child - > param = = ParamType : : INT )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
char c = req_url [ pos ] ;
if ( ( c > = ' 0 ' & & c < = ' 9 ' ) | | c = = ' + ' | | c = = ' - ' )
{
char * eptr ;
errno = 0 ;
long long int value = strtoll ( req_url . data ( ) + pos , & eptr , 10 ) ;
if ( errno ! = ERANGE & & eptr ! = req_url . data ( ) + pos )
{
2021-07-27 07:52:49 +00:00
found_fragment = true ;
2021-07-10 19:45:47 +00:00
params - > int_params . push_back ( value ) ;
2021-07-27 07:52:49 +00:00
if ( child - > blueprint_index ! = idx_max ) blueprints - > push_back ( child - > blueprint_index ) ;
auto ret = find ( req_url , child , eptr - req_url . data ( ) , params , blueprints ) ;
2021-07-10 19:45:47 +00:00
update_found ( ret ) ;
params - > int_params . pop_back ( ) ;
2021-07-27 07:52:49 +00:00
blueprints - > pop_back ( ) ;
2021-07-10 19:45:47 +00:00
}
}
2014-08-06 21:18:21 +00:00
}
2021-07-10 19:45:47 +00:00
else if ( child - > param = = ParamType : : UINT )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
char c = req_url [ pos ] ;
if ( ( c > = ' 0 ' & & c < = ' 9 ' ) | | c = = ' + ' )
{
char * eptr ;
errno = 0 ;
unsigned long long int value = strtoull ( req_url . data ( ) + pos , & eptr , 10 ) ;
if ( errno ! = ERANGE & & eptr ! = req_url . data ( ) + pos )
{
2021-07-27 07:52:49 +00:00
found_fragment = true ;
2021-07-10 19:45:47 +00:00
params - > uint_params . push_back ( value ) ;
2021-07-27 07:52:49 +00:00
if ( child - > blueprint_index ! = idx_max ) blueprints - > push_back ( child - > blueprint_index ) ;
auto ret = find ( req_url , child , eptr - req_url . data ( ) , params , blueprints ) ;
2021-07-10 19:45:47 +00:00
update_found ( ret ) ;
params - > uint_params . pop_back ( ) ;
2021-07-27 07:52:49 +00:00
blueprints - > pop_back ( ) ;
2021-07-10 19:45:47 +00:00
}
}
2014-08-06 21:18:21 +00:00
}
2021-07-10 19:45:47 +00:00
else if ( child - > param = = ParamType : : DOUBLE )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
char c = req_url [ pos ] ;
if ( ( c > = ' 0 ' & & c < = ' 9 ' ) | | c = = ' + ' | | c = = ' - ' | | c = = ' . ' )
{
char * eptr ;
errno = 0 ;
double value = strtod ( req_url . data ( ) + pos , & eptr ) ;
if ( errno ! = ERANGE & & eptr ! = req_url . data ( ) + pos )
{
2021-07-27 07:52:49 +00:00
found_fragment = true ;
2021-07-10 19:45:47 +00:00
params - > double_params . push_back ( value ) ;
2021-07-27 07:52:49 +00:00
if ( child - > blueprint_index ! = idx_max ) blueprints - > push_back ( child - > blueprint_index ) ;
auto ret = find ( req_url , child , eptr - req_url . data ( ) , params , blueprints ) ;
2021-07-10 19:45:47 +00:00
update_found ( ret ) ;
params - > double_params . pop_back ( ) ;
2021-07-27 07:52:49 +00:00
blueprints - > pop_back ( ) ;
2021-07-10 19:45:47 +00:00
}
}
2014-08-06 21:18:21 +00:00
}
2021-07-10 19:45:47 +00:00
else if ( child - > param = = ParamType : : STRING )
{
size_t epos = pos ;
for ( ; epos < req_url . size ( ) ; epos + + )
{
if ( req_url [ epos ] = = ' / ' )
break ;
}
2014-08-06 21:18:21 +00:00
2021-07-10 19:45:47 +00:00
if ( epos ! = pos )
{
2021-07-27 07:52:49 +00:00
found_fragment = true ;
2021-07-10 19:45:47 +00:00
params - > string_params . push_back ( req_url . substr ( pos , epos - pos ) ) ;
2021-07-27 07:52:49 +00:00
if ( child - > blueprint_index ! = idx_max ) blueprints - > push_back ( child - > blueprint_index ) ;
auto ret = find ( req_url , child , epos , params , blueprints ) ;
2021-07-10 19:45:47 +00:00
update_found ( ret ) ;
params - > string_params . pop_back ( ) ;
2021-07-27 07:52:49 +00:00
blueprints - > pop_back ( ) ;
2021-07-10 19:45:47 +00:00
}
}
2014-08-06 21:18:21 +00:00
2021-07-10 19:45:47 +00:00
else if ( child - > param = = ParamType : : PATH )
{
size_t epos = req_url . size ( ) ;
2014-08-06 21:18:21 +00:00
2021-07-10 19:45:47 +00:00
if ( epos ! = pos )
{
2021-07-27 07:52:49 +00:00
found_fragment = true ;
2021-07-10 19:45:47 +00:00
params - > string_params . push_back ( req_url . substr ( pos , epos - pos ) ) ;
2021-07-27 07:52:49 +00:00
if ( child - > blueprint_index ! = idx_max ) blueprints - > push_back ( child - > blueprint_index ) ;
auto ret = find ( req_url , child , epos , params , blueprints ) ;
2021-07-10 19:45:47 +00:00
update_found ( ret ) ;
params - > string_params . pop_back ( ) ;
2021-07-27 07:52:49 +00:00
blueprints - > pop_back ( ) ;
2021-07-10 19:45:47 +00:00
}
}
2014-08-06 21:18:21 +00:00
}
2014-04-10 16:43:33 +00:00
2021-07-10 19:45:47 +00:00
else
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
const std : : string & fragment = child - > key ;
if ( req_url . compare ( pos , fragment . size ( ) , fragment ) = = 0 )
{
2021-07-27 07:52:49 +00:00
found_fragment = true ;
if ( child - > blueprint_index ! = idx_max ) blueprints - > push_back ( child - > blueprint_index ) ;
auto ret = find ( req_url , child , pos + fragment . size ( ) , params , blueprints ) ;
2021-07-10 19:45:47 +00:00
update_found ( ret ) ;
2021-07-27 07:52:49 +00:00
blueprints - > pop_back ( ) ;
2021-07-10 19:45:47 +00:00
}
2014-08-06 21:18:21 +00:00
}
}
2021-07-27 07:52:49 +00:00
if ( ! found_fragment )
found_BP = std : : move ( * blueprints ) ;
return { found , found_BP , match_params } ; //Called after all the recursions have been done
2014-08-06 21:18:21 +00:00
}
2021-07-27 07:52:49 +00:00
//This functions assumes any blueprint info passed is valid
void add ( const std : : string & url , uint16_t rule_index , unsigned bp_prefix_length = 0 , uint16_t blueprint_index = 0xFFFF )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
Node * idx = & head_ ;
2014-08-06 21:18:21 +00:00
2021-07-27 07:52:49 +00:00
bool has_blueprint = bp_prefix_length ! = 0 & & blueprint_index ! = 0xFFFF ;
2014-08-06 21:18:21 +00:00
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 )
{
2021-07-10 19:45:47 +00:00
bool found = false ;
for ( Node * child : idx - > children )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
if ( child - > param = = x . type )
{
idx = child ;
i + = x . name . size ( ) ;
found = true ;
break ;
}
2014-08-06 21:18:21 +00:00
}
2021-07-10 19:45:47 +00:00
if ( found )
break ;
auto new_node_idx = new_node ( idx ) ;
new_node_idx - > param = x . type ;
idx = new_node_idx ;
2014-08-06 21:18:21 +00:00
i + = x . name . size ( ) ;
break ;
}
}
i - - ;
}
else
{
2021-07-10 19:45:47 +00:00
//This part assumes the tree is unoptimized (every node has a max 1 character key)
bool piece_found = false ;
for ( auto & child : idx - > children )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
if ( child - > key [ 0 ] = = c )
{
idx = child ;
piece_found = true ;
break ;
}
2014-08-06 21:18:21 +00:00
}
2021-07-10 19:45:47 +00:00
if ( ! piece_found )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
auto new_node_idx = new_node ( idx ) ;
new_node_idx - > key = c ;
2021-07-27 07:52:49 +00:00
//The assumption here is that you'd only need to add a blueprint index if the tree didn't have the BP prefix.
if ( has_blueprint & & i = = bp_prefix_length )
new_node_idx - > blueprint_index = blueprint_index ;
2021-07-10 19:45:47 +00:00
idx = new_node_idx ;
2014-08-06 21:18:21 +00:00
}
}
}
2021-07-10 19:45:47 +00:00
//check if the last node already has a value (exact url already in Trie)
if ( idx - > rule_index )
2014-08-06 21:18:21 +00:00
throw std : : runtime_error ( " handler already exists for " + url ) ;
2021-07-10 19:45:47 +00:00
idx - > rule_index = rule_index ;
2014-08-06 21:18:21 +00:00
}
2014-04-10 16:43:33 +00:00
2021-07-27 07:52:49 +00:00
size_t get_size ( bool small_enum_flag = false )
2014-08-06 21:18:21 +00:00
{
2021-07-27 07:52:49 +00:00
return get_size ( & head_ , small_enum_flag ) ;
2014-08-06 21:18:21 +00:00
}
2021-07-27 07:52:49 +00:00
size_t get_size ( Node * node , bool small_enum_flag )
2014-08-06 21:18:21 +00:00
{
2021-07-27 07:52:49 +00:00
unsigned size = small_enum_flag ? 3 : 6 ; //rule_index and param
2021-07-10 19:45:47 +00:00
size + = ( node - > key . size ( ) ) ; //each character in the key is 1 byte
for ( auto child : node - > children )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
size + = get_size ( child ) ;
2014-08-06 21:18:21 +00:00
}
2021-07-10 19:45:47 +00:00
return size ;
2014-08-06 21:18:21 +00:00
}
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
2021-07-10 19:45:47 +00:00
Node * new_node ( Node * parent )
2014-08-06 21:18:21 +00:00
{
2021-07-10 19:45:47 +00:00
auto & children = parent - > children ;
children . resize ( children . size ( ) + 1 ) ;
children [ children . size ( ) - 1 ] = new Node ( ) ;
return children [ children . size ( ) - 1 ] ;
2014-08-06 21:18:21 +00:00
}
2021-07-10 19:45:47 +00:00
Node head_ ;
2014-04-10 16:43:33 +00:00
} ;
2021-07-05 09:14:31 +00:00
/// A blueprint can be considered a smaller section of a Crow app, specifically where the router is conecerned.
class Blueprint
{
public :
Blueprint ( const std : : string & prefix ) :
prefix_ ( prefix ) { } ;
2021-07-05 22:53:32 +00:00
Blueprint ( const std : : string & prefix , const std : : string & static_dir ) :
prefix_ ( prefix ) , static_dir_ ( static_dir ) { } ;
2021-07-07 12:51:04 +00:00
Blueprint ( const std : : string & prefix , const std : : string & static_dir , const std : : string & templates_dir ) :
prefix_ ( prefix ) , static_dir_ ( static_dir ) , templates_dir_ ( templates_dir ) { } ;
2021-07-05 09:14:31 +00:00
/*
Blueprint ( Blueprint & other )
{
prefix_ = std : : move ( other . prefix_ ) ;
all_rules_ = std : : move ( other . all_rules_ ) ;
}
Blueprint ( const Blueprint & other )
{
prefix_ = other . prefix_ ;
all_rules_ = other . all_rules_ ;
}
*/
Blueprint ( Blueprint & & value )
{
* this = std : : move ( value ) ;
}
Blueprint & operator = ( const Blueprint & value ) = delete ;
Blueprint & operator = ( Blueprint & & value ) noexcept
{
prefix_ = std : : move ( value . prefix_ ) ;
all_rules_ = std : : move ( value . all_rules_ ) ;
catchall_rule_ = std : : move ( value . catchall_rule_ ) ;
return * this ;
}
bool operator = = ( const Blueprint & value )
{
return value . prefix ( ) = = prefix_ ;
}
bool operator ! = ( const Blueprint & value )
{
return value . prefix ( ) ! = prefix_ ;
}
std : : string prefix ( ) const
{
return prefix_ ;
}
2021-07-05 22:53:32 +00:00
std : : string static_dir ( ) const
{
return static_dir_ ;
}
2021-07-28 19:31:08 +00:00
DynamicRule & new_rule_dynamic ( std : : string & & rule )
{
std : : string new_rule = std : : move ( rule ) ;
new_rule = ' / ' + prefix_ + new_rule ;
2021-07-05 09:14:31 +00:00
auto ruleObject = new DynamicRule ( new_rule ) ;
2021-07-07 12:51:04 +00:00
ruleObject - > custom_templates_base = templates_dir_ ;
2021-07-05 09:14:31 +00:00
all_rules_ . emplace_back ( ruleObject ) ;
return * ruleObject ;
}
template < uint64_t N >
2021-07-28 19:31:08 +00:00
typename black_magic : : arguments < N > : : type : : template rebind < TaggedRule > & new_rule_tagged ( std : : string & & rule )
2021-07-05 09:14:31 +00:00
{
2021-07-28 19:31:08 +00:00
std : : string new_rule = std : : move ( rule ) ;
new_rule = ' / ' + prefix_ + new_rule ;
2021-07-05 09:14:31 +00:00
using RuleT = typename black_magic : : arguments < N > : : type : : template rebind < TaggedRule > ;
auto ruleObject = new RuleT ( new_rule ) ;
2021-07-07 12:51:04 +00:00
ruleObject - > custom_templates_base = templates_dir_ ;
2021-07-05 09:14:31 +00:00
all_rules_ . emplace_back ( ruleObject ) ;
return * ruleObject ;
}
2021-07-27 07:52:49 +00:00
void register_blueprint ( Blueprint & blueprint )
{
if ( blueprints_ . empty ( ) | | std : : find ( blueprints_ . begin ( ) , blueprints_ . end ( ) , & blueprint ) = = blueprints_ . end ( ) )
{
2021-07-28 19:31:08 +00:00
apply_blueprint ( blueprint ) ;
2021-07-27 07:52:49 +00:00
blueprints_ . emplace_back ( & blueprint ) ;
2021-07-28 19:31:08 +00:00
}
else
throw std : : runtime_error ( " blueprint \" " + blueprint . prefix_ + " \" already exists in blueprint \" " + prefix_ + ' \" ' ) ;
}
void apply_blueprint ( Blueprint & blueprint )
{
blueprint . prefix_ = prefix_ + ' / ' + blueprint . prefix_ ;
blueprint . static_dir_ = static_dir_ + ' / ' + blueprint . static_dir_ ;
blueprint . templates_dir_ = templates_dir_ + ' / ' + blueprint . templates_dir_ ;
for ( auto & rule : blueprint . all_rules_ )
{
std : : string new_rule = ' / ' + prefix_ + rule - > rule_ ;
rule - > rule_ = new_rule ;
}
for ( Blueprint * bp_child : blueprint . blueprints_ )
{
Blueprint & bp_ref = * bp_child ;
apply_blueprint ( bp_ref ) ;
}
2021-07-27 07:52:49 +00:00
}
2021-07-05 09:14:31 +00:00
CatchallRule & catchall_rule ( )
{
return catchall_rule_ ;
}
private :
std : : string prefix_ ;
2021-07-05 22:53:32 +00:00
std : : string static_dir_ ;
2021-07-07 12:51:04 +00:00
std : : string templates_dir_ ;
2021-07-05 09:14:31 +00:00
std : : vector < std : : unique_ptr < BaseRule > > all_rules_ ;
CatchallRule catchall_rule_ ;
2021-07-27 07:52:49 +00:00
std : : vector < Blueprint * > blueprints_ ;
2021-07-05 09:14:31 +00:00
friend class Router ;
} ;
2021-04-12 05:25:09 +00:00
2020-11-18 22:13:57 +00:00
/// Handles matching requests to existing rules and upgrade requests.
2014-04-02 16:38:08 +00:00
class Router
{
public :
2017-12-24 15:40:39 +00:00
Router ( )
2015-01-19 10:03:06 +00:00
{
}
2015-02-18 15:57:01 +00:00
DynamicRule & new_rule_dynamic ( const std : : string & rule )
{
auto ruleObject = new DynamicRule ( rule ) ;
2017-12-24 15:40:39 +00:00
all_rules_ . emplace_back ( ruleObject ) ;
2015-02-18 15:57:01 +00:00
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 > ;
2015-01-19 10:03:06 +00:00
2017-12-24 15:40:39 +00:00
auto ruleObject = new RuleT ( rule ) ;
all_rules_ . emplace_back ( ruleObject ) ;
2015-02-18 15:57:01 +00:00
return * ruleObject ;
}
2021-04-12 07:41:55 +00:00
CatchallRule & catchall_rule ( )
2021-04-12 05:25:09 +00:00
{
return catchall_rule_ ;
}
2021-07-27 07:52:49 +00:00
void internal_add_rule_object ( const std : : string & rule , BaseRule * ruleObject , const uint16_t & BP_index , std : : vector < Blueprint * > & blueprints )
2015-02-18 15:57:01 +00:00
{
2017-12-24 15:40:39 +00:00
bool has_trailing_slash = false ;
std : : string rule_without_trailing_slash ;
2015-01-19 10:03:06 +00:00
if ( rule . size ( ) > 1 & & rule . back ( ) = = ' / ' )
{
2017-12-24 15:40:39 +00:00
has_trailing_slash = true ;
rule_without_trailing_slash = rule ;
2015-01-19 10:03:06 +00:00
rule_without_trailing_slash . pop_back ( ) ;
}
2017-12-24 15:40:39 +00:00
ruleObject - > foreach_method ( [ & ] ( int method )
{
per_methods_ [ method ] . rules . emplace_back ( ruleObject ) ;
2021-07-27 07:52:49 +00:00
per_methods_ [ method ] . trie . add ( rule , per_methods_ [ method ] . rules . size ( ) - 1 , BP_index ! = 0xFFFF ? blueprints [ BP_index ] - > prefix ( ) . length ( ) : 0 , BP_index ) ;
2017-12-24 15:40:39 +00:00
2020-10-14 21:09:35 +00:00
// directory case:
2020-11-18 22:13:57 +00:00
// request to '/about' url matches '/about/' rule
2017-12-24 15:40:39 +00:00
if ( has_trailing_slash )
{
2021-07-27 07:52:49 +00:00
per_methods_ [ method ] . trie . add ( rule_without_trailing_slash , RULE_SPECIAL_REDIRECT_SLASH , BP_index ! = 0xFFFF ? blueprints_ [ BP_index ] - > prefix ( ) . length ( ) : 0 , BP_index ) ;
2017-12-24 15:40:39 +00:00
}
} ) ;
2014-04-13 02:24:06 +00:00
}
2021-07-27 07:52:49 +00:00
void register_blueprint ( Blueprint & blueprint )
2014-08-06 21:18:21 +00:00
{
2021-07-27 07:52:49 +00:00
if ( blueprints_ . empty ( ) | | std : : find ( blueprints_ . begin ( ) , blueprints_ . end ( ) , & blueprint ) = = blueprints_ . end ( ) )
{
blueprints_ . emplace_back ( & blueprint ) ;
2021-07-28 19:31:08 +00:00
}
else
throw std : : runtime_error ( " blueprint \" " + blueprint . prefix_ + " \" already exists in router " ) ;
2021-07-27 07:52:49 +00:00
}
void get_recursive_child_methods ( Blueprint * blueprint , std : : vector < HTTPMethod > & methods )
{
//we only need to deal with children if the blueprint has absolutely no methods (meaning its index won't be added to the trie)
if ( blueprint - > static_dir_ = = " " & & blueprint - > all_rules_ . empty ( ) )
2021-07-05 09:14:31 +00:00
{
2021-07-27 07:52:49 +00:00
for ( Blueprint * bp : blueprint - > blueprints_ )
2021-07-05 09:14:31 +00:00
{
2021-07-27 07:52:49 +00:00
get_recursive_child_methods ( bp , methods ) ;
}
}
else if ( blueprint - > static_dir_ ! = " " )
methods . emplace_back ( HTTPMethod : : GET ) ;
for ( auto & rule : blueprint - > all_rules_ )
{
rule - > foreach_method ( [ & methods ] ( unsigned method ) {
HTTPMethod method_final = static_cast < HTTPMethod > ( method ) ;
if ( std : : find ( methods . begin ( ) , methods . end ( ) , method_final ) = = methods . end ( ) )
methods . emplace_back ( method_final ) ;
} ) ;
}
}
void validate_bp ( std : : vector < Blueprint * > blueprints )
{
for ( unsigned i = 0 ; i < blueprints . size ( ) ; i + + )
{
Blueprint * blueprint = blueprints [ i ] ;
if ( blueprint - > static_dir_ = = " " & & blueprint - > all_rules_ . empty ( ) )
{
std : : vector < HTTPMethod > methods ;
get_recursive_child_methods ( blueprint , methods ) ;
for ( HTTPMethod x : methods )
2021-07-05 09:14:31 +00:00
{
2021-07-27 07:52:49 +00:00
int i = static_cast < int > ( x ) ;
per_methods_ [ i ] . trie . add ( blueprint - > prefix ( ) , 0 , blueprint - > prefix ( ) . length ( ) , i ) ;
2021-07-05 09:14:31 +00:00
}
2021-07-27 07:52:49 +00:00
}
for ( auto & rule : blueprint - > all_rules_ )
{
if ( rule )
{
auto upgraded = rule - > upgrade ( ) ;
if ( upgraded )
rule = std : : move ( upgraded ) ;
rule - > validate ( ) ;
internal_add_rule_object ( rule - > rule ( ) , rule . get ( ) , i , blueprints ) ;
}
}
validate_bp ( blueprint - > blueprints_ ) ;
2021-07-05 09:14:31 +00:00
}
2021-07-27 07:52:49 +00:00
}
void validate ( )
{
//Take all the routes from the registered blueprints and add them to `all_rules_` to be processed.
validate_bp ( blueprints_ ) ;
2021-07-05 09:14:31 +00:00
2017-12-24 15:40:39 +00:00
for ( auto & rule : all_rules_ )
2014-08-06 21:18:21 +00:00
{
if ( rule )
2017-12-24 15:40:39 +00:00
{
auto upgraded = rule - > upgrade ( ) ;
if ( upgraded )
rule = std : : move ( upgraded ) ;
2014-08-06 21:18:21 +00:00
rule - > validate ( ) ;
2021-07-27 07:52:49 +00:00
internal_add_rule_object ( rule - > rule ( ) , rule . get ( ) , 0xFFFF , blueprints_ ) ;
2017-12-24 15:40:39 +00:00
}
}
for ( auto & per_method : per_methods_ )
{
per_method . trie . validate ( ) ;
2014-08-06 21:18:21 +00:00
}
}
2014-04-10 16:43:33 +00:00
2021-04-03 02:33:40 +00:00
//TODO maybe add actual_method
2020-10-14 21:09:35 +00:00
template < typename Adaptor >
2017-12-24 15:40:39 +00:00
void handle_upgrade ( const request & req , response & res , Adaptor & & adaptor )
{
if ( req . method > = HTTPMethod : : InternalMethodCount )
return ;
2020-10-15 10:59:15 +00:00
2021-02-21 00:14:30 +00:00
auto & per_method = per_methods_ [ static_cast < int > ( req . method ) ] ;
2017-12-24 15:40:39 +00:00
auto & rules = per_method . rules ;
2021-07-27 07:52:49 +00:00
unsigned rule_index = std : : get < 0 > ( per_method . trie . find ( req . url ) ) ;
2017-12-24 15:40:39 +00:00
2016-08-28 05:46:31 +00:00
if ( ! rule_index )
{
2020-10-14 21:09:17 +00:00
for ( auto & per_method : per_methods_ )
{
2021-07-27 07:52:49 +00:00
if ( std : : get < 0 > ( per_method . trie . find ( req . url ) ) )
2020-10-14 21:09:17 +00:00
{
2020-10-15 10:59:15 +00:00
CROW_LOG_DEBUG < < " Cannot match method " < < req . url < < " " < < method_name ( req . method ) ;
2020-10-14 21:09:17 +00:00
res = response ( 405 ) ;
res . end ( ) ;
return ;
}
}
2020-10-15 10:59:15 +00:00
CROW_LOG_INFO < < " Cannot match rules " < < req . url ;
2016-08-28 05:46:31 +00:00
res = response ( 404 ) ;
res . end ( ) ;
return ;
}
2017-12-24 15:40:39 +00:00
if ( rule_index > = rules . size ( ) )
2016-08-28 05:46:31 +00:00
throw std : : runtime_error ( " Trie internal structure corrupted! " ) ;
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 ;
}
2021-02-21 00:14:30 +00:00
CROW_LOG_DEBUG < < " Matched rule (upgrade) ' " < < rules [ rule_index ] - > rule_ < < " ' " < < static_cast < uint32_t > ( req . method ) < < " / " < < rules [ rule_index ] - > get_methods ( ) ;
2016-08-28 05:46:31 +00:00
// any uncaught exceptions become 500s
try
{
2017-12-24 15:40:39 +00:00
rules [ rule_index ] - > handle_upgrade ( req , res , std : : move ( adaptor ) ) ;
2016-08-28 05:46:31 +00:00
}
catch ( std : : exception & e )
{
CROW_LOG_ERROR < < " An uncaught exception occurred: " < < e . what ( ) ;
res = response ( 500 ) ;
res . end ( ) ;
2020-10-14 21:09:35 +00:00
return ;
2016-08-28 05:46:31 +00:00
}
catch ( . . . )
{
CROW_LOG_ERROR < < " An uncaught exception occurred. The type was unknown so no information was available. " ;
res = response ( 500 ) ;
res . end ( ) ;
2020-10-14 21:09:35 +00:00
return ;
2016-08-28 05:46:31 +00:00
}
2017-12-24 15:40:39 +00:00
}
2016-08-28 05:46:31 +00:00
2021-07-28 19:31:08 +00:00
void get_found_bp ( std : : vector < uint16_t > & bp_i , std : : vector < Blueprint * > & blueprints , std : : vector < Blueprint * > & found_bps , uint16_t index = 0 )
2021-07-27 07:52:49 +00:00
{
2021-07-28 19:31:08 +00:00
if ( index < bp_i . size ( ) )
{
// This statement makes 3 assertions:
// 1. The index is above 0.
// 2. The index does not lie outside the given blueprint list.
// 3. The next blueprint we're adding has a prefix that starts the same as the already added blueprint + a slash (the rest is irrelevant).
//
// This is done to prevent a blueprint that has a prefix of "bp_prefix2" to be assumed as a child of one that has "bp_prefix".
//
// If any of the assertions is untrue, we delete the last item added, and continue using the blueprint list of the blueprint found before, the topmost being the router's list
if ( index > 0 & & bp_i [ index ] < blueprints . size ( ) & & blueprints [ bp_i [ index ] ] - > prefix ( ) . substr ( 0 , found_bps [ index - 1 ] - > prefix ( ) . length ( ) + 1 ) . compare ( std : : string ( found_bps [ index - 1 ] - > prefix ( ) + ' / ' ) ) = = 0 )
{
found_bps . push_back ( blueprints [ bp_i [ index ] ] ) ;
get_found_bp ( bp_i , found_bps . back ( ) - > blueprints_ , found_bps , + + index ) ;
}
else
{
if ( ! found_bps . empty ( ) )
found_bps . pop_back ( ) ;
2021-07-27 07:52:49 +00:00
2021-07-28 19:31:08 +00:00
if ( found_bps . empty ( ) )
{
found_bps . push_back ( blueprints_ [ bp_i [ index ] ] ) ;
get_found_bp ( bp_i , found_bps . back ( ) - > blueprints_ , found_bps , + + index ) ;
}
else
{
Blueprint * last_element = found_bps . back ( ) ;
found_bps . push_back ( last_element - > blueprints_ [ bp_i [ index ] ] ) ;
get_found_bp ( bp_i , found_bps . back ( ) - > blueprints_ , found_bps , + + index ) ;
}
}
}
2021-07-27 07:52:49 +00:00
}
2014-08-06 21:18:21 +00:00
void handle ( const request & req , response & res )
{
2021-03-13 10:51:27 +00:00
HTTPMethod method_actual = req . method ;
2017-12-24 15:40:39 +00:00
if ( req . method > = HTTPMethod : : InternalMethodCount )
return ;
2021-05-23 07:58:01 +00:00
else if ( req . method = = HTTPMethod : : Head )
2021-03-13 10:51:27 +00:00
{
2021-05-23 07:58:01 +00:00
method_actual = HTTPMethod : : Get ;
2021-04-03 10:48:36 +00:00
res . is_head_response = true ;
2021-03-13 10:51:27 +00:00
}
2021-05-23 07:58:01 +00:00
else if ( req . method = = HTTPMethod : : Options )
2021-04-03 02:33:40 +00:00
{
std : : string allow = " OPTIONS, HEAD, " ;
2021-03-13 10:51:27 +00:00
2021-04-03 02:33:40 +00:00
if ( req . url = = " /* " )
{
2021-04-03 03:31:45 +00:00
for ( int i = 0 ; i < static_cast < int > ( HTTPMethod : : InternalMethodCount ) ; i + + )
2021-04-03 02:33:40 +00:00
{
2021-07-10 19:45:47 +00:00
if ( ! per_methods_ [ i ] . trie . is_empty ( ) )
2021-04-03 02:33:40 +00:00
{
2021-04-03 03:31:45 +00:00
allow + = method_name ( static_cast < HTTPMethod > ( i ) ) + " , " ;
2021-04-03 02:33:40 +00:00
}
}
allow = allow . substr ( 0 , allow . size ( ) - 2 ) ;
res = response ( 204 ) ;
res . set_header ( " Allow " , allow ) ;
2021-04-03 10:48:36 +00:00
res . manual_length_header = true ;
2021-04-03 02:33:40 +00:00
res . end ( ) ;
return ;
}
else
{
2021-04-03 03:31:45 +00:00
for ( int i = 0 ; i < static_cast < int > ( HTTPMethod : : InternalMethodCount ) ; i + + )
2021-04-03 02:33:40 +00:00
{
2021-07-27 07:52:49 +00:00
if ( std : : get < 0 > ( per_methods_ [ i ] . trie . find ( req . url ) ) )
2021-04-03 02:33:40 +00:00
{
2021-04-03 03:31:45 +00:00
allow + = method_name ( static_cast < HTTPMethod > ( i ) ) + " , " ;
2021-04-03 02:33:40 +00:00
}
}
2021-04-03 03:00:23 +00:00
if ( allow ! = " OPTIONS, HEAD, " )
2021-04-03 02:33:40 +00:00
{
allow = allow . substr ( 0 , allow . size ( ) - 2 ) ;
res = response ( 204 ) ;
res . set_header ( " Allow " , allow ) ;
2021-04-03 10:48:36 +00:00
res . manual_length_header = true ;
2021-04-03 02:33:40 +00:00
res . end ( ) ;
return ;
}
else
{
CROW_LOG_DEBUG < < " Cannot match rules " < < req . url ;
res = response ( 404 ) ;
res . end ( ) ;
return ;
}
}
}
2021-04-03 03:40:14 +00:00
auto & per_method = per_methods_ [ static_cast < int > ( method_actual ) ] ;
2017-12-24 15:40:39 +00:00
auto & trie = per_method . trie ;
auto & rules = per_method . rules ;
auto found = trie . find ( req . url ) ;
2014-08-06 21:18:21 +00:00
2021-07-27 07:52:49 +00:00
unsigned rule_index = std : : get < 0 > ( found ) ;
2014-08-06 21:18:21 +00:00
if ( ! rule_index )
{
2020-10-14 21:09:17 +00:00
for ( auto & per_method : per_methods_ )
{
2021-07-27 07:52:49 +00:00
if ( std : : get < 0 > ( per_method . trie . find ( req . url ) ) )
2020-10-14 21:09:17 +00:00
{
2021-04-03 02:33:40 +00:00
CROW_LOG_DEBUG < < " Cannot match method " < < req . url < < " " < < method_name ( method_actual ) ;
2020-10-14 21:09:17 +00:00
res = response ( 405 ) ;
res . end ( ) ;
return ;
}
}
2021-04-12 05:25:09 +00:00
2021-07-28 19:31:08 +00:00
std : : vector < Blueprint * > bps_found ;
get_found_bp ( std : : get < 1 > ( found ) , blueprints_ , bps_found ) ;
bool no_bp_catchall = true ;
for ( int i = bps_found . size ( ) - 1 ; i > 0 ; i - - )
2021-07-27 07:52:49 +00:00
{
2021-07-28 19:31:08 +00:00
std : : vector < uint16_t > bpi = std : : get < 1 > ( found ) ;
if ( bps_found [ i ] - > catchall_rule ( ) . has_handler ( ) )
{
no_bp_catchall = false ;
CROW_LOG_DEBUG < < " Cannot match rules " < < req . url < < " . Redirecting to Blueprint \" " < < bps_found [ i ] - > prefix ( ) < < " \" Catchall rule " ;
bps_found [ i ] - > catchall_rule ( ) . handler_ ( req , res ) ;
break ;
}
2021-04-05 07:44:35 +00:00
}
2021-07-28 19:31:08 +00:00
if ( no_bp_catchall )
2021-04-05 07:44:35 +00:00
{
2021-07-28 19:31:08 +00:00
if ( catchall_rule_ . has_handler ( ) )
{
CROW_LOG_DEBUG < < " Cannot match rules " < < req . url < < " . Redirecting to global Catchall rule " ;
catchall_rule_ . handler_ ( req , res ) ;
}
else
{
CROW_LOG_DEBUG < < " Cannot match rules " < < req . url ;
res = response ( 404 ) ;
}
2021-04-05 07:44:35 +00:00
}
2014-12-11 16:38:57 +00:00
res . end ( ) ;
2014-08-06 21:18:21 +00:00
return ;
}
2017-12-24 15:40:39 +00:00
if ( rule_index > = rules . size ( ) )
2014-08-06 21:18:21 +00:00
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 ;
}
2021-02-21 00:14:30 +00:00
CROW_LOG_DEBUG < < " Matched rule ' " < < rules [ rule_index ] - > rule_ < < " ' " < < static_cast < 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
{
2021-07-27 07:52:49 +00:00
rules [ rule_index ] - > handle ( req , res , std : : get < 2 > ( found ) ) ;
2014-12-10 17:24:13 +00:00
}
catch ( std : : exception & e )
{
CROW_LOG_ERROR < < " An uncaught exception occurred: " < < e . what ( ) ;
res = response ( 500 ) ;
res . end ( ) ;
2020-10-14 21:09:35 +00:00
return ;
2014-12-10 17:24:13 +00:00
}
catch ( . . . )
{
CROW_LOG_ERROR < < " An uncaught exception occurred. The type was unknown so no information was available. " ;
res = response ( 500 ) ;
res . end ( ) ;
2020-10-14 21:09:35 +00:00
return ;
2014-12-10 17:24:13 +00:00
}
2014-08-06 21:18:21 +00:00
}
void debug_print ( )
{
2021-02-21 00:14:30 +00:00
for ( int i = 0 ; i < static_cast < int > ( HTTPMethod : : InternalMethodCount ) ; i + + )
2017-12-24 15:40:39 +00:00
{
2021-02-21 00:14:30 +00:00
CROW_LOG_DEBUG < < method_name ( static_cast < HTTPMethod > ( i ) ) ;
2017-12-24 15:40:39 +00:00
per_methods_ [ i ] . trie . debug_print ( ) ;
}
2014-08-06 21:18:21 +00:00
}
2014-04-15 17:57:18 +00:00
2021-07-27 07:52:49 +00:00
std : : vector < Blueprint * > & blueprints ( )
{
return blueprints_ ;
}
2014-04-02 20:31:32 +00:00
private :
2021-04-12 07:41:55 +00:00
CatchallRule catchall_rule_ ;
2021-04-12 05:25:09 +00:00
2017-12-24 15:40:39 +00:00
struct PerMethod
{
std : : vector < BaseRule * > rules ;
Trie trie ;
// rule index 0, 1 has special meaning; preallocate it to avoid duplication.
PerMethod ( ) : rules ( 2 ) { }
} ;
2021-02-21 00:14:30 +00:00
std : : array < PerMethod , static_cast < int > ( HTTPMethod : : InternalMethodCount ) > per_methods_ ;
2017-12-24 15:40:39 +00:00
std : : vector < std : : unique_ptr < BaseRule > > all_rules_ ;
2021-07-27 07:52:49 +00:00
std : : vector < Blueprint * > blueprints_ ;
2021-04-05 07:44:35 +00:00
2014-04-02 16:38:08 +00:00
} ;
}