RESTinio
acceptor.hpp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /*!
6  HTTP-Acceptor handler routine.
7 */
8 
9 #pragma once
10 
11 #include <memory>
12 
13 #include <restinio/connection_count_limiter.hpp>
14 
15 #include <restinio/impl/include_fmtlib.hpp>
16 
17 #include <restinio/impl/connection.hpp>
18 
19 #include <restinio/utils/suppress_exceptions.hpp>
20 
21 namespace restinio
22 {
23 
24 namespace impl
25 {
26 
27 //
28 // socket_supplier_t
29 //
30 
31 /*
32  A helper base class that hides a pool of socket instances.
33 
34  It prepares a socket for new connections.
35  And as it is template class over a socket type
36  it givies an oportunity to customize details for
37  other types of sockets (like `asio::ssl::stream< asio::ip::tcp::socket >`)
38  that can be used.
39 */
40 template < typename Socket >
42 {
43  protected:
44  template < typename Settings >
46  //! Server settings.
47  Settings & settings,
48  //! A context the server runs on.
49  asio_ns::io_context & io_context )
51  {
53 
54  std::generate_n(
57  [this]{
58  return Socket{m_io_context};
59  } );
60 
62  }
63 
64  //! Get the reference to socket.
65  Socket &
67  //! Index of a socket in the pool.
68  std::size_t idx )
69  {
70  return m_sockets.at( idx );
71  }
72 
73  //! Extract the socket via move.
74  Socket
76  //! Index of a socket in the pool.
77  std::size_t idx )
78  {
79  return std::move( socket(idx ) );
80  }
81 
82  //! The number of sockets that can be used for
83  //! cuncurrent accept operations.
84  auto
86  {
87  return m_sockets.size();
88  }
89 
90  private:
91  //! io_context for sockets to run on.
93 
94  //! A temporary socket for receiving new connections.
95  //! \note Must never be empty.
97 };
98 
100 {
101 
102 /*!
103  * @brief A class for holding actual IP-blocker.
104  *
105  * This class holds shared pointer to actual IP-blocker object and
106  * provides actual inspect_incoming() implementation.
107  *
108  * @since v.0.5.1
109  */
110 template< typename Ip_Blocker >
112 {
114 
115  template< typename Settings >
117  const Settings & settings )
119  {}
120 
121  template< typename Socket >
123  inspect_incoming( Socket & socket ) const noexcept
124  {
125  return m_ip_blocker->inspect(
128  } );
129  }
130 };
131 
132 /*!
133  * @brief A specialization of ip_blocker_holder for case of
134  * noop_ip_blocker.
135  *
136  * This class doesn't hold anything and doesn't do anything.
137  *
138  * @since v.0.5.1
139  */
140 template<>
142 {
143  template< typename Settings >
144  ip_blocker_holder_t( const Settings & ) { /* nothing to do */ }
145 
146  template< typename Socket >
148  inspect_incoming( Socket & /*socket*/ ) const noexcept
149  {
151  }
152 };
153 
154 } /* namespace acceptor_details */
155 
156 //
157 // acceptor_t
158 //
159 
160 //! Context for accepting http connections.
161 template < typename Traits >
164  , protected socket_supplier_t< typename Traits::stream_socket_t >
165  , protected acceptor_details::ip_blocker_holder_t< typename Traits::ip_blocker_t >
167 {
168  using ip_blocker_base_t = acceptor_details::ip_blocker_holder_t<
169  typename Traits::ip_blocker_t >;
170 
175 
176  public:
180  using logger_t = typename Traits::logger_t;
181  using strand_t = typename Traits::strand_t;
182  using stream_socket_t = typename Traits::stream_socket_t;
183  using socket_holder_base_t = socket_supplier_t< stream_socket_t >;
184 
185  template < typename Settings >
187  Settings & settings,
188  //! ASIO io_context to run on.
189  asio_ns::io_context & io_context,
190  //! Connection factory.
191  connection_factory_shared_ptr_t connection_factory,
192  //! Logger.
193  logger_t & logger )
196  , m_port{ settings.port() }
198  , m_address{ settings.address() }
206  , m_logger{ logger }
211  },
214  }
215  }
216  {}
217 
218  //! Start listen on port specified in ctor.
219  void
221  {
222  if( m_acceptor.is_open() )
223  {
224  const auto ep = m_acceptor.local_endpoint();
225  m_logger.warn( [&]{
226  return fmt::format( "server already started on {}", ep );
227  } );
228  return;
229  }
230 
232 
234  m_address );
235  if( actual_address )
237 
238  try
239  {
240  m_logger.trace( [&]{
241  return fmt::format( "starting server on {}", ep );
242  } );
243 
244  m_acceptor.open( ep.protocol() );
245 
246  {
247  // Set acceptor options.
249 
251  }
252 
253  m_acceptor.bind( ep );
254  // Since v.0.6.11 the post-bind hook should be invoked.
256  // server end-point can be replaced if port is allocated by
257  // the operating system (e.g. zero is specified as port number
258  // by a user).
260 
261  // Now we can switch acceptor to listen state.
263 
264  // Call accept connections routine.
265  for( std::size_t i = 0; i< this->concurrent_accept_sockets_count(); ++i )
266  {
267  m_logger.info( [&]{
268  return fmt::format( "init accept #{}", i );
269  } );
270 
271  accept_next( i );
272  }
273 
274  m_logger.info( [&]{
275  return fmt::format( "server started on {}", ep );
276  } );
277  }
278  catch( const std::exception & ex )
279  {
280  // Acceptor should be closes in the case of an error.
281  if( m_acceptor.is_open() )
282  m_acceptor.close();
283 
284  m_logger.error( [&]() -> auto {
285  return fmt::format( "failed to start server on {}: {}",
286  ep,
287  ex.what() );
288  } );
289 
290  throw;
291  }
292  }
293 
294  //! Close listener if any.
295  void
297  {
298  if( m_acceptor.is_open() )
299  {
300  close_impl();
301  }
302  else
303  {
304  m_logger.trace( [&]{
305  return fmt::format( "server already closed" );
306  } );
307  }
308  }
309 
310  //! Get an executor for close operation.
311  auto &
313  {
315  }
316 
317  private:
318  //! Get executor for acceptor.
319  auto & get_executor() noexcept { return m_executor; }
320 
321  // Begin of implementation of acceptor_callback_iface_t.
322  /*!
323  * @since v.0.6.12
324  */
325  void
326  call_accept_now( std::size_t index ) noexcept
327  {
329  this->socket( index ).lowest_layer(),
331  get_executor(),
332  [index, ctx = this->shared_from_this()]
333  ( const auto & ec ) noexcept
334  {
335  if( !ec )
336  {
338  }
339  } ) );
340  }
341 
342  /*!
343  * @since v.0.6.12
344  */
345  void
346  schedule_next_accept_attempt( std::size_t index ) noexcept
347  {
348  asio_ns::post(
350  get_executor(),
351  [index, ctx = this->shared_from_this()]() noexcept
352  {
353  ctx->accept_next( index );
354  } ) );
355  }
356 
357  /*!
358  * @brief Helper for suppressing warnings of using `this` in
359  * initilizer list.
360  *
361  * @since v.0.6.12
362  */
365  {
366  return this;
367  }
368  // End of implementation of acceptor_callback_iface_t.
369 
370  //! Set a callback for a new connection.
371  /*!
372  * @note
373  * This method is marked as noexcept in v.0.6.0.
374  * It seems that nothing prevents exceptions from a call to
375  * async_accept. But we just don't know what to do in that case.
376  * So at the moment the call to `std::terminate` because an
377  * exception is raised inside `noexcept` method seems to be an
378  * appropriate solution.
379  */
380  void
381  accept_next( std::size_t i ) noexcept
382  {
384  }
385 
386  //! Accept current connection.
387  /*!
388  * @note
389  * This method is marked as noexcept in v.0.6.0.
390  */
391  void
393  //! socket index in the pool of sockets.
394  std::size_t i,
395  const std::error_code & ec ) noexcept
396  {
397  if( !ec )
398  {
400  m_logger,
401  "accept_current_connection",
402  [this, i] {
404  } );
405  }
406  else
407  {
408  // Something goes wrong with connection.
410  [&]{
411  return fmt::format(
412  "failed to accept connection on socket #{}: {}",
413  i,
414  ec.message() );
415  } );
416  }
417 
418  // Continue accepting.
419  accept_next( i );
420  }
421 
422  /*!
423  * @brief Performs actual actions for accepting a new connection.
424  *
425  * @note
426  * This method can throw. An we expect that it can throw sometimes.
427  *
428  * @since v.0.6.0
429  */
430  void
432  //! socket index in the pool of sockets.
433  std::size_t i )
434  {
435  auto incoming_socket = this->move_socket( i );
436 
437  auto remote_endpoint =
439 
440  m_logger.trace( [&]{
441  return fmt::format(
442  "accept connection from {} on socket #{}",
443  remote_endpoint, i );
444  } );
445 
446  // Since v.0.5.1 the incoming connection must be
447  // inspected by IP-blocker.
448  const auto inspection_result = this->inspect_incoming(
449  incoming_socket );
450 
451  switch( inspection_result )
452  {
454  // New connection can be used. It is disabled by IP-blocker.
455  m_logger.warn( [&]{
456  return fmt::format(
457  "accepted connection from {} on socket #{} denied by"
458  " IP-blocker",
459  remote_endpoint, i );
460  } );
461  // incoming_socket will be closed automatically.
462  break;
463 
465  // Acception of the connection can be continued.
468  remote_endpoint );
469  break;
470  }
471  }
472 
473  void
475  stream_socket_t incoming_socket,
476  endpoint_t remote_endpoint )
477  {
484  },
485  logger = &m_logger]
486  () mutable noexcept
487  {
488  // NOTE: this code block shouldn't throw!
490  *logger,
491  "do_accept_current_connection.create_and_init_connection",
492  [&] {
493  // Create new connection handler.
494  // NOTE: since v.0.6.3 this method throws in
495  // the case of an error. Because of that there is
496  // no need to check the value returned.
498  std::move(sock),
499  std::move(ep),
501 
502  // Start waiting for request message.
503  conn->init();
504  } );
505  };
506 
508  {
509  asio_ns::post(
510  get_executor(),
512  }
513  else
514  {
516  }
517  }
518 
519  //! Close opened acceptor.
520  void
522  {
523  const auto ep = m_acceptor.local_endpoint();
524 
525  // An exception in logger should not prevent a call of close()
526  // for m_acceptor.
528  [&]{
529  return fmt::format( "closing server on {}", ep );
530  } );
531 
532  m_acceptor.close();
533 
534  m_logger.info( [&]{
535  return fmt::format( "server closed on {}", ep );
536  } );
537  }
538 
539  //! Server endpoint.
540  //! \{
544  //! \}
545 
546  //! Server port listener and connection receiver routine.
547  //! \{
550 
551  //! A hook to be called just after a successful call to bind for acceptor.
552  /*!
553  * @since v.0.6.11
554  */
556  //! \}
557 
558  //! Asio executor.
561 
562  //! Do separate an accept operation and connection instantiation.
564 
565  //! Factory for creating connections.
567 
568  logger_t & m_logger;
569 
570  /*!
571  * @brief Actual limiter of active parallel connections.
572  *
573  * @since v.0.6.12
574  */
576 
577  /*!
578  * @brief Helper for extraction of an actual IP-address from an
579  * instance of address_variant.
580  *
581  * Returns an empty value if there is no address inside @a from.
582  *
583  * @since v.0.6.11
584  */
586  static optional_t< asio_ns::ip::address >
589  {
591 
592  if( auto * str_v = get_if<std::string>( &from ) )
593  {
594  auto str_addr = *str_v;
595  if( str_addr == "localhost" )
596  str_addr = "127.0.0.1";
597  else if( str_addr == "ip6-localhost" )
598  str_addr = "::1";
599 
601  }
602  else if( auto * addr_v = get_if<asio_ns::ip::address>( &from ) )
603  {
604  result = *addr_v;
605  }
606 
607  return result;
608  }
609 };
610 
611 } /* namespace impl */
612 
613 } /* namespace restinio */
void accept_current_connection(std::size_t i, const std::error_code &ec) noexcept
Accept current connection.
Definition: acceptor.hpp:392
connection_factory_shared_ptr_t m_connection_factory
Factory for creating connections.
Definition: acceptor.hpp:566
void open()
Start listen on port specified in ctor.
Definition: acceptor.hpp:220
A class for holding actual IP-blocker.
Definition: acceptor.hpp:111
restinio::ip_blocker::inspection_result_t inspect_incoming(Socket &socket) const noexcept
Definition: acceptor.hpp:123
Socket move_socket(std::size_t idx)
Extract the socket via move.
Definition: acceptor.hpp:75
void close_impl()
Close opened acceptor.
Definition: acceptor.hpp:521
const asio_ns::ip::tcp m_protocol
Definition: acceptor.hpp:542
static RESTINIO_NODISCARD optional_t< asio_ns::ip::address > try_extract_actual_address_from_variant(const restinio::details::address_variant_t &from)
Helper for extraction of an actual IP-address from an instance of address_variant.
Definition: acceptor.hpp:587
std::unique_ptr< acceptor_options_setter_t > m_acceptor_options_setter
Server port listener and connection receiver routine.
Definition: acceptor.hpp:548
restinio::ip_blocker::inspection_result_t inspect_incoming(Socket &) const noexcept
Definition: acceptor.hpp:148
const bool m_separate_accept_and_create_connect
Do separate an accept operation and connection instantiation.
Definition: acceptor.hpp:563
Socket & socket(std::size_t idx)
Get the reference to socket.
Definition: acceptor.hpp:66
socket_supplier_t(Settings &settings, asio_ns::io_context &io_context)
Definition: acceptor.hpp:45
std::vector< Socket > m_sockets
A temporary socket for receiving new connections.
Definition: acceptor.hpp:96
void accept_connection_for_socket_with_index(std::size_t i)
Performs actual actions for accepting a new connection.
Definition: acceptor.hpp:431
const std::uint16_t m_port
Server endpoint.
Definition: acceptor.hpp:541
acceptor_t(Settings &settings, asio_ns::io_context &io_context, connection_factory_shared_ptr_t connection_factory, logger_t &logger)
Definition: acceptor.hpp:186
::restinio::connection_count_limits::impl::acceptor_callback_iface_t * self_as_acceptor_callback() noexcept
Helper for suppressing warnings of using this in initilizer list.
Definition: acceptor.hpp:364
void do_accept_current_connection(stream_socket_t incoming_socket, endpoint_t remote_endpoint)
Definition: acceptor.hpp:474
asio_ns::io_context & m_io_context
io_context for sockets to run on.
Definition: acceptor.hpp:92
acceptor_post_bind_hook_t m_acceptor_post_bind_hook
A hook to be called just after a successful call to bind for acceptor.
Definition: acceptor.hpp:555
void close()
Close listener if any.
Definition: acceptor.hpp:296
RESTINIO_NODISCARD char to_lower_case(unsigned char ch)
auto & get_open_close_operations_executor() noexcept
Get an executor for close operation.
Definition: acceptor.hpp:312
void accept_next(std::size_t i) noexcept
Set a callback for a new connection.
Definition: acceptor.hpp:381
strand_t m_open_close_operations_executor
Definition: acceptor.hpp:560
auto & get_executor() noexcept
Get executor for acceptor.
Definition: acceptor.hpp:319
auto concurrent_accept_sockets_count() const noexcept
The number of sockets that can be used for cuncurrent accept operations.
Definition: acceptor.hpp:85
const restinio::details::address_variant_t m_address
Definition: acceptor.hpp:543
asio_ns::ip::tcp::acceptor m_acceptor
Definition: acceptor.hpp:549
default_asio_executor m_executor
Asio executor.
Definition: acceptor.hpp:559
void schedule_next_accept_attempt(std::size_t index) noexcept
Definition: acceptor.hpp:346
void call_accept_now(std::size_t index) noexcept
Definition: acceptor.hpp:326
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
connection_count_limiter_t m_connection_count_limiter
Actual limiter of active parallel connections.
Definition: acceptor.hpp:575