RESTinio
easy_parser_router.hpp
Go to the documentation of this file.
1/*
2 * RESTinio
3 */
4
12#pragma once
13
17
19
20#include <vector>
21
22namespace restinio
23{
24
25namespace router
26{
27
28namespace easy_parser_router
29{
30
31namespace impl
32{
33
35namespace ep = restinio::easy_parser;
36
38
44struct no_match_t {};
45
54template< typename Extra_Data >
56{
57public:
59
60 virtual ~router_entry_t() = default;
61
63
73 const actual_request_handle_t & req,
74 target_path_holder_t & target_path ) const = 0;
75};
76
85template< typename Extra_Data >
87 std::unique_ptr< router_entry_t< Extra_Data > >;
88
89//
90// actual_router_entry_t
91//
105template< typename Extra_Data, typename Producer, typename Handler >
106class actual_router_entry_t : public router_entry_t< Extra_Data >
107{
108 //FIXME: compatibility between Extra_Data and Handler should be
109 //checked by static_assert. If it's possible.
110
113
115 Producer m_producer;
116
118 Handler m_handler;
119
120public:
123
124 template<
125 typename Method_Matcher,
126 typename Producer_Arg,
127 typename Handler_Arg >
129 Method_Matcher && method_matcher,
130 Producer_Arg && producer,
131 Handler_Arg && handler )
132 : m_producer{ std::forward<Producer_Arg>(producer) }
133 , m_handler{ std::forward<Handler_Arg>(handler) }
134 {
135 assign( m_method_matcher, std::forward<Method_Matcher>(method_matcher) );
136 }
137
141 const actual_request_handle_t & req,
142 target_path_holder_t & target_path ) const override
143 {
144 if( m_method_matcher->match( req->header().method() ) )
145 {
146 auto parse_result = easy_parser::try_parse(
147 target_path.view(),
148 m_producer );
149 if( parse_result )
150 {
151 return Producer::invoke_handler( req, m_handler, *parse_result );
152 }
153 }
154
155 return make_unexpected( no_match_t{} );
156 }
157};
158
159//
160// unescape_transformer_t
161//
168template< typename Unescape_Traits >
171{
172 using input_type = std::string;
173
176 transform( input_type && input ) const
177 {
178 return restinio::utils::unescape_percent_encoding< Unescape_Traits >(
179 input );
180 }
181};
182
183//
184// special_produce_tuple_item_clause_t
185//
192template< typename Producer, std::size_t Index >
194 : public ep::impl::consume_value_clause_t<
195 Producer,
196 ep::impl::tuple_item_consumer_t<Index> >
197{
198 using consumer_t = ep::impl::tuple_item_consumer_t<Index>;
199
200 using base_type_t = ep::impl::consume_value_clause_t<
201 Producer,
202 consumer_t >;
203
204 // NOTE: this is just a workaround for VS2017.
205 template< typename Producer_Arg >
207 static Producer
208 make_producer( Producer_Arg && producer )
209 {
210 return { std::forward<Producer_Arg>(producer) };
211 }
212
213public:
214 template< typename Producer_Arg >
215 special_produce_tuple_item_clause_t( Producer_Arg && producer )
216 : base_type_t{
217 make_producer( std::forward<Producer_Arg>(producer) ),
218 consumer_t{} }
219 {}
220};
221
222//
223// special_exact_fixed_size_fragment_clause_t
224//
235template< std::size_t Size >
237 : public ep::impl::consume_value_clause_t<
238 ep::impl::exact_fixed_size_fragment_producer_t<Size>,
239 ep::impl::any_value_skipper_t >
240{
241 using producer_t = ep::impl::exact_fixed_size_fragment_producer_t<Size>;
242 using consumer_t = ep::impl::any_value_skipper_t;
243
244 using base_type_t = ep::impl::consume_value_clause_t<
246 consumer_t >;
247
248public:
250 const char (&fragment)[Size] )
251 : base_type_t{ producer_t{ fragment }, consumer_t{} }
252 {}
253};
254
255//
256// special_exact_fragment_clause_t
257//
269 : public ep::impl::consume_value_clause_t<
270 ep::impl::exact_fragment_producer_t,
271 ep::impl::any_value_skipper_t >
272{
273 using producer_t = ep::impl::exact_fragment_producer_t;
274 using consumer_t = ep::impl::any_value_skipper_t;
275
276 using base_type_t = ep::impl::consume_value_clause_t<
278 consumer_t >;
279
280public:
282 : base_type_t{ producer_t{ std::move(value) }, consumer_t{} }
283 {}
284
286 : base_type_t{
287 producer_t{ std::string{ value.data(), value.size() } },
288 consumer_t{} }
289 {}
290};
291
292namespace dsl_details
293{
294
295// Adds type H to type list R if Is_Producer is true.
296template< typename H, typename R, bool Is_Producer >
298
299template<
300 typename H,
301 template<class...> class To,
302 typename... Results >
303struct add_type_if_necessary_impl< H, To<Results...>, false >
304{
305 using type = To<Results...>;
306};
307
308template<
309 typename H,
310 template<class...> class To,
311 typename... Results >
312struct add_type_if_necessary_impl< H, To<Results...>, true >
313{
314 using type = To<Results..., typename H::result_type>;
315};
316
317// Adds type H to type list R if H is a producer.
318template< typename H, typename R >
320 : add_type_if_necessary_impl< H, R, ep::impl::is_producer_v<H> >
321{};
322
323// Produces a type-list of producers from type-list From.
324template< typename From, typename To >
326
327// Adds a type from Sources to Results only if this type is a producer.
328template<
329 template<class...> class From,
330 typename... Sources,
331 template<class...> class To,
332 typename... Results >
333struct result_tuple_detector< From<Sources...>, To<Results...> >
334{
335 using type = typename result_tuple_detector<
336 meta::tail_of_t< Sources... >,
337 typename add_type_if_necessary<
338 meta::head_of_t< Sources... >,
339 To< Results... > >::type
340 >::type;
341};
342
343template<
344 template<class...> class From,
345 template<class...> class To,
346 typename... Results >
347struct result_tuple_detector< From<>, To<Results...> >
348{
349 using type = To<Results...>;
350};
351
352// Produces a type of std::tuple of producers from type-list Args_Type_List.
353template< typename Args_Type_List >
355{
357 typename result_tuple_detector<
358 Args_Type_List,
360 std::tuple >;
361};
362
363template< typename Args_Type_List >
365
366// Detects an appropriate type of clause for T.
367// If T is a producer then there will be a new clause that
368// consumes a value T produces.
369// In that case Current_Index will also be incremented.
370//
371// If T is not a producer then Current_Index will be kept.
372//
373// If T is a string literal, or std::string, or string_view
374// then T will be replaced by a special clause.
375template< typename T, bool Is_Producer, std::size_t Current_Index >
377{
378 using clause_type = T;
379 static constexpr std::size_t next_index = Current_Index;
380};
381
382template< std::size_t Size, std::size_t Current_Index >
383struct one_clause_type_processor<const char[Size], false, Current_Index>
384{
386 static constexpr std::size_t next_index = Current_Index;
387};
388
389template< std::size_t Current_Index >
390struct one_clause_type_processor<std::string, false, Current_Index>
391{
393 static constexpr std::size_t next_index = Current_Index;
394};
395
396template< std::size_t Current_Index >
397struct one_clause_type_processor<string_view_t, false, Current_Index>
398{
400 static constexpr std::size_t next_index = Current_Index;
401};
402
403template< typename T, std::size_t Current_Index >
404struct one_clause_type_processor<T, true, Current_Index>
405{
407 static constexpr std::size_t next_index = Current_Index + 1u;
408};
409
410// Takes a type-list of user-specified types From and produces a
411// typelist of actual clauses types To.
412//
413// The Current_Index should 0 at the first invocation.
414template< typename From, typename To, std::size_t Current_Index >
416
417template<
418 template<class...> class From,
419 typename... Sources,
420 template<class...> class To,
421 typename... Results,
422 std::size_t Current_Index >
423struct clauses_type_maker< From<Sources...>, To<Results...>, Current_Index >
424{
425private:
426 using head_type = meta::head_of_t< Sources... >;
427
429 head_type,
430 ep::impl::is_producer_v<head_type>,
431 Current_Index >;
432
433public:
434 using type = typename clauses_type_maker<
435 meta::tail_of_t< Sources... >,
436 To< Results..., typename one_clause_type::clause_type >,
437 one_clause_type::next_index >::type;
438};
439
440template<
441 template<class...> class From,
442 template<class...> class To,
443 typename... Results,
444 std::size_t Current_Index >
445struct clauses_type_maker< From<>, To<Results...>, Current_Index >
446{
447 using type = To< Results... >;
448};
449
450// Takes a type-list of user-specified types Args_Type_List and produces a
451// typelist of actual clauses types to be used for parsing.
452template< typename Args_Type_List >
454{
456 typename clauses_type_maker<
457 Args_Type_List,
459 0u >::type,
460 std::tuple >;
461};
462
463template< typename Args_Type_List >
465
466//
467// special_decay
468//
483template< typename T >
485{
486private:
487 using U = std::remove_reference_t<T>;
488
489public:
490 using type = typename std::conditional<
491 std::is_array<U>::value,
492 U,
493 std::remove_cv_t<U>
494 >::type;
495};
496
497} /* namespace dsl_details */
498
499//
500// dsl_processor
501//
515template< typename... Args >
517{
518 static_assert( 0u != sizeof...(Args), "Args can't be an empty list" );
519
522
524
526};
527
528//
529// path_to_tuple_producer_t
530//
539template<
540 typename Target_Type,
541 typename Subitems_Tuple >
543 : public ep::impl::produce_t< Target_Type, Subitems_Tuple >
544{
545 using base_type_t = ep::impl::produce_t< Target_Type, Subitems_Tuple >;
546
547public:
548 using base_type_t::base_type_t;
549
550 template< typename Extra_Data, typename Handler >
552 static auto
555 Handler && handler,
556 typename base_type_t::result_type & type )
557 {
558 return handler( req, type );
559 }
560};
561
562namespace path_to_params_details
563{
564
565template<
566 typename F,
567 typename Extra_Data,
568 typename Tuple,
569 std::size_t... Indexes >
570decltype(auto)
572 F && what,
574 Tuple && params,
575 std::index_sequence<Indexes...> )
576{
577 return std::forward<F>(what)(
578 req,
579 std::get<Indexes>(std::forward<Tuple>(params))... );
580}
581
582//
583// call_with_tuple
584//
592template< typename F, typename Extra_Data, typename Tuple >
593decltype(auto)
595 F && what,
597 Tuple && params )
598{
600 std::forward<F>(what),
601 req,
602 std::forward<Tuple>(params),
603 std::make_index_sequence<
604 std::tuple_size< std::remove_reference_t<Tuple> >::value
605 >{} );
606}
607
608} /* namespace path_to_params_details */
609
610//
611// path_to_params_producer_t
612//
622template<
623 typename Target_Type,
624 typename Subitems_Tuple >
626 : public ep::impl::produce_t< Target_Type, Subitems_Tuple >
627{
628 using base_type_t = ep::impl::produce_t< Target_Type, Subitems_Tuple >;
629
630public:
631 using base_type_t::base_type_t;
632
633 template< typename User_Type, typename Handler >
635 static auto
638 Handler && handler,
639 typename base_type_t::result_type & type )
640 {
641 return path_to_params_details::call_with_tuple( handler, req, type );
642 }
643};
644
645} /* namespace impl */
646
647using namespace restinio::easy_parser;
648
649//
650// path_to_tuple
651//
691template< typename... Args >
693auto
694path_to_tuple( Args && ...args )
695{
696 using dsl_processor = impl::dsl_processor< Args... >;
697 using result_tuple_type = typename dsl_processor::result_tuple;
698 using subclauses_tuple_type = typename dsl_processor::clauses_tuple;
699
700 using producer_type = impl::path_to_tuple_producer_t<
701 result_tuple_type,
702 subclauses_tuple_type >;
703
704 return producer_type{
705 subclauses_tuple_type{ std::forward<Args>(args)... }
706 };
707}
708
709//
710// path_to_params
711//
745template< typename... Args >
747auto
748path_to_params( Args && ...args )
749{
750 using dsl_processor = impl::dsl_processor< Args... >;
751 using result_tuple_type = typename dsl_processor::result_tuple;
752 using subclauses_tuple_type = typename dsl_processor::clauses_tuple;
753
754 using producer_type = impl::path_to_params_producer_t<
755 result_tuple_type,
756 subclauses_tuple_type >;
757
758 return producer_type{
759 subclauses_tuple_type{ std::forward<Args>(args)... }
760 };
761}
762
804inline auto
805path_fragment_p( char separator = '/' )
806{
807 return produce< std::string >(
808 repeat( 1, N,
809 any_symbol_if_not_p( separator ) >> to_container() ) );
810}
811
858template< typename Unescape_Traits =
861auto
863{
865}
866
867} /* namespace easy_parser_router */
868
869//
870// generic_easy_parser_router_t
871//
948template< typename Extra_Data_Factory >
950{
951 using extra_data_t = typename Extra_Data_Factory::data_t;
952
953public:
955
957
959 const generic_easy_parser_router_t & ) = delete;
962
966
970 {
971 using namespace easy_parser_router::impl;
972
973 // Take care of an optional trailing slash.
974 string_view_t path_to_inspect{ req->header().path() };
975 if( path_to_inspect.size() > 1u && '/' == path_to_inspect.back() )
976 path_to_inspect.remove_suffix( 1u );
977
978 target_path_holder_t target_path{ path_to_inspect };
979 for( const auto & entry : m_entries )
980 {
981 const auto r = entry->try_handle( req, target_path );
982 if( r )
983 {
984 return *r;
985 }
986 }
987
988 // Here: none of the routes matches this handler.
990 {
991 // If non matched request handler is set
992 // then call it.
994 }
995
996 return request_not_handled();
997 }
998
999 template<
1000 typename Method_Matcher,
1001 typename Route_Producer,
1002 typename Handler >
1003 void
1005 Method_Matcher && method_matcher,
1006 Route_Producer && route,
1007 Handler && handler )
1008 {
1009 using namespace easy_parser_router::impl;
1010
1011 using producer_type = std::decay_t< Route_Producer >;
1012 using handler_type = std::decay_t< Handler >;
1013
1014 using actual_entry_type = actual_router_entry_t<
1015 extra_data_t, producer_type, handler_type >;
1016
1017 auto entry = std::make_unique< actual_entry_type >(
1018 std::forward<Method_Matcher>(method_matcher),
1019 std::forward<Route_Producer>(route),
1020 std::forward<Handler>(handler) );
1021
1022 m_entries.push_back( std::move(entry) );
1023 }
1024
1026 template< typename Route_Producer, typename Handler >
1027 void
1029 Route_Producer && route,
1030 Handler && handler )
1031 {
1032 this->add_handler(
1033 http_method_get(),
1034 std::forward<Route_Producer>(route),
1035 std::forward<Handler>(handler) );
1036 }
1037
1039 template< typename Route_Producer, typename Handler >
1040 void
1042 Route_Producer && route,
1043 Handler && handler )
1044 {
1045 this->add_handler(
1046 http_method_delete(),
1047 std::forward<Route_Producer>(route),
1048 std::forward<Handler>(handler) );
1049 }
1050
1052 template< typename Route_Producer, typename Handler >
1053 void
1055 Route_Producer && route,
1056 Handler && handler )
1057 {
1058 this->add_handler(
1059 http_method_head(),
1060 std::forward<Route_Producer>(route),
1061 std::forward<Handler>(handler) );
1062 }
1063
1065 template< typename Route_Producer, typename Handler >
1066 void
1068 Route_Producer && route,
1069 Handler && handler )
1070 {
1071 this->add_handler(
1072 http_method_post(),
1073 std::forward<Route_Producer>(route),
1074 std::forward<Handler>(handler) );
1075 }
1076
1078 template< typename Route_Producer, typename Handler >
1079 void
1081 Route_Producer && route,
1082 Handler && handler )
1083 {
1084 this->add_handler(
1085 http_method_put(),
1086 std::forward<Route_Producer>(route),
1087 std::forward<Handler>(handler) );
1088 }
1089
1091 void
1094 {
1096 }
1097
1098private:
1099 using entries_container_t = std::vector<
1101 >;
1102
1104
1108};
1109
1110//
1111// easy_parser_router_t
1112//
1175
1176} /* namespace router */
1177
1178} /* namespace restinio */
1179
An actual implementation of router_entry interface.
Producer m_producer
Parser of a route and producer of argument(s) for request handler.
actual_router_entry_t(Method_Matcher &&method_matcher, Producer_Arg &&producer, Handler_Arg &&handler)
Handler m_handler
Request handler to be used.
RESTINIO_NODISCARD expected_t< request_handling_status_t, no_match_t > try_handle(const actual_request_handle_t &req, target_path_holder_t &target_path) const override
An attempt to match a request against the route.
restinio::router::impl::buffered_matcher_holder_t m_method_matcher
HTTP method to match.
An implementation of a producer for path_to_params case.
static RESTINIO_NODISCARD auto invoke_handler(const generic_request_handle_t< User_Type > &req, Handler &&handler, typename base_type_t::result_type &type)
ep::impl::produce_t< Target_Type, Subitems_Tuple > base_type_t
An implementation of a producer for path_to_tuple case.
ep::impl::produce_t< Target_Type, Subitems_Tuple > base_type_t
static RESTINIO_NODISCARD auto invoke_handler(const generic_request_handle_t< Extra_Data > &req, Handler &&handler, typename base_type_t::result_type &type)
An interface for one entry of easy_parser-based router.
virtual RESTINIO_NODISCARD expected_t< request_handling_status_t, no_match_t > try_handle(const actual_request_handle_t &req, target_path_holder_t &target_path) const =0
An attempt to match a request against the route.
virtual ~router_entry_t()=default
generic_request_handle_t< Extra_Data > actual_request_handle_t
A special clause type for case when exact_fixed_size_fragment_producer should be used without storing...
A special clause type for case when exact_fragment_producer should be used without storing its value.
ep::impl::consume_value_clause_t< producer_t, consumer_t > base_type_t
A special case of produce-consume clause where the produced value is stored into a tuple.
static RESTINIO_NODISCARD Producer make_producer(Producer_Arg &&producer)
ep::impl::consume_value_clause_t< Producer, consumer_t > base_type_t
A generic request router that uses easy_parser for matching requests with handlers.
void add_handler(Method_Matcher &&method_matcher, Route_Producer &&route, Handler &&handler)
typename Extra_Data_Factory::data_t extra_data_t
generic_request_handle_t< extra_data_t > actual_request_handle_t
generic_easy_parser_router_t & operator=(generic_easy_parser_router_t &&)=default
void http_get(Route_Producer &&route, Handler &&handler)
Set handler for HTTP GET request.
void http_head(Route_Producer &&route, Handler &&handler)
Set handler for HTTP HEAD request.
void non_matched_request_handler(generic_non_matched_request_handler_t< extra_data_t > nmrh)
Set handler for requests that don't match any route.
generic_easy_parser_router_t & operator=(const generic_easy_parser_router_t &)=delete
generic_non_matched_request_handler_t< extra_data_t > m_non_matched_request_handler
Handler that is called for requests that don't match any route.
void http_delete(Route_Producer &&route, Handler &&handler)
Set handler for HTTP DELETE request.
generic_easy_parser_router_t(generic_easy_parser_router_t &&)=default
std::vector< easy_parser_router::impl::router_entry_unique_ptr_t< extra_data_t > > entries_container_t
void http_post(Route_Producer &&route, Handler &&handler)
Set handler for HTTP POST request.
generic_easy_parser_router_t(const generic_easy_parser_router_t &)=delete
void http_put(Route_Producer &&route, Handler &&handler)
Set handler for HTTP PUT request.
RESTINIO_NODISCARD request_handling_status_t operator()(actual_request_handle_t req) const
A special class that allows to hold a copy of small-size method_matchers or a pointer to dynamically ...
Helper class for holding a unique instance of char array with target_path value.
RESTINIO_NODISCARD string_view_t view() const noexcept
Get access to the value of target_path.
#define RESTINIO_NODISCARD
An very small, simple and somewhat limited implementation of recursive-descent parser.
Stuff related to method_matchers.
RESTINIO_NODISCARD auto any_symbol_if_not_p(char sentinel) noexcept
A factory function to create a any_symbol_if_not_producer.
RESTINIO_NODISCARD expected_t< typename Producer::result_type, parse_error_t > try_parse(string_view_t from, Producer producer)
Perform the parsing of the specified content by using specified value producer.
RESTINIO_NODISCARD auto repeat(std::size_t min_occurences, std::size_t max_occurences, Clauses &&... clauses)
A factory function to create repetitor of subclauses.
constexpr std::size_t N
A special marker that means infinite repetitions.
RESTINIO_NODISCARD auto to_container()
A factory function to create a to_container_consumer.
typename detect_result_tuple< Args_Type_List >::type detect_result_tuple_t
typename make_clauses_types< Args_Type_List >::type make_clauses_types_t
decltype(auto) call_with_tuple_impl(F &&what, const generic_request_handle_t< Extra_Data > &req, Tuple &&params, std::index_sequence< Indexes... >)
decltype(auto) call_with_tuple(F &&what, const generic_request_handle_t< Extra_Data > &req, Tuple &&params)
A helper function to call a request-handler with a tuple.
std::unique_ptr< router_entry_t< Extra_Data > > router_entry_unique_ptr_t
An alias for unique_ptr of router_entry.
restinio::router::impl::target_path_holder_t target_path_holder_t
RESTINIO_NODISCARD auto path_to_params(Args &&...args)
Describe a route for a handler that accepts params from the route in form of a list of separate argum...
RESTINIO_NODISCARD auto path_fragment_p(char separator='/')
A factory that creates a string-producer that extracts a sequence on symbols until the separator will...
RESTINIO_NODISCARD auto unescape()
A factory for unescape_transformer.
RESTINIO_NODISCARD auto path_to_tuple(Args &&...args)
Describe a route for a handler that accepts params from the route in form of a tuple.
std::function< request_handling_status_t(generic_request_handle_t< Extra_Data >) > generic_non_matched_request_handler_t
A generic type of handler for non-matched requests.
typename impl::tail_of< L... >::type tail_of_t
Metafunction to get the tail of a list of types in a form of type_list.
typename impl::rename< From, To >::type rename_t
Allows to pass all template arguments from one type to another.
typename impl::head_of< L... >::type head_of_t
Metafunction to get the first item from a list of types.
typename impl::transform< Transform_F, From, type_list<> >::type transform_t
Applies a specified meta-function to every item from a specified type-list and return a new type-list...
RESTINIO_NODISCARD constexpr request_handling_status_t request_not_handled() noexcept
nonstd::string_view string_view_t
Definition: string_view.hpp:19
std::shared_ptr< generic_request_t< Extra_Data > > generic_request_handle_t
An alias for shared-pointer to incoming request.
request_handling_status_t
Request handling status.
nonstd::expected< T, E > expected_t
Definition: expected.hpp:22
STL namespace.
The definition of the non_matched_request_handler type.
A special base class to be used with transformers.
typename clauses_type_maker< meta::tail_of_t< Sources... >, To< Results..., typename one_clause_type::clause_type >, one_clause_type::next_index >::type type
meta::rename_t< typename result_tuple_detector< Args_Type_List, meta::type_list<> >::type, std::tuple > type
meta::rename_t< typename clauses_type_maker< Args_Type_List, meta::type_list<>, 0u >::type, std::tuple > type
typename result_tuple_detector< meta::tail_of_t< Sources... >, typename add_type_if_necessary< meta::head_of_t< Sources... >, To< Results... > >::type >::type type
A special analog of std::decay meta-function that is handles array differently.
typename std::conditional< std::is_array< U >::value, U, std::remove_cv_t< U > >::type type
The main meta-function for processing route DSL.
dsl_details::make_clauses_types_t< arg_types > clauses_tuple
dsl_details::detect_result_tuple_t< arg_types > result_tuple
meta::transform_t< dsl_details::special_decay, meta::type_list< Args... > > arg_types
Helper type to indicate a negative match attempt.
A transformer that performs percent-unescaping of an input string.
RESTINIO_NODISCARD result_type transform(input_type &&input) const
virtual RESTINIO_NODISCARD bool match(const http_method_id_t &method) const noexcept=0
Is the specified method can be applied to a route?
The basic building block: a type for representation of a type list.
The default traits for escaping and unexcaping symbols in a query string.
Implementation of target_path_holder helper class.
#define const
Definition: zconf.h:230