From 7805827436177e7103c605399c11dead4dfd5157 Mon Sep 17 00:00:00 2001 From: James Richey Date: Wed, 9 Jun 2021 10:50:14 -0700 Subject: [PATCH] Close sockets with _RealWebSocket destructor A new helper method, terminateConnection(), is added to ensure all the places where sockets are closed cause the socket file descriptor to become invalidated and the client to be put in the correct state. A destructor is added to call terminateConnection() ensuring resources are freed if the WebSocket object is destroyed. Finally, private copy constructor and copy assignment operator are added per the "Rule of Three" ensuring only one instance manages the socket. This should address issue #71: Websocket::close() doesn't disconnect correctly. Calling WebSocket::close() still requests a clean disconnect; however, the socket is guaranteed to be closed when the WebSocket destructor is run. --- easywsclient.cpp | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/easywsclient.cpp b/easywsclient.cpp index 0c98098..3a6dd74 100644 --- a/easywsclient.cpp +++ b/easywsclient.cpp @@ -182,6 +182,13 @@ class _RealWebSocket : public easywsclient::WebSocket , isRxBad(false) { } + ~_RealWebSocket() { + // If the socket has not been closed by the time the destructor is run + // then we ensure it is closed. This might cause an unclean close as + // described in RFC 6455, but this is better than leaking file descriptors. + terminateConnection(); + } + readyStateValues getReadyState() const { return readyState; } @@ -217,8 +224,7 @@ class _RealWebSocket : public easywsclient::WebSocket } else if (ret <= 0) { rxbuf.resize(N); - closesocket(sockfd); - readyState = CLOSED; + terminateConnection(); fputs(ret < 0 ? "Connection error!\n" : "Connection closed!\n", stderr); break; } @@ -233,8 +239,7 @@ class _RealWebSocket : public easywsclient::WebSocket break; } else if (ret <= 0) { - closesocket(sockfd); - readyState = CLOSED; + terminateConnection(); fputs(ret < 0 ? "Connection error!\n" : "Connection closed!\n", stderr); break; } @@ -243,8 +248,7 @@ class _RealWebSocket : public easywsclient::WebSocket } } if (!txbuf.size() && readyState == CLOSING) { - closesocket(sockfd); - readyState = CLOSED; + terminateConnection(); } } @@ -451,6 +455,28 @@ class _RealWebSocket : public easywsclient::WebSocket txbuf.insert(txbuf.end(), header.begin(), header.end()); } + // Immediately terminates the connection to the remote host, if connected. + // + // This ensures resources and open socket file descriptors are cleaned up. + // Typically this is called once the server closes the underlying socket + // connection, however, it can also be used by the client to force an + // unclean close of the connection. + // + // This method does not throw and it is safe to call multiple times. + void terminateConnection() { + if (sockfd != INVALID_SOCKET) { + closesocket(sockfd); + sockfd = INVALID_SOCKET; + readyState = CLOSED; + } + } + +private: + + // WebSockets do not support being copy constructed or copy assigned. + _RealWebSocket(const _RealWebSocket&); + _RealWebSocket& operator=(const _RealWebSocket&); + };