Cross-platform, efficient, customizable, and robust asynchronous HTTP(S)/WebSocket server C++ library with the right balance between performance and ease of use
OTHER License
Bot releases are visible (Hide)
It's a maintenance release. If fixes several issues related to RESTinio's dependencies:
NOTE! If you have no problem with RESTinio-0.7.1 there is no need to update to v.0.7.2. An update may be necessary if you manage RESTinio's dependencies by yorself.
This is the first significant release since v.0.6.13 and there are several breaking changes.
expected
class RESTinio still needs the expected-lite project as a dependency.--address
is required then it can’t be specified as --addr
.RESTINIO_FIND_DEPS
) have been removed. There are several new variables/options that have to be used since v0.7.0: RESTINIO_ASIO_SOURCE
, RESTINIO_DEP_STANDALONE_ASIO
, RESTINIO_DEP_BOOST_ASIO
, RESTINIO_DEP_LLHTTP
, RESTINIO_DEP_FMT
, RESTINIO_DEP_EXPECTED_LITE
and so on. Please see the corresponding section in the documentation for more details.restinio/all.hpp
header file has been deprecated. It will be removed in v.0.8.0. The new core restinio/core.hpp
header file must be used instead.restinio::on_pool_runner_t::stop
changed. Now it may accept on_error-callback. The default on_error-callback calls std::abort
if an exception is thrown in http_server_t::close_async
.Since v.0.6.17 RESTinio can be used with fmtlib in FMT_ENFORCE_COMPILE_STRING
mode. In this mode FMT_STRING
macro has to be used for format string in C++11/14/17 for compile-time checking of format string validity.
Now RESTinio checks the presence of FMT_ENFORCE_COMPILE_STRING
and uses FMT_STRING
when it's required.
NOTE. FMT_ENFORCE_COMPILE_STRING
has to be defined before inclusion of any of RESTinio's headers.
fmtlib-9.1.0 in now used inside RESTinio (and included in restinio-0.6.17-full.* archives).
This release adapts RESTinio to fmtlib-9.0.0.
Since v.0.6.16 RESTinio can be used with fmtlib-8 or fmtlib-9, but fmtlib-8 is still used in RESTinio development.
NOTE: restinio-0.6.14-full.* archives contain asio-1-21-1, fmt-8.1.1 and catch-2.13.9. Versions of 3rd party libraries in restinio/third_party updated to the latest versions:
expected-lite at commit 34524d46e538f1e6ed114d8f8409f7cd173d96e6.
optional-lite at commit ea502a6472c85dbc0174764df218a0c3443c6772.
string-view-lite at commit f7aca36f5caa05e451f6887aa707df89197e6de6.
variant-lite at commit f1af3518e4c28f12b09839b9d2ee37984cbf137a.
This is a maintenance release.
A fix for CMake script for case when RESTINIO_FIND_DEPS=ON
and RESTINIO_FMT_HEADER_ONLY=OFF
(#159).
Usage of std::aligned_storage_t
in implementation of writable_item_t
has been removed (it's deprecated in C++23).
In some places std::launder
is used (if available) to avoid UB accessing a pointer after placement new.
NOTE: restinio-0.6.14-full.* archives contain asio-1-21-1, fmt-8.1.1 and catch-2.13.9. Versions of 3rd party libraries in restinio/third_party
weren't upgraded.
This version introduces a couple of new big features and contains several changes some of that can affect existing code.
A new value not_handled
added to request_handling_status_t
enumeration (+ new helper function request_not_handled()
). This value should be returned if a request-handler doesn't accept nor reject the current request and some other request-handler should be tried (if such a handler exists). If all request-handlers called for request processing return not_handled
then RESTinio treats that value as rejected
.
It's possible now to incorporate some additional (extra) data into a request-object. To do so a user has to perform the following steps:
Define a type for holding such an extra data:
struct per_request_data {
user_indentity user_info_;
...
};
Define a extra-data-factory that should look like:
struct my_extra_data_factory {
using data_t = per_request_data;
void make_within(restinio::extra_data_buffer_t<data_t> buf) {
new(buf.get()) data_t{};
}
};
Or, if extra-data-factory is a simple and stateless object as shown above:
using my_extra_data_factory = restinio::simple_extra_data_factory_t<per_request_data>;
Specify my_extra_data_factory
in server's traits:
struct my_traits : public restinio::default_traits_t {
using extra_data_factory_t = my_extra_data_factory;
};
Change the format of request-handlers this way:
restinio::request_handling_status_t request_handler(
const restinio::generic_request_handle_t<per_request_data> & req);
// or
restinio::request_handling_status_t request_handler(
const restinio::generic_request_handle_t<my_extra_data_factory::data_t> & req);
Extra-data is available inside a request-handler via new method request_t::extra_data
:
restinio::request_handling_status_t request_handler(
const restinio::generic_request_handle_t<per_request_data> & req)
{
auto & ed = req->extra_data();
...
}
See the documentation for more details.
It's possible now to bind several synchronous request-handlers into a chain (somewhat similar to ExpressJS's middleware):
#include <restinio/all.hpp>
#include <restinio/sync_chain/fixed_size.hpp>
auto incoming_req_logger(const restinio::request_handle_t & req)
{
... // Perform logging.
// Allow the passing of the request to the next handler.
return restinio::request_not_handled();
}
auto mandatory_fields_checker(const restinio::request_handle_t & req)
{
... // Perform all necessary checks.
if(!ok) {
// Negative response has to be sent.
return req->create_response(restinio::status_bad_request())
...
.done();
}
// Allow the passing of the request to the next handler.
return restinio::request_not_handled();
}
auto permissions_checker(const restinio::request_handle_t & req)
{
... // Check user credentials and permissions.
if(!ok) {
// Negative response has to be sent.
return req->create_response(restinio::status_unauthorized())
...
.done();
}
// Allow the passing of the request to the next handler.
return restinio::request_not_handled();
}
auto actual_processor(const restinio::request_handle_t & req)
{
... // Do actual request processing.
return restinio::request_accepted();
}
struct my_traits : public restinio::default_traits_t {
// Change the type of request handler.
using request_handler_t = restinio::sync_chain::fixed_size_chain_t<4>;
};
restinio::run(restinio::on_this_thread<my_traits>()
.port(...)
.address(...)
.request_handler(
// Enumerate all handlers in the chain in the order of invocation.
incoming_req_logger,
mandatory_fields_checker,
permissions_checker,
actual_processor)
...
);
When a new incoming request will be parsed the RESTinio will call incoming_req_logger
, then mandatory_req_logger
, then (if necessary) permissions_checker
, and only then (if necessary) actual_processor
.
See the documentation for more details.
A new method incoming_http_msg_limits
added to restinio::server_settings_t
. This method allows to set up limits for the maximum length of various parts of an incoming HTTP message (like URL, HTTP-field’s name and value):
struct my_traits : public restinio::default_traits_t { ... };
restinio::server_settings_t<my_traits> settings;
settings.incoming_http_msg_limits(
restinio::incoming_http_msg_limits_t{}
.max_url_size(8000u)
.max_field_name_size(2048u)
.max_field_value_size(4096u)
);
...
auto server = restinio::run_async(
restinio::own_io_context(),
std::move(settings),
std::thread::hardware_concurrency());
A possibility to limit the number of parallel connection has been added:
struct my_traits : public restinio::default_traits_t {
// Force the usage of connection count limiter.
static constexpr bool use_connection_count_limiter = true;
};
restinio::server_settings_t<my_traits> settings;
settings.max_parallel_connections(1000u);
...
auto server = restinio::run_async(
restinio::own_io_context(),
std::move(settings),
std::thread::hardware_concurrency());
A support for SObjectizer 5.6/5.7 has been added. Now RESTinio can be user either with SObjectizer 5.5 and SObjectizer 5.6/5.7. The version of SObjectizer is detected automatically. But if a user wants to use SObjectizer 5.6/5.7 he/she should set C++ standard to C++17 manually.
Conversion functions passed to restinio::easy_parser::convert
can now return expected_t<T, error_reason_t>
as well as just T
. Returning expected_t<T, error_reason_t>
allows to report conversion errors without throwing an exception. This is a fix for #99.
A new overload for restinio::server_settings_t::address()
method. The new overload accepts an instance of asio::ip::address
(or boost::asio::ip::address
). This is a fix for #100.
A new optional post-bind hook added. This hook is called just after a succesful return from bind()
for server’s acceptor. A reference to asio::ip::tcp::acceptor
is passed to that hook. This new hook can be used for application-specific tuning of bound acceptor or to gathering some information about the acceptor. This is a fix for #126. For example, this code snippet shows how RESTinio server can be started on a random port assigned by the Operating System:
std::promise<unsigned short> port_promise; // For getting the port.
auto server = restinio::run_async(
restinio::use_own_context(),
restinio::server_settings_t{}
.address("localhost")
.port(0u) // Zero means that port will be assigned by the OS.
.acceptor_post_bind_hook(
[&port_promise](asio::ip::tcp::acceptor & acceptor) {
// Gathering the actual port number.
port_promise.set_value(acceptor.local_endpoint().port());
})
.request_handler(...),
4u);
// Now we can safely get the actual port number from the promise.
const auto actual_port = port_promise.get_future().get();
A TLS-context object can be passed to server_settings_t as a shared pointer. This makes it possible to use one TLS-context by several instances of RESTinio server, or TLS-context can be shared between a RESTinio server and other parts of an application.
New example shared_tls_context added.
Now RESTinio works with Asio 1.17. Version 0.6.9 can be used with Asio 1.12, 1.14, 1.16, and 1.17.
Support for incoming requests with chunked encoding has been added. Previous versions of RESTinio didn’t support such requests, HTTP 501 error was returned. Since v.0.6.9 RESTinio accepts such requests and glues all chunks together into the one body. Information about an individual chunk is preserved and is available via request_t::chunked_input_info
.
Since v.0.6.9 the value OFF
for CMake-option RESTINIO_ALLOW_SOBJECTIZER
is handled differently: all tests/examples/benchmarks those require SObjectizer as a dependency won't be compiled. All other tests/examples will be compiled as usual. There is also a new CMake-option RESTINIO_USE_EXTERNAL_SOBJECTIZER
.
New methods for http_header_fields_t
class: remove_all_of
and add_field
.
New helpers for parsing the following HTTP-fields: Connection, Host, Transfer-Encoding.
New tools for easy_parser and HTTP-field parsers: expected_token_p
, expected_caseless_token_p
, symbol_from_range_p
, caseless_exact, caseless_exact_p
.
There are also some thoughts about the future development of RESTinio.
Implementation of extraction of Bearer authentification parameters fixed. Now it looks like:
#include <restinio/all.hpp>
#include <restinio/http_field_parser/bearer_auth.hpp>
...
auto on_request(const restinio::request_handle_t & req) {
using namespace restinio::http_field_parsers::bearer_auth;
const auto auth_params = try_extract_params(*req,
restinio::http_field::authorization);
if(auth_params) { // Parameters successfully extracted.
if(is_valid_user(auth_params->token)) {
...
}
}
...
}
New helper function try_parse_field
for simplification of HTTP-fields parsing added:
#include <restinio/all.hpp>
#include <restinio/helpers/http_fields_parsers/try_parse_field.hpp>
#include <restinio/helpers/http_fields_parsers/accept.hpp>
...
auto on_request(const restinio::request_handle_t & req) {
using namespace restinio::http_field_parsers;
// Try to get and parse the value of `Accept` header.
const auto parse_result = try_parse_field<accept_value_t>(
req, restinio::http_field::accept);
if(const auto * value =
restinio::get_if<accept_value_t>(&parse_result)) {
// Value of the field is successfully parsed.
... // Some usage of parsed value.
}
}
Several new overloads for try_extract_params
functions from restinio::http_field_parsers::basic_auth
and restinio::http_field_parsers::bearer_auth
namespaces. They allow to work with several authentication schemes after the parsing of Authorization
(Proxy-Authorization
) field:
auto on_request(const restinio::request_handle_t & req) {
using namespace restinio::http_field_parsers;
const auto field = try_parse_field<authorization_value_t>(
req, restinio::http_field::authorization);
if(const auto * auth = restinio::get_if<authorization_value_t>(field)) {
// We have valid Authorization field value.
if("basic" == auth->auth_scheme) {
// Basic authentification scheme should be used.
using namespace restinio::http_field_parsers::basic_auth;
const auto params = try_extract_params(auth->auth_params);
if(params) { // Parameters successfully extracted.
if(is_valid_user(params->username, params->password)) {
...
}
}
...
}
else if("bearer" == auth->auth_scheme) {
// Bearer authentification scheme should be used.
using namespace restinio::http_field_parsers::bearer_auth;
const auto params = try_extract_params(auth->auth_params);
if(auth_params) { // Parameters successfully extracted.
if(is_valid_user(auth_params->token)) {
...
}
}
...
}
else {
... // Handling of different schemes.
}
}
}
New helpers for extraction of parameters for Bearer authentication (thanks to @prince-chrismc):
#include <restinio/all.hpp>
#include <restinio/http_field_parser/bearer_auth.hpp>
...
auto on_request(const restinio::request_handle_t & req) {
using namespace restinio::http_field_parsers::bearer_auth;
const auto auth_params = try_extract_params(*req,
restinio::http_field::authorization);
if(auth_params) { // Parameters successfully extracted.
if(is_valid_user(auth_params->client_id, auth_params->client_secret)) {
...
}
}
...
}
New configuration options for CMake-based builds: RESTINIO_USE_EXTERNAL_EXPECTED_LITE
, RESTINIO_USE_EXTERNAL_OPTIONAL_LITE
, RESTINIO_USE_EXTERNAL_STRING_VIEW_LITE
, RESTINIO_USE_EXTERNAL_VARIANT_LITE
.
New helper function run_async
that allows to run an instance of RESTinio's server on a separate thread-pool or thread:
int main() {
auto server = restinio::run_async(
// Asio's io_context to be used.
// HTTP-server will use own Asio's io_context object.
restinio::own_io_context(),
// The settings for the HTTP-server.
restinio::server_settings_t{}
.address("127.0.0.1")
.port(8080)
.request_handler(...),
// The size of thread-pool for the HTTP-server.
16);
// If we are here and run_async doesn't throw then HTTP-server
// is started.
... // Some other actions.
// No need to stop HTTP-server manually. It will be automatically
// stopped in the destructor of `server` object.
}
New helpers for working with HTTP-fields like Authorization
and Proxy-Authorization
, and helpers for the extraction of parameters for Basic authentication:
#include <restinio/all.hpp>
#include <restinio/http_field_parser/basic_auth.hpp>
...
auto on_request(const restinio::request_handle_t & req) {
using namespace restinio::http_field_parsers::basic_auth;
const auto auth_params = try_extract_params(*req,
restinio::http_field::authorization);
if(auth_params) { // Parameters successfully extracted.
if(is_valid_user(auth_params->username, auth_params->password)) {
...
}
}
...
}
Some bug fixes.
An experimental type-safe request-router that can be used as a type-safe alternative of express-like router with additional compile-time checking. That new easy_parser_router allows to write:
namespace epr = restinio::router::easy_parser_router;
router->http_get(
epr::path_to_params("/api/v1/posts/",
epr::non_negative_decimal_number_p<std::uint64_t>(),
"/revisions/",
epr::non_negative_decimal_number_p<std::int16_t>()),
[](const auto & req, std::uint64_t post_id, std::int16_t rev_id) {...});
instead of:
router->http_get("/api/v1/posts/:post_id(\d{1,10})/revisions/:rev_id(\d{1,5})",
[](const auto & req, const auto & params) {
const auto post_id = restinio::cast_to<std::uint64_t>(params["post_id"]);
const auto rev_id = restinio::cast_to<std::int16_t>(params["rev_id"]);
});
More information about the new router can be found here.
An ability to specify a request handler for several HTTP-methods (see #82 for a motivation). It's possible now to write routes like:
router->add_handler(
restinio::router::any_of_methods(
restinio::http_method_lock(), restinio::http_method_unlock()),
"/api/v1/resources/:rid",
[](const auto & req, const auto & params) {...});
router->add_handler(
restinio::router::none_of_methods(
restinio::http_method_get(), restinio::http_method_post(), restinio::http_method_delete()),
"/api/v1/users/:user",
[](const auto & req, const auto & params) {...});
Those new method matchers can be used for express-like or easy_parser-based routers.
More information about method matchers is here.
New RESTINIO_FMT_HEADER_ONLY
CMake option added. It allows to use the compiled version of fmtlib with RESTinio. Thanks for @prince-chrismc for a patch.
Set of symbols supported by restinio::parse_query_traits::javascript_compatible
is extended (#76).
Addition of restinio::parse_query_traits::x_www_form_urlencoded
, restinio::parse_query_traits::relaxed
trais.
Introduction of try_parse_query
function.
Some functions that work with query-string and URI (like parse_query
, try_parse_query
) now do basic control of the validity of UTF-8 sequences represented as percent-encoded characters.
New RESTINIO_USE_EXTERNAL_HTTP_PARSER
option for CMake-based builds.