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,51 @@
Overview
--------
"Each network interface on a host typically has a unique IP address. Sockets with wildcard local addresses can receive messages directed to the specified port number and sent to any of the possible addresses assigned to a host. For example, if a host has two interfaces with addresses 128.32.0.4 and 10.0.0.78, and a socket is bound as in Example 2-17, the process can accept connection requests addressed to 128.32.0.4 or 10.0.0.78. To allow only hosts on a specific network to connect to it, a server binds the address of the interface on the appropriate network." -- [Oracle](https://docs.oracle.com/cd/E19455-01/806-1017/sockets-47146/index.html)
Example
-------
```C++
#include <memory>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
void get_method_handler( const shared_ptr< Session > session )
{
session->close( OK, "Hello, World!", { { "Content-Length", "13" } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_bind_address( "127.0.0.1" );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -XGET 'http://127.0.0.1:1984/resource'

View File

@@ -0,0 +1,83 @@
Overview
--------
"HTTP compression is a capability that can be built into web servers and web clients to improve transfer speed and bandwidth utilization.
HTTP data is compressed before it is sent from the server: compliant browsers will announce what methods are supported to the server before downloading the correct format; browsers that do not support compliant compression method will download uncompressed data. The most common compression schemes include gzip and Deflate; however, a full list of available schemes is maintained by the IANA." -- [Wikipedia](https://en.wikipedia.org/wiki/HTTP_compression)
Example
-------
```C++
#include <map>
#include <memory>
#include <cstdlib>
#include <ciso646>
#include <restbed>
#pragma GCC system_header
#pragma warning (disable: 4334)
#include "miniz.h" //https://github.com/richgel999/miniz
#pragma warning (default: 4334)
using namespace std;
using namespace restbed;
void deflate_method_handler( const shared_ptr< Session > session )
{
const auto request = session->get_request( );
int content_length = request->get_header( "Content-Length", 0 );
session->fetch( content_length, [ request ]( const shared_ptr< Session > session, const Bytes & body )
{
Bytes result = body;
if ( request->get_header( "Content-Encoding", String::lowercase ) == "deflate" )
{
mz_ulong length = compressBound( static_cast< mz_ulong >( body.size( ) ) );
unique_ptr< unsigned char[ ] > data( new unsigned char[ length ] );
const int status = uncompress( data.get( ), &length, body.data( ), static_cast< mz_ulong >( body.size( ) ) );
if ( status not_eq MZ_OK )
{
const auto message = String::format( "Failed to deflate: %s\n", mz_error( status ) );
session->close( 400, message, { { "Content-Length", ::to_string( message.length( ) ) }, { "Content-Type", "text/plain" } } );
return;
}
result = Bytes( data.get( ), data.get( ) + length );
}
session->close( 200, result, { { "Content-Length", ::to_string( result.size( ) ) }, { "Content-Type", "text/plain" } } );
} );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/api/deflate" );
resource->set_method_handler( "POST", deflate_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -H"Content-Encoding: deflate" -X POST --data-binary @<PATH TO ZLIB'ed FILE> 'http://localhost:1984/api/deflate'

View File

@@ -0,0 +1,50 @@
Overview
--------
"One of the fundamental constraints of HTTP and the central design feature of REST is a uniform interface provided by (among other things) a small, fixed set of methods that apply universally to all resources. The uniform interface constraint has a number of upsides and downsides." -- [StackExchange](https://softwareengineering.stackexchange.com/questions/193821/are-there-any-problems-with-implementing-custom-http-methods)
Example
-------
```C++
#include <memory>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
void nop_method_handler( const shared_ptr< Session > session )
{
session->close( 666 );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "NOP", nop_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -X NOP 'http://localhost:1984/resource'

View File

@@ -0,0 +1,51 @@
Overview
--------
"The whole of the Internet is built on conventions. We call them RFCs. While nobody will come and arrest you if you violate an RFC, you do run the risk that your service will not interoperate with the rest of the world." -- [StackExchange](https://softwareengineering.stackexchange.com/questions/218080/should-i-make-up-my-own-http-status-codes-a-la-twitter-420-enhance-your-calm)
Example
-------
```C++
#include <memory>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
void get_method_handler( const shared_ptr< Session > session )
{
session->close( 418 );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
settings->set_status_message( 418, "I'm a teapot" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -X GET 'http://localhost:1984/resource'

View File

@@ -0,0 +1,84 @@
Overview
--------
"Digest access authentication is one of the agreed-upon methods a web server can use to negotiate credentials, such as username or password, with a user's web browser. This can be used to confirm the identity of a user before sending sensitive information, such as online banking transaction history. It applies a hash function to the username and password before sending them over the network. In contrast, basic access authentication uses the easily reversible Base64 encoding instead of encryption, making it non-secure unless used in conjunction with TLS." -- [Wikipedia](https://en.wikipedia.org/wiki/Digest_access_authentication)
Example
-------
```C++
#include <regex>
#include <memory>
#include <string>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
string build_authenticate_header( void )
{
string header = "Digest realm=\"Restbed\",";
header += "algorithm=\"MD5\",";
header += "stale=false,";
header += "opaque=\"0000000000000000\",";
header += "nonce=\"Ny8yLzIwMDIgMzoyNjoyNCBQTQ\"";
return header;
}
void authentication_handler( const shared_ptr< Session > session,
const function< void ( const shared_ptr< Session > ) >& callback )
{
const auto request = session->get_request( );
auto authorisation = request->get_header( "Authorization" );
bool authorised = regex_match( authorisation, regex( ".*response=\"02863beb15feb659dfe4703d610d1b73\".*" ) );
if ( authorised )
{
callback( session );
}
else
{
session->close( UNAUTHORIZED, { { "WWW-Authenticate", build_authenticate_header( ) } } );
}
}
void get_method_handler( const shared_ptr< Session > session )
{
return session->close( OK, "Password Protected Hello, World!", { { "Content-Length", "32" } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.set_authentication_handler( authentication_handler );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v --digest -XGET 'http://Corvusoft:Glasgow@localhost:1984/resource'

View File

@@ -0,0 +1,77 @@
Overview
--------
"Error handling refers to the anticipation, detection, and resolution of programming, application, and communications errors. Specialized programs, called error handlers, are available for some applications. The best programs of this type forestall errors if possible, recover from them when they occur without terminating the application, or (if all else fails) gracefully terminate an affected application and save the error information to a log file." -- [TechTarget](https://searchsoftwarequality.techtarget.com/definition/error-handling)
Example
-------
```C++
#include <memory>
#include <cstdlib>
#include <stdexcept>
#include <restbed>
using namespace std;
using namespace restbed;
void faulty_method_handler( const shared_ptr< Session > )
{
throw SERVICE_UNAVAILABLE;
}
void resource_error_handler( const int, const exception&, const shared_ptr< Session > session )
{
if ( session->is_open( ) )
session->close( 6000, "Custom Resource Internal Server Error", { { "Content-Length", "37" } } );
else
fprintf( stderr, "Custom Resource Internal Server Error\n" );
}
void service_error_handler( const int, const exception&, const shared_ptr< Session > session )
{
if ( session->is_open( ) )
session->close( 5000, "Custom Service Internal Server Error", { { "Content-Length", "36" } } );
else
fprintf( stderr, "Custom Service Internal Server Error\n" );
}
int main( const int, const char** )
{
auto one = make_shared< Resource >( );
one->set_path( "/resources/1" );
one->set_method_handler( "GET", faulty_method_handler );
auto two = make_shared< Resource >( );
two->set_path( "/resources/2" );
two->set_method_handler( "GET", faulty_method_handler );
two->set_error_handler( &resource_error_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( one );
service.publish( two );
service.set_error_handler( service_error_handler );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resources/1'
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resources/2'

View File

@@ -0,0 +1,65 @@
Overview
--------
"HTTPS (HTTP Secure) is an extension of the Hypertext Transfer Protocol (HTTP) for secure communication over a computer network, and is widely used on the Internet. In HTTPS, the communication protocol is encrypted by Transport Layer Security (TLS), or formerly, its predecessor, Secure Sockets Layer (SSL). The protocol is therefore also often referred to as HTTP over TLS, or HTTP over SSL." -- [Wikipedia](https://en.wikipedia.org/wiki/HTTPS)
Example
-------
```C++
#include <memory>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
void get_method_handler( const shared_ptr< Session > session )
{
session->close( OK, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "GET", get_method_handler );
auto ssl_settings = make_shared< SSLSettings >( );
ssl_settings->set_http_disabled( true );
ssl_settings->set_private_key( Uri( "file:///tmp/server.key" ) );
ssl_settings->set_certificate( Uri( "file:///tmp/server.crt" ) );
ssl_settings->set_temporary_diffie_hellman( Uri( "file:///tmp/dh768.pem" ) );
auto settings = make_shared< Settings >( );
settings->set_ssl_settings( ssl_settings );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed -l ssl -l crypt
Execution
---------
> $ cd /tmp
>
> $ openssl genrsa -out server.key 1024
>
> $ openssl req -new -key server.key -out server.csr
>
> $ openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt
>
> $ openssl dhparam -out dh768.pem 768
>
> $ sudo ./example
>
> $ curl -k -v -w'\n' -X GET 'https://localhost/resource'

View File

@@ -0,0 +1,68 @@
Overview
--------
The HTTP client functionality is DEPRECATED and will be removed from future releases. See [Restless](https://github.com/Corvusoft/restless) for an alternative implementation.
Example
-------
```C++
#include <memory>
#include <future>
#include <cstdio>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
void print( const shared_ptr< Response >& response )
{
fprintf( stderr, "*** Response ***\n" );
fprintf( stderr, "Status Code: %i\n", response->get_status_code( ) );
fprintf( stderr, "Status Message: %s\n", response->get_status_message( ).data( ) );
fprintf( stderr, "HTTP Version: %.1f\n", response->get_version( ) );
fprintf( stderr, "HTTP Protocol: %s\n", response->get_protocol( ).data( ) );
for ( const auto header : response->get_headers( ) )
{
fprintf( stderr, "Header '%s' > '%s'\n", header.first.data( ), header.second.data( ) );
}
auto length = response->get_header( "Content-Length", 0 );
Http::fetch( length, response );
fprintf( stderr, "Body: %.*s...\n\n", 25, response->get_body( ).data( ) );
}
int main( const int, const char** )
{
auto request = make_shared< Request >( Uri( "http://www.corvusoft.co.uk:80/?query=search%20term" ) );
request->set_header( "Accept", "*/*" );
request->set_header( "Host", "www.corvusoft.co.uk" );
auto response = Http::sync( request );
print( response );
auto future = Http::async( request, [ ]( const shared_ptr< Request >, const shared_ptr< Response > response )
{
fprintf( stderr, "Printing async response\n" );
print( response );
} );
future.wait( );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example

View File

@@ -0,0 +1,61 @@
Overview
--------
"HTTP persistent connection, also called HTTP keep-alive, or HTTP connection reuse, is the idea of using a single TCP connection to send and receive multiple HTTP requests/responses, as opposed to opening a new connection for every single request/response pair. The newer HTTP/2 protocol uses the same idea and takes it further to allow multiple concurrent requests/responses to be multiplexed over a single connection." -- [Wikipedia](https://en.wikipedia.org/wiki/HTTP_persistent_connection)
Example
-------
```C++
#include <string>
#include <memory>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
void get_intermittent_method_handler( const shared_ptr< Session > session )
{
session->close( OK, "intermittent resource request", { { "Content-Length", "29" }, { "Connection", "close" } } );
}
void get_persistent_method_handler( const shared_ptr< Session > session )
{
session->yield( OK, "persistent resource request", { { "Content-Length", "27" }, { "Connection", "keep-alive" } } );
}
int main( const int, const char** )
{
auto persistent = make_shared< Resource >( );
persistent->set_path( "/resources/persistent" );
persistent->set_method_handler( "GET", get_persistent_method_handler );
auto intermittent = make_shared< Resource >( );
intermittent->set_path( "/resources/intermittent" );
intermittent->set_method_handler( "GET", get_intermittent_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
Service service;
service.publish( persistent );
service.publish( intermittent );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v 'http://localhost:1984/resources/persistent' 'http://localhost:1984/resources/intermittent'

View File

@@ -0,0 +1,115 @@
Overview
--------
"HTTP pipelining is a technique in which multiple HTTP requests are sent on a single TCP connection without waiting for the corresponding responses.
As of 2017, HTTP pipelining is not enabled by default in modern browsers, due to several issues including buggy proxy servers and HOL blocking." -- [Wikipedia](https://en.wikipedia.org/wiki/HTTP_pipelining)
Example
-------
```C++
#include <memory>
#include <cstdlib>
#include <restbed>
#include <stdexcept>
using namespace std;
using namespace restbed;
void get_method_handler_one( const shared_ptr< Session > session )
{
const auto request = session->get_request( );
if ( request->get_header( "Connection", String::lowercase ) == "keep-alive" )
{
session->yield( OK, "Hello, from first resource.\n", { { "Content-Length", "28" }, { "Connection", "keep-alive" } } );
}
else
{
session->close( BAD_REQUEST );
}
}
void get_method_handler_two( const shared_ptr< Session > session )
{
const auto request = session->get_request( );
if ( request->get_header( "Connection", String::lowercase ) == "keep-alive" )
{
session->yield( OK, "Hello, from second resource.\n", { { "Content-Length", "29" }, { "Connection", "keep-alive" } } );
}
else
{
session->close( BAD_REQUEST );
}
}
void get_method_handler_three( const shared_ptr< Session > session )
{
const auto request = session->get_request( );
if ( request->get_header( "Connection", String::lowercase ) == "keep-alive" )
{
session->yield( OK, "Hello, from third resource.\n", { { "Content-Length", "28" }, { "Connection", "keep-alive" } } );
}
else
{
session->close( BAD_REQUEST );
}
}
void error_handler( const int status_code, const exception error, const shared_ptr< Session > session )
{
if ( session not_eq nullptr and session->is_open( ) )
{
string message = error.what( );
message.push_back( '\n' );
//we must leave the socket intact on error,
//otherwise follow up messages will be lost.
session->yield( status_code, message, { { "Content-Length", ::to_string( message.length( ) ) }, { "Connection", "keep-alive" } } );
}
}
int main( const int, const char** )
{
auto resource_one = make_shared< Resource >( );
resource_one->set_path( "/resource/1" );
resource_one->set_method_handler( "GET", get_method_handler_one );
auto resource_two = make_shared< Resource >( );
resource_two->set_path( "/resource/2" );
resource_two->set_method_handler( "GET", get_method_handler_two );
auto resource_three = make_shared< Resource >( );
resource_three->set_path( "/resource/3" );
resource_three->set_method_handler( "GET", get_method_handler_three );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource_one );
service.publish( resource_two );
service.publish( resource_three );
service.set_error_handler( error_handler );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ (echo -e "GET /resource/1 HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\nGET /resource/2 HTTP/1.1\r\nConnection: keep-alive\r\nHost: localhost\r\n\r\nGET /resource/3 HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n"; sleep 5) | netcat localhost 1984
>
> $ (echo -e "GET &&%$£% /resource/1 HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\nGET /resource/2 HTTP/1.1\r\nConnection: keep-alive\r\nHost: localhost\r\n\r\nGET /resource/3 HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n"; sleep 5) | netcat localhost 1984

View File

@@ -0,0 +1,54 @@
Overview
--------
"A web service is a service offered by an electronic device to another electronic device, communicating with each other via the World Wide Web. In a web service, the Web technology such as HTTP—originally designed for human-to-machine communication—is utilized for machine-to-machine communication, more specifically for transferring machine-readable file formats such as XML and JSON." -- [Wikipedia](https://en.wikipedia.org/wiki/Web_service)
Example
-------
```C++
#include <memory>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
void post_method_handler( const shared_ptr< Session > session )
{
const auto request = session->get_request( );
size_t content_length = request->get_header( "Content-Length", 0 );
session->fetch( content_length, [ request ]( const shared_ptr< Session > session, const Bytes & body )
{
fprintf( stdout, "%.*s\n", ( int ) body.size( ), body.data( ) );
session->close( OK, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } );
} );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "POST", post_method_handler );
Service service;
service.publish( resource );
service.start( );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -X POST --data 'Hello, Restbed' 'http://localhost/resource'

View File

@@ -0,0 +1,92 @@
Overview
--------
"Logging is the act of keeping a log. In the simplest case, messages are written to a single log file.
A transaction log is a file (i.e., log) of the communications (i.e., transactions) between a system and the users of that system,[2] or a data collection method that automatically captures the type, content, or time of transactions made by a person from a terminal with that system." -- [Wikipedia](https://en.wikipedia.org/wiki/Log_file)
Example
-------
```C++
#include <memory>
#include <cstdio>
#include <cstdarg>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
class CustomLogger : public Logger
{
public:
void stop( void )
{
return;
}
void start( const shared_ptr< const Settings >& )
{
return;
}
void log( const Level, const char* format, ... )
{
va_list arguments;
va_start( arguments, format );
vfprintf( stderr, format, arguments );
fprintf( stderr, "\n" );
va_end( arguments );
}
void log_if( bool expression, const Level level, const char* format, ... )
{
if ( expression )
{
va_list arguments;
va_start( arguments, format );
log( level, format, arguments );
va_end( arguments );
}
}
};
void get_method_handler( const shared_ptr< Session > session )
{
session->close( OK, "Hello, World!", { { "Content-Length", "13" } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.set_logger( make_shared< CustomLogger >( ) );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resource'

View File

@@ -0,0 +1,52 @@
Overview
--------
"The concept of a web resource is primitive in the web architecture, and is used in the definition of its fundamental elements. The term was first introduced to refer to targets of uniform resource locators (URLs), but its definition has been further extended to include the referent of any uniform resource identifier (RFC 3986), or internationalized resource identifier (RFC 3987). In the Semantic Web, abstract resources and their semantic properties are described using the family of languages based on Resource Description Framework (RDF)." -- [Wikipedia](https://en.wikipedia.org/wiki/Web_resource)
Example
-------
```C++
#include <memory>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
void get_method_handler( const shared_ptr< Session > session )
{
session->close( OK, "Hello, World!", { { "Content-Length", "13" } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_paths( { "/messages", "/queues/{id: [0-9]*}/messages" } );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v 'http://localhost:1984/messages'
>
> $ curl -w'\n' -v 'http://localhost:1984/queues/12/messages'

View File

@@ -0,0 +1,57 @@
Overview
--------
"In computer architecture, multithreading is the ability of a central processing unit (CPU) or a single core in a multi-core processor to execute multiple processes or threads concurrently, appropriately supported by the operating system." -- [Wikipedia](https://en.wikipedia.org/wiki/Multithreading_(computer_architecture))
Example
-------
```C++
#include <memory>
#include <thread>
#include <cstdlib>
#include <restbed>
#include <sstream>
using namespace std;
using namespace restbed;
void get_method_handler( const shared_ptr< Session > session )
{
stringstream id;
id << ::this_thread::get_id( );
auto body = String::format( "Hello From Thread %s\n", id.str( ).data( ) );
session->close( OK, body, { { "Content-Length", ::to_string( body.length( ) ) } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_worker_limit( 4 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -X GET 'http://localhost:1984/resource'

View File

@@ -0,0 +1,129 @@
Overview
--------
"A pluggable authentication module (PAM) is a mechanism to integrate multiple low-level authentication schemes into a high-level application programming interface (API). It allows programs that rely on authentication to be written independently of the underlying authentication scheme. PAM was first proposed by Sun Microsystems in an Open Software Foundation Request for Comments (RFC) 86.0 dated October 1995. It was adopted as the authentication framework of the Common Desktop Environment. As a stand-alone open-source infrastructure, PAM first appeared in Red Hat Linux 3.0.4 in August 1996 in the Linux PAM project. PAM is currently supported in the AIX operating system, DragonFly BSD,[1] FreeBSD, HP-UX, Linux, Mac OS X, NetBSD and Solaris." -- [Wikipedia](https://en.wikipedia.org/wiki/Pluggable_authentication_module)
Example
-------
```C++
#include <memory>
#include <string>
#include <cstring>
#include <cstdlib>
#include <utility>
#include <restbed>
#include <security/pam_appl.h>
#include "base64.h"
using namespace std;
using namespace restbed;
#ifdef __linux__
#define SERVICE "system-auth"
#else
#define SERVICE "chkpasswd"
#endif
struct pam_response *response;
int null_conv( int, const struct pam_message**, struct pam_response** reply, void* )
{
*reply = response;
return PAM_SUCCESS;
}
bool pam_authorisation( const string& username, const string& password )
{
pam_handle_t* handle = nullptr;
struct pam_conv conversation = { null_conv, nullptr };
int status = pam_start( SERVICE, username.data( ), &conversation, &handle );
if ( status == PAM_SUCCESS )
{
response = new pam_response;
response[ 0 ].resp_retcode = 0;
char* pass = new char[ password.length( ) ];
response[ 0 ].resp = strncpy( pass, password.data( ), password.length( ) );
status = pam_authenticate( handle, 0 );
pam_end( handle, PAM_SUCCESS );
}
if ( status not_eq PAM_SUCCESS )
{
fprintf( stderr, "PAM Error: status=%i, message=%s\n", status, pam_strerror( handle, status ) );
fprintf( stderr, "Credentials: username='%s', password='%s'\n\n", username.data( ), password.data( ) );
return false;
}
return true;
}
pair< string, string > decode_header( const string& value )
{
auto data = base64_decode( value.substr( 6 ) );
auto delimiter = data.find_first_of( ':' );
auto username = data.substr( 0, delimiter );
auto password = data.substr( delimiter + 1 );
return make_pair( username, password );
}
void authentication_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback )
{
const auto request = session->get_request( );
const auto credentials = decode_header( request->get_header( "Authorization" ) );
bool authorised = pam_authorisation( credentials.first, credentials.second );
if ( not authorised )
{
session->close( UNAUTHORIZED, { { "WWW-Authenticate", "Basic realm=\"Restbed\"" } } );
}
else
{
callback( session );
}
}
void get_method_handler( const shared_ptr< Session > session )
{
session->close( OK, "Password Protected Hello, World!", { { "Content-Length", "32" } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.set_authentication_handler( authentication_handler );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed -l pam
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -XGET 'http://<USERNAME>:<PASSWORD>@localhost:1984/resource'

View File

@@ -0,0 +1,54 @@
Overview
--------
"A URI path parameter is part of a path segment that occurs after its name. Path parameters offer a unique opportunity to control the representations of resources. Since they can't be manipulated by standard Web forms, they have to be constructed out of band. Since they're part of the path, they're sequential, unlike query strings." -- [Dorian Taylor](https://doriantaylor.com/policy/http-url-path-parameter-syntax)
Example
-------
```C++
#include <string>
#include <memory>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
void get_method_handler( const shared_ptr< Session > session )
{
const auto& request = session->get_request( );
const string body = "Hello, " + request->get_path_parameter( "name" );
session->close( OK, body, { { "Content-Length", ::to_string( body.size( ) ) } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource/{name: .*}" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resource/<YOUR NAME HERE>'

View File

@@ -0,0 +1,109 @@
Overview
--------
"HTTP Basic authentication (BA) implementation is the simplest technique for enforcing access controls to web resources because it does not require cookies, session identifiers, or login pages; rather, HTTP Basic authentication uses standard fields in the HTTP header, removing the need for handshakes." -- [Wikipedia](https://en.wikipedia.org/wiki/Basic_access_authentication)
Example
-------
```C++
#include <memory>
#include <cstdlib>
#include <ciso646>
#include <functional>
#include <restbed>
using namespace std;
using namespace restbed;
void service_authentication_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback )
{
auto authorisation = session->get_request( )->get_header( "Authorization" );
if ( authorisation not_eq "Basic YmVuOjEyMzQ=" and authorisation not_eq "Basic bGF1cmE6NDMyMQ==" )
{
session->close( UNAUTHORIZED, { { "WWW-Authenticate", "Basic realm=\"restbed\"" } } );
}
else
{
callback( session );
}
}
void ben_authentication_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback )
{
auto authorisation = session->get_request( )->get_header( "Authorization" );
if ( authorisation not_eq "Basic YmVuOjEyMzQ=" )
{
session->close( FORBIDDEN );
}
else
{
callback( session );
}
}
void laura_authentication_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback )
{
auto authorisation = session->get_request( )->get_header( "Authorization" );
if ( authorisation not_eq "Basic bGF1cmE6NDMyMQ==" )
{
session->close( FORBIDDEN );
}
else
{
callback( session );
}
}
void get_ben_method_handler( const shared_ptr< Session > session )
{
session->close( OK, "Hi, Ben.", { { "Content-Length", "8" } } );
}
void get_laura_method_handler( const shared_ptr< Session > session )
{
session->close( OK, "Hi, Laura.", { { "Content-Length", "10" } } );
}
int main( const int, const char** )
{
auto ben = make_shared< Resource >( );
ben->set_path( "/ben" );
ben->set_method_handler( "GET", get_ben_method_handler );
ben->set_authentication_handler( ben_authentication_handler );
auto laura = make_shared< Resource >( );
laura->set_path( "/laura" );
laura->set_method_handler( "GET", get_laura_method_handler );
laura->set_authentication_handler( laura_authentication_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( ben );
service.publish( laura );
service.set_authentication_handler( service_authentication_handler );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -XGET 'http://ben:1234@localhost:1984/ben'
> $ curl -w'\n' -v -XGET 'http://laura:4321@localhost:1984/laura'

View File

@@ -0,0 +1,85 @@
Overview
--------
"The resource filtering is designed to filter rest resources. By applying filters you can restrict or allow access to a specific resource determined by a path, method and/or headers."
Example
-------
```C++
#include <string>
#include <memory>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
void get_xml_method_handler( const shared_ptr< Session > session )
{
const multimap< string, string > headers
{
{ "Content-Length", "30" },
{ "Content-Type", "application/xml" }
};
session->close( 200, "<hello><world></world></hello>", headers );
}
void get_json_method_handler( const shared_ptr< Session > session )
{
const multimap< string, string > headers
{
{ "Content-Length", "23" },
{ "Content-Type", "application/json" }
};
session->close( 200, "{ \"Hello\": \", World!\" }", headers );
}
void failed_filter_validation_handler( const shared_ptr< Session > session )
{
session->close( 400 );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_failed_filter_validation_handler( failed_filter_validation_handler );
resource->set_method_handler( "GET", { { "Accept", "application/xml" }, { "Content-Type", "application/xml" } }, &get_xml_method_handler );
resource->set_method_handler( "GET", { { "Accept", "application/json" }, { "Content-Type", "application/json" } }, &get_json_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/json'
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/xml'
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/json' -H'Content-Type: application/json'
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/xml' -H'Content-Type: application/xml'
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/json' -H'Content-Type: application/xml'
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/xml' -H'Content-Type: application/json'

View File

@@ -0,0 +1,129 @@
Overview
--------
"A business rules engine is a software system that executes one or more business rules in a runtime production environment. The rules might come from legal regulation ("An employee can be fired for any reason or no reason but not for an illegal reason"), company policy ("All customers that spend more than $100 at one time will receive a 10% discount"), or other sources. A business rule system enables these company policies and other operational decisions to be defined, tested, executed and maintained separately from application code." -- [Wikipedia](https://en.wikipedia.org/wiki/Business_rules_engine)
Example
-------
```C++
#include <string>
#include <memory>
#include <string>
#include <cstdlib>
#include <ciso646>
#include <functional>
#include <restbed>
using namespace std;
using namespace restbed;
class HostRule : public Rule
{
public:
HostRule( void ) : Rule( )
{
return;
}
virtual ~HostRule( void )
{
return;
}
bool condition( const shared_ptr< Session > ) final override
{
return true;
}
void action( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) final override
{
const auto request = session->get_request( );
if ( not request->has_header( "Host" ) )
{
session->close( BAD_REQUEST, "Bad Request! Host header required.", { { "Content-Length", "34" }, { "Content-Type", "text/plain" } } );
}
else
{
callback( session );
}
}
};
class AcceptRule : public Rule
{
public:
AcceptRule( void ) : Rule( )
{
return;
}
virtual ~AcceptRule( void )
{
return;
}
bool condition( const shared_ptr< Session > session ) final override
{
return session->get_request( )->has_header( "Accept" );
}
void action( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) final override
{
const auto request = session->get_request( );
const auto type = request->get_header( "Accept", String::lowercase );
if ( type not_eq "text/plain" and type not_eq "*/*" )
{
session->close( NOT_ACCEPTABLE,
"Not Acceptable, must be 'text/plain' or '*/*'.",
{ { "Content-Length", "46" }, { "Content-Type", "text/plain" } } );
}
else
{
callback( session );
}
}
};
void get_method_handler( const shared_ptr< Session > session )
{
session->close( OK, "Hello, World!", { { "Content-Length", "13" }, { "Content-Type", "text/plain" } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->add_rule( make_shared< AcceptRule >( ) );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.add_rule( make_shared< HostRule >( ) );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -X GET 'http://localhost:1984/resource'
>
> $ curl -w'\n' -v -X GET 'http://localhost:1984/resource' -H"Host:"
>
> $ curl -w'\n' -v -X GET 'http://localhost:1984/resource' -H"Accept: application/json"

View File

@@ -0,0 +1,62 @@
Overview
--------
"Run loops are part of the fundamental infrastructure associated with threads. A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep when there is none." -- [Apple](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html)
Example
-------
```C++
#include <chrono>
#include <memory>
#include <string>
#include <cstdlib>
#include <restbed>
#include <functional>
using namespace std;
using namespace restbed;
void multi_run_task( void )
{
fprintf( stderr, "multi run task executed.\n" );
}
void single_run_task( void )
{
fprintf( stderr, "single run task executed.\n" );
}
void get_method_handler( const shared_ptr< Session > session )
{
session->close( 200 );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/api" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
auto service = make_shared< Service >( );
service->publish( resource );
service->schedule( single_run_task );
service->schedule( multi_run_task, chrono::milliseconds( 1000 ) );
service->start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example

View File

@@ -0,0 +1,136 @@
Overview
--------
"SSEs are sent over traditional HTTP. That means they do not require a special protocol or server implementation to get working. WebSockets on the other hand, require full-duplex connections and new Web Socket servers to handle the protocol. In addition, Server-Sent Events have a variety of features that WebSockets lack by design such as automatic reconnection, event IDs, and the ability to send arbitrary events." -- [html5rocks](https://www.html5rocks.com/en/tutorials/eventsource/basics/)
Example
-------
```C++
#include <map>
#include <chrono>
#include <string>
#include <memory>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
using namespace std::chrono;
vector< shared_ptr< Session > > sessions;
void register_event_source_handler( const shared_ptr< Session > session )
{
const auto headers = multimap< string, string > {
{ "Connection", "keep-alive" },
{ "Cache-Control", "no-cache" },
{ "Content-Type", "text/event-stream" },
{ "Access-Control-Allow-Origin", "*" } //Only required for demo purposes.
};
session->yield( OK, headers, [ ]( const shared_ptr< Session > session )
{
sessions.push_back( session );
} );
}
void event_stream_handler( void )
{
static size_t counter = 0;
const auto message = "data: event " + to_string( counter ) + "\n\n";
sessions.erase(
std::remove_if(sessions.begin(), sessions.end(),
[](const shared_ptr<Session> &a) {
return a->is_closed();
}),
sessions.end());
for ( auto session : sessions )
{
session->yield( message );
}
counter++;
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/stream" );
resource->set_method_handler( "GET", register_event_source_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
auto service = make_shared< Service >( );
service->publish( resource );
service->schedule( event_stream_handler, seconds( 2 ) );
service->start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
Client
------
```HTML
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
function add_event( data )
{
var li = document.createElement( "li" );
li.appendChild( document.createTextNode( "> " + data ) );
var ul = document.getElementById( "events" );
ul.appendChild( li );
}
if ( !!window.EventSource )
{
var source = new EventSource( "http://localhost:1984/stream" );
source.addEventListener( "message", function( evt )
{
add_event( evt.data );
}, false );
source.addEventListener( "open", function( evt )
{
add_event( "EventSource open." );
}, false );
source.addEventListener( 'error', function( evt )
{
if ( evt.readyState == EventSource.CLOSED )
{
add_event( "EventSource closed." );
}
}, false );
}
else
{
alert( "WebSockets NOT supported by your Browser!" );
}
</script>
</head>
<body>
<h1>Incoming Events</h1>
<ul id="events">
</ul>
</body>
</html>
```

View File

@@ -0,0 +1,69 @@
Overview
--------
"HTTP Basic authentication (BA) implementation is the simplest technique for enforcing access controls to web resources because it does not require cookies, session identifiers, or login pages; rather, HTTP Basic authentication uses standard fields in the HTTP header, removing the need for handshakes." -- [Wikipedia](https://en.wikipedia.org/wiki/Basic_access_authentication)
Example
-------
```C++
#include <memory>
#include <cstdlib>
#include <ciso646>
#include <functional>
#include <restbed>
using namespace std;
using namespace restbed;
void authentication_handler( const shared_ptr< Session > session,
const function< void ( const shared_ptr< Session > ) >& callback )
{
auto authorisation = session->get_request( )->get_header( "Authorization" );
if ( authorisation not_eq "Basic Q29ydnVzb2Z0OkdsYXNnb3c=" )
{
session->close( UNAUTHORIZED, { { "WWW-Authenticate", "Basic realm=\"restbed\"" } } );
}
else
{
callback( session );
}
}
void get_method_handler( const shared_ptr< Session > session )
{
session->close( OK, "Password Protected Hello, World!", { { "Content-Length", "32" } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.set_authentication_handler( authentication_handler );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -XGET 'http://Corvusoft:Glasgow@localhost:1984/resource'

View File

@@ -0,0 +1,75 @@
Overview
--------
"Web server refers to server software, or hardware dedicated to running said software, that can serve contents to the World Wide Web. A web server processes incoming network requests over the HTTP protocol (and several other related protocols)." -- [Wikipedia](https://en.wikipedia.org/wiki/Web_server)
Example
-------
```C++
#include <string>
#include <memory>
#include <cstdlib>
#include <fstream>
#include <restbed>
#include <streambuf>
using namespace std;
using namespace restbed;
void get_method_handler( const shared_ptr< Session > session )
{
const auto request = session->get_request( );
const string filename = request->get_path_parameter( "filename" );
ifstream stream( "./" + filename, ifstream::in );
if ( stream.is_open( ) )
{
const string body = string( istreambuf_iterator< char >( stream ), istreambuf_iterator< char >( ) );
const multimap< string, string > headers
{
{ "Content-Type", "text/html" },
{ "Content-Length", ::to_string( body.length( ) ) }
};
session->close( OK, body, headers );
}
else
{
session->close( NOT_FOUND );
}
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/static/{filename: [a-z]*\\.html}" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ touch index.html
>
> $ ./example
>
> $ curl -w'\n' -v -X GET 'http://localhost:1984/static/index.html'

View File

@@ -0,0 +1,155 @@
Overview
--------
"Sessions are a simple way to store data for individual users against a unique session ID. This can be used to persist state information between requests. Session IDs are normally sent to the browser via session cookies and the ID is used to retrieve existing session data. The absence of an ID or session cookie lets PHP know to create a new session, and generate a new session ID." -- [PHP Group](http://php.net/manual/en/session.examples.basic.php)
Example
-------
```C++
#include <map>
#include <mutex>
#include <chrono>
#include <string>
#include <memory>
#include <random>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
class InMemorySessionManager : public SessionManager
{
public:
InMemorySessionManager( void ) : m_sessions_lock( ),
m_sessions( )
{
return;
}
~InMemorySessionManager( void )
{
return;
}
void start( const shared_ptr< const Settings >& )
{
return;
}
void create( const function< void ( const shared_ptr< Session > ) >& callback )
{
static const string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static uniform_int_distribution< > selector( 0, 35 );
auto seed = static_cast< unsigned int >( chrono::high_resolution_clock::now( ).time_since_epoch( ).count( ) );
static mt19937 generator( seed );
string key = "";
for ( int index = 0; index < 32; index++ )
{
key += charset.at( selector( generator ) );
}
callback( make_shared< Session >( key ) );
}
void load( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback )
{
const auto request = session->get_request( );
unique_lock< mutex > lock( m_sessions_lock );
auto previous_session = m_sessions.find( request->get_header( "SessionID" ) );
if ( previous_session not_eq m_sessions.end( ) )
{
const auto id = previous_session->second->get_id( );
session->set_id( id );
for ( const auto key : previous_session->second->keys( ) )
{
session->set( key, previous_session->second->get( key ) );
}
}
lock.unlock( );
const auto key = session->get_id( );
session->set_header( "SessionID", key );
callback( session );
}
void save( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback )
{
unique_lock< mutex > lock( m_sessions_lock );
m_sessions[ session->get_id( ) ] = session;
lock.unlock( );
callback( session );
}
void stop( void )
{
return;
}
private:
mutex m_sessions_lock;
map< string, shared_ptr< Session > > m_sessions;
};
void get_method_handler( const shared_ptr< Session > session )
{
string body = "Previous Session Data\n";
for ( const auto key : session->keys( ) )
{
string value = session->get( key );
body += key + "=" + value + "\n";
}
const auto request = session->get_request( );
for ( const auto query_parameter : request->get_query_parameters( ) )
{
session->set( query_parameter.first, query_parameter.second );
}
session->close( OK, body, { { "Connection", "close" } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
Service service;
service.publish( resource );
service.set_session_manager( make_shared< InMemorySessionManager >( ) );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -X GET 'http://localhost:1984/resource?location=UK'
>
> $ curl -w'\n' -v -X GET 'http://localhost:1984/resource' -H"SessionID: <Previously returned SessionID header value>"

View File

@@ -0,0 +1,71 @@
Overview
--------
"A signal is a software interrupt delivered to a process. The operating system uses signals to report exceptional situations to an executing program. Some signals report errors such as references to invalid memory addresses; others report asynchronous events, such as disconnection of a phone line." -- [GNU](http://www.gnu.org/software/libc/manual/html_node/Signal-Handling.html)
Example
-------
```C++
#include <memory>
#include <cstdlib>
#include <restbed>
#include <csignal>
#include <sys/types.h>
#ifdef _WIN32
#include <process.h>
#else
#include <unistd.h>
#endif
using namespace std;
using namespace restbed;
void sighup_handler( const int signal_number )
{
fprintf( stderr, "Received SIGINT signal number '%i'.\n", signal_number );
}
void sigterm_handler( const int signal_number )
{
fprintf( stderr, "Received SIGTERM signal number '%i'.\n", signal_number );
}
void ready_handler( Service& )
{
#ifdef _WIN32
fprintf( stderr, "Service PID is '%i'.\n", _getpid( ) );
#else
fprintf( stderr, "Service PID is '%i'.\n", getpid( ) );
#endif
}
int main( const int, const char** )
{
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
Service service;
service.set_ready_handler( ready_handler );
service.set_signal_handler( SIGINT, sighup_handler );
service.set_signal_handler( SIGTERM, sigterm_handler );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ kill -s SIGINT <PID>
>
> $ kill -s SIGTERM <PID>

View File

@@ -0,0 +1,129 @@
Overview
--------
"In computing, syslog is a standard for message logging. It allows separation of the software that generates messages, the system that stores them, and the software that reports and analyzes them. Each message is labeled with a facility code, indicating the software type generating the message, and assigned a severity label.
Computer system designers may use syslog for system management and security auditing as well as general informational, analysis, and debugging messages. A wide variety of devices, such as printers, routers, and message receivers across many platforms use the syslog standard. This permits the consolidation of logging data from different types of systems in a central repository. Implementations of syslog exist for many operating systems." -- [Wikipedia](https://en.wikipedia.org/wiki/Syslog)
Example
-------
```C++
#include <memory>
#include <cstdarg>
#include <cstdlib>
#include <restbed>
#include <syslog.h>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
using namespace restbed;
class SyslogLogger : public Logger
{
public:
void stop( void )
{
return;
}
void start( const shared_ptr< const Settings >& )
{
return;
}
void log( const Level level, const char* format, ... )
{
setlogmask( LOG_UPTO( LOG_DEBUG ) );
openlog( "Corvusoft Restbed", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1 );
int priority = 0;
switch ( level )
{
case FATAL:
priority = LOG_CRIT;
break;
case ERROR:
priority = LOG_ERR;
break;
case WARNING:
priority = LOG_WARNING;
break;
case SECURITY:
priority = LOG_ALERT;
break;
case INFO:
case DEBUG:
default:
priority = LOG_NOTICE;
}
va_list arguments;
va_start( arguments, format );
vsyslog( priority, format, arguments );
va_end( arguments );
closelog( );
}
void log_if( bool expression, const Level level, const char* format, ... )
{
if ( expression )
{
va_list arguments;
va_start( arguments, format );
log( level, format, arguments );
va_end( arguments );
}
}
};
void get_method_handler( const shared_ptr< Session > session )
{
session->close( OK, "Hello, World!", { { "Content-Length", "13" } } );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resource" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.set_logger( make_shared< SyslogLogger >( ) );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resource'

View File

@@ -0,0 +1,107 @@
Overview
--------
"Chunked transfer encoding is a streaming data transfer mechanism available in version 1.1 of the Hypertext Transfer Protocol (HTTP). In chunked transfer encoding, the data stream is divided into a series of non-overlapping "chunks". The chunks are sent out and received independently of one another. No knowledge of the data stream outside the currently-being-processed chunk is necessary for both the sender and the receiver at any given time.
Each chunk is preceded by its size in bytes. The transmission ends when a zero-length chunk is received. The chunked keyword in the Transfer-Encoding header is used to indicate chunked transfer." -- [Wikipedia](https://en.wikipedia.org/wiki/Chunked_transfer_encoding)
Example
-------
```C++
#include <string>
#include <memory>
#include <cstring>
#include <cstdlib>
#include <ciso646>
#include <iostream>
#include <restbed>
using namespace std;
using namespace restbed;
void read_chunk_size( const shared_ptr< Session > session, const Bytes& data )
{
if ( not data.empty( ) )
{
const string length( data.begin( ), data.end( ) );
if ( length not_eq "0\r\n" )
{
const auto chunk_size = stoul( length, nullptr, 16 ) + strlen( "\r\n" );
session->fetch( chunk_size, read_chunk );
return;
}
}
session->close( OK );
const auto request = session->get_request( );
const auto body = request->get_body( );
fprintf( stdout, "Complete body content: %.*s\n", static_cast< int >( body.size( ) ), body.data( ) );
}
void read_chunk( const shared_ptr< Session > session, const Bytes& data )
{
cout << "Partial body chunk: " << data.size( ) << " bytes" << endl;
session->fetch( "\r\n", read_chunk_size );
}
void post_method_handler( const shared_ptr< Session > session )
{
const auto request = session->get_request( );
if ( request->get_header( "Transfer-Encoding", String::lowercase ) == "chunked" )
{
session->fetch( "\r\n", read_chunk_size );
}
else if ( request->has_header( "Content-Length" ) )
{
int length = request->get_header( "Content-Length", 0 );
session->fetch( length, [ ]( const shared_ptr< Session > session, const Bytes& )
{
const auto request = session->get_request( );
const auto body = request->get_body( );
fprintf( stdout, "Complete body content: %.*s\n", static_cast< int >( body.size( ) ), body.data( ) );
session->close( OK );
} );
}
else
{
session->close( BAD_REQUEST );
}
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resources" );
resource->set_method_handler( "POST", post_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -X POST --header "Transfer-Encoding: chunked" -d @<PATH TO LARGE FILE> 'http://localhost:1984/resources'

View File

@@ -0,0 +1,64 @@
Overview
--------
"Chunked transfer encoding is a streaming data transfer mechanism available in version 1.1 of the Hypertext Transfer Protocol (HTTP). In chunked transfer encoding, the data stream is divided into a series of non-overlapping "chunks". The chunks are sent out and received independently of one another. No knowledge of the data stream outside the currently-being-processed chunk is necessary for both the sender and the receiver at any given time.
Each chunk is preceded by its size in bytes. The transmission ends when a zero-length chunk is received. The chunked keyword in the Transfer-Encoding header is used to indicate chunked transfer." -- [Wikipedia](https://en.wikipedia.org/wiki/Chunked_transfer_encoding)
Example
-------
```C++
#include <string>
#include <chrono>
#include <memory>
#include <cstdlib>
#include <restbed>
using namespace std;
using namespace restbed;
void get_method_handler( const shared_ptr< Session > session )
{
session->yield( OK, "8\r\nrestbed \r\n", { { "Transfer-Encoding", "chunked" } }, [ ]( const shared_ptr< Session > session )
{
//pause to simulate backend processing.
session->sleep_for( chrono::milliseconds( 500 ), [ ]( const shared_ptr< Session > session )
{
session->yield( "10\r\nchunked encoding\r\n", [ ]( const shared_ptr< Session > session )
{
session->close( "0\r\n\r\n" );
} );
} );
} );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/resources/item" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
settings->set_default_header( "Connection", "close" );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed
Execution
---------
> $ ./example
>
> $ curl -w'\n' -v -XGET 'http://localhost:1984/resources/item'

View File

@@ -0,0 +1,366 @@
Overview
--------
"WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection. The WebSocket protocol was standardized by the IETF as RFC 6455 in 2011, and the WebSocket API in Web IDL is being standardized by the W3C. WebSocket is a different TCP protocol from HTTP." -- [Wikipedia](https://en.wikipedia.org/wiki/WebSocket)
Example
-------
```C++
#include <map>
#include <chrono>
#include <string>
#include <cstring>
#include <memory>
#include <utility>
#include <cstdlib>
#include <restbed>
#include <system_error>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
using namespace std;
using namespace restbed;
using namespace std::chrono;
shared_ptr< Service > service = nullptr;
map< string, shared_ptr< WebSocket > > sockets = { };
string base64_encode( const unsigned char* input, int length )
{
BIO* bmem, *b64;
BUF_MEM* bptr;
b64 = BIO_new( BIO_f_base64( ) );
bmem = BIO_new( BIO_s_mem( ) );
b64 = BIO_push( b64, bmem );
BIO_write( b64, input, length );
( void ) BIO_flush( b64 );
BIO_get_mem_ptr( b64, &bptr );
char* buff = ( char* )malloc( bptr->length );
memcpy( buff, bptr->data, bptr->length - 1 );
buff[ bptr->length - 1 ] = 0;
BIO_free_all( b64 );
return buff;
}
multimap< string, string > build_websocket_handshake_response_headers( const shared_ptr< const Request >& request )
{
auto key = request->get_header( "Sec-WebSocket-Key" );
key.append( "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" );
Byte hash[ SHA_DIGEST_LENGTH ];
SHA1( reinterpret_cast< const unsigned char* >( key.data( ) ), key.length( ), hash );
multimap< string, string > headers;
headers.insert( make_pair( "Upgrade", "websocket" ) );
headers.insert( make_pair( "Connection", "Upgrade" ) );
headers.insert( make_pair( "Sec-WebSocket-Accept", base64_encode( hash, SHA_DIGEST_LENGTH ) ) );
return headers;
}
void ping_handler( void )
{
for ( auto entry : sockets )
{
auto key = entry.first;
auto socket = entry.second;
if ( socket->is_open( ) )
{
socket->send( WebSocketMessage::PING_FRAME );
}
else
{
socket->close( );
}
}
}
void close_handler( const shared_ptr< WebSocket > socket )
{
if ( socket->is_open( ) )
{
auto response = make_shared< WebSocketMessage >( WebSocketMessage::CONNECTION_CLOSE_FRAME, Bytes( { 10, 00 } ) );
socket->send( response );
}
const auto key = socket->get_key( );
sockets.erase( key );
fprintf( stderr, "Closed connection to %s.\n", key.data( ) );
}
void error_handler( const shared_ptr< WebSocket > socket, const error_code error )
{
const auto key = socket->get_key( );
fprintf( stderr, "WebSocket Errored '%s' for %s.\n", error.message( ).data( ), key.data( ) );
}
void message_handler( const shared_ptr< WebSocket > source, const shared_ptr< WebSocketMessage > message )
{
const auto opcode = message->get_opcode( );
if ( opcode == WebSocketMessage::PING_FRAME )
{
auto response = make_shared< WebSocketMessage >( WebSocketMessage::PONG_FRAME, message->get_data( ) );
source->send( response );
}
else if ( opcode == WebSocketMessage::PONG_FRAME )
{
//Ignore PONG_FRAME.
//
//Every time the ping_handler is scheduled to run, it fires off a PING_FRAME to each
//WebSocket. The client, if behaving correctly, will respond with a PONG_FRAME.
//
//On each occasion the underlying TCP socket sees any packet data transfer, whether
//a PING, PONG, TEXT, or BINARY... frame. It will automatically reset the timeout counter
//leaving the connection active; see also Settings::set_connection_timeout.
return;
}
else if ( opcode == WebSocketMessage::CONNECTION_CLOSE_FRAME )
{
source->close( );
}
else if ( opcode == WebSocketMessage::BINARY_FRAME )
{
//We don't support binary data.
auto response = make_shared< WebSocketMessage >( WebSocketMessage::CONNECTION_CLOSE_FRAME, Bytes( { 10, 03 } ) );
source->send( response );
}
else if ( opcode == WebSocketMessage::TEXT_FRAME )
{
auto response = make_shared< WebSocketMessage >( *message );
response->set_mask( 0 );
for ( auto socket : sockets )
{
auto destination = socket.second;
destination->send( response );
}
const auto key = source->get_key( );
const auto data = String::format( "Received message '%.*s' from %s\n", message->get_data( ).size( ), message->get_data( ).data( ), key.data( ) );
fprintf( stderr, "%s", data.data( ) );
}
}
void get_method_handler( const shared_ptr< Session > session )
{
const auto request = session->get_request( );
const auto connection_header = request->get_header( "connection", String::lowercase );
if ( connection_header.find( "upgrade" ) not_eq string::npos )
{
if ( request->get_header( "upgrade", String::lowercase ) == "websocket" )
{
const auto headers = build_websocket_handshake_response_headers( request );
session->upgrade( SWITCHING_PROTOCOLS, headers, [ ]( const shared_ptr< WebSocket > socket )
{
if ( socket->is_open( ) )
{
socket->set_close_handler( close_handler );
socket->set_error_handler( error_handler );
socket->set_message_handler( message_handler );
socket->send( "Welcome to Corvusoft Chat!", [ ]( const shared_ptr< WebSocket > socket )
{
const auto key = socket->get_key( );
sockets.insert( make_pair( key, socket ) );
fprintf( stderr, "Sent welcome message to %s.\n", key.data( ) );
} );
}
else
{
fprintf( stderr, "WebSocket Negotiation Failed: Client closed connection.\n" );
}
} );
return;
}
}
session->close( BAD_REQUEST );
}
int main( const int, const char** )
{
auto resource = make_shared< Resource >( );
resource->set_path( "/chat" );
resource->set_method_handler( "GET", get_method_handler );
auto settings = make_shared< Settings >( );
settings->set_port( 1984 );
service = make_shared< Service >( );
service->publish( resource );
service->schedule( ping_handler, milliseconds( 5000 ) );
service->start( settings );
return EXIT_SUCCESS;
}
```
Build
-----
> $ clang++ -o example example.cpp -l restbed -l ssl -l crypt
Execution
---------
> $ ./example
Client
------
```HTML
<!DOCTYPE HTML>
<html>
<head>
<style>
html, body {
margin: 0;
}
ul {
list-style: none;
}
li {
text-align: left;
}
input {
width: 40%;
height: 40px;
line-height: 40px;
margin-bottom: 10px;
}
a {
margin: 10px;
}
.disabled {
color: #000;
pointer-events: none;
}
#controls {
width: 100%;
bottom: 10%;
position: absolute;
text-align: center;
}
</style>
<script type="text/javascript">
function on_return_submit( evt )
{
if ( window.restbed.ws === null || window.restbed.ws.readyState !== window.restbed.ws.OPEN )
{
return;
}
if( evt && evt.keyCode == 13 )
{
var message = document.getElementById( "message" );
window.restbed.ws.send( message.value );
message.value = "";
}
}
function toggle_control_access( )
{
var open = document.getElementById( "open" );
open.disabled = !open.disabled;
var message = document.getElementById( "message" );
message.disabled = !message.disabled;
var close = document.getElementById( "close" );
close.className = ( close.className === "disabled" ) ? "" : "disabled";
}
function add_message( message )
{
var li = document.createElement( "li" );
li.appendChild( document.createTextNode( "> " + message ) );
var ul = document.getElementById( "messages" );
ul.appendChild( li );
}
function open( )
{
if ( "WebSocket" in window )
{
var ws = new WebSocket( "ws://localhost:1984/chat" );
ws.onopen = function( )
{
add_message( "Established connection." );
toggle_control_access( );
};
ws.onmessage = function( evt )
{
add_message( evt.data );
};
ws.onclose = function( evt )
{
add_message( "Connection closed with RFC6455 code " + evt.code + "." );
toggle_control_access( );
};
ws.onerror = function( evt )
{
add_message( "Error: socket connection interrupted." );
};
window.restbed.ws = ws;
}
else
{
alert( "WebSockets NOT supported by your Browser!" );
}
}
function close( )
{
window.restbed.ws.close( );
}
( function( )
{
window.restbed = { ws: null };
} )( );
</script>
</head>
<body>
<div>
<ul id="messages"></ul>
<div id="controls">
<input id="message" type="text" onKeyPress="return on_return_submit( event )" disabled/>
<div>
<a id="open" href="javascript:open( )">Open Chat</a>
<a id="close" href="javascript:close( )" class="disabled">Close Chat</a>
<div>
</div>
</div>
</body>
</html>
```