Feature/networking issues#137
Conversation
Fixes an issue where protocol-level websocket responses were not sent back to the connected peer, which could leave the peer waiting indefinitely for a response. For example, when using the OpenSpace API to disconnect - the api would not be notified that the connection is terminated until 10-20 seconds later when an internal ping fails.
There was an issue where if the peer sends a Websocket close frame - ghoul would respond with the corect handshake (previous fix) but if the peer does not properly close the TCP socket this could leave the OpenSpace socket connection open indefinitely(or until shutdown). This fix removes the socket regardles after receiving and sending the close frame
There was a problem hiding this comment.
Pull request overview
This PR addresses WebSocket networking edge cases by ensuring protocol-level frames generated by websocketpp (Ping/Pong/Close handshakes) are actually written back to the peer, and by proactively closing the underlying TCP socket on WebSocket close to avoid lingering connections.
Changes:
- Drain websocketpp’s protocol-generated output after
read_someand write it back to the TCP socket. - Introduce
TcpSocket::closeConnection()as a non-blocking close primitive and use it fromWebSocket::onClose. - Add API documentation for
closeConnection()in the TcpSocket header.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/io/socket/websocket.cpp | Sends websocketpp protocol output after read_some; closes underlying TCP connection on close. |
| src/io/socket/tcpsocket.cpp | Adds TcpSocket::closeConnection() to stop I/O loops and close the socket without disconnect()’s join behavior. |
| include/ghoul/io/socket/tcpsocket.h | Declares and documents the new closeConnection() API. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| bool TcpSocket::waitForOutputQueueDrained(std::chrono::milliseconds timeout) { | ||
| std::unique_lock lock(_outputQueueMutex); | ||
| return _outputNotifier.wait_for( | ||
| lock, | ||
| timeout, | ||
| [this]() { | ||
| return _outputQueue.empty() || _shouldStopThreads || | ||
| (!_isConnected && !_isConnecting); | ||
| } | ||
| ); | ||
| } |
| _outputQueue.erase(_outputQueue.begin(), _outputQueue.begin() + nSentBytes); | ||
| } | ||
| _outputNotifier.notify_all(); // Let anyone waiting on drainage know |
There was a problem hiding this comment.
Noticed an issue when trying: Closing the Showcomposer while OpenSpace is running now leads to a mutex-related crash
It does not happen when opening the WebUI in a browser and closing it, or the ui.openspaceproject.com pages
Did all my tests using the empty profile
This PR fixes some networking issues where we did not previously send back a protocol response when needed, for example when a WebSocket disconnects.
Ensure protocol-level WebSocket responses (Ping/Pong/Close, etc)
Fixes an issue where protocol-level WebSocket responses were not sent back to the connected peer, which could leave the peer waiting indefinitely for a response. For example, when using the OpenSpace API to disconnect, the API would not be notified that the connection has been terminated until 10-20 seconds later, when an internal ping fails.
Also fixes an additional issue: possible lingering sockets on our end if the handshake/tear-up is not complete by the peer.
Ensure socket closes even if handshake close is not complete from peer
There was an issue where if the peer sends a WebSocket close frame, Ghoul would respond with the correct handshake (previous fix), but if the peer does not properly close the TCP socket, this could leave the OpenSpace socket connection open indefinitely(or until shutdown). This fix removes the socket regardless after receiving and sending the close frame