RESTinio
Loading...
Searching...
No Matches
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 >
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 >
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(
227 RESTINIO_FMT_FORMAT_STRING( "server already started on {}" ),
228 fmtlib_tools::streamed( ep ) );
229 } );
230 return;
231 }
232
233 asio_ns::ip::tcp::endpoint ep{ m_protocol, m_port };
234
235 const auto actual_address = try_extract_actual_address_from_variant(
236 m_address );
237 if( actual_address )
238 ep.address( *actual_address );
239
240 try
241 {
242 m_logger.trace( [&]{
243 return fmt::format(
244 RESTINIO_FMT_FORMAT_STRING( "starting server on {}" ),
245 fmtlib_tools::streamed( ep ) );
246 } );
247
248 m_acceptor.open( ep.protocol() );
249
250 {
251 // Set acceptor options.
252 acceptor_options_t options{ m_acceptor };
253
254 (*m_acceptor_options_setter)( options );
255 }
256
257 m_acceptor.bind( ep );
258 // Since v.0.6.11 the post-bind hook should be invoked.
259 m_acceptor_post_bind_hook( m_acceptor );
260 // server end-point can be replaced if port is allocated by
261 // the operating system (e.g. zero is specified as port number
262 // by a user).
263 ep = m_acceptor.local_endpoint();
264
265 // Now we can switch acceptor to listen state.
266 m_acceptor.listen( asio_ns::socket_base::max_connections );
267
268 // Call accept connections routine.
269 for( std::size_t i = 0; i< this->concurrent_accept_sockets_count(); ++i )
270 {
271 m_logger.info( [&]{
272 return fmt::format(
273 RESTINIO_FMT_FORMAT_STRING( "init accept #{}" ), i );
274 } );
275
276 accept_next( i );
277 }
278
279 m_logger.info( [&]{
280 return fmt::format(
281 RESTINIO_FMT_FORMAT_STRING( "server started on {}" ),
282 fmtlib_tools::streamed( ep ) );
283 } );
284 }
285 catch( const std::exception & ex )
286 {
287 // Acceptor should be closes in the case of an error.
288 if( m_acceptor.is_open() )
289 m_acceptor.close();
290
291 m_logger.error( [&]() -> auto {
292 return fmt::format(
294 "failed to start server on {}: {}" ),
295 fmtlib_tools::streamed( ep ),
296 ex.what() );
297 } );
298
299 throw;
300 }
301 }
302
304 void
306 {
307 if( m_acceptor.is_open() )
308 {
309 close_impl();
310 }
311 else
312 {
313 // v.0.7.0: suppress exceptions from logging.
315 [&]{
316 return fmt::format(
317 RESTINIO_FMT_FORMAT_STRING( "server already closed" ) );
318 } );
319 }
320 }
321
323 auto &
325 {
326 return m_open_close_operations_executor;
327 }
328
329 private:
331 auto & get_executor() noexcept { return m_executor; }
332
333 // Begin of implementation of acceptor_callback_iface_t.
337 void
338 call_accept_now( std::size_t index ) noexcept override
339 {
340 m_acceptor.async_accept(
341 this->socket( index ).lowest_layer(),
342 asio_ns::bind_executor(
343 get_executor(),
344 [index, ctx = this->shared_from_this()]
345 ( const auto & ec ) noexcept
346 {
347 if( !ec )
348 {
349 ctx->accept_current_connection( index, ec );
350 }
351 } ) );
352 }
353
357 void
358 schedule_next_accept_attempt( std::size_t index ) noexcept override
359 {
360 asio_ns::post(
361 asio_ns::bind_executor(
362 get_executor(),
363 [index, ctx = this->shared_from_this()]() noexcept
364 {
365 ctx->accept_next( index );
366 } ) );
367 }
368
377 {
378 return this;
379 }
380 // End of implementation of acceptor_callback_iface_t.
381
383
392 void
393 accept_next( std::size_t i ) noexcept
394 {
395 m_connection_count_limiter.accept_next( i );
396 }
397
399
403 void
406 std::size_t i,
407 const std::error_code & ec ) noexcept
408 {
409 if( !ec )
410 {
412 m_logger,
413 "accept_current_connection",
414 [this, i] {
415 accept_connection_for_socket_with_index( i );
416 } );
417 }
418 else
419 {
420 // Something goes wrong with connection.
422 [&]{
423 return fmt::format(
425 "failed to accept connection on socket #{}: {}" ),
426 i,
427 ec.message() );
428 } );
429 }
430
431 // Continue accepting.
432 accept_next( i );
433 }
434
443 void
446 std::size_t i )
447 {
448 auto incoming_socket = this->move_socket( i );
449
450 auto remote_endpoint =
451 incoming_socket.lowest_layer().remote_endpoint();
452
453 m_logger.trace( [&]{
454 return fmt::format(
456 "accept connection from {} on socket #{}" ),
457 fmtlib_tools::streamed( remote_endpoint ),
458 i );
459 } );
460
461 // Since v.0.5.1 the incoming connection must be
462 // inspected by IP-blocker.
463 const auto inspection_result = this->inspect_incoming(
464 incoming_socket );
465
466 switch( inspection_result )
467 {
469 // New connection can be used. It is disabled by IP-blocker.
470 m_logger.warn( [&]{
471 return fmt::format(
473 "accepted connection from {} on socket #{} denied by"
474 " IP-blocker" ),
475 fmtlib_tools::streamed( remote_endpoint ),
476 i );
477 } );
478 // incoming_socket will be closed automatically.
479 break;
480
482 // Acception of the connection can be continued.
483 do_accept_current_connection(
484 std::move(incoming_socket),
485 remote_endpoint );
486 break;
487 }
488 }
489
490 void
492 stream_socket_t incoming_socket,
493 endpoint_t remote_endpoint )
494 {
495 auto create_and_init_connection =
496 [sock = std::move(incoming_socket),
497 factory = m_connection_factory,
498 ep = std::move(remote_endpoint),
499 lifetime_monitor = connection_lifetime_monitor_t{
500 &m_connection_count_limiter
501 },
502 logger = &m_logger]
503 () mutable noexcept
504 {
505 // NOTE: this code block shouldn't throw!
507 *logger,
508 "do_accept_current_connection.create_and_init_connection",
509 [&] {
510 // Create new connection handler.
511 // NOTE: since v.0.6.3 this method throws in
512 // the case of an error. Because of that there is
513 // no need to check the value returned.
514 auto conn = factory->create_new_connection(
515 std::move(sock),
516 std::move(ep),
517 std::move(lifetime_monitor) );
518
519 // Start waiting for request message.
520 conn->init();
521 } );
522 };
523
524 if( m_separate_accept_and_create_connect )
525 {
526 asio_ns::post(
527 get_executor(),
528 std::move( create_and_init_connection ) );
529 }
530 else
531 {
532 create_and_init_connection();
533 }
534 }
535
537 void
539 {
540 const auto ep = m_acceptor.local_endpoint();
541
542 // An exception in logger should not prevent a call of close()
543 // for m_acceptor.
545 [&]{
546 return fmt::format(
547 RESTINIO_FMT_FORMAT_STRING( "closing server on {}" ),
548 fmtlib_tools::streamed( ep ) );
549 } );
550
551 m_acceptor.close();
552
553 // v.0.7.0: Suppress exceptions from this logging too.
555 [&]{
556 return fmt::format(
557 RESTINIO_FMT_FORMAT_STRING( "server closed on {}" ),
558 fmtlib_tools::streamed( ep ) );
559 } );
560 }
561
564 const std::uint16_t m_port;
565 const asio_ns::ip::tcp m_protocol;
568
571 std::unique_ptr< acceptor_options_setter_t > m_acceptor_options_setter;
572 asio_ns::ip::tcp::acceptor m_acceptor;
573
575
580
584
587
590
592
599
608 [[nodiscard]]
609 static std::optional< asio_ns::ip::address >
612 {
613 std::optional< asio_ns::ip::address > result;
614
615 if( auto * str_v = std::get_if<std::string>( &from ) )
616 {
617 auto str_addr = *str_v;
618 if( str_addr == "localhost" )
619 str_addr = "127.0.0.1";
620 else if( str_addr == "ip6-localhost" )
621 str_addr = "::1";
622
623 result = asio_ns::ip::address::from_string( str_addr );
624 }
625 else if( auto * addr_v = std::get_if<asio_ns::ip::address>( &from ) )
626 {
627 result = *addr_v;
628 }
629
630 return result;
631 }
632};
633
634} /* namespace impl */
635
636} /* namespace restinio */
An adapter for setting acceptor options before running server.
Definition settings.hpp:184
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:572
void close_impl()
Close opened acceptor.
Definition acceptor.hpp:538
void accept_connection_for_socket_with_index(std::size_t i)
Performs actual actions for accepting a new connection.
Definition acceptor.hpp:444
::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:376
typename Traits::strand_t strand_t
Definition acceptor.hpp:181
static std::optional< 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:610
const asio_ns::ip::tcp m_protocol
Definition acceptor.hpp:565
connection_factory_shared_ptr_t m_connection_factory
Factory for creating connections.
Definition acceptor.hpp:589
connection_count_limiter_t m_connection_count_limiter
Actual limiter of active parallel connections.
Definition acceptor.hpp:598
strand_t m_open_close_operations_executor
Definition acceptor.hpp:583
void do_accept_current_connection(stream_socket_t incoming_socket, endpoint_t remote_endpoint)
Definition acceptor.hpp:491
void accept_next(std::size_t i) noexcept
Set a callback for a new connection.
Definition acceptor.hpp:393
void schedule_next_accept_attempt(std::size_t index) noexcept override
Definition acceptor.hpp:358
typename Traits::stream_socket_t stream_socket_t
Definition acceptor.hpp:182
void call_accept_now(std::size_t index) noexcept override
Definition acceptor.hpp:338
typename connection_count_limit_types< Traits >::limiter_t connection_count_limiter_t
Definition acceptor.hpp:171
const std::uint16_t m_port
Server endpoint.
Definition acceptor.hpp:564
std::shared_ptr< connection_factory_t > connection_factory_shared_ptr_t
Definition acceptor.hpp:178
void accept_current_connection(std::size_t i, const std::error_code &ec) noexcept
Accept current connection.
Definition acceptor.hpp:404
std::unique_ptr< acceptor_options_setter_t > m_acceptor_options_setter
Server port listener and connection receiver routine.
Definition acceptor.hpp:571
auto & get_open_close_operations_executor() noexcept
Get an executor for close operation.
Definition acceptor.hpp:324
void close()
Close listener if any.
Definition acceptor.hpp:305
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:578
const restinio::details::address_variant_t m_address
Definition acceptor.hpp:566
typename Traits::logger_t logger_t
Definition acceptor.hpp:180
typename connection_count_limit_types< Traits >::lifetime_monitor_t connection_lifetime_monitor_t
Definition acceptor.hpp:173
default_asio_executor m_executor
Asio executor.
Definition acceptor.hpp:582
const bool m_separate_accept_and_create_connect
Do separate an accept operation and connection instantiation.
Definition acceptor.hpp:586
auto & get_executor() noexcept
Get executor for acceptor.
Definition acceptor.hpp:331
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.
Stuff related to limits of active parallel connections.
A special wrapper around fmtlib include files.
#define RESTINIO_FMT_FORMAT_STRING(s)
std::variant< no_address_specified_t, std::string, asio_ns::ip::address > address_variant_t
A type of variant for holding IP address for a server in various representations.
Definition settings.hpp:459
inspection_result_t
Enumeration of result of inspecting new incoming connection.
@ 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_info_noexcept(Logger &&logger, Message_Builder &&builder) noexcept
void log_error_noexcept(Logger &&logger, Message_Builder &&builder) noexcept
void log_trace_noexcept(Logger &&logger, Message_Builder &&builder) noexcept
asio_ns::ip::tcp::endpoint endpoint_t
An alias for endpoint type from Asio.
run_on_this_thread_settings_t< Traits > on_this_thread()
A special marker for the case when http_server must be run on the context of the current thread.
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:433
asio_ns::executor default_asio_executor
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.
Utilities for suppressing exceptions from some code block.