Stiffstream / restinio

Cross-platform, efficient, customizable, and robust asynchronous HTTP(S)/WebSocket server C++ library with the right balance between performance and ease of use
Other
1.15k stars 93 forks source link

Handling requests using long living objects with Express Router #75

Closed guteksan closed 4 years ago

guteksan commented 4 years ago

In my project, for each endpoint I would like to have an object of, say, Endpoint_Handler, which lives for the service lifetime and handles incoming requests for this endpoint. I have a problem applying this using Express Router. Have a look at the code:

auto server_handler()
{
    auto router = std::make_unique<router_t>();

    router->http_get("/endpoint1", [](auto req, auto param) {
            // work on response using Endpoint1_Handler outliving lambda 
            return req->create_response() ...
        });
     router->http_get("/endpoint2", [](auto req, auto param) {
            // work on response using Endpoint2_Handler outliving lambda
            return req->create_response() ...
        });
    return router;
}
...

restinio::run(
    restinio::on_this_thread<traits_t>()
    .port(2001)
    .address("0.0.0.0")
    .request_handler(server_handler())
);

I would like to avoid creating Endpoint_Handler each time the request has come, but using lambda allows me to use only objects from the lambda scope. Normally, it could be solved by passing external objects as lambda captures. The problem is, that with this design pattern it can't be guaranteed that the captured object lives as long as the router. Apparently, I can't also extend router to contain some long living objects that can be used for handling requests. And of course, I would like to avoid using global objects.

Could you please suggest a solution, how this problem could be resolved with restinio, preferably with the Express Router?

eao197 commented 4 years ago

Hi!

I don't fully understand your issue. Why you can't just do something like that:

auto server_handler(Endpoint1_Handler & h1, Endpoint2_Handler &h2)
{
    auto router = std::make_unique<router_t>();

    router->http_get("/endpoint1", [&h1](auto req, auto param) {
            // work on response using h1 outliving lambda 
            return req->create_response() ...
        });
     router->http_get("/endpoint2", [&h2](auto req, auto param) {
            // work on response using h2 outliving lambda
            return req->create_response() ...
        });
    return router;
}
...
Endpoint1_Handler h1;
Endpoint2_Handler h2;
restinio::run(
    restinio::on_this_thread<traits_t>()
    .port(2001)
    .address("0.0.0.0")
    .request_handler(server_handler(h1, h2))
);

?

guteksan commented 4 years ago

Yes, this solution has also occured to me, but I thought that maybe there is a better solution, when Express Router and Endpoint Handlers have bound lifetime. I was curious what is the intended way of solving such issues in Restinio. Thank you for suggesting this and your prompt response.

eao197 commented 4 years ago

At the moment RESTinio has no way to bind some resource lifetime to the lifetime of RESTinio's service. But if you want some lifetime bounds you can consider the usage of std::shared_ptr:

auto server_handler(
  const std::shared_ptr<Endpoint1_Handler> & h1,
  const std::shared_ptr<Endpoint2_Handler> & h2)
{
    auto router = std::make_unique<router_t>();

    // NOTE: h1 is captured by value.
    router->http_get("/endpoint1", [h1](auto req, auto param) {
            // work on response using h1 outliving lambda 
            return req->create_response() ...
        });
     // NOTE: h2 is captured by value.
     router->http_get("/endpoint2", [h2](auto req, auto param) {
            // work on response using h2 outliving lambda
            return req->create_response() ...
        });
    return router;
}
...
auto h1 = std::make_shared<Endpoint1_Handler>(...);
auto h2 = std::make_shared<Endpoint2_Handler>(...);
restinio::run(
    restinio::on_this_thread<traits_t>()
    .port(2001)
    .address("0.0.0.0")
    .request_handler(server_handler(h1, h2))
);

In that case you will have the guarantee that h1 and h2 will live while your request handlers live.

guteksan commented 4 years ago

Thank you