2014-03-31 16:51:50 +00:00
# pragma once
2016-08-29 12:13:24 +00:00
# include <chrono>
2022-06-06 14:38:46 +00:00
# define ASIO_STANDALONE
# include <asio.hpp>
2015-09-20 13:06:00 +00:00
# ifdef CROW_ENABLE_SSL
2022-06-06 14:38:46 +00:00
# include <asio/ssl.hpp>
2015-09-20 13:06:00 +00:00
# endif
2014-07-08 09:31:52 +00:00
# include <cstdint>
2014-05-02 05:17:49 +00:00
# include <atomic>
2014-08-07 16:14:27 +00:00
# include <future>
2014-10-23 17:45:34 +00:00
# include <vector>
2014-07-29 11:20:50 +00:00
# include <memory>
2021-07-03 20:02:32 +00:00
# include "crow/version.h"
2016-09-21 14:11:06 +00:00
# include "crow/http_connection.h"
# include "crow/logging.h"
2021-11-19 17:42:25 +00:00
# include "crow/task_timer.h"
2014-03-31 16:51:50 +00:00
2014-04-26 17:19:59 +00:00
namespace crow
2014-03-31 16:51:50 +00:00
{
2022-06-06 14:38:46 +00:00
using tcp = asio : : ip : : tcp ;
2016-03-14 18:43:45 +00:00
2021-11-25 11:45:38 +00:00
template < typename Handler , typename Adaptor = SocketAdaptor , typename . . . Middlewares >
2014-03-31 16:51:50 +00:00
class Server
{
public :
2021-11-25 11:45:38 +00:00
Server ( Handler * handler , std : : string bindaddr , uint16_t port , std : : string server_name = std : : string ( " Crow/ " ) + VERSION , std : : tuple < Middlewares . . . > * middlewares = nullptr , uint16_t concurrency = 1 , uint8_t timeout = 5 , typename Adaptor : : context * adaptor_ctx = nullptr ) :
2022-06-06 14:38:46 +00:00
acceptor_ ( io_service_ , tcp : : endpoint ( asio : : ip : : address : : from_string ( bindaddr ) , port ) ) ,
2021-12-08 02:21:03 +00:00
signals_ ( io_service_ ) ,
2021-11-25 11:45:38 +00:00
tick_timer_ ( io_service_ ) ,
handler_ ( handler ) ,
2021-12-20 08:04:55 +00:00
concurrency_ ( concurrency ) ,
2021-11-25 11:45:38 +00:00
timeout_ ( timeout ) ,
server_name_ ( server_name ) ,
port_ ( port ) ,
bindaddr_ ( bindaddr ) ,
2021-12-23 02:39:39 +00:00
task_queue_length_pool_ ( concurrency_ - 1 ) ,
2021-11-25 11:45:38 +00:00
middlewares_ ( middlewares ) ,
adaptor_ctx_ ( adaptor_ctx )
2021-11-27 12:28:50 +00:00
{ }
2014-03-31 16:51:50 +00:00
2016-08-29 12:13:24 +00:00
void set_tick_function ( std : : chrono : : milliseconds d , std : : function < void ( ) > f )
{
tick_interval_ = d ;
tick_function_ = f ;
}
void on_tick ( )
{
tick_function_ ( ) ;
2022-06-06 14:34:41 +00:00
tick_timer_ . expires_after ( std : : chrono : : milliseconds ( tick_interval_ . count ( ) ) ) ;
2022-06-07 00:43:53 +00:00
tick_timer_ . async_wait ( [ this ] ( const asio : : error_code & ec ) {
2021-11-27 17:44:51 +00:00
if ( ec )
return ;
on_tick ( ) ;
} ) ;
2016-08-29 12:13:24 +00:00
}
2014-03-31 16:51:50 +00:00
void run ( )
{
2021-12-23 02:39:39 +00:00
uint16_t worker_thread_count = concurrency_ - 1 ;
for ( int i = 0 ; i < worker_thread_count ; i + + )
2022-06-06 14:38:46 +00:00
io_service_pool_ . emplace_back ( new asio : : io_service ( ) ) ;
2021-12-23 02:39:39 +00:00
get_cached_date_str_pool_ . resize ( worker_thread_count ) ;
task_timer_pool_ . resize ( worker_thread_count ) ;
2014-08-17 09:35:21 +00:00
2014-04-17 06:50:28 +00:00
std : : vector < std : : future < void > > v ;
2016-09-06 00:39:13 +00:00
std : : atomic < int > init_count ( 0 ) ;
2021-12-23 02:39:39 +00:00
for ( uint16_t i = 0 ; i < worker_thread_count ; i + + )
2014-04-17 06:50:28 +00:00
v . push_back (
2021-11-25 11:45:38 +00:00
std : : async (
2021-11-27 12:28:50 +00:00
std : : launch : : async , [ this , i , & init_count ] {
2021-11-25 11:45:38 +00:00
// thread local date string get function
auto last = std : : chrono : : steady_clock : : now ( ) ;
2015-02-20 04:44:46 +00:00
2021-11-25 11:45:38 +00:00
std : : string date_str ;
2021-11-27 17:44:51 +00:00
auto update_date_str = [ & ] {
2021-11-25 11:45:38 +00:00
auto last_time_t = time ( 0 ) ;
tm my_tm ;
2015-02-20 04:44:46 +00:00
2021-02-08 16:25:02 +00:00
# if defined(_MSC_VER) || defined(__MINGW32__)
2021-11-25 11:45:38 +00:00
gmtime_s ( & my_tm , & last_time_t ) ;
2015-02-20 04:44:46 +00:00
# else
2021-11-25 11:45:38 +00:00
gmtime_r ( & last_time_t , & my_tm ) ;
2015-02-20 04:44:46 +00:00
# endif
2021-11-25 11:45:38 +00:00
date_str . resize ( 100 ) ;
size_t date_str_sz = strftime ( & date_str [ 0 ] , 99 , " %a, %d %b %Y %H:%M:%S GMT " , & my_tm ) ;
date_str . resize ( date_str_sz ) ;
} ;
update_date_str ( ) ;
2021-11-27 17:44:51 +00:00
get_cached_date_str_pool_ [ i ] = [ & ] ( ) - > std : : string {
2021-11-25 11:45:38 +00:00
if ( std : : chrono : : steady_clock : : now ( ) - last > = std : : chrono : : seconds ( 1 ) )
2015-02-20 04:44:46 +00:00
{
2021-11-25 11:45:38 +00:00
last = std : : chrono : : steady_clock : : now ( ) ;
update_date_str ( ) ;
}
return date_str ;
} ;
2015-02-20 04:44:46 +00:00
2021-11-25 11:45:38 +00:00
// initializing task timers
detail : : task_timer task_timer ( * io_service_pool_ [ i ] ) ;
task_timer . set_default_timeout ( timeout_ ) ;
task_timer_pool_ [ i ] = & task_timer ;
2021-12-03 16:40:10 +00:00
task_queue_length_pool_ [ i ] = 0 ;
2014-09-06 19:30:53 +00:00
2021-11-27 17:44:51 +00:00
init_count + + ;
while ( 1 )
2021-11-25 11:45:38 +00:00
{
try
2016-08-28 05:46:31 +00:00
{
2021-11-25 11:45:38 +00:00
if ( io_service_pool_ [ i ] - > run ( ) = = 0 )
2017-09-17 03:44:15 +00:00
{
2021-11-25 11:45:38 +00:00
// when io_service.run returns 0, there are no more works to do.
break ;
2017-09-17 03:44:15 +00:00
}
2016-08-28 05:46:31 +00:00
}
2021-11-27 17:44:51 +00:00
catch ( std : : exception & e )
2021-11-25 11:45:38 +00:00
{
CROW_LOG_ERROR < < " Worker Crash: An uncaught exception occurred: " < < e . what ( ) ;
2016-08-28 05:46:31 +00:00
}
2021-11-27 17:44:51 +00:00
}
} ) ) ;
2016-08-29 12:13:24 +00:00
2021-07-03 20:02:32 +00:00
if ( tick_function_ & & tick_interval_ . count ( ) > 0 )
2016-08-29 12:13:24 +00:00
{
2022-06-06 14:34:41 +00:00
tick_timer_ . expires_after ( std : : chrono : : milliseconds ( tick_interval_ . count ( ) ) ) ;
2021-11-25 11:45:38 +00:00
tick_timer_ . async_wait (
2022-06-07 00:43:53 +00:00
[ this ] ( const asio : : error_code & ec ) {
2021-11-25 11:45:38 +00:00
if ( ec )
return ;
on_tick ( ) ;
} ) ;
2016-08-29 12:13:24 +00:00
}
2021-11-14 12:04:19 +00:00
port_ = acceptor_ . local_endpoint ( ) . port ( ) ;
handler_ - > port ( port_ ) ;
2021-11-22 13:03:55 +00:00
2021-12-23 02:39:39 +00:00
CROW_LOG_INFO < < server_name_ < < " server is running at " < < ( handler_ - > ssl_used ( ) ? " https:// " : " http:// " ) < < bindaddr_ < < " : " < < acceptor_ . local_endpoint ( ) . port ( ) < < " using " < < concurrency_ < < " threads " ;
2017-09-17 17:58:53 +00:00
CROW_LOG_INFO < < " Call `app.loglevel(crow::LogLevel::Warning)` to hide Info level logs. " ;
2014-05-20 16:17:56 +00:00
2014-05-02 05:17:49 +00:00
signals_ . async_wait (
2022-06-07 00:43:53 +00:00
[ & ] ( const asio : : error_code & /*error*/ , int /*signal_number*/ ) {
2021-11-25 11:45:38 +00:00
stop ( ) ;
} ) ;
2014-08-17 09:35:21 +00:00
2021-12-23 02:39:39 +00:00
while ( worker_thread_count ! = init_count )
2016-09-06 00:39:13 +00:00
std : : this_thread : : yield ( ) ;
2015-02-20 04:44:46 +00:00
2014-08-17 09:35:21 +00:00
do_accept ( ) ;
2021-11-25 11:45:38 +00:00
std : : thread (
2021-11-27 12:28:50 +00:00
[ this ] {
2022-06-13 08:53:27 +00:00
notify_start ( ) ;
2021-11-25 11:45:38 +00:00
io_service_ . run ( ) ;
CROW_LOG_INFO < < " Exiting. " ;
} )
. join ( ) ;
2014-03-31 16:51:50 +00:00
}
2014-04-15 13:08:23 +00:00
void stop ( )
{
2022-05-20 11:14:55 +00:00
shutting_down_ = true ; // Prevent the acceptor from taking new connections
2022-05-18 09:28:51 +00:00
for ( auto & io_service : io_service_pool_ )
2021-11-06 03:06:18 +00:00
{
if ( io_service ! = nullptr )
{
2022-05-18 09:28:51 +00:00
CROW_LOG_INFO < < " Closing IO service " < < & io_service ;
2022-05-20 11:14:55 +00:00
io_service - > stop ( ) ; // Close all io_services (and HTTP connections)
2021-11-06 03:06:18 +00:00
}
}
CROW_LOG_INFO < < " Closing main IO service ( " < < & io_service_ < < ' ) ' ;
2022-05-20 11:14:55 +00:00
io_service_ . stop ( ) ; // Close main io_service
2014-04-15 13:08:23 +00:00
}
2022-06-13 08:53:27 +00:00
/// Wait until the server has properly started
void wait_for_start ( )
{
std : : unique_lock < std : : mutex > lock ( start_mutex_ ) ;
if ( ! server_started_ )
cv_started_ . wait ( lock ) ;
}
2020-12-19 07:15:57 +00:00
void signal_clear ( )
{
signals_ . clear ( ) ;
}
void signal_add ( int signal_number )
{
signals_ . add ( signal_number ) ;
}
2014-03-31 16:51:50 +00:00
private :
2021-12-07 10:06:18 +00:00
uint16_t pick_io_service_idx ( )
2014-08-17 09:35:21 +00:00
{
2021-12-07 10:06:18 +00:00
uint16_t min_queue_idx = 0 ;
2021-11-29 15:56:12 +00:00
// TODO improve load balancing
2022-04-22 18:12:22 +00:00
// size_t is used here to avoid the security issue https://codeql.github.com/codeql-query-help/cpp/cpp-comparison-with-wider-type/
// even though the max value of this can be only uint16_t as concurrency is uint16_t.
for ( size_t i = 1 ; i < task_queue_length_pool_ . size ( ) & & task_queue_length_pool_ [ min_queue_idx ] > 0 ; i + + )
2021-12-07 10:06:18 +00:00
// No need to check other io_services if the current one has no tasks
2021-11-29 15:56:12 +00:00
{
if ( task_queue_length_pool_ [ i ] < task_queue_length_pool_ [ min_queue_idx ] )
min_queue_idx = i ;
}
return min_queue_idx ;
2014-08-17 09:35:21 +00:00
}
2014-03-31 16:51:50 +00:00
void do_accept ( )
{
2022-05-18 11:08:40 +00:00
if ( ! shutting_down_ )
2022-05-18 10:49:50 +00:00
{
uint16_t service_idx = pick_io_service_idx ( ) ;
2022-06-06 14:38:46 +00:00
asio : : io_service & is = * io_service_pool_ [ service_idx ] ;
2022-05-18 10:49:50 +00:00
task_queue_length_pool_ [ service_idx ] + + ;
CROW_LOG_DEBUG < < & is < < " { " < < service_idx < < " } queue length: " < < task_queue_length_pool_ [ service_idx ] ;
auto p = new Connection < Adaptor , Handler , Middlewares . . . > (
is , handler_ , server_name_ , middlewares_ ,
get_cached_date_str_pool_ [ service_idx ] , * task_timer_pool_ [ service_idx ] , adaptor_ctx_ , task_queue_length_pool_ [ service_idx ] ) ;
acceptor_ . async_accept (
p - > socket ( ) ,
2022-06-07 00:43:53 +00:00
[ this , p , & is , service_idx ] ( asio : : error_code ec ) {
2022-05-18 10:49:50 +00:00
if ( ! ec )
{
is . post (
[ p ] {
p - > start ( ) ;
} ) ;
}
else
{
task_queue_length_pool_ [ service_idx ] - - ;
CROW_LOG_DEBUG < < & is < < " { " < < service_idx < < " } queue length: " < < task_queue_length_pool_ [ service_idx ] ;
delete p ;
}
do_accept ( ) ;
} ) ;
}
2014-03-31 16:51:50 +00:00
}
2022-06-13 08:53:27 +00:00
/// Notify anything using `wait_for_start()` to proceed
void notify_start ( )
{
std : : unique_lock < std : : mutex > lock ( start_mutex_ ) ;
server_started_ = true ;
cv_started_ . notify_all ( ) ;
}
2014-03-31 16:51:50 +00:00
private :
2022-06-06 14:38:46 +00:00
asio : : io_service io_service_ ;
std : : vector < std : : unique_ptr < asio : : io_service > > io_service_pool_ ;
2021-11-19 17:42:25 +00:00
std : : vector < detail : : task_timer * > task_timer_pool_ ;
2015-02-20 04:44:46 +00:00
std : : vector < std : : function < std : : string ( ) > > get_cached_date_str_pool_ ;
2014-03-31 16:51:50 +00:00
tcp : : acceptor acceptor_ ;
2022-05-18 11:08:40 +00:00
bool shutting_down_ = false ;
2022-06-13 08:53:27 +00:00
bool server_started_ { false } ;
std : : condition_variable cv_started_ ;
std : : mutex start_mutex_ ;
2022-06-06 14:38:46 +00:00
asio : : signal_set signals_ ;
2022-06-06 14:34:41 +00:00
2022-06-06 14:38:46 +00:00
asio : : basic_waitable_timer < std : : chrono : : high_resolution_clock > tick_timer_ ;
2014-05-02 05:17:49 +00:00
2014-03-31 16:51:50 +00:00
Handler * handler_ ;
2021-12-23 02:39:39 +00:00
uint16_t concurrency_ { 2 } ;
2021-11-19 17:42:25 +00:00
std : : uint8_t timeout_ ;
2020-12-03 01:15:05 +00:00
std : : string server_name_ ;
2014-05-20 16:17:56 +00:00
uint16_t port_ ;
2016-03-14 18:43:45 +00:00
std : : string bindaddr_ ;
2021-11-30 18:54:37 +00:00
std : : vector < std : : atomic < unsigned int > > task_queue_length_pool_ ;
2014-09-06 19:30:53 +00:00
2016-08-29 12:13:24 +00:00
std : : chrono : : milliseconds tick_interval_ ;
std : : function < void ( ) > tick_function_ ;
2014-10-23 17:33:03 +00:00
std : : tuple < Middlewares . . . > * middlewares_ ;
2015-09-20 13:06:00 +00:00
typename Adaptor : : context * adaptor_ctx_ ;
2014-03-31 16:51:50 +00:00
} ;
2021-11-25 11:45:38 +00:00
} // namespace crow