Rewrote the server in cpp with the frontend in svelte

This commit is contained in:
2023-10-20 13:02:21 +02:00
commit 03b22ebb61
4168 changed files with 831370 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <vector>
#include <cstdint>
//Project Includes
//External Includes
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
typedef uint8_t Byte;
typedef std::vector< Byte > Bytes;
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <string>
#include <utility>
#include <sstream>
#include <algorithm>
#include <type_traits>
//Project Includes
#include <corvusoft/restbed/string.hpp>
//External Includes
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class Common
{
public:
//Friends
//Definitions
//Constructors
//Functionality
template< typename Type >
static Type parse_parameter( const std::string& value, const Type default_value )
{
std::istringstream stream( value );
Type parameter;
stream >> parameter;
if ( stream.fail( ) )
{
return default_value;
}
return parameter;
}
template< typename Type >
static bool has_parameter( const std::string& name, const Type& parameters )
{
const auto key = String::lowercase( name );
const auto iterator = std::find_if( parameters.begin( ), parameters.end( ), [ &key ]( const std::pair< std::string, std::string >& value )
{
return ( key == String::lowercase( value.first ) );
} );
return iterator not_eq parameters.end( );
}
static std::string transform( const std::string& value, const std::function< std::string ( const std::string& ) >& transform )
{
return ( transform == nullptr ) ? value : transform( value );
}
//Getters
template< typename Type >
static Type get_parameters( const std::string& name, const Type& parameters )
{
if ( name.empty( ) )
{
return parameters;
}
const auto key = String::lowercase( name );
Type results;
for ( const auto& parameter : parameters )
{
if ( key == String::lowercase( parameter.first ) )
{
results.insert( parameter );
}
}
return results;
}
//Setters
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
Common( void ) = delete;
Common( const Common& original ) = delete;
virtual ~Common( void ) = delete;
//Functionality
//Getters
//Setters
//Operators
Common& operator =( const Common& value ) = delete;
//Properties
};
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <typeinfo>
//Project Includes
#include <corvusoft/restbed/context_placeholder_base.hpp>
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define CONTEXT_PLACEHOLDER_EXPORT __declspec(dllexport)
#else
#define CONTEXT_PLACEHOLDER_EXPORT __declspec(dllimport)
#endif
#else
#define CONTEXT_PLACEHOLDER_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
template< typename Type >
class CONTEXT_PLACEHOLDER_EXPORT ContextPlaceholder : public ContextPlaceholderBase
{
public:
//Friends
//Definitions
//Constructor
ContextPlaceholder( const Type& value ) : ContextPlaceholderBase( ),
m_value( value )
{
return;
}
virtual ~ContextPlaceholder( void )
{
return;
}
//Functionality
//Getters
const std::type_info& type( void ) const
{
return typeid( Type );
}
//Setters
//Operators
operator Type( void )
{
return m_value;
}
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
ContextPlaceholder( void ) = delete;
ContextPlaceholder( const ContextPlaceholder& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
ContextPlaceholder& operator =( const ContextPlaceholder& value ) = delete;
//Properties
const Type m_value;
};
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <typeinfo>
//Project Includes
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define CONTEXT_PLACEHOLDER_BASE_EXPORT __declspec(dllexport)
#else
#define CONTEXT_PLACEHOLDER_BASE_EXPORT __declspec(dllimport)
#endif
#else
#define CONTEXT_PLACEHOLDER_BASE_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class CONTEXT_PLACEHOLDER_BASE_EXPORT ContextPlaceholderBase
{
public:
//Friends
//Definitions
//Constructor
//Functionality
//Getters
virtual const std::type_info& type( void ) const = 0;
//Setters
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
ContextPlaceholderBase( void ) = default;
virtual ~ContextPlaceholderBase( void ) = default;
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
ContextPlaceholderBase( const ContextPlaceholderBase& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
ContextPlaceholderBase& operator =( const ContextPlaceholderBase& value ) = delete;
//Properties
};
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <memory>
#include <ciso646>
#include <typeinfo>
#include <stdexcept>
//Project Includes
#include <corvusoft/restbed/context_placeholder.hpp>
#include <corvusoft/restbed/context_placeholder_base.hpp>
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define CONTEXT_VALUE_EXPORT __declspec(dllexport)
#else
#define CONTEXT_VALUE_EXPORT __declspec(dllimport)
#endif
#else
#define CONTEXT_VALUE_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class CONTEXT_VALUE_EXPORT ContextValue
{
public:
//Friends
//Definitions
//Constructors
template< typename Type >
ContextValue( const Type& value ) : m_placeholder( new ContextPlaceholder< Type >( value ) )
{
return;
}
ContextValue( const ContextValue& original ) : m_placeholder( original.m_placeholder )
{
return;
}
virtual ~ContextValue( void )
{
return;
}
//Functionality
//Getters
//Setters
//Operators
template< typename Type > operator Type( void ) const
{
if ( typeid( Type ) not_eq m_placeholder->type( ) )
{
throw std::bad_cast( );
}
auto placeholder = static_cast< ContextPlaceholder< Type >* >( m_placeholder.get( ) );
return *placeholder;
}
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
ContextValue( void ) = delete;
//Functionality
//Getters
//Setters
//Operators
ContextValue& operator =( const ContextValue& value ) = delete;
//Properties
const std::shared_ptr< ContextPlaceholderBase > m_placeholder;
};
}

View 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 );
}
}
}

View 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
};
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
};
}
}

View File

@@ -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 { };
};
}
}

View File

@@ -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 { };
};
}
}

View File

@@ -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 );
}
}
}
}

View File

@@ -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;
};
}
}

View 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. Thats 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. Thats 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;
}
}
}

View 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
};
}
}

View 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;
}
}
}

View 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;
};
}
}

View File

@@ -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" }
};
};
}
}

View 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
}
}
}

View 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
};
}
}

View File

@@ -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 = "";
};
}
}

View 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;
};
}
}

View File

@@ -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 );
}
} );
}
}
}

View File

@@ -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
};
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
};
}
}

View File

@@ -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;
};
}
}

View File

