acceptor.hpp
Go to the documentation of this file.
/*
restinio
*/
#pragma once
#include <memory>
#include <restinio/connection_count_limiter.hpp>
#include <restinio/impl/include_fmtlib.hpp>
#include <restinio/impl/connection.hpp>
#include <restinio/utils/suppress_exceptions.hpp>
namespace restinio
{
namespace impl
{
//
// socket_supplier_t
//
/*
A helper base class that hides a pool of socket instances.
It prepares a socket for new connections.
And as it is template class over a socket type
it givies an oportunity to customize details for
other types of sockets (like `asio::ssl::stream< asio::ip::tcp::socket >`)
that can be used.
*/
template < typename Socket >
class socket_supplier_t
{
protected:
template < typename Settings >
socket_supplier_t(
Settings & settings,
asio_ns::io_context & io_context )
: m_io_context{ io_context }
{
m_sockets.reserve( settings.concurrent_accepts_count() );
std::generate_n(
std::back_inserter( m_sockets ),
settings.concurrent_accepts_count(),
[this]{
return Socket{m_io_context};
} );
assert( m_sockets.size() == settings.concurrent_accepts_count() );
}
Socket &
socket(
std::size_t idx )
{
return m_sockets.at( idx );
}
Socket
move_socket(
std::size_t idx )
{
return std::move( socket(idx ) );
}
auto
concurrent_accept_sockets_count() const noexcept
{
return m_sockets.size();
}
private:
asio_ns::io_context & m_io_context;
std::vector< Socket > m_sockets;
};
namespace acceptor_details
{
template< typename Ip_Blocker >
struct ip_blocker_holder_t
{
std::shared_ptr< Ip_Blocker > m_ip_blocker;
template< typename Settings >
ip_blocker_holder_t(
const Settings & settings )
: m_ip_blocker{ settings.ip_blocker() }
{}
template< typename Socket >
restinio::ip_blocker::inspection_result_t
inspect_incoming( Socket & socket ) const noexcept
{
return m_ip_blocker->inspect(
restinio::ip_blocker::incoming_info_t{
socket.lowest_layer().remote_endpoint()
} );
}
};
template<>
struct ip_blocker_holder_t< restinio::ip_blocker::noop_ip_blocker_t >
{
template< typename Settings >
ip_blocker_holder_t( const Settings & ) { /* nothing to do */ }
template< typename Socket >
restinio::ip_blocker::inspection_result_t
inspect_incoming( Socket & /*socket*/ ) const noexcept
{
return restinio::ip_blocker::inspection_result_t::allow;
}
};
} /* namespace acceptor_details */
//
// acceptor_t
//
template < typename Traits >
class acceptor_t final
: public std::enable_shared_from_this< acceptor_t< Traits > >
, protected socket_supplier_t< typename Traits::stream_socket_t >
, protected acceptor_details::ip_blocker_holder_t< typename Traits::ip_blocker_t >
, protected restinio::connection_count_limits::impl::acceptor_callback_iface_t
{
using ip_blocker_base_t = acceptor_details::ip_blocker_holder_t<
typename Traits::ip_blocker_t >;
using connection_count_limiter_t =
typename connection_count_limit_types< Traits >::limiter_t;
using connection_lifetime_monitor_t =
typename connection_count_limit_types< Traits >::lifetime_monitor_t;
public:
using connection_factory_t = impl::connection_factory_t< Traits >;
using connection_factory_shared_ptr_t =
std::shared_ptr< connection_factory_t >;
using logger_t = typename Traits::logger_t;
using strand_t = typename Traits::strand_t;
using stream_socket_t = typename Traits::stream_socket_t;
using socket_holder_base_t = socket_supplier_t< stream_socket_t >;
template < typename Settings >
acceptor_t(
Settings & settings,
asio_ns::io_context & io_context,
connection_factory_shared_ptr_t connection_factory,
logger_t & logger )
: socket_holder_base_t{ settings, io_context }
, ip_blocker_base_t{ settings }
, m_port{ settings.port() }
, m_protocol{ settings.protocol() }
, m_address{ settings.address() }
, m_acceptor_options_setter{ settings.acceptor_options_setter() }
, m_acceptor{ io_context }
, m_acceptor_post_bind_hook{ settings.giveaway_acceptor_post_bind_hook() }
, m_executor{ io_context.get_executor() }
, m_open_close_operations_executor{ io_context.get_executor() }
, m_separate_accept_and_create_connect{ settings.separate_accept_and_create_connect() }
, m_connection_factory{ std::move( connection_factory ) }
, m_logger{ logger }
, m_connection_count_limiter{
self_as_acceptor_callback(),
restinio::connection_count_limits::max_parallel_connections_t{
settings.max_parallel_connections()
},
restinio::connection_count_limits::max_active_accepts_t{
settings.concurrent_accepts_count()
}
}
{}
void
open()
{
if( m_acceptor.is_open() )
{
const auto ep = m_acceptor.local_endpoint();
m_logger.warn( [&]{
return fmt::format( "server already started on {}", ep );
} );
return;
}
asio_ns::ip::tcp::endpoint ep{ m_protocol, m_port };
const auto actual_address = try_extract_actual_address_from_variant(
m_address );
if( actual_address )
ep.address( *actual_address );
try
{
m_logger.trace( [&]{
return fmt::format( "starting server on {}", ep );
} );
m_acceptor.open( ep.protocol() );
{
// Set acceptor options.
acceptor_options_t options{ m_acceptor };
(*m_acceptor_options_setter)( options );
}
m_acceptor.bind( ep );
// Since v.0.6.11 the post-bind hook should be invoked.
m_acceptor_post_bind_hook( m_acceptor );
// server end-point can be replaced if port is allocated by
// the operating system (e.g. zero is specified as port number
// by a user).
ep = m_acceptor.local_endpoint();
// Now we can switch acceptor to listen state.
m_acceptor.listen( asio_ns::socket_base::max_connections );
// Call accept connections routine.
for( std::size_t i = 0; i< this->concurrent_accept_sockets_count(); ++i )
{
m_logger.info( [&]{
return fmt::format( "init accept #{}", i );
} );
accept_next( i );
}
m_logger.info( [&]{
return fmt::format( "server started on {}", ep );
} );
}
catch( const std::exception & ex )
{
// Acceptor should be closes in the case of an error.
if( m_acceptor.is_open() )
m_acceptor.close();
m_logger.error( [&]() -> auto {
return fmt::format( "failed to start server on {}: {}",
ep,
ex.what() );
} );
throw;
}
}
void
close()
{
if( m_acceptor.is_open() )
{
close_impl();
}
else
{
m_logger.trace( [&]{
return fmt::format( "server already closed" );
} );
}
}
auto &
get_open_close_operations_executor() noexcept
{
return m_open_close_operations_executor;
}
private:
auto & get_executor() noexcept { return m_executor; }
// Begin of implementation of acceptor_callback_iface_t.
void
call_accept_now( std::size_t index ) noexcept
{
m_acceptor.async_accept(
this->socket( index ).lowest_layer(),
asio_ns::bind_executor(
get_executor(),
[index, ctx = this->shared_from_this()]
( const auto & ec ) noexcept
{
if( !ec )
{
ctx->accept_current_connection( index, ec );
}
} ) );
}
void
schedule_next_accept_attempt( std::size_t index ) noexcept
{
asio_ns::post(
asio_ns::bind_executor(
get_executor(),
[index, ctx = this->shared_from_this()]() noexcept
{
ctx->accept_next( index );
} ) );
}
::restinio::connection_count_limits::impl::acceptor_callback_iface_t *
self_as_acceptor_callback() noexcept
{
return this;
}
// End of implementation of acceptor_callback_iface_t.
void
accept_next( std::size_t i ) noexcept
{
m_connection_count_limiter.accept_next( i );
}
void
accept_current_connection(
std::size_t i,
const std::error_code & ec ) noexcept
{
if( !ec )
{
restinio::utils::suppress_exceptions(
m_logger,
"accept_current_connection",
[this, i] {
accept_connection_for_socket_with_index( i );
} );
}
else
{
// Something goes wrong with connection.
restinio::utils::log_error_noexcept( m_logger,
[&]{
return fmt::format(
"failed to accept connection on socket #{}: {}",
i,
ec.message() );
} );
}
// Continue accepting.
accept_next( i );
}
void
accept_connection_for_socket_with_index(
std::size_t i )
{
auto incoming_socket = this->move_socket( i );
auto remote_endpoint =
incoming_socket.lowest_layer().remote_endpoint();
m_logger.trace( [&]{
return fmt::format(
"accept connection from {} on socket #{}",
remote_endpoint, i );
} );
// Since v.0.5.1 the incoming connection must be
// inspected by IP-blocker.
const auto inspection_result = this->inspect_incoming(
incoming_socket );
switch( inspection_result )
{
case restinio::ip_blocker::inspection_result_t::deny:
// New connection can be used. It is disabled by IP-blocker.
m_logger.warn( [&]{
return fmt::format(
"accepted connection from {} on socket #{} denied by"
" IP-blocker",
remote_endpoint, i );
} );
// incoming_socket will be closed automatically.
break;
case restinio::ip_blocker::inspection_result_t::allow:
// Acception of the connection can be continued.
do_accept_current_connection(
std::move(incoming_socket),
remote_endpoint );
break;
}
}
void
do_accept_current_connection(
stream_socket_t incoming_socket,
endpoint_t remote_endpoint )
{
auto create_and_init_connection =
[sock = std::move(incoming_socket),
factory = m_connection_factory,
ep = std::move(remote_endpoint),
lifetime_monitor = connection_lifetime_monitor_t{
&m_connection_count_limiter
},
logger = &m_logger]
() mutable noexcept
{
// NOTE: this code block shouldn't throw!
restinio::utils::suppress_exceptions(
*logger,
"do_accept_current_connection.create_and_init_connection",
[&] {
// Create new connection handler.
// NOTE: since v.0.6.3 this method throws in
// the case of an error. Because of that there is
// no need to check the value returned.
auto conn = factory->create_new_connection(
std::move(sock),
std::move(ep),
std::move(lifetime_monitor) );
// Start waiting for request message.
conn->init();
} );
};
if( m_separate_accept_and_create_connect )
{
asio_ns::post(
get_executor(),
std::move( create_and_init_connection ) );
}
else
{
create_and_init_connection();
}
}
void
close_impl()
{
const auto ep = m_acceptor.local_endpoint();
// An exception in logger should not prevent a call of close()
// for m_acceptor.
restinio::utils::log_trace_noexcept( m_logger,
[&]{
return fmt::format( "closing server on {}", ep );
} );
m_acceptor.close();
m_logger.info( [&]{
return fmt::format( "server closed on {}", ep );
} );
}
const std::uint16_t m_port;
const asio_ns::ip::tcp m_protocol;
const restinio::details::address_variant_t m_address;
std::unique_ptr< acceptor_options_setter_t > m_acceptor_options_setter;
asio_ns::ip::tcp::acceptor m_acceptor;
acceptor_post_bind_hook_t m_acceptor_post_bind_hook;
default_asio_executor m_executor;
strand_t m_open_close_operations_executor;
const bool m_separate_accept_and_create_connect;
connection_factory_shared_ptr_t m_connection_factory;
logger_t & m_logger;
connection_count_limiter_t m_connection_count_limiter;
RESTINIO_NODISCARD
static optional_t< asio_ns::ip::address >
try_extract_actual_address_from_variant(
const restinio::details::address_variant_t & from )
{
optional_t< asio_ns::ip::address > result;
if( auto * str_v = get_if<std::string>( &from ) )
{
auto str_addr = *str_v;
if( str_addr == "localhost" )
str_addr = "127.0.0.1";
else if( str_addr == "ip6-localhost" )
str_addr = "::1";
result = asio_ns::ip::address::from_string( str_addr );
}
else if( auto * addr_v = get_if<asio_ns::ip::address>( &from ) )
{
result = *addr_v;
}
return result;
}
};
} /* namespace impl */
} /* namespace restinio */
nonstd::optional_lite::optional
class optional
Definition: optional.hpp:919
nonstd::variants::variant
Definition: variant.hpp:1301
restinio::acceptor_options_t
An adapter for setting acceptor options before running server.
Definition: settings.hpp:185
restinio::connection_count_limits::connection_lifetime_monitor_t
Helper type for controlling the lifetime of the connection.
Definition: connection_count_limiter.hpp:375
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::impl::acceptor_t
Context for accepting http connections.
Definition: acceptor.hpp:167
restinio::impl::acceptor_t::open
void open()
Start listen on port specified in ctor.
Definition: acceptor.hpp:220
restinio::impl::acceptor_t::m_acceptor
asio_ns::ip::tcp::acceptor m_acceptor
Definition: acceptor.hpp:549
restinio::impl::acceptor_t::close_impl
void close_impl()
Close opened acceptor.
Definition: acceptor.hpp:521
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::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
restinio::impl::acceptor_t::strand_t
typename Traits::strand_t strand_t
Definition: acceptor.hpp:181
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::impl::acceptor_t::m_protocol
const asio_ns::ip::tcp m_protocol
Definition: acceptor.hpp:542
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::connection_count_limiter_t
typename connection_count_limit_types< Traits >::limiter_t connection_count_limiter_t
Definition: acceptor.hpp:172
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::impl::acceptor_t::m_open_close_operations_executor
strand_t m_open_close_operations_executor
Definition: acceptor.hpp:560
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::m_logger
logger_t & m_logger
Definition: acceptor.hpp:568
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::acceptor_t::schedule_next_accept_attempt
void schedule_next_accept_attempt(std::size_t index) noexcept
Definition: acceptor.hpp:346
restinio::impl::acceptor_t::stream_socket_t
typename Traits::stream_socket_t stream_socket_t
Definition: acceptor.hpp:182
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
restinio::impl::acceptor_t::m_port
const std::uint16_t m_port
Server endpoint.
Definition: acceptor.hpp:541
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::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::impl::acceptor_t::close
void close()
Close listener if any.
Definition: acceptor.hpp:296
restinio::impl::acceptor_t::call_accept_now
void call_accept_now(std::size_t index) noexcept
Definition: acceptor.hpp:326
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::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::impl::acceptor_t::m_address
const restinio::details::address_variant_t m_address
Definition: acceptor.hpp:543
restinio::impl::acceptor_t::logger_t
typename Traits::logger_t logger_t
Definition: acceptor.hpp:180
restinio::impl::acceptor_t::m_executor
default_asio_executor m_executor
Asio executor.
Definition: acceptor.hpp:559
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::get_executor
auto & get_executor() noexcept
Get executor for acceptor.
Definition: acceptor.hpp:319
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
restinio::impl::connection_factory_t
Factory for connections.
Definition: connection.hpp:1709
restinio::impl::socket_supplier_t
Definition: acceptor.hpp:42
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::socket_supplier_t::socket
Socket & socket(std::size_t idx)
Get the reference to socket.
Definition: acceptor.hpp:66
restinio::impl::socket_supplier_t::m_sockets
std::vector< Socket > m_sockets
A temporary socket for receiving new connections.
Definition: acceptor.hpp:96
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::socket_supplier_t::move_socket
Socket move_socket(std::size_t idx)
Extract the socket via move.
Definition: acceptor.hpp:75
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::ip_blocker::incoming_info_t
An information about new incoming connection to be passed to IP-blocker object.
Definition: ip_blocker.hpp:64
RESTINIO_NODISCARD
#define RESTINIO_NODISCARD
Definition: compiler_features.hpp:33
connection_count_limiter.hpp
Stuff related to limits of active parallel connections.
connection.hpp
include_fmtlib.hpp
A special wrapper around fmtlib include files.
nonstd::optional_lite::std11::move
T & move(T &t)
Definition: optional.hpp:465
restinio::connection_count_limits::max_active_accepts_t
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.
Definition: connection_count_limiter.hpp:52
restinio::connection_count_limits::max_parallel_connections_t
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.
Definition: connection_count_limiter.hpp:39
restinio::easy_parser
Definition: easy_parser.hpp:39
restinio::ip_blocker::inspection_result_t
inspection_result_t
Enumeration of result of inspecting new incoming connection.
Definition: ip_blocker.hpp:31
restinio::ip_blocker::inspection_result_t::deny
@ deny
New connection is disabled and should be closed.
restinio::ip_blocker::inspection_result_t::allow
@ allow
New connection is allowed to be processed further.
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::utils::log_error_noexcept
void log_error_noexcept(Logger &&logger, Message_Builder &&builder) noexcept
Definition: suppress_exceptions.hpp:71
restinio::utils::log_trace_noexcept
void log_trace_noexcept(Logger &&logger, Message_Builder &&builder) noexcept
Definition: suppress_exceptions.hpp:32
restinio::utils::from_string
Value_Type from_string(string_view_t s)
Get a value from string.
Definition: from_string.hpp:105
restinio
Definition: asio_include.hpp:21
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
restinio::default_asio_executor
asio_ns::executor default_asio_executor
Definition: asio_include.hpp:224
restinio::endpoint_t
asio_ns::ip::tcp::endpoint endpoint_t
An alias for endpoint type from Asio.
Definition: common_types.hpp:177
std
STL namespace.
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::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_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::impl::acceptor_details::ip_blocker_holder_t
A class for holding actual IP-blocker.
Definition: acceptor.hpp:112
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::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_details::ip_blocker_holder_t::ip_blocker_holder_t
ip_blocker_holder_t(const Settings &settings)
Definition: acceptor.hpp:116
restinio::ip_blocker::noop_ip_blocker_t
The default no-op IP-blocker.
Definition: ip_blocker.hpp:94
suppress_exceptions.hpp
Utilities for suppressing exceptions from some code block.
const
#define const
Definition: zconf.h:230