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 <fmt/format.h>
14 #include <fmt/ostream.h>
15 
16 #include <restinio/impl/connection.hpp>
17 
18 namespace restinio
19 {
20 
21 namespace impl
22 {
23 
24 //
25 // socket_supplier_t
26 //
27 
28 /*
29  A helper base class that hides a pool of socket instances.
30 
31  It prepares a socket for new connections.
32  And as it is template class over a socket type
33  it givies an oportunity to customize details for
34  other types of sockets (like `asio::ssl::stream< asio::ip::tcp::socket >`)
35  that can be used.
36 */
37 template < typename Socket >
39 {
40  protected:
41  template < typename Settings >
43  //! Server settings.
44  Settings & settings,
45  //! A context the server runs on.
46  asio_ns::io_context & io_context )
48  {
50 
51  std::generate_n(
54  [this]{
55  return Socket{m_io_context};
56  } );
57 
59  }
60 
61  //! Get the reference to socket.
62  Socket &
64  //! Index of a socket in the pool.
65  std::size_t idx )
66  {
67  return m_sockets.at( idx );
68  }
69 
70  //! Extract the socket via move.
71  Socket
73  //! Index of a socket in the pool.
74  std::size_t idx )
75  {
76  return std::move( socket(idx ) );
77  }
78 
79  //! The number of sockets that can be used for
80  //! cuncurrent accept operations.
81  auto
83  {
84  return m_sockets.size();
85  }
86 
87  private:
88  //! io_context for sockets to run on.
90 
91  //! A temporary socket for receiving new connections.
92  //! \note Must never be empty.
94 };
95 
97 {
98 
99 /*!
100  * @brief A class for holding actual IP-blocker.
101  *
102  * This class holds shared pointer to actual IP-blocker object and
103  * provides actual inspect_incoming() implementation.
104  *
105  * @since v.0.5.1
106  */
107 template< typename Ip_Blocker >
109 {
111 
112  template< typename Settings >
114  const Settings & settings )
116  {}
117 
118  template< typename Socket >
120  inspect_incoming( Socket & socket ) const noexcept
121  {
122  return m_ip_blocker->inspect(
125  } );
126  }
127 };
128 
129 /*!
130  * @brief A specialization of ip_blocker_holder for case of
131  * noop_ip_blocker.
132  *
133  * This class doesn't hold anything and doesn't do anything.
134  *
135  * @since v.0.5.1
136  */
137 template<>
139 {
140  template< typename Settings >
141  ip_blocker_holder_t( const Settings & ) { /* nothing to do */ }
142 
143  template< typename Socket >
145  inspect_incoming( Socket & socket ) const noexcept
146  {
148  }
149 };
150 
151 } /* namespace acceptor_details */
152 
153 //
154 // acceptor_t
155 //
156 
157 //! Context for accepting http connections.
158 template < typename Traits >
161  , protected socket_supplier_t< typename Traits::stream_socket_t >
162  , protected acceptor_details::ip_blocker_holder_t< typename Traits::ip_blocker_t >
163 {
164  using ip_blocker_base_t = acceptor_details::ip_blocker_holder_t<
165  typename Traits::ip_blocker_t >;
166 
167  public:
171  using logger_t = typename Traits::logger_t;
172  using strand_t = typename Traits::strand_t;
173  using stream_socket_t = typename Traits::stream_socket_t;
174  using socket_holder_base_t = socket_supplier_t< stream_socket_t >;
175 
176  template < typename Settings >
178  Settings & settings,
179  //! ASIO io_context to run on.
180  asio_ns::io_context & io_context,
181  //! Connection factory.
182  connection_factory_shared_ptr_t connection_factory,
183  //! Logger.
184  logger_t & logger )
187  , m_port{ settings.port() }
189  , m_address{ settings.address() }
196  , m_logger{ logger }
197  {}
198 
199  //! Start listen on port specified in ctor.
200  void
202  {
203  if( m_acceptor.is_open() )
204  {
205  const auto ep = m_acceptor.local_endpoint();
206  m_logger.warn( [&]{
207  return fmt::format( "server already started on {}", ep );
208  } );
209  return;
210  }
211 
213 
214  if( !m_address.empty() )
215  {
216  auto addr = m_address;
217  if( addr == "localhost" )
218  addr = "127.0.0.1";
219  else if( addr == "ip6-localhost" )
220  addr = "::1";
221 
223  }
224 
225  try
226  {
227  m_logger.trace( [&]{
228  return fmt::format( "starting server on {}", ep );
229  } );
230 
231  m_acceptor.open( ep.protocol() );
232 
233  {
234  // Set acceptor options.
236 
238  }
239 
240  m_acceptor.bind( ep );
242 
243  // Call accept connections routine.
244  for( std::size_t i = 0; i< this->cuncurrent_accept_sockets_count(); ++i )
245  {
246  m_logger.info( [&]{
247  return fmt::format( "init accept #{}", i );
248  } );
249 
250  accept_next( i );
251  }
252 
253  m_logger.info( [&]{
254  return fmt::format( "server started on {}", ep );
255  } );
256  }
257  catch( const std::exception & ex )
258  {
259  m_logger.error( [&]() -> auto {
260  return fmt::format( "failed to start server on {}: {}",
261  ep,
262  ex.what() );
263  } );
264 
265  throw;
266  }
267  }
268 
269  //! Close listener if any.
270  void
272  {
273  if( m_acceptor.is_open() )
274  {
275  close_impl();
276  }
277  else
278  {
279  m_logger.trace( [&]{
280  return fmt::format( "server already closed" );
281  } );
282  }
283  }
284 
285  //! Get an executor for close operation.
286  auto &
288  {
290  }
291 
292  private:
293  //! Get executor for acceptor.
294  auto & get_executor() noexcept { return m_executor; }
295 
296  //! Set a callback for a new connection.
297  void
298  accept_next( std::size_t i )
299  {
301  this->socket( i ).lowest_layer(),
303  get_executor(),
304  [ i, ctx = this->shared_from_this() ]( const auto & ec ){
305  if( !ec )
306  {
308  }
309  } ) );
310  }
311 
312  //! Accept current connection.
313  void
315  //! socket index in the pool of sockets.
316  std::size_t i,
317  const std::error_code & ec )
318  {
319  if( !ec )
320  {
321  auto remote_endpoint =
322  this->socket( i ).lowest_layer().remote_endpoint();
323 
324  m_logger.trace( [&]{
325  return fmt::format(
326  "accept connection from {} on socket #{}",
327  remote_endpoint, i );
328  } );
329 
330  // Since v.0.5.1 the incoming connection must be
331  // inspected by IP-blocker.
332  auto incoming_socket = this->move_socket( i );
333  const auto inspection_result = this->inspect_incoming(
334  incoming_socket );
335 
336  switch( inspection_result )
337  {
339  // New connection can be used. It is disabled by IP-blocker.
340  m_logger.warn( [&]{
341  return fmt::format(
342  "accepted connection from {} on socket #{} denied by"
343  " IP-blocker",
344  remote_endpoint, i );
345  } );
346  // incoming_socket will be closed automatically.
347  break;
348 
350  // Acception of the connection can be continued.
353  remote_endpoint );
354  break;
355  }
356  }
357  else
358  {
359  // Something goes wrong with connection.
360  m_logger.error( [&]{
361  return fmt::format(
362  "failed to accept connection on socket #{}: {}",
363  i,
364  ec.message() );
365  } );
366  }
367 
368  // Continue accepting.
369  accept_next( i );
370  }
371 
372  void
374  stream_socket_t incoming_socket,
375  endpoint_t remote_endpoint )
376  {
380  ep = std::move(remote_endpoint)]() mutable {
381  // Create new connection handler.
383  std::move(sock), std::move(ep) );
384 
385  //! If connection handler was created,
386  // then start waiting for request message.
387  if( conn )
388  conn->init();
389  };
390 
392  {
393  asio_ns::post(
394  get_executor(),
396  }
397  else
398  {
400  }
401  }
402 
403  //! Close opened acceptor.
404  void
406  {
407  const auto ep = m_acceptor.local_endpoint();
408 
409  m_logger.trace( [&]{
410  return fmt::format( "closing server on {}", ep );
411  } );
412 
413  m_acceptor.close();
414 
415  m_logger.info( [&]{
416  return fmt::format( "server closed on {}", ep );
417  } );
418  }
419 
420  //! Server endpoint.
421  //! \{
425  //! \}
426 
427  //! Server port listener and connection receiver routine.
428  //! \{
431  //! \}
432 
433  //! Asio executor.
436 
437  //! Do separate an accept operation and connection instantiation.
439 
440  //! Factory for creating connections.
442 
443  logger_t & m_logger;
444 };
445 
446 } /* namespace impl */
447 
448 } /* namespace restinio */
connection_factory_shared_ptr_t m_connection_factory
Factory for creating connections.
Definition: acceptor.hpp:441
void open()
Start listen on port specified in ctor.
Definition: acceptor.hpp:201
A class for holding actual IP-blocker.
Definition: acceptor.hpp:108
restinio::ip_blocker::inspection_result_t inspect_incoming(Socket &socket) const noexcept
Definition: acceptor.hpp:120
Socket move_socket(std::size_t idx)
Extract the socket via move.
Definition: acceptor.hpp:72
void close_impl()
Close opened acceptor.
Definition: acceptor.hpp:405
const asio_ns::ip::tcp m_protocol
Definition: acceptor.hpp:423
std::unique_ptr< acceptor_options_setter_t > m_acceptor_options_setter
Server port listener and connection receiver routine.
Definition: acceptor.hpp:429
auto cuncurrent_accept_sockets_count() const noexcept
The number of sockets that can be used for cuncurrent accept operations.
Definition: acceptor.hpp:82
const bool m_separate_accept_and_create_connect
Do separate an accept operation and connection instantiation.
Definition: acceptor.hpp:438
void accept_next(std::size_t i)
Set a callback for a new connection.
Definition: acceptor.hpp:298
Socket & socket(std::size_t idx)
Get the reference to socket.
Definition: acceptor.hpp:63
socket_supplier_t(Settings &settings, asio_ns::io_context &io_context)
Definition: acceptor.hpp:42
std::vector< Socket > m_sockets
A temporary socket for receiving new connections.
Definition: acceptor.hpp:93
const std::uint16_t m_port
Server endpoint.
Definition: acceptor.hpp:422
void accept_current_connection(std::size_t i, const std::error_code &ec)
Accept current connection.
Definition: acceptor.hpp:314
restinio::ip_blocker::inspection_result_t inspect_incoming(Socket &socket) const noexcept
Definition: acceptor.hpp:145
const std::string m_address
Definition: acceptor.hpp:424
acceptor_t(Settings &settings, asio_ns::io_context &io_context, connection_factory_shared_ptr_t connection_factory, logger_t &logger)
Definition: acceptor.hpp:177
void do_accept_current_connection(stream_socket_t incoming_socket, endpoint_t remote_endpoint)
Definition: acceptor.hpp:373
asio_ns::io_context & m_io_context
io_context for sockets to run on.
Definition: acceptor.hpp:89
asio_ns::executor m_executor
Asio executor.
Definition: acceptor.hpp:434
void close()
Close listener if any.
Definition: acceptor.hpp:271
auto & get_open_close_operations_executor() noexcept
Get an executor for close operation.
Definition: acceptor.hpp:287
strand_t m_open_close_operations_executor
Definition: acceptor.hpp:435
auto & get_executor() noexcept
Get executor for acceptor.
Definition: acceptor.hpp:294
asio_ns::ip::tcp::acceptor m_acceptor
Definition: acceptor.hpp:430
Context for accepting http connections.
Definition: acceptor.hpp:159
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