[PR #432] [MERGED] fix(rtsp): keep RTSP server alive when a client disconnects mid-request #465

Closed
opened 2026-10-15 13:00:46 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/games-on-whales/wolf/pull/432
Author: @JBailes
Created: 6/14/2026
Status: ✅ Merged
Merged: 6/14/2026
Merged by: @ABeltramo

Base: stable ← Head: fix/rtsp-server-crash-on-client-reset


📝 Commits (1)

  • 7351162 fix(rtsp): keep RTSP server alive when a client disconnects mid-request

📊 Changes

1 file changed (+49 additions, -11 deletions)

View changed files

📝 src/moonlight-server/rtsp/net.hpp (+49 -11)

📄 Description

Problem

A client that opens an RTSP connection, sends a malformed/partial request, and immediately resets the connection (Moonlight does exactly this on a rejected message) leaves the socket disconnected. Wolf then performed socket operations on the dead socket that throw boost::system::system_error (ENOTCONN, "Transport endpoint is not connected"):

  • tcp_connection::close() used the throwing shutdown()/close() overloads;
  • start() called socket().remote_endpoint() on the now-disconnected socket.

These run inside io_context.run(), so the exception unwinds all the way out of run_server(), which logs:

ERROR | Unable to create RTSP server on port: 48010 ex: shutdown: Transport endpoint is not connected [system:107 ...]

and returns — the entire RTSP server dies. Every subsequent connection gets ECONNREFUSED, so no stream can be set up (clients report being unable to start the RTSP handshake). A single early-disconnecting client takes down streaming for everyone until Wolf restarts.

Reproduced with a packet capture: client sends a 1-byte message then RSTs → Wolf sends 400 BAD REQUEST → close() → shutdown() throws → server gone.

Fix

  • close() — use the non-throwing error_code overloads of shutdown()/close().
  • start() — resolve remote_endpoint() non-throwing and bail cleanly if the peer is already gone (also avoids a redundant second remote_endpoint() call).
  • handle_accept() — re-arm the acceptor before servicing the connection and wrap start() in try/catch, so one bad connection can never stop the accept loop.
  • run_server() — resume io_context.run() after a handler exception instead of letting it tear the server down (defense in depth).

Testing

Verified on a live deployment: before the fix, a single \n+RST probe to :48010 killed the server (subsequent connects refused); after, the server survives repeated reset-probes and keeps serving, and Moonlight streaming (Wolf UI + Steam) works.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/games-on-whales/wolf/pull/432 **Author:** [@JBailes](https://github.com/JBailes) **Created:** 6/14/2026 **Status:** ✅ Merged **Merged:** 6/14/2026 **Merged by:** [@ABeltramo](https://github.com/ABeltramo) **Base:** `stable` ← **Head:** `fix/rtsp-server-crash-on-client-reset` --- ### 📝 Commits (1) - [`7351162`](https://github.com/games-on-whales/wolf/commit/735116254aa54a22a35c3252d88581e8549f6a5d) fix(rtsp): keep RTSP server alive when a client disconnects mid-request ### 📊 Changes **1 file changed** (+49 additions, -11 deletions) <details> <summary>View changed files</summary> 📝 `src/moonlight-server/rtsp/net.hpp` (+49 -11) </details> ### 📄 Description ## Problem A client that opens an RTSP connection, sends a malformed/partial request, and immediately resets the connection (Moonlight does exactly this on a rejected message) leaves the socket disconnected. Wolf then performed socket operations on the dead socket that throw `boost::system::system_error` (ENOTCONN, *"Transport endpoint is not connected"*): - `tcp_connection::close()` used the **throwing** `shutdown()`/`close()` overloads; - `start()` called `socket().remote_endpoint()` on the now-disconnected socket. These run inside `io_context.run()`, so the exception unwinds all the way out of `run_server()`, which logs: ``` ERROR | Unable to create RTSP server on port: 48010 ex: shutdown: Transport endpoint is not connected [system:107 ...] ``` and returns — **the entire RTSP server dies**. Every subsequent connection gets `ECONNREFUSED`, so no stream can be set up (clients report being unable to start the RTSP handshake). A single early-disconnecting client takes down streaming for everyone until Wolf restarts. Reproduced with a packet capture: client sends a 1-byte message then RSTs → Wolf sends `400 BAD REQUEST` → `close()` → `shutdown()` throws → server gone. ## Fix - **`close()`** — use the non-throwing `error_code` overloads of `shutdown()`/`close()`. - **`start()`** — resolve `remote_endpoint()` non-throwing and bail cleanly if the peer is already gone (also avoids a redundant second `remote_endpoint()` call). - **`handle_accept()`** — re-arm the acceptor *before* servicing the connection and wrap `start()` in try/catch, so one bad connection can never stop the accept loop. - **`run_server()`** — resume `io_context.run()` after a handler exception instead of letting it tear the server down (defense in depth). ## Testing Verified on a live deployment: before the fix, a single `\n`+RST probe to :48010 killed the server (subsequent connects refused); after, the server survives repeated reset-probes and keeps serving, and Moonlight streaming (Wolf UI + Steam) works. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-15 13:00:46 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
games-on-whales/wolf#465
No description provided.