RESTinio
Loading...
Searching...
No Matches
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
19namespace restinio
20{
21
22namespace 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
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 */
56 [[nodiscard]]
57 virtual bool
58 match( const http_method_id_t & method ) const noexcept = 0;
59};
60
61namespace 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 */
79template< typename Matcher >
81{
83
84public :
85 template< typename... Args >
86 allocated_matcher_proxy_t( Args && ...args )
88 {}
89
90 [[nodiscard]]
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
114public :
116 : m_method{ std::move(method) }
117 {}
118
119 [[nodiscard]]
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 */
138template< std::size_t Size >
140{
141 std::array< http_method_id_t, Size > m_methods;
142
143public :
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
156 std::copy( values.begin(), values.end(), m_methods.begin() );
157 }
158
159 [[nodiscard]]
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 */
182template< std::size_t Size >
184 : public fixed_size_any_of_matcher_t<Size>
185{
187
188public :
190
191 [[nodiscard]]
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 =
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>),
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.
255 alignas(alignment) std::array<char, buffer_size> m_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
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
282public :
284
286 {
287 cleanup();
288 }
289
291 const buffered_matcher_holder_t & ) = delete;
292
295 const buffered_matcher_holder_t & ) = delete;
296
298 buffered_matcher_holder_t && other ) noexcept
299 {
300 move_from( other );
301 }
302
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 {
338 m_matcher = new(m_buffer.data()) Target_Type{ std::forward<Args>(args)... };
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 {
346 using actual_type = allocated_matcher_proxy_t<Target_Type>;
347 m_matcher = new(m_buffer.data()) actual_type{ std::forward<Args>(args)... };
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 [[nodiscard]]
358 get() const noexcept { return m_matcher; }
359
360 //! Get the pointer to actual matcher inside the holder.
361 [[nodiscard]]
363 operator->() const noexcept { return m_matcher; }
364
365 //! Get a reference to actual matcher inside the holder.
366 [[nodiscard]]
368 operator*() const noexcept { return *m_matcher; }
369
370 friend void
372 {
373 holder.assign< simple_matcher_t >( std::move(method) );
374 }
375
376 template< typename Arg >
377 friend void
378 assign( buffered_matcher_holder_t & holder, Arg && method_matcher )
379 {
380 using pure_method_matcher_type = std::decay_t<Arg>;
381
382 static_assert( std::is_base_of<
383 method_matcher_t, pure_method_matcher_type >::value,
384 "Arg should be derived from method_matcher_t" );
385
386 holder.assign< pure_method_matcher_type >(
387 std::forward<Arg>(method_matcher) );
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 */
414template< typename... Args >
415[[nodiscard]]
416impl::fixed_size_any_of_matcher_t< sizeof...(Args) >
417any_of_methods( Args && ...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 */
446template< typename... Args >
447[[nodiscard]]
448impl::fixed_size_none_of_matcher_t< sizeof...(Args) >
449none_of_methods( Args && ...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
479public:
481
482 [[nodiscard]]
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 {
496 m_methods.emplace_back( std::move(method) );
497 return *this;
498 }
499
500 [[nodiscard]]
501 std::size_t
502 size() const noexcept
503 {
504 return m_methods.size();
505 }
506
507 [[nodiscard]]
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
540public:
542
543 [[nodiscard]]
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 {
557 m_methods.emplace_back( std::move(method) );
558 return *this;
559 }
560
561 [[nodiscard]]
562 std::size_t
563 size() const noexcept
564 {
565 return m_methods.size();
566 }
567
568 [[nodiscard]]
569 bool
570 empty() const noexcept
571 {
572 return m_methods.empty();
573 }
574};
575
576} /* namespace router */
577
578} /* namespace restinio */
A type for representation of HTTP method ID.
friend constexpr bool operator==(const http_method_id_t &a, const http_method_id_t &b) noexcept
An implementation of method_matcher that allows a method if it's found in a dynamic list of allowed m...
dynamic_any_of_methods_matcher_t & add(http_method_id_t method)
bool match(const http_method_id_t &method) const noexcept override
Is the specified method can be applied to a route?
An implementation of method_matcher that allows a method if it isn't found in a dynamic list of disab...
bool match(const http_method_id_t &method) const noexcept override
Is the specified method can be applied to a route?
dynamic_none_of_methods_matcher_t & add(http_method_id_t method)
A proxy for actual method_matcher that will be allocated in dynamic memory.
bool match(const http_method_id_t &method) const noexcept override
Is the specified method can be applied to a route?
A special class that allows to hold a copy of small-size method_matchers or a pointer to dynamically ...
method_matcher_t * operator->() const noexcept
Get the pointer to actual matcher inside the holder.
std::array< char, buffer_size > m_buffer
The internal buffer.
friend void assign(buffered_matcher_holder_t &holder, Arg &&method_matcher)
friend void assign(buffered_matcher_holder_t &holder, http_method_id_t method)
void move_from(buffered_matcher_holder_t &other)
buffered_matcher_holder_t & operator=(buffered_matcher_holder_t &&other) noexcept
buffered_matcher_holder_t & operator=(const buffered_matcher_holder_t &)=delete
method_matcher_t *(*)(void *object, void *buffer) pfn_move_t
A type of free function to be used to move a value of an object to the specified buffer.
method_matcher_t & operator*() const noexcept
Get a reference to actual matcher inside the holder.
void assign(Args &&... args)
Creates an instance of Target_Type and initializes it with arguments Args.
buffered_matcher_holder_t(buffered_matcher_holder_t &&other) noexcept
static constexpr std::size_t alignment
Alignment to be used by the internal buffer.
method_matcher_t * get() const noexcept
Get the pointer to actual matcher inside the holder.
static constexpr std::size_t buffer_size
The size of the internal buffer.
method_matcher_t * m_matcher
A pointer to actual matcher allocated inside the internall buffer.
buffered_matcher_holder_t(const buffered_matcher_holder_t &)=delete
A matcher that finds a value in the vector of allowed values of fixed size.
fixed_size_any_of_matcher_t(std::initializer_list< http_method_id_t > values)
Initializing constructor.
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
A matcher that finds a value in the vector of disabled values of fixed size.
fixed_size_any_of_matcher_t< Size > base_type_t
bool match(const http_method_id_t &method) const noexcept override
Is the specified method can be applied to a route?
A simple method_matcher that compares just one user-specified value.
bool match(const http_method_id_t &method) const noexcept override
Is the specified method can be applied to a route?
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't found in the list o...
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's found in the list of al...
An interface of method_matcher.
virtual bool match(const http_method_id_t &method) const noexcept=0
Is the specified method can be applied to a route?
method_matcher_t(method_matcher_t &&)=default
method_matcher_t & operator=(const method_matcher_t &)=default
method_matcher_t & operator=(method_matcher_t &&)=default
method_matcher_t(const method_matcher_t &)=default