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