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"
2022-02-01 13:31:05 +00:00
# include "crow/middleware.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
{
2021-08-04 20:58:38 +00:00
2022-02-05 15:15:19 +00:00
constexpr const uint16_t INVALID_BP_ID { ( ( uint16_t ) - 1 ) } ;
2021-11-21 15:57:07 +00:00
2020-11-18 22:13:57 +00:00
/// A base class for all rules.
2021-12-03 03:39:23 +00:00
2021-11-25 11:45:38 +00:00
///
/// Used to provide a common interface for code dealing with different types of rules.<br>
2020-11-18 22:13:57 +00:00
/// 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 :
2021-11-25 11:45:38 +00:00
BaseRule ( std : : string rule ) :
rule_ ( std : : move ( rule ) )
2021-11-27 12:28:50 +00:00
{ }
2015-02-18 15:57:01 +00:00
2014-04-13 02:24:06 +00:00
virtual ~ BaseRule ( )
2021-11-27 12:28:50 +00:00
{ }
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
2022-02-01 13:31:05 +00:00
virtual void handle ( 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 ( ) ;
}
2021-08-29 17:12:46 +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
2021-11-25 11:45:38 +00:00
template < typename F >
2017-12-24 15:40:39 +00:00
void foreach_method ( F f )
{
2021-11-25 11:45:38 +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-11-25 11:45:38 +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 ;
2021-11-25 11:45:38 +00:00
template < typename T >
2015-02-20 02:58:41 +00:00
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
{
2021-11-25 11:45:38 +00:00
template < typename T , int Pos >
2015-02-18 15:57:01 +00:00
struct call_pair
{
using type = T ;
static const int pos = Pos ;
} ;
2014-04-14 15:31:51 +00:00
2021-11-25 11:45:38 +00:00
template < typename H1 >
2015-02-18 15:57:01 +00:00
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 ;
2022-02-01 13:31:05 +00:00
request & req ;
2015-02-18 15:57:01 +00:00
response & res ;
} ;
2014-04-14 15:31:51 +00:00
2021-11-25 11:45:38 +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
2021-11-27 12:28:50 +00:00
{ } ;
2014-04-15 13:46:28 +00:00
2021-11-25 11:45:38 +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 > > ;
2021-11-25 11:45:38 +00:00
call < F , NInt + 1 , NUint , NDouble , NString , black_magic : : S < Args1 . . . > , pushed > ( ) ( cparams ) ;
2015-02-18 15:57:01 +00:00
}
} ;
2014-04-15 13:46:28 +00:00
2021-11-25 11:45:38 +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 > > ;
2021-11-25 11:45:38 +00:00
call < F , NInt , NUint + 1 , NDouble , NString , black_magic : : S < Args1 . . . > , pushed > ( ) ( cparams ) ;
2015-02-18 15:57:01 +00:00
}
} ;
2014-04-15 13:46:28 +00:00
2021-11-25 11:45:38 +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 > > ;
2021-11-25 11:45:38 +00:00
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
} ;
2021-11-25 11:45:38 +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 > > ;
2021-11-25 11:45:38 +00:00
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
} ;
2021-11-25 11:45:38 +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 (
2021-11-25 11:45:38 +00:00
cparams . req ,
cparams . res ,
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
} ;
2021-11-25 11:45:38 +00:00
template < typename Func , typename . . . ArgsWrapped >
2015-02-18 15:57:01 +00:00
struct Wrapped
{
2021-11-25 11:45:38 +00:00
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 )
2015-02-18 15:57:01 +00:00
{
handler_ = (
2015-02-20 02:58:41 +00:00
# ifdef CROW_CAN_USE_CPP14
2021-11-25 11:45:38 +00:00
[ f = std : : move ( f ) ]
2015-02-20 02:58:41 +00:00
# else
2021-11-25 11:45:38 +00:00
[ f ]
2015-02-20 02:58:41 +00:00
# endif
2021-11-27 12:28:50 +00:00
( const request & , response & res , Args . . . args ) {
2021-11-25 11:45:38 +00:00
res = response ( f ( args . . . ) ) ;
res . end ( ) ;
} ) ;
2015-02-18 15:57:01 +00:00
}
2021-11-25 11:45:38 +00:00
template < typename Req , typename . . . Args >
2015-02-18 15:57:01 +00:00
struct req_handler_wrapper
{
2021-11-25 11:45:38 +00:00
req_handler_wrapper ( Func f ) :
f ( std : : move ( f ) )
2015-02-18 15:57:01 +00:00
{
}
void operator ( ) ( const request & req , response & res , Args . . . args )
{
res = response ( f ( req , args . . . ) ) ;
res . end ( ) ;
}
Func f ;
} ;
2021-11-25 11:45:38 +00:00
template < typename . . . Args >
2016-12-06 15:22:10 +00:00
void set_ ( Func f , typename std : : enable_if <
2021-11-25 11:45:38 +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 )
2015-02-18 15:57:01 +00:00
{
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 ( ) ;
} ) ; */
}
2021-11-25 11:45:38 +00:00
template < typename . . . Args >
2016-12-06 15:22:10 +00:00
void set_ ( Func f , typename std : : enable_if <
2021-11-25 11:45:38 +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 )
2015-02-18 15:57:01 +00:00
{
handler_ = std : : move ( f ) ;
}
2021-11-25 11:45:38 +00:00
template < typename . . . Args >
2015-02-18 15:57:01 +00:00
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
2021-11-25 11:45:38 +00:00
template < typename . . . Args >
2015-02-18 15:57:01 +00:00
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
} ;
2021-11-25 11:45:38 +00:00
template < typename . . . Args >
2015-02-18 15:57:01 +00:00
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_ ;
2022-02-01 13:31:05 +00:00
void operator ( ) ( request & req , response & res , const routing_params & params )
2015-02-18 15:57:01 +00:00
{
detail : : routing_handler_call_helper : : call <
2021-11-25 11:45:38 +00:00
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-18 15:57:01 +00:00
}
} ;
2021-11-25 11:45:38 +00:00
} // namespace routing_handler_call_helper
} // namespace detail
2015-02-18 15:57:01 +00:00
2021-04-12 05:25:09 +00:00
class CatchallRule
{
public :
2022-03-23 22:55:46 +00:00
/// @cond SKIP
2021-11-25 11:45:38 +00:00
CatchallRule ( ) { }
2021-04-12 05:25:09 +00:00
2021-11-25 11:45:38 +00:00
template < typename Func >
2021-04-12 05:25:09 +00:00
typename std : : enable_if < black_magic : : CallHelper < Func , black_magic : : S < > > : : value , void > : : type
2021-11-25 11:45:38 +00:00
operator ( ) ( Func & & f )
2021-04-12 05:25:09 +00:00
{
static_assert ( ! std : : is_same < void , decltype ( f ( ) ) > : : value ,
2021-11-25 11:45:38 +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
2021-11-25 11:45:38 +00:00
[ f = std : : move ( f ) ]
2021-04-12 05:25:09 +00:00
# else
2021-11-25 11:45:38 +00:00
[ f ]
2021-04-12 05:25:09 +00:00
# endif
2021-11-27 12:28:50 +00:00
( const request & , response & res ) {
2021-11-25 11:45:38 +00:00
res = response ( f ( ) ) ;
res . end ( ) ;
} ) ;
2021-04-12 05:25:09 +00:00
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2021-04-12 05:25:09 +00:00
typename std : : enable_if <
2021-11-25 11:45:38 +00:00
! black_magic : : CallHelper < Func , black_magic : : S < > > : : value & &
2021-04-12 05:25:09 +00:00
black_magic : : CallHelper < Func , black_magic : : S < crow : : request > > : : value ,
2021-11-25 11:45:38 +00:00
void > : : type
operator ( ) ( Func & & f )
2021-04-12 05:25:09 +00:00
{
static_assert ( ! std : : is_same < void , decltype ( f ( std : : declval < crow : : request > ( ) ) ) > : : value ,
2021-11-25 11:45:38 +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
2021-11-25 11:45:38 +00:00
[ f = std : : move ( f ) ]
2021-04-12 05:25:09 +00:00
# else
2021-11-25 11:45:38 +00:00
[ f ]
2021-04-12 05:25:09 +00:00
# endif
2021-11-27 12:28:50 +00:00
( const crow : : request & req , crow : : response & res ) {
2021-11-25 11:45:38 +00:00
res = response ( f ( req ) ) ;
res . end ( ) ;
} ) ;
2021-04-12 05:25:09 +00:00
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2021-04-12 05:25:09 +00:00
typename std : : enable_if <
2021-11-25 11:45:38 +00:00
! black_magic : : CallHelper < Func , black_magic : : S < > > : : value & &
2021-04-12 05:25:09 +00:00
! black_magic : : CallHelper < Func , black_magic : : S < crow : : request > > : : value & &
black_magic : : CallHelper < Func , black_magic : : S < crow : : response & > > : : value ,
2021-11-25 11:45:38 +00:00
void > : : type
operator ( ) ( Func & & f )
2021-04-12 05:25:09 +00:00
{
2021-11-25 11:45:38 +00:00
static_assert ( std : : is_same < void , decltype ( f ( std : : declval < crow : : response & > ( ) ) ) > : : value ,
" Handler function with response argument should have void return type " ) ;
handler_ = (
2021-04-12 05:25:09 +00:00
# ifdef CROW_CAN_USE_CPP14
2021-11-25 11:45:38 +00:00
[ f = std : : move ( f ) ]
2021-04-12 05:25:09 +00:00
# else
2021-11-25 11:45:38 +00:00
[ f ]
2021-04-12 05:25:09 +00:00
# endif
2021-11-27 12:28:50 +00:00
( const crow : : request & , crow : : response & res ) {
2021-04-12 05:25:09 +00:00
f ( res ) ;
2021-11-25 11:45:38 +00:00
} ) ;
2021-04-12 05:25:09 +00:00
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2021-04-12 05:25:09 +00:00
typename std : : enable_if <
2021-11-25 11:45:38 +00:00
! black_magic : : CallHelper < Func , black_magic : : S < > > : : value & &
2021-04-12 05:25:09 +00:00
! black_magic : : CallHelper < Func , black_magic : : S < crow : : request > > : : value & &
! black_magic : : CallHelper < Func , black_magic : : S < crow : : response & > > : : value ,
2021-11-25 11:45:38 +00:00
void > : : type
operator ( ) ( Func & & f )
2021-04-12 05:25:09 +00:00
{
static_assert ( std : : is_same < void , decltype ( f ( std : : declval < crow : : request > ( ) , std : : declval < crow : : response & > ( ) ) ) > : : value ,
2021-11-25 11:45:38 +00:00
" Handler function with response argument should have void return type " ) ;
2021-04-12 05:25:09 +00:00
2021-11-25 11:45:38 +00:00
handler_ = std : : move ( f ) ;
2021-04-12 05:25:09 +00:00
}
2022-03-23 22:55:46 +00:00
/// @endcond
2021-04-12 05:25:09 +00:00
bool has_handler ( )
{
return ( handler_ ! = nullptr ) ;
}
protected :
friend class Router ;
2021-11-25 11:45:38 +00:00
2021-04-12 05:25:09 +00:00
private :
std : : function < void ( const crow : : request & , crow : : response & ) > handler_ ;
} ;
2020-11-18 22:13:57 +00:00
/// A rule dealing with websockets.
2021-12-03 03:39:23 +00:00
2020-11-18 22:13:57 +00:00
///
2021-11-25 11:45:38 +00:00
/// Provides the interface for the user to put in the necessary handlers for a websocket to work.
2022-05-06 17:02:43 +00:00
template < typename App >
2017-12-24 15:40:39 +00:00
class WebSocketRule : public BaseRule
{
2016-08-28 05:46:31 +00:00
using self_t = WebSocketRule ;
2021-11-25 11:45:38 +00:00
2017-12-24 15:40:39 +00:00
public :
2022-05-06 17:02:43 +00:00
WebSocketRule ( std : : string rule , App * app ) :
BaseRule ( std : : move ( rule ) ) ,
2022-05-15 01:46:49 +00:00
app_ ( app ) ,
2022-05-15 02:10:05 +00:00
max_payload_ ( app_ - > websocket_max_payload ( ) )
2021-11-27 12:28:50 +00:00
{ }
2016-08-28 05:46:31 +00:00
2017-12-24 15:40:39 +00:00
void validate ( ) override
2021-11-27 12:28:50 +00:00
{ }
2016-08-28 05:46:31 +00:00
2022-02-01 13:31:05 +00:00
void handle ( request & , response & res , const routing_params & ) override
2017-12-24 15:40:39 +00:00
{
res = response ( 404 ) ;
res . end ( ) ;
}
2021-08-29 17:12:46 +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
{
2022-05-15 00:51:51 +00:00
auto * conn = new crow : : websocket : : Connection < SocketAdaptor , App > ( req , std : : move ( adaptor ) , app_ , open_handler_ , message_handler_ , close_handler_ , error_handler_ , accept_handler_ ) ;
2022-05-15 00:54:21 +00:00
conn - > set_max_payload_size ( max_payload_ ) ;
2017-12-24 15:40:39 +00:00
}
2021-08-29 17:12:46 +00:00
# ifdef CROW_ENABLE_SSL
2016-08-28 05:46:31 +00:00
void handle_upgrade ( const request & req , response & , SSLAdaptor & & adaptor ) override
2017-12-24 15:40:39 +00:00
{
2022-05-06 17:02:43 +00:00
new crow : : websocket : : Connection < SSLAdaptor , App > ( req , std : : move ( adaptor ) , app_ , open_handler_ , message_handler_ , close_handler_ , error_handler_ , accept_handler_ ) ;
2017-12-24 15:40:39 +00:00
}
2016-08-28 05:46:31 +00:00
# endif
2022-05-15 00:51:51 +00:00
self_t & maxpayload ( uint64_t payload )
{
max_payload_ = payload ;
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2017-12-24 15:40:39 +00:00
self_t & onopen ( Func f )
{
open_handler_ = f ;
return * this ;
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2017-12-24 15:40:39 +00:00
self_t & onmessage ( Func f )
{
message_handler_ = f ;
return * this ;
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2017-12-24 15:40:39 +00:00
self_t & onclose ( Func f )
{
close_handler_ = f ;
return * this ;
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2017-12-24 15:40:39 +00:00
self_t & onerror ( Func f )
{
error_handler_ = f ;
return * this ;
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2017-12-24 15:40:39 +00:00
self_t & onaccept ( Func f )
{
accept_handler_ = f ;
return * this ;
}
protected :
2022-05-06 17:02:43 +00:00
App * app_ ;
2017-12-24 15:40:39 +00:00
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_ ;
2022-05-15 00:51:51 +00:00
uint64_t max_payload_ ;
2017-12-24 15:40:39 +00:00
} ;
2016-08-28 05:46:31 +00:00
2020-11-18 22:13:57 +00:00
/// Allows the user to assign parameters using functions.
2021-12-03 03:39:23 +00:00
2020-11-18 22:13:57 +00:00
///
/// `rule.name("name").methods(HTTPMethod::POST)`
2021-11-25 11:45:38 +00:00
template < typename T >
2015-02-20 02:58:41 +00:00
struct RuleParameterTraits
{
using self_t = T ;
2022-05-06 17:02:43 +00:00
template < typename App >
WebSocketRule < App > & websocket ( App * app )
2017-12-24 15:40:39 +00:00
{
2022-05-06 17:02:43 +00:00
auto p = new WebSocketRule < App > ( static_cast < self_t * > ( this ) - > rule_ , app ) ;
2021-02-21 00:14:30 +00:00
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
}
2021-11-25 11:45:38 +00:00
template < typename . . . MethodArgs >
self_t & methods ( HTTPMethod method , MethodArgs . . . args_method )
2015-02-20 02:58:41 +00:00
{
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
}
} ;
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 :
2021-11-25 11:45:38 +00:00
DynamicRule ( std : : string rule ) :
BaseRule ( std : : move ( rule ) )
2021-11-27 12:28:50 +00:00
{ }
2015-02-18 15:57:01 +00:00
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
}
2022-02-01 13:31:05 +00:00
void handle ( request & req , response & res , const routing_params & params ) override
2015-02-18 15:57:01 +00:00
{
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 ) ;
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2015-02-18 15:57:01 +00:00
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
2021-11-25 11:45:38 +00:00
template < typename Func , size_t . . . Indices >
2015-03-15 08:51:55 +00:00
# else
2021-11-25 11:45:38 +00:00
template < typename Func , unsigned . . . Indices >
2015-03-15 08:51:55 +00:00
# endif
2022-02-01 13:31:05 +00:00
std : : function < void ( request & , response & , const routing_params & ) >
2021-11-25 11:45:38 +00:00
wrap ( Func f , black_magic : : seq < Indices . . . > )
2015-02-18 15:57:01 +00:00
{
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 (
2021-11-25 11:45:38 +00:00
black_magic : : get_parameter_tag_runtime ( rule_ . c_str ( ) ) ,
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_ <
2021-11-25 11:45:38 +00:00
typename function_t : : template arg < Indices > . . . > ( std : : move ( f ) ) ;
2015-02-18 15:57:01 +00:00
return ret ;
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2015-02-18 15:57:01 +00:00
void operator ( ) ( std : : string name , Func & & f )
{
name_ = std : : move ( name ) ;
( * this ) . template operator ( ) < Func > ( std : : forward ( f ) ) ;
}
2021-11-25 11:45:38 +00:00
2015-02-18 15:57:01 +00:00
private :
2022-02-01 13:31:05 +00:00
std : : function < void ( request & , response & , const routing_params & ) > erased_handler_ ;
2015-02-18 15:57:01 +00:00
} ;
2020-11-18 22:13:57 +00:00
/// Default rule created when CROW_ROUTE is called.
2021-11-25 11:45:38 +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
2021-11-25 11:45:38 +00:00
TaggedRule ( std : : string rule ) :
BaseRule ( std : : move ( rule ) )
2021-11-27 12:28:50 +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
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2022-02-01 20:21:07 +00:00
void operator ( ) ( Func & & f )
2014-04-02 20:31:32 +00:00
{
2017-01-09 12:24:01 +00:00
handler_ = (
# ifdef CROW_CAN_USE_CPP14
2021-11-25 11:45:38 +00:00
[ f = std : : move ( f ) ]
2017-01-09 12:24:01 +00:00
# else
2021-11-25 11:45:38 +00:00
[ f ]
2017-01-09 12:24:01 +00:00
# endif
2022-02-01 13:31:05 +00:00
( crow : : request & req , crow : : response & res , Args . . . args ) {
detail : : wrapped_handler_call ( req , res , f , std : : forward < Args > ( args ) . . . ) ;
2021-11-25 11:45:38 +00:00
} ) ;
2014-04-13 02:24:06 +00:00
}
2021-11-25 11:45:38 +00:00
template < typename Func >
2014-04-20 08:45:10 +00:00
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
2022-02-01 13:31:05 +00:00
void handle ( request & req , response & res , const routing_params & params ) override
2014-08-05 18:54:38 +00:00
{
2021-07-07 12:51:04 +00:00
if ( ! custom_templates_base . empty ( ) )
mustache : : set_base ( custom_templates_base ) ;
2022-03-18 10:55:27 +00:00
else if ( mustache : : detail : : get_template_base_directory_ref ( ) ! = mustache : : detail : : get_global_template_base_directory_ref ( ) )
mustache : : set_base ( mustache : : detail : : get_global_template_base_directory_ref ( ) ) ;
2021-07-07 12:51:04 +00:00
2015-02-18 15:57:01 +00:00
detail : : routing_handler_call_helper : : call <
2022-02-01 20:21:07 +00:00
detail : : routing_handler_call_helper : : call_params < decltype ( handler_ ) > ,
2021-11-25 11:45:38 +00:00
0 , 0 , 0 , 0 ,
black_magic : : S < Args . . . > ,
black_magic : : S < > > ( ) (
2022-02-01 20:21:07 +00:00
detail : : routing_handler_call_helper : : call_params < decltype ( handler_ ) > { handler_ , params , req , res } ) ;
2022-02-01 13:31:05 +00:00
}
/// Enable local middleware for this handler
template < typename App , typename . . . Middlewares >
crow : : detail : : handler_call_bridge < TaggedRule < Args . . . > , App , Middlewares . . . >
middlewares ( )
{
// the handler_call_bridge allows the functor to be placed directly after this function
// instead of wrapping it with more parentheses
return { this } ;
2014-04-02 20:31:32 +00:00
}
2014-04-09 23:17:08 +00:00
private :
2022-02-01 13:31:05 +00:00
std : : function < void ( crow : : request & , crow : : response & , Args . . . ) > handler_ ;
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.
2021-08-03 09:46:41 +00:00
uint16_t blueprint_index { INVALID_BP_ID } ;
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
{
2021-11-25 11:45:38 +00:00
return ! rule_index & &
blueprint_index = = INVALID_BP_ID & &
children . size ( ) < 2 & &
param = = ParamType : : MAX & &
2021-11-27 17:44:51 +00:00
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 ( )
2021-11-27 12:28:50 +00:00
{ }
2014-04-15 13:46:28 +00:00
2021-12-03 03:39:23 +00:00
/// Check whether or not the trie is empty.
2021-04-03 02:33:40 +00:00
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-11-25 11:45:38 +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 ) ;
2021-11-25 11:45:38 +00:00
delete ( child_temp ) ;
2014-08-06 21:18:21 +00:00
optimizeNode ( node ) ;
}
else
{
2021-11-25 11:45:38 +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-11-25 11:45:38 +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-11-25 11:45:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < " <int> " ;
2021-07-10 19:45:47 +00:00
break ;
case ParamType : : UINT :
2021-11-25 11:45:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < " <uint> " ;
2021-07-10 19:45:47 +00:00
break ;
case ParamType : : DOUBLE :
2021-11-25 11:45:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < " <double> " ;
2021-07-10 19:45:47 +00:00
break ;
case ParamType : : STRING :
2021-11-25 11:45:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < " <string> " ;
2021-07-10 19:45:47 +00:00
break ;
case ParamType : : PATH :
2021-11-25 11:45:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < " <path> " ;
2021-07-10 19:45:47 +00:00
break ;
default :
2021-11-25 11:45: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-11-25 11:45:38 +00:00
CROW_LOG_DEBUG < < std : : string ( 2 * level , ' ' ) < < node - > key ;
2014-04-15 13:46:28 +00:00
2021-11-25 11:45:38 +00:00
for ( auto & child : node - > children )
2014-08-06 21:18:21 +00:00
{
2021-11-25 11:45:38 +00:00
debug_node_print ( child , level + 1 ) ;
2014-08-06 21:18:21 +00:00
}
}
2021-11-25 11:45:38 +00:00
public :
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-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-11-25 11:45:38 +00:00
uint16_t found { } ; //The rule index to be found
2021-07-27 07:52:49 +00:00
std : : vector < uint16_t > found_BP ; //The Blueprint indices to be found
2021-11-25 11:45:38 +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-11-27 12:28:50 +00:00
auto update_found = [ & found , & found_BP , & match_params ] ( std : : tuple < uint16_t , std : : vector < uint16_t > , routing_params > & ret ) {
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 ) ;
2022-03-30 12:50:11 +00:00
return std : : tuple < uint16_t , std : : vector < uint16_t > , routing_params > { node - > rule_index , * blueprints , * params } ;
2021-07-27 07:52:49 +00:00
}
bool found_fragment = false ;
2021-07-10 19:45:47 +00:00
2021-11-25 11:45:38 +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 ;
2021-11-25 11:45:38 +00:00
long long int value = strtoll ( req_url . data ( ) + pos , & eptr , 10 ) ;
if ( errno ! = ERANGE & & eptr ! = req_url . data ( ) + pos )
2021-07-10 19:45:47 +00:00
{
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-08-03 09:46:41 +00:00
if ( child - > blueprint_index ! = INVALID_BP_ID ) blueprints - > push_back ( child - > blueprint_index ) ;
2021-07-27 07:52:49 +00:00
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-08-19 19:14:45 +00:00
if ( ! blueprints - > empty ( ) ) 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 ;
2021-11-25 11:45:38 +00:00
unsigned long long int value = strtoull ( req_url . data ( ) + pos , & eptr , 10 ) ;
if ( errno ! = ERANGE & & eptr ! = req_url . data ( ) + pos )
2021-07-10 19:45:47 +00:00
{
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-08-03 09:46:41 +00:00
if ( child - > blueprint_index ! = INVALID_BP_ID ) blueprints - > push_back ( child - > blueprint_index ) ;
2021-07-27 07:52:49 +00:00
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-08-19 19:14:45 +00:00
if ( ! blueprints - > empty ( ) ) 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 ;
2021-11-25 11:45:38 +00:00
double value = strtod ( req_url . data ( ) + pos , & eptr ) ;
if ( errno ! = ERANGE & & eptr ! = req_url . data ( ) + pos )
2021-07-10 19:45:47 +00:00
{
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-08-03 09:46:41 +00:00
if ( child - > blueprint_index ! = INVALID_BP_ID ) blueprints - > push_back ( child - > blueprint_index ) ;
2021-07-27 07:52:49 +00:00
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-08-19 19:14:45 +00:00
if ( ! blueprints - > empty ( ) ) 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 ;
2021-11-25 11:45:38 +00:00
for ( ; epos < req_url . size ( ) ; epos + + )
2021-07-10 19:45:47 +00:00
{
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-11-25 11:45:38 +00:00
params - > string_params . push_back ( req_url . substr ( pos , epos - pos ) ) ;
2021-08-03 09:46:41 +00:00
if ( child - > blueprint_index ! = INVALID_BP_ID ) blueprints - > push_back ( child - > blueprint_index ) ;
2021-07-27 07:52:49 +00:00
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-08-19 19:14:45 +00:00
if ( ! blueprints - > empty ( ) ) 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-11-25 11:45:38 +00:00
params - > string_params . push_back ( req_url . substr ( pos , epos - pos ) ) ;
2021-08-03 09:46:41 +00:00
if ( child - > blueprint_index ! = INVALID_BP_ID ) blueprints - > push_back ( child - > blueprint_index ) ;
2021-07-27 07:52:49 +00:00
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-08-19 19:14:45 +00:00
if ( ! blueprints - > empty ( ) ) 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 ;
2021-08-03 09:46:41 +00:00
if ( child - > blueprint_index ! = INVALID_BP_ID ) blueprints - > push_back ( child - > blueprint_index ) ;
2021-07-27 07:52:49 +00:00
auto ret = find ( req_url , child , pos + fragment . size ( ) , params , blueprints ) ;
2021-07-10 19:45:47 +00:00
update_found ( ret ) ;
2021-08-19 19:14:45 +00:00
if ( ! blueprints - > empty ( ) ) 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 ) ;
2022-03-30 12:50:11 +00:00
return std : : tuple < uint16_t , std : : vector < uint16_t > , routing_params > { 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
2021-08-04 20:58:38 +00:00
void add ( const std : : string & url , uint16_t rule_index , unsigned bp_prefix_length = 0 , uint16_t blueprint_index = INVALID_BP_ID )
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-08-04 20:58:38 +00:00
bool has_blueprint = bp_prefix_length ! = 0 & & blueprint_index ! = INVALID_BP_ID ;
2021-07-27 07:52:49 +00:00
2021-11-25 11:45:38 +00:00
for ( unsigned i = 0 ; i < url . size ( ) ; i + + )
2014-08-06 21:18:21 +00:00
{
char c = url [ i ] ;
if ( c = = ' < ' )
{
static struct ParamTraits
{
ParamType type ;
std : : string name ;
} paramTraits [ ] =
2021-11-25 11:45:38 +00:00
{
{ 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 )
2014-08-06 21:18:21 +00:00
{
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 ;
}
}
2021-11-25 11:45:38 +00:00
i - - ;
2014-08-06 21:18:21 +00:00
}
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-30 10:09:01 +00:00
size_t get_size ( )
2014-08-06 21:18:21 +00:00
{
2021-07-30 10:09:01 +00:00
return get_size ( & head_ ) ;
2014-08-06 21:18:21 +00:00
}
2021-07-30 10:09:01 +00:00
size_t get_size ( Node * node )
2014-08-06 21:18:21 +00:00
{
2021-11-25 11:45:38 +00:00
unsigned size = 5 ; //rule_index, blueprint_index, and param
2021-07-10 19:45:47 +00:00
size + = ( node - > key . size ( ) ) ; //each character in the key is 1 byte
2021-11-25 11:45:38 +00:00
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 :
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 ;
2021-11-25 11:45:38 +00:00
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-08-03 09:46:41 +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.
2021-12-03 03:39:23 +00:00
2021-07-30 10:09:01 +00:00
///
/// You can use blueprints to assign a common prefix to rules' prefix, set custom static and template folders, and set a custom catchall route.
/// You can also assign nest blueprints for maximum Compartmentalization.
2021-07-05 09:14:31 +00:00
class Blueprint
{
public :
Blueprint ( const std : : string & prefix ) :
2021-11-25 11:45:38 +00:00
prefix_ ( prefix ) { } ;
2021-07-05 09:14:31 +00:00
2021-07-05 22:53:32 +00:00
Blueprint ( const std : : string & prefix , const std : : string & static_dir ) :
2021-11-25 11:45:38 +00:00
prefix_ ( prefix ) , static_dir_ ( static_dir ) { } ;
2021-07-05 22:53:32 +00:00
2021-07-07 12:51:04 +00:00
Blueprint ( const std : : string & prefix , const std : : string & static_dir , const std : : string & templates_dir ) :
2021-11-25 11:45:38 +00:00
prefix_ ( prefix ) , static_dir_ ( static_dir ) , templates_dir_ ( templates_dir ) { } ;
2021-07-07 12:51:04 +00:00
2021-11-25 11:45:38 +00:00
/*
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 ) ;
}
2021-11-25 11:45:38 +00:00
Blueprint & operator = ( const Blueprint & value ) = delete ;
2021-07-05 09:14:31 +00:00
2021-11-25 11:45:38 +00:00
Blueprint & operator = ( Blueprint & & value ) noexcept
2021-07-05 09:14:31 +00:00
{
prefix_ = std : : move ( value . prefix_ ) ;
all_rules_ = std : : move ( value . all_rules_ ) ;
catchall_rule_ = std : : move ( value . catchall_rule_ ) ;
return * this ;
}
2021-11-25 11:45:38 +00:00
bool operator = = ( const Blueprint & value )
2021-07-05 09:14:31 +00:00
{
return value . prefix ( ) = = prefix_ ;
}
2021-11-25 11:45:38 +00:00
bool operator ! = ( const Blueprint & value )
2021-07-05 09:14:31 +00:00
{
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 ;
}
2021-11-25 11:45:38 +00:00
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_ + ' \" ' ) ;
}
2021-07-30 10:09:01 +00:00
CatchallRule & catchall_rule ( )
{
return catchall_rule_ ;
}
private :
2021-07-28 19:31:08 +00:00
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-30 10:09:01 +00:00
std : : string prefix_ ;
std : : string static_dir_ ;
std : : string templates_dir_ ;
std : : vector < std : : unique_ptr < BaseRule > > all_rules_ ;
CatchallRule catchall_rule_ ;
std : : vector < Blueprint * > blueprints_ ;
2021-07-05 09:14:31 +00:00
2021-07-30 10:09:01 +00:00
friend class Router ;
2021-07-05 09:14:31 +00:00
} ;
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 ( )
2021-11-27 12:28:50 +00:00
{ }
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 ;
}
2021-11-25 11:45:38 +00:00
template < uint64_t N >
2014-04-13 02:24:06 +00:00
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
2021-11-27 12:28:50 +00:00
ruleObject - > foreach_method ( [ & ] ( int method ) {
2021-11-27 17:44:51 +00:00
per_methods_ [ method ] . rules . emplace_back ( ruleObject ) ;
per_methods_ [ method ] . trie . add ( rule , per_methods_ [ method ] . rules . size ( ) - 1 , BP_index ! = INVALID_BP_ID ? blueprints [ BP_index ] - > prefix ( ) . length ( ) : 0 , BP_index ) ;
2017-12-24 15:40:39 +00:00
2021-11-27 17:44:51 +00:00
// directory case:
// request to '/about' url matches '/about/' rule
if ( has_trailing_slash )
{
per_methods_ [ method ] . trie . add ( rule_without_trailing_slash , RULE_SPECIAL_REDIRECT_SLASH , BP_index ! = INVALID_BP_ID ? blueprints_ [ BP_index ] - > prefix ( ) . length ( ) : 0 , BP_index ) ;
}
} ) ;
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-08-04 20:58:38 +00:00
if ( std : : find ( blueprints_ . begin ( ) , blueprints_ . end ( ) , & blueprint ) = = blueprints_ . end ( ) )
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 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)
2021-08-04 20:58:38 +00:00
if ( blueprint - > static_dir_ . empty ( ) & & blueprint - > all_rules_ . empty ( ) )
2021-07-05 09:14:31 +00:00
{
2021-11-25 11:45:38 +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 ) ;
}
}
2021-08-04 20:58:38 +00:00
else if ( ! blueprint - > static_dir_ . empty ( ) )
2021-08-06 17:01:13 +00:00
methods . emplace_back ( HTTPMethod : : Get ) ;
2021-11-25 11:45:38 +00:00
for ( auto & rule : blueprint - > all_rules_ )
2021-07-27 07:52:49 +00:00
{
2021-11-27 12:28:50 +00:00
rule - > foreach_method ( [ & methods ] ( unsigned method ) {
2021-07-27 07:52:49 +00:00
HTTPMethod method_final = static_cast < HTTPMethod > ( method ) ;
if ( std : : find ( methods . begin ( ) , methods . end ( ) , method_final ) = = methods . end ( ) )
2021-11-27 17:44:51 +00:00
methods . emplace_back ( method_final ) ;
} ) ;
2021-07-27 07:52:49 +00:00
}
}
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
}
2021-11-25 11:45:38 +00:00
for ( auto & rule : blueprint - > all_rules_ )
2021-07-27 07:52:49 +00:00
{
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
2021-11-25 11:45:38 +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-08-04 20:58:38 +00:00
internal_add_rule_object ( rule - > rule ( ) , rule . get ( ) , INVALID_BP_ID , blueprints_ ) ;
2017-12-24 15:40:39 +00:00
}
}
2021-11-25 11:45:38 +00:00
for ( auto & per_method : per_methods_ )
2017-12-24 15:40:39 +00:00
{
per_method . trie . validate ( ) ;
2014-08-06 21:18:21 +00:00
}
}
2014-04-10 16:43:33 +00:00
2021-11-25 11:45:38 +00:00
// TODO maybe add actual_method
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 )
{
2021-11-25 11:45:38 +00:00
for ( auto & per_method : per_methods_ )
2020-10-14 21:09:17 +00:00
{
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 ) ;
2022-02-05 15:15:19 +00:00
// TODO(ipkn) absolute url building
2016-08-28 05:46:31 +00:00
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
}
2021-11-25 11:45:38 +00:00
catch ( std : : exception & e )
2016-08-28 05:46:31 +00:00
{
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
}
2021-11-25 11:45:38 +00:00
catch ( . . . )
2016-08-28 05:46:31 +00:00
{
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-08-04 20:58:38 +00:00
// 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
2021-11-27 12:28:50 +00:00
auto verify_prefix = [ & bp_i , & index , & blueprints , & found_bps ] ( ) {
2021-08-04 20:58:38 +00:00
return index > 0 & &
2021-11-25 11:45:38 +00:00
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 ;
2021-08-04 20:58:38 +00:00
} ;
2021-07-28 19:31:08 +00:00
if ( index < bp_i . size ( ) )
{
2021-08-04 20:58:38 +00:00
if ( verify_prefix ( ) )
2021-07-28 19:31:08 +00:00
{
found_bps . push_back ( blueprints [ bp_i [ index ] ] ) ;
get_found_bp ( bp_i , found_bps . back ( ) - > blueprints_ , found_bps , + + index ) ;
}
else
{
2021-08-21 01:49:17 +00:00
if ( found_bps . size ( ) < 2 )
2021-07-28 19:31:08 +00:00
{
2021-08-21 01:49:17 +00:00
found_bps . clear ( ) ;
2021-07-28 19:31:08 +00:00
found_bps . push_back ( blueprints_ [ bp_i [ index ] ] ) ;
}
else
{
2021-08-21 01:49:17 +00:00
found_bps . pop_back ( ) ;
2021-11-25 11:45:38 +00:00
Blueprint * last_element = found_bps . back ( ) ;
2021-07-28 19:31:08 +00:00
found_bps . push_back ( last_element - > blueprints_ [ bp_i [ index ] ] ) ;
}
2021-08-21 11:35:59 +00:00
get_found_bp ( bp_i , found_bps . back ( ) - > blueprints_ , found_bps , + + index ) ;
2021-07-28 19:31:08 +00:00
}
}
2021-07-27 07:52:49 +00:00
}
2021-08-21 01:49:17 +00:00
/// Is used to handle errors, you insert the error code, found route, request, and response. and it'll either call the appropriate catchall route (considering the blueprint system) and send you a status string (which is mainly used for debug messages), or just set the response code to the proper error code.
std : : string get_error ( unsigned short code , std : : tuple < uint16_t , std : : vector < uint16_t > , routing_params > & found , const request & req , response & res )
{
res . code = code ;
std : : vector < Blueprint * > bps_found ;
get_found_bp ( std : : get < 1 > ( found ) , blueprints_ , bps_found ) ;
2021-11-25 11:45:38 +00:00
for ( int i = bps_found . size ( ) - 1 ; i > 0 ; i - - )
2021-08-21 01:49:17 +00:00
{
std : : vector < uint16_t > bpi = std : : get < 1 > ( found ) ;
if ( bps_found [ i ] - > catchall_rule ( ) . has_handler ( ) )
{
bps_found [ i ] - > catchall_rule ( ) . handler_ ( req , res ) ;
# ifdef CROW_ENABLE_DEBUG
return std : : string ( " Redirected to Blueprint \" " + bps_found [ i ] - > prefix ( ) + " \" Catchall rule " ) ;
# else
return std : : string ( ) ;
# endif
}
}
if ( catchall_rule_ . has_handler ( ) )
{
catchall_rule_ . handler_ ( req , res ) ;
# ifdef CROW_ENABLE_DEBUG
return std : : string ( " Redirected to global Catchall rule " ) ;
# else
return std : : string ( ) ;
# endif
}
return std : : string ( ) ;
}
2022-02-01 13:31:05 +00:00
void handle ( request & req , response & res )
2014-08-06 21:18:21 +00:00
{
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 ;
2022-02-05 15:15:19 +00:00
res . skip_body = 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-11-25 11:45:38 +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
}
}
2021-11-25 11:45:38 +00:00
allow = allow . substr ( 0 , allow . size ( ) - 2 ) ;
res = response ( 204 ) ;
res . set_header ( " Allow " , allow ) ;
res . end ( ) ;
return ;
2021-04-03 02:33:40 +00:00
}
else
{
2021-11-25 11:45:38 +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
{
2021-11-25 11:45:38 +00:00
allow = allow . substr ( 0 , allow . size ( ) - 2 ) ;
2021-04-03 02:33:40 +00:00
res = response ( 204 ) ;
res . set_header ( " Allow " , allow ) ;
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 )
{
2021-11-25 11:45:38 +00:00
for ( auto & per_method : per_methods_ )
2020-10-14 21:09:17 +00:00
{
2021-08-21 01:49:17 +00:00
if ( std : : get < 0 > ( per_method . trie . find ( req . url ) ) ) //Route found, but in another method
2020-10-14 21:09:17 +00:00
{
2021-08-21 11:35:59 +00:00
const std : : string error_message ( get_error ( 405 , found , req , res ) ) ;
2021-08-21 01:49:17 +00:00
CROW_LOG_DEBUG < < " Cannot match method " < < req . url < < " " < < method_name ( method_actual ) < < " . " < < error_message ;
2020-10-14 21:09:17 +00:00
res . end ( ) ;
return ;
}
}
2021-08-21 01:49:17 +00:00
//Route does not exist anywhere
2021-04-12 05:25:09 +00:00
2021-08-21 11:35:59 +00:00
const std : : string error_message ( get_error ( 404 , found , req , res ) ) ;
2021-08-21 01:49:17 +00:00
CROW_LOG_DEBUG < < " Cannot match rules " < < req . url < < " . " < < error_message ;
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 ) ;
2022-02-05 15:15:19 +00:00
// TODO(ipkn) absolute url building
2015-01-19 10:03:06 +00:00
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
}
2021-11-25 11:45:38 +00:00
catch ( std : : exception & e )
2014-12-10 17:24:13 +00:00
{
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
}
2021-11-25 11:45:38 +00:00
catch ( . . . )
2014-12-10 17:24:13 +00:00
{
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-11-25 11:45:38 +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.
2021-11-25 11:45:38 +00:00
PerMethod ( ) :
rules ( 2 ) { }
2017-12-24 15:40:39 +00:00
} ;
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_ ;
2014-04-02 16:38:08 +00:00
} ;
2021-11-25 11:45:38 +00:00
} // namespace crow