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.