#include <iostream>
#include <so_5/all.hpp>
#include <random>
#include <tuple>
struct do_processing
{
};
template < typename RESP >
RESP
init_resp( RESP resp )
{
resp.append_header( restinio::http_field::server, "RESTinio sample server /v.0.7" );
resp.append_header_date_field();
return resp;
}
class auth_performer
{
std::mt19937 m_generator{ std::random_device{}() };
std::uniform_int_distribution<> m_pause_generator{ 75, 750 };
const so_5::mchain_t m_processing_ch;
public:
struct do_auth
{
std::string m_username;
std::string m_password;
};
struct ask_for_credentials
{
};
auth_performer( so_5::mchain_t processing_ch )
: m_processing_ch{ std::move(processing_ch) }
{}
void on_do_processing( do_processing cmd )
{
const auto req = cmd.m_controller->request_handle();
if( restinio::http_method_get() == req->header().method() )
{
std::string_view target = req->header().request_target();
if( target.size() > 1u && '/' == target.back() )
target.remove_suffix( 1u );
if( "/admin" == target || "/stats" == target )
{
using namespace restinio::http_field_parsers::basic_auth;
restinio::http_field::authorization );
if( result )
{
const std::chrono::milliseconds pause{
m_pause_generator( m_generator )
};
so_5::send_delayed< so_5::mutable_msg< do_auth > >(
m_processing_ch,
pause,
result->username,
result->password,
std::move(cmd.m_controller) );
}
else
{
so_5::send< so_5::mutable_msg<ask_for_credentials> >(
m_processing_ch,
std::move(cmd.m_controller) );
}
}
}
if( cmd.m_controller )
next( std::move(cmd.m_controller) );
}
void on_do_auth( do_auth cmd )
{
if( "user" == cmd.m_username && "12345" == cmd.m_password )
{
next( std::move(cmd.m_controller) );
}
else
{
so_5::send_delayed< so_5::mutable_msg<ask_for_credentials> >(
m_processing_ch,
std::chrono::milliseconds{ 1750 },
std::move(cmd.m_controller) );
}
}
void on_ask_for_credentials( ask_for_credentials cmd )
{
init_resp( cmd.m_controller->request_handle()->create_response(
.append_header( restinio::http_field::content_type,
"text/plain; charset=utf-8" )
.append_header( restinio::http_field::www_authenticate,
R"(Basic realm="Username/Password required", charset="utf-8")" )
.set_body( "Unauthorized access forbidden")
.done();
}
};
[[nodiscard]]
std::shared_ptr< express_router_t > create_request_handler()
{
auto router = std::make_shared< express_router_t >();
"/",
[]( const auto & req, const auto & ) {
init_resp( req->create_response() )
.append_header( restinio::http_field::content_type, "text/plain; charset=utf-8" )
.set_body( "Hello world!")
.done();
} );
"/json",
[]( const auto & req, const auto & ) {
init_resp( req->create_response() )
.append_header( restinio::http_field::content_type, "application/json" )
.set_body( R"-({"message" : "Hello world!"})-")
.done();
} );
"/html",
[]( const auto & req, const auto & ) {
init_resp( req->create_response() )
.append_header( restinio::http_field::content_type, "text/html; charset=utf-8" )
.set_body(
"<html>\r\n"
" <head><title>Hello from RESTinio!</title></head>\r\n"
" <body>\r\n"
" <center><h1>Hello world</h1></center>\r\n"
" </body>\r\n"
"</html>\r\n" )
.done();
} );
"/stats",
[]( const auto & req, const auto & ) {
init_resp( req->create_response() )
.append_header( restinio::http_field::content_type, "text/plain; charset=utf-8" )
.set_body( "Statistics for that site is not available now")
.done();
} );
"/admin",
[]( const auto & req, const auto & ) {
init_resp( req->create_response() )
.append_header( restinio::http_field::content_type, "text/html; charset=utf-8" )
.set_body(
"<html>\r\n"
" <head><title>Admin panel</title></head>\r\n"
" <body>\r\n"
" <center><h1>NOT IMPLEMENTED YET</h1></center>\r\n"
" </body>\r\n"
"</html>\r\n" )
.done();
} );
}
void auth_thread_func(const so_5::mchain_t& req_ch)
{
const auto auth_ch = so_5::create_mchain( req_ch->environment() );
auth_performer authorither{ auth_ch };
bool stop = false;
select(
so_5::from_all().handle_all()
.on_close( [&stop](const auto &) { stop = true; } )
.stop_on( [&stop]{ return stop; } ),
so_5::receive_case( req_ch,
[&]( so_5::mutable_mhood_t<do_processing> cmd ) {
authorither.on_do_processing( std::move(*cmd) );
} ),
so_5::receive_case( auth_ch,
[&]( so_5::mutable_mhood_t<auth_performer::do_auth> cmd ) {
authorither.on_do_auth( std::move(*cmd) );
},
[&]( so_5::mutable_mhood_t<auth_performer::ask_for_credentials> cmd ) {
authorither.on_ask_for_credentials( std::move(*cmd) );
} )
);
}
void processing_thread_func( const so_5::mchain_t& req_ch )
{
bool stop = false;
receive(
so_5::from( req_ch ).handle_all()
.on_close( [&stop](const auto &) { stop = true; } )
.stop_on( [&stop]{ return stop; } ),
[
router = create_request_handler()]
( so_5::mutable_mhood_t<do_processing> cmd ) {
std::ignore = (*router)(cmd->m_controller->request_handle());
});
}
int main()
{
try
{
so_5::wrapped_env_t sobj;
std::thread auth_thread, processing_thread;
auto threads_joiner = so_5::auto_join( auth_thread, processing_thread );
auto auth_ch = so_5::create_mchain( sobj );
auto processing_ch = so_5::create_mchain( sobj );
auto ch_closer = so_5::auto_close_drop_content( auth_ch, processing_ch );
auth_thread = std::thread{ auth_thread_func, auth_ch };
processing_thread = std::thread{ processing_thread_func, processing_ch };
.port( 8080 )
.address( "localhost" )
.request_handler(
[auth_ch]( auto controller ) {
so_5::send< so_5::mutable_msg<do_processing> >(
auth_ch,
std::move(controller) );
},
[processing_ch]( auto controller ) {
so_5::send< so_5::mutable_msg<do_processing> >(
processing_ch,
std::move(controller) );
} ) );
}
catch( const std::exception & ex )
{
std::cerr << "Error: " << ex.what() << std::endl;
return 1;
}
return 0;
}
Chain of async handlers of the fixed size.
Helpers for dealing with basic authentification.
Timer factory implementation using asio timers.
A holder of fixed-size chain of asynchronous handlers.
Include all core header files in one.
constexpr schedule_result_t ok() noexcept
Helper function to be used if scheduling was successful.
void next(unique_async_handling_controller_t< Extra_Data_Factory > controller)
Command to try to switch to the next handler in an async chain.
std::unique_ptr< async_handling_controller_t< Extra_Data_Factory > > unique_async_handling_controller_t
Short alias for unique_ptr to async_handling_controller.
expected_t< params_t, extraction_error_t > try_extract_params(const authorization_value_t &http_field)
Helper function for getting parameters of basic authentification from an already parsed HTTP-field.
generic_express_router_t< Regex_Engine, no_extra_data_factory_t > express_router_t
A type of express-like router for the case when the default extra-data-factory is specified in the se...
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.
ostream_logger_t< null_mutex_t > single_threaded_ostream_logger_t
http_status_line_t status_unauthorized()
void run(asio_ns::io_context &ioctx, run_on_this_thread_settings_t< Traits > &&settings)
Helper function for running http server until ctrl+c is hit.
constexpr request_handling_status_t request_accepted() noexcept
Helper functions for parsing values of HTTP-fields.