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 >
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(
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 m_logger.trace( [&]{
314 return fmt::format(
315 RESTINIO_FMT_FORMAT_STRING( "server already closed" ) );
316 } );
317 }
318 }
319
321 auto &
323 {
324 return m_open_close_operations_executor;
325 }
326
327 private:
329 auto & get_executor() noexcept { return m_executor; }
330
331 // Begin of implementation of acceptor_callback_iface_t.
335 void
336 call_accept_now( std::size_t index ) noexcept override
337 {
338 m_acceptor.async_accept(
339 this->socket( index ).lowest_layer(),
340 asio_ns::bind_executor(
341 get_executor(),
342 [index, ctx = this->shared_from_this()]
343 ( const auto & ec ) noexcept
344 {
345 if( !ec )
346 {
347 ctx->accept_current_connection( index, ec );
348 }
349 } ) );
350 }
351
355 void
356 schedule_next_accept_attempt( std::size_t index ) noexcept override
357 {
358 asio_ns::post(
359 asio_ns::bind_executor(
360 get_executor(),
361 [index, ctx = this->shared_from_this()]() noexcept
362 {
363 ctx->accept_next( index );
364 } ) );
365 }
366
375 {
376 return this;
377 }
378 // End of implementation of acceptor_callback_iface_t.
379
381
390 void
391 accept_next( std::size_t i ) noexcept
392 {
393 m_connection_count_limiter.accept_next( i );
394 }
395
397
401 void
404 std::size_t i,
405 const std::error_code & ec ) noexcept
406 {
407 if( !ec )
408 {
410 m_logger,
411 "accept_current_connection",
412 [this, i] {
413 accept_connection_for_socket_with_index( i );
414 } );
415 }
416 else
417 {
418 // Something goes wrong with connection.
420 [&]{
421 return fmt::format(
423 "failed to accept connection on socket #{}: {}" ),
424 i,
425 ec.message() );
426 } );
427 }
428
429 // Continue accepting.
430 accept_next( i );
431 }
432
441 void
444 std::size_t i )
445 {
446 auto incoming_socket = this->move_socket( i );
447
448 auto remote_endpoint =
449 incoming_socket.lowest_layer().remote_endpoint();
450
451 m_logger.trace( [&]{
452 return fmt::format(
454 "accept connection from {} on socket #{}" ),
455 fmtlib_tools::streamed( remote_endpoint ),
456 i );
457 } );
458
459 // Since v.0.5.1 the incoming connection must be
460 // inspected by IP-blocker.
461 const auto inspection_result = this->inspect_incoming(
462 incoming_socket );
463
464 switch( inspection_result )
465 {
467 // New connection can be used. It is disabled by IP-blocker.
468 m_logger.warn( [&]{
469 return fmt::format(
471 "accepted connection from {} on socket #{} denied by"
472 " IP-blocker" ),
473 fmtlib_tools::streamed( remote_endpoint ),
474 i );
475 } );
476 // incoming_socket will be closed automatically.
477 break;
478
480 // Acception of the connection can be continued.
481 do_accept_current_connection(
482 std::move(incoming_socket),
483 remote_endpoint );
484 break;
485 }
486 }
487
488 void
490 stream_socket_t incoming_socket,
491 endpoint_t remote_endpoint )
492 {
493 auto create_and_init_connection =
494 [sock = std::move(incoming_socket),
495 factory = m_connection_factory,
496 ep = std::move(remote_endpoint),
497 lifetime_monitor = connection_lifetime_monitor_t{
498 &m_connection_count_limiter
499 },
500 logger = &m_logger]
501 () mutable noexcept
502 {
503 // NOTE: this code block shouldn't throw!
505 *logger,
506 "do_accept_current_connection.create_and_init_connection",
507 [&] {
508 // Create new connection handler.
509 // NOTE: since v.0.6.3 this method throws in
510 // the case of an error. Because of that there is
511 // no need to check the value returned.
512 auto conn = factory->create_new_connection(
513 std::move(sock),
514 std::move(ep),
515 std::move(lifetime_monitor) );
516
517 // Start waiting for request message.
518 conn->init();
519 } );
520 };
521
522 if( m_separate_accept_and_create_connect )
523 {
524 asio_ns::post(
525 get_executor(),
526 std::move( create_and_init_connection ) );
527 }
528 else
529 {
530 create_and_init_connection();
531 }
532 }
533
535 void
537 {
538 const auto ep = m_acceptor.local_endpoint();
539
540 // An exception in logger should not prevent a call of close()
541 // for m_acceptor.
543 [&]{
544 return fmt::format(
545 RESTINIO_FMT_FORMAT_STRING( "closing server on {}" ),
546 fmtlib_tools::streamed( ep ) );
547 } );
548
549 m_acceptor.close();
550
551 m_logger.info( [&]{
552 return fmt::format(
553 RESTINIO_FMT_FORMAT_STRING( "server closed on {}" ),
554 fmtlib_tools::streamed( ep ) );
555 } );
556 }
557
560 const std::uint16_t m_port;
561 const asio_ns::ip::tcp m_protocol;
564
567 std::unique_ptr< acceptor_options_setter_t > m_acceptor_options_setter;
568 asio_ns::ip::tcp::acceptor m_acceptor;
569
571
576
580
583
586
588
595
608 {
610
611 if( auto * str_v = get_if<std::string>( &from ) )
612 {
613 auto str_addr = *str_v;
614 if( str_addr == "localhost" )
615 str_addr = "127.0.0.1";
616 else if( str_addr == "ip6-localhost" )
617 str_addr = "::1";
618
619 result = asio_ns::ip::address::from_string( str_addr );
620 }
621 else if( auto * addr_v = get_if<asio_ns::ip::address>( &from ) )
622 {
623 result = *addr_v;
624 }
625
626 return result;
627 }
628};
629
630} /* namespace impl */
631
632} /* 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:568
void close_impl()
Close opened acceptor.
Definition: acceptor.hpp:536
void accept_connection_for_socket_with_index(std::size_t i)
Performs actual actions for accepting a new connection.
Definition: acceptor.hpp:442
::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:374
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:561
connection_factory_shared_ptr_t m_connection_factory
Factory for creating connections.
Definition: acceptor.hpp:585
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:594
strand_t m_open_close_operations_executor
Definition: acceptor.hpp:579
void do_accept_current_connection(stream_socket_t incoming_socket, endpoint_t remote_endpoint)
Definition: acceptor.hpp:489
void accept_next(std::size_t i) noexcept
Set a callback for a new connection.
Definition: acceptor.hpp:391
void schedule_next_accept_attempt(std::size_t index) noexcept override
Definition: acceptor.hpp:356
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
void call_accept_now(std::size_t index) noexcept override
Definition: acceptor.hpp:336
const std::uint16_t m_port
Server endpoint.
Definition: acceptor.hpp:560
void accept_current_connection(std::size_t i, const std::error_code &ec) noexcept
Accept current connection.
Definition: acceptor.hpp:402
std::unique_ptr< acceptor_options_setter_t > m_acceptor_options_setter
Server port listener and connection receiver routine.
Definition: acceptor.hpp:567
auto & get_open_close_operations_executor() noexcept
Get an executor for close operation.
Definition: acceptor.hpp:322
void close()
Close listener if any.
Definition: acceptor.hpp:305
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:606
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:574
const restinio::details::address_variant_t m_address
Definition: acceptor.hpp:562
typename Traits::logger_t logger_t
Definition: acceptor.hpp:180
default_asio_executor m_executor
Asio executor.
Definition: acceptor.hpp:578
const bool m_separate_accept_and_create_connect
Do separate an accept operation and connection instantiation.
Definition: acceptor.hpp:582
auto & get_executor() noexcept
Get executor for acceptor.
Definition: acceptor.hpp:329
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.
#define RESTINIO_FMT_FORMAT_STRING(s)
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
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