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
21namespace restinio
22{
23
24namespace 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*/
40template < 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
99namespace acceptor_details
100{
101
110template< 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
140template<>
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
161template < typename Traits >
162class 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(),
209 restinio::connection_count_limits::max_parallel_connections_t{
210 settings.max_parallel_connections()
211 },
212 restinio::connection_count_limits::max_active_accepts_t{
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 */
An adapter for setting acceptor options before running server.
Definition: settings.hpp:185
Helper type for controlling the lifetime of the connection.
An interface of acceptor to be used by connection count limiters.
Context for accepting http connections.
Definition: acceptor.hpp:167
void open()
Start listen on port specified in ctor.
Definition: acceptor.hpp:220
asio_ns::ip::tcp::acceptor m_acceptor
Definition: acceptor.hpp:549
void close_impl()
Close opened acceptor.
Definition: acceptor.hpp:521
void accept_connection_for_socket_with_index(std::size_t i)
Performs actual actions for accepting a new connection.
Definition: acceptor.hpp:431
::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
typename Traits::strand_t strand_t
Definition: acceptor.hpp:181
std::shared_ptr< connection_factory_t > connection_factory_shared_ptr_t
Definition: acceptor.hpp:179
const asio_ns::ip::tcp m_protocol
Definition: acceptor.hpp:542
connection_factory_shared_ptr_t m_connection_factory
Factory for creating connections.
Definition: acceptor.hpp:566
typename connection_count_limit_types< Traits >::limiter_t connection_count_limiter_t
Definition: acceptor.hpp:172
connection_count_limiter_t m_connection_count_limiter
Actual limiter of active parallel connections.
Definition: acceptor.hpp:575
strand_t m_open_close_operations_executor
Definition: acceptor.hpp:560
void do_accept_current_connection(stream_socket_t incoming_socket, endpoint_t remote_endpoint)
Definition: acceptor.hpp:474
void accept_next(std::size_t i) noexcept
Set a callback for a new connection.
Definition: acceptor.hpp:381
void schedule_next_accept_attempt(std::size_t index) noexcept
Definition: acceptor.hpp:346
typename Traits::stream_socket_t stream_socket_t
Definition: acceptor.hpp:182
typename connection_count_limit_types< Traits >::lifetime_monitor_t connection_lifetime_monitor_t
Definition: acceptor.hpp:174
const std::uint16_t m_port
Server endpoint.
Definition: acceptor.hpp:541
void accept_current_connection(std::size_t i, const std::error_code &ec) noexcept
Accept current connection.
Definition: acceptor.hpp:392
std::unique_ptr< acceptor_options_setter_t > m_acceptor_options_setter
Server port listener and connection receiver routine.
Definition: acceptor.hpp:548
auto & get_open_close_operations_executor() noexcept
Get an executor for close operation.
Definition: acceptor.hpp:312
void close()
Close listener if any.
Definition: acceptor.hpp:296
void call_accept_now(std::size_t index) noexcept
Definition: acceptor.hpp:326
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
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
const restinio::details::address_variant_t m_address
Definition: acceptor.hpp:543
typename Traits::logger_t logger_t
Definition: acceptor.hpp:180
default_asio_executor m_executor
Asio executor.
Definition: acceptor.hpp:559
const bool m_separate_accept_and_create_connect
Do separate an accept operation and connection instantiation.
Definition: acceptor.hpp:563
auto & get_executor() noexcept
Get executor for acceptor.
Definition: acceptor.hpp:319
acceptor_t(Settings &settings, asio_ns::io_context &io_context, connection_factory_shared_ptr_t connection_factory, logger_t &logger)
Definition: acceptor.hpp:186
auto concurrent_accept_sockets_count() const noexcept
The number of sockets that can be used for cuncurrent accept operations.
Definition: acceptor.hpp:85
Socket & socket(std::size_t idx)
Get the reference to socket.
Definition: acceptor.hpp:66
std::vector< Socket > m_sockets
A temporary socket for receiving new connections.
Definition: acceptor.hpp:96
socket_supplier_t(Settings &settings, asio_ns::io_context &io_context)
Definition: acceptor.hpp:45
Socket move_socket(std::size_t idx)
Extract the socket via move.
Definition: acceptor.hpp:75
asio_ns::io_context & m_io_context
io_context for sockets to run on.
Definition: acceptor.hpp:92
An information about new incoming connection to be passed to IP-blocker object.
Definition: ip_blocker.hpp:64
#define RESTINIO_NODISCARD
Stuff related to limits of active parallel connections.
A special wrapper around fmtlib include files.
restinio::utils::tagged_scalar_t< std::size_t, max_active_accepts_tag > max_active_accepts_t
A kind of strict typedef for maximum count of active accepts.
restinio::utils::tagged_scalar_t< std::size_t, max_parallel_connections_tag > max_parallel_connections_t
A kind of strict typedef for maximum count of active connections.
inspection_result_t
Enumeration of result of inspecting new incoming connection.
Definition: ip_blocker.hpp:31
@ deny
New connection is disabled and should be closed.
@ allow
New connection is allowed to be processed further.
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...
void log_error_noexcept(Logger &&logger, Message_Builder &&builder) noexcept
void log_trace_noexcept(Logger &&logger, Message_Builder &&builder) noexcept
Value_Type from_string(string_view_t s)
Get a value from string.
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
asio_ns::executor default_asio_executor
asio_ns::ip::tcp::endpoint endpoint_t
An alias for endpoint type from Asio.
STL namespace.
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
restinio::ip_blocker::inspection_result_t inspect_incoming(Socket &) const noexcept
Definition: acceptor.hpp:148
A class for holding actual IP-blocker.
Definition: acceptor.hpp:112
restinio::ip_blocker::inspection_result_t inspect_incoming(Socket &socket) const noexcept
Definition: acceptor.hpp:123
The default no-op IP-blocker.
Definition: ip_blocker.hpp:94
Utilities for suppressing exceptions from some code block.
#define const
Definition: zconf.h:230