@@ -0,0 +1,285 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
#include <memory>
#include <string>
#include <ciso646>
#include <cstdint>
#include <cstdlib>
#include <clocale>
#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/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"
#include "corvusoft/restbed/detail/response_impl.hpp"
//External Includes
#include <asio/error.hpp>
#include <asio/buffer.hpp>
//System Namespaces
using std::free;
using std::bind;
using std::string;
using std::future;
using std::function;
using std::setlocale;
using std::error_code;
using std::shared_ptr;
using std::make_shared;
using std::runtime_error;
using std::invalid_argument;
using std::placeholders::_1;
using std::placeholders::_2;
//Project Namespaces
using restbed::detail::HttpImpl;
using restbed::detail::SocketImpl;
using restbed::detail::RequestImpl;
using restbed::detail::ResponseImpl;
//External Namespaces
using asio::buffer;
namespace restbed
{
Bytes Http::to_bytes( const shared_ptr< Request >& value )
{
return HttpImpl::to_bytes( value );
}
Bytes Http::to_bytes( const shared_ptr< Response >& value )
{
char* locale = nullptr;
if (auto current_locale = setlocale( LC_NUMERIC, nullptr ) )
{
locale = strdup(current_locale);
setlocale( LC_NUMERIC, "C" );
}
auto data = String::format( "%s/%.1f %i %s\r\n",
value->get_protocol( ).data( ),
value->get_version( ),
value->get_status_code( ),
value->get_status_message( ).data( ) );
if (locale) {
setlocale( LC_NUMERIC, locale );
free( locale );
}
auto headers = value->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 = value->get_body( );
if ( not body.empty( ) )
{
bytes.insert( bytes.end( ), body.begin( ), body.end( ) );
}
return bytes;
}
void Http::close( const shared_ptr< Request >& value )
{
if ( value not_eq nullptr and value->m_pimpl->m_socket not_eq nullptr )
{
value->m_pimpl->m_socket->close( );
}
}
bool Http::is_open( const shared_ptr< Request >& value )
{
if ( value not_eq nullptr and value->m_pimpl->m_socket not_eq nullptr )
{
return value->m_pimpl->m_socket->is_open( );
}
return false;
}
bool Http::is_closed( const shared_ptr< Request >& value )
{
return not is_open( value );
}
const shared_ptr< Response > Http::sync( const shared_ptr< Request > request, const shared_ptr< const Settings >& settings )
{
auto response = Http::async( request, nullptr, settings );
response.wait( );
return response.get( );
}
future< shared_ptr< Response > > Http::async( const shared_ptr< Request > request, const function< void ( const shared_ptr< Request >, const shared_ptr< Response > ) >& callback, const shared_ptr< const Settings >& settings )
{
#ifndef BUILD_SSL
if ( settings->get_ssl_settings( ) not_eq nullptr )
{
throw runtime_error( "Not Implemented! Rebuild Restbed with SSL funcationality enabled." );
}
#endif
bool finished = true;
auto completion_handler = callback;
if ( completion_handler == nullptr )
{
finished = false;
completion_handler = [ &finished ]( const shared_ptr< Request >, const shared_ptr< Response > )
{
finished = true;
};
}
HttpImpl::socket_setup( request, settings );
request->m_pimpl->m_response = make_shared< Response >( );
request->m_pimpl->m_response->m_pimpl->m_request = request.get( );
if ( request->m_pimpl->m_socket not_eq nullptr and request->m_pimpl->m_socket->is_closed( ) )
{
request->m_pimpl->m_socket->connect( request->get_host( ), request->get_port( ), bind( HttpImpl::request_handler, _1, request, completion_handler ) );
}
else if ( request->m_pimpl->m_socket not_eq nullptr )
{
request->m_pimpl->m_socket->start_write( Http::to_bytes( request ), bind( HttpImpl::write_handler, _1, _2, request, completion_handler ) );
}
if ( finished )
{
return std::async( std::launch::async, [ ]( const shared_ptr< Request > request ) -> shared_ptr< Response >
{
request->m_pimpl->m_io_service->run( );
return request->m_pimpl->m_response;
}, request );
}
else
{
do
{
request->m_pimpl->m_io_service->run_one( );
}
while ( finished == false and not request->m_pimpl->m_io_service->stopped( ) );
std::promise< shared_ptr< Response > > result;
result.set_value( request->m_pimpl->m_response );
return result.get_future( );
}
}
Bytes Http::fetch( const size_t length, const shared_ptr< Response >& response )
{
if ( response == nullptr )
{
throw invalid_argument( String::empty );
}
auto request = response->m_pimpl->m_request;
if ( request == nullptr or request->m_pimpl->m_buffer == nullptr or request->m_pimpl->m_socket == nullptr )
{
throw invalid_argument( String::empty );
}
Bytes data = { };
if ( length > request->m_pimpl->m_buffer->size( ) )
{
error_code error;
const size_t size = length - request->m_pimpl->m_buffer->size( );
request->m_pimpl->m_socket->start_read( request->m_pimpl->m_buffer, size, error );
if ( error and error not_eq asio::error::eof )
{
throw runtime_error( String::format( "Socket receive failed: '%s'", error.message( ).data( ) ) );
}
const auto data_ptr = asio::buffer_cast< const Byte* >( request->m_pimpl->m_buffer->data( ) );
data = Bytes( data_ptr, data_ptr + length );
request->m_pimpl->m_buffer->consume( length );
}
else
{
const auto data_ptr = asio::buffer_cast< const Byte* >( request->m_pimpl->m_buffer->data( ) );
data = Bytes( data_ptr, data_ptr + length );
request->m_pimpl->m_buffer->consume( length );
}
auto& body = response->m_pimpl->m_body;
if ( body.empty( ) )
{
body = data;
}
else
{
body.insert( body.end( ), data.begin( ), data.end( ) );
}
return data;
}
Bytes Http::fetch( const string& delimiter, const shared_ptr< Response >& response )
{
if ( response == nullptr )
{
throw invalid_argument( String::empty );
}
auto request = response->m_pimpl->m_request;
if ( request == nullptr or request->m_pimpl->m_buffer == nullptr or request->m_pimpl->m_socket == nullptr )
{
throw invalid_argument( String::empty );
}
error_code error;
const size_t size = request->m_pimpl->m_socket->start_read( request->m_pimpl->m_buffer, delimiter, error );
if ( error )
{
throw runtime_error( String::format( "Socket receive failed: '%s'", error.message( ).data( ) ) );
}
const auto data_ptr = asio::buffer_cast< const Byte* >( request->m_pimpl->m_buffer->data( ) );
const Bytes data( data_ptr, data_ptr + size );
request->m_pimpl->m_buffer->consume( size );
auto& body = response->m_pimpl->m_body;
if ( body.empty( ) )
{
body = data;
}
else
{
body.insert( body.end( ), data.begin( ), data.end( ) );
}
return data;
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <string>
#include <memory>
#include <future>
#include <cstddef>
#include <functional>
//Project Includes
#include <corvusoft/restbed/byte.hpp>
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define HTTP_EXPORT __declspec(dllexport)
#else
#define HTTP_EXPORT __declspec(dllimport)
#endif
#else
#define HTTP_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class Request;
class Response;
class Settings;
class [[deprecated("HTTP client is deprecated; we will release a complimentary client framework at a future date.")]] HTTP_EXPORT Http
{
public:
//Friends
//Definitions
//Constructors
//Functionality
static Bytes to_bytes( const std::shared_ptr< Request >& value );
static Bytes to_bytes( const std::shared_ptr< Response >& value );
static void close( const std::shared_ptr< Request >& value );
static bool is_open( const std::shared_ptr< Request >& value );
static bool is_closed( const std::shared_ptr< Request >& value );
static const std::shared_ptr< Response > sync( const std::shared_ptr< Request > request, const std::shared_ptr< const Settings >& settings = std::make_shared< Settings >( ) );
static std::future< std::shared_ptr< Response > > async( const std::shared_ptr< Request > request, const std::function< void ( const std::shared_ptr< Request >, const std::shared_ptr< Response > ) >& callback, const std::shared_ptr< const Settings >& settings = std::make_shared< Settings >( ) );
static Bytes fetch( const std::size_t length, const std::shared_ptr< Response >& response );
static Bytes fetch( const std::string& delimiter, const std::shared_ptr< Response >& response );
//Getters
//Setters
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
Http( void ) = delete;
virtual ~Http( void ) = delete;
Http( const Http& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
Http& operator =( const Http& value ) = delete;
//Properties
};
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
#if defined(_WIN32)
#undef ERROR
#endif
//System Includes
#include <string>
#include <memory>
//Project Includes
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define LOGGER_EXPORT __declspec(dllexport)
#else
#define LOGGER_EXPORT __declspec(dllimport)
#endif
#else
#define LOGGER_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class Settings;
class LOGGER_EXPORT Logger
{
public:
//Friends
//Definitions
enum Level : int
{
INFO = 0000,
DEBUG = 1000,
FATAL = 2000,
ERROR = 3000,
WARNING = 4000,
SECURITY = 5000
};
//Constructors
//Functionality
virtual void stop( void ) = 0;
virtual void start( const std::shared_ptr< const Settings >& settings ) = 0;
virtual void log( const Level level, const char* format, ... ) = 0;
virtual void log_if( bool expression, const Level level, const char* format, ... ) = 0;
//Getters
//Setters
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
Logger( void ) = default;
explicit Logger( const Logger& original ) = default;
virtual ~Logger( void ) = default;
//Functionality
//Getters
//Setters
//Operators
Logger& operator =( const Logger& value ) = default;
//Properties
private:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
};
}

View File

@@ -0,0 +1,296 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
#include <utility>
#include <ciso646>
#include <algorithm>
//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/detail/socket_impl.hpp"
#include "corvusoft/restbed/detail/request_impl.hpp"
#include "corvusoft/restbed/detail/response_impl.hpp"
//External Includes
//System Namespaces
using std::map;
using std::pair;
using std::stof;
using std::stod;
using std::string;
using std::function;
using std::multimap;
using std::make_pair;
using std::unique_ptr;
using std::shared_ptr;
using std::make_shared;
using std::out_of_range;
using std::invalid_argument;
//Project Namespaces
using restbed::Common;
using restbed::detail::RequestImpl;
//External Namespaces
namespace restbed
{
Request::Request( void ) : m_pimpl( new detail::RequestImpl )
{
return;
}
Request::Request( const Uri& value ) : m_pimpl( new detail::RequestImpl )
{
m_pimpl->m_uri = make_shared< Uri >( value );
m_pimpl->m_path = value.get_path( );
m_pimpl->m_port = value.get_port( );
m_pimpl->m_host = value.get_authority( );
m_pimpl->m_query_parameters = value.get_query_parameters( );
m_pimpl->m_protocol = String::uppercase( value.get_scheme( ) );
if ( m_pimpl->m_path.empty( ) )
{
m_pimpl->m_path = "/";
}
if ( m_pimpl->m_port == 0 )
{
m_pimpl->m_port = ( m_pimpl->m_protocol == "HTTPS" ) ? 443 : 80;
}
}
Request::~Request( void )
{
return;
}
bool Request::has_header( const string& name ) const
{
return Common::has_parameter( name, m_pimpl->m_headers );
}
bool Request::has_path_parameter( const string& name ) const
{
return Common::has_parameter( name, m_pimpl->m_path_parameters );
}
bool Request::has_query_parameter( const string& name ) const
{
return Common::has_parameter( name, m_pimpl->m_query_parameters );
}
uint16_t Request::get_port( void ) const
{
return m_pimpl->m_port;
}
double Request::get_version( void ) const
{
return m_pimpl->m_version;
}
const Bytes& Request::get_body( void ) const
{
return m_pimpl->m_body;
}
const shared_ptr< const Response > Request::get_response( void ) const
{
return m_pimpl->m_response;
}
string Request::get_host( const function< string ( const string& ) >& transform ) const
{
return Common::transform( m_pimpl->m_host, transform );
}
string Request::get_path( const function< string ( const string& ) >& transform ) const
{
return Common::transform( m_pimpl->m_path, transform );
}
string Request::get_method( const function< string ( const string& ) >& transform ) const
{
return Common::transform( m_pimpl->m_method, transform );
}
string Request::get_protocol( const function< string ( const string& ) >& transform ) const
{
return Common::transform( m_pimpl->m_protocol, transform );
}
void Request::get_body( string& body, const function< string ( const Bytes& ) >& transform ) const
{
body = ( transform == nullptr ) ? String::to_string( m_pimpl->m_body ) : transform( m_pimpl->m_body );
}
string Request::get_header( const string& name, const string& default_value ) const
{
if ( name.empty( ) )
{
return default_value;
}
const auto headers = Common::get_parameters( name, m_pimpl->m_headers );
return ( headers.empty( ) ) ? default_value : headers.begin( )->second;
}
string Request::get_header( const string& name, const function< string ( const string& ) >& transform ) const
{
if ( name.empty( ) )
{
return String::empty;
}
const auto headers = Common::get_parameters( name, m_pimpl->m_headers );
const auto value = ( headers.empty( ) ) ? String::empty : headers.begin( )->second;
return Common::transform( value, transform );
}
multimap< string, string > Request::get_headers( const string& name ) const
{
return Common::get_parameters( name, m_pimpl->m_headers );
}
string Request::get_query_parameter( const string& name, const string& default_value ) const
{
if ( name.empty( ) )
{
return default_value;
}
const auto parameters = Common::get_parameters( name, m_pimpl->m_query_parameters );
return ( parameters.empty( ) ) ? default_value : parameters.begin( )->second;
}
string Request::get_query_parameter( const string& name, const function< string ( const string& ) >& transform ) const
{
if ( name.empty( ) )
{
return String::empty;
}
const auto parameters = Common::get_parameters( name, m_pimpl->m_query_parameters );
const auto value = ( parameters.empty( ) ) ? String::empty : parameters.begin( )->second;
return Common::transform( value, transform );
}
multimap< string, string > Request::get_query_parameters( const string& name ) const
{
return Common::get_parameters( name, m_pimpl->m_query_parameters );
}
string Request::get_path_parameter( const string& name, const string& default_value ) const
{
if ( name.empty( ) )
{
return default_value;
}
const auto parameters = Common::get_parameters( name, m_pimpl->m_path_parameters );
return ( parameters.empty( ) ) ? default_value : parameters.begin( )->second;
}
string Request::get_path_parameter( const string& name, const function< string ( const string& ) >& transform ) const
{
if ( name.empty( ) )
{
return String::empty;
}
const auto parameters = Common::get_parameters( name, m_pimpl->m_path_parameters );
const auto value = ( parameters.empty( ) ) ? String::empty : parameters.begin( )->second;
return Common::transform( value, transform );
}
map< string, string > Request::get_path_parameters( const string& name ) const
{
return Common::get_parameters( name, m_pimpl->m_path_parameters );
}
void Request::set_body( const Bytes& value )
{
m_pimpl->m_body = value;
}
void Request::set_body( const string& value )
{
m_pimpl->m_body = String::to_bytes( value );
}
void Request::set_port( const uint16_t value )
{
if ( m_pimpl->m_socket not_eq nullptr )
{
m_pimpl->m_socket->close( );
}
m_pimpl->m_port = value;
}
void Request::set_version( const double value )
{
m_pimpl->m_version = value;
}
void Request::set_path( const string& value )
{
m_pimpl->m_path = value;
}
void Request::set_host( const string& value )
{
if ( m_pimpl->m_socket not_eq nullptr )
{
m_pimpl->m_socket->close( );
}
m_pimpl->m_host = value;
}
void Request::set_method( const string& value )
{
m_pimpl->m_method = value;
}
void Request::set_protocol( const string& value )
{
m_pimpl->m_protocol = value;
}
void Request::add_header( const string& name, const string& value )
{
m_pimpl->m_headers.insert( make_pair( name, value ) );
}
void Request::set_header( const string& name, const string& value )
{
m_pimpl->m_headers.erase( name );
m_pimpl->m_headers.insert( make_pair( name, value ) );
}
void Request::set_headers( const multimap< string, string >& values )
{
m_pimpl->m_headers = values;
}
void Request::set_query_parameter( const string& name, const string& value )
{
m_pimpl->m_query_parameters.insert( make_pair( name, value ) );
}
void Request::set_query_parameters( const multimap< string, string >& values )
{
m_pimpl->m_query_parameters = values;
}
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <map>
#include <limits>
#include <string>
#include <memory>
#include <cstdint>
#include <stdexcept>
#include <functional>
#include <type_traits>
//Project Includes
#include <corvusoft/restbed/byte.hpp>
#include <corvusoft/restbed/common.hpp>
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define REQUEST_EXPORT __declspec(dllexport)
#else
#define REQUEST_EXPORT __declspec(dllimport)
#endif
#else
#define REQUEST_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class Uri;
class Http;
class Session;
class Response;
namespace detail
{
class HttpImpl;
class SessionImpl;
class ServiceImpl;
struct RequestImpl;
class WebSocketManagerImpl;
}
class REQUEST_EXPORT Request
{
public:
//Friends
//Definitions
//Constructors
Request( void );
Request( const Uri& value );
virtual ~Request( void );
//Functionality
bool has_header( const std::string& name ) const;
bool has_path_parameter( const std::string& name ) const;
bool has_query_parameter( const std::string& name ) const;
//Getters
uint16_t get_port( void ) const;
double get_version( void ) const;
const Bytes& get_body( void ) const;
const std::shared_ptr< const Response > get_response( void ) const;
std::string get_host( const std::function< std::string ( const std::string& ) >& transform = nullptr ) const;
std::string get_path( const std::function< std::string ( const std::string& ) >& transform = nullptr ) const;
std::string get_method( const std::function< std::string ( const std::string& ) >& transform = nullptr ) const;
std::string get_protocol( const std::function< std::string ( const std::string& ) >& transform = nullptr ) const;
void get_body( std::string& body, const std::function< std::string ( const Bytes& ) >& transform = nullptr ) const;
std::string get_header( const std::string& name, const std::string& default_value = "" ) const;
std::string get_header( const std::string& name, const std::function< std::string ( const std::string& ) >& transform ) const;
template <typename Type, std::enable_if_t< std::is_arithmetic< std::remove_reference_t< Type > >::value > * = nullptr> inline
Type get_header( const std::string& name, const Type default_value ) const
{
return Common::parse_parameter( get_header( name ), default_value );
}
std::multimap< std::string, std::string > get_headers( const std::string& name = "" ) const;
std::string get_query_parameter( const std::string& name, const std::string& default_value = "" ) const;
std::string get_query_parameter( const std::string& name, const std::function< std::string ( const std::string& ) >& transform ) const;
template <typename Type, std::enable_if_t< std::is_arithmetic< std::remove_reference_t< Type > >::value > * = nullptr> inline
Type get_query_parameter( const std::string& name, const Type default_value ) const
{
return Common::parse_parameter( get_query_parameter( name ), default_value );
}
std::multimap< std::string, std::string > get_query_parameters( const std::string& name = "" ) const;
std::string get_path_parameter( const std::string& name, const std::string& default_value = "" ) const;
std::string get_path_parameter( const std::string& name, const std::function< std::string ( const std::string& ) >& transform ) const;
template <typename Type, std::enable_if_t< std::is_arithmetic< std::remove_reference_t< Type > >::value > * = nullptr> inline
Type get_path_parameter( const std::string& name, const Type default_value ) const
{
return Common::parse_parameter( get_path_parameter( name ), default_value );
}
std::map< std::string, std::string > get_path_parameters( const std::string& name = "" ) const;
//Setters
void set_body( const Bytes& value );
void set_body( const std::string& value );
void set_port( const uint16_t value );
void set_version( const double value );
void set_path( const std::string& value );
void set_host( const std::string& value );
void set_method( const std::string& value );
void set_protocol( const std::string& value );
void add_header( const std::string& name, const std::string& value );
void set_header( const std::string& name, const std::string& value );
void set_headers( const std::multimap< std::string, std::string >& values );
void set_query_parameter( const std::string& name, const std::string& value );
void set_query_parameters( const std::multimap< std::string, std::string >& values );
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
friend Http;
friend Session;
friend detail::HttpImpl;
friend detail::SessionImpl;
friend detail::ServiceImpl;
friend detail::WebSocketManagerImpl;
//Definitions
//Constructors
Request( const Request& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
Request& operator =( const Request& value ) = delete;
//Properties
std::unique_ptr< detail::RequestImpl > m_pimpl;
};
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
#include <map>
#include <string>
#include <vector>
#include <memory>
#include <utility>
#include <stdexcept>
#include <functional>
#include <algorithm>
//Project Includes
#include "corvusoft/restbed/rule.hpp"
#include "corvusoft/restbed/session.hpp"
#include "corvusoft/restbed/resource.hpp"
#include "corvusoft/restbed/detail/resource_impl.hpp"
//External Includes
//System Namespaces
using std::set;
using std::string;
using std::multimap;
using std::function;
using std::exception;
using std::unique_ptr;
using std::shared_ptr;
using std::invalid_argument;
//Project Namespaces
using restbed::detail::ResourceImpl;
//External Namespaces
namespace restbed
{
Resource::Resource( void ) : m_pimpl( new ResourceImpl )
{
return;
}
Resource::~Resource( void )
{
return;
}
void Resource::add_rule( const shared_ptr< Rule >& rule )
{
if ( rule not_eq nullptr )
{
m_pimpl->m_rules.push_back( rule );
stable_sort( m_pimpl->m_rules.begin( ), m_pimpl->m_rules.end( ), [ ]( const shared_ptr< const Rule >& lhs, const shared_ptr< const Rule >& rhs )
{
return lhs->get_priority( ) < rhs->get_priority( );
} );
}
}
void Resource::add_rule( const shared_ptr< Rule >& rule, const int priority )
{
if ( rule not_eq nullptr )
{
rule->set_priority( priority );
add_rule( rule );
}
}
void Resource::set_path( const string& value )
{
m_pimpl->m_paths = { value };
}
void Resource::set_paths( const set< string >& values )
{
m_pimpl->m_paths = values;
}
void Resource::set_default_header( const string& name, const string& value )
{
m_pimpl->m_default_headers.insert( make_pair( name, value ) );
}
void Resource::set_default_headers( const multimap< string, string >& values )
{
m_pimpl->m_default_headers = values;
}
void Resource::set_failed_filter_validation_handler( const function< void ( const shared_ptr< Session > ) >& value )
{
m_pimpl->m_failed_filter_validation_handler = value;
}
void Resource::set_error_handler( const function< void ( const int, const exception&, const shared_ptr< Session > ) >& value )
{
m_pimpl->m_error_handler = value;
}
void Resource::set_authentication_handler( const function< void ( const shared_ptr< Session >, const function< void ( const shared_ptr< Session > ) >& ) >& value )
{
m_pimpl->m_authentication_handler = value;
}
void Resource::set_method_handler( const string& method, const function< void ( const shared_ptr< Session > ) >& callback )
{
static const multimap< string, string > empty { };
set_method_handler( method, empty, callback );
}
void Resource::set_method_handler( const string& method, const multimap< string, string >& filters, const function< void ( const shared_ptr< Session > ) >& callback )
{
if ( method.empty( ) )
{
throw invalid_argument( "Attempt to set resource handler to an empty protocol method." );
}
if ( callback not_eq nullptr )
{
m_pimpl->m_methods.insert( method );
m_pimpl->m_method_handlers.insert( make_pair( method, make_pair( filters, callback ) ) );
}
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <map>
#include <set>
#include <string>
#include <functional>
//Project Includes
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define RESOURCE_EXPORT __declspec(dllexport)
#else
#define RESOURCE_EXPORT __declspec(dllimport)
#endif
#else
#define RESOURCE_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class Rule;
class Session;
class Service;
namespace detail
{
class SessionImpl;
class ServiceImpl;
struct ResourceImpl;
}
class RESOURCE_EXPORT Resource
{
public:
//Friends
//Definitions
//Constructors
Resource( void );
virtual ~Resource( void );
//Functionality
void add_rule( const std::shared_ptr< Rule >& rule );
void add_rule( const std::shared_ptr< Rule >& rule, const int priority );
//Getters
//Setters
void set_path( const std::string& value );
void set_paths( const std::set< std::string >& values );
void set_default_header( const std::string& name, const std::string& value );
void set_default_headers( const std::multimap< std::string, std::string >& values );
void set_failed_filter_validation_handler( const std::function< void ( const std::shared_ptr< Session > ) >& value );
void set_error_handler( const std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) >& value );
void set_authentication_handler( const std::function< void ( const std::shared_ptr< Session >, const std::function< void ( const std::shared_ptr< Session > ) >& ) >& value );
void set_method_handler( const std::string& method, const std::function< void ( const std::shared_ptr< Session > ) >& callback );
void set_method_handler( const std::string& method, const std::multimap< std::string, std::string >& filters, const std::function< void ( const std::shared_ptr< Session > ) >& callback );
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
friend Service;
friend detail::ServiceImpl;
friend detail::SessionImpl;
//Definitions
//Constructors
Resource( const Resource& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
Resource& operator =( const Resource& value ) = delete;
//Properties
std::unique_ptr< detail::ResourceImpl > m_pimpl;
};
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
#include <utility>
#include <ciso646>
#include <algorithm>
#include <stdexcept>
//Project Includes
#include "corvusoft/restbed/string.hpp"
#include "corvusoft/restbed/request.hpp"
#include "corvusoft/restbed/response.hpp"
#include "corvusoft/restbed/detail/response_impl.hpp"
//External Includes
//System Namespaces
using std::map;
using std::pair;
using std::string;
using std::function;
using std::multimap;
using std::unique_ptr;
using std::shared_ptr;
using std::invalid_argument;
//Project Namespaces
using restbed::Common;
using restbed::Request;
using restbed::detail::ResponseImpl;
//External Namespaces
namespace restbed
{
Response::Response( void ) : m_pimpl( new ResponseImpl )
{
return;
}
Response::~Response( void )
{
return;
}
bool Response::has_header( const string& name ) const
{
return Common::has_parameter( name, m_pimpl->m_headers );
}
Bytes Response::get_body( void ) const
{
return m_pimpl->m_body;
}
double Response::get_version( void ) const
{
return m_pimpl->m_version;
}
int Response::get_status_code( void ) const
{
return m_pimpl->m_status_code;
}
string Response::get_protocol( void ) const
{
return m_pimpl->m_protocol;
}
string Response::get_status_message( void ) const
{
return m_pimpl->m_status_message;
}
void Response::get_body( string& body, const function< string ( const Bytes& ) >& transform ) const
{
body = ( transform == nullptr ) ? string( m_pimpl->m_body.begin( ), m_pimpl->m_body.end( ) ) : transform( m_pimpl->m_body );
}
string Response::get_header( const string& name, const string& default_value ) const
{
if ( name.empty( ) )
{
return default_value;
}
const auto headers = Common::get_parameters( name, m_pimpl->m_headers );
return ( headers.empty( ) ) ? default_value : headers.begin( )->second;
}
string Response::get_header( const string& name, const function< string ( const string& ) >& transform ) const
{
if ( name.empty( ) )
{
return String::empty;
}
const auto headers = Common::get_parameters( name, m_pimpl->m_headers );
const auto value = ( headers.empty( ) ) ? String::empty : headers.begin( )->second;
return Common::transform( value, transform );
}
multimap< string, string > Response::get_headers( const string& name ) const
{
return Common::get_parameters( name, m_pimpl->m_headers );
}
void Response::set_body( const Bytes& value )
{
m_pimpl->m_body = value;
}
void Response::set_body( const string& value )
{
m_pimpl->m_body = String::to_bytes( value );
}
void Response::set_version( const double value )
{
m_pimpl->m_version = value;
}
void Response::set_status_code( const int value )
{
m_pimpl->m_status_code = value;
}
void Response::set_protocol( const string& value )
{
m_pimpl->m_protocol = value;
}
void Response::set_status_message( const string& value )
{
m_pimpl->m_status_message = value;
}
void Response::add_header( const string& name, const string& value )
{
m_pimpl->m_headers.insert( make_pair( name, value ) );
}
void Response::set_header( const string& name, const string& value )
{
m_pimpl->m_headers.erase( name );
m_pimpl->m_headers.insert( make_pair( name, value ) );
}
void Response::set_headers( const multimap< string, string >& values )
{
m_pimpl->m_headers = values;
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <map>
#include <string>
#include <limits>
#include <memory>
#include <cstdint>
#include <functional>
//Project Includes
#include <corvusoft/restbed/byte.hpp>
#include <corvusoft/restbed/common.hpp>
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define RESPONSE_EXPORT __declspec(dllexport)
#else
#define RESPONSE_EXPORT __declspec(dllimport)
#endif
#else
#define RESPONSE_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class Http;
namespace detail
{
struct ResponseImpl;
}
class RESPONSE_EXPORT Response
{
public:
//Friends
//Definitions
//Constructors
Response( void );
virtual ~Response( void );
//Functionality
bool has_header( const std::string& name ) const;
//Getters
Bytes get_body( void ) const;
double get_version( void ) const;
int get_status_code( void ) const;
std::string get_protocol( void ) const;
std::string get_status_message( void ) const;
void get_body( std::string& body, const std::function< std::string ( const Bytes& ) >& transform = nullptr ) const;
template< typename Type, typename std::enable_if< std::is_arithmetic< Type >::value, Type >::type = 0 >
Type get_header( const std::string& name, const Type default_value ) const
{
return Common::parse_parameter( get_header( name ), default_value );
}
std::string get_header( const std::string& name, const std::string& default_value ) const;
std::string get_header( const std::string& name, const std::function< std::string ( const std::string& ) >& transform = nullptr ) const;
std::multimap< std::string, std::string > get_headers( const std::string& name = "" ) const;
//Setters
void set_body( const Bytes& value );
void set_body( const std::string& value );
void set_version( const double value );
void set_status_code( const int value );
void set_protocol( const std::string& protocol );
void set_status_message( const std::string& value );
void add_header( const std::string& name, const std::string& value );
void set_header( const std::string& name, const std::string& value );
void set_headers( const std::multimap< std::string, std::string >& values );
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
friend Http;
//Definitions
//Constructors
Response( const Response& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
Response& operator =( const Response& value ) = delete;
//Properties
std::unique_ptr< detail::ResponseImpl > m_pimpl;
};
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
//Project Includes
#include "corvusoft/restbed/rule.hpp"
#include "corvusoft/restbed/detail/rule_impl.hpp"
//External Includes
//System Namespaces
using std::unique_ptr;
using std::shared_ptr;
//Project Namespaces
using restbed::detail::RuleImpl;
//External Namespaces
namespace restbed
{
bool Rule::condition( const shared_ptr< Session > )
{
return true;
}
int Rule::get_priority( void ) const
{
return m_pimpl->m_priority;
}
void Rule::set_priority( const int value )
{
m_pimpl->m_priority = value;
}
Rule::Rule( void ) : m_pimpl( new RuleImpl )
{
return;
}
Rule::~Rule( void )
{
return;
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <memory>
#include <functional>
//Project Includes
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define RULE_EXPORT __declspec(dllexport)
#else
#define RULE_EXPORT __declspec(dllimport)
#endif
#else
#define RULE_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class Session;
namespace detail
{
struct RuleImpl;
}
class RULE_EXPORT Rule
{
public:
//Friends
//Definitions
//Constructors
//Functionality
virtual bool condition( const std::shared_ptr< Session > session );
virtual void action( const std::shared_ptr< Session > session, const std::function< void ( const std::shared_ptr< Session > ) >& callback ) = 0;
//Getters
int get_priority( void ) const;
//Setters
void set_priority( const int value );
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
Rule( void );
virtual ~Rule( void );
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
explicit Rule( const Rule& ) = delete;
//Functionality
//Getters
//Setters
//Operators
Rule& operator =( const Rule& ) = delete;
//Properties
std::unique_ptr< detail::RuleImpl > m_pimpl;
};
}

View File

@@ -0,0 +1,479 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
#include <thread>
#include <vector>
#include <utility>
#include <cstdint>
#include <ciso646>
#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/service.hpp"
#include "corvusoft/restbed/session.hpp"
#include "corvusoft/restbed/resource.hpp"
#include "corvusoft/restbed/settings.hpp"
#include "corvusoft/restbed/ssl_settings.hpp"
#include "corvusoft/restbed/session_manager.hpp"
#include "corvusoft/restbed/detail/service_impl.hpp"
#include "corvusoft/restbed/detail/resource_impl.hpp"
#include "corvusoft/restbed/detail/web_socket_manager_impl.hpp"
//External Includes
#include <asio/io_service.hpp>
#include <asio/steady_timer.hpp>
#ifdef BUILD_SSL
#include <asio/ssl.hpp>
#endif
//System Namespaces
using std::map;
using std::bind;
using std::promise;
using std::future;
using std::future_status;
using std::async;
using std::launch;
using std::string;
using std::vector;
using std::function;
using std::exception;
using std::to_string;
using std::unique_ptr;
using std::shared_ptr;
using std::error_code;
using std::make_shared;
using std::stable_sort;
using std::runtime_error;
using std::chrono::seconds;
using std::invalid_argument;
using std::chrono::milliseconds;
using std::chrono::steady_clock;
using std::chrono::duration_cast;
//Project Namespaces
using restbed::detail::ServiceImpl;
using restbed::detail::WebSocketManagerImpl;
//External Namespaces
using asio::io_service;
using asio::steady_timer;
namespace restbed
{
Service::Service( void ) : m_pimpl( new ServiceImpl )
{
return;
}
Service::~Service( void )
{
try
{
stop( );
}
catch ( ... )
{
m_pimpl->log( Logger::WARNING, "Service failed graceful wind down." );
}
}
bool Service::is_up( void ) const
{
return m_pimpl->m_uptime not_eq steady_clock::time_point::min( );
}
bool Service::is_down( void ) const
{
return not is_up( );
}
void Service::stop( void )
{
m_pimpl->m_uptime = steady_clock::time_point::min( );
if ( m_pimpl->m_io_service not_eq nullptr )
{
m_pimpl->m_io_service->stop( );
}
if ( m_pimpl->m_session_manager not_eq nullptr )
{
m_pimpl->m_session_manager->stop( );
}
if ( m_pimpl->m_workers_stopped )
{
m_pimpl->m_workers_stopped->wait_for( seconds( 1 ) );
m_pimpl->m_workers_stopped.reset( );
}
if ( m_pimpl->m_logger not_eq nullptr )
{
m_pimpl->log( Logger::INFO, "Service halted." );
m_pimpl->m_logger->stop( );
}
}
void Service::start( const shared_ptr< const Settings >& settings )
{
m_pimpl->setup_signal_handler( );
m_pimpl->m_settings = settings;
if ( m_pimpl->m_settings == nullptr )
{
m_pimpl->m_settings = make_shared< Settings >( );
}
#ifdef BUILD_SSL
m_pimpl->m_ssl_settings = m_pimpl->m_settings->get_ssl_settings( );
#else
if ( m_pimpl->m_settings->get_ssl_settings( ) not_eq nullptr )
{
throw runtime_error( "Not Implemented! Rebuild Restbed with SSL functionality enabled." );
}
#endif
if ( m_pimpl->m_logger not_eq nullptr )
{
m_pimpl->m_logger->start( m_pimpl->m_settings );
}
if ( m_pimpl->m_session_manager == nullptr )
{
m_pimpl->m_session_manager = make_shared< SessionManager >( );
}
m_pimpl->m_session_manager->start( m_pimpl->m_settings );
m_pimpl->m_web_socket_manager = make_shared< WebSocketManagerImpl >( );
stable_sort( m_pimpl->m_rules.begin( ), m_pimpl->m_rules.end( ), [ ]( const shared_ptr< const Rule >& lhs, const shared_ptr< const Rule >& rhs )
{
return lhs->get_priority( ) < rhs->get_priority( );
} );
#ifdef BUILD_IPC
m_pimpl->ipc_start( );
#endif
m_pimpl->http_start( );
#ifdef BUILD_SSL
m_pimpl->https_start( );
#endif
for ( const auto& route : m_pimpl->m_resource_paths )
{
auto path = String::format( "/%s/%s", m_pimpl->m_settings->get_root( ).data( ), route.second.data( ) );
path = String::replace( "//", "/", path );
m_pimpl->log( Logger::INFO, String::format( "Resource published on route '%s'.", path.data( ) ) );
}
if ( m_pimpl->m_ready_handler not_eq nullptr )
{
m_pimpl->m_io_service->post( m_pimpl->m_ready_handler );
}
m_pimpl->m_uptime = steady_clock::now( );
unsigned int limit = m_pimpl->m_settings->get_worker_limit( );
if ( limit == 0 )
{
m_pimpl->m_io_service->run( );
}
else
{
promise<void> all_signalled;
m_pimpl->m_workers_stopped = unique_ptr<future<void> >(new future<void>(all_signalled.get_future()));
vector<future<void> > signals;
for ( unsigned int count = 0; count < limit; count++ )
{
signals.push_back( async(launch::async, [ this ]( )
{
m_pimpl->m_io_service->run( );
} ) );
}
try
{
while (!signals.empty())
{
signals.erase(std::remove_if(signals.begin(), signals.end(),
[](future<void>& signal) {
return (signal.wait_for(milliseconds(5)) == future_status::ready) ?
(signal.get(), true) : // this will throw if the worker thread ended with an exception
false;
}),
signals.end());
}
all_signalled.set_value();
}
catch(...)
{
m_pimpl->m_io_service->stop( );
all_signalled.set_value();
throw;
}
}
}
void Service::restart( const shared_ptr< const Settings >& settings )
{
try
{
stop( );
}
catch ( ... )
{
m_pimpl->log( Logger::WARNING, "Service failed graceful reboot." );
}
start( settings );
}
void Service::add_rule( const shared_ptr< Rule >& rule )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
if ( rule not_eq nullptr )
{
m_pimpl->m_rules.push_back( rule );
}
}
void Service::add_rule( const shared_ptr< Rule >& rule, const int priority )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
if ( rule not_eq nullptr )
{
rule->set_priority( priority );
m_pimpl->m_rules.push_back( rule );
}
}
void Service::publish( const shared_ptr< const Resource >& resource )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
if ( resource == nullptr )
{
return;
}
auto paths = resource->m_pimpl->m_paths;
if ( not m_pimpl->has_unique_paths( paths ) )
{
throw invalid_argument( "Resource would pollute namespace. Please ensure all published resources have unique paths." );
}
for ( auto& path : paths )
{
const string sanitised_path = m_pimpl->sanitise_path( path );
m_pimpl->m_resource_paths[ sanitised_path ] = path;
m_pimpl->m_resource_routes[ sanitised_path ] = resource;
}
const auto& methods = resource->m_pimpl->m_methods;
m_pimpl->m_supported_methods.insert( methods.begin( ), methods.end( ) );
}
void Service::suppress( const shared_ptr< const Resource >& resource )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
if ( resource == nullptr )
{
return;
}
for ( const auto& path : resource->m_pimpl->m_paths )
{
if ( m_pimpl->m_resource_routes.erase( path ) )
{
m_pimpl->log( Logger::INFO, String::format( "Suppressed resource route '%s'.", path.data( ) ) );
}
else
{
m_pimpl->log( Logger::WARNING, String::format( "Failed to suppress resource route '%s'; Not Found!", path.data( ) ) );
}
}
}
void Service::schedule( const function< void ( void ) >& task, const milliseconds& interval )
{
if ( task == nullptr )
{
return;
}
if ( interval == milliseconds::zero( ) )
{
m_pimpl->m_io_service->post( task );
return;
}
auto timer = make_shared< steady_timer >( *m_pimpl->m_io_service );
timer->expires_from_now( interval );
timer->async_wait( [ this, task, interval, timer ]( const error_code& )
{
task( );
schedule( task, interval );
} );
}
const seconds Service::get_uptime( void ) const
{
if ( is_down( ) )
{
return seconds( 0 );
}
return duration_cast< seconds >( steady_clock::now( ) - m_pimpl->m_uptime );
}
const shared_ptr< const Uri > Service::get_http_uri( void ) const
{
return m_pimpl->get_http_uri( );
}
const shared_ptr< const Uri > Service::get_https_uri( void ) const
{
return m_pimpl->get_https_uri( );
}
void Service::set_logger( const shared_ptr< Logger >& value )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
m_pimpl->m_logger = value;
}
void Service::set_session_manager( const shared_ptr< SessionManager >& value )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
m_pimpl->m_session_manager = value;
}
void Service::set_ready_handler( const function< void ( Service& ) >& value )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
if ( value not_eq nullptr )
{
m_pimpl->m_ready_handler = bind( value, std::ref( *this ) );
}
}
void Service::set_signal_handler( const int signal, const function< void ( const int ) >& value )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
if ( value not_eq nullptr )
{
m_pimpl->m_signal_handlers[ signal ] = value;
}
}
void Service::set_not_found_handler( const function< void ( const shared_ptr< Session > ) >& value )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
m_pimpl->m_not_found_handler = value;
}
void Service::set_method_not_allowed_handler( const function< void ( const shared_ptr< Session > ) >& value )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
m_pimpl->m_method_not_allowed_handler = value;
}
void Service::set_method_not_implemented_handler( const function< void ( const shared_ptr< Session > ) >& value )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
m_pimpl->m_method_not_implemented_handler = value;
}
void Service::set_failed_filter_validation_handler( const function< void ( const shared_ptr< Session > ) >& value )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
m_pimpl->m_failed_filter_validation_handler = value;
}
void Service::set_error_handler( const function< void ( const int, const exception&, const shared_ptr< Session > ) >& value )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
if ( value == nullptr )
{
m_pimpl->m_error_handler = ServiceImpl::default_error_handler;
}
m_pimpl->m_error_handler = value;
}
void Service::set_authentication_handler( const function< void ( const shared_ptr< Session >, const function< void ( const shared_ptr< Session > ) >& ) >& value )
{
if ( is_up( ) )
{
throw runtime_error( "Runtime modifications of the service are prohibited." );
}
m_pimpl->m_authentication_handler = value;
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <map>
#include <chrono>
#include <memory>
#include <string>
#include <stdexcept>
#include <functional>
//Project Includes
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define SERVICE_EXPORT __declspec(dllexport)
#else
#define SERVICE_EXPORT __declspec(dllimport)
#endif
#else
#define SERVICE_EXPORT
#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;
namespace detail
{
class ServiceImpl;
}
class SERVICE_EXPORT Service
{
public:
//Friends
//Definitions
//Constructors
Service( void );
virtual ~Service( void );
//Functionality
bool is_up( void ) const;
bool is_down( void ) const;
void stop( void );
void start( const std::shared_ptr< const Settings >& settings = nullptr );
void restart( const std::shared_ptr< const Settings >& settings = nullptr );
void add_rule( const std::shared_ptr< Rule >& rule );
void add_rule( const std::shared_ptr< Rule >& rule, const int priority );
void publish( const std::shared_ptr< const Resource >& resource );
void suppress( const std::shared_ptr< const Resource >& resource );
void schedule( const std::function< void ( void ) >& task, const std::chrono::milliseconds& interval = std::chrono::milliseconds::zero( ) );
//Getters
const std::chrono::seconds get_uptime( void ) const;
const std::shared_ptr< const Uri > get_http_uri( void ) const;
const std::shared_ptr< const Uri > get_https_uri( void ) const;
//Setters
void set_logger( const std::shared_ptr< Logger >& value );
void set_session_manager( const std::shared_ptr< SessionManager >& value );
void set_ready_handler( const std::function< void ( Service& ) >& value );
void set_signal_handler( const int signal, const std::function< void ( const int ) >& value );
void set_not_found_handler( const std::function< void ( const std::shared_ptr< Session > ) >& value );
void set_method_not_allowed_handler( const std::function< void ( const std::shared_ptr< Session > ) >& value );
void set_method_not_implemented_handler( const std::function< void ( const std::shared_ptr< Session > ) >& value );
void set_failed_filter_validation_handler( const std::function< void ( const std::shared_ptr< Session > ) >& value );
void set_error_handler( const std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) >& value );
void set_authentication_handler( const std::function< void ( const std::shared_ptr< Session >, const std::function< void ( const std::shared_ptr< Session > ) >& ) >& value );
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
Service( const Service& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
Service& operator =( const Service& value ) = delete;
//Properties
std::unique_ptr< detail::ServiceImpl > m_pimpl;
};
}

