@ -23,6 +23,7 @@
# include "common/Log.h"
# include <array>
# include <chrono>
# include <memory>
# include <limits>
# include <utility>
@ -74,10 +75,13 @@ namespace restapi
m_socket ( std : : move ( socket ) , context ) ,
m_connectionManager ( manager ) ,
m_requestHandler ( handler ) ,
m_deadline ( m_socket . get_executor ( ) ) ,
m_lexer ( HTTPLexer ( false ) ) ,
m_continue ( false ) ,
m_contResult ( HTTPLexer : : INDETERMINATE ) ,
m_headerBytes ( 0U ) ,
m_stopped ( false ) ,
m_waitingForHandler ( false ) ,
m_persistent ( persistent ) ,
m_debug ( debug )
{
@ -87,12 +91,20 @@ namespace restapi
/**
* @ brief Start the first asynchronous operation for the connection .
*/
void start ( ) { handshake ( ) ; }
void start ( )
{
armDeadline ( HANDSHAKE_TIMEOUT_SECONDS , " TLS handshake " ) ;
handshake ( ) ;
}
/**
* @ brief Stop all asynchronous operations associated with the connection .
*/
void stop ( )
{
m_stopped = true ;
m_waitingForHandler = false ;
asio : : error_code ignoredError ;
m_deadline . cancel ( ignoredError ) ;
try
{
if ( m_socket . lowest_layer ( ) . is_open ( ) ) {
@ -112,7 +124,14 @@ namespace restapi
m_socket . async_handshake ( asio : : ssl : : stream_base : : server , [ this , self ] ( asio : : error_code ec ) {
if ( ! ec ) {
armDeadline ( REQUEST_TIMEOUT_SECONDS , " request " ) ;
read ( ) ;
return ;
}
if ( ec ! = asio : : error : : operation_aborted ) {
: : LogError ( LOG_REST , " SecureServerConnection::handshake(), %s, code = %u " , ec . message ( ) . c_str ( ) , ec . value ( ) ) ;
m_connectionManager . stop ( self ) ;
}
} ) ;
}
@ -195,13 +214,23 @@ namespace restapi
m_continue = false ;
m_contResult = HTTPLexer : : INDETERMINATE ;
m_headerBytes = 0U ;
m_requestHandler . handleRequest ( m_request , m_reply ) ;
if ( m_debug ) {
Utils : : dump ( 1U , " SecureServerConnection::read(), HTTPS Reply Content " , ( uint8_t * ) m_reply . content . c_str ( ) , m_reply . content . length ( ) ) ;
}
write ( ) ;
armDeadline ( HANDLER_TIMEOUT_SECONDS , " handler " ) ;
m_waitingForHandler = true ;
dispatchRequest ( m_requestHandler , m_request ,
[ this , self ] ( HTTPPayload reply ) mutable {
asio : : post ( m_socket . get_executor ( ) , [ this , self , reply ] ( ) mutable {
if ( m_stopped | | ! m_waitingForHandler | | ! m_socket . lowest_layer ( ) . is_open ( ) )
return ;
m_waitingForHandler = false ;
cancelDeadline ( ) ;
m_reply = std : : move ( reply ) ;
if ( m_debug ) {
Utils : : dump ( 1U , " SecureServerConnection::read(), HTTPS Reply Content " , ( uint8_t * ) m_reply . content . c_str ( ) , m_reply . content . length ( ) ) ;
}
write ( ) ;
} ) ;
} , 0 ) ;
}
else if ( result = = HTTPLexer : : BAD ) {
m_continue = false ;
@ -242,12 +271,15 @@ namespace restapi
{
auto self = this - > shared_from_this ( ) ;
armDeadline ( WRITE_TIMEOUT_SECONDS , " write " ) ;
if ( m_persistent ) {
m_reply . headers . add ( " Connection " , " keep-alive " ) ;
}
auto buffers = m_reply . toBuffers ( ) ;
asio : : async_write ( m_socket , buffers , [ this , self ] ( asio : : error_code ec , std : : size_t ) {
cancelDeadline ( ) ;
if ( m_persistent ) {
m_lexer . reset ( ) ;
m_reply . headers = HTTPHeaders ( ) ;
@ -255,6 +287,7 @@ namespace restapi
m_reply . content = " " ;
m_request = HTTPPayload ( ) ;
m_headerBytes = 0U ;
armDeadline ( REQUEST_TIMEOUT_SECONDS , " request " ) ;
read ( ) ;
}
else {
@ -278,6 +311,72 @@ namespace restapi
} ) ;
}
/**
* @ brief Dispatches through an asynchronous request - handler interface when available .
* @ tparam Handler Request - handler type .
* @ tparam CompletionHandler Completion - handler type .
* @ param handler Request handler .
* @ param request HTTP request .
* @ param completionHandler Handler invoked with the completed reply .
* @ param priority Overload priority selector .
*/
template < typename Handler , typename CompletionHandler >
static auto dispatchRequest ( Handler & handler , const HTTPPayload & request , CompletionHandler completionHandler , int priority )
- > decltype ( handler . handleRequestAsync ( request , completionHandler ) , void ( ) )
{
( void ) priority ;
handler . handleRequestAsync ( request , completionHandler ) ;
}
/**
* @ brief Dispatches through the legacy synchronous request - handler interface .
* @ tparam Handler Request - handler type .
* @ tparam CompletionHandler Completion - handler type .
* @ param handler Request handler .
* @ param request HTTP request .
* @ param completionHandler Handler invoked with the completed reply .
*/
template < typename Handler , typename CompletionHandler >
static void dispatchRequest ( Handler & handler , const HTTPPayload & request , CompletionHandler completionHandler , long )
{
HTTPPayload reply ;
handler . handleRequest ( request , reply ) ;
completionHandler ( std : : move ( reply ) ) ;
}
/**
* @ brief Arms the connection deadline for the current operation .
* @ param timeoutSeconds Deadline duration in seconds .
* @ param operation Operation name used for diagnostics .
*/
void armDeadline ( uint32_t timeoutSeconds , const char * operation )
{
auto self = this - > shared_from_this ( ) ;
m_deadline . expires_from_now ( std : : chrono : : seconds ( timeoutSeconds ) ) ;
m_deadline . async_wait ( [ this , self , operation ] ( asio : : error_code ec ) {
if ( ! ec & & ! m_stopped ) {
: : LogError ( LOG_REST , " HTTPS connection timed out during %s " , operation ) ;
if ( m_waitingForHandler ) {
m_waitingForHandler = false ;
m_reply = HTTPPayload : : statusPayload ( HTTPPayload : : SERVICE_UNAVAILABLE , " application/json " ) ;
write ( ) ;
}
else {
m_connectionManager . stop ( self ) ;
}
}
} ) ;
}
/**
* @ brief Cancels the active connection deadline .
*/
void cancelDeadline ( )
{
asio : : error_code ignoredError ;
m_deadline . cancel ( ignoredError ) ;
}
/**
* @ brief Parses the Content - Length header value .
* @ param value The string value of the Content - Length header .
@ -310,6 +409,7 @@ namespace restapi
ConnectionManagerType & m_connectionManager ;
RequestHandlerType & m_requestHandler ;
asio : : steady_timer m_deadline ;
std : : array < char , 8192 > m_buffer ;
@ -320,9 +420,15 @@ namespace restapi
bool m_continue ;
HTTPLexer : : ResultType m_contResult ;
size_t m_headerBytes ;
bool m_stopped ;
bool m_waitingForHandler ;
static constexpr size_t MAX_HTTP_HEADER_LENGTH = 32768U ;
static constexpr size_t MAX_HTTP_CONTENT_LENGTH = 1048576U ;
static constexpr uint32_t HANDSHAKE_TIMEOUT_SECONDS = 15U ;
static constexpr uint32_t REQUEST_TIMEOUT_SECONDS = 30U ;
static constexpr uint32_t HANDLER_TIMEOUT_SECONDS = 60U ;
static constexpr uint32_t WRITE_TIMEOUT_SECONDS = 30U ;
bool m_persistent ;
bool m_debug ;