Using restinio::run

Simple forms of restinio::run

The simplest way of running RESTinio server is the usage of restinio::run functions. For example, to run single-threaded RESTinio server on the context of the current thread:

restinio::run(
  restinio::on_this_thread()
    .port(8080)
    .address("localhost")
    .request_handler([](auto req) {
      return req->create_response().set_body("Hello, World!").done();
    }));
// The current thread will be blocked until RESTinio server finishes its work.

To run multi-threaded RESTinio server on the context of thread pool:

restinio::run(
  restinio::on_thread_pool(16) // Thread pool size is 16 threads.
    .port(8080)
    .address("localhost")
    .request_handler([](auto req) {
      return req->create_response().set_body("Hello, World!").done();
    }));
// The current thread will be blocked until RESTinio server finishes its work.

Note that run() doesn’t provide a way to stop the server from outside of run(). It means that run() will block the current thread until the server will finish its work by itself. However inside run() a signal handler for SIGINT it installed and the server finishes its work when SIGINT is sent to the application (for example if the user breaks the application by pressing Ctrl+C/Ctrl+Break). Such approach seems to be appropriate for very simple servers like small test programs or quick-and-dirty prototypes. If you need more control you should use restinio::run_async function or restinio::http_server_t class.

A user-defined traits for RESTinio server can be passed as template parameters for restinio::on_this_thread and restinio::on_thread_pool helpers. For example:

using my_traits_t = restinio::traits_t<
    restinio::asio_timer_manager_t,
    restinio::single_threaded_ostream_logger_t,
    restinio::router::express_router_t >;
restinio::run(
  restinio::on_this_thread<my_traits_t>()
    .port(8080)
    .address("localhost")
    .request_handler([](auto req) {
      return req->create_response().set_body("Hello, World!").done();
    }));
// The current thread will be blocked until RESTinio server finishes its work.

Version of restinio::run for an existing http_server_t object

Since v.0.5.1 there is another version of restinio::run function that can be used for an existing http_server_t object. This form can be used in case when an access to http_server_t is necessary from some worker thread. For example:

// Create server instance on the main thread.
using my_server_t = restinio::http_server_t< my_server_traits_t >;
my_server_t server{
   restinio::own_io_context(),
   [](auto & settings) {
      settings.port(...);
      settings.address(...);
      settings.request_handler(...);
      ...
   }
};
// Create a separate thread to control execution of RESTinio's server.
std::thread restinio_control_thread{ [&server] {
      // Use restinio::run to launch RESTinio's server.
      // This run() will return only if server stopped from
      // some other thread.
      restinio::run( restinio::on_thread_pool(
            4, // Count of worker threads for RESTinio.
            restinio::skip_break_signal_handling(), // Don't react to Ctrl+C.
            server) // Server to be run.
         );
   }
};
... // Some application-dependent logic there. Maybe creation of
   // some GUI windows.

// Now RESTinio can be stopped.
restinio::initiate_shutdown(server);
// Wait for completeness of RESTinio's shutdown.
restinio_control_thread.join();

NOTE. There is only restinio::run(restinio::on_thread_pool()) form for running RESTinio with already existing http_server_t instance. If you need some other form of restinio::run please open an issue on GitHub.

run_async function

A helper function restinio::run_async was introduced in v.0.6.7. It allows to run an instance of RESTinio’s server on a separate thread or thread pool. For example:

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

The run_async returns control when HTTP-server is started or some startup failure is detected. But if a failure is detected then an exception is thrown. So if run_async returns successfuly then HTTP-server is working.

The run_async function returns an instance of restinio::running_service_instance_t<Http_Server> class that can be used for controlling a work of started HTTP-server:

auto server = restinio::run_async(...);

... // Some application specific logic.

// Initiate a shutdown of HTTP-server.
server.stop();

... // Some other application specific actions.

// Wait until HTTP-server finishes its work.
server.wait();

Please note that there is no need to call stop() and wait() methods manually, they will automatically be called by the destructor of running_service_instance_t.

A server to be run asynchronously can be parametrized by custom user-defined traits:

struct my_traits : public restinio::default_traits_t {
   ...
};

auto server = restinio::run_async<my_traits>(
   restinio::own_io_context(),
   restinio::server_settings_t<my_traits>{}
      .address(...)
      .port(...)
      .request_handler(...),
   16u);

NOTE. The function run_async doesn’t install handlers for Ctrl+C/SIGINT signals.

A helper class on_pool_runner_t

