Skip to content

Commit 7f96a06

Browse files
committed
Fix websocket client lifetime races during broadcast
1 parent 5e902c7 commit 7f96a06

1 file changed

Lines changed: 27 additions & 23 deletions

File tree

src/AsyncWebSocket.cpp

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,6 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) {
358358

359359
void AsyncWebSocketClient::_onPoll() {
360360
asyncsrv::unique_lock_type lock(_lock);
361-
362361
if (!_client) {
363362
return;
364363
}
@@ -446,7 +445,6 @@ bool AsyncWebSocketClient::canSend() const {
446445

447446
bool AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t *data, size_t len, bool mask) {
448447
asyncsrv::lock_guard_type lock(_lock);
449-
450448
if (!_client) {
451449
return false;
452450
}
@@ -463,7 +461,6 @@ bool AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t *data, si
463461

464462
bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) {
465463
asyncsrv::unique_lock_type lock(_lock);
466-
467464
if (!_client || !buffer || buffer->empty() || _status != WS_CONNECTED) {
468465
return false;
469466
}
@@ -952,7 +949,6 @@ bool AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len) {
952949

953950
IPAddress AsyncWebSocketClient::remoteIP() const {
954951
asyncsrv::lock_guard_type lock(_lock);
955-
956952
if (!_client) {
957953
return IPAddress((uint32_t)0U);
958954
}
@@ -962,7 +958,6 @@ IPAddress AsyncWebSocketClient::remoteIP() const {
962958

963959
uint16_t AsyncWebSocketClient::remotePort() const {
964960
asyncsrv::lock_guard_type lock(_lock);
965-
966961
if (!_client) {
967962
return 0;
968963
}
@@ -991,14 +986,10 @@ AsyncWebSocketClient *AsyncWebSocket::_newClient(AsyncWebServerRequest *request)
991986
}
992987

993988
void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient *client) {
994-
asyncsrv::lock_guard_type lock(_lock);
995-
const auto client_id = client->id();
996-
const auto iter = std::find_if(std::begin(_clients), std::end(_clients), [client_id](const AsyncWebSocketClient &c) {
997-
return c.id() == client_id;
998-
});
999-
if (iter != std::end(_clients)) {
1000-
_clients.erase(iter);
1001-
}
989+
(void)client;
990+
// Defer removal to cleanupClients(). Disconnect callbacks can fire while
991+
// iterating _clients for broadcast sends, and erasing here invalidates the
992+
// active iterator in the caller.
1002993
}
1003994

1004995
bool AsyncWebSocket::availableForWriteAll() {
@@ -1055,17 +1046,30 @@ void AsyncWebSocket::closeAll(uint16_t code, const char *message) {
10551046
}
10561047

10571048
void AsyncWebSocket::cleanupClients(uint16_t maxClients) {
1058-
asyncsrv::lock_guard_type lock(_lock);
1059-
const size_t c = count();
1060-
if (c > maxClients) {
1061-
async_ws_log_v("[%s] CLEANUP %" PRIu32 " (%u/%" PRIu16 ")", _url.c_str(), _clients.front().id(), c, maxClients);
1062-
_clients.front().close();
1063-
}
1049+
std::list<AsyncWebSocketClient> removed_clients;
1050+
{
1051+
asyncsrv::lock_guard_type lock(_lock);
1052+
const size_t connected = std::count_if(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient &c) {
1053+
return c.status() == WS_CONNECTED;
1054+
});
1055+
1056+
if (connected > maxClients) {
1057+
const auto connected_iter = std::find_if(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient &c) {
1058+
return c.status() == WS_CONNECTED;
1059+
});
1060+
if (connected_iter != std::end(_clients)) {
1061+
async_ws_log_v("[%s] CLEANUP %" PRIu32 " (%u/%" PRIu16 ")", _url.c_str(), connected_iter->id(), connected, maxClients);
1062+
connected_iter->close();
1063+
}
1064+
}
10641065

1065-
for (auto i = _clients.begin(); i != _clients.end(); ++i) {
1066-
if (i->shouldBeDeleted()) {
1067-
_clients.erase(i);
1068-
break;
1066+
for (auto iter = _clients.begin(); iter != _clients.end();) {
1067+
if (iter->shouldBeDeleted()) {
1068+
auto current = iter++;
1069+
removed_clients.splice(removed_clients.end(), _clients, current);
1070+
} else {
1071+
++iter;
1072+
}
10691073
}
10701074
}
10711075
}

0 commit comments

Comments
 (0)