RESTinio
request_handler.hpp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /*!
6  HTTP-request handlers routine.
7 */
8 
9 #pragma once
10 
11 #include <restinio/compiler_features.hpp>
12 
13 #include <restinio/exception.hpp>
14 #include <restinio/http_headers.hpp>
15 #include <restinio/message_builders.hpp>
16 #include <restinio/chunked_input_info.hpp>
17 #include <restinio/impl/connection_base.hpp>
18 
19 #include <array>
20 #include <functional>
21 #include <iosfwd>
22 #include <new>
23 
24 namespace restinio
25 {
26 
27 //
28 // extra_data_buffer_t
29 //
30 /*!
31  * @brief Helper for holding a pointer to a buffer where a new
32  * object of type Extra_Data should be constructed.
33  *
34  * This class is intended to make the construction of new objects
35  * of type Extra_Data inside a preallocated buffer more type-safe.
36  *
37  * An instance of Extra_Data is incorporated into a request object
38  * by holding a buffer of necessary capacity and alignment inside
39  * request object. The `make_within` method of extra-data-factory
40  * is called for the construction of new instance of Extra_Data
41  * in that buffer. If raw void pointer will be passed to
42  * `make_within` method then it would make possible a case when
43  * wrong extra-data-factory can be used.
44  *
45  * But if a pointer to the buffer for new instance will be wrapped
46  * into extra_data_buffer_t then it allows additional type checks
47  * from the compiler. That is why a extra-data-factory receives
48  * extra_data_buffer_t<Extra_Data> as a parameter to `make_within`
49  * instead of raw pointers.
50  *
51  * @since v.0.6.13
52  */
53 template< typename Extra_Data >
55 {
56  void * m_buffer;
57 
58 public:
59  extra_data_buffer_t( void * buffer ) : m_buffer{ buffer } {}
60 
61  [[nodiscard]]
62  void *
63  get() const noexcept { return m_buffer; }
64 };
65 
66 //
67 // no_extra_data_factory_t
68 //
69 /*!
70  * @brief The default extra-data-factory to be used in server's traits if
71  * a user doesn't specify own one.
72  *
73  * This factory doesn't nothing. And holds an empty struct as `data_t` member.
74  *
75  * @since v.0.6.13
76  */
78 {
79  /*!
80  * @brief A type of extra-data to be incorporated into a request object
81  * by the default.
82  */
83  struct data_t {};
84 
85  void
87  {
88  new(buffer.get()) data_t{};
89  }
90 };
91 
92 //
93 // simple_extra_data_factory_t
94 //
95 /*!
96  * @brief A helper template class for cases when extra-data-factory is
97  * just a simple stateless object.
98  *
99  * Usage example:
100  * @code
101  * struct first_stage_data { ... };
102  * struct second_stage_data { ... };
103  * struct third_stage_data { ... };
104  *
105  * using my_extra_data_factory = restinio::simple_extra_data_factory_t<
106  * std::tuple<first_stage_data, second_stage_data, third_stage_data> >;
107  *
108  * struct my_traits : public restinio::default_traits_t
109  * {
110  * using extra_data_factory_t = my_extra_data_factory;
111  * };
112  * @endcode
113  *
114  * @tparam Extra_Data Type of extra-data to be incorporated into request-objects.
115  *
116  * @since v.0.6.13
117  */
118 template< typename Extra_Data >
120 {
121  using data_t = Extra_Data;
122 
123  void
125  noexcept( noexcept(new(buffer.get()) data_t{}) )
126  {
127  new(buffer.get()) data_t{};
128  }
129 };
130 
131 template< typename Extra_Data >
132 class generic_request_t;
133 
134 namespace impl
135 {
136 
137 template< typename Extra_Data >
140 
141 //
142 // generic_request_extra_data_holder_t
143 //
144 /*!
145  * @brief Helper class for holding a buffer for extra-data object to
146  * be incorporated into a request object.
147  *
148  * It constructs a new object inside internal buffer @a m_data in
149  * the constructor and correctly destroys extra-data object in the
150  * destructor.
151  *
152  * @since v.0.6.13
153  */
154 template< typename Extra_Data >
156 {
157  alignas(Extra_Data) std::array<char, sizeof(Extra_Data)> m_data;
158 
159 public:
160  template< typename Factory >
162  Factory & factory )
163  {
165  }
166 
168  {
169  get_ptr()->~Extra_Data();
170  }
171 
172  [[nodiscard]]
173  Extra_Data *
174  get_ptr() noexcept
175  {
176  // Because the content of m_data.data() is rewritten by
177  // placement new we have to use std::launder to avoid UB.
178  return std::launder(
179  reinterpret_cast<Extra_Data *>(m_data.data()) );
180  }
181 
182  [[nodiscard]]
183  const Extra_Data *
184  get_ptr() const noexcept
185  {
186  // Because the content of m_data.data() is rewritten by
187  // placement new we have to use std::launder to avoid UB.
188  return std::launder(
189  reinterpret_cast<const Extra_Data *>(m_data.data()) );
190  }
191 };
192 
193 } /* namespace impl */
194 
195 //
196 // generic_request_t
197 //
198 
199 //! HTTP Request data.
200 /*!
201  Provides acces to header and body, and creates response builder
202  for a given request.
203 
204  @tparam Extra_Data The type of extra-data to be incorporated into
205  a request object.
206 */
207 template< typename Extra_Data >
208 class generic_request_t final
209  : public std::enable_shared_from_this< generic_request_t< Extra_Data > >
210 {
211  template< typename UD >
212  friend impl::connection_handle_t &
214 
215  public:
216  //! Old-format initializing constructor.
217  /*!
218  * Can be used in cases where chunked_input_info_t is not
219  * available (or needed).
220  */
221  template< typename Extra_Data_Factory >
223  request_id_t request_id,
224  http_request_header_t header,
225  std::string body,
226  impl::connection_handle_t connection,
227  endpoint_t remote_endpoint,
228  Extra_Data_Factory & extra_data_factory )
229  : generic_request_t{
230  request_id,
231  std::move( header ),
232  std::move( body ),
233  chunked_input_info_unique_ptr_t{},
234  std::move( connection ),
235  std::move( remote_endpoint ),
236  extra_data_factory
237  }
238  {}
239 
240  //! New-format initializing constructor.
241  /*!
242  * @since v.0.6.9
243  */
244  template< typename Extra_Data_Factory >
246  request_id_t request_id,
247  http_request_header_t header,
248  std::string body,
249  chunked_input_info_unique_ptr_t chunked_input_info,
250  impl::connection_handle_t connection,
251  endpoint_t remote_endpoint,
252  Extra_Data_Factory & extra_data_factory )
253  : m_request_id{ request_id }
254  , m_header{ std::move( header ) }
255  , m_body{ std::move( body ) }
256  , m_chunked_input_info{ std::move( chunked_input_info ) }
257  , m_connection{ std::move( connection ) }
258  , m_connection_id{ m_connection->connection_id() }
259  , m_remote_endpoint{ std::move( remote_endpoint ) }
260  , m_extra_data_holder{ extra_data_factory }
261  {}
262 
263  //! Get request header.
264  const http_request_header_t &
265  header() const noexcept
266  {
267  return m_header;
268  }
269 
270  //! Get request body.
271  const std::string &
272  body() const noexcept
273  {
274  return m_body;
275  }
276 
277  template < typename Output = restinio_controlled_output_t >
278  auto
279  create_response( http_status_line_t status_line = status_ok() )
280  {
282 
283  return response_builder_t< Output >{
284  status_line,
285  std::move( m_connection ),
286  m_request_id,
287  m_header.should_keep_alive() };
288  }
289 
290  //! Get request id.
291  auto request_id() const noexcept { return m_request_id; }
292 
293  //! Get connection id.
294  connection_id_t connection_id() const noexcept { return m_connection_id; }
295 
296  //! Get the remote endpoint of the underlying connection.
297  const endpoint_t & remote_endpoint() const noexcept { return m_remote_endpoint; }
298 
299  //! Get optional info about chunked input.
300  /*!
301  * @note
302  * nullptr will be returned if chunked-encoding wasn't used in
303  * the incoming request.
304  *
305  * @since v.0.6.9
306  */
308  chunked_input_info() const noexcept
309  {
310  return m_chunked_input_info.get();
311  }
312 
313  /*!
314  * @brief Get writeable access to extra-data object incorporated
315  * into a request object.
316  *
317  * @note
318  * This method is present always but it has the sense only if
319  * Extra_Data is not no_extra_data_factory_t::data_t.
320  *
321  * Usage example:
322  * @code
323  * struct my_extra_data_factory {
324  * struct data_t {
325  * user_identity user_id_;
326  * ...
327  * };
328  *
329  * void make_within(restinio::extra_data_buffer_t<data_t> buf) {
330  * new(buf.get()) data_t{};
331  * }
332  * };
333  *
334  * struct my_traits : public restinio::default_traits_t {
335  * using extra_data_factory_t = my_extra_data_factory;
336  * };
337  *
338  * restinio::request_handling_status_t authentificator(
339  * const restinio::generic_request_handle_t<my_extra_data_factory::data_t> & req) {
340  * auto & ud = req->extra_data();
341  * ...
342  * ud.user_id_ = some_calculated_user_id;
343  * }
344  * @endcode
345  *
346  * @since v.0.6.13
347  */
348  [[nodiscard]]
349  Extra_Data &
350  extra_data() noexcept
351  {
352  return *m_extra_data_holder.get_ptr();
353  }
354 
355  /*!
356  * @brief Get readonly access to extra-data object incorporated
357  * into a request object.
358  *
359  * @note
360  * This method is present always but it has the sense only if
361  * Extra_Data is not no_extra_data_factory_t::data_t.
362  *
363  * Usage example:
364  * @code
365  * struct my_extra_data_factory {
366  * struct data_t {
367  * user_identity user_id_;
368  * ...
369  * };
370  *
371  * void make_within(restinio::extra_data_buffer_t<data_t> buf) {
372  * new(buf.get()) data_t{};
373  * }
374  * };
375  *
376  * struct my_traits : public restinio::default_traits_t {
377  * using extra_data_factory_t = my_extra_data_factory;
378  * };
379  *
380  * restinio::request_handling_status_t actual_handler(
381  * const restinio::generic_request_handle_t<my_extra_data_factory::data_t> & req) {
382  * const auto & ud = req->extra_data();
383  * if(ud.user_id_.valid()) {
384  * ...
385  * }
386  * else {
387  * ...
388  * }
389  * }
390  * @endcode
391  *
392  * @since v.0.6.13
393  */
394  [[nodiscard]]
395  const Extra_Data &
396  extra_data() const noexcept
397  {
398  return *m_extra_data_holder.get_ptr();
399  }
400 
401  private:
402  void
404  {
405  if( !m_connection )
406  {
407  throw exception_t{ "connection already moved" };
408  }
409  }
410 
413  const std::string m_body;
414 
415  //! Optional description for chunked-encoding.
416  /*!
417  * It is present only if chunked-encoded body is found in the
418  * incoming request.
419  *
420  * @since v.0.6.9
421  */
423 
426 
427  //! Remote endpoint for underlying connection.
429 
430  /*!
431  * @brief An instance of extra-data that is incorporated into
432  * a request object.
433  *
434  * @since v.0.6.13
435  */
437 };
438 
439 template< typename Extra_Data >
440 std::ostream &
442  std::ostream & o,
443  const generic_request_t< Extra_Data > & req )
444 {
445  o << "{req_id: " << req.request_id() << ", "
446  "conn_id: " << req.connection_id() << ", "
447  "path: " << req.header().path() << ", "
448  "query: " << req.header().query() << "}";
449 
450  return o;
451 }
452 
453 //! An alias for shared-pointer to incoming request.
454 template< typename Extra_Data >
457 
458 //! An alias for incoming request without additional extra-data.
459 /*!
460  * For compatibility with previous versions.
461  *
462  * @since v.0.6.13
463  */
464 using request_t = generic_request_t< no_extra_data_factory_t::data_t >;
465 
466 //! An alias for handle for incoming request without additional extra-data.
467 /*!
468  * For compatibility with previous versions.
469  *
470  * @since v.0.6.13
471  */
473 
474 //
475 // default_request_handler_t
476 //
477 
480 
481 namespace impl
482 {
483 
484 template< typename Extra_Data >
487 {
488  return req.m_connection;
489 }
490 
491 } /* namespace impl */
492 
493 
494 } /* namespace restinio */
std::array< char, sizeof(Extra_Data)> m_data
const http_request_header_t & header() const noexcept
Get request header.
Helper for holding a pointer to a buffer where a new object of type Extra_Data should be constructed...
void make_within(extra_data_buffer_t< data_t > buffer) noexcept(noexcept(new(buffer.get()) data_t{}))
nullable_pointer_t< const chunked_input_info_t > chunked_input_info() const noexcept
Get optional info about chunked input.
const http_request_header_t m_header
friend impl::connection_handle_t & impl::access_req_connection(generic_request_t< UD > &) noexcept
A helper template class for cases when extra-data-factory is just a simple stateless object...
connection_handle_t & access_req_connection(generic_request_t< Extra_Data > &) noexcept
generic_request_t(request_id_t request_id, http_request_header_t header, std::string body, impl::connection_handle_t connection, endpoint_t remote_endpoint, Extra_Data_Factory &extra_data_factory)
Old-format initializing constructor.
const connection_id_t m_connection_id
std::enable_if< std::is_same< Parameter_Container, query_string_params_t >::value||std::is_same< Parameter_Container, router::route_params_t >::value, std::optional< Value_Type > >::type opt_value(const Parameter_Container &params, string_view_t key)
Gets the value of a parameter specified by key wrapped in std::optional<Value_Type> if parameter exis...
Definition: value_or.hpp:64
auto request_id() const noexcept
Get request id.
generic_request_t(request_id_t request_id, http_request_header_t header, std::string body, chunked_input_info_unique_ptr_t chunked_input_info, impl::connection_handle_t connection, endpoint_t remote_endpoint, Extra_Data_Factory &extra_data_factory)
New-format initializing constructor.
Extra_Data & extra_data() noexcept
Get writeable access to extra-data object incorporated into a request object.
impl::connection_handle_t m_connection
const endpoint_t & remote_endpoint() const noexcept
Get the remote endpoint of the underlying connection.
const Extra_Data & extra_data() const noexcept
Get readonly access to extra-data object incorporated into a request object.
A type of extra-data to be incorporated into a request object by the default.
const chunked_input_info_unique_ptr_t m_chunked_input_info
Optional description for chunked-encoding.
The default extra-data-factory to be used in server&#39;s traits if a user doesn&#39;t specify own one...
const request_id_t m_request_id
void make_within(extra_data_buffer_t< data_t > buffer) noexcept
std::ostream & operator<<(std::ostream &o, const generic_request_t< Extra_Data > &req)
auto create_response(http_status_line_t status_line=status_ok())
void * get() const noexcept
impl::generic_request_extra_data_holder_t< Extra_Data > m_extra_data_holder
An instance of extra-data that is incorporated into a request object.
const std::string & body() const noexcept
Get request body.
Helper class for holding a buffer for extra-data object to be incorporated into a request object...
connection_id_t connection_id() const noexcept
Get connection id.
const endpoint_t m_remote_endpoint
Remote endpoint for underlying connection.