I'm currently maintaining a C++ REST Server developed in C++. It provides some features like middleware and routes.
Routes are stored inside an inner structure of the router class:
//! The http router
//!
//! allow us to parse route on a server using regex to match the good route for a given url
//! and extract the possible url variables
class router {
private:
//! routes datas
//!
//! contains:
//! * the regex to parse the routes
//! * an std::vector with the list of variable inside the routes
//! * 4 std::function, one for each REST methods
struct rest_routes {
std::regex regex;
std::vector<std::string> vars_name;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> get;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> post;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> put;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> del;
};
};
Everything works well during the execution: routes can be configured and added to the router and if someone request the server on an existing route, callback are executed and the server send a response as expected.
Here is an example of a route configuration where we create a route /admin/cameras/:cam_id
for an HTTP DELETE
request:
// delete a camera
router.del("/admin/cameras/:cam_id",
std::bind(&admin_service::remove_camera, service, std::placeholders::_1));
In this example, admin_service::remove_camera
is a member function and service
is a shared_ptr
containing a pointer to an admin_service
object. If someone requests this route, admin_service::remove_camera
is called.
However, the server segfaults at the end of the execution (when we exit the server).
I have traced the origin of the segfault, and it comes from the destructor of... std::function
. To be more precise, it happened during the destruction of one of the std::function
contained in the std::pair
s get
, post
, put
and del
.
I can say this because, when I put the following debug code:
struct rest_routes {
~rest_routes() {
std::cout << "BEGIN DTOR rest_routes" << std::endl;
std::cout << "BEGIN get" << std::endl;
get.first = nullptr;
std::cout << "END get" << std::endl;
std::cout << "BEGIN post" << std::endl;
post.first = nullptr;
std::cout << "END post" << std::endl;
std::cout << "BEGIN put" << std::endl;
put.first = nullptr;
std::cout << "END put" << std::endl;
std::cout << "BEGIN del" << std::endl;
del.first = nullptr;
std::cout << "END del" << std::endl;
std::cout << "END DTOR rest_routes" << std::endl;
}
std::regex regex;
std::vector<std::string> vars_name;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> get;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> post;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> put;
std::pair<std::function<http::response(http::request&)>, std::vector<std::string>> del;
};
I got the following output:
BEGIN DTOR rest_routes
BEGIN get
END get
BEGIN post
END post
BEGIN put
END put
BEGIN del
Segmentation Fault
I can't figure how a std::function can segfault during destruction or assignment...
I first thought that, maybe, std::function
took a reference to the std::shared_ptr service
instead of taking it by value and deleted the raw pointer it contains more than once. But when I put some debug output, I can see that the shared_ptr
counter is incremented after the call to router.del
.
Does someone have some idea about this issue?