RESTinio
method_matcher.hpp
Go to the documentation of this file.
1 /*
2  * RESTinio
3  */
4 
5 /**
6  * @file
7  * @brief Stuff related to method_matchers.
8  *
9  * @since v.0.6.6
10  */
11 
12 #pragma once
13 
14 #include <restinio/http_headers.hpp>
15 
16 #include <initializer_list>
17 #include <vector>
18 
19 namespace restinio
20 {
21 
22 namespace router
23 {
24 
25 //
26 // method_matcher_t
27 //
28 /*!
29  * @brief An interface of method_matcher.
30  *
31  * Method_matchers are used by routers to detect an applicability
32  * of an incoming request to a route. If method_matcher_t::match()
33  * returns `true` then HTTP method from an incoming request is
34  * applicable to a route.
35  *
36  * @since v.0.6.6
37  */
39 {
40  method_matcher_t( const method_matcher_t & ) = default;
42  operator=( const method_matcher_t & ) = default;
43 
44  method_matcher_t( method_matcher_t && ) = default;
46  operator=( method_matcher_t && ) = default;
47 
48  method_matcher_t() = default;
49  virtual ~method_matcher_t() = default;
50 
51  //! Is the specified method can be applied to a route?
52  /*!
53  * \retval true if @a method can be applied to a route.
54  * \retval false if @a method can't be applied to a route.
55  */
57  virtual bool
58  match( const http_method_id_t & method ) const noexcept = 0;
59 };
60 
61 namespace impl
62 {
63 
64 //
65 // allocated_matcher_proxy_t
66 //
67 /*!
68  * @brief A proxy for actual method_matcher that will be allocated
69  * in dynamic memory.
70  *
71  * An instance of Matcher class will be allocated automatically in
72  * the constructor of allocated_matcher_proxy_t.
73  *
74  * @note
75  * This is a moveable class.
76  *
77  * @since v.0.6.6
78  */
79 template< typename Matcher >
81 {
83 
84 public :
85  template< typename... Args >
86  allocated_matcher_proxy_t( Args && ...args )
88  {}
89 
91  bool
92  match( const http_method_id_t & method ) const noexcept override
93  {
94  return m_matcher->match( method );
95  }
96 };
97 
98 //
99 // simple_matcher_t
100 //
101 /*!
102  * @brief A simple method_matcher that compares just one user-specified
103  * value.
104  *
105  * The allowed value is specified in the constructor and can't be changed
106  * after that.
107  *
108  * @since v.0.6.6
109  */
111 {
113 
114 public :
115  simple_matcher_t( http_method_id_t method )
116  : m_method{ std::move(method) }
117  {}
118 
120  bool
121  match( const http_method_id_t & method ) const noexcept override
122  {
123  return m_method == method;
124  }
125 };
126 
127 //
128 // fixed_size_any_of_matcher_t
129 //
130 /*!
131  * @brief A matcher that finds a value in the vector of allowed values
132  * of fixed size.
133  *
134  * A method is allowed if it's found in the vector of allowed values.
135  *
136  * @since v.0.6.6
137  */
138 template< std::size_t Size >
140 {
142 
143 public :
144  /*!
145  * @brief Initializing constructor.
146  *
147  * @attention
148  * The values.size() is expected to be equal to Size. The behavior is
149  * undefined otherwise.
150  */
152  std::initializer_list< http_method_id_t > values )
153  {
154  assert( Size == values.size() );
155 
157  }
158 
160  bool
161  match( const http_method_id_t & method ) const noexcept override
162  {
163  for( const auto & m : m_methods )
164  if( m == method )
165  return true;
166 
167  return false;
168  }
169 };
170 
171 //
172 // fixed_size_none_of_matcher_t
173 //
174 /*!
175  * @brief A matcher that finds a value in the vector of disabled values
176  * of fixed size.
177  *
178  * A method is allowed if it isn't found in the vector of disabled values.
179  *
180  * @since v.0.6.6
181  */
182 template< std::size_t Size >
184  : public fixed_size_any_of_matcher_t<Size>
185 {
186  using base_type_t = fixed_size_any_of_matcher_t<Size>;
187 
188 public :
189  using base_type_t::base_type_t;
190 
192  bool
193  match( const http_method_id_t & method ) const noexcept override
194  {
195  return !base_type_t::match( method );
196  }
197 };
198 
199 //
200 // buffered_matcher_holder_t
201 //
202 /*!
203  * @brief A special class that allows to hold a copy of small-size
204  * method_matchers or a pointer to dynamically allocated large-size
205  * method_matchers.
206  *
207  * An instance of this class looks like a smart pointer to
208  * method_matcher_t. This smart pointer is moveable, but not
209  * copyable (it's like unique_ptr).
210  *
211  * A value is set by assign() method:
212  * @code
213  * buffered_matcher_holder_t matcher;
214  * matcher.assign<fixed_size_any_of_matcher_t<10>>(
215  * std::initializer_list<http_method_id_t>{
216  * http_method_get(),
217  * http_method_head(),
218  * ...
219  * });
220  * @endcode
221  *
222  * @since v.0.6.6
223  */
225 {
226  //! The size of the internal buffer.
227  static constexpr std::size_t buffer_size =
228  sizeof(fixed_size_any_of_matcher_t<4>);
229 
230  //! Alignment to be used by the internal buffer.
231  static constexpr std::size_t alignment =
232  std::max( {
233  alignof(simple_matcher_t),
234  alignof(fixed_size_any_of_matcher_t<4>),
236  fixed_size_any_of_matcher_t<20>>) } );
237 
238  //! A type of free function to be used to move a value of
239  //! an object to the specified buffer.
240  /*!
241  * This function should allocate a new instance in @a buffer and
242  * move the content of @a object into it. The pointer to the allocated
243  * instance should be returned.
244  */
245  using pfn_move_t = method_matcher_t* (*)(void* object, void* buffer);
246 
247  //! A pointer to actual matcher allocated inside the internall buffer.
248  /*!
249  * Can be nullptr. For example: just after the creation and before
250  * the call to assign(). Or after a move-constructor or move-operator.
251  */
253 
254  //! The internal buffer.
256 
257  //! An actual move-function.
258  /*!
259  * Can be nullptr if assign() is not called yet.
260  */
261  pfn_move_t m_mover{ nullptr };
262 
263  void
265  {
267  }
268 
269  void
271  {
272  if( other.m_matcher )
273  {
274  m_matcher = other.m_mover( other.m_matcher, m_buffer.data() );
275  m_mover = other.m_mover;
276 
277  other.m_matcher = nullptr;
278  other.m_mover = nullptr;
279  }
280  }
281 
282 public :
283  buffered_matcher_holder_t() = default;
284 
286  {
287  cleanup();
288  }
289 
291  const buffered_matcher_holder_t & ) = delete;
292 
294  operator=(
295  const buffered_matcher_holder_t & ) = delete;
296 
298  buffered_matcher_holder_t && other ) noexcept
299  {
300  move_from( other );
301  }
302 
304  operator=( buffered_matcher_holder_t && other ) noexcept
305  {
306  if( this != &other )
307  {
308  cleanup();
309  move_from( other );
310  }
311 
312  return *this;
313  }
314 
315  /*!
316  * @brief Creates an instance of Target_Type and initializes it
317  * with arguments Args.
318  *
319  * Previous value of buffered_matcher_holder_t will be destroyed.
320  *
321  * A new object is created in the internal buffer if its size is
322  * not greater than buffer_size. Otherwise a new object is created
323  * in dynamic memory and allocated_matcher_proxy_t for it
324  * is placed into the internal buffer.
325  *
326  */
327  template< typename Target_Type, typename... Args >
328  void
329  assign( Args &&... args )
330  {
331  static_assert( alignof(Target_Type) <= alignment,
332  "Target_Type should have appropriate alignment" );
333 
334  cleanup();
335 
336  if( sizeof(Target_Type) <= buffer_size )
337  {
339  m_mover = [](void * raw_what, void * dest_storage) -> method_matcher_t * {
340  auto * what = reinterpret_cast<Target_Type *>(raw_what);
341  return new(dest_storage) Target_Type{ std::move(*what) };
342  };
343  }
344  else
345  {
348  m_mover = [](void * raw_what, void * dest_storage) -> method_matcher_t * {
349  auto * what = reinterpret_cast<actual_type *>(raw_what);
350  return new(dest_storage) actual_type{ std::move(*what) };
351  };
352  }
353  }
354 
355  //! Get the pointer to actual matcher inside the holder.
356  RESTINIO_NODISCARD
357  method_matcher_t *
358  get() const noexcept { return m_matcher; }
359 
360  //! Get the pointer to actual matcher inside the holder.
363  operator->() const noexcept { return m_matcher; }
364 
365  //! Get a reference to actual matcher inside the holder.
368  operator*() const noexcept { return *m_matcher; }
369 
370  friend void
372  {
374  }
375 
376  template< typename Arg >
377  friend void
379  {
381 
382  static_assert( std::is_base_of<
384  "Arg should be derived from method_matcher_t" );
385 
388  }
389 };
390 
391 } /* namespace impl */
392 
393 //
394 // any_of_methods
395 //
396 /*!
397  * @brief A factory function that creates a method_matcher that allows
398  * a method if it's found in the list of allowed methods.
399  *
400  * Usage example:
401  * @code
402  * router->add_handler(
403  * restinio::router::any_of_methods(
404  * restinio::http_method_get(), restinio::http_method_head()),
405  * "/users/:id",
406  * [](const auto & req, auto & params) {...});
407  * @endcode
408  *
409  * @note
410  * Returns the created object by value without any allocations.
411  *
412  * @since v.0.6.6
413  */
414 template< typename... Args >
418 {
419  return { std::initializer_list<http_method_id_t>{ std::forward<Args>(args)... } };
420 }
421 
422 //
423 // none_of_methods
424 //
425 /*!
426  * @brief A factory function that creates a method_matcher that allows
427  * a method if it isn't found in the list of disabled methods.
428  *
429  * Usage example:
430  * @code
431  * router->add_handler(
432  * restinio::router::none_of_methods(
433  * restinio::http_method_get(), restinio::http_method_head()),
434  * "/users/:id",
435  * [](const auto & req, auto &) {
436  * return req->create_response(status_method_not_allowed())
437  * .connection_close().done();
438  * });
439  * @endcode
440  *
441  * @note
442  * Returns the created object by value without any allocations.
443  *
444  * @since v.0.6.6
445  */
446 template< typename... Args >
450 {
451  return { std::initializer_list<http_method_id_t>{ std::forward<Args>(args)... } };
452 }
453 
454 //
455 // dynamic_any_of_methods_matcher_t
456 //
457 /*!
458  * @brief An implementation of method_matcher that allows a method
459  * if it's found in a dynamic list of allowed methods.
460  *
461  * Usage example:
462  * @code
463  * restinio::router::dynamic_any_of_methods_matcher_t matcher;
464  * if(config.handle_get_method())
465  * matcher.add(restinio::http_method_get());
466  * if(config.handle_head_method())
467  * matcher.add(restinio::http_method_head());
468  * router->add_handler(matcher, // Or std::move(matcher) if matcher is no more needed.
469  * "/users/:id",
470  * [](const auto & req, auto & params) {...});
471  * @endcode
472  *
473  * @since v.0.6.6
474  */
476 {
478 
479 public:
481 
483  bool
484  match( const http_method_id_t & method ) const noexcept override
485  {
486  for( const auto & m : m_methods )
487  if( m == method )
488  return true;
489 
490  return false;
491  }
492 
495  {
497  return *this;
498  }
499 
501  std::size_t
502  size() const noexcept
503  {
504  return m_methods.size();
505  }
506 
508  bool
509  empty() const noexcept
510  {
511  return m_methods.empty();
512  }
513 };
514 
515 //
516 // dynamic_none_of_methods_matcher_t
517 //
518 /*!
519  * @brief An implementation of method_matcher that allows a method
520  * if it isn't found in a dynamic list of disabled methods.
521  *
522  * Usage example:
523  * @code
524  * restinio::router::dynamic_none_of_methods_matcher_t matcher;
525  * if(config.handle_get_method())
526  * matcher.add(restinio::http_method_get());
527  * if(config.handle_head_method())
528  * matcher.add(restinio::http_method_head());
529  * router->add_handler(matcher, // Or std::move(matcher) if matcher is no more needed.
530  * "/users/:id",
531  * [](const auto & req, auto & params) {...});
532  * @endcode
533  *
534  * @since v.0.6.6
535  */
537 {
539 
540 public:
542 
544  bool
545  match( const http_method_id_t & method ) const noexcept override
546  {
547  for( const auto & m : m_methods )
548  if( m == method )
549  return false;
550 
551  return true;
552  }
553 
556  {
558  return *this;
559  }
560 
562  std::size_t
563  size() const noexcept
564  {
565  return m_methods.size();
566  }
567 
569  bool
570  empty() const noexcept
571  {
572  return m_methods.empty();
573  }
574 };
575 
576 } /* namespace router */
577 
578 } /* namespace restinio */
method_matcher_t & operator=(method_matcher_t &&)=default
std::array< char, buffer_size > m_buffer
The internal buffer.
RESTINIO_NODISCARD bool match(const http_method_id_t &method) const noexcept override
Is the specified method can be applied to a route?
method_matcher_t(const method_matcher_t &)=default
void assign(Args &&... args)
Creates an instance of Target_Type and initializes it with arguments Args.
buffered_matcher_holder_t & operator=(const buffered_matcher_holder_t &)=delete
buffered_matcher_holder_t & operator=(buffered_matcher_holder_t &&other) noexcept
fixed_size_any_of_matcher_t(std::initializer_list< http_method_id_t > values)
Initializing constructor.
RESTINIO_NODISCARD bool empty() const noexcept
friend void assign(buffered_matcher_holder_t &holder, Arg &&method_matcher)
RESTINIO_NODISCARD bool match(const http_method_id_t &method) const noexcept override
Is the specified method can be applied to a route?
std::array< http_method_id_t, Size > m_methods
RESTINIO_NODISCARD impl::fixed_size_any_of_matcher_t< sizeof...(Args) > any_of_methods(Args &&...args)
A factory function that creates a method_matcher that allows a method if it&#39;s found in the list of al...
static constexpr std::size_t buffer_size
The size of the internal buffer.
RESTINIO_NODISCARD bool empty() const noexcept
void move_from(buffered_matcher_holder_t &other)
method_matcher_t & operator=(const method_matcher_t &)=default
virtual RESTINIO_NODISCARD bool match(const http_method_id_t &method) const noexcept=0
Is the specified method can be applied to a route?
pfn_move_t m_mover
An actual move-function.
buffered_matcher_holder_t(buffered_matcher_holder_t &&other) noexcept
RESTINIO_NODISCARD bool match(const http_method_id_t &method) const noexcept override
Is the specified method can be applied to a route?
method_matcher_t(method_matcher_t &&)=default
method_matcher_t * m_matcher
A pointer to actual matcher allocated inside the internall buffer.
static constexpr std::size_t alignment
Alignment to be used by the internal buffer.
buffered_matcher_holder_t(const buffered_matcher_holder_t &)=delete
RESTINIO_NODISCARD impl::fixed_size_none_of_matcher_t< sizeof...(Args) > none_of_methods(Args &&...args)
A factory function that creates a method_matcher that allows a method if it isn&#39;t found in the list o...
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
RESTINIO_NODISCARD bool match(const http_method_id_t &method) const noexcept override
Is the specified method can be applied to a route?