RESTinio
Loading...
Searching...
No Matches
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
24namespace 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 */
53template< typename Extra_Data >
55{
56 void * m_buffer;
57
58public:
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 */
118template< 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
131template< typename Extra_Data >
133
134namespace impl
135{
136
137template< typename Extra_Data >
139access_req_connection( generic_request_t<Extra_Data> & ) noexcept;
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 */
154template< typename Extra_Data >
156{
157 alignas(Extra_Data) std::array<char, sizeof(Extra_Data)> m_data;
158
159public:
160 template< typename Factory >
162 Factory & factory )
163 {
164 factory.make_within( extra_data_buffer_t<Extra_Data>{ m_data.data() } );
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*/
207template< typename Extra_Data >
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 )
231 std::move( header ),
232 std::move( body ),
234 std::move( connection ),
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 ) }
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
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
412 const http_request_header_t m_header;
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
439template< typename Extra_Data >
440std::ostream &
441operator<<(
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.
454template< typename Extra_Data >
456 std::shared_ptr< generic_request_t< 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 */
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 */
472using request_handle_t = std::shared_ptr< request_t >;
473
474//
475// default_request_handler_t
476//
477
479 std::function< request_handling_status_t ( request_handle_t ) >;
480
481namespace impl
482{
483
484template< typename Extra_Data >
486access_req_connection( generic_request_t<Extra_Data> & req ) noexcept
487{
488 return req.m_connection;
489}
490
491} /* namespace impl */
492
493
494} /* namespace restinio */
Exception class for all exceptions thrown by RESTinio.
Definition exception.hpp:26
exception_t(const char *err)
Definition exception.hpp:29
Helper for holding a pointer to a buffer where a new object of type Extra_Data should be constructed.
void * get() const noexcept
const http_request_header_t & header() const noexcept
Get request header.
const chunked_input_info_unique_ptr_t m_chunked_input_info
Optional description for chunked-encoding.
const std::string & body() const noexcept
Get request body.
auto request_id() const noexcept
Get request id.
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.
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.
const endpoint_t & remote_endpoint() const noexcept
Get the remote endpoint of the underlying connection.
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 Extra_Data & extra_data() const noexcept
Get readonly access to extra-data object 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.
auto create_response(http_status_line_t status_line=status_ok())
nullable_pointer_t< const chunked_input_info_t > chunked_input_info() const noexcept
Get optional info about chunked input.
impl::connection_handle_t m_connection
const http_request_header_t m_header
friend impl::connection_handle_t & impl::access_req_connection(generic_request_t< UD > &) noexcept
Extra_Data & extra_data() noexcept
Get writeable access to extra-data object incorporated into a request object.
const connection_id_t m_connection_id
HTTP response header status line.
Helper class for holding a buffer for extra-data object to be incorporated into a request object.
std::array< char, sizeof(Extra_Data)> m_data
connection_handle_t & access_req_connection(generic_request_t< Extra_Data > &) noexcept
std::shared_ptr< connection_base_t > connection_handle_t
Alias for http connection handle.
std::function< request_handling_status_t(request_handle_t) > default_request_handler_t
asio_ns::ip::tcp::endpoint endpoint_t
An alias for endpoint type from Asio.
unsigned int request_id_t
Request id in scope of single connection.
std::shared_ptr< generic_request_t< Extra_Data > > generic_request_handle_t
An alias for shared-pointer to incoming request.
std::shared_ptr< request_t > request_handle_t
An alias for handle for incoming request without additional extra-data.
http_status_line_t status_ok()
request_handling_status_t
Request handling status.
std::uint64_t connection_id_t
Type for ID of connection.
generic_request_t< no_extra_data_factory_t::data_t > request_t
An alias for incoming request without additional extra-data.
A type of extra-data to be incorporated into a request object by the default.
The default extra-data-factory to be used in server's traits if a user doesn't specify own one.
void make_within(extra_data_buffer_t< data_t > buffer) noexcept
Tag type for RESTinio controlled output response builder.
A helper template class for cases when extra-data-factory is just a simple stateless object.
void make_within(extra_data_buffer_t< data_t > buffer) noexcept(noexcept(new(buffer.get()) data_t{}))