-
-
Notifications
You must be signed in to change notification settings - Fork 383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Close websockets when app is terminated #269
Conversation
terminated. This is incomplete and needs more work.
CI build killed because more work needs to be done anyway. |
Actually, This isn't a bad place for a bot like me |
Also fixed issue where enabling SSL prevented compilation.
--- include/crow/http_server.h (before formatting)
+++ include/crow/http_server.h (after formatting)
@@ -161,16 +161,16 @@
void stop()
{
- should_close_ = false; //Prevent the acceptor from taking new connections
+ should_close_ = false; //Prevent the acceptor from taking new connections
while (handler_->websocket_count.load(std::memory_order_release) != 0) //Wait for the websockets to close properly
{
}
- for(auto& io_service : io_service_pool_)
+ for (auto& io_service : io_service_pool_)
{
if (io_service != nullptr)
{
- CROW_LOG_INFO << "Closing IO service " << &io_service;
- io_service->stop(); //Close all io_services (and HTTP connections)
+ CROW_LOG_INFO << "Closing IO service " << &io_service;
+ io_service->stop(); //Close all io_services (and HTTP connections)
}
}
--- include/crow/websocket.h (before formatting)
+++ include/crow/websocket.h (after formatting)
@@ -106,7 +106,7 @@
signals_.clear();
- for (auto snum: handler_->signals())
+ for (auto snum : handler_->signals())
signals_.add(snum);
// Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
@@ -118,13 +118,14 @@
s.getDigestBytes(digest);
signals_.async_wait(
- [&](const boost::system::error_code& e, int /*signal_number*/){
- if (!e){
- CROW_LOG_INFO << "quitting " << this;
- do_not_destroy_ = true;
- close("Quitter");
- }
- });
+ [&](const boost::system::error_code& e, int /*signal_number*/) {
+ if (!e)
+ {
+ CROW_LOG_INFO << "quitting " << this;
+ do_not_destroy_ = true;
+ close("Quitter");
+ }
+ });
start(crow::utility::base64encode((unsigned char*)digest, 20));
}
@@ -657,14 +658,14 @@
bool is_close_handler_called_{false};
- // **WARNING**
- // SETTING THIS PREVENTS THE OBJECT FROM BEING DELETED,
- // AND WILL ABSOLUTELY CAUSE A MEMORY LEAK!!
- // ONLY USE IF THE APPLICATION IS BEING TERMINATED!!
- bool do_not_destroy_{false};
- // **WARNING**
-
- std::atomic<int>& websocket_count_;
+ // **WARNING**
+ // SETTING THIS PREVENTS THE OBJECT FROM BEING DELETED,
+ // AND WILL ABSOLUTELY CAUSE A MEMORY LEAK!!
+ // ONLY USE IF THE APPLICATION IS BEING TERMINATED!!
+ bool do_not_destroy_{false};
+ // **WARNING**
+
+ std::atomic<int>& websocket_count_;
std::function<void(crow::websocket::connection&)> open_handler_;
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_; |
This makes sense because the adaptor is moved to be a member of the websocket connection, thus deleting the connection would also delete the adaptor, which is most likely the reason behind the segfault. |
Adding |
--- include/crow/websocket.h (before formatting)
+++ include/crow/websocket.h (after formatting)
@@ -106,7 +106,7 @@
signals_.clear();
- for (auto snum: handler_->signals())
+ for (auto snum : handler_->signals())
signals_.add(snum);
// Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
@@ -118,12 +118,13 @@
s.getDigestBytes(digest);
signals_.async_wait(
- [&](const boost::system::error_code& e, int /*signal_number*/){
- if (!e){
- CROW_LOG_INFO << "Quitting Websocket: " << this;
- close("Server Application Terminated");
- }
- });
+ [&](const boost::system::error_code& e, int /*signal_number*/) {
+ if (!e)
+ {
+ CROW_LOG_INFO << "Quitting Websocket: " << this;
+ close("Server Application Terminated");
+ }
+ });
start(crow::utility::base64encode((unsigned char*)digest, 20));
}
|
@mrozigor @luca-schlecker I believe this PR is ready to be merged, Could you please take a look at it? Thanks. |
As mentioned in #34. Crow doesn't properly close websocket connections when terminated, instead closing the socket directly. Which could cause a problem with websocket clients if they're not configured to handle unclean closures.
This Draft PR blocks the main thread until all websocket connections have been cleanly closed (which closes #34).
It does so by keeping track of the number of currently open websockets, which is incremented up once the connection object is created, and decremented whenever it is destroyed. The
SIGINT
orSIGTERM
is sent to both the connection and the server. the server'sdo_accept
method is halted and the thread is then blocked until thewebsocket_count
reaches0
, the connections just call theclose()
method, which automatically decrements the count once complete.There are a few things I'm not comfortable with in the PR:
The fact that I'm sending the websocket count from the App through the router'shandle_upgrade
method (which makes little sense) and to the websocket connection.Continuing the subject of sending metadata, I've (for now) hardcoded the signals into thewebsocket::connection
constructor, which makesCrow::signal_clear()
andCrow::signal_add()
methods completely useless, since they don't affect the websocket connections.io_service.stop()
on the related IO Context causes aSEGFAULT
. (More details below)The fourth problem is no big deal. The other 3 however I do need help in.
To deal with numbers 1 & 2 my Idea is to create a new class named eithercrow::connection_context
orcrow::metadata
which would contain the websocket count, signals, and potentially other data that the App needs to send to connections, adaptors, etc..I didn't work on it because I need other opinions since this is an architectural change.
The third issue is quite a bit more complicated. Here's the text I wrote (on Gitter) while looking into it:
I've already worked around the issue using the
should_close_
boolean. Which prevents the websocket object from being deleted after being closed. I particularly don't like this because A. it ignores the base issue that something that doesn't exist is being referenced. and B. It potentially causes a memory leak since there's no actual reference to the websocket connection:Here's the code responsible for creating websocket connections (notice how it is created in the heap and left to deal with itself)
Crow/include/crow/routing.h
Lines 403 to 406 in b5137c5