RESTinio
acceptor.hpp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
9 #pragma once
10 
11 #include <memory>
12 
14 
16 
18 
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 >
47  Settings & settings,
49  asio_ns::io_context & io_context )
50  : m_io_context{ io_context }
51  {
52  m_sockets.reserve( settings.concurrent_accepts_count() );
53 
54  std::generate_n(
55  std::back_inserter( m_sockets ),
56  settings.concurrent_accepts_count(),
57  [this]{
58  return Socket{m_io_context};
59  } );
60 
61  assert( m_sockets.size() == settings.concurrent_accepts_count() );
62  }
63 
65  Socket &
68  std::size_t idx )
69  {
70  return m_sockets.at( idx );
71  }
72 
74  Socket
77  std::size_t idx )
78  {
79  return std::move( socket(idx ) );
80  }
81 
84  auto
86  {
87  return m_sockets.size();
88  }
89 
90  private:
92  asio_ns::io_context & m_io_context;
93 
96  std::vector< Socket > m_sockets;
97 };
98 
99 namespace acceptor_details
100 {
101 
110 template< typename Ip_Blocker >
112 {
113  std::shared_ptr< Ip_Blocker > m_ip_blocker;
114 
115  template< typename Settings >
117  const Settings & settings )
118  : m_ip_blocker{ settings.ip_blocker() }
119  {}
120 
121  template< typename Socket >
123  inspect_incoming( Socket & socket ) const noexcept
124  {
125  return m_ip_blocker->inspect(
127  socket.lowest_layer().remote_endpoint()
128  } );
129  }
130 };
131 
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 
161 template < typename Traits >
162 class acceptor_t final
163  : public std::enable_shared_from_this< acceptor_t< 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 {
169  typename Traits::ip_blocker_t >;
170 
175 
176  public:
179  std::shared_ptr< connection_factory_t >;
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;
184 
185  template < typename Settings >
187  Settings & settings,
189  asio_ns::io_context & io_context,
191  connection_factory_shared_ptr_t connection_factory,
193  logger_t & logger )
194  : socket_holder_base_t{ settings, io_context }
195  , ip_blocker_base_t{ settings }
196  , m_port{ settings.port() }
197  , m_protocol{ settings.protocol() }
198  , m_address{ settings.address() }
199  , m_acceptor_options_setter{ settings.acceptor_options_setter() }
200  , m_acceptor{ io_context }
201  , m_acceptor_post_bind_hook{ settings.giveaway_acceptor_post_bind_hook() }
202  , m_executor{ io_context.get_executor() }
203  , m_open_close_operations_executor{ io_context.get_executor() }
204  , m_separate_accept_and_create_connect{ settings.separate_accept_and_create_connect() }
205  , m_connection_factory{ std::move( connection_factory ) }
206  , m_logger{ logger }
207  , m_connection_count_limiter{
208  self_as_acceptor_callback(),
210  settings.max_parallel_connections()
211  },
213  settings.concurrent_accepts_count()
214  }
215  }
216  {}
217 
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 
231  asio_ns::ip::tcp::endpoint ep{ m_protocol, m_port };
232 
233  const auto actual_address = try_extract_actual_address_from_variant(
234  m_address );
235  if( actual_address )
236  ep.address( *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.
248  acceptor_options_t options{ m_acceptor };
249 
250  (*m_acceptor_options_setter)( options );
251  }
252 
253  m_acceptor.bind( ep );
254  // Since v.0.6.11 the post-bind hook should be invoked.
255  m_acceptor_post_bind_hook( m_acceptor );
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).
259  ep = m_acceptor.local_endpoint();
260 
261  // Now we can switch acceptor to listen state.
262  m_acceptor.listen( asio_ns::socket_base::max_connections );
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 
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 
311  auto &
313  {
314  return m_open_close_operations_executor;
315  }
316 
317  private:
319  auto & get_executor() noexcept { return m_executor; }
320 
321  // Begin of implementation of acceptor_callback_iface_t.
325  void
326  call_accept_now( std::size_t index ) noexcept
327  {
328  m_acceptor.async_accept(
329  this->socket( index ).lowest_layer(),
330  asio_ns::bind_executor(
331  get_executor(),
332  [index, ctx = this->shared_from_this()]
333  ( const auto & ec ) noexcept
334  {
335  if( !ec )
336  {
337  ctx->accept_current_connection( index, ec );
338  }
339  } ) );
340  }
341 
345  void
346  schedule_next_accept_attempt( std::size_t index ) noexcept
347  {
348  asio_ns::post(
349  asio_ns::bind_executor(
350  get_executor(),
351  [index, ctx = this->shared_from_this()]() noexcept
352  {
353  ctx->accept_next( index );
354  } ) );
355  }
356 
365  {
366  return this;
367  }
368  // End of implementation of acceptor_callback_iface_t.
369 
371 
380  void
381  accept_next( std::size_t i ) noexcept
382  {
383  m_connection_count_limiter.accept_next( i );
384  }
385 
387 
391  void
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] {
403  accept_connection_for_socket_with_index( 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 
430  void
433  std::size_t i )
434  {
435  auto incoming_socket = this->move_socket( i );
436 
437  auto remote_endpoint =
438  incoming_socket.lowest_layer().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.
466  do_accept_current_connection(
467  std::move(incoming_socket),
468  remote_endpoint );
469  break;
470  }
471  }
472 
473  void
475  stream_socket_t incoming_socket,
476  endpoint_t remote_endpoint )
477  {
478  auto create_and_init_connection =
479  [sock = std::move(incoming_socket),
480  factory = m_connection_factory,
481  ep = std::move(remote_endpoint),
482  lifetime_monitor = connection_lifetime_monitor_t{
483  &m_connection_count_limiter
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.
497  auto conn = factory->create_new_connection(
498  std::move(sock),
499  std::move(ep),
500  std::move(lifetime_monitor) );
501 
502  // Start waiting for request message.
503  conn->init();
504  } );
505  };
506 
507  if( m_separate_accept_and_create_connect )
508  {
509  asio_ns::post(
510  get_executor(),
511  std::move( create_and_init_connection ) );
512  }
513  else
514  {
515  create_and_init_connection();
516  }
517  }
518 
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 
541  const std::uint16_t m_port;
542  const asio_ns::ip::tcp m_protocol;
545 
548  std::unique_ptr< acceptor_options_setter_t > m_acceptor_options_setter;
549  asio_ns::ip::tcp::acceptor m_acceptor;
550 
552 
557 
561 
564 
567 
569 
576 
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 
600  result = asio_ns::ip::address::from_string( str_addr );
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 */
restinio::acceptor_options_t
An adapter for setting acceptor options before running server.
Definition: settings.hpp:185
restinio::impl::acceptor_t::m_acceptor_options_setter
std::unique_ptr< acceptor_options_setter_t > m_acceptor_options_setter
Server port listener and connection receiver routine.
Definition: acceptor.hpp:548
restinio::impl::acceptor_t::get_open_close_operations_executor
auto & get_open_close_operations_executor() noexcept
Get an executor for close operation.
Definition: acceptor.hpp:312
RESTINIO_NODISCARD
#define RESTINIO_NODISCARD
Definition: compiler_features.hpp:33
restinio::impl::acceptor_t::connection_count_limiter_t
typename connection_count_limit_types< Traits >::limiter_t connection_count_limiter_t
Definition: acceptor.hpp:172
restinio::impl::connection_factory_t
Factory for connections.
Definition: connection.hpp:1691
restinio::ip_blocker::inspection_result_t
inspection_result_t
Enumeration of result of inspecting new incoming connection.
Definition: ip_blocker.hpp:31
connection.hpp
restinio::ip_blocker::deny
constexpr inspection_result_t deny() noexcept
Shorthand for inspection_result_t::deny.
Definition: ip_blocker.hpp:44
nonstd::optional_lite::std11::move
T & move(T &t)
Definition: optional.hpp:421
restinio::endpoint_t
asio_ns::ip::tcp::endpoint endpoint_t
An alias for endpoint type from Asio.
Definition: common_types.hpp:158
restinio::connection_count_limits::connection_lifetime_monitor_t
Helper type for controlling the lifetime of the connection.
Definition: connection_count_limiter.hpp:375
restinio::impl::acceptor_t::try_extract_actual_address_from_variant
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
restinio::impl::acceptor_t::strand_t
typename Traits::strand_t strand_t
Definition: acceptor.hpp:181
restinio::impl::socket_supplier_t
Definition: acceptor.hpp:42
restinio::impl::acceptor_t::m_executor
default_asio_executor m_executor
Asio executor.
Definition: acceptor.hpp:559
restinio::impl::acceptor_t::m_acceptor
asio_ns::ip::tcp::acceptor m_acceptor
Definition: acceptor.hpp:549
restinio::impl::acceptor_t::m_logger
logger_t & m_logger
Definition: acceptor.hpp:568
restinio::impl::acceptor_details::ip_blocker_holder_t
A class for holding actual IP-blocker.
Definition: acceptor.hpp:112
restinio::impl::socket_supplier_t::m_io_context
asio_ns::io_context & m_io_context
io_context for sockets to run on.
Definition: acceptor.hpp:92
restinio::impl::acceptor_details::ip_blocker_holder_t::m_ip_blocker
std::shared_ptr< Ip_Blocker > m_ip_blocker
Definition: acceptor.hpp:113
restinio::impl::acceptor_t
Context for accepting http connections.
Definition: acceptor.hpp:167
restinio::impl::acceptor_t::call_accept_now
void call_accept_now(std::size_t index) noexcept
Definition: acceptor.hpp:326
restinio::impl::acceptor_t::m_port
const std::uint16_t m_port
Server endpoint.
Definition: acceptor.hpp:541
restinio::impl::socket_supplier_t::concurrent_accept_sockets_count
auto concurrent_accept_sockets_count() const noexcept
The number of sockets that can be used for cuncurrent accept operations.
Definition: acceptor.hpp:85
restinio::impl::acceptor_t::logger_t
typename Traits::logger_t logger_t
Definition: acceptor.hpp:180
connection_count_limiter.hpp
Stuff related to limits of active parallel connections.
restinio::connection_count_limit_types::limiter_t
typename std::conditional< Traits::use_connection_count_limiter, connection_count_limits::connection_count_limiter_t< typename Traits::strand_t >, connection_count_limits::noop_connection_count_limiter_t >::type limiter_t
Definition: connection_count_limiter.hpp:466
restinio::utils::log_error_noexcept
void log_error_noexcept(Logger &&logger, Message_Builder &&builder) noexcept
Definition: suppress_exceptions.hpp:71
restinio::impl::acceptor_details::ip_blocker_holder_t< restinio::ip_blocker::noop_ip_blocker_t >::ip_blocker_holder_t
ip_blocker_holder_t(const Settings &)
Definition: acceptor.hpp:144
restinio::default_asio_executor
asio_ns::executor default_asio_executor
Definition: asio_include.hpp:224
restinio::impl::socket_supplier_t::move_socket
Socket move_socket(std::size_t idx)
Extract the socket via move.
Definition: acceptor.hpp:75
restinio::utils::tagged_scalar_t
Helper template for defining tagged scalar types.
Definition: tagged_scalar.hpp:54
restinio::impl::acceptor_t::do_accept_current_connection
void do_accept_current_connection(stream_socket_t incoming_socket, endpoint_t remote_endpoint)
Definition: acceptor.hpp:474
restinio::impl::acceptor_t::connection_factory_shared_ptr_t
std::shared_ptr< connection_factory_t > connection_factory_shared_ptr_t
Definition: acceptor.hpp:179
restinio::connection_count_limits::impl::acceptor_callback_iface_t
An interface of acceptor to be used by connection count limiters.
Definition: connection_count_limiter.hpp:78
restinio::utils::log_trace_noexcept
void log_trace_noexcept(Logger &&logger, Message_Builder &&builder) noexcept
Definition: suppress_exceptions.hpp:32
restinio::impl::acceptor_t::connection_lifetime_monitor_t
typename connection_count_limit_types< Traits >::lifetime_monitor_t connection_lifetime_monitor_t
Definition: acceptor.hpp:174
nonstd::optional_lite::optional
class optional
Definition: optional.hpp:839
restinio::impl::acceptor_details::ip_blocker_holder_t::inspect_incoming
restinio::ip_blocker::inspection_result_t inspect_incoming(Socket &socket) const noexcept
Definition: acceptor.hpp:123
restinio::utils::suppress_exceptions
void suppress_exceptions(Logger &&logger, const char *block_description, Lambda &&lambda) noexcept
Helper function for execution a block of code with suppression of any exceptions raised inside that b...
Definition: suppress_exceptions.hpp:94
restinio::impl::socket_supplier_t::m_sockets
std::vector< Socket > m_sockets
A temporary socket for receiving new connections.
Definition: acceptor.hpp:96
include_fmtlib.hpp
A special wrapper around fmtlib include files.
restinio::impl::acceptor_t::m_address
const restinio::details::address_variant_t m_address
Definition: acceptor.hpp:543
restinio::ip_blocker::inspection_result_t::deny
@ deny
New connection is disabled and should be closed.
restinio::acceptor_post_bind_hook_t
std::function< void(asio_ns::ip::tcp::acceptor &) > acceptor_post_bind_hook_t
A type of callback to be called after a successful invocation of bind() function for the acceptor.
Definition: settings.hpp:435
suppress_exceptions.hpp
Utilities for suppressing exceptions from some code block.
restinio::impl::acceptor_t::m_open_close_operations_executor
strand_t m_open_close_operations_executor
Definition: acceptor.hpp:560
restinio::impl::acceptor_t::accept_connection_for_socket_with_index
void accept_connection_for_socket_with_index(std::size_t i)
Performs actual actions for accepting a new connection.
Definition: acceptor.hpp:431
restinio::impl::socket_supplier_t::socket_supplier_t
socket_supplier_t(Settings &settings, asio_ns::io_context &io_context)
Definition: acceptor.hpp:45
restinio::impl::acceptor_details::ip_blocker_holder_t::ip_blocker_holder_t
ip_blocker_holder_t(const Settings &settings)
Definition: acceptor.hpp:116
restinio::impl::acceptor_t::accept_next
void accept_next(std::size_t i) noexcept
Set a callback for a new connection.
Definition: acceptor.hpp:381
restinio::impl::socket_supplier_t::socket
Socket & socket(std::size_t idx)
Get the reference to socket.
Definition: acceptor.hpp:66
restinio::impl::acceptor_t::open
void open()
Start listen on port specified in ctor.
Definition: acceptor.hpp:220
restinio
Definition: asio_include.hpp:21
restinio::impl::acceptor_details::ip_blocker_holder_t< restinio::ip_blocker::noop_ip_blocker_t >::inspect_incoming
restinio::ip_blocker::inspection_result_t inspect_incoming(Socket &) const noexcept
Definition: acceptor.hpp:148
restinio::impl::acceptor_t::get_executor
auto & get_executor() noexcept
Get executor for acceptor.
Definition: acceptor.hpp:319
restinio::impl::acceptor_t::m_separate_accept_and_create_connect
const bool m_separate_accept_and_create_connect
Do separate an accept operation and connection instantiation.
Definition: acceptor.hpp:563
restinio::impl::acceptor_t::stream_socket_t
typename Traits::stream_socket_t stream_socket_t
Definition: acceptor.hpp:182
restinio::impl::acceptor_t::m_connection_factory
connection_factory_shared_ptr_t m_connection_factory
Factory for creating connections.
Definition: acceptor.hpp:566
restinio::impl::acceptor_t::m_acceptor_post_bind_hook
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
restinio::ip_blocker::incoming_info_t
An information about new incoming connection to be passed to IP-blocker object.
Definition: ip_blocker.hpp:64
restinio::impl::acceptor_t::m_connection_count_limiter
connection_count_limiter_t m_connection_count_limiter
Actual limiter of active parallel connections.
Definition: acceptor.hpp:575
restinio::ip_blocker::noop_ip_blocker_t
The default no-op IP-blocker.
Definition: ip_blocker.hpp:94
restinio::impl::acceptor_t::close
void close()
Close listener if any.
Definition: acceptor.hpp:296
restinio::utils::from_string
Value_Type from_string(string_view_t s)
Get a value from string.
Definition: from_string.hpp:105
restinio::impl::acceptor_t::self_as_acceptor_callback
::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
nonstd::variants::variant
Definition: variant.hpp:1209
restinio::impl::acceptor_t::close_impl
void close_impl()
Close opened acceptor.
Definition: acceptor.hpp:521
restinio::impl::acceptor_t::accept_current_connection
void accept_current_connection(std::size_t i, const std::error_code &ec) noexcept
Accept current connection.
Definition: acceptor.hpp:392
restinio::easy_parser
Definition: easy_parser.hpp:39
restinio::impl::acceptor_t::m_protocol
const asio_ns::ip::tcp m_protocol
Definition: acceptor.hpp:542
restinio::impl::acceptor_t::acceptor_t
acceptor_t(Settings &settings, asio_ns::io_context &io_context, connection_factory_shared_ptr_t connection_factory, logger_t &logger)
Definition: acceptor.hpp:186
const
#define const
Definition: zconf.h:230
restinio::impl::acceptor_t::schedule_next_accept_attempt
void schedule_next_accept_attempt(std::size_t index) noexcept
Definition: acceptor.hpp:346