View File

@@ -0,0 +1,476 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
#include <ciso646>
#include <system_error>
//Project Includes
#include "corvusoft/restbed/session.hpp"
#include "corvusoft/restbed/request.hpp"
#include "corvusoft/restbed/response.hpp"
#include "corvusoft/restbed/web_socket.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/web_socket_impl.hpp"
#include "corvusoft/restbed/detail/web_socket_manager_impl.hpp"
//External Includes
#include <asio/buffer.hpp>
//System Namespaces
using std::set;
using std::string;
using std::function;
using std::multimap;
using std::error_code;
using std::unique_ptr;
using std::shared_ptr;
using std::make_shared;
using std::runtime_error;
using std::placeholders::_1;
using std::placeholders::_2;
using std::invalid_argument;
using std::chrono::milliseconds;
//Project Namespaces
using restbed::detail::SessionImpl;
//External Namespaces
using asio::buffer;
namespace restbed
{
static Bytes empty_body = { };
static multimap< string, string > empty_headers = { };
Session::Session( const string& id ) : m_pimpl( new SessionImpl )
{
m_pimpl->m_id = id;
}
bool Session::has( const string& name ) const
{
return m_pimpl->m_context.find( name ) not_eq m_pimpl->m_context.end( );
}
void Session::erase( const string& name )
{
if ( name.empty( ) )
{
m_pimpl->m_context.clear( );
}
else
{
m_pimpl->m_context.erase( name );
}
}
const set< string > Session::keys( void ) const
{
std::set< string > keys;
for ( const auto& value : m_pimpl->m_context )
{
keys.insert( value.first );
}
return keys;
}
bool Session::is_open( void ) const
{
return m_pimpl->m_request not_eq nullptr and
m_pimpl->m_request->m_pimpl->m_socket not_eq nullptr and
m_pimpl->m_request->m_pimpl->m_socket->is_open( );
}
bool Session::is_closed( void ) const
{
return not is_open( );
}
void Session::close( const Bytes& body )
{
auto session = shared_from_this( );
if ( is_closed( ) )
{
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( "Close failed: session already closed." ), session );
}
m_pimpl->m_request->m_pimpl->m_socket->start_write( body, [ this, session ]( const error_code & error, size_t )
{
if ( error )
{
const auto message = String::format( "Close failed: %s", error.message( ).data( ) );
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( message ), session );
}
m_pimpl->m_manager->save( session, [ this, session ]( const shared_ptr< Session > )
{
m_pimpl->m_request->m_pimpl->m_socket->close( );
} );
} );
}
void Session::close( const Response& response )
{
auto session = shared_from_this( );
if ( is_closed( ) )
{
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( "Close failed: session already closed." ), session );
}
m_pimpl->transmit( response, [ this, session ]( const error_code & error, size_t )
{
if ( error )
{
const auto message = String::format( "Close failed: %s", error.message( ).data( ) );
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( message ), session );
}
m_pimpl->m_manager->save( session, [ this ]( const shared_ptr< Session > )
{
m_pimpl->m_request->m_pimpl->m_socket->close( );
} );
} );
}
void Session::close( const string& body )
{
close( String::to_bytes( body ) );
}
void Session::close( const int status, const Bytes& body )
{
close( status, body, empty_headers );
}
void Session::close( const int status, const string& body )
{
close( status, String::to_bytes( body ), empty_headers );
}
void Session::close( const int status, const multimap< string, string >& headers )
{
close( status, empty_body, headers );
}
void Session::close( const int status, const string& body, const multimap< string, string >& headers )
{
close( status, String::to_bytes( body ), headers );
}
void Session::close( const int status, const Bytes& body, const multimap< string, string >& headers )
{
Response response;
response.set_body( body );
response.set_headers( headers );
response.set_status_code( status );
close( response );
}
void Session::yield( const Bytes& body, const function< void ( const shared_ptr< Session > ) >& callback )
{
auto session = shared_from_this( );
if ( is_closed( ) )
{
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( "Yield failed: session already closed." ), session );
}
m_pimpl->m_request->m_pimpl->m_socket->start_write( body, [ this, session, callback ]( const error_code & error, size_t )
{
if ( error )
{
const auto message = String::format( "Yield failed: %s", error.message( ).data( ) );
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( message ), session );
}
if ( callback not_eq nullptr )
{
callback( session );
}
} );
}
void Session::yield( const string& body, const function< void ( const shared_ptr< Session > ) >& callback )
{
yield( String::to_bytes( body ), callback );
}
void Session::yield( const Response& response, const function< void ( const shared_ptr< Session > ) >& callback )
{
auto session = shared_from_this( );
if ( is_closed( ) )
{
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( "Yield failed: session already closed." ), session );
}
m_pimpl->transmit( response, [ this, session, callback ]( const error_code & error, size_t )
{
if ( error )
{
const auto message = String::format( "Yield failed: %s", error.message( ).data( ) );
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( message ), session );
}
if ( callback == nullptr )
{
m_pimpl->m_request->m_pimpl->m_socket->start_read( m_pimpl->m_request->m_pimpl->m_buffer, "\r\n\r\n", [ this, session ]( const error_code & error, const size_t length )
{
m_pimpl->m_keep_alive_callback( error, length, session );
} );
}
else
{
callback( session );
}
} );
}
void Session::yield( const int status, const Bytes& body, const function< void ( const shared_ptr< Session > ) >& callback )
{
yield( status, body, empty_headers, callback );
}
void Session::yield( const int status, const string& body, const function< void ( const shared_ptr< Session > ) >& callback )
{
yield( status, String::to_bytes( body ), empty_headers, callback );
}
void Session::yield( const int status, const multimap< string, string >& headers, const function< void ( const shared_ptr< Session > ) >& callback )
{
yield( status, empty_body, headers, callback );
}
void Session::yield( const int status, const string& body, const multimap< string, string >& headers, const function< void ( const shared_ptr< Session > ) >& callback )
{
yield( status, String::to_bytes( body ), headers, callback );
}
void Session::yield( const int status, const Bytes& body, const multimap< string, string >& headers, const function< void ( const shared_ptr< Session > ) >& callback )
{
Response response;
response.set_body( body );
response.set_headers( headers );
response.set_status_code( status );
yield( response, callback );
}
void Session::fetch( const size_t length, const function< void ( const shared_ptr< Session >, const Bytes& ) >& callback )
{
auto session = shared_from_this( );
if ( is_closed( ) )
{
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( "Fetch failed: session already closed." ), session );
}
if ( length > m_pimpl->m_request->m_pimpl->m_buffer->size( ) )
{
size_t size = length - m_pimpl->m_request->m_pimpl->m_buffer->size( );
m_pimpl->m_request->m_pimpl->m_socket->start_read( m_pimpl->m_request->m_pimpl->m_buffer, size, [ this, session, length, callback ]( const error_code & error, size_t )
{
if ( error )
{
const auto message = String::format( "Fetch failed: %s", error.message( ).data( ) );
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( message ), session );
}
m_pimpl->fetch_body( length, session, callback );
} );
}
else
{
m_pimpl->fetch_body( length, session, callback );
}
}
void Session::fetch( const string& delimiter, const function< void ( const shared_ptr< Session >, const Bytes& ) >& callback )
{
auto session = shared_from_this( );
if ( is_closed( ) )
{
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( "Fetch failed: session already closed." ), session );
}
m_pimpl->m_request->m_pimpl->m_socket->start_read( m_pimpl->m_request->m_pimpl->m_buffer, delimiter, [ this, session, callback ]( const error_code & error, size_t length )
{
if ( error )
{
const auto message = String::format( "Fetch failed: %s", error.message( ).data( ) );
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( message ), session );
}
m_pimpl->fetch_body( length, session, callback );
} );
}
void Session::upgrade( const int status, const function< void ( const shared_ptr< WebSocket > ) >& callback )
{
upgrade( status, empty_body, empty_headers, callback );
}
void Session::upgrade( const int status, const Bytes& body, const function< void ( const shared_ptr< WebSocket > ) >& callback )
{
upgrade( status, body, empty_headers, callback );
}
void Session::upgrade( const int status, const string& body, const function< void ( const shared_ptr< WebSocket > ) >& callback )
{
upgrade( status, String::to_bytes( body ), empty_headers, callback );
}
void Session::upgrade( const int status, const multimap< string, string >& headers, const function< void ( const shared_ptr< WebSocket > ) >& callback )
{
upgrade( status, empty_body, headers, callback );
}
void Session::upgrade( const int status, const Bytes& body, const multimap< string, string >& headers, const function< void ( const shared_ptr< WebSocket > ) >& callback )
{
auto socket = m_pimpl->m_web_socket_manager->create( shared_from_this( ) );
yield( status, body, headers, bind( callback, socket ) );
}
void Session::upgrade( const int status, const string& body, const multimap< string, string >& headers, const function< void ( const shared_ptr< WebSocket > ) >& callback )
{
upgrade( status, String::to_bytes( body ), headers, callback );
}
void Session::sleep_for( const milliseconds& delay, const function< void ( const shared_ptr< Session > ) >& callback )
{
auto session = shared_from_this( );
if ( is_closed( ) )
{
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( "Sleep failed: session already closed." ), session );
}
m_pimpl->m_request->m_pimpl->m_socket->sleep_for( delay, [ session, callback, this ]( const error_code & error )
{
if ( error )
{
const auto message = String::format( "Wait failed: %s", error.message( ).data( ) );
const auto error_handler = m_pimpl->get_error_handler( );
return error_handler( 500, runtime_error( message ), session );
}
if ( callback not_eq nullptr )
{
callback( session );
}
} );
}
const string& Session::get_id( void ) const
{
return m_pimpl->m_id;
}
const string Session::get_origin( void ) const
{
if ( m_pimpl->m_request == nullptr or m_pimpl->m_request->m_pimpl->m_socket == nullptr )
{
return "";
}
return m_pimpl->m_request->m_pimpl->m_socket->get_remote_endpoint( );
}
const string Session::get_destination( void ) const
{
if ( m_pimpl->m_request == nullptr or m_pimpl->m_request->m_pimpl->m_socket == nullptr )
{
return "";
}
return m_pimpl->m_request->m_pimpl->m_socket->get_local_endpoint( );
}
const shared_ptr< const Request > Session::get_request( void ) const
{
return m_pimpl->m_request;
}
const shared_ptr< const Resource > Session::get_resource( void ) const
{
return m_pimpl->m_resource;
}
const multimap< string, string >& Session::get_headers( void ) const
{
return m_pimpl->m_headers;
}
const ContextValue& Session::get( const string& name ) const
{
return m_pimpl->m_context.at( name );
}
const ContextValue& Session::get( const string& name, const ContextValue& default_value ) const
{
if ( has( name ) )
{
return m_pimpl->m_context.at( name );
}
return default_value;
}
void Session::set_id( const string& value )
{
m_pimpl->m_id = value;
}
void Session::set( const string& name, const ContextValue& value )
{
if ( has( name ) )
{
m_pimpl->m_context.erase( name );
}
m_pimpl->m_context.insert( make_pair( name, value ) );
}
void Session::add_header( const string& name, const string& value )
{
m_pimpl->m_headers.insert( make_pair( name, value ) );
}
void Session::set_header( const string& name, const string& value )
{
m_pimpl->m_headers.erase( name );
m_pimpl->m_headers.insert( make_pair( name, value ) );
}
void Session::set_headers( const multimap< string, string >& values )
{
m_pimpl->m_headers = values;
}
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <map>
#include <set>
#include <chrono>
#include <string>
#include <memory>
#include <functional>
//Project Includes
#include <corvusoft/restbed/byte.hpp>
#include <corvusoft/restbed/string.hpp>
#include <corvusoft/restbed/context_value.hpp>
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define SESSION_EXPORT __declspec(dllexport)
#else
#define SESSION_EXPORT __declspec(dllimport)
#endif
#else
#define SESSION_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class Request;
class Session;
class Response;
class Resource;
class WebSocket;
namespace detail
{
class SessionImpl;
class ServiceImpl;
class WebSocketManagerImpl;
}
class SESSION_EXPORT Session : public std::enable_shared_from_this< Session >
{
public:
//Friends
//Definitions
//Constructors
explicit Session( const std::string& id );
~Session( void ) = default;
//Functionality
bool has( const std::string& name ) const;
void erase( const std::string& name = "" );
const std::set< std::string > keys( void ) const;
bool is_open( void ) const;
bool is_closed( void ) const;
void close( const Bytes& body );
void close( const Response& response );
void close( const std::string& body = "" );
void close( const int status, const Bytes& body );
void close( const int status, const std::string& body = "" );
void close( const int status, const std::multimap< std::string, std::string >& headers );
void close( const int status, const std::string& body, const std::multimap< std::string, std::string >& headers );
void close( const int status, const Bytes& body, const std::multimap< std::string, std::string >& headers );
void yield( const Bytes& data, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr );
void yield( const std::string& data, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr );
void yield( const Response& response, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr );
void yield( const int status, const std::string& body, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr );
void yield( const int status, const Bytes& body = { }, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr );
void yield( const int status, const std::multimap< std::string, std::string >& headers, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr );
void yield( const int status, const Bytes& body, const std::multimap< std::string, std::string >& headers, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr );
void yield( const int status, const std::string& body, const std::multimap< std::string, std::string >& headers, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr );
void fetch( const std::size_t length, const std::function< void ( const std::shared_ptr< Session >, const Bytes& ) >& callback );
void fetch( const std::string& delimiter, const std::function< void ( const std::shared_ptr< Session >, const Bytes& ) >& callback );
void upgrade( const int status, const std::function< void ( const std::shared_ptr< WebSocket > ) >& callback );
void upgrade( const int status, const Bytes& body, const std::function< void ( const std::shared_ptr< WebSocket > ) >& callback );
void upgrade( const int status, const std::string& body, const std::function< void ( const std::shared_ptr< WebSocket > ) >& callback );
void upgrade( const int status, const std::multimap< std::string, std::string >& headers, const std::function< void ( const std::shared_ptr< WebSocket > ) >& callback );
void upgrade( const int status, const Bytes& body, const std::multimap< std::string, std::string >& headers, const std::function< void ( const std::shared_ptr< WebSocket > ) >& callback );
void upgrade( const int status, const std::string& body, const std::multimap< std::string, std::string >& headers, const std::function< void ( const std::shared_ptr< WebSocket > ) >& callback );
void sleep_for( const std::chrono::milliseconds& delay, const std::function< void ( const std::shared_ptr< Session > ) >& callback );
//Getters
const std::string& get_id( void ) const;
const std::string get_origin( void ) const;
const std::string get_destination( void ) const;
const std::shared_ptr< const Request > get_request( void ) const;
const std::shared_ptr< const Resource > get_resource( void ) const;
const std::multimap< std::string, std::string >& get_headers( void ) const;
const ContextValue& get( const std::string& name ) const;
const ContextValue& get( const std::string& name, const ContextValue& default_value ) const;
//Setters
void set_id( const std::string& value );
void set( const std::string& name, const ContextValue& value );
void add_header( const std::string& name, const std::string& value );
void set_header( const std::string& name, const std::string& value );
void set_headers( const std::multimap< std::string, std::string >& values );
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
friend detail::ServiceImpl;
friend detail::SessionImpl;
friend detail::WebSocketManagerImpl;
//Definitions
//Constructors
Session( void ) = delete;
Session( const Session& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
Session& operator =( const Session& value ) = delete;
//Properties
std::shared_ptr< detail::SessionImpl > m_pimpl;
};
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
#include <ciso646>
//Project Includes
#include "corvusoft/restbed/string.hpp"
#include "corvusoft/restbed/session.hpp"
#include "corvusoft/restbed/settings.hpp"
#include "corvusoft/restbed/session_manager.hpp"
//External Includes
//System Namespaces
using std::string;
using std::function;
using std::to_string;
using std::shared_ptr;
using std::make_shared;
//Project Namespaces
//External Namespaces
namespace restbed
{
SessionManager::SessionManager( void )
{
return;
}
SessionManager::~SessionManager( void )
{
return;
}
void SessionManager::stop( void )
{
return;
}
void SessionManager::start( const shared_ptr< const Settings >& )
{
return;
}
void SessionManager::create( const function< void ( const shared_ptr< Session > ) >& callback )
{
callback( make_shared< Session >( String::empty ) );
}
void SessionManager::load( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback )
{
callback( session );
}
void SessionManager::save( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback )
{
callback( session );
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <memory>
#include <functional>
//Project Includes
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define SESSION_MANAGER_EXPORT __declspec(dllexport)
#else
#define SESSION_MANAGER_EXPORT __declspec(dllimport)
#endif
#else
#define SESSION_MANAGER_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class Session;
class Settings;
namespace detail
{
struct SessionManagerImpl;
}
class SESSION_MANAGER_EXPORT SessionManager
{
public:
//Friends
//Definitions
//Constructors
SessionManager( void );
virtual ~SessionManager( void );
//Functionality
virtual void stop( void );
virtual void start( const std::shared_ptr< const Settings >& settings );
virtual void create( const std::function< void ( const std::shared_ptr< Session > ) >& callback );
virtual void load( const std::shared_ptr< Session > session, const std::function< void ( const std::shared_ptr< Session > ) >& callback );
virtual void save( const std::shared_ptr< Session > session, const std::function< void ( const std::shared_ptr< Session > ) >& callback );
//Getters
//Setters
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
SessionManager( const SessionManager& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
SessionManager& operator =( const SessionManager& value ) = delete;
//Properties
private:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
};
}

View File

@@ -0,0 +1,230 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
//Project Includes
#include "corvusoft/restbed/settings.hpp"
#include "corvusoft/restbed/detail/settings_impl.hpp"
//External Includes
//System Namespaces
using std::map;
using std::string;
using std::multimap;
using std::make_pair;
using std::unique_ptr;
using std::shared_ptr;
using std::chrono::seconds;
using std::chrono::milliseconds;
using std::chrono::duration_cast;
//Project Namespaces
using restbed::detail::SettingsImpl;
//External Namespaces
namespace restbed
{
Settings::Settings( void ) : m_pimpl( new SettingsImpl )
{
return;
}
Settings::~Settings( void )
{
return;
}
uint16_t Settings::get_port( void ) const
{
return m_pimpl->m_port;
}
string Settings::get_root( void ) const
{
return m_pimpl->m_root;
}
bool Settings::get_reuse_address( void ) const
{
return m_pimpl->m_reuse_address;
}
unsigned int Settings::get_worker_limit( void ) const
{
return m_pimpl->m_worker_limit;
}
unsigned int Settings::get_connection_limit( void ) const
{
return m_pimpl->m_connection_limit;
}
string Settings::get_bind_address( void ) const
{
return m_pimpl->m_bind_address;
}
bool Settings::get_case_insensitive_uris( void ) const
{
return m_pimpl->m_case_insensitive_uris;
}
milliseconds Settings::get_connection_timeout( void ) const
{
return m_pimpl->m_connection_timeout;
}
bool Settings::get_keep_alive( void ) const
{
return m_pimpl->m_keep_alive;
}
uint32_t Settings::get_keep_alive_start( void ) const
{
return m_pimpl->m_keep_alive_start;
}
uint32_t Settings::get_keep_alive_interval( void ) const
{
return m_pimpl->m_keep_alive_interval;
}
uint32_t Settings::get_keep_alive_cnt( void ) const
{
return m_pimpl->m_keep_alive_cnt;
}
string Settings::get_status_message( const int code ) const
{
return ( m_pimpl->m_status_messages.count( code ) ) ? m_pimpl->m_status_messages.at( code ) : "No Appropriate Status Message Found";
}
map< int, string > Settings::get_status_messages( void ) const
{
return m_pimpl->m_status_messages;
}
string Settings::get_property( const string& name ) const
{
return ( m_pimpl->m_properties.count( name ) ) ? m_pimpl->m_properties.at( name ) : "";
}
map< string, string > Settings::get_properties( void ) const
{
return m_pimpl->m_properties;
}
shared_ptr< const SSLSettings > Settings::get_ssl_settings( void ) const
{
return m_pimpl->m_ssl_settings;
}
multimap< string, string > Settings::get_default_headers( void ) const
{
return m_pimpl->m_default_headers;
}
void Settings::set_port( const uint16_t value )
{
m_pimpl->m_port = value;
}
void Settings::set_root( const string& value )
{
m_pimpl->m_root = value;
}
void Settings::set_reuse_address( const bool value )
{
m_pimpl->m_reuse_address = value;
}
void Settings::set_worker_limit( const unsigned int value )
{
m_pimpl->m_worker_limit = value;
}
void Settings::set_connection_limit( const unsigned int value )
{
m_pimpl->m_connection_limit = value;
}
void Settings::set_bind_address( const string& value )
{
m_pimpl->m_bind_address = value;
}
void Settings::set_case_insensitive_uris( const bool value )
{
m_pimpl->m_case_insensitive_uris = value;
}
void Settings::set_connection_timeout( const seconds& value )
{
m_pimpl->m_connection_timeout = duration_cast< milliseconds >( value );
}
void Settings::set_connection_timeout( const milliseconds& value )
{
m_pimpl->m_connection_timeout = value;
}
void Settings::set_keep_alive( bool value )
{
m_pimpl->m_keep_alive = value;
}
void Settings::set_keep_alive_start( const uint32_t value )
{
m_pimpl->m_keep_alive_start = value;
}
void Settings::set_keep_alive_interval( const uint32_t value )
{
m_pimpl->m_keep_alive_interval = value;
}
void Settings::set_keep_alive_cnt( const uint32_t value )
{
m_pimpl->m_keep_alive_cnt = value;
}
void Settings::set_status_message( const int code, const string& message )
{
m_pimpl->m_status_messages[ code ] = message;
}
void Settings::set_status_messages( const map< int, string >& values )
{
m_pimpl->m_status_messages = values;
}
void Settings::set_property( const string& name, const string& value )
{
m_pimpl->m_properties[ name ] = value;
}
void Settings::set_properties( const map< string, string >& values )
{
m_pimpl->m_properties = values;
}
void Settings::set_ssl_settings( const shared_ptr< const SSLSettings >& values )
{
m_pimpl->m_ssl_settings = values;
}
void Settings::set_default_header( const string& name, const string& value )
{
m_pimpl->m_default_headers.insert( make_pair( name, value ) );
}
void Settings::set_default_headers( const multimap< string, string >& values )
{
m_pimpl->m_default_headers = values;
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <map>
#include <chrono>
#include <memory>
#include <string>
#include <cstdint>
//Project Includes
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define SETTINGS_EXPORT __declspec(dllexport)
#else
#define SETTINGS_EXPORT __declspec(dllimport)
#endif
#else
#define SETTINGS_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class SSLSettings;
namespace detail
{
struct SettingsImpl;
}
class SETTINGS_EXPORT Settings
{
public:
//Friends
//Definitions
//Constructors
Settings( void );
virtual ~Settings( void );
//Functionality
//Getters
uint16_t get_port( void ) const;
std::string get_root( void ) const;
bool get_reuse_address( void ) const;
unsigned int get_worker_limit( void ) const;
unsigned int get_connection_limit( void ) const;
std::string get_bind_address( void ) const;
bool get_case_insensitive_uris( void ) const;
std::chrono::milliseconds get_connection_timeout( void ) const;
bool get_keep_alive( void ) const;
uint32_t get_keep_alive_start( void ) const;
uint32_t get_keep_alive_interval( void ) const;
uint32_t get_keep_alive_cnt( void ) const;
std::string get_status_message( const int code ) const;
std::map< int, std::string > get_status_messages( void ) const;
std::string get_property( const std::string& name ) const;
std::map< std::string, std::string > get_properties( void ) const;
std::shared_ptr< const SSLSettings > get_ssl_settings( void ) const;
std::multimap< std::string, std::string > get_default_headers( void ) const;
//Setters
void set_port( const uint16_t value );
void set_root( const std::string& value );
void set_reuse_address( const bool value );
void set_worker_limit( const unsigned int value );
void set_connection_limit( const unsigned int value );
void set_bind_address( const std::string& value );
void set_case_insensitive_uris( const bool value );
void set_connection_timeout( const std::chrono::seconds& value );
void set_connection_timeout( const std::chrono::milliseconds& value );
void set_keep_alive( bool value );
void set_keep_alive_start( const uint32_t value );
void set_keep_alive_interval( const uint32_t value );
void set_keep_alive_cnt( const uint32_t value );
void set_status_message( const int code, const std::string& message );
void set_status_messages( const std::map< int, std::string >& values );
void set_property( const std::string& name, const std::string& value );
void set_properties( const std::map< std::string, std::string >& values );
void set_ssl_settings( const std::shared_ptr< const SSLSettings >& value );
void set_default_header( const std::string& name, const std::string& value );
void set_default_headers( const std::multimap< std::string, std::string >& values );
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
Settings( const Settings& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
Settings& operator =( const Settings& value ) = delete;
//Properties
std::unique_ptr< detail::SettingsImpl > m_pimpl;
};
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
//Project Includes
#include "corvusoft/restbed/uri.hpp"
#include "corvusoft/restbed/string.hpp"
#include "corvusoft/restbed/ssl_settings.hpp"
#include "corvusoft/restbed/detail/ssl_settings_impl.hpp"
//External Includes
//System Namespaces
using std::string;
using std::unique_ptr;
//Project Namespaces
using restbed::detail::SSLSettingsImpl;
//External Namespaces
namespace restbed
{
SSLSettings::SSLSettings( void ) : m_pimpl( new SSLSettingsImpl )
{
return;
}
SSLSettings::~SSLSettings( void )
{
return;
}
bool SSLSettings::has_disabled_http( void ) const
{
return m_pimpl->m_http_disabled;
}
bool SSLSettings::has_enabled_sslv2( void ) const
{
return m_pimpl->m_sslv2_enabled;
}
bool SSLSettings::has_enabled_sslv3( void ) const
{
return m_pimpl->m_sslv3_enabled;
}
bool SSLSettings::has_enabled_tlsv1( void ) const
{
return m_pimpl->m_tlsv1_enabled;
}
bool SSLSettings::has_enabled_tlsv11( void ) const
{
return m_pimpl->m_tlsv11_enabled;
}
bool SSLSettings::has_enabled_tlsv12( void ) const
{
return m_pimpl->m_tlsv12_enabled;
}
bool SSLSettings::has_enabled_compression( void ) const
{
return m_pimpl->m_compression_enabled;
}
bool SSLSettings::has_enabled_default_workarounds( void ) const
{
return m_pimpl->m_default_workarounds_enabled;
}
bool SSLSettings::has_enabled_single_diffie_hellman_use( void ) const
{
return m_pimpl->m_single_diffie_hellman_use_enabled;
}
uint16_t SSLSettings::get_port( void ) const
{
return m_pimpl->m_port;
}
string SSLSettings::get_bind_address( void ) const
{
return m_pimpl->m_bind_address;
}
string SSLSettings::get_certificate( void ) const
{
return m_pimpl->m_certificate;
}
string SSLSettings::get_passphrase( void ) const
{
return m_pimpl->m_passphrase;
}
string SSLSettings::get_private_key( void ) const
{
return m_pimpl->m_private_key;
}
string SSLSettings::get_private_rsa_key( void ) const
{
return m_pimpl->m_private_rsa_key;
}
string SSLSettings::get_certificate_chain( void ) const
{
return m_pimpl->m_certificate_chain;
}
string SSLSettings::get_temporary_diffie_hellman( void ) const
{
return m_pimpl->m_temporary_diffie_hellman;
}
string SSLSettings::get_certificate_authority_pool( void ) const
{
return m_pimpl->m_certificate_authority_pool;
}
void SSLSettings::set_port( const uint16_t value )
{
m_pimpl->m_port = value;
}
void SSLSettings::set_bind_address( const string& value )
{
m_pimpl->m_bind_address = value;
}
void SSLSettings::set_http_disabled( const bool value )
{
m_pimpl->m_http_disabled = value;
}
void SSLSettings::set_sslv2_enabled( const bool value )
{
m_pimpl->m_sslv2_enabled = value;
}
void SSLSettings::set_sslv3_enabled( const bool value )
{
m_pimpl->m_sslv3_enabled = value;
}
void SSLSettings::set_tlsv1_enabled( const bool value )
{
m_pimpl->m_tlsv1_enabled = value;
}
void SSLSettings::set_tlsv11_enabled( const bool value )
{
m_pimpl->m_tlsv11_enabled = value;
}
void SSLSettings::set_tlsv12_enabled( const bool value )
{
m_pimpl->m_tlsv12_enabled = value;
}
void SSLSettings::set_compression_enabled( const bool value )
{
m_pimpl->m_compression_enabled = value;
}
void SSLSettings::set_default_workarounds_enabled( const bool value )
{
m_pimpl->m_default_workarounds_enabled = value;
}
void SSLSettings::set_single_diffie_hellman_use_enabled( const bool value )
{
m_pimpl->m_single_diffie_hellman_use_enabled = value;
}
void SSLSettings::set_certificate( const Uri& value )
{
m_pimpl->m_certificate = Uri::decode( String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ) );
}
void SSLSettings::set_certificate_chain( const Uri& value )
{
m_pimpl->m_certificate_chain = Uri::decode( String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ) );
}
void SSLSettings::set_certificate_authority_pool( const Uri& value )
{
m_pimpl->m_certificate_authority_pool = Uri::decode( String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ) );
}
void SSLSettings::set_passphrase( const string& value )
{
m_pimpl->m_passphrase = value;
}
void SSLSettings::set_private_key( const Uri& value )
{
m_pimpl->m_private_key = Uri::decode( String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ) );
}
void SSLSettings::set_private_rsa_key( const Uri& value )
{
m_pimpl->m_private_rsa_key = Uri::decode( String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ) );
}
void SSLSettings::set_temporary_diffie_hellman( const Uri& value )
{
m_pimpl->m_temporary_diffie_hellman = Uri::decode( String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ) );
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <string>
#include <memory>
#include <cstdint>
//Project Includes
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define SSL_SETTINGS_EXPORT __declspec(dllexport)
#else
#define SSL_SETTINGS_EXPORT __declspec(dllimport)
#endif
#else
#define SSL_SETTINGS_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class Uri;
namespace detail
{
struct SSLSettingsImpl;
}
class SSL_SETTINGS_EXPORT SSLSettings
{
public:
//Friends
//Definitions
//Constructors
SSLSettings( void );
virtual ~SSLSettings( void );
//Functionality
bool has_disabled_http( void ) const;
bool has_enabled_sslv2( void ) const;
bool has_enabled_sslv3( void ) const;
bool has_enabled_tlsv1( void ) const;
bool has_enabled_tlsv11( void ) const;
bool has_enabled_tlsv12( void ) const;
bool has_enabled_compression( void ) const;
bool has_enabled_default_workarounds( void ) const;
bool has_enabled_single_diffie_hellman_use( void ) const;
//Getters
uint16_t get_port( void ) const;
std::string get_bind_address( void ) const;
std::string get_certificate( void ) const;
std::string get_passphrase( void ) const;
std::string get_private_key( void ) const;
std::string get_private_rsa_key( void ) const;
std::string get_certificate_chain( void ) const;
std::string get_temporary_diffie_hellman( void ) const;
std::string get_certificate_authority_pool( void ) const;
//Setters
void set_port( const uint16_t value );
void set_bind_address( const std::string& value );
void set_http_disabled( const bool value );
void set_sslv2_enabled( const bool value );
void set_sslv3_enabled( const bool value );
void set_tlsv1_enabled( const bool value );
void set_tlsv11_enabled( const bool value );
void set_tlsv12_enabled( const bool value );
void set_compression_enabled( const bool value );
void set_default_workarounds_enabled( const bool value );
void set_single_diffie_hellman_use_enabled( const bool value );
void set_certificate( const Uri& value );
void set_certificate_chain( const Uri& value );
void set_certificate_authority_pool( const Uri& value );
void set_passphrase( const std::string& value );
void set_private_key( const Uri& value );
void set_private_rsa_key( const Uri& value );
void set_temporary_diffie_hellman( const Uri& value );
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
SSLSettings( const SSLSettings& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
SSLSettings& operator =( const SSLSettings& value ) = delete;
//Properties
std::unique_ptr< detail::SSLSettingsImpl > m_pimpl;
};
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
//Project Includes
//External Includes
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
enum : int
{
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
RESET_CONTENT = 205,
PARTIAL_CONTENT = 206,
MULTI_STATUS = 207,
ALREADY_REPORTED = 208,
IM_USED = 226,
MULTIPLE_CHOICES = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
USE_PROXY = 305,
RESERVED = 306,
TEMPORARY_REDIRECT = 307,
PERMANENT_REDIRECT = 308,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
PAYMENT_REQUIRED = 402,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
PROXY_AUTHENTICATION_REQUIRED = 407,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PRECONDITION_FAILED = 412,
REQUEST_ENTITY_TOO_LARGE = 413,
REQUEST_URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
REQUESTED_RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
UNPROCESSABLE_ENTITY = 422,
LOCKED = 423,
FAILED_DEPENDENCY = 424,
UPGRADE_REQUIRED = 426,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
VARIANT_ALSO_NEGOTIATES = 506,
INSUFFICIENT_STORAGE = 507,
LOOP_DETECTED = 508,
NOT_EXTENDED = 510,
NETWORK_AUTHENTICATION_REQUIRED = 511
};
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
#include <regex>
#include <memory>
#include <ciso646>
#include <algorithm>
//Project Includes
#include "corvusoft/restbed/string.hpp"
//External Includes
//System Namespaces
using std::regex;
using std::string;
using std::vector;
using std::smatch;
using std::multimap;
using std::transform;
using std::unique_ptr;
using std::regex_match;
using std::regex_replace;
using std::regex_constants::icase;
//Project Namespaces
//External Namespaces
namespace restbed
{
const string String::empty = "";
Bytes String::to_bytes( const string& value )
{
return Bytes( value.begin( ), value.end( ) );
}
string String::to_string( const Bytes& value )
{
return string( value.begin( ), value.end( ) );
}
string String::lowercase( const string& value )
{
string result = "";
transform( value.begin( ), value.end( ), back_inserter( result ), [ ]( const char value ) { return static_cast< char >( tolower( value ) ); } );
return result;
}
string String::uppercase( const string& value )
{
string result = "";
transform( value.begin( ), value.end( ), back_inserter( result ), [ ]( const char value ) { return static_cast< char >( toupper( value ) ); } );
return result;
}
string String::format( const char* format, ... )
{
va_list arguments;
va_start( arguments, format );
string formatted = "";
string::size_type length = FORMAT_BUFFER_INITIAL_LENGTH;
string::size_type required_length = String::format( formatted, length, format, arguments );
va_end( arguments );
if ( required_length > length )
{
va_start( arguments, format );
String::format( formatted, required_length, format, arguments );
va_end( arguments );
}
return formatted;
}
vector< string > String::split( const string& value, const char delimiter )
{
vector< string > tokens;
string::size_type start = 0;
string::size_type end = 0;
while ( ( end = value.find( delimiter, start ) ) not_eq string::npos )
{
const auto text = value.substr( start, end - start );
if ( not text.empty( ) )
{
tokens.push_back( text );
}
start = end + 1;
}
const auto token = value.substr( start );
if ( not token.empty( ) )
{
tokens.push_back( token );
}
return tokens;
}
string String::join( const multimap< string, string >& values, const string& pair_delimiter, const string& delimiter )
{
string result = "";
for ( auto value : values )
{
result += value.first + pair_delimiter + value.second + delimiter;
}
if ( not result.empty( ) )
{
const size_t position = result.find_last_not_of( delimiter );
if ( string::npos not_eq position )
{
result = result.substr( 0, position + 1 );
}
}
return result;
}
string String::remove( const string& target, const string& value, const Option option )
{
return replace( target, "", value, option );
}
string String::replace( const string& target, const string& substitute, const string& value, const Option option )
{
if ( target.empty( ) )
{
return value;
}
static const regex escape( "([.^$|()\\[\\]{}*+?\\\\])" );
const auto expression = regex_replace( target, escape, "\\$1" );
auto pattern = regex( expression );
if ( option & String::Option::CASE_INSENSITIVE )
{
pattern.assign( expression, icase );
}
smatch match;
string result = value;
while ( regex_search( result, match, pattern ) )
{
result = regex_replace( result, pattern, substitute );
}
return result;
}
string::size_type String::format( string& output, const string::size_type length, const char* format, va_list arguments )
{
unique_ptr< char[ ] > formatted( new char[ length + 1 ] );
int required_length = vsnprintf( formatted.get( ), length + 1, format, arguments );
if ( required_length == -1 )
{
required_length = 0;
}
output = formatted.get( );
return required_length;
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <map>
#include <string>
#include <vector>
#include <cstdarg>
//Project Includes
#include <corvusoft/restbed/byte.hpp>
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define STRING_EXPORT __declspec(dllexport)
#else
#define STRING_EXPORT __declspec(dllimport)
#endif
#else
#define STRING_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class STRING_EXPORT String
{
public:
//Friends
//Definitions
enum Option : int
{
CASE_SENSITIVE = 0,
CASE_INSENSITIVE = 1
};
static const size_t FORMAT_BUFFER_INITIAL_LENGTH = 2048;
//Constructors
//Functionality
static Bytes to_bytes( const std::string& value );
static std::string to_string( const Bytes& value );
static std::string lowercase( const std::string& value );
static std::string uppercase( const std::string& value );
static std::string format( const char* format, ... );
static std::vector< std::string > split( const std::string& text, const char delimiter );
static std::string join( const std::multimap< std::string, std::string >& values, const std::string& pair_delimiter, const std::string& delimiter );
static std::string remove( const std::string& needle, const std::string& haystack, const Option option = CASE_SENSITIVE );
static std::string replace( const std::string& target, const std::string& substitute, const std::string& value, const Option option = CASE_SENSITIVE );
//Getters
//Setters
//Operators
//Properties
static const std::string empty;
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
String( void ) = delete;
String( const String& original ) = delete;
virtual ~String( void ) = delete;
//Functionality
static std::string::size_type format( std::string& output, const std::string::size_type length, const char* format, va_list arguments );
//Getters
//Setters
//Operators
String& operator =( const String& value ) = delete;
//Properties
};
}

View File

@@ -0,0 +1,402 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
#include <regex>
#include <cstdio>
#include <cstdlib>
#include <stdexcept>
#if defined(_WIN32)
#include <ciso646>
#include <winsock2.h>
#pragma comment( lib, "Ws2_32.lib" )
#else
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#endif
//Project Includes
#include "corvusoft/restbed/uri.hpp"
#include "corvusoft/restbed/string.hpp"
#include "corvusoft/restbed/detail/uri_impl.hpp"
//External Includes
//System Namespaces
using std::stoi;
using std::regex;
using std::smatch;
using std::strtol;
using std::string;
using std::multimap;
using std::snprintf;
using std::to_string;
using std::unique_ptr;
using std::runtime_error;
using std::invalid_argument;
//Project Namespaces
using restbed::detail::UriImpl;
//External Namespaces
namespace restbed
{
Uri::Uri( const string& value, bool relative ) : m_pimpl( new UriImpl )
{
if ( not is_valid( value ) )
{
throw invalid_argument( "Argument is not a valid URI: " + value );
}
m_pimpl->m_uri = value;
m_pimpl->m_relative = relative;
}
Uri::Uri( const Uri& original ) : m_pimpl( new UriImpl( *original.m_pimpl ) )
{
return;
}
Uri::~Uri( void )
{
return;
}
bool Uri::is_relative( void ) const
{
return m_pimpl->m_relative;
}
bool Uri::is_absolute( void ) const
{
return not m_pimpl->m_relative;
}
string Uri::to_string( void ) const
{
return m_pimpl->m_uri;
}
bool Uri::is_valid( const string& value )
{
static const regex pattern( "^[a-zA-Z][a-zA-Z0-9+\\-.]*://(([a-zA-Z0-9\\-._~%!$&'()*+,;=]+)(:([a-zA-Z0-9\\-._~%!$&'()*+,;=]+))?@)?([a-zA-Z0-9\\-._~%!$&'()*+,;=:\\[\\]]*(:[0-9]+)?)?[a-zA-Z0-9\\-._~%!$&'()*+,;=:@/]+(\\?[a-zA-Z0-9\\-._~%!$&'()*+,;=:@/]*)?(#[a-zA-Z0-9\\-._~%!$&'()*+,;=:@/?]*)?$" );
return regex_match( value, pattern );
}
Uri Uri::parse( const string& value )
{
return Uri( value );
}
string Uri::decode( const Bytes& value )
{
return decode( string( value.begin( ), value.end( ) ) );
}
string Uri::decode( const string& value )
{
static const char hex_to_dec[256] =
{
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1,
/* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
};
if( value.size( ) < 3 ) {
return value;
}
string::size_type valuesize = value.size( );
string result;
result.reserve( valuesize );
char c1 = 0;
char c2 = 0;
unsigned char cindex = 0;
string::size_type index = 0;
for ( ; index < ( valuesize - 2 ); index++ )
{
if ( value[index] == '%' )
{
cindex = value[ index + 1 ];
c1 = hex_to_dec[ cindex ];
cindex = value[ index + 2 ];
c2 = hex_to_dec[ cindex ];
if ( c1 != -1 && c2 != -1 )
{
result.push_back( ( c1 << 4 ) + c2 );
index += 2;
continue;
}
}
result.push_back( value[ index ] );
}
for ( ; index < valuesize; index++ )
{
result.push_back( value[ index ] );
}
return result;
}
string Uri::decode_parameter( const string& value )
{
return decode( String::replace( "+", " ", value ) );
}
string Uri::encode( const Bytes& value )
{
const bool unsafe_carachters[256] =
{
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0 */ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
/* 1 */ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
/* 2 */ 1,0,1,1, 1,1,1,0, 0,0,0,1, 1,0,0,1,
/* 3 */ 0,0,0,0, 0,0,0,0, 0,0,1,1, 1,1,1,1,
/* 4 */ 1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* 5 */ 0,0,0,0, 0,0,0,0, 0,0,0,1, 1,1,1,0,
/* 6 */ 1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* 7 */ 0,0,0,0, 0,0,0,0, 0,0,0,1, 1,1,0,1,
/* 8 */ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
/* 9 */ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
/* A */ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
/* B */ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
/* C */ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
/* D */ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
/* E */ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
/* F */ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1
};
static const char dec_to_hex[] = "0123456789ABCDEF";
string encoded;
encoded.reserve( value.size() );
for ( Byte character : value )
{
if ( unsafe_carachters[ character ] )
{
encoded.push_back( '%' );
encoded.push_back( dec_to_hex[ character >> 4 ] );
encoded.push_back( dec_to_hex[ character & 0x0F ]);
}
else
{
encoded.push_back( character );
}
}
return encoded;
}
string Uri::encode( const string& value )
{
return encode( Bytes( value.begin( ), value.end( ) ) );
}
string Uri::encode_parameter( const string& value )
{
return encode( value );
}
uint16_t Uri::get_port( void ) const
{
smatch match;
string port = String::empty;
static const regex pattern( "^[a-zA-Z][a-zA-Z0-9+\\-.]*://(([a-zA-Z0-9\\-._~%!$&'()*+,;=]+)(:([a-zA-Z0-9\\-._~%!$&'()*+,;=]+))?@)?([a-zA-Z0-9\\-._~%]+|\\[[a-zA-Z0-9\\-._~%!$&'()*+,;=:]+\\]):([0-9]+)" );
if ( regex_search( m_pimpl->m_uri, match, pattern ) )
{
port = match[ 6 ];
}
else
{
const auto scheme = get_scheme( );
if ( not scheme.empty( ) )
{
const struct servent* entry = getservbyname( scheme.data( ), nullptr );
if ( entry not_eq nullptr )
{
port = ::to_string( ntohs( entry->s_port ) );
}
}
}
if ( port.empty( ) )
{
return 0;
}
return static_cast< uint16_t >( stoi( port ) );
}
string Uri::get_path( void ) const
{
static const regex pattern( "^([a-zA-Z][a-zA-Z0-9+\\-.]*://([^/?#]+)?)?([a-zA-Z0-9\\-._~%!$&'()*+,;=:@/]*)" );
smatch match;
if ( regex_search( m_pimpl->m_uri, match, pattern ) )
{
return ( is_absolute( ) ) ? match[ 3 ] : string( match[ 2 ] ) + string( match[ 3 ] );
}
return String::empty;
}
string Uri::get_query( void ) const
{
smatch match;
static const regex pattern( "^[^?#]+\\?([^#]+)" );
if ( regex_search( m_pimpl->m_uri, match, pattern ) )
{
return match[ 1 ];
}
return String::empty;
}
string Uri::get_scheme( void ) const
{
smatch match;
static const regex pattern( "^([a-zA-Z][a-zA-Z0-9+\\-.]*):" );
if ( regex_search( m_pimpl->m_uri, match, pattern ) )
{
return match[ 1 ];
}
return String::empty;
}
string Uri::get_fragment( void ) const
{
smatch match;
static const regex pattern( "#(.+)" );
if ( regex_search( m_pimpl->m_uri, match, pattern ) )
{
return match[ 1 ];
}
return String::empty;
}
string Uri::get_username( void ) const
{
smatch match;
static const regex pattern( "^[a-zA-Z0-9+\\-.]+://([a-zA-Z0-9\\-._~%!$&'()*+,;=]+)(:([a-zA-Z0-9\\-._~%!$&'()*+,;=]+))?@" );
if ( regex_search( m_pimpl->m_uri, match, pattern ) )
{
return match[ 1 ];
}
return String::empty;
}
string Uri::get_password( void ) const
{
smatch match;
static const regex pattern( "^[a-zA-Z0-9+\\-.]+://([a-zA-Z0-9\\-._~%!$&'()*+,;=]+):([a-zA-Z0-9\\-._~%!$&'()*+,;=]+)@" );
if ( regex_search( m_pimpl->m_uri, match, pattern ) )
{
return match[ 2 ];
}
return String::empty;
}
string Uri::get_authority( void ) const
{
string authority = String::empty;
if ( is_relative( ) ) return authority;
smatch match;
static const regex pattern( "^[a-zA-Z][a-zA-Z0-9+\\-.]*://(([a-zA-Z0-9\\-._~%!$&'()*+,;=]+)(:([a-zA-Z0-9\\-._~%!$&'()*+,;=]+))?@)?([a-zA-Z0-9\\-._~%]+|\\[[a-zA-Z0-9\\-._~%!$&'()*+,;=:]+\\])" );
if ( regex_search( m_pimpl->m_uri, match, pattern ) ) authority = match[ 5 ];
else return authority;
if ( authority.front( ) == '[' ) authority.erase( 0, 1 );
if ( authority.back( ) == ']' ) authority.erase( authority.length( ) - 1, 1 );
return authority;
}
multimap< string, string > Uri::get_query_parameters( void ) const
{
multimap< string, string > parameters;
auto query = String::split( get_query( ), '&' );
for ( auto parameter : query )
{
auto index = parameter.find_first_of( '=' );
auto name = decode_parameter( parameter.substr( 0, index ) );
auto value = (index not_eq string::npos) ? decode_parameter( parameter.substr( index + 1, parameter.length( ) ) ) : "";
parameters.insert( make_pair( name, value ) );
}
return parameters;
}
Uri& Uri::operator =( const Uri& rhs )
{
m_pimpl->m_uri = rhs.m_pimpl->m_uri;
return *this;
}
bool Uri::operator <( const Uri& rhs ) const
{
return m_pimpl->m_uri < rhs.m_pimpl->m_uri;
}
bool Uri::operator >( const Uri& rhs ) const
{
return m_pimpl->m_uri > rhs.m_pimpl->m_uri;
}
bool Uri::operator ==( const Uri& rhs ) const
{
return m_pimpl->m_uri == rhs.m_pimpl->m_uri;
}
bool Uri::operator !=( const Uri& rhs ) const
{
return m_pimpl->m_uri not_eq rhs.m_pimpl->m_uri;
}
Uri::Uri( void ) : m_pimpl( new UriImpl )
{
return;
}
}

View File

@@ -0,0 +1,152 @@
/*
* 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
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define URI_EXPORT __declspec(dllexport)
#else
#define URI_EXPORT __declspec(dllimport)
#endif
#else
#define URI_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
namespace detail
{
struct UriImpl;
}
class URI_EXPORT Uri
{
public:
//Friends
//Definitions
static const bool Relative = true;
static const bool Absolute = false;
//Constructors
explicit Uri( const std::string& value, bool relative = false );
Uri( const Uri& original );
virtual ~Uri( void );
//Functionality
bool is_relative( void ) const;
bool is_absolute( void ) const;
std::string to_string( void ) const;
static bool is_valid( const std::string& value );
static Uri parse( const std::string& value );
static std::string decode( const Bytes& value );
static std::string decode( const std::string& value );
static std::string decode_parameter( const std::string& value );
static std::string encode( const Bytes& value );
static std::string encode( const std::string& value );
static std::string encode_parameter( const std::string& value );
//Getters
uint16_t get_port( void ) const;
std::string get_path( void ) const;
std::string get_query( void ) const;
std::string get_scheme( void ) const;
std::string get_fragment( void ) const;
std::string get_username( void ) const;
std::string get_password( void ) const;
std::string get_authority( void ) const;
std::multimap< std::string, std::string > get_query_parameters( void ) const;
//Setters
//Operators
Uri& operator =( const Uri& rhs );
bool operator <( const Uri& rhs ) const;
bool operator >( const Uri& rhs ) const;
bool operator ==( const Uri& rhs ) const;
bool operator !=( const Uri& rhs ) const;
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
Uri( void );
//Functionality
//Getters
//Setters
//Operators
//Properties
std::unique_ptr< detail::UriImpl > m_pimpl;
};
}

View File

@@ -0,0 +1,209 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
#include <ciso646>
//Project Includes
#include "corvusoft/restbed/string.hpp"
#include "corvusoft/restbed/web_socket.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::ref;
using std::bind;
using std::string;
using std::function;
using std::error_code;
using std::shared_ptr;
using std::make_shared;
using std::placeholders::_1;
//Project Namespaces
using restbed::detail::SocketImpl;
using restbed::detail::WebSocketImpl;
using restbed::detail::WebSocketManagerImpl;
//External Namespaces
namespace restbed
{
WebSocket::WebSocket( void ) : m_pimpl( new WebSocketImpl )
{
return;
}
WebSocket::~WebSocket( void )
{
try
{
close( );
}
catch ( ... )
{
m_pimpl->log( Logger::WARNING, "Service failed graceful wind down." );
}
}
bool WebSocket::is_open( void ) const
{
return m_pimpl->m_socket not_eq nullptr and m_pimpl->m_socket->is_open( );
}
bool WebSocket::is_closed( void ) const
{
return m_pimpl->m_socket not_eq nullptr and m_pimpl->m_socket->is_closed( );
}
void WebSocket::close( void )
{
auto socket = shared_from_this( );
if ( m_pimpl->m_close_handler not_eq nullptr )
{
m_pimpl->m_close_handler( socket );
}
m_pimpl->m_manager->destroy( socket );
m_pimpl->m_socket->close( );
}
void WebSocket::send( const Bytes& body, const function< void ( const shared_ptr< WebSocket > ) > callback )
{
send( make_shared< WebSocketMessage >( WebSocketMessage::BINARY_FRAME, body ), callback );
}
void WebSocket::send( const string& body, const function< void ( const shared_ptr< WebSocket > ) > callback )
{
send( make_shared< WebSocketMessage >( WebSocketMessage::TEXT_FRAME, body ), callback );
}
void WebSocket::send( const WebSocketMessage::OpCode opcode, const function< void ( const shared_ptr< WebSocket > ) > callback )
{
send( make_shared< WebSocketMessage >( opcode ), callback );
}
void WebSocket::send( const shared_ptr< WebSocketMessage > message, const function< void ( const shared_ptr< WebSocket > ) > callback )
{
const auto data = m_pimpl->m_manager->compose( message );
m_pimpl->m_socket->start_write( data, [ this, callback ]( const error_code & code, size_t )
{
if ( code )
{
if ( m_pimpl->m_error_handler not_eq nullptr )
{
m_pimpl->m_error_handler( shared_from_this( ), code );
}
return;
}
if ( callback not_eq nullptr )
{
callback( shared_from_this( ) );
}
} );
}
string WebSocket::get_key( void ) const
{
return m_pimpl->m_key;
}
shared_ptr< Logger > WebSocket::get_logger( void ) const
{
return m_pimpl->m_logger;
}
shared_ptr< SocketImpl > WebSocket::get_socket( void ) const
{
return m_pimpl->m_socket;
}
function< void ( const shared_ptr< WebSocket > ) > WebSocket::get_open_handler( void ) const
{
return m_pimpl->m_open_handler;
}
function< void ( const shared_ptr< WebSocket > ) > WebSocket::get_close_handler( void ) const
{
return m_pimpl->m_close_handler;
}
function< void ( const shared_ptr< WebSocket >, const error_code ) > WebSocket::get_error_handler( void ) const
{
return m_pimpl->m_error_handler;
}
function< void ( const shared_ptr< WebSocket >, const shared_ptr< WebSocketMessage > ) > WebSocket::get_message_handler( void ) const
{
return m_pimpl->m_message_handler;
}
void WebSocket::set_key( const string& value )
{
m_pimpl->m_key = value;
}
void WebSocket::set_logger( const shared_ptr< Logger >& value )
{
m_pimpl->m_logger = value;
}
void WebSocket::set_socket( const shared_ptr< SocketImpl >& value )
{
m_pimpl->m_socket = value;
}
void WebSocket::set_open_handler( const function< void ( const shared_ptr< WebSocket > ) >& value )
{
m_pimpl->m_open_handler = value;
}
void WebSocket::set_close_handler( const function< void ( const shared_ptr< WebSocket > ) >& value )
{
if ( value == nullptr )
{
return;
}
m_pimpl->m_close_handler = [ value, this ]( const shared_ptr< WebSocket >& socket )
{
value( socket );
m_pimpl->m_manager->destroy( socket );
};
}
void WebSocket::set_error_handler( const function< void ( const shared_ptr< WebSocket >, const error_code ) >& value )
{
if ( value == nullptr )
{
return;
}
m_pimpl->m_error_handler = [ value, this ]( const shared_ptr< WebSocket > socket, const error_code code )
{
if ( m_pimpl->m_error_handler_invoked == false )
{
value( socket, code );
}
};
}
void WebSocket::set_message_handler( const function< void ( const shared_ptr< WebSocket >, const shared_ptr< WebSocketMessage > ) >& value )
{
if ( value == nullptr )
{
return;
}
m_pimpl->m_message_handler = value;
m_pimpl->listen( shared_from_this( ) );
}
}

View File

@@ -0,0 +1,148 @@
/*
* 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/web_socket_message.hpp>
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define WEB_SOCKET_EXPORT __declspec(dllexport)
#else
#define WEB_SOCKET_EXPORT __declspec(dllimport)
#endif
#else
#define WEB_SOCKET_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
class Logger;
class Session;
namespace detail
{
class SocketImpl;
class WebSocketImpl;
class WebSocketManagerImpl;
}
class WEB_SOCKET_EXPORT WebSocket : public std::enable_shared_from_this< WebSocket >
{
public:
//Friends
//Definitions
//Constructors
WebSocket( void );
~WebSocket( void );
//Functionality
bool is_open( void ) const;
bool is_closed( void ) const;
void close( void );
void send( const Bytes& body, const std::function< void ( const std::shared_ptr< WebSocket > ) > callback = nullptr );
void send( const std::string& body, const std::function< void ( const std::shared_ptr< WebSocket > ) > callback = nullptr );
void send( const WebSocketMessage::OpCode opcode, const std::function< void ( const std::shared_ptr< WebSocket > ) > callback = nullptr );
void send( const std::shared_ptr< WebSocketMessage > message, const std::function< void ( const std::shared_ptr< WebSocket > ) > callback = nullptr );
//Getters
std::string get_key( void ) const;
std::shared_ptr< Logger > get_logger( void ) const;
std::shared_ptr< detail::SocketImpl > get_socket( void ) const;
std::function< void ( const std::shared_ptr< WebSocket > ) > get_open_handler( void ) const;
std::function< void ( const std::shared_ptr< WebSocket > ) > get_close_handler( void ) const;
std::function< void ( const std::shared_ptr< WebSocket >, const std::error_code ) > get_error_handler( void ) const;
std::function< void ( const std::shared_ptr< WebSocket >, const std::shared_ptr< WebSocketMessage > ) > get_message_handler( void ) const;
//Setters
void set_key( const std::string& value );
void set_logger( const std::shared_ptr< Logger >& value );
void set_socket( const std::shared_ptr< detail::SocketImpl >& value );
void set_open_handler( const std::function< void ( const std::shared_ptr< WebSocket > ) >& value );
void set_close_handler( const std::function< void ( const std::shared_ptr< WebSocket > ) >& value );
void set_error_handler( const std::function< void ( const std::shared_ptr< WebSocket >, const std::error_code ) >& value );
void set_message_handler( const std::function< void ( const std::shared_ptr< WebSocket >, const std::shared_ptr< WebSocketMessage > ) >& value );
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
friend Session;
friend detail::WebSocketManagerImpl;
//Definitions
//Constructors
WebSocket( const WebSocket& original ) = delete;
//Functionality
//Getters
//Setters
//Operators
WebSocket& operator =( const WebSocket& value ) = delete;
//Properties
std::unique_ptr< detail::WebSocketImpl > m_pimpl;
};
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
//System Includes
//Project Includes
#include "corvusoft/restbed/string.hpp"
#include "corvusoft/restbed/web_socket_message.hpp"
#include "corvusoft/restbed/detail/web_socket_message_impl.hpp"
//External Includes
//System Namespaces
using std::tuple;
using std::string;
using std::uint8_t;
using std::uint32_t;
using std::uint64_t;
using std::shared_ptr;
using std::make_tuple;
//Project Namespaces
using restbed::detail::WebSocketMessageImpl;
//External Namespaces
namespace restbed
{
WebSocketMessage::WebSocketMessage( void ) : m_pimpl( new WebSocketMessageImpl )
{
return;
}
WebSocketMessage::WebSocketMessage( const WebSocketMessage& original ) : m_pimpl( new WebSocketMessageImpl )
{
*m_pimpl = *original.m_pimpl;
}
WebSocketMessage::WebSocketMessage( const WebSocketMessage::OpCode code, const Bytes& data ) : WebSocketMessage( code, data, 0 )
{
return;
}
WebSocketMessage::WebSocketMessage( const WebSocketMessage::OpCode code, const string& data ) : WebSocketMessage( code, data, 0 )
{
return;
}
WebSocketMessage::WebSocketMessage( const WebSocketMessage::OpCode code, const Bytes& data, const uint32_t mask ) : m_pimpl( new WebSocketMessageImpl )
{
m_pimpl->m_data = data;
m_pimpl->m_mask = mask;
m_pimpl->m_opcode = code;
m_pimpl->m_mask_flag = ( mask == 0 ) ? false : true;
const auto length = data.size( );
if ( length <= 125 )
{
m_pimpl->m_length = static_cast< uint8_t >( length );
}
else
{
m_pimpl->m_extended_length = length;
m_pimpl->m_length = ( length < 65535 ) ? 126 : 127;
}
}
WebSocketMessage::WebSocketMessage( const WebSocketMessage::OpCode code, const string& data, const uint32_t mask ) : WebSocketMessage( code, String::to_bytes( data ), mask )
{
return;
}
WebSocketMessage::~WebSocketMessage( void )
{
return;
}
Bytes WebSocketMessage::get_data( void ) const
{
return m_pimpl->m_data;
}
WebSocketMessage::OpCode WebSocketMessage::get_opcode( void ) const
{
return m_pimpl->m_opcode;
}
uint32_t WebSocketMessage::get_mask( void ) const
{
return m_pimpl->m_mask;
}
uint8_t WebSocketMessage::get_length( void ) const
{
return m_pimpl->m_length;
}
uint64_t WebSocketMessage::get_extended_length( void ) const
{
return m_pimpl->m_extended_length;
}
bool WebSocketMessage::get_mask_flag( void ) const
{
return m_pimpl->m_mask_flag;
}
bool WebSocketMessage::get_final_frame_flag( void ) const
{
return m_pimpl->m_final_frame_flag;
}
tuple< bool, bool, bool > WebSocketMessage::get_reserved_flags( void ) const
{
return make_tuple( m_pimpl->m_reserved_flag_one, m_pimpl->m_reserved_flag_two, m_pimpl->m_reserved_flag_three );
}
void WebSocketMessage::set_data( const Bytes& value )
{
m_pimpl->m_data = value;
}
void WebSocketMessage::set_data( const string& value )
{
m_pimpl->m_data = String::to_bytes( value );
}
void WebSocketMessage::set_opcode( const WebSocketMessage::OpCode value )
{
m_pimpl->m_opcode = value;
}
void WebSocketMessage::set_mask( const uint32_t value )
{
m_pimpl->m_mask = value;
m_pimpl->m_mask_flag = ( value == 0 ) ? false : true;
}
void WebSocketMessage::set_length( const uint8_t value )
{
m_pimpl->m_length = value;
}
void WebSocketMessage::set_extended_length( const uint64_t value )
{
m_pimpl->m_extended_length = value;
}
void WebSocketMessage::set_mask_flag( const bool value )
{
m_pimpl->m_mask_flag = value;
}
void WebSocketMessage::set_final_frame_flag( const bool value )
{
m_pimpl->m_final_frame_flag = value;
}
void WebSocketMessage::set_reserved_flags( const bool rsv1, const bool rsv2, const bool rsv3 )
{
m_pimpl->m_reserved_flag_one = rsv1;
m_pimpl->m_reserved_flag_two = rsv2;
m_pimpl->m_reserved_flag_three = rsv3;
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright 2013-2020, Corvusoft Ltd, All Rights Reserved.
*/
#pragma once
//System Includes
#include <tuple>
#include <string>
#include <memory>
#include <cstdint>
#include <system_error>
//Project Includes
#include <corvusoft/restbed/byte.hpp>
//External Includes
//Windows DLL Exports
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(_WIN64)
#ifdef WIN_DLL_EXPORT
#define WEB_SOCKET_MESSAGE_EXPORT __declspec(dllexport)
#else
#define WEB_SOCKET_MESSAGE_EXPORT __declspec(dllimport)
#endif
#else
#define WEB_SOCKET_MESSAGE_EXPORT
#endif
//System Namespaces
//Project Namespaces
//External Namespaces
namespace restbed
{
//Forward Declarations
namespace detail
{
struct WebSocketMessageImpl;
}
class WEB_SOCKET_MESSAGE_EXPORT WebSocketMessage
{
public:
//Friends
//Definitions
enum OpCode : uint8_t
{
CONTINUATION_FRAME = 0x00,
TEXT_FRAME = 0x01,
BINARY_FRAME = 0x02,
CONNECTION_CLOSE_FRAME = 0x08,
PING_FRAME = 0x09,
PONG_FRAME = 0x0A
};
//Constructors
WebSocketMessage( void );
WebSocketMessage( const WebSocketMessage& original );
WebSocketMessage( const OpCode code, const Bytes& data = { } );
WebSocketMessage( const OpCode code, const std::string& data );
WebSocketMessage( const OpCode code, const Bytes& data, const std::uint32_t mask );
WebSocketMessage( const OpCode code, const std::string& data, const std::uint32_t mask );
virtual ~WebSocketMessage( void );
//Functionality
Bytes to_bytes( void ) const;
//Getters
Bytes get_data( void ) const;
OpCode get_opcode( void ) const;
std::uint32_t get_mask( void ) const;
std::uint8_t get_length( void ) const;
std::uint64_t get_extended_length( void ) const;
bool get_mask_flag( void ) const;
bool get_final_frame_flag( void ) const;
std::tuple< bool, bool, bool > get_reserved_flags( void ) const;
//Setters
void set_data( const Bytes& value );
void set_data( const std::string& value );
void set_opcode( const OpCode value );
void set_mask( const std::uint32_t value );
void set_length( const std::uint8_t value );
void set_extended_length( const std::uint64_t value );
void set_mask_flag( const bool value );
void set_final_frame_flag( const bool value );
void set_reserved_flags( const bool rsv1, const bool rsv2, const bool rsv3 );
//Operators
//Properties
protected:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
//Properties
private:
//Friends
//Definitions
//Constructors
//Functionality
//Getters
//Setters
//Operators
WebSocketMessage& operator =( const WebSocketMessage& value ) = delete;
//Properties
std::unique_ptr< detail::WebSocketMessageImpl > m_pimpl;
};
}