From fcd9daf69087f838a814650ecf4f35335ead256f Mon Sep 17 00:00:00 2001 From: neatnoise Date: Sun, 17 May 2026 16:01:33 +0200 Subject: [PATCH] fix(web): disconnect only the disabled client instead of all sessions --- src/confighttp.cpp | 5 ++++- src/nvhttp.cpp | 14 ++++++++++++++ src/nvhttp.h | 1 + src/rtsp.cpp | 18 ++++++++++++++++++ src/rtsp.h | 2 ++ src/stream.cpp | 6 ++++++ src/stream.h | 1 + 7 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/confighttp.cpp b/src/confighttp.cpp index 2f703dbca5c..13c993fad5f 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -882,7 +882,10 @@ namespace confighttp { output_tree["status"] = nvhttp::set_client_enabled(uuid, enabled); if (!enabled && output_tree["status"]) { - rtsp_stream::terminate_sessions(); + auto cert = nvhttp::get_cert_by_uuid(uuid); + if (!cert.empty()) { + rtsp_stream::terminate_sessions_by_cert(cert); + } if (rtsp_stream::session_count() == 0 && proc::proc.running() > 0) { proc::proc.terminate(); diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 4db8c6e4e94..72e9dc5cd05 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -144,6 +144,9 @@ namespace nvhttp { client_t client_root; std::atomic session_id_counter; + // Set by TLS verify callback, read by launch/resume handler (single-threaded HTTPS server) + std::string last_verified_client_cert; // NOSONAR(cpp:S5421) - intentionally mutable global + using args_t = SimpleWeb::CaseInsensitiveMultimap; using resp_https_t = std::shared_ptr::Response>; using req_https_t = std::shared_ptr::Request>; @@ -326,6 +329,7 @@ namespace nvhttp { launch_session->rtsp_iv_counter = 0; } launch_session->rtsp_url_scheme = launch_session->rtsp_cipher ? "rtspenc://"s : "rtsp://"s; + launch_session->client_cert = last_verified_client_cert; // Generate the unique identifiers for this connection that we will send later during RTSP handshake unsigned char raw_payload[8]; @@ -1141,6 +1145,7 @@ namespace nvhttp { return verified; } + last_verified_client_cert = pem; verified = 1; return verified; @@ -1255,6 +1260,15 @@ namespace nvhttp { return false; } + std::string get_cert_by_uuid(const std::string_view uuid) { + for (const auto &named_cert : client_root.named_devices) { + if (named_cert.uuid == uuid) { + return named_cert.cert; + } + } + return {}; + } + bool is_client_enabled(const std::string_view cert_pem) { const client_t &client = client_root; for (const auto &named_cert : client.named_devices) { diff --git a/src/nvhttp.h b/src/nvhttp.h index c24d018c969..f502c1b35cf 100644 --- a/src/nvhttp.h +++ b/src/nvhttp.h @@ -191,6 +191,7 @@ namespace nvhttp { * @return true if the client was found and updated. */ bool set_client_enabled(std::string_view uuid, bool enabled); + std::string get_cert_by_uuid(std::string_view uuid); /** * @brief Get all paired clients. diff --git a/src/rtsp.cpp b/src/rtsp.cpp index 4dd8b12c8e0..4e28df9e20d 100644 --- a/src/rtsp.cpp +++ b/src/rtsp.cpp @@ -569,6 +569,20 @@ namespace rtsp_stream { } } + void clear_by_cert(std::string_view cert) { + auto lg = _session_slots.lock(); + for (auto i = _session_slots->begin(); i != _session_slots->end();) { + auto &slot = *(*i); + if (stream::session::client_cert(slot) == cert) { + stream::session::stop(slot); + stream::session::join(slot); + i = _session_slots->erase(i); + } else { + i++; + } + } + } + /** * @brief Removes the provided session from the set of sessions. * @param session The session to remove. @@ -643,6 +657,10 @@ namespace rtsp_stream { server.clear(true); } + void terminate_sessions_by_cert(std::string_view cert) { + server.clear_by_cert(cert); + } + int send(tcp::socket &sock, const std::string_view &sv) { std::size_t bytes_send = 0; diff --git a/src/rtsp.h b/src/rtsp.h index 6dfa16e12e8..d2bf39a072c 100644 --- a/src/rtsp.h +++ b/src/rtsp.h @@ -39,6 +39,7 @@ namespace rtsp_stream { std::optional rtsp_cipher; std::string rtsp_url_scheme; uint32_t rtsp_iv_counter; + std::string client_cert; }; void launch_session_raise(std::shared_ptr launch_session); @@ -59,6 +60,7 @@ namespace rtsp_stream { * @brief Terminates all running streaming sessions. */ void terminate_sessions(); + void terminate_sessions_by_cert(std::string_view cert); /** * @brief Runs the RTSP server loop. diff --git a/src/stream.cpp b/src/stream.cpp index 8b303ef24da..c0da8671cd5 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -405,6 +405,7 @@ namespace stream { } control; std::uint32_t launch_session_id; + std::string client_cert; safe::mail_raw_t::event_t shutdown_event; safe::signal_t controlEnd; @@ -1902,6 +1903,10 @@ namespace stream { return session.state.load(std::memory_order_relaxed); } + const std::string &client_cert(session_t &session) { + return session.client_cert; + } + void stop(session_t &session) { while_starting_do_nothing(session.state); auto expected = state_e::RUNNING; @@ -2009,6 +2014,7 @@ namespace stream { session->shutdown_event = mail->event(mail::shutdown); session->launch_session_id = launch_session.id; + session->client_cert = launch_session.client_cert; session->config = config; diff --git a/src/stream.h b/src/stream.h index 53afff4fabe..5ee377764e7 100644 --- a/src/stream.h +++ b/src/stream.h @@ -51,5 +51,6 @@ namespace stream { void stop(session_t &session); void join(session_t &session); state_e state(session_t &session); + const std::string &client_cert(session_t &session); } // namespace session } // namespace stream