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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,844 @@
Overview
--------
This document is intended to communicate core architectural decisions within the system. For this reason alone accuracy has suffered. It does not concern itself with interface specifics and primarily focuses on architectural decisions made during the design and development phase, see [API](API.md) for contract details.
All class definitions within the system strictly adhere to the [Opaque Pointer](https://en.wikipedia.org/wiki/Opaque_pointer) idiom. However, this level of detail in the following suite of class diagrams is omitted for clarity; along with pointers, references and other background noise.
Unless otherwise specified all primary data-types originate within the Standard Template Library (STL).
Interpretation
--------------
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](http://tools.ietf.org/pdf/rfc2119.pdf).
Table of Contents
-----------------
1. [Overview](#overview)
2. [Interpretation](#interpretation)
3. [Terminology](#terminology)
4. [System Entities](#system-entities)
5. [Entity Interactions](#entity-interactions)
6. [Dependency Tree](#dependency-tree)
7. [Event Loop](#event-loop)
8. [Thread Allocation](#thread-allocation)
9. [Future Direction](#future-direction)
10. [Further Reading](#further-reading)
Terminology
-----------
| Term | Definition |
|--------------|-----------------------------------------------------------------------------------------------------------------------------|
| Resource | A network addressable entity i.e Queue. |
| Service | A server responsible for request routing and processing. |
| Client | A remote endpoint responsible for generating requests. |
| Logger | A component making a systematic recording of events, observations, or measurements. |
| Charset | A CHARacter SET is used to represent a repertoire of symbols i.e UTF-8. |
| URI | Uniform Resource Identifier. |
| UUID | Universally Unique IDentifier. |
| Path | String identifier uniquely addressing a resource. |
System Entities
---------------
- [Byte/Bytes](#bytebytes)
- [Resource](#resource)
- [Callback](#callback)
- [StatusCode](#statuscode)
- [String::Option](#stringoption)
- [String](#string)
- [URI](#uri)
- [Request](#request)
- [Response](#response)
- [Session](#session)
- [SessionManager](#sessionmanager)
- [Rule](#rule)
- [SSLSettings](#sslsettings)
- [Settings](#settings)
- [WebSocket](#websocket)
- [WebSocketMessage](#websocketmessage)
- [WebSocketMessage::OpCode](#websocketmessageopcode)
- [Logger](#logger)
- [Logger::Level](#loggerlevel)
- [Service](#service)
### Byte/Bytes
Byte represents an unsigned 8-bit wide data-type, Bytes provides container functionality with STL [vector](http://en.cppreference.com/w/cpp/container/vector) collection semantics.
```
+----------------+
| <<typedef>> |
| Bytes |
+----------------+
| vector< Byte > |
+--------@-------+
|
|
|
+--------+-------+
| <<typedef>> |
| Byte |
+----------------+
| uint8_t |
+----------------+
```
### Resource
Resource represents an network communication endpoint. This is the primary data-structure used throughout to represent RESTful resources. All resource specific filteration, request processing rules, and authentication must be placed on this entity.
```
+------------------------------------------------------------------------+
| <<class>> |
| Resources |
+------------------------------------------------------------------------+
| + add_rule(Rule) void |
| + add_rule(Rule, integer) void |
| + set_path(string) void |
| + set_paths(set<string>) void |
| + set_default_header(string,string) void |
| + set_default_headers(multimap<string,string>) void |
| + set_failed_filter_validation_handler(Callback) void |
| + set_error_handler(Callback) void |
| + set_authentication_handler(void) void |
| + set_method_handler(string,Callback) void |
| + set_method_handler(string,multimap<string,string>,Callback) void |
+------------------------------------------------------------------------+
```
### Callback
Represents a functor with variable parameters and return; this is used to help illustrate the design without introducing unnecessary complexity.
```
+-----------------+
| <<typedef>> |
| Callback |
+-----------------+
| std::function |
+-----------------+
```
### StatusCode
[Enumeration](http://en.cppreference.com/w/cpp/language/enum) of HTTP response status codes as outlined in [RFC 7231 sub-section 6.1](https://tools.ietf.org/html/rfc7231#section-6.1).
```
+---------------------------+
| <<enum>> |
| StatusCode |
+---------------------------+
| See RFC 7231 for details. |
+---------------------------+
```
### String::Option
[Enumeration](http://en.cppreference.com/w/cpp/language/enum) of possible string case sensitivity options.
```
+--------------------+
| <<enum>> |
| String::Option |
+--------------------+
| + CASE_SENSITIVE |
| + CASE_INSENSITIVE |
+------------------ -+
```
### String
Utility class with static scope offering a common suite of string manipulation routines.
```
+---------------------------------------------------------------+
| <<static>> |
| String |
+---------------------------------------------------------------+
| + to_bytes(string) Bytes |
| + to_string(Bytes) string |
| + lowercase(string) string |
| + uppercase(string,string) string |
| + format(char*,...) string |
| + join(multimap<string,string>,string,string) string |
| + remove(string,string,Option) string |
| + replace(string,string,string,Option) string |
| + split(string,string) vector<string> |
+--------------------------------^------------------------------+
|
|
|
+----------------v----------------+
| <<enum>> |
| String::Option |
+---------------------------------+
| See String::Option for details. |
+---------------------------------+
```
### URI
Represents a Uniform Resource Identifier as specified in [RFC 3986](https://www.ietf.org/rfc/rfc3986.txt).
> A generic URI is of the form:
>
> scheme:[//[user:password@]host\[:port]][/]path[?query][#fragment]
```
+-------------------------------------------------------+
| <<class>> |
| Uri |
+-------------------------------------------------------+
| + is_relative(void) boolean |
| + is_absolute(void) boolean |
| + is_valid(string) boolean |
| + parse(string) Uri |
| + to_string(void) string |
| + decode(Bytes) string |
| + decode(string) string |
| + decode_parameter(string) string |
| + encode(Bytes) string |
| + encode(string) string |
| + encode_parameter(string) string |
| + get_port(void) unsigned 16-bit integer |
| + get_path(void) string |
| + get_query(void) string |
| + get_scheme(void) string |
| + get_fragment(void) string |
| + get_username(void) string |
| + get_password(void) string |
| + get_authority(void) string |
| + get_query_parameters(void) multimap<string,string> |
+-------------------------------------------------------+
```
### Request
Represents a HTTP request with additional helper methods for manipulating data, and code readability.
```
+--------------------------------------------------------------------------------+
| <<class>> |
| Request |
+--------------------------------------------------------------------------------+
| + has_header(string) boolean |
| + has_path_parameter(string) boolean |
| + has_query_parameter(string) boolean |
| + get_port(void) unsigned 16-bit integer |
| + get_version(void) double |
| + get_body(void) Bytes |
| + get_body(Callback) Bytes |
| + get_response(void) Response |
| + get_host(Callback) string |
| + get_path(Callback) string |
| + get_method(Callback) string |
| + get_protocol(Callback) string |
| + get_header(string,template<Type>) template<Type> |
| + get_header(string,string) string |
| + get_header(string,Callback) string |
| + get_headers(string) multimap<string,string> |
| + get_query_parameter(string,template<Type>) template<Type> |
| + get_query_parameter(string,string) string |
| + get_query_parameter(string,Callback) string |
| + get_query_parameters(string) multimap<string,string> |
| + get_path_parameter(string,template<Type>) template<Type> |
| + get_path_parameter(string,string) string |
| + get_path_parameter(string,Callback) string |
| + get_path_parameters(string) map<string,string> |
| + set_body(Bytes) void |
| + set_body(string) void |
| + set_port(unsigned 16-bit integer) void |
| + set_version(double) void |
| + set_path(string) void |
| + set_host(string) void |
| + set_method(string) void |
| + set_protocol(string) void |
| + add_header(string,string) void |
| + set_header(string,string) void |
| + set_headers(multimap<string,string>) void |
| + set_query_parameter(string,string) void |
| + set_query_parameters(multimap<string,string>) void |
+------------------------------------------@-------------------------------------+
| 1
|
|
| 1
+------------------+-----------------+
| <<class>> |
| Response |
+------------------------------------+
| See Response for details. |
+------------------------------------+
```
### Response
Represents a HTTP response with additional helper methods for manipulating data, and improving code readability.
```
+-----------------------------------------------------------------+
| <<class>> |
| Response |
+-----------------------------------------------------------------+
| + has_header(string) boolean |
| + get_body(void) Bytes |
| + get_body(Callback) Bytes |
| + get_version(void) double |
| + get_status_code(void) integer |
| + get_header(string,template<Type>) template<Type> |
| + get_header(string,string) string |
| + get_header(string,Callback) string |
| + get_headers(string) multimap<string,string> |
| + set_body(Bytes) void |
| + set_body(string) void |
| + set_version(double) void |
| + set_status_code(integer) void |
| + set_protocol(string) void |
| + set_status_message(string) void |
| + add_header(string,string) void |
| + set_header(string,string) void |
| + set_headers(multimap<string,string>) void |
+-----------------------------------------------------------------+
```
### Session
Represents a conversation between a client and the service. Internally this class holds the network state and exposes public functionality to interact with the service event-loop for asynchronous data acquisition and/or sleep states.
```
+-------------------------------------------------------------------------------------+
| <<class>> |
| Session |
+-------------------------------------------------------------------------------------+
| + has(string) boolean |
| + erase(string) void |
| + keys(void) set<string> |
| + is_open(void) boolean |
| + is_closed(void) boolean |
| + close(Bytes) void |
| + close(Response) void |
| + close(string) void |
| + close(integer,Bytes) void |
| + close(integer,string) void |
| + close(integer,multimap<string,string>) void |
| + close(integer,string,multimap<string,string>) void |
| + close(integer,Bytes,multimap<string,string>) void |
| + yield(Bytes,Callback) void |
| + yield(string,Callback) void |
| + yield(Response,Callback) void |
| + yield(integer,string,Callback) void |
| + yield(integer,Bytes,Callback) void |
| + yield(integer,multimap<string,string>,Callback) void |
| + yield(integer,Bytes,multimap<string,string>,Callback) void |
| + yield(integer,string,multimap<string,string>,Callback) void |
| + fetch(size_t,Callback) void |
| + fetch(string,Callback) void |
| + upgrade(integer,Callback) void |
| + upgrade(integer,Bytes,Callback) void |
| + upgrade(integer,string,Callback) void |
| + upgrade(integer,multimap<string,string>,Callback) void |
| + upgrade(integer,Bytes,multimap<string,string>,Callback) void |
| + upgrade(integer,string,multimap<string,string>,Callback) void |
| + sleep_for(milliseconds,Callback) void |
| + get_id(void) string |
| + get_origin(void) string |
| + get_destination(void) string |
| + get_request(void) Request |
| + get_resource(void) Resource |
| + get_headers(void) multimap<string,string> |
| + get(string) ContextValue |
| + get(string,ContextValue) ContextValue |
| + set_id(string) void |
| + set(string,ContextValue) void |
| + add_headerstring,string) void |
| + set_header(string,string) void |
| + set_headers(multimap<string,string>) void |
+----------------------------------------@--------------------------------------------+
1 | 1
|
+------------------+-------------------+
| |
| |
1 | | 1
+------------------+----------------+ +------------------+-----------------+
| <<class>> | | <<class>> |
| Request | | Resource |
+-----------------------------------+ +------------------------------------+
| See Request for details. | | See Resource for details. |
+-----------------------------------+ +------------------------------------+
```
### SessionManager
Abstract Class detailing the required contract for SessionManager extensions. No default implementation is supplied with the codebase and it is the responsibility of third-party developers to implement desired characteristics.
```
+---------------------------------+
| <<abstract>> |
| SessionManager |
+---------------------------------+ +---------------------------+
| + stop(void) void | | <<class>> |
| + start(Settings) void | 1 1 | Settings |
| + create(Callback) void @---------+---------------------------+
| + load(Session,Callback) void | | See Settings for details. |
| + save(Session,Callback) void | +---------------------------+
+----------------^----------------+
|
|
+-------------v------------+
| <<class>> |
| Session |
+--------------------------+
| See Session for details. |
+--------------------------+
```
### Rule
Represents a reusable source of request validation and processing; see [Rules Engine](https://github.com/Corvusoft/restbed/tree/master/example/rules_engine/source) example for details.
```
+-------------------------------------+
| <<interface>> |
| Rule |
+-------------------------------------+
| + condition(Session) boolean |
| + action(Session,Callback) void |
| + get_priority(void) integer |
| + set_priority(integer) void |
+-----------------^-------------------+
|
|
+-------------v------------+
| <<class>> |
| Session |
+--------------------------+
| See Session for details. |
+--------------------------+
```
### SSLSettings
Represents Secure Socket Layer service configuration.
```
+---------------------------------------------------------------------------+
| <<class>> |
| SSLSettings |
+---------------------------------------------------------------------------+
| See restbed::SSLSettings for details. |
+---------------------------------------------------------------------------+
| + has_disabled_http(void) boolean |
| + has_enabled_sslv2(void) boolean |
| + has_enabled_sslv3(void) boolean |
| + has_enabled_tlsv1(void) boolean |
| + has_enabled_tlsv11(void) boolean |
| + has_enabled_tlsv12(void) boolean |
| + has_enabled_compression(void) boolean |
| + has_enabled_default_workarounds(void) boolean |
| + has_enabled_single_diffie_hellman_use(void) boolean |
| + get_port(void) unsigned 16-bit integer |
| + get_bind_address(void) string |
| + get_certificate(void) string |
| + get_passphrase(void) string |
| + get_private_key(void) string |
| + get_private_rsa_key(void) string |
| + get_certificate_chain(void) string |
| + get_temporary_diffie_hellman(void) string |
| + get_certificate_authority_pool(void) string |
| + set_port(unsigned 16-bit integer) void |
| + set_bind_address(string) void |
| + set_http_disabled(boolean) void |
| + set_sslv2_enabled(boolean) void |
| + set_sslv3_enabled(boolean) void |
| + set_tlsv1_enabled(boolean) void |
| + set_tlsv11_enabled(boolean) void |
| + set_tlsv12_enabled(boolean) void |
| + set_compression_enabled(boolean) void |
| + set_default_workarounds_enabled(boolean) void |
| + set_single_diffie_hellman_use_enabled(boolean) void |
| + set_certificate(Uri) void |
| + set_certificate_chain(Uri) void |
| + set_certificate_authority_pool(Uri) void |
| + set_passphrase(string) void |
| + set_private_key(Uri) void |
| + set_private_rsa_key(Uri) void |
| + set_temporary_diffie_hellman(Uri) void |
+--------------------------------------^------------------------------------+
|
|
+----------v-----------+
| <<class>> |
| Uri |
+----------------------+
| See Uri for details. |
+----------------------+
```
### Settings
Represents the primary point of Service, SessionManager, and Logger configuration.
```
+-------------------------------------------------------------------------+
| <<class>> |
| Settings |
+-------------------------------------------------------------------------+
| + get_port(void) unsigned 16-bit integer |
| + get_root(void) string |
| + get_worker_limit(void) unsigned integer |
| + get_connection_limit(void) unsigned integer |
| + get_bind_address(void) string |
| + get_case_insensitive_uris(void) boolean |
| + get_connection_timeout(void) milliseconds |
| + get_status_message(integer) string |
| + get_status_messages(void) map<integer,string> |
| + get_property(string) string |
| + get_properties(void) map<string,string> |
| + get_ssl_settings(void) SSLSettings |
| + get_default_headers(void) multimap<string,string> |
| + set_port(unsigned 16-bit integer) void |
| + set_root(string) void |
| + set_worker_limit(unsigned integer) void |
| + set_connection_limit(unsigned integer) void |
| + set_bind_address(string) void |
| + set_case_insensitive_uris(boolean) void |
| + set_connection_timeout(seconds) void |
| + set_connection_timeout(milliseconds) void |
| + set_status_message(integer,string) void |
| + set_status_messages(map<integer,string>) void |
| + set_property(string,string) void |
| + set_properties(map<string,string>) void |
| + set_ssl_settings(SSLSettings) void |
| + set_default_header(string,string) void |
| + set_default_headers(multimap<string,string>) void |
+------------------------------------@------------------------------------+
|
|
|
+--------------+---------------+
| <<class>> |
| SSLSettings |
+------------------------------+
| See SSLSettings for details. |
+------------------------------+
```
### WebSocket
Represents a WebSocket connection. Internally this class holds the network state and exposes public functionality for full-duplex client interaction.
```
+-----------------------------------------------------+
| <<class>> |
| WebSocket |
+-----------------------------------------------------+
| + is_open(void) boolean |
| + is_closed(void) boolean |
| + close(void) void |
| + send(Bytes,Callback) void |
| + send(string,Callback) void |
| + send(WebSocketMessage::OpCode,Callback) void |
| + send(WebSocketMessage,Callback) void |
| + get_key(void) string |
| + get_logger(void) Logger |
| + get_open_handler(void) Callback |
| + get_close_handler(void) Callback |
| + get_error_handler(void) Callback |
| + get_message_handler(void) Callback |
| + set_key(string) void |
| + set_logger(Logger) void |
| + set_open_handler(Callback) void |
| + set_close_handler(Callback) void |
| + set_error_handler(Callback) void |
| + set_message_handler(Callback) void |
+------------------------^----------------------------+
|
|
+------------------+---------------------+
| |
| |
| |
+------------------v----------------+ +--------------------v----------------------+
| <<class>> | | <<enum>> |
| WebSocketMessage | | WebSocketMessage::OpCode |
+-----------------------------------+ +-------------------------------------------+
| See WebSocketMessage for details. | | See WebSocketMessage::OpCode for details. |
+-----------------------------------+ +-------------------------------------------+
```
### WebSocketMessage
Class representing a single WebSocket data message.
```
+--------------------------------------------------------------------------------+
| <<class>> |
| WebSocketMessage |
+--------------------------------------------------------------------------------+
| + to_bytes(void) Bytes |
| + get_data(void) Bytes |
| + get_opcode(void) WebSocketMessage::OpCode |
| + get_mask(void) unsigned 32-bit integer |
| + get_length(void) unsigned 8-bit integer |
| + get_extended_length(void) unsigned 64-bit integer |
| + get_mask_flag(void) boolean |
| + get_final_frame_flag(void) boolean |
| + get_reserved_flags(void) tuple<boolean,boolean,boolean> |
| + set_data(Bytes) void |
| + set_data(string) void |
| + set_opcode(WebSocketMessage::OpCode) void |
| + set_mask(unsigned 32-bit integer) void |
| + set_length(unsigned 8-bit integer) void |
| + set_extended_length(unsigned 64-bit integer) void |
| + set_mask_flag(boolean) void |
| + set_final_frame_flag(boolean) void |
| + set_reserved_flags(boolean,boolean,boolean) void |
+--------------------------------------------------------------------------------+
|
|
|
+--------------------v----------------------+
| <<enum>> |
| WebSocketMessage::OpCode |
+-------------------------------------------+
| See WebSocketMessage::OpCode for details. |
+-------------------------------------------+
```
### WebSocketMessage::OpCode
[Enumeration](http://en.cppreference.com/w/cpp/language/enum) used in conjunction with the [WebSocket](#websocket) and [WebSocketMessage](#websocketmessage) to detail the message category.
```
+--------------------------+
| <<enum>> |
| WebSocketMessage::OpCode |
+--------------------------+
| CONTINUATION_FRAME |
| TEXT_FRAME |
| BINARY_FRAME |
| CONNECTION_CLOSE_FRAME |
| PING_FRAME |
| PONG_FRAME |
+--------------------------+
```
### Logger
Interface detailing the required contract for logger extensions. No default logger is supplied with the codebase and it is the responsibility of third-party developers to implement the desired characteristics.
```
+-----------------------------------------------+
| <<interface>> |
| Logger |
+-----------------------------------------------+
| + stop(void) void |
| + start(Settings) void |
| + log(Logger::Level,string) void |
| + log_if(condition,Logger::Level,string) void |
+----------------------^------------------------+
|
|
|
+---------------v----------------+
| <<enum>> |
| Logger::Level |
+--------------------------------+
| See Logger::Level for details. |
+--------------------------------+
```
### Logger::Level
[Enumeration](http://en.cppreference.com/w/cpp/language/enum) used in conjunction with the [Logger interface](#logger) to detail the level of severity towards a particular log entry.
```
+---------------+
| <<enum>> |
| Logger::Level |
+---------------+
| INFO |
| DEBUG |
| FATAL |
| ERROR |
| WARNING |
| SECURITY |
+---------------+
```
### Service
The service is responsible for managing the publicly available RESTful resources, HTTP compliance, scheduling of the socket data and insuring incoming requests are processed in a timely fashion.
```
+-----------------------------------------------------------+
| <<class>> |
| Service |
+-----------------------------------------------------------+
| + is_up(void) boolean |
| + is_down(void) boolean |
| + stop(void) void |
| + start(Settings) void |
| + restart(Settings) void |
| + add_rule(Rule) void |
| + add_rule(Rule,integer) void |
| + publish(Resource) void |
| + suppress(Resource) void |
| + schedule(Callback,milliseconds) void |
| + get_uptime( void ) seconds |
| + get_http_uri( void ) Uri |
| + get_https_uri( void ) Uri |
| + set_logger(Logger) void |
| + set_session_manager(SessionManager) void |
| + set_ready_handler(Callback) void |
| + set_signal_handler(integer,Callback) void |
| + set_not_found_handler(Callback) void |
| + set_method_not_allowed_handler(Callback) void |
| + set_method_not_implemented_handler(Callback) void |
| + set_failed_filter_validation_handler(Callback) void |
| + set_error_handler(Callback) void |
| + set_authentication_handler(Callback) void |
+-----------------------------O-----------------------------+
|
|
+-------------------------------+--------------+------------------+--------------------------+
| | | |
| | | |
| | | |
+--------------+------------+ +---------------+-----------------+ +------------+------------+ +----------+------------+
| <<class>> | | <<interface>> | | <<interface>> | | <<interface>> |
| Settings | | SessionManager | | Logger | | Rule |
+---------------------------+ +---------------------------------+ +-------------------------+ +-----------------------+
| See Settings for details. | | See SessionManager for details. | | See Logger for details. | | See Rule for details. |
+---------------------------+ +---------------------------------+ +-------------------------+ +-----------------------+
```
Entity Interactions
-------------------
### Request Processing
```
[client] [service] [session manager] [resource]
| HTTP Request | | |
|------------------------------------>| | |
| | Create Session | |
| |------------------------------------>| |
| | | |
| |<------------------------------------| |
| +---| | |
| Parse Request | | | |
| +-->| | |
| +---| | |
| Perform Service Authentication | | | |
| +-->| | |
| | Load Persistent Session Data | |
| |------------------------------------>| |
| | | |
| |<------------------------------------| |
| +---| | |
| Process Service Ruleset | | | |
| +-->| | |
| | Route Request to Resource |
| |-------------------------------------------------------------------------->|
| | | +---|
| | | Perform Resource Authentication | |
| | | +-->|
| | | +---|
| | | Process Resource Ruleset | |
| | | +-->|
| | | +---|
| | | Process Method Filters | |
| | | +-->|
| | | +---|
| | | Invoke Method Handler | |
| | | +-->|
| | HTTP Response | |
|<----------------------------------------------------------------------------------------------------------------|
| | | |
```
Dependency Tree
---------------
```
+----------------------------+
| Restbed |
+----------------------------+
| Asynchronous Web Framework |
+----------------------------+
|
+------------------+------------------+
| |
+--------------------+ +----------------------+
| ASIO | | OpenSSL |
+--------------------+ +----------------------+
| Asynchronous I/O . | | Secure Socket Layer. |
+--------------------+ +----------------------+
```
Event Loop
----------
The asynchronous nature of the framework is achieved by employing an [event-reaction loop](https://en.wikipedia.org/wiki/Event_loop), allowing Non-Block I/O and timed waits for external stimuli. This architectural decision allows the framework to scale and address the [C10K Problem](https://en.wikipedia.org/wiki/C10k_problem), all the while keeping resource consumption to a minimum and optimising thread allocation.
Thread allocation
-----------------
By default the framework will only allocate a single thread to process all incoming requests and scheduled work loads. This can be increased by altering the `Settings::set_worker_limit` attribute. It is recommended that you should not exceed the available hardware limit, see below for a suggested implementation.
``` C++
#include <thread>
#include <cstdlib>
#include <restbed>
using namespace restbed;
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_worker_limit( std::thread::hardware_concurrency( ) );
Service service;
service.publish( resource );
service.start( settings );
return EXIT_SUCCESS;
}
```
Future Direction
----------------
It is the aim of the core development team to remove ASIO as a dependency due to the tight coupling of IO and Event Loop principles. This setup leads to cross contamination of concerns and forces design decisions on dependees. The team will present an event-loop offering superior handling and management of event-reaction task processing in the first quarter of 2017, and integration within Restbed before close of the same year.
The ContextValue which provides boost::any functionality for Session state shall be replaced by std::any within the C++17 standard in future releases. If std::any is not supported a replacement drop-in shall be provided during compile time.
WebSocketManager and ResourceCache shall be exposed for customisation by third-party developers.
Application Layer (HTTP, HTTP2, SPDY, etc...) will be exposed for customisation by third-party developers.
Network Layer (TCP, UDP, RS232, etc...) shall be exposed for customisation by third-party developers.
The secure socket logic layer will be exposed to allow alternative implementations OpenSSL, GnuTLS, PolarSSL, MatrixSSL, etc...
Client functionality will be extracted from the Restbed framework, this decision has been made due to conflicting concepts on the client/server side i.e A HTTP Session has differing properties depending on the which side of the communication channel you find yourself. Additionally this will aid in simplifying required APIs on each end, leading to a more self documenting codebase. To follow the latest progress please review the [Restless project](https://github.com/corvusoft/restless).
Further Reading
---------------
[Opaque Pointer](https://en.wikipedia.org/wiki/Opaque_pointer) - In computer programming, an opaque pointer is a special case of an opaque data type, a datatype declared to be a pointer to a record or data structure of some unspecified type.
[Uniform Resource Identifier (URI): Generic Syntax](https://tools.ietf.org/html/rfc3986) - A Uniform Resource Identifier (URI) is a compact sequence of characters that identifies an abstract or physical resource. This specification defines the generic URI syntax and a process for resolving URI references that might be in relative form, along with guidelines and security considerations for the use of URIs on the Internet. The URI syntax defines a grammar that is a superset of all valid URIs, allowing an implementation to parse the common components of a URI reference without knowing the scheme-specific requirements of every possible identifier. This specification does not define a generative grammar for URIs; that task is performed by the individual specifications of each URI scheme.
[C10K Problem](https://en.wikipedia.org/wiki/C10k_problem) - The C10K Problem refers to the inability of a server to scale beyond 10,000 connections or clients due to resource exhaustion. Servers that employ the thread-per-client model, for example, can be confounded when pooled threads spend too much time waiting on blocking operations.
[ASIO](http://think-async.com/) - Asio is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach.
[Eventloop](https://en.wikipedia.org/wiki/Event_loop) - In computer science, the event loop, message dispatcher, message loop, message pump, or run loop is a programming construct that waits for and dispatches events or messages in a program. It works by making a request to some internal or external "event provider" (that generally blocks the request until an event has arrived), and then it calls the relevant event handler ("dispatches the event"). The event-loop may be used in conjunction with a reactor, if the event provider follows the file interface, which can be selected or 'polled' (the Unix system call, not actual polling). The event loop almost always operates asynchronously with the message originator.

View File

@@ -0,0 +1,265 @@
Overview
--------
This document sets out the expected coding style for those who wish to participate in this project. With this standard we will maintain a readable codebase that can be easily cannibalised by new and old developers alike.
To ensure the codebase meets this standard please install the supplied [GIT pre-commit](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) hook into your local copy. You're also required to install the [Artistic Style](http://astyle.sourceforge.net/) tool on your system for this hook to be successful; you must have version 2.05 or greater installed.
```
cd restbed/
cp tool/git/pre-commit .git/hooks
chmod +x .git/hooks/pre-commit
```
**most importantly:** "know when to be inconsistent -- sometimes the style guide just doesn't apply. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don't hesitate to ask!" -- Guido van Rossum, Barry Warsaw, Nick Coghlan.
Interpretation
--------------
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](http://tools.ietf.org/pdf/rfc2119.pdf).
Table of Contents
-----------------
1. [Overview](#overview)
2. [Interpretation](#interpretation)
3. [Brackets](#brackets)
4. [Indentation](#indentation)
5. [Comments](#comments)
6. [Properties/Variables](#propertiesvariables)
7. [Classes](#classes)
8. [Enumerations](#enumerations)
9. [Structures](#structures)
10. [Methods/Functions](#methodsfunctions)
11. [Whitespace](#whitespace)
12. [Pointers/References](#pointersreferences)
13. [Exceptions](#exceptions)
14. [Namespaces](#namespaces)
15. [Optimisations](#optimisations)
### Brackets
- Allman style broken braces, and avoid unbraced one line conditional statements.
```C++
if ( condition == true )
{
...
}
else
{
...
}
```
### Indentation
- Do **not** use tabs. All indentation is of 4 spaces.
- Class, Namespace, Struct, If, For, Switch, etc.. statements **must** be indented.
```C++
namespace transport
{
class Car( void )
{
public:
Car( const int number_of_wheels ) : m_number_of_wheels
{
switch( m_number_of_wheels )
{
case 1:
break;
default:
break;
}
}
private:
uint8_t m_number_of_wheels;
}
}
```
### Comments
- Comments **should** be avoided as they add an additional layer of management and/or technical-debt.
- Commenting is just as ineffective as poorly readable code.
- Code should be self documenting with descriptive naming conventions applied to functions, variables, files, directories, etc...
- Commenting may be a sign you need to split the logic into multiple manageable blocks.
### Properties/Variables
- Property and Variables names **must** follow the [Snake-case naming convention](https://en.wikipedia.org/wiki/snake_case).
- Property and Variables **must** be initialised to a known state on declaration.
```C++
int limit = 0;
int person_age = 21;
string name = String::empty;
```
### Classes
- Class property names **must** be prefixed with `m_` and follow the [Snake-case naming convention](https://en.wikipedia.org/wiki/snake_case).
- Class properties **must** be initialised to a known state on instance creation.
- Class properties **must** be private in scope.
- Class getter/setter accessor methods **must not** return non-const pointers/references.
- There **must** be **no** using namespace declarations in class header files.
- Forward declarations are favoured over `#include` within class header files; with the exception of the standard template library.
- Empty method bodies (when unavoidable) *shall* be marked with a single return.
- Public classes **must** implement an [opaque pointer](http://en.wikipedia.org/wiki/Opaque_pointer).
- Class names **must** start each word boundary with an UPPERCASED letter.
```C++
class Person
{
public:
Person( void ) : m_age( 0 )
{
return;
}
int get_age( void ) const
{
return m_age;
}
void set_age( const int value )
{
m_age = value;
}
private:
int m_age;.
}
```
### Enumerations
- Enumerations **must** be strongly typed.
- Enumeration fields **must** be UPPERCASED.
- Enumeration fields **must** be initialised to a known state.
- Enumeration names **must** start each word boundary with an UPPERCASED letter.
```C++
enum LogLevel : int
{
INFO = 0000,
DEBUG = 1000,
FATAL = 2000,
ERROR = 3000,
WARNING = 4000,
SECURITY = 5000
}
```
### Structures
- Structure property names **must** follow the [Snake-case naming convention](https://en.wikipedia.org/wiki/snake_case).
- Structure properties **must** be initialised to a known state on instance creation.
- Structure names **must** start each word boundary with an UPPERCASED letter.
```C++
struct HttpRequest
{
Bytes body = { };
uint16_t port = 80;
double version = 1.1;
std::string host = "";
std::string path = "/";
std::string method = "GET";
std::multimap< std::string, std::string > headers { };
}
```
### Methods/Functions
- Functions and Methods **must** use the [Snake-case naming convention](https://en.wikipedia.org/wiki/snake_case).
- Functions and Methods **should** perform one mental operation which is reflected in their name.
- Function and Method declarations **should** avoid similar argument types.
- It is recommended that Functions and Methods are no greater than 70 lines of code. If you find that the [LOC](https://en.wikipedia.org/wiki/Source_lines_of_code) exceed this limit, it may be an indication that it is performing more than one mental operation; see rule 2.
```C++
int ping( Hostname hostname, Interface interface, int16_t port )
{
auto port = port;
auto hostname = hostname.to_string( );
auto interface = hostname.to_string( )
...
return ttl;
}
```
### Whitespace
- Whitespace is free, don't be scared to use it.
```C++
int process_exit_status = 0;
const string filename = "/bin/ls";
do
{
Process reaction = load_process( filename );
reaction.run( );
process_exit_status = reaction.get_exit_status( );
}
while ( process_exit_status not_eq -1 );
```
### Pointers/References
- Pointers and References **must** be aligned to the data type (left).
```C++
int* age = nullptr;
char* forename = nullptr;
string& surname = m_surname;
```
### Exceptions
- Logic within catch-blocks **must** be short & sweet.
- Name exceptions per Java styling.
```C++
try
{
start( );
}
catch ( const invalid_argument& ia )
{
stop( );
}
catch ( const runtime_error& re )
{
stop( );
}
catch ( const exception& ex )
{
stop( );
}
```
### Namespaces
- **Do not** include entire Namespaces, import only the artifact you're interested in.
- Namespace names **must only** contain lowercased letters.
```C++
using std::mutex;
using std::string;
using std::thread;
namespace restbed
{
...
}
```
### Optimisations
- Avoid premature optimisation, **readable, maintainable code is more important**.

View File

@@ -0,0 +1,145 @@
Overview
--------
Corvusoft's development teams frequently employee the use of the [Unified Modeling Language](https://en.wikipedia.org/wiki/Unified_Modeling_Language) (UML) to assist in communicating core concepts and design decisions of a system or sub-system to technical and non-technical parties alike.
Of the many tools available within UML we primarily employ [Class diagrams](https://en.wikipedia.org/wiki/Class_diagram) for detailing attributes, operations, and relationships. Behavioral characteristics and entity interactions are illustrated with [Sequence diagrams](https://en.wikipedia.org/wiki/Sequence_diagram).
This document outlines our interpretation of relevant UML concepts and aims in creating a level playing field for all participants during software design discussions.
Interpretation
--------------
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](http://tools.ietf.org/pdf/rfc2119.pdf).
Table of Contents
-----------------
1. [Overview](#overview)
2. [Interpretation](#interpretation)
3. [Class Diagrams](#class-diagrams)
4. [Sequence Diagrams](#sequence-diagrams)
### Class Diagrams
In software engineering, a class diagram in the Unified Modeling Language (UML) is a type of static structure diagram that describes the structure of a system by showing the system's classes, their attributes, operations (or methods), and the relationships among objects.
- [Relationships](#relationships)
- [Association](#association)
- [Aggregation](#aggregation)
- [Composition](#composition)
- [Generalisation / Inheritance](#generalisation--inheritance)
- [Realisation](#realisation)
- [Dependency](#dependency)
- [Sterotypes](#sterotypes)
- [Multiplicity](#multiplicity)
- [Visibility](#visibility)
- [Example](#example)
#### Relationships
```
Association: ^ Aggregation: O Composition: @ Generalisation: # Realisation: : Dependency: _
| | | | | |
<-+-> O-+-O @-+-@ #-+-# :-+-: |-+-|
| | | | | |
V O @ # : _
```
##### Association
Association describes logical connections or relationships between two or more entities.
##### Aggregation
Aggregation (shared association) is a variant of the "has a" association relationship; aggregation is more specific than association. It is an association that represents a part-whole or part-of relationship.
##### Composition
Composition (not-shared association) is a stronger variant of the "has a" association relationship; composition is more specific than aggregation, emphasizing the dependence of the contained class to the life cycle of the container class.
##### Generalisation / Inheritance
Generalisation refers to a type of relationship wherein one associated class is a child of another by virtue of assuming the same functionalities of the parent class; in other words, the child class is a specific type of the parent class.
##### Realisation
Realisation denotes the implementation of the interface defined in one class by another class.
##### Dependency
Dependency is a uni-directional semantic relationship between two classes. It illustrates that one class relies on the functionality exported from another class.
#### Sterotypes
| &lt;&lt;Sterotype&gt;&gt; | Description |
|:-------------------------:|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| static | Indicates an entity whose lifetime or "extent" extends across the entire run of the program. |
| class | Represents an entity providing initial values for state and implementations of behavior. |
| typedef | Used to create an alias for any other data-type. As such, it is often used to simplify the syntax of declaring complex data structures. |
| enum | enumeration sterotype shows a set of named values called elements, members, enumeral, or enumerators of the type. The enumerator names are usually identifiers that behave as constants in the program. |
| interface | Shows a common means for unrelated objects to communicate with each other. These are definitions of methods and values which the objects agree upon in order to co-operate. |
| abstract | An abstract type may provide no implementation, or an incomplete implementation; the entity cannot be instantiated directly. |
### Multiplicity
Optional notation indicating the range of entities within a relationship.
| Notation | Description |
|:--------:|--------------------------------|
| 0..1 | No instances, or one instance. |
| 1 | Exactly one instance. |
| 0..\* | Zero or more instances. |
| \* | Zero or more instances. |
| 1..\* | One or more instances. |
### Visibility
It is encouraged to only show public methods, helping to reduce rework of the documentation during each software development cycle. The use of Private, Protected, Derived, and Package visibility should only be present when highlighting important core design decision. For example inheriting from a base class and altering parent method/property visibility.
| Symbol | Description |
|:------:|-------------|
| \+ | Public |
| \- | Private |
| \# | Protected |
| ~ | Package |
To specify the visibility of a class member (i.e. any attributes or methods), the notation must be placed before the member's name.
#### Example
The following diagram shows that a Session class exposes two public accessor methods, and therefore is composed of a one-to-one relationship with both the Request and Response classes.
```
+----------------------------+
| <<class>> | 1
1 +----@+ Session +@----+
| |----------------------------| |
| | + get_request( ) Request | |
| | + get_response( ) Response | |
| +----------------------------+ |
| |
1 | | 1
+------+-------+ +------+-------+
| <<class>> | | <<class>> |
| Request | | Response |
+--------------+ +--------------+
```
### Sequence Diagrams
A [Sequence Diagram](https://en.wikipedia.org/wiki/Sequence_diagram) is an interaction diagram that shows how objects operate with one another and in what order. It is a construct of a [Message Sequence Chart](https://en.wikipedia.org/wiki/Message_sequence_chart). A sequence diagram shows object interactions arranged in time sequence.
##### Example
```
[client] [exchange] [formatter] [repository]
| | ' |
| Destroy (DELETE) resource | ' |
|---------------------------------------->| ' |
| |----------------------------------------->|
| | Destroy resource records |
| 204 No Content status |<-----------------------------------------|
|<----------------------------------------| ' |
| | ' |
```

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