RESTinio
express.hpp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /*!
6  Express.js style router.
7 */
8 
9 #pragma once
10 
11 #include <restinio/request_handler.hpp>
12 #include <restinio/optional.hpp>
13 
14 #include <restinio/path2regex/path2regex.hpp>
15 
16 #include <restinio/router/std_regex_engine.hpp>
17 
18 #include <restinio/utils/from_string.hpp>
19 #include <restinio/utils/percent_encoding.hpp>
20 
21 #include <map>
22 #include <vector>
23 
24 namespace restinio
25 {
26 
27 namespace router
28 {
29 
30 namespace impl
31 {
32 
34 
35 } /* namespace impl */
36 
37 //
38 // route_params_t
39 //
40 
41 //! Parameters extracted from route.
42 /*!
43  Holds values of parameters extracted from route.
44 
45  All values are stored as string views,
46  route_params_t instance owns bufers used by these views.
47  And that leads to following limittions:
48  once a copy of a string view is created it is
49  important to use it with respect to life time of route_params_t
50  instance to which this parameter bind belongs.
51  String view is valid during route_params_t
52  instance life time.
53 */
54 class route_params_t final
55 {
56  public:
57  using named_parameters_container_t =
58  std::vector< std::pair< string_view_t, string_view_t > >;
59  using indexed_parameters_container_t =
60  std::vector< string_view_t >;
61 
62  private:
63  friend struct impl::route_params_accessor_t;
64 
65  void
67  std::unique_ptr< char[] > request_target,
68  std::shared_ptr< std::string > key_names_buffer,
69  string_view_t match,
70  named_parameters_container_t named_parameters,
71  indexed_parameters_container_t indexed_parameters )
72  {
73  m_request_target = std::move( request_target );
74  m_key_names_buffer = std::move( key_names_buffer );
75  m_match = std::move( match );
76  m_named_parameters = std::move( named_parameters );
77  m_indexed_parameters = std::move( indexed_parameters );
78  }
79 
80  public:
81  route_params_t() = default;
82 
83  route_params_t( route_params_t && ) = default;
84  route_params_t & operator = ( route_params_t && ) = default;
85 
86  route_params_t( const route_params_t & ) = delete;
87  route_params_t & operator = ( const route_params_t & ) = delete;
88 
89  //! Matched route.
90  string_view_t match() const noexcept { return m_match; }
91 
92  //! Get named parameter.
94  operator [] ( string_view_t key ) const
95  {
96  return find_named_parameter_with_check( key ).second;
97  }
98 
99  //! Check parameter.
100  bool
101  has( string_view_t key ) const noexcept
102  {
103  return m_named_parameters.end() != find_named_parameter( key );
104  }
105 
106  //! Get the value of a parameter if it exists.
107  //! @since v.0.4.4
109  get_param( string_view_t key ) const noexcept
110  {
111  const auto it = find_named_parameter( key );
112 
113  return m_named_parameters.end() != it ?
114  optional_t< string_view_t >{ it->second } :
115  optional_t< string_view_t >{ nullopt };
116  }
117 
118  //! Get indexed parameter.
120  operator [] ( std::size_t i ) const
121  {
122  if( i >= m_indexed_parameters.size() )
123  throw exception_t{ fmt::format( "invalid parameter index: {}", i ) };
124 
125  return m_indexed_parameters.at( i );
126  }
127 
128  //! Get number of parameters.
129  //! \{
130  auto named_parameters_size() const noexcept { return m_named_parameters.size(); }
131  auto indexed_parameters_size() const noexcept { return m_indexed_parameters.size(); }
132  //! \}
133 
134  private:
137  {
138  return
139  std::find_if(
140  m_named_parameters.begin(),
141  m_named_parameters.end(),
142  [&]( const auto p ){
143  return key == p.first;
144  } );
145  }
146 
149  {
150  auto it = find_named_parameter( key );
151 
152  if( m_named_parameters.end() == it )
153  throw exception_t{
154  fmt::format(
155  "invalid parameter name: {}",
156  std::string{ key.data(), key.size() } ) };
157 
158  return *it;
159  }
160 
161  //! A raw request target.
162  /*!
163  All parameters values are defined as string views
164  refering parts of this beffer.
165 
166  \note `std::unique_ptr< char[] >` is used here on purpose,
167  because if we consider std::string, then it has an issue
168  when SSO is applied. It is important that
169  parameters that refering buffer are valid after move operations with
170  the buffer. And std::strings with SSO applied cannot guarantee this.
171  Vector on the other hand gives this guarantee.
172  */
174 
175  //! Shared buffer for string_view of named parameterts names.
177 
178  //! Matched pattern.
180 
181  //! Named params.
183 
184  //! Indexed params.
186 };
187 
188 namespace impl
189 {
190 
191 //
192 // route_params_accessor_t
193 //
194 
195 //! Route params private internals accessor.
197 {
198  //! Init parameters with a matched route params.
199  static void
201  route_params_t & rp,
202  std::unique_ptr< char[] > request_target,
203  std::shared_ptr< std::string > key_names_buffer,
204  string_view_t match_,
205  route_params_t::named_parameters_container_t named_parameters,
206  route_params_t::indexed_parameters_container_t indexed_parameters )
207  {
208  rp.match(
209  std::move( request_target ),
210  std::move( key_names_buffer ),
211  std::move( match_ ),
212  std::move( named_parameters ),
213  std::move( indexed_parameters ) );
214  }
215 
216  //! Get values containers for all parameters (used in unit tests).
217  //! \{
218  static const auto &
219  named_parameters( const route_params_t & rp ) noexcept
220  {
221  return rp.m_named_parameters;
222  }
223 
224  static const auto &
225  indexed_parameters( const route_params_t & rp ) noexcept
226  {
227  return rp.m_indexed_parameters;
228  }
229  //! \}
230 };
231 
232 //
233 // route_params_appender_t
234 //
235 
236 //! Helper class for gthering parameters from route.
238 {
239  public:
241  route_params_t::named_parameters_container_t & named_parameters,
242  route_params_t::indexed_parameters_container_t & indexed_parameters )
245  {}
246 
251 
252  void
253  add_named_param( string_view_t key, string_view_t value )
254  {
255  m_named_parameters.emplace_back( key, value );
256  }
257 
258  void
259  add_indexed_param( string_view_t value )
260  {
261  m_indexed_parameters.emplace_back( value );
262  }
263 
264  private:
267 };
268 
271 
272 //
273 // route_matcher_t
274 //
275 
276 //! A matcher for a given path.
277 template < typename Regex_Engine = std_regex_engine_t >
279 {
280  public:
281  using regex_t = typename Regex_Engine::compiled_regex_t;
282  using match_results_t = typename Regex_Engine::match_results_t;
283 
284  //! Creates matcher with a given parameters.
286  http_method_id_t method,
287  regex_t route_regex,
288  std::shared_ptr< std::string > named_params_buffer,
289  param_appender_sequence_t param_appender_sequence )
290  : m_method{ method }
294  {}
295 
296  route_matcher_t() = default;
297  route_matcher_t( route_matcher_t && ) = default;
298 
299  //! Try to match a given request target with this route.
300  bool
302  string_view_t target_path,
303  route_params_t & parameters ) const
304  {
306  if( Regex_Engine::try_match(
307  target_path,
309  matches ) )
310  {
312 
313  // Data for route_params_t initialization.
314 
315  std::unique_ptr< char[] > captured_params{ new char[ target_path.size() ] };
317 
318  const string_view_t match{
322 
325 
327 
328  // Std regex and pcre engines handle
329  // trailing groups with empty values differently.
330  // Std despite they are empty includes them in the list of match results;
331  // Pcre on the other hand does not.
332  // So the second for is for pushing empty values
333  std::size_t i = 1;
334  for( ; i < matches.size(); ++i )
335  {
336  const auto & m = matches[ i ];
343  }
344 
345  for( ; i < m_param_appender_sequence.size() + 1; ++i )
346  {
350  }
351 
352  // Init route parameters.
354  parameters,
356  m_named_params_buffer, // Do not move (it is used on each match).
357  std::move( match ),
360 
361  return true;
362  }
363 
364  return false;
365  }
366 
367  inline bool
368  operator () (
369  const http_request_header_t & h,
370  route_params_t & parameters ) const
371  {
372  return m_method == h.method() && match_route( h.path(), parameters );
373  }
374 
375  private:
376  //! HTTP method to match.
378 
379  //! Regex of a given route.
380  regex_t m_route_regex;
381 
382  //! Buffer for named parameters names string views.
384 
385  //! Parameters values.
387 };
388 
389 } /* namespace impl */
390 
391 //
392 // express_request_handler_t
393 //
394 
397 
398 //
399 // express_unmatched_request_handler_t
400 //
401 
404 
405 //
406 // express_route_entry_t
407 //
408 
409 //! A single express route entry.
410 /*!
411  Might be helpful for use without express_router_t,
412  if only a single route is needed.
413  It gives the same help with route parameters.
414 */
415 template < typename Regex_Engine = std_regex_engine_t>
417 {
418  using matcher_init_data_t =
421  http_method_id_t method,
422  matcher_init_data_t matcher_data,
423  express_request_handler_t handler )
424  : m_matcher{
425  method,
429  , m_handler{ std::move( handler ) }
430  {}
431 
432  public:
433  express_route_entry_t( const express_route_entry_t & ) = delete;
434  express_route_entry_t & operator = ( const express_route_entry_t & ) = delete;
435 
436  express_route_entry_t() = default;
439  operator = ( express_route_entry_t && ) = default;
440 
442  http_method_id_t method,
443  string_view_t route_path,
444  const path2regex::options_t & options,
445  express_request_handler_t handler )
447  method,
449  route_path,
450  options ),
451  std::move( handler ) }
452  {}
453 
455  http_method_id_t method,
456  string_view_t route_path,
457  express_request_handler_t handler )
459  method,
460  route_path,
462  std::move( handler ) }
463  {}
464 
465  //! Checks if request header matches entry,
466  //! and if so, set route params.
467  bool
468  match( const http_request_header_t & h, route_params_t & params ) const
469  {
470  return m_matcher( h, params );
471  }
472 
473  //! Calls a handler of given request with given params.
476  {
477  return m_handler( std::move( rh ), std::move( rp ) );
478  }
479 
480  //! Try to match the entry and calls a handler with extracted params.
483  {
485  if( match( rh->header(), params ) )
486  return handle( std::move( rh ), std::move( params ) );
487 
488  return request_rejected();
489  }
490 
491  private:
492  impl::route_matcher_t< Regex_Engine > m_matcher;
494 };
495 
496 //
497 // express_router_t
498 //
499 
500 //! Express.js style router.
501 /*
502  Express routers acts as a request handler (it means it is a function-object
503  that can be called as a restinio request handler).
504  It aggregates several endpoint-handlers and picks one or none of them to handle the request.
505  The choice of the handler to execute depends on request target and HTTP method.
506  If router finds no handler matching the request then request is considered unmatched.
507  It is possible to set a handler for unmatched requests, otherwise router rejects the request and
508  RESTinio takes care of it.
509 
510  There is a difference between ordinary restinio request handler
511  and the one that is used with experss router: express_request_handler_t.
512  The signature of a handlers that can be put in router
513  has an additional parameter -- a container with parameters extracted from URI (request target).
514 */
515 template < typename Regex_Engine = std_regex_engine_t>
517 {
518  public:
519  express_router_t() = default;
520  express_router_t( express_router_t && ) = default;
521 
524  {
526  for( const auto & entry : m_handlers )
527  {
528  if( entry.match( req->header(), params ) )
529  {
530  return entry.handle( std::move( req ), std::move( params ) );
531  }
532  }
533 
534  // Here: none of the routes matches this handler.
535 
537  {
538  // If non matched request handler is set
539  // then call it.
541  }
542 
543  return request_rejected();
544  }
545 
546  //! Add handlers.
547  //! \{
548  void
550  http_method_id_t method,
551  string_view_t route_path,
552  express_request_handler_t handler )
553  {
554  add_handler(
555  method,
556  route_path,
558  std::move( handler ) );
559  }
560 
561  void
563  http_method_id_t method,
564  string_view_t route_path,
565  const path2regex::options_t & options,
566  express_request_handler_t handler )
567  {
569  }
570 
571  void
573  string_view_t route_path,
574  express_request_handler_t handler )
575  {
576  add_handler(
578  route_path,
579  std::move( handler ) );
580  }
581 
582  void
584  string_view_t route_path,
585  const path2regex::options_t & options,
586  express_request_handler_t handler )
587  {
588  add_handler(
590  route_path,
591  options,
592  std::move( handler ) );
593  }
594 
595  void
597  string_view_t route_path,
598  express_request_handler_t handler )
599  {
600  add_handler(
601  http_method_get(),
602  route_path,
603  std::move( handler ) );
604  }
605 
606  void
608  string_view_t route_path,
609  const path2regex::options_t & options,
610  express_request_handler_t handler )
611  {
612  add_handler(
613  http_method_get(),
614  route_path,
615  options,
616  std::move( handler ) );
617  }
618 
619  void
621  string_view_t route_path,
622  express_request_handler_t handler )
623  {
624  add_handler(
626  route_path,
627  std::move( handler ) );
628  }
629 
630  void
632  string_view_t route_path,
633  const path2regex::options_t & options,
634  express_request_handler_t handler )
635  {
636  add_handler(
638  route_path,
639  options,
640  std::move( handler ) );
641  }
642 
643  void
645  string_view_t route_path,
646  express_request_handler_t handler )
647  {
648  add_handler(
650  route_path,
651  std::move( handler ) );
652  }
653 
654  void
656  string_view_t route_path,
657  const path2regex::options_t & options,
658  express_request_handler_t handler )
659  {
660  add_handler(
662  route_path,
663  options,
664  std::move( handler ) );
665  }
666 
667  void
669  string_view_t route_path,
670  express_request_handler_t handler )
671  {
672  add_handler(
673  http_method_put(),
674  route_path,
675  std::move( handler ) );
676  }
677 
678  void
680  string_view_t route_path,
681  const path2regex::options_t & options,
682  express_request_handler_t handler )
683  {
684  add_handler(
685  http_method_put(),
686  route_path,
687  options,
688  std::move( handler ) );
689  }
690  //! \}
691 
692  //! Set handler for requests that don't match any route.
693  void
694  non_matched_request_handler( non_matched_request_handler_t nmrh )
695  {
697  }
698 
699  private:
700  using route_entry_t = express_route_entry_t< Regex_Engine >;
701 
702  //! A list of existing routes.
704 
705  //! Handler that is called for requests that don't match any route.
707 };
708 
709 } /* namespace router */
710 
711 //! Cast named parameter value to a given type.
712 template < typename Value_Type >
713 Value_Type
714 get( const router::route_params_t & params, string_view_t key )
715 {
716  return get< Value_Type >( params[ key ] );
717 }
718 
719 //! Cast indexed parameter value to a given type.
720 template < typename Value_Type >
721 Value_Type
722 get( const router::route_params_t & params, std::size_t index )
723 {
724  return get< Value_Type >( params[ index ] );
725 }
726 
727 } /* namespace restinio */
impl::route_matcher_t< Regex_Engine > m_matcher
Definition: express.hpp:492
void non_matched_request_handler(non_matched_request_handler_t nmrh)
Set handler for requests that don&#39;t match any route.
Definition: express.hpp:694
void http_get(string_view_t route_path, express_request_handler_t handler)
Definition: express.hpp:596
bool operator()(const http_request_header_t &h, route_params_t &parameters) const
Definition: express.hpp:368
static const auto & indexed_parameters(const route_params_t &rp) noexcept
Definition: express.hpp:225
express_route_entry_t & operator=(express_route_entry_t &&)=default
route_params_appender_t(route_params_t::named_parameters_container_t &named_parameters, route_params_t::indexed_parameters_container_t &indexed_parameters)
Definition: express.hpp:240
string_view_t operator[](std::size_t i) const
Get indexed parameter.
Definition: express.hpp:120
http_method_id_t m_method
HTTP method to match.
Definition: express.hpp:377
void add_indexed_param(string_view_t value)
Definition: express.hpp:259
route_matcher_t(route_matcher_t &&)=default
Route params private internals accessor.
Definition: express.hpp:196
route_params_t & operator=(route_params_t &&)=default
void http_post(string_view_t route_path, express_request_handler_t handler)
Definition: express.hpp:644
void http_head(string_view_t route_path, express_request_handler_t handler)
Definition: express.hpp:620
void http_delete(string_view_t route_path, const path2regex::options_t &options, express_request_handler_t handler)
Definition: express.hpp:583
express_route_entry_t()=default
void add_named_param(string_view_t key, string_view_t value)
Definition: express.hpp:253
route_matcher_t(http_method_id_t method, regex_t route_regex, std::shared_ptr< std::string > named_params_buffer, param_appender_sequence_t param_appender_sequence)
Creates matcher with a given parameters.
Definition: express.hpp:285
regex_t m_route_regex
Regex of a given route.
Definition: express.hpp:380
auto indexed_parameters_size() const noexcept
Definition: express.hpp:131
Express.js style router.
Definition: express.hpp:516
void http_put(string_view_t route_path, express_request_handler_t handler)
Definition: express.hpp:668
std::shared_ptr< std::string > m_named_params_buffer
Buffer for named parameters names string views.
Definition: express.hpp:383
void add_handler(http_method_id_t method, string_view_t route_path, const path2regex::options_t &options, express_request_handler_t handler)
Definition: express.hpp:562
auto named_parameters_size() const noexcept
Get number of parameters.
Definition: express.hpp:130
express_request_handler_t m_handler
Definition: express.hpp:493
std::vector< route_entry_t > m_handlers
A list of existing routes.
Definition: express.hpp:703
std::shared_ptr< std::string > m_key_names_buffer
Shared buffer for string_view of named parameterts names.
Definition: express.hpp:176
std::unique_ptr< char[] > m_request_target
A raw request target.
Definition: express.hpp:173
request_handling_status_t handle(request_handle_t rh, route_params_t rp) const
Calls a handler of given request with given params.
Definition: express.hpp:475
static const auto & named_parameters(const route_params_t &rp) noexcept
Get values containers for all parameters (used in unit tests).
Definition: express.hpp:219
named_parameters_container_t::const_reference find_named_parameter_with_check(string_view_t key) const
Definition: express.hpp:148
static void match(route_params_t &rp, std::unique_ptr< char[] > request_target, std::shared_ptr< std::string > key_names_buffer, string_view_t match_, route_params_t::named_parameters_container_t named_parameters, route_params_t::indexed_parameters_container_t indexed_parameters)
Init parameters with a matched route params.
Definition: express.hpp:200
express_router_t(express_router_t &&)=default
indexed_parameters_container_t m_indexed_parameters
Indexed params.
Definition: express.hpp:185
string_view_t m_match
Matched pattern.
Definition: express.hpp:179
void http_post(string_view_t route_path, const path2regex::options_t &options, express_request_handler_t handler)
Definition: express.hpp:655
A matcher for a given path.
Definition: express.hpp:278
route_params_t(route_params_t &&)=default
named_parameters_container_t m_named_parameters
Named params.
Definition: express.hpp:182
bool match(const http_request_header_t &h, route_params_t &params) const
Checks if request header matches entry, and if so, set route params.
Definition: express.hpp:468
express_route_entry_t(http_method_id_t method, string_view_t route_path, const path2regex::options_t &options, express_request_handler_t handler)
Definition: express.hpp:441
A single express route entry.
Definition: express.hpp:416
Value_Type get(const router::route_params_t &params, std::size_t index)
Cast indexed parameter value to a given type.
Definition: express.hpp:722
string_view_t match() const noexcept
Matched route.
Definition: express.hpp:90
route_params_t & operator=(const route_params_t &)=delete
express_route_entry_t(http_method_id_t method, string_view_t route_path, express_request_handler_t handler)
Definition: express.hpp:454
bool match_route(string_view_t target_path, route_params_t &parameters) const
Try to match a given request target with this route.
Definition: express.hpp:301
route_params_appender_t & operator=(const route_params_appender_t &)=delete
express_route_entry_t(const express_route_entry_t &)=delete
request_handling_status_t try_to_handle(request_handle_t rh) const
Try to match the entry and calls a handler with extracted params.
Definition: express.hpp:482
route_params_t::named_parameters_container_t & m_named_parameters
Definition: express.hpp:265
route_params_appender_t & operator=(route_params_appender_t &&)=delete
route_params_appender_t(const route_params_appender_t &)=delete
express_route_entry_t & operator=(const express_route_entry_t &)=delete
param_appender_sequence_t m_param_appender_sequence
Parameters values.
Definition: express.hpp:386
void http_head(string_view_t route_path, const path2regex::options_t &options, express_request_handler_t handler)
Definition: express.hpp:631
bool has(string_view_t key) const noexcept
Check parameter.
Definition: express.hpp:101
named_parameters_container_t::const_iterator find_named_parameter(string_view_t key) const noexcept
Definition: express.hpp:136
void http_delete(string_view_t route_path, express_request_handler_t handler)
Definition: express.hpp:572
void match(std::unique_ptr< char[] > request_target, std::shared_ptr< std::string > key_names_buffer, string_view_t match, named_parameters_container_t named_parameters, indexed_parameters_container_t indexed_parameters)
Definition: express.hpp:66
void http_get(string_view_t route_path, const path2regex::options_t &options, express_request_handler_t handler)
Definition: express.hpp:607
Helper class for gthering parameters from route.
Definition: express.hpp:237
optional_t< string_view_t > get_param(string_view_t key) const noexcept
Get the value of a parameter if it exists.
Definition: express.hpp:109
void add_handler(http_method_id_t method, string_view_t route_path, express_request_handler_t handler)
Add handlers.
Definition: express.hpp:549
non_matched_request_handler_t m_non_matched_request_handler
Handler that is called for requests that don&#39;t match any route.
Definition: express.hpp:706
request_handling_status_t operator()(request_handle_t req) const
Definition: express.hpp:523
route_params_appender_t(route_params_appender_t &&)=delete
void http_put(string_view_t route_path, const path2regex::options_t &options, express_request_handler_t handler)
Definition: express.hpp:679
route_params_t(const route_params_t &)=delete
express_route_entry_t(express_route_entry_t &&)=default
route_params_t::indexed_parameters_container_t & m_indexed_parameters
Definition: express.hpp:266
std::enable_if< std::is_same< Parameter_Container, query_string_params_t >::value||std::is_same< Parameter_Container, router::route_params_t >::value, optional_t< Value_Type > >::type opt_value(const Parameter_Container &params, string_view_t key)
Gets the value of a parameter specified by key wrapped in optional_t<Value_Type> if parameter exists ...
Definition: value_or.hpp:64