Sometimes it is necessary to run http_server_t on a thread pool but without the creation of separate control thread like in the example shown above. In that case a helper class on_pool_runner_t can be used:

// HTTP-server to be run on a thread pool.
restinio::http_server_t< my_traits > server{...};

// Launch HTTP-server on a thread pool.
restinio::on_pool_runner_t< restinio::http_server_t<my_traits> > runner{
      16,
      server
};
runner.start();

... // Some application specific code here.

// Now the server can be stopped.
runner.stop(); // (1)
runner.wait();

NOTE. A manual call to stop() and wait() methods as shown in the point (1) is not necessary: the destructor of on_pool_runner_t will call them automatically if the HTTP-server is not stopped yet.

There are two versions of on_pool_runner_t::start method.

The simplest one is shown above. It doesn’t receive any parameters. It is intended to be used for very simple cases where you don’t care about any errors related to launching of a new server.

There is also a more complicated version of start() method that accepts two callbacks (like http_server_t::open_async()). That method should be used if a user wants to get more controll:

// HTTP-server to be run on a thread pool.
restinio::http_server_t< my_traits > server{...};

// Launch HTTP-server on a thread pool.
restinio::on_pool_runner_t< restinio::http_server_t<my_traits> > runner{
      16,
      server
};

// Use std::promise object for acquiring the result of launching
// a new server.
std::promise<void> run_promise;
runner.start(
   // This callback will be called by RESTinio on successful start.
   [&run_promise]() noexcept {
      // Set a signal about the completion of start procedure.
      run_promise.set_value();
   },
   // This callback will be call by RESTinio in the case of an error.
   [&run_promise](std::exception_ptr ex) noexcept {
      // Store the exception and set a signal of the completion
      // of start procedure.
      run_promise.set_exception(std::move(ex));
   });
// Wait the result of server start.
// NOTE: std::future::get will throw in the case of error, because
// the second callback stored the exception into run_promise.
run_promise.get_future().get();

... // Some application specific code here.

NOTE. Class on_pool_runner_t doesn’t install handlers for Ctrl+C/SIGINT signals.

Running http_server manually

There can be cases where the usage of approaches described about is not enough. In such cases a user has to deal with http_server_t by his/herself.

To run server there are open()/close() methods :

class http_server_t
{
  // ...
  public:
    template <
        typename Server_Open_Ok_CB,
        typename Server_Open_Error_CB >
    void
    open_async(
      Server_Open_Ok_CB && open_ok_cb,
      Server_Open_Error_CB && open_err_cb )

    void
    open_sync();

    template <
        typename Server_Close_Ok_CB,
        typename Server_Close_Error_CB >
    void
    close_async(
      Server_Close_Ok_CB && close_ok_cb,
      Server_Close_Error_CB && close_err_cb );

    void
    close_sync();
    //...
}

There are sync methods for starting/stopping server and async. To choose the right method it is necessary to understand that RESTinio doesn’t start and run io_context that it runs on. So the user is responsible for running io_context. Sync versions of open()/close() methods assume they are called on the context of a running io_context. For example:

// Create and initialize object.
restinio::http_server_t< my_traits_t > server{
  restinio::own_io_context(),
  [&]( auto & settings ){
    //
    settings
      .port( args.port() )
      // .set_more_params( ... )
      .request_handler(
        []( restinio::request_handle_t req ){
            // Handle request.
        } );
  } };

// Post initial action to asio event loop.
asio::post( server.io_context(),
  [&] {
    // Starting the server in a sync way.
    server.open_sync();
  } );

// Running server.
server.io_context().run();

Async versions of open()/close() methods can be used from any thread. But it is not guaranteed that server is already started when method finishes. When using open_async() user provides two callbacks, the first one is called if the server starts successfully, and the second one is for handling an error. For example:

asio::io_context io_ctx;
restinio::http_server_t< my_traits_t > server{
    restinio::external_io_context(io_ctx),
    [&]( auto & settings ) { ... } };

// Launch thread on which server will work.
std::thread server_thread{ [&] {
    io_ctx.run();
  } };

// Start server in async way. Actual start will be performed
// on the context of server_thread.
server.open_async(
    // Ok callback. Nothing to do.
    []{},
    // Error callback. Rethrow an exception.
    []( auto ex_ptr ) {
      std::rethrow_exception( ex_ptr );
    } );
...
// Wait while server_thread finishes its work.
server_thread.join();

Refer to restinio/http_server.hpp for details.