diff --git a/src/websocket_server.cpp b/src/websocket_server.cpp index ff8aca7..6937da9 100644 --- a/src/websocket_server.cpp +++ b/src/websocket_server.cpp @@ -237,8 +237,13 @@ int WebSocketServer::wsCallback(struct lws *wsi, if (!instance) return 0; auto *ud = reinterpret_cast(user); + switch (reason) { case LWS_CALLBACK_ESTABLISHED: { + if (!ud) { + std::cerr << "[ESTABLISHED] ud ist nullptr" << std::endl; + return 0; + } ud->pongReceived = true; ud->connectionTime = 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: // 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->lastPongTime = std::chrono::steady_clock::now(); ud->pingTimeoutCount = 0; @@ -306,38 +315,57 @@ int WebSocketServer::wsCallback(struct lws *wsi, } } else if (event == "getConnections") { // Admin-Funktion: Liste aller aktiven Verbindungen + if (!ud) { + std::cerr << "[RECEIVE] getConnections: ud ist nullptr" << std::endl; + break; + } + if (ud->userId.empty()) { std::cerr << "[RECEIVE] getConnections: User-ID nicht gesetzt" << std::endl; - json errorResponse = { - {"event", "getConnectionsResponse"}, - {"success", false}, - {"error", "User-ID nicht gesetzt"} - }; - instance->sendMessageToConnection(wsi, errorResponse.dump()); + try { + json errorResponse = { + {"event", "getConnectionsResponse"}, + {"success", false}, + {"error", "User-ID nicht gesetzt"} + }; + 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; } // Prüfe Mainadmin-Rechte - if (!instance->isMainAdmin(ud->userId)) { - std::cerr << "[RECEIVE] getConnections: Zugriff verweigert für User " << ud->userId << std::endl; - json errorResponse = { + try { + if (!instance || !instance->isMainAdmin(ud->userId)) { + 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"}, - {"success", false}, - {"error", "Zugriff verweigert: Nur Mainadmin-User können Verbindungen abfragen"} + {"success", true}, + {"data", connections} }; - instance->sendMessageToConnection(wsi, errorResponse.dump()); - break; + if (instance && wsi) { + 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 { std::cout << "[RECEIVE] Unbekanntes Event: " << event << std::endl; } @@ -350,6 +378,10 @@ int WebSocketServer::wsCallback(struct lws *wsi, break; } 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 std::string messageToSend; { @@ -575,12 +607,19 @@ nlohmann::json WebSocketServer::getActiveConnections() { }; for (auto* wsi : connList) { + if (!wsi) continue; auto* ud = reinterpret_cast(lws_wsi_user(wsi)); - if (ud) { + if (!ud) continue; + + try { // Berechne Verbindungsdauer seit ESTABLISHED + // Verwende lastPongTime als Fallback, falls connectionTime nicht gesetzt ist 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( - now - ud->connectionTime).count(); + now - connectionTime).count(); // Berechne Zeit seit letztem Pong auto timeSinceLastPong = std::chrono::duration_cast( @@ -593,6 +632,8 @@ nlohmann::json WebSocketServer::getActiveConnections() { {"pongReceived", ud->pongReceived} }; 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) { + if (!wsi) continue; auto* ud = reinterpret_cast(lws_wsi_user(wsi)); - if (ud && ud->userId.empty()) { + if (!ud || !ud->userId.empty()) continue; + + try { 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( - now - ud->connectionTime).count(); + now - connectionTime).count(); auto timeSinceLastPong = std::chrono::duration_cast( now - ud->lastPongTime).count(); @@ -624,6 +672,8 @@ nlohmann::json WebSocketServer::getActiveConnections() { {"status", "unauthenticated"} }; 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) { - if (!wsi) return; - - auto* ud = reinterpret_cast(lws_wsi_user(wsi)); - if (!ud) return; - - { - std::lock_guard lock(ud->messageQueueMutex); - ud->messageQueue.push(message); + if (!wsi) { + std::cerr << "[sendMessageToConnection] wsi ist nullptr" << std::endl; + return; } - lws_callback_on_writable(wsi); + if (!context) { + std::cerr << "[sendMessageToConnection] context ist nullptr" << std::endl; + return; + } + + auto* ud = reinterpret_cast(lws_wsi_user(wsi)); + if (!ud) { + std::cerr << "[sendMessageToConnection] ud ist nullptr" << std::endl; + return; + } + + try { + bool wasEmpty = false; + { + std::lock_guard 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> &workerList) {