Rewrote the server in cpp with the frontend in svelte
This commit is contained in:
321
lib/restbed-4.8/source/corvusoft/restbed/detail/http_impl.cpp
Normal file
321
lib/restbed-4.8/source/corvusoft/restbed/detail/http_impl.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
//System Includes
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
#include <clocale>
|
||||
#include <ciso646>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/uri.hpp"
|
||||
#include "corvusoft/restbed/string.hpp"
|
||||
#include "corvusoft/restbed/request.hpp"
|
||||
#include "corvusoft/restbed/response.hpp"
|
||||
#include "corvusoft/restbed/settings.hpp"
|
||||
#include "corvusoft/restbed/ssl_settings.hpp"
|
||||
#include "corvusoft/restbed/detail/http_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/socket_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/request_impl.hpp"
|
||||
|
||||
#ifdef BUILD_IPC
|
||||
#include "corvusoft/restbed/detail/ipc_socket_impl.hpp"
|
||||
#endif
|
||||
|
||||
//External Includes
|
||||
#include <asio/error.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/buffer.hpp>
|
||||
#include <asio/streambuf.hpp>
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
#include <asio/ssl.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_IPC
|
||||
#include <asio/local/stream_protocol.hpp>
|
||||
#endif
|
||||
|
||||
//System Namespaces
|
||||
using std::free;
|
||||
using std::bind;
|
||||
using std::stod;
|
||||
using std::regex;
|
||||
using std::smatch;
|
||||
using std::string;
|
||||
using std::istream;
|
||||
using std::function;
|
||||
using std::multimap;
|
||||
using std::setlocale;
|
||||
using std::to_string;
|
||||
using std::shared_ptr;
|
||||
using std::error_code;
|
||||
using std::make_shared;
|
||||
using std::placeholders::_1;
|
||||
using std::placeholders::_2;
|
||||
|
||||
#ifdef BUILD_IPC
|
||||
using asio::local::stream_protocol;
|
||||
#endif
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
using asio::buffer;
|
||||
using asio::ip::tcp;
|
||||
using asio::streambuf;
|
||||
using asio::io_service;
|
||||
#ifdef BUILD_SSL
|
||||
using asio::ssl::stream;
|
||||
#endif
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
Bytes HttpImpl::to_bytes( const shared_ptr< Request >& request )
|
||||
{
|
||||
auto path = request->get_path( );
|
||||
|
||||
auto parameters = request->get_query_parameters( );
|
||||
|
||||
if ( not parameters.empty( ) )
|
||||
{
|
||||
string query = String::empty;
|
||||
|
||||
for ( const auto parameter : parameters )
|
||||
{
|
||||
query += Uri::encode_parameter( parameter.first ) + "=" + Uri::encode_parameter( parameter.second ) + "&";
|
||||
}
|
||||
|
||||
path += "?" + query.substr( 0, query.length( ) - 1 );
|
||||
}
|
||||
|
||||
auto uri = request->m_pimpl->m_uri;
|
||||
|
||||
if ( uri not_eq nullptr and not uri->get_fragment( ).empty( ) )
|
||||
{
|
||||
path += "#" + uri->get_fragment( );
|
||||
}
|
||||
|
||||
char* locale = nullptr;
|
||||
if (auto current_locale = setlocale( LC_NUMERIC, nullptr ) )
|
||||
{
|
||||
locale = strdup(current_locale);
|
||||
setlocale( LC_NUMERIC, "C" );
|
||||
}
|
||||
|
||||
auto data = String::format( "%s %s %s/%.1f\r\n",
|
||||
request->get_method( ).data( ),
|
||||
path.data( ),
|
||||
"HTTP",
|
||||
request->get_version( ) );
|
||||
|
||||
if (locale)
|
||||
{
|
||||
setlocale( LC_NUMERIC, locale );
|
||||
free( locale );
|
||||
}
|
||||
|
||||
auto headers = request->get_headers( );
|
||||
|
||||
if ( not headers.empty( ) )
|
||||
{
|
||||
data += String::join( headers, ": ", "\r\n" ) + "\r\n";
|
||||
}
|
||||
|
||||
data += "\r\n";
|
||||
|
||||
auto bytes = String::to_bytes( data );
|
||||
auto body = request->get_body( );
|
||||
|
||||
if ( not body.empty( ) )
|
||||
{
|
||||
bytes.insert( bytes.end( ), body.begin( ), body.end( ) );
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void HttpImpl::socket_setup( const shared_ptr< Request >& request, const shared_ptr< const Settings >& settings )
|
||||
{
|
||||
if ( request->m_pimpl->m_socket == nullptr )
|
||||
{
|
||||
if ( request->m_pimpl->m_io_service == nullptr )
|
||||
{
|
||||
request->m_pimpl->m_io_service = make_shared< asio::io_service >( );
|
||||
}
|
||||
|
||||
if ( String::uppercase( request->m_pimpl->m_protocol ) == "HTTP" )
|
||||
{
|
||||
auto socket = make_shared< tcp::socket >( *request->m_pimpl->m_io_service );
|
||||
request->m_pimpl->m_socket = make_shared< SocketImpl >( *request->m_pimpl->m_io_service, socket );
|
||||
}
|
||||
#ifdef BUILD_SSL
|
||||
else if ( String::uppercase( request->m_pimpl->m_protocol ) == "HTTPS" )
|
||||
{
|
||||
ssl_socket_setup( request, settings->get_ssl_settings( ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_IPC
|
||||
else if ( String::uppercase( request->m_pimpl->m_protocol ) == "LOCAL" )
|
||||
{
|
||||
auto socket = make_shared< stream_protocol::socket >( *request->m_pimpl->m_io_service );
|
||||
request->m_pimpl->m_socket = make_shared< IPCSocketImpl >( *request->m_pimpl->m_io_service, socket );
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
auto socket = make_shared< tcp::socket >( *request->m_pimpl->m_io_service );
|
||||
request->m_pimpl->m_socket = make_shared< SocketImpl >( *request->m_pimpl->m_io_service, socket );
|
||||
}
|
||||
}
|
||||
|
||||
request->m_pimpl->m_socket->set_timeout( settings->get_connection_timeout( ) );
|
||||
}
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
void HttpImpl::ssl_socket_setup( const shared_ptr< Request >& request, const shared_ptr< const SSLSettings >& settings )
|
||||
{
|
||||
asio::ssl::context context( asio::ssl::context::sslv23 );
|
||||
shared_ptr< asio::ssl::stream< asio::ip::tcp::socket > > socket = nullptr;
|
||||
|
||||
if ( settings not_eq nullptr )
|
||||
{
|
||||
const auto pool = settings->get_certificate_authority_pool( );
|
||||
|
||||
if ( pool.empty( ) )
|
||||
{
|
||||
context.set_default_verify_paths( );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _WIN32
|
||||
context.load_verify_file(settings->get_certificate_authority_pool());
|
||||
#else
|
||||
context.add_verify_path( settings->get_certificate_authority_pool( ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
socket = make_shared< asio::ssl::stream< asio::ip::tcp::socket > >( *request->m_pimpl->m_io_service, context );
|
||||
socket->set_verify_mode( asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert );
|
||||
}
|
||||
else
|
||||
{
|
||||
socket = make_shared< asio::ssl::stream< asio::ip::tcp::socket > >( *request->m_pimpl->m_io_service, context );
|
||||
socket->set_verify_mode( asio::ssl::verify_none );
|
||||
}
|
||||
|
||||
socket->set_verify_callback( asio::ssl::rfc2818_verification( request->get_host( ) ) );
|
||||
request->m_pimpl->m_socket = make_shared< SocketImpl >( *request->m_pimpl->m_io_service, socket );
|
||||
}
|
||||
#endif
|
||||
void HttpImpl::request_handler( const error_code& error, const shared_ptr< Request >& request, const function< void ( const shared_ptr< Request >, const shared_ptr< Response > ) >& callback )
|
||||
{
|
||||
if ( error )
|
||||
{
|
||||
const auto body = String::format( "Failed to locate HTTP endpoint: %s", error.message( ).data( ) );
|
||||
return callback( request, create_error_response( request, body ) );
|
||||
}
|
||||
|
||||
request->m_pimpl->m_socket->start_write( to_bytes( request ), bind( write_handler, _1, _2, request, callback ) );
|
||||
}
|
||||
|
||||
void HttpImpl::write_handler( const error_code& error, const size_t, const shared_ptr< Request >& request, const function< void ( const shared_ptr< Request >, const shared_ptr< Response > ) >& callback )
|
||||
{
|
||||
if ( error )
|
||||
{
|
||||
const auto body = String::format( "Socket write failed: %s", error.message( ).data( ) );
|
||||
return callback( request, create_error_response( request, body ) );
|
||||
}
|
||||
|
||||
request->m_pimpl->m_buffer = make_shared< asio::streambuf >( );
|
||||
request->m_pimpl->m_socket->start_read( request->m_pimpl->m_buffer, "\r\n", bind( read_status_handler, _1, _2, request, callback ) );
|
||||
}
|
||||
|
||||
const shared_ptr< Response > HttpImpl::create_error_response( const shared_ptr< Request >& request, const string& message )
|
||||
{
|
||||
auto response = request->m_pimpl->m_response;
|
||||
response->set_protocol( request->get_protocol( ) );
|
||||
response->set_version( request->get_version( ) );
|
||||
response->set_status_code( 0 );
|
||||
response->set_status_message( "Error" );
|
||||
response->set_header( "Content-Type", "text/plain; utf-8" );
|
||||
response->set_header( "Content-Length", ::to_string( message.length( ) ) );
|
||||
response->set_body( message );
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void HttpImpl::read_status_handler( const error_code& error, const size_t, const shared_ptr< Request >& request, const function< void ( const shared_ptr< Request >, const shared_ptr< Response > ) >& callback )
|
||||
{
|
||||
if ( error )
|
||||
{
|
||||
const auto body = String::format( "Failed to read HTTP response status line: %s", error.message( ).data( ) );
|
||||
return callback( request, create_error_response( request, body ) );
|
||||
}
|
||||
|
||||
istream response_stream( request->m_pimpl->m_buffer.get( ) );
|
||||
string status_line = String::empty;
|
||||
getline( response_stream, status_line );
|
||||
|
||||
smatch matches;
|
||||
static const regex status_line_pattern( "^([a-zA-Z]+)\\/(\\d*\\.?\\d*) (-?\\d+) (.*)\\r$" );
|
||||
|
||||
if ( not regex_match( status_line, matches, status_line_pattern ) or matches.size( ) not_eq 5 )
|
||||
{
|
||||
const auto body = String::format( "HTTP response status line malformed: '%s'", status_line.data( ) );
|
||||
return callback( request, create_error_response( request, body ) );
|
||||
}
|
||||
|
||||
auto response = request->m_pimpl->m_response;
|
||||
response->set_protocol( matches[ 1 ].str( ) );
|
||||
response->set_version( stod( matches[ 2 ].str( ) ) );
|
||||
response->set_status_code( stoi( matches[ 3 ].str( ) ) );
|
||||
response->set_status_message( matches[ 4 ].str( ) );
|
||||
|
||||
request->m_pimpl->m_socket->start_read( request->m_pimpl->m_buffer, "\r\n\r\n", bind( read_headers_handler, _1, _2, request, callback ) );
|
||||
}
|
||||
|
||||
void HttpImpl::read_headers_handler( const error_code& error, const size_t, const shared_ptr< Request >& request, const function< void ( const shared_ptr< Request >, const shared_ptr< Response > ) >& callback )
|
||||
{
|
||||
if ( error == asio::error::eof )
|
||||
{
|
||||
return callback( request, request->m_pimpl->m_response );
|
||||
}
|
||||
|
||||
if ( error )
|
||||
{
|
||||
const auto body = String::format( "Failed to read HTTP response status headers: '%s'", error.message( ).data( ) );
|
||||
return callback( request, create_error_response( request, body ) );
|
||||
}
|
||||
|
||||
string header = String::empty;
|
||||
multimap< string, string > headers = { };
|
||||
istream response_stream( request->m_pimpl->m_buffer.get( ) );
|
||||
|
||||
while ( getline( response_stream, header ) and header not_eq "\r" )
|
||||
{
|
||||
static const regex header_pattern( "^([^:.]*): *(.*)\\s*$" );
|
||||
smatch matches;
|
||||
|
||||
if ( not regex_match( header, matches, header_pattern ) or matches.size( ) not_eq 3 )
|
||||
{
|
||||
const auto body = String::format( "Malformed HTTP response header: '%s'", header.data( ) );
|
||||
return callback( request, create_error_response( request, body ) );
|
||||
}
|
||||
|
||||
headers.insert( make_pair( matches[ 1 ], matches[ 2 ] ) );
|
||||
}
|
||||
|
||||
auto response = request->m_pimpl->m_response;
|
||||
response->set_headers( headers );
|
||||
|
||||
callback( request, response );
|
||||
}
|
||||
}
|
||||
}
|
||||
110
lib/restbed-4.8/source/corvusoft/restbed/detail/http_impl.hpp
Normal file
110
lib/restbed-4.8/source/corvusoft/restbed/detail/http_impl.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <system_error>
|
||||
|
||||
//Project Includes
|
||||
#include <corvusoft/restbed/byte.hpp>
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Fordward Declarations
|
||||
class Request;
|
||||
class Response;
|
||||
class Settings;
|
||||
class SSLSettings;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
class HttpImpl
|
||||
{
|
||||
public:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
|
||||
//Functionality
|
||||
static Bytes to_bytes( const std::shared_ptr< Request >& value );
|
||||
|
||||
static void socket_setup( const std::shared_ptr< Request >& request, const std::shared_ptr< const Settings >& settings );
|
||||
#ifdef BUILD_SSL
|
||||
static void ssl_socket_setup( const std::shared_ptr< Request >& request, const std::shared_ptr< const SSLSettings >& settings );
|
||||
#endif
|
||||
static void request_handler( const std::error_code& error, const std::shared_ptr< Request >& request, const std::function< void ( const std::shared_ptr< Request >, const std::shared_ptr< Response > ) >& callback );
|
||||
|
||||
static void write_handler( const std::error_code& error, const std::size_t length, const std::shared_ptr< Request >& request, const std::function< void ( const std::shared_ptr< Request >, const std::shared_ptr< Response > ) >& callback );
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
|
||||
protected:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
|
||||
//Functionality
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
|
||||
private:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
HttpImpl( void ) = delete;
|
||||
|
||||
virtual ~HttpImpl( void ) = delete;
|
||||
|
||||
HttpImpl( const HttpImpl& original ) = delete;
|
||||
|
||||
//Functionality
|
||||
static const std::shared_ptr< Response > create_error_response( const std::shared_ptr< Request >& request, const std::string& message );
|
||||
|
||||
static void read_status_handler( const std::error_code& error, const std::size_t length, const std::shared_ptr< Request >& request, const std::function< void ( const std::shared_ptr< Request >, const std::shared_ptr< Response > ) >& callback );
|
||||
|
||||
static void read_headers_handler( const std::error_code& error, const std::size_t length, const std::shared_ptr< Request >& request, const std::function< void ( const std::shared_ptr< Request >, const std::shared_ptr< Response > ) >& callback );
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
HttpImpl& operator =( const HttpImpl& value ) = delete;
|
||||
|
||||
//Properties
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
#ifdef BUILD_IPC
|
||||
|
||||
//System Includes
|
||||
#include <future>
|
||||
#include <ciso646>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/logger.hpp"
|
||||
#include "corvusoft/restbed/detail/socket_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/ipc_socket_impl.hpp"
|
||||
|
||||
//External Includes
|
||||
#include <asio/read.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <asio/connect.hpp>
|
||||
#include <asio/read_until.hpp>
|
||||
|
||||
//System Namespaces
|
||||
using std::get;
|
||||
using std::bind;
|
||||
using std::size_t;
|
||||
using std::string;
|
||||
using std::promise;
|
||||
using std::function;
|
||||
using std::to_string;
|
||||
using std::error_code;
|
||||
using std::shared_ptr;
|
||||
using std::make_shared;
|
||||
using std::runtime_error;
|
||||
using std::placeholders::_1;
|
||||
using std::chrono::milliseconds;
|
||||
using std::chrono::steady_clock;
|
||||
|
||||
//Project Namespaces
|
||||
using restbed::detail::IPCSocketImpl;
|
||||
|
||||
//External Namespaces
|
||||
using asio::io_service;
|
||||
using asio::steady_timer;
|
||||
using asio::local::stream_protocol;
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
IPCSocketImpl::IPCSocketImpl( asio::io_context& context, const shared_ptr< stream_protocol::socket >& socket, const shared_ptr< Logger >& logger ) : SocketImpl( context ),
|
||||
m_error_handler( nullptr ),
|
||||
m_is_open( socket->is_open( ) ),
|
||||
m_pending_writes( ),
|
||||
m_logger( logger ),
|
||||
m_timeout( 0 ),
|
||||
m_io_service( context ),
|
||||
m_timer( make_shared< asio::steady_timer >( m_io_service ) ),
|
||||
m_strand( make_shared< io_service::strand > ( m_io_service ) ),
|
||||
m_socket( socket )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void IPCSocketImpl::close( void )
|
||||
{
|
||||
m_is_open = false;
|
||||
|
||||
if ( m_timer not_eq nullptr )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
}
|
||||
|
||||
if ( m_socket not_eq nullptr )
|
||||
{
|
||||
m_socket->close( );
|
||||
}
|
||||
}
|
||||
|
||||
bool IPCSocketImpl::is_open( void ) const
|
||||
{
|
||||
return m_is_open;
|
||||
}
|
||||
|
||||
bool IPCSocketImpl::is_closed( void ) const
|
||||
{
|
||||
return not m_is_open;
|
||||
}
|
||||
|
||||
void IPCSocketImpl::connect( const string&, const uint16_t, const function< void ( const error_code& ) >& callback )
|
||||
{
|
||||
m_socket->async_connect( stream_protocol::endpoint("/tmp/restbed.sock"), [ this, callback ]( const error_code & error )
|
||||
{
|
||||
m_is_open = true;
|
||||
callback( error );
|
||||
} );
|
||||
}
|
||||
|
||||
void IPCSocketImpl::sleep_for( const milliseconds& delay, const function< void ( const error_code& ) >& callback )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( delay );
|
||||
m_timer->async_wait( callback );
|
||||
}
|
||||
|
||||
void IPCSocketImpl::start_write(const Bytes& data, const std::function< void ( const std::error_code&, std::size_t ) >& callback)
|
||||
{
|
||||
m_strand->post([this, data, callback] { write_helper(data, callback); });
|
||||
}
|
||||
|
||||
size_t IPCSocketImpl::start_read(const shared_ptr< asio::streambuf >& data, const string& delimiter, error_code& error)
|
||||
{
|
||||
return read( data, delimiter,error );
|
||||
}
|
||||
|
||||
size_t IPCSocketImpl::start_read(const shared_ptr< asio::streambuf >& data, const size_t length, error_code& error)
|
||||
{
|
||||
return read( data, length, error );
|
||||
}
|
||||
|
||||
void IPCSocketImpl::start_read( const std::size_t length, const function< void ( const Bytes ) > success, const function< void ( const error_code ) > failure )
|
||||
{
|
||||
m_strand->post([this, length, success, failure] {
|
||||
read(length, success, failure);
|
||||
});
|
||||
}
|
||||
|
||||
void IPCSocketImpl::start_read(const shared_ptr< asio::streambuf >& data, const size_t length, const function< void ( const error_code&, size_t ) >& callback)
|
||||
{
|
||||
m_strand->post([this, data, length, callback]
|
||||
{
|
||||
read(data, length, callback);
|
||||
});
|
||||
}
|
||||
|
||||
void IPCSocketImpl::start_read(const shared_ptr< asio::streambuf >& data, const string& delimiter, const function< void ( const error_code&, size_t ) >& callback)
|
||||
{
|
||||
m_strand->post([this, data, delimiter, callback]
|
||||
{
|
||||
read(data, delimiter, callback);
|
||||
});
|
||||
}
|
||||
|
||||
string IPCSocketImpl::get_local_endpoint( void )
|
||||
{
|
||||
return "/tmp/restbed.sock";
|
||||
}
|
||||
|
||||
string IPCSocketImpl::get_remote_endpoint( void )
|
||||
{
|
||||
return "/tmp/restbed.sock";
|
||||
}
|
||||
|
||||
void IPCSocketImpl::set_timeout( const milliseconds& value )
|
||||
{
|
||||
m_timeout = value;
|
||||
}
|
||||
|
||||
void IPCSocketImpl::set_keep_alive( const uint32_t, const uint32_t, const uint32_t)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
shared_ptr< IPCSocketImpl > IPCSocketImpl::shared_from_this( void )
|
||||
{
|
||||
return std::dynamic_pointer_cast< IPCSocketImpl >( SocketImpl::shared_from_this( ) );
|
||||
//return shared_ptr< IPCSocketImpl >( this ); //test for circular reference and memory leak.
|
||||
}
|
||||
|
||||
void IPCSocketImpl::connection_timeout_handler( const shared_ptr< IPCSocketImpl > socket, const error_code& error )
|
||||
{
|
||||
if ( error or socket == nullptr or socket->m_timer->expires_at( ) > steady_clock::now( ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
socket->close( );
|
||||
|
||||
if ( m_error_handler not_eq nullptr )
|
||||
{
|
||||
m_error_handler( 408, runtime_error( "The socket timed out waiting for the request." ), nullptr );
|
||||
}
|
||||
}
|
||||
|
||||
void IPCSocketImpl::write( void )
|
||||
{
|
||||
if(m_is_open)
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &IPCSocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
|
||||
asio::async_write( *m_socket, asio::buffer( get<0>(m_pending_writes.front()).data( ), get<0>(m_pending_writes.front()).size( ) ), m_strand->wrap( [ this ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
auto callback = get<2>(m_pending_writes.front());
|
||||
auto & retries = get<1>(m_pending_writes.front());
|
||||
auto & buffer = get<0>(m_pending_writes.front());
|
||||
if(length < buffer.size() && retries < MAX_WRITE_RETRIES && error not_eq asio::error::operation_aborted)
|
||||
{
|
||||
++retries;
|
||||
buffer.erase(buffer.begin(),buffer.begin() + length);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pending_writes.pop();
|
||||
}
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
if(!m_pending_writes.empty())
|
||||
{
|
||||
write();
|
||||
}
|
||||
} ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
while(!m_pending_writes.empty())
|
||||
{
|
||||
m_pending_writes.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IPCSocketImpl::write( const Bytes& data, const function< void ( const error_code&, size_t ) >& callback )
|
||||
{
|
||||
const auto buffer = make_shared< Bytes >( data );
|
||||
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &IPCSocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
|
||||
asio::async_write( *m_socket, asio::buffer( buffer->data( ), buffer->size( ) ), m_strand->wrap( [ this, callback, buffer ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
} ) );
|
||||
}
|
||||
|
||||
void IPCSocketImpl::write_helper(const Bytes& data, const function< void ( const error_code&, size_t ) >& callback)
|
||||
{
|
||||
const uint8_t retries = 0;
|
||||
m_pending_writes.push(make_tuple(data, retries, callback));
|
||||
if(m_pending_writes.size() == 1)
|
||||
{
|
||||
write();
|
||||
}
|
||||
}
|
||||
|
||||
size_t IPCSocketImpl::read( const shared_ptr< asio::streambuf >& data, const size_t length, error_code& error )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &IPCSocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
|
||||
size_t size = 0;
|
||||
auto finished = std::make_shared<bool>(false);
|
||||
auto sharedError = std::make_shared<error_code>();
|
||||
auto sharedSize = std::make_shared<size_t>(0);
|
||||
|
||||
asio::async_read( *m_socket, *data, asio::transfer_at_least( length ),
|
||||
[ finished, sharedSize, sharedError ]( const error_code & error, size_t size ) {
|
||||
*sharedError = error;
|
||||
*sharedSize = size;
|
||||
*finished = true;
|
||||
});
|
||||
|
||||
while (!*finished)
|
||||
m_io_service.run_one();
|
||||
error = *sharedError;
|
||||
size = *sharedSize;
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void IPCSocketImpl::read( const std::size_t length, const function< void ( const Bytes ) > success, const function< void ( const error_code ) > failure )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &IPCSocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
|
||||
auto data = make_shared< asio::streambuf >( );
|
||||
asio::async_read( *m_socket, *data, asio::transfer_exactly( length ), [ this, data, success, failure ]( const error_code code, const size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( code )
|
||||
{
|
||||
m_is_open = false;
|
||||
failure( code );
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto data_ptr = asio::buffer_cast< const Byte* >( data->data( ) );
|
||||
success( Bytes( data_ptr, data_ptr + length ) );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
void IPCSocketImpl::read( const shared_ptr< asio::streambuf >& data, const size_t length, const function< void ( const error_code&, size_t ) >& callback )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &IPCSocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
|
||||
asio::async_read( *m_socket, *data, asio::transfer_at_least( length ), m_strand->wrap( [ this, callback ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
} ) );
|
||||
}
|
||||
|
||||
size_t IPCSocketImpl::read( const shared_ptr< asio::streambuf >& data, const string& delimiter, error_code& error )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( bind( &IPCSocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) );
|
||||
|
||||
size_t length = 0;
|
||||
auto finished = std::make_shared<bool>(false);
|
||||
auto sharedError = std::make_shared<error_code>();
|
||||
auto sharedLength = std::make_shared<size_t>(0);
|
||||
|
||||
asio::async_read_until( *m_socket, *data, delimiter,
|
||||
[ finished, sharedLength, sharedError ]( const error_code & error, size_t length ) {
|
||||
*sharedError = error;
|
||||
*sharedLength = length;
|
||||
*finished = true;
|
||||
});
|
||||
|
||||
while (!*finished)
|
||||
m_io_service.run_one();
|
||||
error = *sharedError;
|
||||
length = *sharedLength;
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void IPCSocketImpl::read( const shared_ptr< asio::streambuf >& data, const string& delimiter, const function< void ( const error_code&, size_t ) >& callback )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &IPCSocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
|
||||
asio::async_read_until( *m_socket, *data, delimiter, m_strand->wrap( [ this, callback ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
} ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef BUILD_IPC
|
||||
|
||||
//System Includes
|
||||
#include <queue>
|
||||
#include <tuple>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <functional>
|
||||
#include <system_error>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/byte.hpp"
|
||||
#include "corvusoft/restbed/detail/socket_impl.hpp"
|
||||
|
||||
//External Includes
|
||||
#include <asio/streambuf.hpp>
|
||||
#include <asio/steady_timer.hpp>
|
||||
#include <asio/io_service.hpp>
|
||||
#include <asio/io_service_strand.hpp>
|
||||
#include <asio/local/stream_protocol.hpp>
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
class Logger;
|
||||
class Session;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
class IPCSocketImpl : virtual public SocketImpl
|
||||
{
|
||||
public:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
IPCSocketImpl( asio::io_context& context, const std::shared_ptr< asio::local::stream_protocol::socket >& socket, const std::shared_ptr< Logger >& logger = nullptr );
|
||||
|
||||
~IPCSocketImpl( void ) = default;
|
||||
|
||||
//Functionality
|
||||
void close( void ) override;
|
||||
|
||||
bool is_open( void ) const override;
|
||||
|
||||
bool is_closed( void ) const override;
|
||||
|
||||
void connect( const std::string& hostname, const uint16_t port, const std::function< void ( const std::error_code& ) >& callback ) override;
|
||||
|
||||
void sleep_for( const std::chrono::milliseconds& delay, const std::function< void ( const std::error_code& ) >& callback ) override;
|
||||
|
||||
void start_write(const Bytes& data, const std::function< void ( const std::error_code&, std::size_t ) >& callback) override;
|
||||
|
||||
size_t start_read( const std::shared_ptr< asio::streambuf >& data, const std::string& delimiter, std::error_code& error ) override;
|
||||
|
||||
size_t start_read( const std::shared_ptr< asio::streambuf >& data, const std::size_t length, std::error_code& error ) override;
|
||||
|
||||
void start_read(const std::size_t length, const std::function< void ( const Bytes ) > success, const std::function< void ( const std::error_code ) > failure ) override;
|
||||
|
||||
void start_read( const std::shared_ptr< asio::streambuf >& data, const std::size_t length, const std::function< void ( const std::error_code&, std::size_t ) >& callback ) override;
|
||||
|
||||
void start_read(const std::shared_ptr< asio::streambuf >& data, const std::string& delimiter, const std::function< void ( const std::error_code&, std::size_t ) >& callback ) override;
|
||||
|
||||
//Getters
|
||||
std::string get_local_endpoint( void ) override;
|
||||
|
||||
std::string get_remote_endpoint( void ) override;
|
||||
|
||||
//Setters
|
||||
void set_timeout( const std::chrono::milliseconds& value ) override;
|
||||
|
||||
void set_keep_alive( const uint32_t start, const uint32_t interval, const uint32_t cnt) override;
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > m_error_handler;
|
||||
|
||||
protected:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
|
||||
//Functionality
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
|
||||
private:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
IPCSocketImpl( const IPCSocketImpl& original ) = delete;
|
||||
|
||||
//Functionality
|
||||
std::shared_ptr< IPCSocketImpl > shared_from_this( void );
|
||||
|
||||
void connection_timeout_handler( const std::shared_ptr< IPCSocketImpl > socket, const std::error_code& error );
|
||||
|
||||
void write( void );
|
||||
|
||||
void write( const Bytes& data, const std::function< void ( const std::error_code&, std::size_t ) >& callback );
|
||||
|
||||
void write_helper( const Bytes& data, const std::function< void ( const std::error_code&, std::size_t ) >& callback );
|
||||
|
||||
size_t read( const std::shared_ptr< asio::streambuf >& data, const std::size_t length, std::error_code& error );
|
||||
|
||||
void read( const std::size_t length, const std::function< void ( const Bytes ) > success, const std::function< void ( const std::error_code ) > failure );
|
||||
|
||||
void read( const std::shared_ptr< asio::streambuf >& data, const std::size_t length, const std::function< void ( const std::error_code&, std::size_t ) >& callback );
|
||||
|
||||
size_t read( const std::shared_ptr< asio::streambuf >& data, const std::string& delimiter, std::error_code& error );
|
||||
|
||||
void read( const std::shared_ptr< asio::streambuf >& data, const std::string& delimiter, const std::function< void ( const std::error_code&, std::size_t ) >& callback );
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
IPCSocketImpl& operator =( const IPCSocketImpl& value ) = delete;
|
||||
|
||||
//Properties
|
||||
bool m_is_open;
|
||||
|
||||
const uint8_t MAX_WRITE_RETRIES = 5;
|
||||
|
||||
std::queue< std::tuple< Bytes, uint8_t, std::function< void ( const std::error_code&, std::size_t ) > > > m_pending_writes;
|
||||
|
||||
std::shared_ptr< Logger > m_logger;
|
||||
|
||||
std::chrono::milliseconds m_timeout;
|
||||
|
||||
asio::io_context &m_io_service;
|
||||
|
||||
std::shared_ptr< asio::steady_timer > m_timer;
|
||||
|
||||
std::shared_ptr< asio::io_service::strand > m_strand;
|
||||
|
||||
std::shared_ptr< asio::local::stream_protocol::socket > m_socket;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
//Project Includes
|
||||
#include <corvusoft/restbed/byte.hpp>
|
||||
|
||||
//External Includes
|
||||
#include <asio/streambuf.hpp>
|
||||
#include <asio/io_service.hpp>
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
class Uri;
|
||||
class Response;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
class SocketImpl;
|
||||
|
||||
struct RequestImpl
|
||||
{
|
||||
Bytes m_body { };
|
||||
|
||||
uint16_t m_port = 80;
|
||||
|
||||
double m_version = 1.1;
|
||||
|
||||
std::string m_host = "";
|
||||
|
||||
std::string m_path = "/";
|
||||
|
||||
std::string m_method = "GET";
|
||||
|
||||
std::string m_protocol = "HTTP";
|
||||
|
||||
std::shared_ptr< Uri > m_uri = nullptr;
|
||||
|
||||
std::shared_ptr< Response > m_response = nullptr;
|
||||
|
||||
std::multimap< std::string, std::string > m_headers { };
|
||||
|
||||
std::map< std::string, std::string > m_path_parameters { };
|
||||
|
||||
std::multimap< std::string, std::string > m_query_parameters { };
|
||||
|
||||
std::shared_ptr< asio::io_service > m_io_service = nullptr;
|
||||
|
||||
std::shared_ptr< SocketImpl > m_socket = nullptr;
|
||||
|
||||
std::shared_ptr< asio::streambuf > m_buffer = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
//Project Includes
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
class Rule;
|
||||
class Session;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
struct ResourceImpl
|
||||
{
|
||||
std::set< std::string > m_paths { };
|
||||
|
||||
std::set< std::string > m_methods { };
|
||||
|
||||
std::vector< std::shared_ptr< Rule > > m_rules { };
|
||||
|
||||
std::multimap< std::string, std::string > m_default_headers { };
|
||||
|
||||
std::function< void ( const std::shared_ptr< Session > ) > m_failed_filter_validation_handler = nullptr;
|
||||
|
||||
std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > m_error_handler = nullptr;
|
||||
|
||||
std::function< void ( const std::shared_ptr< Session >, const std::function< void ( const std::shared_ptr< Session > ) >& ) > m_authentication_handler = nullptr;
|
||||
|
||||
std::multimap< std::string, std::pair< std::multimap< std::string, std::string >, std::function< void ( const std::shared_ptr< Session > ) > > > m_method_handlers { };
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/byte.hpp"
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
class Request;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
struct ResponseImpl
|
||||
{
|
||||
Bytes m_body { };
|
||||
|
||||
double m_version = 1.1;
|
||||
|
||||
int m_status_code = 0;
|
||||
|
||||
Request* m_request = nullptr;
|
||||
|
||||
std::string m_protocol = "HTTP";
|
||||
|
||||
std::string m_status_message = "";
|
||||
|
||||
std::multimap< std::string, std::string > m_headers { };
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstddef>
|
||||
|
||||
//Project Includes
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
class Rule;
|
||||
class Session;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
static void rule_engine( const std::shared_ptr< Session > session,
|
||||
const std::vector< std::shared_ptr< Rule > >& rules,
|
||||
const std::function< void ( const std::shared_ptr< Session > ) >& callback,
|
||||
std::size_t index = 0 )
|
||||
{
|
||||
while ( index not_eq rules.size( ) )
|
||||
{
|
||||
auto rule = rules.at( index );
|
||||
index++;
|
||||
|
||||
if ( rule->condition( session ) )
|
||||
{
|
||||
rule->action( session, bind( rule_engine, session, rules, callback, index ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( index == rules.size( ) )
|
||||
{
|
||||
callback( session );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <memory>
|
||||
|
||||
//Project Includes
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
struct RuleImpl
|
||||
{
|
||||
int m_priority = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
951
lib/restbed-4.8/source/corvusoft/restbed/detail/service_impl.cpp
Normal file
951
lib/restbed-4.8/source/corvusoft/restbed/detail/service_impl.cpp
Normal file
@@ -0,0 +1,951 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
//System Includes
|
||||
#include <regex>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <ciso646>
|
||||
#include <cstdlib>
|
||||
#include <clocale>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/uri.hpp"
|
||||
#include "corvusoft/restbed/rule.hpp"
|
||||
#include "corvusoft/restbed/logger.hpp"
|
||||
#include "corvusoft/restbed/string.hpp"
|
||||
#include "corvusoft/restbed/request.hpp"
|
||||
#include "corvusoft/restbed/session.hpp"
|
||||
#include "corvusoft/restbed/resource.hpp"
|
||||
#include "corvusoft/restbed/settings.hpp"
|
||||
#include "corvusoft/restbed/status_code.hpp"
|
||||
#include "corvusoft/restbed/ssl_settings.hpp"
|
||||
#include "corvusoft/restbed/session_manager.hpp"
|
||||
#include "corvusoft/restbed/detail/socket_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/request_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/service_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/session_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/resource_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/ipc_socket_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/rule_engine_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/web_socket_manager_impl.hpp"
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
using std::set;
|
||||
using std::map;
|
||||
using std::free;
|
||||
using std::pair;
|
||||
using std::bind;
|
||||
using std::regex;
|
||||
using std::string;
|
||||
using std::smatch;
|
||||
using std::istream;
|
||||
using std::find_if;
|
||||
using std::function;
|
||||
using std::setlocale;
|
||||
using std::multimap;
|
||||
using std::to_string;
|
||||
using std::exception;
|
||||
using std::shared_ptr;
|
||||
using std::error_code;
|
||||
using std::regex_error;
|
||||
using std::make_shared;
|
||||
using std::runtime_error;
|
||||
using std::placeholders::_1;
|
||||
using std::placeholders::_2;
|
||||
using std::placeholders::_3;
|
||||
using std::current_exception;
|
||||
using std::rethrow_exception;
|
||||
using std::chrono::steady_clock;
|
||||
using std::regex_constants::icase;
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
using asio::ip::tcp;
|
||||
using asio::io_service;
|
||||
using asio::signal_set;
|
||||
using asio::ip::address;
|
||||
using asio::socket_base;
|
||||
using asio::system_error;
|
||||
|
||||
#if BUILD_IPC
|
||||
using asio::local::stream_protocol;
|
||||
#endif
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
ServiceImpl::ServiceImpl( void ) : m_uptime( steady_clock::time_point::min( ) ),
|
||||
m_logger( nullptr ),
|
||||
m_supported_methods( ),
|
||||
m_settings( nullptr ),
|
||||
m_io_service( make_shared< ::io_service >( ) ),
|
||||
m_signal_set( nullptr ),
|
||||
m_session_manager( nullptr ),
|
||||
m_web_socket_manager( nullptr ),
|
||||
m_rules( ),
|
||||
m_workers_stopped( ),
|
||||
#ifdef BUILD_SSL
|
||||
m_ssl_settings( nullptr ),
|
||||
m_ssl_context( nullptr ),
|
||||
m_ssl_acceptor( nullptr ),
|
||||
#endif
|
||||
#ifdef BUILD_IPC
|
||||
m_ipc_acceptor( nullptr ),
|
||||
#endif
|
||||
m_acceptor( nullptr ),
|
||||
m_resource_paths( ),
|
||||
m_resource_routes( ),
|
||||
m_ready_handler( nullptr ),
|
||||
m_signal_handlers( ),
|
||||
m_not_found_handler( nullptr ),
|
||||
m_method_not_allowed_handler( nullptr ),
|
||||
m_method_not_implemented_handler( nullptr ),
|
||||
m_failed_filter_validation_handler( nullptr ),
|
||||
m_error_handler( ServiceImpl::default_error_handler ),
|
||||
m_authentication_handler( nullptr )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ServiceImpl::~ServiceImpl( void )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void ServiceImpl::http_start( void )
|
||||
{
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_ssl_settings == nullptr or m_ssl_settings->has_disabled_http( ) == false )
|
||||
{
|
||||
#endif
|
||||
|
||||
if ( not m_settings->get_bind_address( ).empty( ) )
|
||||
{
|
||||
const auto address = address::from_string( m_settings->get_bind_address( ) );
|
||||
m_acceptor = make_shared< tcp::acceptor >( *m_io_service, tcp::endpoint( address, m_settings->get_port( ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_acceptor = make_shared< tcp::acceptor >( *m_io_service, tcp::endpoint( tcp::v6( ), m_settings->get_port( ) ) );
|
||||
}
|
||||
|
||||
m_acceptor->set_option( socket_base::reuse_address( m_settings->get_reuse_address( ) ) );
|
||||
m_acceptor->listen( m_settings->get_connection_limit( ) );
|
||||
|
||||
http_listen( );
|
||||
|
||||
const auto location = get_http_uri( )->to_string( );
|
||||
log( Logger::INFO, String::format( "Service accepting HTTP connections at '%s'.", location.data( ) ) );
|
||||
#ifdef BUILD_SSL
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void ServiceImpl::http_listen( void ) const
|
||||
{
|
||||
auto socket = make_shared< tcp::socket >( *m_io_service );
|
||||
m_acceptor->async_accept( *socket, bind( &ServiceImpl::create_session, this, socket, _1 ) );
|
||||
}
|
||||
|
||||
void ServiceImpl::setup_signal_handler( void )
|
||||
{
|
||||
if ( m_signal_handlers.empty( ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_signal_set = make_shared< signal_set >( *m_io_service );
|
||||
|
||||
for ( const auto signal_handler : m_signal_handlers )
|
||||
{
|
||||
m_signal_set->add( signal_handler.first );
|
||||
}
|
||||
|
||||
m_signal_set->async_wait( bind( &ServiceImpl::signal_handler, this, _1, _2 ) );
|
||||
}
|
||||
|
||||
void ServiceImpl::signal_handler( const error_code& error, const int signal_number ) const
|
||||
{
|
||||
if ( error )
|
||||
{
|
||||
log( Logger::WARNING, String::format( "Failed to process signal '%i', '%s'.", signal_number, error.message( ).data( ) ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( m_signal_handlers.count( signal_number ) )
|
||||
{
|
||||
m_signal_handlers.at( signal_number )( signal_number );
|
||||
}
|
||||
|
||||
m_signal_set->async_wait( bind( &ServiceImpl::signal_handler, this, _1, _2 ) );
|
||||
}
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
void ServiceImpl::https_start( void )
|
||||
{
|
||||
if ( m_ssl_settings not_eq nullptr )
|
||||
{
|
||||
m_ssl_context = make_shared< asio::ssl::context >( asio::ssl::context::sslv23 );
|
||||
m_ssl_context->set_default_verify_paths( );
|
||||
|
||||
auto passphrase = m_ssl_settings->get_passphrase( );
|
||||
m_ssl_context->set_password_callback( [ passphrase ]( size_t, asio::ssl::context::password_purpose )
|
||||
{
|
||||
return passphrase;
|
||||
} );
|
||||
|
||||
auto filename = m_ssl_settings->get_temporary_diffie_hellman( );
|
||||
|
||||
if ( not filename.empty( ) )
|
||||
{
|
||||
m_ssl_context->use_tmp_dh_file( filename );
|
||||
}
|
||||
|
||||
filename = m_ssl_settings->get_certificate_authority_pool( );
|
||||
|
||||
if ( not filename.empty( ) )
|
||||
{
|
||||
m_ssl_context->add_verify_path( filename );
|
||||
}
|
||||
|
||||
filename = m_ssl_settings->get_certificate_chain( );
|
||||
|
||||
if ( not filename.empty( ) )
|
||||
{
|
||||
m_ssl_context->use_certificate_chain_file( filename );
|
||||
}
|
||||
|
||||
filename = m_ssl_settings->get_certificate( );
|
||||
|
||||
if ( not filename.empty( ) )
|
||||
{
|
||||
m_ssl_context->use_certificate_file( filename, asio::ssl::context::pem );
|
||||
}
|
||||
|
||||
filename = m_ssl_settings->get_private_key( );
|
||||
|
||||
if ( not filename.empty( ) )
|
||||
{
|
||||
m_ssl_context->use_private_key_file( filename, asio::ssl::context::pem );
|
||||
}
|
||||
|
||||
filename = m_ssl_settings->get_private_rsa_key( );
|
||||
|
||||
if ( not filename.empty( ) )
|
||||
{
|
||||
m_ssl_context->use_rsa_private_key_file( filename, asio::ssl::context::pem );
|
||||
}
|
||||
|
||||
asio::ssl::context::options options = 0;
|
||||
options = ( m_ssl_settings->has_enabled_tlsv1( ) ) ? options : options | asio::ssl::context::no_tlsv1;
|
||||
options = ( m_ssl_settings->has_enabled_sslv2( ) ) ? options : options | asio::ssl::context::no_sslv2;
|
||||
options = ( m_ssl_settings->has_enabled_sslv3( ) ) ? options : options | asio::ssl::context::no_sslv3;
|
||||
options = ( m_ssl_settings->has_enabled_tlsv11( ) ) ? options : options | asio::ssl::context::no_tlsv1_1;
|
||||
options = ( m_ssl_settings->has_enabled_tlsv12( ) ) ? options : options | asio::ssl::context::no_tlsv1_2;
|
||||
options = ( m_ssl_settings->has_enabled_compression( ) ) ? options : options | asio::ssl::context::no_compression;
|
||||
options = ( m_ssl_settings->has_enabled_default_workarounds( ) ) ? options | asio::ssl::context::default_workarounds : options;
|
||||
options = ( m_ssl_settings->has_enabled_single_diffie_hellman_use( ) ) ? options | asio::ssl::context::single_dh_use : options;
|
||||
m_ssl_context->set_options( options );
|
||||
|
||||
if ( not m_ssl_settings->get_bind_address( ).empty( ) )
|
||||
{
|
||||
const auto address = address::from_string( m_ssl_settings->get_bind_address( ) );
|
||||
m_ssl_acceptor = make_shared< tcp::acceptor >( *m_io_service, tcp::endpoint( address, m_ssl_settings->get_port( ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ssl_acceptor = make_shared< tcp::acceptor >( *m_io_service, tcp::endpoint( tcp::v6( ), m_ssl_settings->get_port( ) ) );
|
||||
}
|
||||
|
||||
m_ssl_acceptor->set_option( socket_base::reuse_address( m_settings->get_reuse_address( ) ) );
|
||||
m_ssl_acceptor->listen( m_settings->get_connection_limit( ) );
|
||||
|
||||
https_listen( );
|
||||
|
||||
const auto location = get_https_uri( )->to_string( );
|
||||
log( Logger::INFO, String::format( "Service accepting HTTPS connections at '%s'.", location.data( ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceImpl::https_listen( void ) const
|
||||
{
|
||||
auto socket = make_shared< asio::ssl::stream< tcp::socket > >( *m_io_service, *m_ssl_context );
|
||||
m_ssl_acceptor->async_accept( socket->lowest_layer( ), bind( &ServiceImpl::create_ssl_session, this, socket, _1 ) );
|
||||
}
|
||||
|
||||
void ServiceImpl::create_ssl_session( const shared_ptr< asio::ssl::stream< tcp::socket > >& socket, const error_code& error ) const
|
||||
{
|
||||
if ( not error )
|
||||
{
|
||||
socket->async_handshake( asio::ssl::stream_base::server, [ this, socket ]( const error_code & error )
|
||||
{
|
||||
if ( error )
|
||||
{
|
||||
log( Logger::SECURITY, String::format( "Failed SSL handshake, '%s'.", error.message( ).data( ) ) );
|
||||
return;
|
||||
}
|
||||
|
||||
auto connection = make_shared< SocketImpl >( *m_io_service, socket, m_logger );
|
||||
connection->set_timeout( m_settings->get_connection_timeout( ) );
|
||||
if (m_settings->get_keep_alive()) {
|
||||
connection->set_keep_alive( m_settings->get_keep_alive_start(),
|
||||
m_settings->get_keep_alive_interval(),
|
||||
m_settings->get_keep_alive_cnt());
|
||||
}
|
||||
|
||||
m_session_manager->create( [ this, connection ]( const shared_ptr< Session > session )
|
||||
{
|
||||
session->m_pimpl->m_settings = m_settings;
|
||||
session->m_pimpl->m_manager = m_session_manager;
|
||||
session->m_pimpl->m_web_socket_manager = m_web_socket_manager;
|
||||
session->m_pimpl->m_error_handler = m_error_handler;
|
||||
session->m_pimpl->m_request = make_shared< Request >( );
|
||||
session->m_pimpl->m_request->m_pimpl->m_socket = connection;
|
||||
session->m_pimpl->m_request->m_pimpl->m_socket->m_error_handler = m_error_handler;
|
||||
session->m_pimpl->m_request->m_pimpl->m_buffer = make_shared< asio::streambuf >( );
|
||||
session->m_pimpl->m_keep_alive_callback = bind( &ServiceImpl::parse_request, this, _1, _2, _3 );
|
||||
session->m_pimpl->m_request->m_pimpl->m_socket->start_read( session->m_pimpl->m_request->m_pimpl->m_buffer, "\r\n\r\n", bind( &ServiceImpl::parse_request, this, _1, _2, session ) );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( socket not_eq nullptr and socket->lowest_layer( ).is_open( ) )
|
||||
{
|
||||
socket->lowest_layer( ).close( );
|
||||
}
|
||||
|
||||
log( Logger::WARNING, String::format( "Failed to create session, '%s'.", error.message( ).data( ) ) );
|
||||
}
|
||||
|
||||
https_listen( );
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_IPC
|
||||
void ServiceImpl::ipc_start( void )
|
||||
{
|
||||
const string location = "/tmp/restbed.sock";
|
||||
::remove( location.data( ) );
|
||||
|
||||
m_ipc_acceptor = make_shared< stream_protocol::acceptor >( *m_io_service, stream_protocol::endpoint( location ) );
|
||||
m_ipc_acceptor->set_option( socket_base::reuse_address( m_settings->get_reuse_address( ) ) );
|
||||
m_ipc_acceptor->listen( m_settings->get_connection_limit( ) );
|
||||
ipc_listen( );
|
||||
|
||||
log( Logger::INFO, String::format( "Service accepting HTTP connections at '%s'.", location.data( ) ) );
|
||||
}
|
||||
|
||||
void ServiceImpl::ipc_listen( void ) const
|
||||
{
|
||||
auto socket = make_shared< stream_protocol::socket >( *m_io_service );
|
||||
m_ipc_acceptor->async_accept( *socket, bind( &ServiceImpl::create_ipc_session, this, socket, _1 ) );
|
||||
}
|
||||
|
||||
void ServiceImpl::create_ipc_session( const shared_ptr< stream_protocol::socket >& socket, const error_code& error ) const
|
||||
{
|
||||
if ( not error )
|
||||
{
|
||||
auto connection = make_shared< IPCSocketImpl >( *m_io_service, socket, m_logger );
|
||||
connection->set_timeout( m_settings->get_connection_timeout( ) );
|
||||
if (m_settings->get_keep_alive()) {
|
||||
connection->set_keep_alive( m_settings->get_keep_alive_start(),
|
||||
m_settings->get_keep_alive_interval(),
|
||||
m_settings->get_keep_alive_cnt());
|
||||
}
|
||||
|
||||
m_session_manager->create( [ this, connection ]( const shared_ptr< Session > session )
|
||||
{
|
||||
session->m_pimpl->m_settings = m_settings;
|
||||
session->m_pimpl->m_manager = m_session_manager;
|
||||
session->m_pimpl->m_web_socket_manager = m_web_socket_manager;
|
||||
session->m_pimpl->m_error_handler = m_error_handler;
|
||||
session->m_pimpl->m_request = make_shared< Request >( );
|
||||
session->m_pimpl->m_request->m_pimpl->m_socket = connection;
|
||||
session->m_pimpl->m_request->m_pimpl->m_socket->m_error_handler = m_error_handler;
|
||||
session->m_pimpl->m_request->m_pimpl->m_buffer = make_shared< asio::streambuf >( );
|
||||
session->m_pimpl->m_keep_alive_callback = bind( &ServiceImpl::parse_request, this, _1, _2, _3 );
|
||||
session->m_pimpl->m_request->m_pimpl->m_socket->start_read( session->m_pimpl->m_request->m_pimpl->m_buffer, "\r\n\r\n", bind( &ServiceImpl::parse_request, this, _1, _2, session ) );
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( socket not_eq nullptr and socket->is_open( ) )
|
||||
{
|
||||
socket->close( );
|
||||
}
|
||||
|
||||
log( Logger::WARNING, String::format( "Failed to create session, '%s'.", error.message( ).data( ) ) );
|
||||
}
|
||||
|
||||
ipc_listen( );
|
||||
}
|
||||
#endif
|
||||
|
||||
string ServiceImpl::sanitise_path( const string& path ) const
|
||||
{
|
||||
if ( path == "/" )
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
smatch matches;
|
||||
string sanitised_path = "";
|
||||
static const regex pattern( "^\\{[a-zA-Z0-9_\\-]+: ?(.*)\\}$" );
|
||||
|
||||
for ( auto folder : String::split( path, '/' ) )
|
||||
{
|
||||
if ( folder.front( ) == '{' and folder.back( ) == '}' )
|
||||
{
|
||||
if ( not regex_match( folder, matches, pattern ) or matches.size( ) not_eq 2 )
|
||||
{
|
||||
throw runtime_error( String::format( "Resource path parameter declaration is malformed '%s'.", folder.data( ) ) );
|
||||
}
|
||||
|
||||
sanitised_path += '/' + matches[ 1 ].str( );
|
||||
}
|
||||
else
|
||||
{
|
||||
sanitised_path += '/' + folder;
|
||||
}
|
||||
}
|
||||
|
||||
if ( path.back( ) == '/' )
|
||||
{
|
||||
sanitised_path += '/';
|
||||
}
|
||||
|
||||
return sanitised_path;
|
||||
}
|
||||
|
||||
void ServiceImpl::not_found( const shared_ptr< Session > session ) const
|
||||
{
|
||||
log( Logger::INFO, String::format( "'%s' resource route not found '%s'.",
|
||||
session->get_origin( ).data( ),
|
||||
session->get_request( )->get_path( ).data( ) ) );
|
||||
|
||||
if ( m_not_found_handler not_eq nullptr )
|
||||
{
|
||||
m_not_found_handler( session );
|
||||
}
|
||||
else
|
||||
{
|
||||
session->close( NOT_FOUND );
|
||||
}
|
||||
}
|
||||
|
||||
bool ServiceImpl::has_unique_paths( const set< string >& paths ) const
|
||||
{
|
||||
if ( paths.empty( ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for ( const auto& path : paths )
|
||||
{
|
||||
if ( m_resource_routes.count( path ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ServiceImpl::log( const Logger::Level level, const string& message ) const
|
||||
{
|
||||
if ( m_logger not_eq nullptr )
|
||||
{
|
||||
try
|
||||
{
|
||||
m_logger->log( level, "%s", message.data( ) );
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
fprintf( stderr, "Failed to create log entry: %s", message.data( ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceImpl::method_not_allowed( const shared_ptr< Session > session ) const
|
||||
{
|
||||
log( Logger::INFO, String::format( "'%s' '%s' method not allowed '%s'.",
|
||||
session->get_origin( ).data( ),
|
||||
session->get_request( )->get_method( ).data( ),
|
||||
session->get_request( )->get_path( ).data( ) ) );
|
||||
|
||||
if ( m_method_not_allowed_handler not_eq nullptr )
|
||||
{
|
||||
m_method_not_allowed_handler( session );
|
||||
}
|
||||
else
|
||||
{
|
||||
session->close( METHOD_NOT_ALLOWED );
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceImpl::method_not_implemented( const shared_ptr< Session > session ) const
|
||||
{
|
||||
log( Logger::INFO, String::format( "'%s' '%s' method not implemented '%s'.",
|
||||
session->get_origin( ).data( ),
|
||||
session->get_request( )->get_method( ).data( ),
|
||||
session->get_request( )->get_path( ).data( ) ) );
|
||||
|
||||
if ( m_method_not_implemented_handler not_eq nullptr )
|
||||
{
|
||||
m_method_not_implemented_handler( session );
|
||||
}
|
||||
else
|
||||
{
|
||||
session->close( NOT_IMPLEMENTED );
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceImpl::failed_filter_validation( const shared_ptr< Session > session ) const
|
||||
{
|
||||
log( Logger::INFO, String::format( "'%s' failed filter validation '%s'.",
|
||||
session->get_origin( ).data( ),
|
||||
session->get_request( )->get_path( ).data( ) ) );
|
||||
|
||||
if ( m_failed_filter_validation_handler not_eq nullptr )
|
||||
{
|
||||
m_failed_filter_validation_handler( session );
|
||||
}
|
||||
else
|
||||
{
|
||||
session->close( BAD_REQUEST, { { "Connection", "close" } } );
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceImpl::router( const shared_ptr< Session > session ) const
|
||||
{
|
||||
log( Logger::INFO, String::format( "Incoming '%s' request from '%s' for route '%s'.",
|
||||
session->get_request( )->get_method( ).data( ),
|
||||
session->get_origin( ).data( ),
|
||||
session->get_request( )->get_path( ).data( ) ) );
|
||||
|
||||
if ( session->is_closed( ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rule_engine( session, m_rules, [ this ]( const shared_ptr< Session > session )
|
||||
{
|
||||
const auto resource_route = find_if( m_resource_routes.begin( ), m_resource_routes.end( ), bind( &ServiceImpl::resource_router, this, session, _1 ) );
|
||||
|
||||
if ( resource_route == m_resource_routes.end( ) )
|
||||
{
|
||||
return not_found( session );
|
||||
}
|
||||
|
||||
const auto path = resource_route->first;
|
||||
session->m_pimpl->m_resource = resource_route->second;
|
||||
const auto request = session->get_request( );
|
||||
extract_path_parameters( path, request );
|
||||
|
||||
const auto callback = [ this ]( const shared_ptr< Session > session )
|
||||
{
|
||||
rule_engine( session, session->m_pimpl->m_resource->m_pimpl->m_rules, [ this ]( const shared_ptr< Session > session )
|
||||
{
|
||||
if ( session->is_closed( ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto request = session->get_request( );
|
||||
auto method_handler = find_method_handler( session );
|
||||
|
||||
if ( method_handler == nullptr )
|
||||
{
|
||||
if ( m_supported_methods.count( request->get_method( ) ) == 0 )
|
||||
{
|
||||
method_handler = bind( &ServiceImpl::method_not_implemented, this, _1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
method_handler = bind( &ServiceImpl::method_not_allowed, this, _1 );
|
||||
}
|
||||
}
|
||||
|
||||
method_handler( session );
|
||||
} );
|
||||
};
|
||||
|
||||
if ( session->m_pimpl->m_resource->m_pimpl->m_authentication_handler not_eq nullptr )
|
||||
{
|
||||
session->m_pimpl->m_resource->m_pimpl->m_authentication_handler( session, callback );
|
||||
}
|
||||
else
|
||||
{
|
||||
callback( session );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
void ServiceImpl::create_session( const shared_ptr< tcp::socket >& socket, const error_code& error ) const
|
||||
{
|
||||
if ( not error )
|
||||
{
|
||||
auto connection = make_shared< SocketImpl >( *m_io_service, socket, m_logger );
|
||||
connection->set_timeout( m_settings->get_connection_timeout( ) );
|
||||
if (m_settings->get_keep_alive()) {
|
||||
connection->set_keep_alive( m_settings->get_keep_alive_start(),
|
||||
m_settings->get_keep_alive_interval(),
|
||||
m_settings->get_keep_alive_cnt());
|
||||
}
|
||||
|
||||
m_session_manager->create( [ this, connection ]( const shared_ptr< Session > session )
|
||||
{
|
||||
session->m_pimpl->m_settings = m_settings;
|
||||
session->m_pimpl->m_manager = m_session_manager;
|
||||
session->m_pimpl->m_web_socket_manager = m_web_socket_manager;
|
||||
session->m_pimpl->m_error_handler = m_error_handler;
|
||||
session->m_pimpl->m_request = make_shared< Request >( );
|
||||
session->m_pimpl->m_request->m_pimpl->m_socket = connection;
|
||||
session->m_pimpl->m_request->m_pimpl->m_socket->m_error_handler = m_error_handler;
|
||||
session->m_pimpl->m_request->m_pimpl->m_buffer = make_shared< asio::streambuf >( );
|
||||
session->m_pimpl->m_keep_alive_callback = bind( &ServiceImpl::parse_request, this, _1, _2, _3 );
|
||||
session->m_pimpl->m_request->m_pimpl->m_socket->start_read( session->m_pimpl->m_request->m_pimpl->m_buffer, "\r\n\r\n", bind( &ServiceImpl::parse_request, this, _1, _2, session ) );
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( socket not_eq nullptr and socket->is_open( ) )
|
||||
{
|
||||
socket->close( );
|
||||
}
|
||||
|
||||
log( Logger::WARNING, String::format( "Failed to create session, '%s'.", error.message( ).data( ) ) );
|
||||
}
|
||||
|
||||
http_listen( );
|
||||
}
|
||||
|
||||
void ServiceImpl::extract_path_parameters( const string& sanitised_path, const shared_ptr< const Request >& request ) const
|
||||
{
|
||||
smatch matches;
|
||||
static const regex pattern( "^\\{([a-zA-Z0-9_\\-]+): ?.*\\}$" );
|
||||
|
||||
const auto folders = String::split( request->get_path( ), '/' );
|
||||
const auto declarations = String::split( m_settings->get_root( ) + "/" + m_resource_paths.at( sanitised_path ), '/' );
|
||||
|
||||
// Clear previous parameters in case of "keep-alive" request
|
||||
request->m_pimpl->m_path_parameters.clear();
|
||||
|
||||
for ( size_t index = 0; index < folders.size( ) and index < declarations.size( ); index++ )
|
||||
{
|
||||
const auto declaration = declarations[ index ];
|
||||
|
||||
if ( declaration.front( ) == '{' and declaration.back( ) == '}' )
|
||||
{
|
||||
regex_match( declaration, matches, pattern );
|
||||
request->m_pimpl->m_path_parameters.insert( make_pair( matches[ 1 ].str( ), folders[ index ] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function< void ( const shared_ptr< Session > ) > ServiceImpl::find_method_handler( const shared_ptr< Session > session ) const
|
||||
{
|
||||
const auto request = session->get_request( );
|
||||
const auto resource = session->get_resource( );
|
||||
const auto method_handlers = resource->m_pimpl->m_method_handlers.equal_range( request->get_method( ) );
|
||||
|
||||
bool failed_filter_validation = false;
|
||||
function< void ( const shared_ptr< Session > ) > method_handler = nullptr;
|
||||
|
||||
for ( auto handler = method_handlers.first; handler not_eq method_handlers.second and method_handler == nullptr; handler++ )
|
||||
{
|
||||
method_handler = handler->second.second;
|
||||
|
||||
for ( const auto& filter : handler->second.first )
|
||||
{
|
||||
for ( const auto& header : request->get_headers( filter.first ) )
|
||||
{
|
||||
if ( not regex_match( header.second, regex( filter.second ) ) )
|
||||
{
|
||||
method_handler = nullptr;
|
||||
failed_filter_validation = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( failed_filter_validation and method_handler == nullptr )
|
||||
{
|
||||
const auto handler = resource->m_pimpl->m_failed_filter_validation_handler;
|
||||
method_handler = ( handler == nullptr ) ? bind( &ServiceImpl::failed_filter_validation, this, _1 ) : handler;
|
||||
}
|
||||
|
||||
return method_handler;
|
||||
}
|
||||
|
||||
void ServiceImpl::authenticate( const shared_ptr< Session > session ) const
|
||||
{
|
||||
if ( m_authentication_handler not_eq nullptr )
|
||||
{
|
||||
m_authentication_handler( session, [ this ]( const shared_ptr< Session > session )
|
||||
{
|
||||
m_session_manager->load( session, bind( &ServiceImpl::router, this, _1 ) );
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_session_manager->load( session, bind( &ServiceImpl::router, this, _1 ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool ServiceImpl::resource_router( const shared_ptr< Session > session, const pair< string, shared_ptr< const Resource > >& route ) const
|
||||
{
|
||||
const auto request = session->get_request( );
|
||||
const auto path_folders = String::split( request->get_path( ), '/' );
|
||||
const auto route_folders = String::split( m_settings->get_root( ) + "/" + route.first, '/' );
|
||||
|
||||
if ( path_folders.empty( ) and route_folders.empty( ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool match = false;
|
||||
|
||||
if ( path_folders.size( ) == route_folders.size( ) )
|
||||
{
|
||||
for ( size_t index = 0; index < path_folders.size( ); index++ )
|
||||
{
|
||||
if ( m_settings->get_case_insensitive_uris( ) )
|
||||
{
|
||||
match = regex_match( path_folders[ index ], regex( route_folders[ index ], icase ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
match = regex_match( path_folders[ index ], regex( route_folders[ index ] ) );
|
||||
}
|
||||
|
||||
if ( not match )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
void ServiceImpl::default_error_handler( const int status, const exception& error, const shared_ptr< Session > session )
|
||||
{
|
||||
if ( session not_eq nullptr and session->is_open( ) )
|
||||
{
|
||||
string body = error.what( );
|
||||
session->close( status, body, { { "Content-Type", "text/plain" }, { "Content-Length", ::to_string( body.length( ) ) } } );
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceImpl::discard_request( istream& stream )
|
||||
{
|
||||
string line = String::empty;
|
||||
|
||||
while ( getline( stream, line ) )
|
||||
{
|
||||
if ( line == "\r" )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const map< string, string > ServiceImpl::parse_request_line( istream& stream )
|
||||
{
|
||||
smatch matches;
|
||||
static const regex pattern( "^([0-9a-zA-Z]*) ([a-zA-Z0-9:@_~!,;=#%&'\\-\\.\\/\\?\\$\\(\\)\\*\\+]+) (HTTP\\/[0-9]\\.[0-9])\\s*$" );
|
||||
string data = "";
|
||||
getline( stream, data );
|
||||
|
||||
if ( not regex_match( data, matches, pattern ) or matches.size( ) not_eq 4 )
|
||||
{
|
||||
throw runtime_error( "Your client has issued a malformed or illegal request status line. That’s all we know." );
|
||||
}
|
||||
|
||||
const string protocol = matches[ 3 ].str( );
|
||||
const auto delimiter = protocol.find_first_of( "/" );
|
||||
|
||||
return map< string, string >
|
||||
{
|
||||
{ "path", matches[ 2 ].str( ) },
|
||||
{ "method", matches[ 1 ].str( ) },
|
||||
{ "version", protocol.substr( delimiter + 1 ) },
|
||||
{ "protocol", protocol.substr( 0, delimiter ) }
|
||||
};
|
||||
}
|
||||
|
||||
const multimap< string, string > ServiceImpl::parse_request_headers( istream& stream )
|
||||
{
|
||||
smatch matches;
|
||||
string data = "";
|
||||
multimap< string, string > headers;
|
||||
static const regex pattern( "^([^:.]*): *(.*)\\s*$" );
|
||||
|
||||
while ( getline( stream, data ) and data not_eq "\r" )
|
||||
{
|
||||
if ( not regex_match( data, matches, pattern ) or matches.size( ) not_eq 3 )
|
||||
{
|
||||
throw runtime_error( "Your client has issued a malformed or illegal request header. That’s all we know." );
|
||||
}
|
||||
|
||||
headers.insert( make_pair( matches[ 1 ].str( ), matches[ 2 ].str( ) ) );
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
void ServiceImpl::parse_request( const error_code& error, size_t, const shared_ptr< Session > session ) const
|
||||
{
|
||||
istream stream( session->m_pimpl->m_request->m_pimpl->m_buffer.get( ) );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
discard_request( stream );
|
||||
const auto error_handler = get_error_handler( session );
|
||||
return error_handler( 400, runtime_error( error.message( ) ), session );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const auto items = parse_request_line( stream );
|
||||
const auto uri = Uri::parse( "http://localhost" + items.at( "path" ) );
|
||||
|
||||
session->m_pimpl->m_request->m_pimpl->m_body.clear( );
|
||||
session->m_pimpl->m_request->m_pimpl->m_path = Uri::decode( uri.get_path( ) );
|
||||
session->m_pimpl->m_request->m_pimpl->m_method = items.at( "method" );
|
||||
session->m_pimpl->m_request->m_pimpl->m_headers = parse_request_headers( stream );
|
||||
session->m_pimpl->m_request->m_pimpl->m_query_parameters = uri.get_query_parameters( );
|
||||
|
||||
char* locale = strdup( setlocale( LC_NUMERIC, nullptr ) );
|
||||
setlocale( LC_NUMERIC, "C" );
|
||||
session->m_pimpl->m_request->m_pimpl->m_version = stod( items.at( "version" ) );
|
||||
setlocale( LC_NUMERIC, locale );
|
||||
free( locale );
|
||||
|
||||
authenticate( session );
|
||||
}
|
||||
catch ( const int status_code )
|
||||
{
|
||||
discard_request( stream );
|
||||
const auto error_handler = get_error_handler( session );
|
||||
error_handler( status_code, runtime_error( m_settings->get_status_message( status_code ) ), session );
|
||||
}
|
||||
catch ( const regex_error& re )
|
||||
{
|
||||
discard_request( stream );
|
||||
const auto error_handler = get_error_handler( session );
|
||||
error_handler( 500, re, session );
|
||||
}
|
||||
catch ( const runtime_error& re )
|
||||
{
|
||||
discard_request( stream );
|
||||
const auto error_handler = get_error_handler( session );
|
||||
error_handler( 400, re, session );
|
||||
}
|
||||
catch ( const exception& ex )
|
||||
{
|
||||
discard_request( stream );
|
||||
const auto error_handler = get_error_handler( session );
|
||||
error_handler( 500, ex, session );
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
discard_request( stream );
|
||||
auto cex = current_exception( );
|
||||
|
||||
if ( cex not_eq nullptr )
|
||||
{
|
||||
try
|
||||
{
|
||||
rethrow_exception( cex );
|
||||
}
|
||||
catch ( const exception& ex )
|
||||
{
|
||||
const auto error_handler = get_error_handler( session );
|
||||
error_handler( 500, ex, session );
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
const auto error_handler = get_error_handler( session );
|
||||
error_handler( 500, runtime_error( "Internal Server Error" ), session );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto error_handler = get_error_handler( session );
|
||||
error_handler( 500, runtime_error( "Internal Server Error" ), session );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const shared_ptr< const Uri > ServiceImpl::get_http_uri( void ) const
|
||||
{
|
||||
if ( m_acceptor == nullptr )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto endpoint = m_acceptor->local_endpoint( );
|
||||
auto address = endpoint.address( );
|
||||
auto uri = String::empty;
|
||||
|
||||
if ( address.is_v6( ) )
|
||||
{
|
||||
uri = String::format( "http://[%s]:%u", address.to_string( ).data( ), endpoint.port( ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
uri = String::format( "http://%s:%u", address.to_string( ).data( ), endpoint.port( ) );
|
||||
}
|
||||
|
||||
return make_shared< const Uri >( uri );
|
||||
}
|
||||
|
||||
const shared_ptr< const Uri > ServiceImpl::get_https_uri( void ) const
|
||||
{
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_ssl_acceptor == nullptr )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto endpoint = m_ssl_acceptor->local_endpoint( );
|
||||
auto address = endpoint.address( );
|
||||
auto uri = String::empty;
|
||||
|
||||
if ( address.is_v6( ) )
|
||||
{
|
||||
uri = String::format( "https://[%s]:%u", address.to_string( ).data( ), endpoint.port( ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
uri = String::format( "https://%s:%u", address.to_string( ).data( ), endpoint.port( ) );
|
||||
}
|
||||
|
||||
return make_shared< const Uri >( uri );
|
||||
#else
|
||||
throw runtime_error( "Not Implemented! Rebuild Restbed with SSL funcationality enabled." );
|
||||
#endif
|
||||
}
|
||||
|
||||
const function< void ( const int, const exception&, const shared_ptr< Session > ) > ServiceImpl::get_error_handler( const shared_ptr< Session >& session ) const
|
||||
{
|
||||
return ( session->m_pimpl->m_resource not_eq nullptr and session->m_pimpl->m_resource->m_pimpl->m_error_handler not_eq nullptr ) ? session->m_pimpl->m_resource->m_pimpl->m_error_handler : m_error_handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
230
lib/restbed-4.8/source/corvusoft/restbed/detail/service_impl.hpp
Normal file
230
lib/restbed-4.8/source/corvusoft/restbed/detail/service_impl.hpp
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <functional>
|
||||
#include <system_error>
|
||||
|
||||
//Project Includes
|
||||
|
||||
//External Includes
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/signal_set.hpp>
|
||||
#include <asio/io_service.hpp>
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
#include <asio/ssl.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_IPC
|
||||
#include <asio/local/stream_protocol.hpp>
|
||||
#endif
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
class Uri;
|
||||
class Rule;
|
||||
class Logger;
|
||||
class Session;
|
||||
class Resource;
|
||||
class Settings;
|
||||
class SessionManager;
|
||||
class SSLSettings;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
class WebSocketManagerImpl;
|
||||
|
||||
class ServiceImpl
|
||||
{
|
||||
public:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
ServiceImpl( void );
|
||||
|
||||
virtual ~ServiceImpl( void );
|
||||
|
||||
//Functionality
|
||||
void http_start( void );
|
||||
|
||||
void http_listen( void ) const;
|
||||
#ifdef BUILD_SSL
|
||||
void https_start( void );
|
||||
|
||||
void https_listen( void ) const;
|
||||
|
||||
void create_ssl_session( const std::shared_ptr< asio::ssl::stream< asio::ip::tcp::socket > >& socket, const std::error_code& error ) const;
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_IPC
|
||||
void ipc_start( void );
|
||||
|
||||
void ipc_listen( void ) const;
|
||||
|
||||
void create_ipc_session( const std::shared_ptr< asio::local::stream_protocol::socket >& socket, const std::error_code& error ) const;
|
||||
#endif
|
||||
|
||||
void setup_signal_handler( void );
|
||||
|
||||
void signal_handler( const std::error_code& error, const int signal_number ) const;
|
||||
|
||||
std::string sanitise_path( const std::string& path ) const;
|
||||
|
||||
void not_found( const std::shared_ptr< Session > session ) const;
|
||||
|
||||
bool has_unique_paths( const std::set< std::string >& paths ) const;
|
||||
|
||||
void log( const Logger::Level level, const std::string& message ) const;
|
||||
|
||||
void method_not_allowed( const std::shared_ptr< Session > session ) const;
|
||||
|
||||
void method_not_implemented( const std::shared_ptr< Session > session ) const;
|
||||
|
||||
void failed_filter_validation( const std::shared_ptr< Session > session ) const;
|
||||
|
||||
void router( const std::shared_ptr< Session > session ) const;
|
||||
|
||||
void create_session( const std::shared_ptr< asio::ip::tcp::socket >& socket, const std::error_code& error ) const;
|
||||
|
||||
void extract_path_parameters( const std::string& sanitised_path, const std::shared_ptr< const Request >& request ) const;
|
||||
|
||||
std::function< void ( const std::shared_ptr< Session > ) > find_method_handler( const std::shared_ptr< Session > session ) const;
|
||||
|
||||
void authenticate( const std::shared_ptr< Session > session ) const;
|
||||
|
||||
bool resource_router( const std::shared_ptr< Session > session, const std::pair< std::string, std::shared_ptr< const Resource > >& route ) const;
|
||||
|
||||
static void default_error_handler( const int status, const std::exception& error, const std::shared_ptr< Session > session );
|
||||
|
||||
static void discard_request( std::istream& stream );
|
||||
|
||||
static const std::map< std::string, std::string > parse_request_line( std::istream& stream );
|
||||
|
||||
static const std::multimap< std::string, std::string > parse_request_headers( std::istream& stream );
|
||||
|
||||
void parse_request( const std::error_code& error, std::size_t length, const std::shared_ptr< Session > session ) const;
|
||||
|
||||
//Getters
|
||||
const std::shared_ptr< const Uri > get_http_uri( void ) const;
|
||||
|
||||
const std::shared_ptr< const Uri > get_https_uri( void ) const;
|
||||
|
||||
const std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > get_error_handler( const std::shared_ptr< Session >& session ) const;
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
std::chrono::steady_clock::time_point m_uptime;
|
||||
|
||||
std::shared_ptr< Logger > m_logger;
|
||||
|
||||
std::set< std::string > m_supported_methods;
|
||||
|
||||
std::shared_ptr< const Settings > m_settings;
|
||||
|
||||
std::shared_ptr< asio::io_service > m_io_service;
|
||||
|
||||
std::shared_ptr< asio::signal_set > m_signal_set;
|
||||
|
||||
std::shared_ptr< SessionManager > m_session_manager;
|
||||
|
||||
std::shared_ptr< WebSocketManagerImpl > m_web_socket_manager;
|
||||
|
||||
std::vector< std::shared_ptr< Rule > > m_rules;
|
||||
|
||||
std::unique_ptr< std::future<void > > m_workers_stopped;
|
||||
#ifdef BUILD_SSL
|
||||
std::shared_ptr< const SSLSettings > m_ssl_settings;
|
||||
|
||||
std::shared_ptr< asio::ssl::context > m_ssl_context;
|
||||
|
||||
std::shared_ptr< asio::ip::tcp::acceptor > m_ssl_acceptor;
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_IPC
|
||||
std::shared_ptr< asio::local::stream_protocol::acceptor > m_ipc_acceptor;
|
||||
#endif
|
||||
std::shared_ptr< asio::ip::tcp::acceptor > m_acceptor;
|
||||
|
||||
std::map< std::string, std::string > m_resource_paths;
|
||||
|
||||
std::map< std::string, std::shared_ptr< const Resource > > m_resource_routes;
|
||||
|
||||
std::function< void ( void ) > m_ready_handler;
|
||||
|
||||
std::map< int, std::function< void ( const int ) > > m_signal_handlers;
|
||||
|
||||
std::function< void ( const std::shared_ptr< Session > ) > m_not_found_handler;
|
||||
|
||||
std::function< void ( const std::shared_ptr< Session > ) > m_method_not_allowed_handler;
|
||||
|
||||
std::function< void ( const std::shared_ptr< Session > ) > m_method_not_implemented_handler;
|
||||
|
||||
std::function< void ( const std::shared_ptr< Session > ) > m_failed_filter_validation_handler;
|
||||
|
||||
std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > m_error_handler;
|
||||
|
||||
std::function< void ( const std::shared_ptr< Session >, const std::function< void ( const std::shared_ptr< Session > ) >& ) > m_authentication_handler;
|
||||
|
||||
protected:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
|
||||
//Functionality
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
|
||||
private:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
ServiceImpl( const ServiceImpl& original ) = delete;
|
||||
|
||||
//Functionality
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
ServiceImpl& operator =( const ServiceImpl& value ) = delete;
|
||||
|
||||
//Properties
|
||||
};
|
||||
}
|
||||
}
|
||||
212
lib/restbed-4.8/source/corvusoft/restbed/detail/session_impl.cpp
Normal file
212
lib/restbed-4.8/source/corvusoft/restbed/detail/session_impl.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
//System Includes
|
||||
#include <regex>
|
||||
#include <utility>
|
||||
#include <ciso646>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/uri.hpp"
|
||||
#include "corvusoft/restbed/http.hpp"
|
||||
#include "corvusoft/restbed/string.hpp"
|
||||
#include "corvusoft/restbed/session.hpp"
|
||||
#include "corvusoft/restbed/request.hpp"
|
||||
#include "corvusoft/restbed/response.hpp"
|
||||
#include "corvusoft/restbed/resource.hpp"
|
||||
#include "corvusoft/restbed/settings.hpp"
|
||||
#include "corvusoft/restbed/context_value.hpp"
|
||||
#include "corvusoft/restbed/session_manager.hpp"
|
||||
#include "corvusoft/restbed/detail/socket_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/request_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/session_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/resource_impl.hpp"
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
using std::map;
|
||||
using std::set;
|
||||
using std::regex;
|
||||
using std::smatch;
|
||||
using std::string;
|
||||
using std::getline;
|
||||
using std::istream;
|
||||
using std::function;
|
||||
using std::multimap;
|
||||
using std::make_pair;
|
||||
using std::exception;
|
||||
using std::to_string;
|
||||
using std::ssub_match;
|
||||
using std::shared_ptr;
|
||||
using std::error_code;
|
||||
using std::make_shared;
|
||||
using std::regex_match;
|
||||
using std::regex_error;
|
||||
using std::runtime_error;
|
||||
using std::placeholders::_1;
|
||||
using std::rethrow_exception;
|
||||
using std::current_exception;
|
||||
using std::chrono::milliseconds;
|
||||
|
||||
//Project Namespaces
|
||||
using restbed::detail::SessionImpl;
|
||||
|
||||
//External Namespaces
|
||||
using asio::buffer;
|
||||
using asio::streambuf;
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
SessionImpl::SessionImpl( void ) : m_id( String::empty ),
|
||||
m_request( nullptr ),
|
||||
m_resource( nullptr ),
|
||||
m_settings( nullptr ),
|
||||
m_manager( nullptr ),
|
||||
m_web_socket_manager( nullptr ),
|
||||
m_headers( ),
|
||||
m_context( ),
|
||||
m_error_handler( nullptr ),
|
||||
m_keep_alive_callback( nullptr ),
|
||||
m_error_handler_invoked( false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SessionImpl::~SessionImpl( void )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void SessionImpl::fetch_body( const size_t length, const shared_ptr< Session > session, const function< void ( const shared_ptr< Session >, const Bytes& ) >& callback ) const
|
||||
{
|
||||
const auto data_ptr = asio::buffer_cast< const Byte* >( session->m_pimpl->m_request->m_pimpl->m_buffer->data( ) );
|
||||
const auto data = Bytes( data_ptr, data_ptr + length );
|
||||
session->m_pimpl->m_request->m_pimpl->m_buffer->consume( length );
|
||||
|
||||
auto& body = m_request->m_pimpl->m_body;
|
||||
|
||||
if ( body.empty( ) )
|
||||
{
|
||||
body = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
body.insert( body.end( ), data.begin( ), data.end( ) );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
callback(session, data);
|
||||
}
|
||||
catch ( const int status_code )
|
||||
{
|
||||
const auto error_handler = session->m_pimpl->get_error_handler();
|
||||
error_handler( status_code, runtime_error( m_settings->get_status_message( status_code ) ), session );
|
||||
}
|
||||
catch ( const regex_error& re )
|
||||
{
|
||||
const auto error_handler = session->m_pimpl->get_error_handler();
|
||||
error_handler( 500, re, session );
|
||||
}
|
||||
catch ( const runtime_error& re )
|
||||
{
|
||||
const auto error_handler = session->m_pimpl->get_error_handler();
|
||||
error_handler( 400, re, session );
|
||||
}
|
||||
catch ( const exception& ex )
|
||||
{
|
||||
const auto error_handler = session->m_pimpl->get_error_handler();
|
||||
error_handler( 500, ex, session );
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
auto cex = current_exception( );
|
||||
|
||||
if ( cex not_eq nullptr )
|
||||
{
|
||||
try
|
||||
{
|
||||
rethrow_exception( cex );
|
||||
}
|
||||
catch ( const exception& ex )
|
||||
{
|
||||
const auto error_handler = session->m_pimpl->get_error_handler();
|
||||
error_handler( 500, ex, session );
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
const auto error_handler = session->m_pimpl->get_error_handler();
|
||||
error_handler( 500, runtime_error( "Internal Server Error" ), session );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto error_handler = session->m_pimpl->get_error_handler();
|
||||
error_handler( 500, runtime_error( "Internal Server Error" ), session );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SessionImpl::transmit( const Response& response, const function< void ( const error_code&, size_t ) >& callback ) const
|
||||
{
|
||||
auto hdrs = m_settings->get_default_headers( );
|
||||
|
||||
if ( m_resource not_eq nullptr )
|
||||
{
|
||||
const auto m_resource_headers = m_resource->m_pimpl->m_default_headers;
|
||||
hdrs.insert( m_resource_headers.begin( ), m_resource_headers.end( ) );
|
||||
}
|
||||
|
||||
hdrs.insert( m_headers.begin( ), m_headers.end( ) );
|
||||
|
||||
auto response_headers = response.get_headers( );
|
||||
hdrs.insert( response_headers.begin( ), response_headers.end( ) );
|
||||
|
||||
auto payload = make_shared< Response >( );
|
||||
payload->set_headers( hdrs );
|
||||
payload->set_body( response.get_body( ) );
|
||||
payload->set_version( response.get_version( ) );
|
||||
payload->set_protocol( response.get_protocol( ) );
|
||||
payload->set_status_code( response.get_status_code( ) );
|
||||
payload->set_status_message( response.get_status_message( ) );
|
||||
|
||||
if ( payload->get_status_message( ).empty( ) )
|
||||
{
|
||||
payload->set_status_message( m_settings->get_status_message( payload->get_status_code( ) ) );
|
||||
}
|
||||
|
||||
m_request->m_pimpl->m_socket->start_write( Http::to_bytes( payload ), callback );
|
||||
}
|
||||
|
||||
const function< void ( const int, const exception&, const shared_ptr< Session > ) > SessionImpl::get_error_handler( void )
|
||||
{
|
||||
if ( m_error_handler_invoked )
|
||||
{
|
||||
return [ ]( const int, const exception&, const shared_ptr< Session > ) { };
|
||||
}
|
||||
|
||||
m_error_handler_invoked = true;
|
||||
|
||||
auto error_handler = ( m_resource not_eq nullptr and m_resource->m_pimpl->m_error_handler not_eq nullptr ) ? m_resource->m_pimpl->m_error_handler : m_error_handler;
|
||||
|
||||
if ( error_handler == nullptr )
|
||||
{
|
||||
return [ ]( const int, const exception&, const shared_ptr< Session > session )
|
||||
{
|
||||
if ( session not_eq nullptr and session->is_open( ) )
|
||||
{
|
||||
session->close( );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return error_handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
125
lib/restbed-4.8/source/corvusoft/restbed/detail/session_impl.hpp
Normal file
125
lib/restbed-4.8/source/corvusoft/restbed/detail/session_impl.hpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <istream>
|
||||
#include <functional>
|
||||
#include <system_error>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/byte.hpp"
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
class Session;
|
||||
class Request;
|
||||
class Response;
|
||||
class Resource;
|
||||
class Settings;
|
||||
class SessionManager;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
class WebSocketManagerImpl;
|
||||
|
||||
class SessionImpl
|
||||
{
|
||||
public:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
SessionImpl( void );
|
||||
|
||||
SessionImpl( const SessionImpl& original ) = delete;
|
||||
|
||||
virtual ~SessionImpl( void );
|
||||
|
||||
//Functionality
|
||||
void fetch_body( const std::size_t length, const std::shared_ptr< Session > session, const std::function< void ( const std::shared_ptr< Session >, const Bytes& ) >& callback ) const;
|
||||
|
||||
void transmit( const Response& response, const std::function< void ( const std::error_code&, std::size_t ) >& callback ) const;
|
||||
|
||||
//Getters
|
||||
const std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > get_error_handler( void );
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
SessionImpl& operator =( const SessionImpl& value ) = delete;
|
||||
|
||||
//Properties
|
||||
std::string m_id;
|
||||
|
||||
std::shared_ptr< const Request > m_request;
|
||||
|
||||
std::shared_ptr< const Resource > m_resource;
|
||||
|
||||
std::shared_ptr< const Settings > m_settings;
|
||||
|
||||
std::shared_ptr< SessionManager > m_manager;
|
||||
|
||||
std::shared_ptr< WebSocketManagerImpl > m_web_socket_manager;
|
||||
|
||||
std::multimap< std::string, std::string > m_headers;
|
||||
|
||||
std::map< std::string, const ContextValue > m_context;
|
||||
|
||||
std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > m_error_handler;
|
||||
|
||||
std::function< void ( const std::error_code& error, std::size_t length, const std::shared_ptr< Session > ) > m_keep_alive_callback;
|
||||
|
||||
protected:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
|
||||
//Functionality
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
|
||||
private:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
|
||||
//Functionality
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
bool m_error_handler_invoked;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
//Project Includes
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
class SSLSettings;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
struct SettingsImpl
|
||||
{
|
||||
uint16_t m_port = 80;
|
||||
|
||||
std::string m_root = "/";
|
||||
|
||||
bool m_reuse_address = true;
|
||||
|
||||
unsigned int m_worker_limit = 0;
|
||||
|
||||
unsigned int m_connection_limit = 128;
|
||||
|
||||
std::string m_bind_address = "";
|
||||
|
||||
bool m_case_insensitive_uris = true;
|
||||
|
||||
bool m_keep_alive = true;
|
||||
|
||||
uint32_t m_keep_alive_start = 900;
|
||||
|
||||
uint32_t m_keep_alive_interval = 900;
|
||||
|
||||
uint32_t m_keep_alive_cnt = 3;
|
||||
|
||||
std::map< std::string, std::string > m_properties { };
|
||||
|
||||
std::shared_ptr< const SSLSettings > m_ssl_settings = nullptr;
|
||||
|
||||
std::multimap< std::string, std::string > m_default_headers { };
|
||||
|
||||
std::chrono::milliseconds m_connection_timeout = std::chrono::milliseconds( 5000 );
|
||||
|
||||
std::map< int, std::string > m_status_messages
|
||||
{
|
||||
{ 100, "Continue" },
|
||||
{ 101, "Switching Protocols" },
|
||||
{ 102, "Processing" },
|
||||
{ 200, "OK" },
|
||||
{ 201, "Created" },
|
||||
{ 202, "Accepted" },
|
||||
{ 203, "Non-Authoritative Information" },
|
||||
{ 204, "No Content" },
|
||||
{ 205, "Reset Content" },
|
||||
{ 206, "Partial Content" },
|
||||
{ 207, "Multi-Status" },
|
||||
{ 208, "Already Reported" },
|
||||
{ 226, "IM Used" },
|
||||
{ 300, "Multiple Choices" },
|
||||
{ 301, "Moved Permanently" },
|
||||
{ 302, "Found" },
|
||||
{ 303, "See Other" },
|
||||
{ 304, "Not Modified" },
|
||||
{ 305, "Use Proxy" },
|
||||
{ 306, "Reserved" },
|
||||
{ 307, "Temporary Redirect" },
|
||||
{ 308, "Permanent Redirect" },
|
||||
{ 400, "Bad Request" },
|
||||
{ 401, "Unauthorized" },
|
||||
{ 402, "Payment Required" },
|
||||
{ 403, "Forbidden" },
|
||||
{ 404, "Not Found" },
|
||||
{ 405, "Method Not Allowed" },
|
||||
{ 406, "Not Acceptable" },
|
||||
{ 407, "Proxy Authentication Required" },
|
||||
{ 408, "Request Timeout" },
|
||||
{ 409, "Conflict" },
|
||||
{ 410, "Gone" },
|
||||
{ 411, "Length Required" },
|
||||
{ 412, "Precondition Failed" },
|
||||
{ 413, "Request Entity Too Large" },
|
||||
{ 414, "Request URI Too Long" },
|
||||
{ 415, "Unsupported Media Type" },
|
||||
{ 416, "Requested Range Not Satisfiable" },
|
||||
{ 417, "Expectation Failed" },
|
||||
{ 422, "Unprocessable Entity" },
|
||||
{ 423, "Locked" },
|
||||
{ 424, "Failed Dependency" },
|
||||
{ 426, "Upgrade Required" },
|
||||
{ 428, "Precondition Required" },
|
||||
{ 429, "Too Many Requests" },
|
||||
{ 431, "Request Header Fields Too Large" },
|
||||
{ 500, "Internal Server Error" },
|
||||
{ 501, "Not Implemented" },
|
||||
{ 502, "Bad Gateway" },
|
||||
{ 503, "Service Unavailable" },
|
||||
{ 504, "Gateway Timeout" },
|
||||
{ 505, "HTTP Version Not Supported" },
|
||||
{ 506, "Variant Also Negotiates" },
|
||||
{ 507, "Insufficient Storage" },
|
||||
{ 508, "Loop Detected" },
|
||||
{ 510, "Not Extended" },
|
||||
{ 511, "Network Authentication Required" }
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
728
lib/restbed-4.8/source/corvusoft/restbed/detail/socket_impl.cpp
Normal file
728
lib/restbed-4.8/source/corvusoft/restbed/detail/socket_impl.cpp
Normal file
@@ -0,0 +1,728 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
//System Includes
|
||||
#include <future>
|
||||
#include <ciso646>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/logger.hpp"
|
||||
#include "corvusoft/restbed/detail/socket_impl.hpp"
|
||||
|
||||
//External Includes
|
||||
#include <asio/read.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <asio/connect.hpp>
|
||||
#include <asio/read_until.hpp>
|
||||
|
||||
|
||||
//System Namespaces
|
||||
using std::get;
|
||||
using std::bind;
|
||||
using std::size_t;
|
||||
using std::string;
|
||||
using std::promise;
|
||||
using std::function;
|
||||
using std::to_string;
|
||||
using std::error_code;
|
||||
using std::shared_ptr;
|
||||
using std::make_shared;
|
||||
using std::runtime_error;
|
||||
using std::placeholders::_1;
|
||||
using std::chrono::milliseconds;
|
||||
using std::chrono::steady_clock;
|
||||
|
||||
//Project Namespaces
|
||||
using restbed::detail::SocketImpl;
|
||||
|
||||
//External Namespaces
|
||||
using asio::ip::tcp;
|
||||
using asio::io_service;
|
||||
using asio::steady_timer;
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
using asio::ssl::stream;
|
||||
#endif
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
SocketImpl::SocketImpl( asio::io_context& context, const shared_ptr< tcp::socket >& socket, const shared_ptr< Logger >& logger ) : m_error_handler( nullptr ),
|
||||
m_is_open( socket->is_open( ) ),
|
||||
m_pending_writes( ),
|
||||
m_logger( logger ),
|
||||
m_timeout( 0 ),
|
||||
m_io_service( context ),
|
||||
m_timer( make_shared< asio::steady_timer >( m_io_service ) ),
|
||||
m_strand( make_shared< io_service::strand > ( m_io_service ) ),
|
||||
m_resolver( nullptr ),
|
||||
m_socket( socket )
|
||||
#ifdef BUILD_SSL
|
||||
, m_ssl_socket( nullptr )
|
||||
#endif
|
||||
{
|
||||
return;
|
||||
}
|
||||
#ifdef BUILD_SSL
|
||||
SocketImpl::SocketImpl( asio::io_context& context, const shared_ptr< asio::ssl::stream< tcp::socket > >& socket, const shared_ptr< Logger >& logger ) : m_error_handler( nullptr ),
|
||||
m_is_open( socket->lowest_layer( ).is_open( ) ),
|
||||
m_pending_writes( ),
|
||||
m_logger( logger ),
|
||||
m_timeout( 0 ),
|
||||
m_io_service( context ),
|
||||
m_timer( make_shared< asio::steady_timer >( m_io_service ) ),
|
||||
m_strand( make_shared< io_service::strand > ( m_io_service ) ),
|
||||
m_resolver( nullptr ),
|
||||
m_socket( nullptr ),
|
||||
m_ssl_socket( socket )
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SocketImpl::close( void )
|
||||
{
|
||||
m_is_open = false;
|
||||
|
||||
if ( m_timer not_eq nullptr )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
}
|
||||
|
||||
if ( m_socket not_eq nullptr )
|
||||
{
|
||||
m_socket->close( );
|
||||
}
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_ssl_socket not_eq nullptr )
|
||||
{
|
||||
m_ssl_socket->lowest_layer( ).close( );
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SocketImpl::is_open( void ) const
|
||||
{
|
||||
return m_is_open;
|
||||
}
|
||||
|
||||
bool SocketImpl::is_closed( void ) const
|
||||
{
|
||||
return not m_is_open;
|
||||
}
|
||||
|
||||
void SocketImpl::connect( const string& hostname, const uint16_t port, const function< void ( const error_code& ) >& callback )
|
||||
{
|
||||
m_resolver = make_shared< tcp::resolver >( m_io_service );
|
||||
tcp::resolver::query query( hostname, ::to_string( port ) );
|
||||
|
||||
m_resolver->async_resolve( query, [ this, callback ]( const error_code & error, tcp::resolver::iterator endpoint_iterator )
|
||||
{
|
||||
if ( not error )
|
||||
{
|
||||
#ifdef BUILD_SSL
|
||||
auto& socket = ( m_socket not_eq nullptr ) ? *m_socket : m_ssl_socket->lowest_layer( );
|
||||
#else
|
||||
auto& socket = *m_socket;
|
||||
#endif
|
||||
asio::async_connect( socket, endpoint_iterator, [ this, callback ]( const error_code & error, tcp::resolver::iterator )
|
||||
{
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_ssl_socket not_eq nullptr )
|
||||
{
|
||||
m_ssl_socket->handshake( asio::ssl::stream_base::client );
|
||||
}
|
||||
|
||||
#endif
|
||||
m_is_open = true;
|
||||
callback( error );
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
callback( error );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
void SocketImpl::sleep_for( const milliseconds& delay, const function< void ( const error_code& ) >& callback )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( delay );
|
||||
m_timer->async_wait( callback );
|
||||
}
|
||||
|
||||
void SocketImpl::start_write(const Bytes& data, const std::function< void ( const std::error_code&, std::size_t ) >& callback)
|
||||
{
|
||||
m_strand->post([this, data, callback] { write_helper(data, callback); });
|
||||
}
|
||||
|
||||
size_t SocketImpl::start_read(const shared_ptr< asio::streambuf >& data, const string& delimiter, error_code& error)
|
||||
{
|
||||
return read( data, delimiter,error );
|
||||
}
|
||||
|
||||
size_t SocketImpl::start_read(const shared_ptr< asio::streambuf >& data, const size_t length, error_code& error)
|
||||
{
|
||||
return read( data, length, error );
|
||||
}
|
||||
|
||||
void SocketImpl::start_read( const std::size_t length, const function< void ( const Bytes ) > success, const function< void ( const error_code ) > failure )
|
||||
{
|
||||
m_strand->post([this, length, success, failure] {
|
||||
read(length, success, failure);
|
||||
});
|
||||
}
|
||||
|
||||
void SocketImpl::start_read(const shared_ptr< asio::streambuf >& data, const size_t length, const function< void ( const error_code&, size_t ) >& callback)
|
||||
{
|
||||
m_strand->post([this, data, length, callback]
|
||||
{
|
||||
read(data, length, callback);
|
||||
});
|
||||
}
|
||||
|
||||
void SocketImpl::start_read(const shared_ptr< asio::streambuf >& data, const string& delimiter, const function< void ( const error_code&, size_t ) >& callback)
|
||||
{
|
||||
m_strand->post([this, data, delimiter, callback]
|
||||
{
|
||||
read(data, delimiter, callback);
|
||||
});
|
||||
}
|
||||
|
||||
string SocketImpl::get_local_endpoint( void )
|
||||
{
|
||||
error_code error;
|
||||
tcp::endpoint endpoint;
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_socket not_eq nullptr )
|
||||
{
|
||||
#endif
|
||||
endpoint = m_socket->local_endpoint( error );
|
||||
#ifdef BUILD_SSL
|
||||
}
|
||||
else
|
||||
{
|
||||
endpoint = m_ssl_socket->lowest_layer( ).local_endpoint( error );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
auto address = endpoint.address( );
|
||||
auto local = address.is_v4( ) ? address.to_string( ) + ":" : "[" + address.to_string( ) + "]:";
|
||||
local += ::to_string( endpoint.port( ) );
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
string SocketImpl::get_remote_endpoint( void )
|
||||
{
|
||||
error_code error;
|
||||
tcp::endpoint endpoint;
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_socket not_eq nullptr )
|
||||
{
|
||||
#endif
|
||||
endpoint = m_socket->remote_endpoint( error );
|
||||
#ifdef BUILD_SSL
|
||||
}
|
||||
else
|
||||
{
|
||||
endpoint = m_ssl_socket->lowest_layer( ).remote_endpoint( error );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
auto address = endpoint.address( );
|
||||
auto remote = address.is_v4( ) ? address.to_string( ) + ":" : "[" + address.to_string( ) + "]:";
|
||||
remote += ::to_string( endpoint.port( ) );
|
||||
|
||||
return remote;
|
||||
}
|
||||
|
||||
void SocketImpl::set_timeout( const milliseconds& value )
|
||||
{
|
||||
m_timeout = value;
|
||||
}
|
||||
|
||||
void SocketImpl::set_keep_alive( const uint32_t start, const uint32_t interval, const uint32_t cnt)
|
||||
{
|
||||
(void) cnt;
|
||||
(void) start;
|
||||
(void) interval;
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
auto& socket = ( m_socket not_eq nullptr ) ? *m_socket : m_ssl_socket->lowest_layer( );
|
||||
#else
|
||||
auto& socket = *m_socket;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string val = "1";
|
||||
setsockopt(socket.native_handle(), SOL_SOCKET, SO_KEEPALIVE, val.c_str(), sizeof(val));
|
||||
|
||||
// TCP_KEEPIDLE and TCP_KEEPINTVL are available since Win 10 version 1709
|
||||
// TCP_KEEPCNT since Win 10 version 1703
|
||||
#ifdef TCP_KEEPIDLE
|
||||
std::string start_str = std::to_string(start);
|
||||
setsockopt(socket.native_handle(), IPPROTO_TCP, TCP_KEEPIDLE,
|
||||
start_str.c_str(), sizeof(start_str));
|
||||
#endif
|
||||
#ifdef TCP_KEEPINTVL
|
||||
std::string interval_str = std::to_string(interval);
|
||||
setsockopt(socket.native_handle(), IPPROTO_TCP, TCP_KEEPINTVL,
|
||||
interval_str.c_str(), sizeof(interval_str));
|
||||
#endif
|
||||
#ifdef TCP_KEEPCNT
|
||||
std::string cnt_str = std::to_string(cnt);
|
||||
setsockopt(socket.native_handle(), IPPROTO_TCP, TCP_KEEPCNT,
|
||||
cnt_str.c_str(), sizeof(cnt_str));
|
||||
#endif
|
||||
#else
|
||||
uint32_t val = 1;
|
||||
setsockopt(socket.native_handle(), SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(uint32_t));
|
||||
#ifdef __APPLE__
|
||||
setsockopt(socket.native_handle(), IPPROTO_TCP, TCP_KEEPALIVE, &start, sizeof(uint32_t));
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
setsockopt(socket.native_handle(), IPPROTO_TCP, SO_KEEPALIVE, &start, sizeof(uint32_t));
|
||||
#else
|
||||
// Linux based systems
|
||||
setsockopt(socket.native_handle(), SOL_TCP, TCP_KEEPIDLE, &start, sizeof(uint32_t));
|
||||
setsockopt(socket.native_handle(), SOL_TCP, TCP_KEEPINTVL, &interval, sizeof(uint32_t));
|
||||
setsockopt(socket.native_handle(), SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(uint32_t));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
SocketImpl::SocketImpl( asio::io_context& context ) : m_error_handler( nullptr ),
|
||||
m_is_open( false ),
|
||||
m_pending_writes( ),
|
||||
m_logger( nullptr ),
|
||||
m_timeout( 0 ),
|
||||
m_io_service( context ),
|
||||
m_timer( make_shared< asio::steady_timer >( m_io_service ) ),
|
||||
m_strand( make_shared< io_service::strand > ( m_io_service ) ),
|
||||
m_resolver( nullptr ),
|
||||
m_socket( nullptr )
|
||||
#ifdef BUILD_SSL
|
||||
, m_ssl_socket( nullptr )
|
||||
#endif
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void SocketImpl::connection_timeout_handler( const shared_ptr< SocketImpl > socket, const error_code& error )
|
||||
{
|
||||
if ( error or socket == nullptr or socket->m_timer->expires_at( ) > steady_clock::now( ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
socket->close( );
|
||||
|
||||
if ( m_error_handler not_eq nullptr )
|
||||
{
|
||||
m_error_handler( 408, runtime_error( "The socket timed out waiting for the request." ), nullptr );
|
||||
}
|
||||
}
|
||||
|
||||
void SocketImpl::write( void )
|
||||
{
|
||||
if(m_is_open)
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &SocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
#ifdef BUILD_SSL
|
||||
if ( m_socket not_eq nullptr )
|
||||
{
|
||||
#endif
|
||||
asio::async_write( *m_socket, asio::buffer( get<0>(m_pending_writes.front()).data( ), get<0>(m_pending_writes.front()).size( ) ), m_strand->wrap( [ this ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
auto callback = get<2>(m_pending_writes.front());
|
||||
auto & retries = get<1>(m_pending_writes.front());
|
||||
auto & buffer = get<0>(m_pending_writes.front());
|
||||
if(length < buffer.size() && retries < MAX_WRITE_RETRIES && error not_eq asio::error::operation_aborted)
|
||||
{
|
||||
++retries;
|
||||
buffer.erase(buffer.begin(),buffer.begin() + length);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pending_writes.pop();
|
||||
}
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
if(!m_pending_writes.empty())
|
||||
{
|
||||
write();
|
||||
}
|
||||
} ) );
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
}
|
||||
else
|
||||
{
|
||||
asio::async_write(*m_ssl_socket, asio::buffer( get<0>(m_pending_writes.front()).data( ), get<0>(m_pending_writes.front()).size( ) ), m_strand->wrap( [ this ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
auto callback = get<2>(m_pending_writes.front());
|
||||
auto & retries = get<1>(m_pending_writes.front());
|
||||
auto & buffer = get<0>(m_pending_writes.front());
|
||||
if(length < buffer.size() && retries < MAX_WRITE_RETRIES && error not_eq asio::error::operation_aborted)
|
||||
{
|
||||
++retries;
|
||||
buffer.erase(buffer.begin(),buffer.begin() + length);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pending_writes.pop();
|
||||
}
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
if(!m_pending_writes.empty())
|
||||
{
|
||||
write();
|
||||
}
|
||||
} ) );
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
while(!m_pending_writes.empty())
|
||||
{
|
||||
m_pending_writes.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SocketImpl::write( const Bytes& data, const function< void ( const error_code&, size_t ) >& callback )
|
||||
{
|
||||
const auto buffer = make_shared< Bytes >( data );
|
||||
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &SocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_socket not_eq nullptr )
|
||||
{
|
||||
#endif
|
||||
asio::async_write( *m_socket, asio::buffer( buffer->data( ), buffer->size( ) ), m_strand->wrap( [ this, callback, buffer ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
} ) );
|
||||
#ifdef BUILD_SSL
|
||||
}
|
||||
else
|
||||
{
|
||||
asio::async_write( *m_ssl_socket, asio::buffer( buffer->data( ), buffer->size( ) ), m_strand->wrap( [ this, callback, buffer ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
} ) );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SocketImpl::write_helper(const Bytes& data, const function< void ( const error_code&, size_t ) >& callback)
|
||||
{
|
||||
const uint8_t retries = 0;
|
||||
m_pending_writes.push(make_tuple(data, retries, callback));
|
||||
if(m_pending_writes.size() == 1)
|
||||
{
|
||||
write();
|
||||
}
|
||||
}
|
||||
|
||||
size_t SocketImpl::read( const shared_ptr< asio::streambuf >& data, const size_t length, error_code& error )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &SocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
|
||||
size_t size = 0;
|
||||
auto finished = std::make_shared<bool>(false);
|
||||
auto sharedError = std::make_shared<error_code>();
|
||||
auto sharedSize = std::make_shared<size_t>(0);
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_socket not_eq nullptr )
|
||||
{
|
||||
#endif
|
||||
asio::async_read( *m_socket, *data, asio::transfer_at_least( length ),
|
||||
[ finished, sharedSize, sharedError ]( const error_code & error, size_t size ) {
|
||||
*sharedError = error;
|
||||
*sharedSize = size;
|
||||
*finished = true;
|
||||
});
|
||||
#ifdef BUILD_SSL
|
||||
}
|
||||
else
|
||||
{
|
||||
asio::async_read( *m_ssl_socket, *data, asio::transfer_at_least( length ),
|
||||
[ finished, sharedSize, sharedError ]( const error_code & error, size_t size ) {
|
||||
*sharedError = error;
|
||||
*sharedSize = size;
|
||||
*finished = true;
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
while (!*finished)
|
||||
m_io_service.run_one();
|
||||
error = *sharedError;
|
||||
size = *sharedSize;
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void SocketImpl::read( const std::size_t length, const function< void ( const Bytes ) > success, const function< void ( const error_code ) > failure )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &SocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_socket not_eq nullptr )
|
||||
{
|
||||
#endif
|
||||
auto data = make_shared< asio::streambuf >( );
|
||||
asio::async_read( *m_socket, *data, asio::transfer_exactly( length ), [ this, data, success, failure ]( const error_code code, const size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( code )
|
||||
{
|
||||
m_is_open = false;
|
||||
failure( code );
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto data_ptr = asio::buffer_cast< const Byte* >( data->data( ) );
|
||||
success( Bytes( data_ptr, data_ptr + length ) );
|
||||
}
|
||||
} );
|
||||
#ifdef BUILD_SSL
|
||||
}
|
||||
else
|
||||
{
|
||||
auto data = make_shared< asio::streambuf >( );
|
||||
asio::async_read( *m_ssl_socket, *data, asio::transfer_exactly( length ), [ this, data, success, failure ]( const error_code code, const size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( code )
|
||||
{
|
||||
m_is_open = false;
|
||||
failure( code );
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto data_ptr = asio::buffer_cast< const Byte* >( data->data( ) );
|
||||
success( Bytes( data_ptr, data_ptr + length ) );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void SocketImpl::read( const shared_ptr< asio::streambuf >& data, const size_t length, const function< void ( const error_code&, size_t ) >& callback )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &SocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_socket not_eq nullptr )
|
||||
{
|
||||
#endif
|
||||
asio::async_read( *m_socket, *data, asio::transfer_at_least( length ), m_strand->wrap( [ this, callback ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
} ) );
|
||||
#ifdef BUILD_SSL
|
||||
}
|
||||
else
|
||||
{
|
||||
asio::async_read( *m_ssl_socket, *data, asio::transfer_at_least( length ), m_strand->wrap( [ this, callback ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
} ) );
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t SocketImpl::read( const shared_ptr< asio::streambuf >& data, const string& delimiter, error_code& error )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( bind( &SocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) );
|
||||
|
||||
size_t length = 0;
|
||||
auto finished = std::make_shared<bool>(false);
|
||||
auto sharedError = std::make_shared<error_code>();
|
||||
auto sharedLength = std::make_shared<size_t>(0);
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_socket not_eq nullptr )
|
||||
{
|
||||
#endif
|
||||
asio::async_read_until( *m_socket, *data, delimiter,
|
||||
[ finished, sharedLength, sharedError ]( const error_code & error, size_t length ) {
|
||||
*sharedError = error;
|
||||
*sharedLength = length;
|
||||
*finished = true;
|
||||
});
|
||||
#ifdef BUILD_SSL
|
||||
}
|
||||
else
|
||||
{
|
||||
asio::async_read_until( *m_ssl_socket, *data, delimiter,
|
||||
[ finished, sharedLength, sharedError ]( const error_code & error, size_t length ) {
|
||||
*sharedError = error;
|
||||
*sharedLength = length;
|
||||
*finished = true;
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
while (!*finished)
|
||||
m_io_service.run_one();
|
||||
error = *sharedError;
|
||||
length = *sharedLength;
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void SocketImpl::read( const shared_ptr< asio::streambuf >& data, const string& delimiter, const function< void ( const error_code&, size_t ) >& callback )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
m_timer->expires_from_now( m_timeout );
|
||||
m_timer->async_wait( m_strand->wrap( bind( &SocketImpl::connection_timeout_handler, this, shared_from_this( ), _1 ) ) );
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
|
||||
if ( m_socket not_eq nullptr )
|
||||
{
|
||||
#endif
|
||||
asio::async_read_until( *m_socket, *data, delimiter, m_strand->wrap( [ this, callback ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
} ) );
|
||||
#ifdef BUILD_SSL
|
||||
}
|
||||
else
|
||||
{
|
||||
asio::async_read_until( *m_ssl_socket, *data, delimiter, m_strand->wrap( [ this, callback ]( const error_code & error, size_t length )
|
||||
{
|
||||
m_timer->cancel( );
|
||||
|
||||
if ( error )
|
||||
{
|
||||
m_is_open = false;
|
||||
}
|
||||
|
||||
if ( error not_eq asio::error::operation_aborted )
|
||||
{
|
||||
callback( error, length );
|
||||
}
|
||||
} ) );
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
178
lib/restbed-4.8/source/corvusoft/restbed/detail/socket_impl.hpp
Normal file
178
lib/restbed-4.8/source/corvusoft/restbed/detail/socket_impl.hpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <queue>
|
||||
#include <tuple>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <functional>
|
||||
#include <system_error>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/byte.hpp"
|
||||
|
||||
//External Includes
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/streambuf.hpp>
|
||||
#include <asio/steady_timer.hpp>
|
||||
#include <asio/io_service.hpp>
|
||||
#include <asio/io_service_strand.hpp>
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
#include <asio/ssl.hpp>
|
||||
#endif
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
class Logger;
|
||||
class Session;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
class SocketImpl : public std::enable_shared_from_this<SocketImpl>
|
||||
{
|
||||
public:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
SocketImpl( asio::io_context& context, const std::shared_ptr< asio::ip::tcp::socket >& socket, const std::shared_ptr< Logger >& logger = nullptr );
|
||||
#ifdef BUILD_SSL
|
||||
SocketImpl( asio::io_context& context, const std::shared_ptr< asio::ssl::stream< asio::ip::tcp::socket > >& socket, const std::shared_ptr< Logger >& logger = nullptr );
|
||||
#endif
|
||||
~SocketImpl( void ) = default;
|
||||
|
||||
//Functionality
|
||||
virtual void close( void );
|
||||
|
||||
virtual bool is_open( void ) const;
|
||||
|
||||
virtual bool is_closed( void ) const;
|
||||
|
||||
virtual void connect( const std::string& hostname, const uint16_t port, const std::function< void ( const std::error_code& ) >& callback );
|
||||
|
||||
virtual void sleep_for( const std::chrono::milliseconds& delay, const std::function< void ( const std::error_code& ) >& callback );
|
||||
|
||||
virtual void start_write(const Bytes& data, const std::function< void ( const std::error_code&, std::size_t ) >& callback);
|
||||
|
||||
virtual size_t start_read( const std::shared_ptr< asio::streambuf >& data, const std::string& delimiter, std::error_code& error );
|
||||
|
||||
virtual size_t start_read( const std::shared_ptr< asio::streambuf >& data, const std::size_t length, std::error_code& error );
|
||||
|
||||
virtual void start_read(const std::size_t length, const std::function< void ( const Bytes ) > success, const std::function< void ( const std::error_code ) > failure );
|
||||
|
||||
virtual void start_read( const std::shared_ptr< asio::streambuf >& data, const std::size_t length, const std::function< void ( const std::error_code&, std::size_t ) >& callback );
|
||||
|
||||
virtual void start_read(const std::shared_ptr< asio::streambuf >& data, const std::string& delimiter, const std::function< void ( const std::error_code&, std::size_t ) >& callback );
|
||||
|
||||
//Getters
|
||||
virtual std::string get_local_endpoint( void );
|
||||
|
||||
virtual std::string get_remote_endpoint( void );
|
||||
|
||||
//Setters
|
||||
virtual void set_timeout( const std::chrono::milliseconds& value );
|
||||
|
||||
virtual void set_keep_alive( const uint32_t start, const uint32_t interval, const uint32_t cnt);
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > m_error_handler;
|
||||
|
||||
protected:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
SocketImpl( asio::io_context& context );
|
||||
|
||||
//Functionality
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
|
||||
private:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
SocketImpl( const SocketImpl& original ) = delete;
|
||||
|
||||
//Functionality
|
||||
void connection_timeout_handler( const std::shared_ptr< SocketImpl > socket, const std::error_code& error );
|
||||
|
||||
void write( void );
|
||||
|
||||
void write( const Bytes& data, const std::function< void ( const std::error_code&, std::size_t ) >& callback );
|
||||
|
||||
void write_helper( const Bytes& data, const std::function< void ( const std::error_code&, std::size_t ) >& callback );
|
||||
|
||||
size_t read( const std::shared_ptr< asio::streambuf >& data, const std::size_t length, std::error_code& error );
|
||||
|
||||
void read( const std::size_t length, const std::function< void ( const Bytes ) > success, const std::function< void ( const std::error_code ) > failure );
|
||||
|
||||
void read( const std::shared_ptr< asio::streambuf >& data, const std::size_t length, const std::function< void ( const std::error_code&, std::size_t ) >& callback );
|
||||
|
||||
size_t read( const std::shared_ptr< asio::streambuf >& data, const std::string& delimiter, std::error_code& error );
|
||||
|
||||
void read( const std::shared_ptr< asio::streambuf >& data, const std::string& delimiter, const std::function< void ( const std::error_code&, std::size_t ) >& callback );
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
SocketImpl& operator =( const SocketImpl& value ) = delete;
|
||||
|
||||
//Properties
|
||||
bool m_is_open;
|
||||
|
||||
const uint8_t MAX_WRITE_RETRIES = 5;
|
||||
|
||||
std::queue< std::tuple< Bytes, uint8_t, std::function< void ( const std::error_code&, std::size_t ) > > > m_pending_writes;
|
||||
|
||||
std::shared_ptr< Logger > m_logger;
|
||||
|
||||
std::chrono::milliseconds m_timeout;
|
||||
|
||||
asio::io_context &m_io_service;
|
||||
|
||||
std::shared_ptr< asio::steady_timer > m_timer;
|
||||
|
||||
std::shared_ptr< asio::io_service::strand > m_strand;
|
||||
|
||||
std::shared_ptr< asio::ip::tcp::resolver > m_resolver;
|
||||
|
||||
std::shared_ptr< asio::ip::tcp::socket > m_socket;
|
||||
|
||||
#ifdef BUILD_SSL
|
||||
std::shared_ptr< asio::ssl::stream< asio::ip::tcp::socket > > m_ssl_socket;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
//Project Includes
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
struct SSLSettingsImpl
|
||||
{
|
||||
uint16_t m_port = 443;
|
||||
|
||||
bool m_http_disabled = false;
|
||||
|
||||
bool m_sslv2_enabled = true;
|
||||
|
||||
bool m_sslv3_enabled = true;
|
||||
|
||||
bool m_tlsv1_enabled = true;
|
||||
|
||||
bool m_tlsv11_enabled = true;
|
||||
|
||||
bool m_tlsv12_enabled = true;
|
||||
|
||||
bool m_compression_enabled = true;
|
||||
|
||||
bool m_default_workarounds_enabled = true;
|
||||
|
||||
bool m_single_diffie_hellman_use_enabled = true;
|
||||
|
||||
std::string m_bind_address = "";
|
||||
|
||||
std::string m_passphrase = "";
|
||||
|
||||
std::string m_private_key = "";
|
||||
|
||||
std::string m_private_rsa_key = "";
|
||||
|
||||
std::string m_certificate = "";
|
||||
|
||||
std::string m_certificate_chain = "";
|
||||
|
||||
std::string m_certificate_authority_pool = "";
|
||||
|
||||
std::string m_temporary_diffie_hellman = "";
|
||||
};
|
||||
}
|
||||
}
|
||||
35
lib/restbed-4.8/source/corvusoft/restbed/detail/uri_impl.hpp
Normal file
35
lib/restbed-4.8/source/corvusoft/restbed/detail/uri_impl.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <string>
|
||||
|
||||
//Project Includes
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
struct UriImpl
|
||||
{
|
||||
std::string m_uri = "";
|
||||
|
||||
bool m_relative = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
//System Includes
|
||||
#include <ciso646>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/web_socket.hpp"
|
||||
#include "corvusoft/restbed/web_socket_message.hpp"
|
||||
#include "corvusoft/restbed/detail/socket_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/web_socket_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/web_socket_manager_impl.hpp"
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
using std::bind;
|
||||
using std::string;
|
||||
using std::shared_ptr;
|
||||
using std::error_code;
|
||||
using std::placeholders::_1;
|
||||
|
||||
//Project Namespaces
|
||||
using restbed::detail::SocketImpl;
|
||||
using restbed::detail::WebSocketImpl;
|
||||
using restbed::detail::WebSocketManagerImpl;
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
WebSocketImpl::WebSocketImpl( void )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WebSocketImpl::~WebSocketImpl( void )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void WebSocketImpl::log( const Logger::Level level, const string& message ) const
|
||||
{
|
||||
if ( m_logger not_eq nullptr )
|
||||
{
|
||||
try
|
||||
{
|
||||
m_logger->log( level, "%s", message.data( ) );
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
fprintf( stderr, "Failed to create log entry: %s", message.data( ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketImpl::listen( const shared_ptr< WebSocket > socket )
|
||||
{
|
||||
m_socket->start_read( 2, bind( &WebSocketImpl::parse_flags, this, _1, socket ), [ this, socket ]( const error_code code )
|
||||
{
|
||||
if ( m_error_handler not_eq nullptr )
|
||||
{
|
||||
m_error_handler( socket, code );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
void WebSocketImpl::parse_flags( const Bytes data, const shared_ptr< WebSocket > socket )
|
||||
{
|
||||
auto message = m_manager->parse( data );
|
||||
|
||||
auto length = message->get_length( );
|
||||
|
||||
if ( length == 126 )
|
||||
{
|
||||
length = 2;
|
||||
}
|
||||
else if ( length == 127 )
|
||||
{
|
||||
length = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 0;
|
||||
}
|
||||
|
||||
if ( message->get_mask_flag( ) == true )
|
||||
{
|
||||
length += 4;
|
||||
}
|
||||
|
||||
m_socket->start_read( length, bind( &WebSocketImpl::parse_length_and_mask, this, _1, data, socket ), [ this, socket ]( const error_code code )
|
||||
{
|
||||
if ( m_error_handler not_eq nullptr )
|
||||
{
|
||||
m_error_handler( socket, code );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
void WebSocketImpl::parse_payload( const Bytes data, Bytes packet, const shared_ptr< WebSocket > socket )
|
||||
{
|
||||
packet.insert( packet.end( ), data.begin( ), data.end( ) );
|
||||
auto message = m_manager->parse( packet );
|
||||
|
||||
if ( m_message_handler not_eq nullptr )
|
||||
{
|
||||
m_message_handler( socket, message );
|
||||
}
|
||||
|
||||
listen( socket );
|
||||
}
|
||||
|
||||
void WebSocketImpl::parse_length_and_mask( const Bytes data, Bytes packet, const shared_ptr< WebSocket > socket )
|
||||
{
|
||||
packet.insert( packet.end( ), data.begin( ), data.end( ) );
|
||||
auto message = m_manager->parse( packet );
|
||||
|
||||
auto length = message->get_extended_length( );
|
||||
|
||||
if ( length == 0 )
|
||||
{
|
||||
length = message->get_length( );
|
||||
}
|
||||
|
||||
m_socket->start_read( length, bind( &WebSocketImpl::parse_payload, this, _1, packet, socket ), [ this, socket ]( const error_code code )
|
||||
{
|
||||
if ( m_error_handler not_eq nullptr )
|
||||
{
|
||||
m_error_handler( socket, code );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/byte.hpp"
|
||||
#include "corvusoft/restbed/logger.hpp"
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
class WebSocket;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
class SocketImpl;
|
||||
class WebSocketManagerImpl;
|
||||
|
||||
class WebSocketImpl
|
||||
{
|
||||
public:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
WebSocketImpl( void );
|
||||
|
||||
virtual ~WebSocketImpl( void );
|
||||
|
||||
//Functionality
|
||||
void log( const Logger::Level level, const std::string& message ) const;
|
||||
|
||||
void listen( const std::shared_ptr< WebSocket > socket );
|
||||
|
||||
void parse_flags( const Bytes data, const std::shared_ptr< WebSocket > socket );
|
||||
|
||||
void parse_payload( const Bytes data, Bytes packet, const std::shared_ptr< WebSocket > socket );
|
||||
|
||||
void parse_length_and_mask( const Bytes data, Bytes packet, const std::shared_ptr< WebSocket > socket );
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
std::string m_key = "";
|
||||
|
||||
bool m_error_handler_invoked = false;
|
||||
|
||||
std::shared_ptr< Logger > m_logger = nullptr;
|
||||
|
||||
std::shared_ptr< SocketImpl > m_socket = nullptr;
|
||||
|
||||
std::shared_ptr< WebSocketManagerImpl > m_manager = nullptr;
|
||||
|
||||
std::function< void ( const std::shared_ptr< WebSocket > ) > m_open_handler = nullptr;
|
||||
|
||||
std::function< void ( const std::shared_ptr< WebSocket > ) > m_close_handler = nullptr;
|
||||
|
||||
std::function< void ( const std::shared_ptr< WebSocket >, const std::error_code ) > m_error_handler = nullptr;
|
||||
|
||||
std::function< void ( const std::shared_ptr< WebSocket >, const std::shared_ptr< WebSocketMessage > ) > m_message_handler = nullptr;
|
||||
|
||||
protected:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
|
||||
//Functionality
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
|
||||
private:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
WebSocketImpl( const WebSocketImpl& original ) = delete;
|
||||
|
||||
//Functionality
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
WebSocketImpl& operator =( const WebSocketImpl& value ) = delete;
|
||||
|
||||
//Properties
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
//System Includes
|
||||
#include <tuple>
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <ciso646>
|
||||
#include <system_error>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/logger.hpp"
|
||||
#include "corvusoft/restbed/session.hpp"
|
||||
#include "corvusoft/restbed/request.hpp"
|
||||
#include "corvusoft/restbed/web_socket.hpp"
|
||||
#include "corvusoft/restbed/web_socket_message.hpp"
|
||||
#include "corvusoft/restbed/detail/socket_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/request_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/session_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/web_socket_impl.hpp"
|
||||
#include "corvusoft/restbed/detail/web_socket_manager_impl.hpp"
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
using std::get;
|
||||
using std::tuple;
|
||||
using std::string;
|
||||
using std::mt19937;
|
||||
using std::function;
|
||||
using std::to_string;
|
||||
using std::shared_ptr;
|
||||
using std::error_code;
|
||||
using std::make_shared;
|
||||
using std::random_device;
|
||||
using std::placeholders::_1;
|
||||
using std::uniform_int_distribution;
|
||||
|
||||
//Project Namespaces
|
||||
using restbed::detail::WebSocketManagerImpl;
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
WebSocketManagerImpl::WebSocketManagerImpl( void ) : m_logger( nullptr ),
|
||||
m_sockets( )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
shared_ptr< WebSocketMessage > WebSocketManagerImpl::parse( const Bytes& packet )
|
||||
{
|
||||
if ( packet.empty( ) )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Byte byte = packet[ 0 ];
|
||||
|
||||
auto message = make_shared< WebSocketMessage >( );
|
||||
message->set_final_frame_flag( ( byte & 0x80 ) ? true : false );
|
||||
message->set_reserved_flags( ( byte & 0x40 ) ? true : false,
|
||||
( byte & 0x20 ) ? true : false,
|
||||
( byte & 0x10 ) ? true : false );
|
||||
message->set_opcode( static_cast< WebSocketMessage::OpCode >( byte & 0x0F ) );
|
||||
|
||||
const auto packet_length = packet.size( );
|
||||
|
||||
if ( packet_length == 1 )
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
byte = packet[ 1 ];
|
||||
message->set_mask_flag( ( byte & 0x80 ) ? true : false );
|
||||
message->set_length( byte & 0x7F );
|
||||
|
||||
if ( packet_length == 2 )
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
size_t offset = 2;
|
||||
uint64_t length = message->get_length( );
|
||||
|
||||
if ( length == 126 )
|
||||
{
|
||||
if ( ( packet_length - ( offset + 1 ) ) <= 0 )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
length = packet[ offset++ ] << 8;
|
||||
length |= packet[ offset++ ] ;
|
||||
|
||||
message->set_extended_length( length );
|
||||
}
|
||||
else if ( length == 127 )
|
||||
{
|
||||
if ( ( packet_length - ( offset + 3 ) ) <= 0 )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
length |= packet[ offset++ ] << 24;
|
||||
length |= packet[ offset++ ] << 16;
|
||||
length |= packet[ offset++ ] << 8;
|
||||
length = packet[ offset++ ] ;
|
||||
|
||||
message->set_extended_length( length );
|
||||
}
|
||||
|
||||
if ( message->get_mask_flag( ) == true )
|
||||
{
|
||||
if ( ( packet_length - ( offset + 3 ) ) <= 0 )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t mask = packet[ offset++ ] << 24;
|
||||
mask |= packet[ offset++ ] << 16;
|
||||
mask |= packet[ offset++ ] << 8;
|
||||
mask |= packet[ offset++ ] ;
|
||||
|
||||
message->set_mask( mask );
|
||||
}
|
||||
|
||||
Bytes payload( packet.begin( ) + offset, packet.end( ) );
|
||||
|
||||
if ( message->get_mask_flag( ) == true )
|
||||
{
|
||||
auto masking_key = message->get_mask( );
|
||||
|
||||
Byte mask[ 4 ] = { };
|
||||
mask[ 0 ] = ( masking_key >> 24 ) & 0xFF;
|
||||
mask[ 1 ] = ( masking_key >> 16 ) & 0xFF;
|
||||
mask[ 2 ] = ( masking_key >> 8 ) & 0xFF;
|
||||
mask[ 3 ] = masking_key & 0xFF;
|
||||
|
||||
for ( size_t index = 0; index < payload.size( ); index++ )
|
||||
{
|
||||
payload[ index ] ^= mask[ index % 4 ];
|
||||
}
|
||||
}
|
||||
|
||||
message->set_data( payload );
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
Bytes WebSocketManagerImpl::compose( const shared_ptr< WebSocketMessage >& message )
|
||||
{
|
||||
Byte byte = 0x80;
|
||||
|
||||
if ( message->get_final_frame_flag( ) == false )
|
||||
{
|
||||
byte = 0x00;
|
||||
}
|
||||
|
||||
auto reserved_flags = message->get_reserved_flags( );
|
||||
|
||||
if ( get< 0 >( reserved_flags ) )
|
||||
{
|
||||
byte |= 0x40;
|
||||
}
|
||||
|
||||
if ( get< 1 >( reserved_flags ) )
|
||||
{
|
||||
byte |= 0x20;
|
||||
}
|
||||
|
||||
if ( get< 2 >( reserved_flags ) )
|
||||
{
|
||||
byte |= 0x10;
|
||||
}
|
||||
|
||||
byte |= ( message->get_opcode( ) & 0x0F );
|
||||
|
||||
Bytes frame = { byte };
|
||||
|
||||
auto length = message->get_length( );
|
||||
auto mask_flag = message->get_mask_flag( );
|
||||
|
||||
if ( length == 126 )
|
||||
{
|
||||
auto extended_length = message->get_extended_length( );
|
||||
frame.push_back( ( mask_flag ) ? 254 : 126 );
|
||||
frame.push_back( ( extended_length >> 8 ) & 0xFF );
|
||||
frame.push_back( extended_length & 0xFF );
|
||||
}
|
||||
else if ( length == 127 )
|
||||
{
|
||||
auto extended_length = message->get_extended_length( );
|
||||
frame.push_back( ( mask_flag ) ? 255 : 127 );
|
||||
frame.push_back( ( extended_length >> 56 ) & 0xFF );
|
||||
frame.push_back( ( extended_length >> 48 ) & 0xFF );
|
||||
frame.push_back( ( extended_length >> 40 ) & 0xFF );
|
||||
frame.push_back( ( extended_length >> 32 ) & 0xFF );
|
||||
frame.push_back( ( extended_length >> 24 ) & 0xFF );
|
||||
frame.push_back( ( extended_length >> 16 ) & 0xFF );
|
||||
frame.push_back( ( extended_length >> 8 ) & 0xFF );
|
||||
frame.push_back( extended_length & 0xFF );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( mask_flag )
|
||||
{
|
||||
length |= 0x80;
|
||||
}
|
||||
else
|
||||
{
|
||||
length &= ~0x80;
|
||||
}
|
||||
|
||||
frame.push_back( length );
|
||||
}
|
||||
|
||||
if ( mask_flag )
|
||||
{
|
||||
auto masking_key = message->get_mask( );
|
||||
|
||||
uint8_t mask[ 4 ] = { };
|
||||
mask[ 0 ] = masking_key & 0xFF;
|
||||
mask[ 1 ] = ( masking_key >> 8 ) & 0xFF;
|
||||
mask[ 2 ] = ( masking_key >> 16 ) & 0xFF;
|
||||
mask[ 3 ] = ( masking_key >> 24 ) & 0xFF;
|
||||
|
||||
frame.push_back( mask[ 3 ] );
|
||||
frame.push_back( mask[ 2 ] );
|
||||
frame.push_back( mask[ 1 ] );
|
||||
frame.push_back( mask[ 0 ] );
|
||||
|
||||
auto data = message->get_data( );
|
||||
|
||||
for ( size_t index = 0; index < data.size( ); index++ )
|
||||
{
|
||||
auto datum = data.at( index );
|
||||
datum ^= mask[ index % 4 ];
|
||||
frame.push_back( datum );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto data = message->get_data( );
|
||||
frame.insert( frame.end( ), data.begin( ), data.end( ) );
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
shared_ptr< WebSocket > WebSocketManagerImpl::create( const std::shared_ptr< Session >& session )
|
||||
{
|
||||
if ( session == nullptr )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static auto& charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
thread_local static mt19937 generator{ random_device{ }( ) };
|
||||
thread_local static uniform_int_distribution< string::size_type > pick( 0, sizeof( charset ) - 2 );
|
||||
|
||||
string key;
|
||||
auto length = 16;
|
||||
key.reserve( length );
|
||||
while( length-- ) key += charset[ pick( generator ) ];
|
||||
|
||||
auto socket = make_shared< WebSocket >( );
|
||||
socket->set_key( key );
|
||||
socket->set_logger( m_logger );
|
||||
socket->set_socket( session->m_pimpl->m_request->m_pimpl->m_socket );
|
||||
socket->m_pimpl->m_manager = shared_from_this( );
|
||||
|
||||
m_sockets.insert( make_pair( key, socket ) );
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
shared_ptr< WebSocket > WebSocketManagerImpl::read( const string& key )
|
||||
{
|
||||
auto socket = m_sockets.find( key );
|
||||
return ( socket not_eq m_sockets.end( ) ) ? socket->second : nullptr;
|
||||
}
|
||||
|
||||
shared_ptr< WebSocket > WebSocketManagerImpl::update( const shared_ptr< WebSocket >& socket )
|
||||
{
|
||||
return socket;
|
||||
}
|
||||
|
||||
void WebSocketManagerImpl::destroy( const shared_ptr< WebSocket >& socket )
|
||||
{
|
||||
if ( socket == nullptr )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_sockets.erase( socket->get_key( ) );
|
||||
}
|
||||
|
||||
shared_ptr< Logger > WebSocketManagerImpl::get_logger( void ) const
|
||||
{
|
||||
return m_logger;
|
||||
}
|
||||
|
||||
void WebSocketManagerImpl::set_logger( const shared_ptr< Logger >& value )
|
||||
{
|
||||
m_logger = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
//Project Includes
|
||||
#include "corvusoft/restbed/byte.hpp"
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
class Logger;
|
||||
class Session;
|
||||
class WebSocket;
|
||||
class WebSocketMessage;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
class SocketImpl;
|
||||
|
||||
class WebSocketManagerImpl : public std::enable_shared_from_this< WebSocketManagerImpl >
|
||||
{
|
||||
public:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
WebSocketManagerImpl( void );
|
||||
|
||||
~WebSocketManagerImpl( void ) = default;
|
||||
|
||||
//Functionality
|
||||
std::shared_ptr< WebSocketMessage > parse( const Bytes& packet );
|
||||
|
||||
Bytes compose( const std::shared_ptr< WebSocketMessage >& message );
|
||||
|
||||
std::shared_ptr< WebSocket > create( const std::shared_ptr< Session >& session );
|
||||
|
||||
std::shared_ptr< WebSocket > read( const std::string& key );
|
||||
|
||||
std::shared_ptr< WebSocket > update( const std::shared_ptr< WebSocket >& socket );
|
||||
|
||||
void destroy( const std::shared_ptr< WebSocket >& socket );
|
||||
|
||||
//Getters
|
||||
std::shared_ptr< Logger > get_logger( void ) const;
|
||||
|
||||
//Setters
|
||||
void set_logger( const std::shared_ptr< Logger >& value );
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
|
||||
protected:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
|
||||
//Functionality
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
|
||||
//Properties
|
||||
|
||||
private:
|
||||
//Friends
|
||||
|
||||
//Definitions
|
||||
|
||||
//Constructors
|
||||
WebSocketManagerImpl( const WebSocketManagerImpl& original ) = delete;
|
||||
|
||||
//Functionality
|
||||
|
||||
//Getters
|
||||
|
||||
//Setters
|
||||
|
||||
//Operators
|
||||
WebSocketManagerImpl& operator =( const WebSocketManagerImpl& value ) = delete;
|
||||
|
||||
//Properties
|
||||
std::shared_ptr< Logger > m_logger;
|
||||
|
||||
std::map< std::string, std::shared_ptr< WebSocket > > m_sockets;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//System Includes
|
||||
#include <cstdint>
|
||||
|
||||
//Project Includes
|
||||
#include <corvusoft/restbed/byte.hpp>
|
||||
#include <corvusoft/restbed/web_socket.hpp>
|
||||
|
||||
//External Includes
|
||||
|
||||
//System Namespaces
|
||||
|
||||
//Project Namespaces
|
||||
|
||||
//External Namespaces
|
||||
|
||||
namespace restbed
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//Forward Declarations
|
||||
|
||||
struct WebSocketMessageImpl
|
||||
{
|
||||
Bytes m_data = { };
|
||||
|
||||
std::uint32_t m_mask = 0;
|
||||
|
||||
std::uint8_t m_length = 0;
|
||||
|
||||
std::uint64_t m_extended_length = 0;
|
||||
|
||||
bool m_mask_flag = false;
|
||||
|
||||
bool m_final_frame_flag = true;
|
||||
|
||||
bool m_reserved_flag_one = false;
|
||||
|
||||
bool m_reserved_flag_two = false;
|
||||
|
||||
bool m_reserved_flag_three = false;
|
||||
|
||||
WebSocketMessage::OpCode m_opcode = WebSocketMessage::OpCode::BINARY_FRAME;
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user