RESTinio Documentation

repository·master·Indexed 23 days ago

https://github.com/stiffstream/restinio

A high-performance, asynchronous HTTP/1.1 server framework for C++. RESTinio features Express-style routing, middleware, and built-in support for HTTP transformations such as zlib compression. It supports advanced request handling patterns, including asynchronous chained handlers via fixed_size_chain_t, integration with SObjectizer for offloading processing to worker threads, and Basic Authentication credential extraction using http_field_parsers.

Tokens
24.6K
Snippets
50
Records
79
Agent score
78%

What's inside RESTinio

  1. Generate client certificates (Alice/Bob)

    master

    To simulate different clients (e.g., Alice and Bob) for TLS testing, generate a key, a request, and a signed certificate for each.

    For Alice:

    openssl genrsa -out alice.key 2048
    openssl req -new -key alice.key -out alice.req
    openssl x509 -req -in alice.req -CA ca.cer -CAkey ca.key -set_serial 101 -extensions client -days 3650 -outform PEM -out alice.cer

    For Bob:

    openssl genrsa -out bob.key 2048
    openssl req -new -key bob.key -out bob.req
    openssl x509 -req -in bob.req -CA ca.cer -CAkey ca.key -set_serial 101 -extensions client -days 3650 -outform PEM -out bob.cer
    # Alice
    openssl genrsa -out alice.key 2048
    openssl req -new -key alice.key -out alice.req
    openssl x509 -req -in alice.req -CA ca.cer -CAkey ca.key -set_serial 101 -extensions client -days 3650 -outform PEM -out alice.cer
    
    # Bob
    openssl genrsa -out bob.key 2048
    openssl req -new -key bob.key -out bob.req
    openssl x509 -req -in bob.req -CA ca.cer -CAkey ca.key -set_serial 101 -extensions client -days 3650 -outform PEM -out bob.cer
  2. Generate a server certificate and key

    master

    Follow these steps to create a signed server certificate:

    1. Generate the server key:
    openssl genrsa -out server.key 2048
    1. Generate a certificate signing request (CSR):
    openssl req -new -key server.key -out server.req -sha256
    1. Sign the request with your CA:
    openssl x509 -req -in server.req -CA ca.cer -CAkey ca.key -set_serial 100 -extensions server -days 3650 -outform PEM -out server.cer -sha256
    1. Cleanup:
    rm server.req
    openssl genrsa -out server.key 2048
    openssl req -new -key server.key -out server.req -sha256
    openssl x509 -req -in server.req -CA ca.cer -CAkey ca.key -set_serial 100 -extensions server -days 3650 -outform PEM -out server.cer -sha256
    rm server.req
  3. Manage WebSocket connection lifecycle

    master

    WebSocket connections in RESTinio can be managed through the rws::ws_handle_t:

    • Closing a connection: To gracefully close a connection after receiving a close frame, use wsh->send_message(*m) followed by wsh->shutdown().
    • Killing a connection: To abruptly terminate a connection (e.g., if a client is deemed dead due to inactivity), use wsh->kill().
    • Connection ID: Use wsh->connection_id() to get a unique identifier for the connection, which is useful for tracking connections in a registry.

    Important: Because WebSocket handles are often managed via shared pointers in user-defined registries, ensure that you remove the handler from your registry when the connection is closed or killed to allow the handler to be destroyed.

    // Graceful shutdown
    wsh->send_message( *m );
    wsh->shutdown();
    
    // Abrupt kill
    wsh->kill();
  4. Attach custom data to requests using extra data factories

    master

    RESTinio allows you to attach custom, per-request data structures to every incoming request. This is useful for storing authentication results, user identities, or session information that needs to be accessed by various handlers in a request processing chain.

    To implement this:

    1. Define your custom data structure (e.g., per_request_data_t).
    2. Use restinio::simple_extra_data_factory_t<T> to create a factory for your type.
    3. Register the factory in your server_traits_t using the extra_data_factory_t alias.
    4. Access the data in any handler using req->extra_data().

    If you are using the Express router, you must use restinio::router::generic_express_router_t and provide your extra data factory type to ensure the router is compatible with the extra data.

  5. Use the easy_parser_router for RESTful routing

    master

    The easy_parser_router_t (found in restinio::router) provides a high-level way to define routes that automatically parse path parameters into specific types. It uses a declarative syntax to map HTTP methods and paths to handler functions.

    Key features include:

    • Path Parameter Parsing: Convert segments of a URL directly into C++ types (e.g., integers, strings).
    • Parameter Composition: Use operators like >> to chain parsers (e.g., parsing a fragment and then unescaping it).
    • Method Constraints: Use none_of_methods to explicitly handle or reject specific HTTP methods for a given path.
    • Flexible Handlers: Supports handlers that take either just the request_handle_t or the request handle plus the parsed parameters.
  6. Implement HTTP compression using restinio::transforms::zlib

    master

    RESTinio provides a zlib transform facility to handle HTTP content encoding (gzip, deflate, or identity). To use it, you create a body appender using restinio::transforms::zlib::body_appender, which wraps a response object and applies the selected compression algorithm to the data being appended.

    Key components:

    • rtz::make_gzip_compress_params(level): Creates parameters for gzip compression.
    • rtz::make_deflate_compress_params(level): Creates parameters for deflate compression.
    • rtz::make_identity_params(): Creates parameters for no compression (identity).
    • rtz::body_appender(response, params): Returns an appender object used to write data to the response.
    • ba.append(data): Appends a single block of data.
    • ba.make_chunk(data) and ba.flush(): Used with restinio::chunked_output_t to stream data in chunks.
    • ba.complete(): Finalizes the transformation and the response body.
  7. Chain multiple request handlers using restinio::run

    master

    You can chain multiple request handlers together by passing them as multiple arguments to the .request_handler() method when configuring the server. This allows you to implement a middleware-like pattern where one handler can decide whether to process a request or delegate it to the next handler in the chain.

    To delegate to the next handler, a handler must return restinio::request_not_handled(). If a handler processes the request and wants to stop the chain, it should return restinio::request_accepted().

    In the example below, the first handler (create_auth_handler) checks for authentication. If authentication fails, it returns a 401 Unauthorized response and restinio::request_accepted(). If authentication succeeds, it returns restinio::request_not_handled(), allowing the second handler (create_request_handler) to take over.

    restinio::run(
        restinio::on_this_thread<traits_t>()
            .port( 8080 )
            .address( "localhost" )
            .request_handler(
                create_auth_handler(),
                create_request_handler() )
    );
  8. Configure and run a RESTinio HTTP server

    master

    To run a RESTinio server, you must define server traits, instantiate the server with settings, and then use a runner to manage the execution lifecycle.

    1. Define Traits: Create a struct inheriting from restinio::default_traits_t. You can specify a logger_t (e.g., restinio::shared_ostream_logger_t).
    2. Instantiate Server: Use restinio::http_server_t< my_server_traits_t >. The constructor takes an io_context (e.g., restinio::own_io_context()) and a restinio::server_settings_t< my_server_traits_t > object.
    3. Configure Settings: Use the fluent interface on server_settings_t to set the .port(), .address(), and .request_handler().
    4. Run with a Pool Runner: To run the server on a thread pool, use restinio::on_pool_runner_t< server_t >. Pass the number of threads (e.g., std::thread::hardware_concurrency()) and the server instance to the runner, then call .start().
    5. Stop the Server: The server can be stopped manually by calling runner.stop() and then runner.wait() to ensure a clean shutdown.
    struct my_server_traits_t : public restinio::default_traits_t
    {
    	using logger_t = restinio::shared_ostream_logger_t;
    };
    
    using server_t = restinio::http_server_t< my_server_traits_t %>;
    
    server_t server{
    		restinio::own_io_context(),
    		restinio::server_settings_t< my_server_traits_t >{}
    			.port( 8080 )
    			.address( "localhost" )
    			.request_handler( handler )
    };
    
    // Run server on a separate thread_pool.
    restinio::on_pool_runner_t< server_t > runner{
    		std::thread::hardware_concurrency(),
    		server
    };
    runner.start();
    
    // To stop:
    // runner.stop();
    // runner.wait();
  9. Configure TLS in RESTinio

    master

    To enable TLS, you must provide an SSL context (compatible with asio::ssl::context) and register it with the server configuration.

    1. Create an asio_ns::ssl::context (where asio_ns is restinio::asio_ns).
    2. Configure certificates, private keys, and verification modes (e.g., restinio::asio::ssl::verify_peer).
    3. Pass the context to the server using .tls_context(std::move(tls_context)).

    Example configuration:

    namespace asio_ns = restinio::asio_ns;
    auto tls_context = asio_ns::ssl::context{ asio_ns::ssl::context::sslv23 };
    tls_context.use_certificate_chain_file("server.cer");
    tls_context.use_private_key_file("server.key", asio_ns::ssl::context::pem);
    tls_context.set_verify_mode(asio_ns::ssl::verify_peer | asio_ns::ssl::verify_fail_if_no_peer_cert);
    asio_ns::ssl::context tls_context{ asio_ns::ssl::context::sslv23 };
    tls_context.set_options(
        asio_ns::ssl::context::default_workarounds
        | asio_ns::ssl::context::no_sslv2
        | asio_ns::ssl::context::single_dh_use );
    
    tls_context.use_certificate_chain_file( certs_dir + "/server.cer" );
    tls_context.use_private_key_file(
        certs_dir + "/server.key",
        asio_ns::ssl::context::pem );
    tls_context.set_verify_mode(
        asio_ns::ssl::verify_peer
        | asio_ns::ssl::verify_fail_if_no_peer_cert );
    tls_context.load_verify_file( certs_dir + "/ca.cer" );
    tls_context.use_tmp_dh_file( certs_dir + "/dh2048.pem" );