Improve error handling and null checks in WebSocket server callbacks
- Add null checks for user data in various WebSocket callback functions to prevent crashes and improve stability. - Enhance error logging to provide clearer insights into issues related to user data and connection management. - Refactor the handling of active connections to ensure robust error handling during data processing and message sending.
This commit is contained in:
committed by
Torsten (PC)
parent
dafdbf0a84
commit
9c7b682a36
@@ -237,8 +237,13 @@ int WebSocketServer::wsCallback(struct lws *wsi,
|
|||||||
if (!instance) return 0;
|
if (!instance) return 0;
|
||||||
|
|
||||||
auto *ud = reinterpret_cast<WebSocketUserData*>(user);
|
auto *ud = reinterpret_cast<WebSocketUserData*>(user);
|
||||||
|
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case LWS_CALLBACK_ESTABLISHED: {
|
case LWS_CALLBACK_ESTABLISHED: {
|
||||||
|
if (!ud) {
|
||||||
|
std::cerr << "[ESTABLISHED] ud ist nullptr" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
ud->pongReceived = true;
|
ud->pongReceived = true;
|
||||||
ud->connectionTime = std::chrono::steady_clock::now();
|
ud->connectionTime = std::chrono::steady_clock::now();
|
||||||
ud->lastPingTime = std::chrono::steady_clock::now();
|
ud->lastPingTime = std::chrono::steady_clock::now();
|
||||||
@@ -260,6 +265,10 @@ int WebSocketServer::wsCallback(struct lws *wsi,
|
|||||||
}
|
}
|
||||||
case LWS_CALLBACK_RECEIVE_PONG:
|
case LWS_CALLBACK_RECEIVE_PONG:
|
||||||
// WebSocket Pong-Frame empfangen (automatische Antwort auf Ping)
|
// WebSocket Pong-Frame empfangen (automatische Antwort auf Ping)
|
||||||
|
if (!ud) {
|
||||||
|
std::cerr << "[RECEIVE_PONG] ud ist nullptr" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
ud->pongReceived = true;
|
ud->pongReceived = true;
|
||||||
ud->lastPongTime = std::chrono::steady_clock::now();
|
ud->lastPongTime = std::chrono::steady_clock::now();
|
||||||
ud->pingTimeoutCount = 0;
|
ud->pingTimeoutCount = 0;
|
||||||
@@ -306,38 +315,57 @@ int WebSocketServer::wsCallback(struct lws *wsi,
|
|||||||
}
|
}
|
||||||
} else if (event == "getConnections") {
|
} else if (event == "getConnections") {
|
||||||
// Admin-Funktion: Liste aller aktiven Verbindungen
|
// Admin-Funktion: Liste aller aktiven Verbindungen
|
||||||
|
if (!ud) {
|
||||||
|
std::cerr << "[RECEIVE] getConnections: ud ist nullptr" << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ud->userId.empty()) {
|
if (ud->userId.empty()) {
|
||||||
std::cerr << "[RECEIVE] getConnections: User-ID nicht gesetzt" << std::endl;
|
std::cerr << "[RECEIVE] getConnections: User-ID nicht gesetzt" << std::endl;
|
||||||
json errorResponse = {
|
try {
|
||||||
{"event", "getConnectionsResponse"},
|
json errorResponse = {
|
||||||
{"success", false},
|
{"event", "getConnectionsResponse"},
|
||||||
{"error", "User-ID nicht gesetzt"}
|
{"success", false},
|
||||||
};
|
{"error", "User-ID nicht gesetzt"}
|
||||||
instance->sendMessageToConnection(wsi, errorResponse.dump());
|
};
|
||||||
|
if (instance && wsi) {
|
||||||
|
instance->sendMessageToConnection(wsi, errorResponse.dump());
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[RECEIVE] Fehler beim Senden der Fehlerantwort: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prüfe Mainadmin-Rechte
|
// Prüfe Mainadmin-Rechte
|
||||||
if (!instance->isMainAdmin(ud->userId)) {
|
try {
|
||||||
std::cerr << "[RECEIVE] getConnections: Zugriff verweigert für User " << ud->userId << std::endl;
|
if (!instance || !instance->isMainAdmin(ud->userId)) {
|
||||||
json errorResponse = {
|
std::cerr << "[RECEIVE] getConnections: Zugriff verweigert für User " << ud->userId << std::endl;
|
||||||
|
json errorResponse = {
|
||||||
|
{"event", "getConnectionsResponse"},
|
||||||
|
{"success", false},
|
||||||
|
{"error", "Zugriff verweigert: Nur Mainadmin-User können Verbindungen abfragen"}
|
||||||
|
};
|
||||||
|
if (instance && wsi) {
|
||||||
|
instance->sendMessageToConnection(wsi, errorResponse.dump());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hole aktive Verbindungen
|
||||||
|
json connections = instance->getActiveConnections();
|
||||||
|
json response = {
|
||||||
{"event", "getConnectionsResponse"},
|
{"event", "getConnectionsResponse"},
|
||||||
{"success", false},
|
{"success", true},
|
||||||
{"error", "Zugriff verweigert: Nur Mainadmin-User können Verbindungen abfragen"}
|
{"data", connections}
|
||||||
};
|
};
|
||||||
instance->sendMessageToConnection(wsi, errorResponse.dump());
|
if (instance && wsi) {
|
||||||
break;
|
instance->sendMessageToConnection(wsi, response.dump());
|
||||||
|
std::cout << "[RECEIVE] getConnections: Verbindungen an Mainadmin gesendet" << std::endl;
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[RECEIVE] Fehler bei getConnections: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hole aktive Verbindungen
|
|
||||||
json connections = instance->getActiveConnections();
|
|
||||||
json response = {
|
|
||||||
{"event", "getConnectionsResponse"},
|
|
||||||
{"success", true},
|
|
||||||
{"data", connections}
|
|
||||||
};
|
|
||||||
instance->sendMessageToConnection(wsi, response.dump());
|
|
||||||
std::cout << "[RECEIVE] getConnections: Verbindungen an Mainadmin gesendet" << std::endl;
|
|
||||||
} else {
|
} else {
|
||||||
std::cout << "[RECEIVE] Unbekanntes Event: " << event << std::endl;
|
std::cout << "[RECEIVE] Unbekanntes Event: " << event << std::endl;
|
||||||
}
|
}
|
||||||
@@ -350,6 +378,10 @@ int WebSocketServer::wsCallback(struct lws *wsi,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LWS_CALLBACK_SERVER_WRITEABLE: {
|
case LWS_CALLBACK_SERVER_WRITEABLE: {
|
||||||
|
if (!ud) {
|
||||||
|
std::cerr << "[WRITEABLE] ud ist nullptr" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
// Prüfe ob es eine Nachricht zum Senden gibt
|
// Prüfe ob es eine Nachricht zum Senden gibt
|
||||||
std::string messageToSend;
|
std::string messageToSend;
|
||||||
{
|
{
|
||||||
@@ -575,12 +607,19 @@ nlohmann::json WebSocketServer::getActiveConnections() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (auto* wsi : connList) {
|
for (auto* wsi : connList) {
|
||||||
|
if (!wsi) continue;
|
||||||
auto* ud = reinterpret_cast<WebSocketUserData*>(lws_wsi_user(wsi));
|
auto* ud = reinterpret_cast<WebSocketUserData*>(lws_wsi_user(wsi));
|
||||||
if (ud) {
|
if (!ud) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
// Berechne Verbindungsdauer seit ESTABLISHED
|
// Berechne Verbindungsdauer seit ESTABLISHED
|
||||||
|
// Verwende lastPongTime als Fallback, falls connectionTime nicht gesetzt ist
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
auto connectionTime = ud->connectionTime.time_since_epoch().count() != 0
|
||||||
|
? ud->connectionTime
|
||||||
|
: ud->lastPongTime;
|
||||||
auto connectionDuration = std::chrono::duration_cast<std::chrono::seconds>(
|
auto connectionDuration = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
now - ud->connectionTime).count();
|
now - connectionTime).count();
|
||||||
|
|
||||||
// Berechne Zeit seit letztem Pong
|
// Berechne Zeit seit letztem Pong
|
||||||
auto timeSinceLastPong = std::chrono::duration_cast<std::chrono::seconds>(
|
auto timeSinceLastPong = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
@@ -593,6 +632,8 @@ nlohmann::json WebSocketServer::getActiveConnections() {
|
|||||||
{"pongReceived", ud->pongReceived}
|
{"pongReceived", ud->pongReceived}
|
||||||
};
|
};
|
||||||
userConnections["connections"].push_back(connInfo);
|
userConnections["connections"].push_back(connInfo);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[getActiveConnections] Fehler beim Verarbeiten einer Verbindung: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,11 +649,18 @@ nlohmann::json WebSocketServer::getActiveConnections() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (auto* wsi : allConnections) {
|
for (auto* wsi : allConnections) {
|
||||||
|
if (!wsi) continue;
|
||||||
auto* ud = reinterpret_cast<WebSocketUserData*>(lws_wsi_user(wsi));
|
auto* ud = reinterpret_cast<WebSocketUserData*>(lws_wsi_user(wsi));
|
||||||
if (ud && ud->userId.empty()) {
|
if (!ud || !ud->userId.empty()) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
// Verwende lastPongTime als Fallback, falls connectionTime nicht gesetzt ist
|
||||||
|
auto connectionTime = ud->connectionTime.time_since_epoch().count() != 0
|
||||||
|
? ud->connectionTime
|
||||||
|
: ud->lastPongTime;
|
||||||
auto connectionDuration = std::chrono::duration_cast<std::chrono::seconds>(
|
auto connectionDuration = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
now - ud->connectionTime).count();
|
now - connectionTime).count();
|
||||||
auto timeSinceLastPong = std::chrono::duration_cast<std::chrono::seconds>(
|
auto timeSinceLastPong = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
now - ud->lastPongTime).count();
|
now - ud->lastPongTime).count();
|
||||||
|
|
||||||
@@ -624,6 +672,8 @@ nlohmann::json WebSocketServer::getActiveConnections() {
|
|||||||
{"status", "unauthenticated"}
|
{"status", "unauthenticated"}
|
||||||
};
|
};
|
||||||
unauthenticatedConnections["connections"].push_back(connInfo);
|
unauthenticatedConnections["connections"].push_back(connInfo);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[getActiveConnections] Fehler beim Verarbeiten einer unauthentifizierten Verbindung: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,17 +684,42 @@ nlohmann::json WebSocketServer::getActiveConnections() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::sendMessageToConnection(struct lws *wsi, const std::string &message) {
|
void WebSocketServer::sendMessageToConnection(struct lws *wsi, const std::string &message) {
|
||||||
if (!wsi) return;
|
if (!wsi) {
|
||||||
|
std::cerr << "[sendMessageToConnection] wsi ist nullptr" << std::endl;
|
||||||
auto* ud = reinterpret_cast<WebSocketUserData*>(lws_wsi_user(wsi));
|
return;
|
||||||
if (!ud) return;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(ud->messageQueueMutex);
|
|
||||||
ud->messageQueue.push(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lws_callback_on_writable(wsi);
|
if (!context) {
|
||||||
|
std::cerr << "[sendMessageToConnection] context ist nullptr" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* ud = reinterpret_cast<WebSocketUserData*>(lws_wsi_user(wsi));
|
||||||
|
if (!ud) {
|
||||||
|
std::cerr << "[sendMessageToConnection] ud ist nullptr" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
bool wasEmpty = false;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(ud->messageQueueMutex);
|
||||||
|
wasEmpty = ud->messageQueue.empty();
|
||||||
|
ud->messageQueue.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nur wenn die Queue leer war, den Callback aufrufen
|
||||||
|
// (sonst wird er bereits durch den WRITEABLE-Handler aufgerufen)
|
||||||
|
if (wasEmpty) {
|
||||||
|
// Verwende lws_cancel_service, um den Service zu benachrichtigen, anstatt
|
||||||
|
// lws_callback_on_writable direkt aufzurufen (sicherer während Callbacks)
|
||||||
|
lws_cancel_service(context);
|
||||||
|
// Alternativ: lws_callback_on_writable(wsi) sollte auch funktionieren
|
||||||
|
lws_callback_on_writable(wsi);
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "[sendMessageToConnection] Fehler: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketServer::setWorkers(const std::vector<std::unique_ptr<Worker>> &workerList) {
|
void WebSocketServer::setWorkers(const std::vector<std::unique_ptr<Worker>> &workerList) {
|
||||||
|
|||||||
Reference in New Issue
Block a user