Compare commits
	
		
			4 Commits
		
	
	
		
			v3.1.2
			...
			feature/se
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 1ac02fdc0e | ||
|  | 687956358d | ||
|  | 1a42c92325 | ||
|  | 6bb00b6788 | 
| @@ -1 +1 @@ | |||||||
| docker/Dockerfile.fedora | docker/Dockerfile.ubuntu_xenial | ||||||
| @@ -30,7 +30,9 @@ namespace ix | |||||||
|         _host(host), |         _host(host), | ||||||
|         _backlog(backlog), |         _backlog(backlog), | ||||||
|         _maxConnections(maxConnections), |         _maxConnections(maxConnections), | ||||||
|  |         _serverFd(-1), | ||||||
|         _stop(false), |         _stop(false), | ||||||
|  |         _stopGc(false), | ||||||
|         _connectionStateFactory(&ConnectionState::createConnectionState) |         _connectionStateFactory(&ConnectionState::createConnectionState) | ||||||
|     { |     { | ||||||
|  |  | ||||||
| @@ -124,9 +126,15 @@ namespace ix | |||||||
|  |  | ||||||
|     void SocketServer::start() |     void SocketServer::start() | ||||||
|     { |     { | ||||||
|         if (_thread.joinable()) return; // we've already been started |         if (!_thread.joinable()) | ||||||
|  |         { | ||||||
|  |             _thread = std::thread(&SocketServer::run, this); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         _thread = std::thread(&SocketServer::run, this); |         if (!_gcThread.joinable()) | ||||||
|  |         { | ||||||
|  |             _gcThread = std::thread(&SocketServer::runGC, this); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void SocketServer::wait() |     void SocketServer::wait() | ||||||
| @@ -142,21 +150,21 @@ namespace ix | |||||||
|  |  | ||||||
|     void SocketServer::stop() |     void SocketServer::stop() | ||||||
|     { |     { | ||||||
|         while (true) |         // Stop accepting connections, and close the 'accept' thread | ||||||
|  |         if (_thread.joinable()) | ||||||
|         { |         { | ||||||
|             if (closeTerminatedThreads()) break; |             _stop = true; | ||||||
|  |             _thread.join(); | ||||||
|             // wait 10ms and try again later. |             _stop = false; | ||||||
|             // we could have a timeout, but if we exit of here |  | ||||||
|             // we leaked threads, it is quite bad. |  | ||||||
|             std::this_thread::sleep_for(std::chrono::milliseconds(10)); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!_thread.joinable()) return; // nothing to do |         // Join all threads and make sure that all connections are terminated | ||||||
|  |         if (_gcThread.joinable()) | ||||||
|         _stop = true; |         { | ||||||
|         _thread.join(); |             _stopGc = true; | ||||||
|         _stop = false; |             _gcThread.join(); | ||||||
|  |             _stopGc = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         _conditionVariable.notify_one(); |         _conditionVariable.notify_one(); | ||||||
|         Socket::closeSocket(_serverFd); |         Socket::closeSocket(_serverFd); | ||||||
| @@ -175,7 +183,7 @@ namespace ix | |||||||
|     // field becomes true, and we can use that to know that we can join that thread |     // field becomes true, and we can use that to know that we can join that thread | ||||||
|     // and remove it from our _connectionsThreads data structure (a list). |     // and remove it from our _connectionsThreads data structure (a list). | ||||||
|     // |     // | ||||||
|     bool SocketServer::closeTerminatedThreads() |     void SocketServer::closeTerminatedThreads() | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_connectionsThreadsMutex); |         std::lock_guard<std::mutex> lock(_connectionsThreadsMutex); | ||||||
|         auto it = _connectionsThreads.begin(); |         auto it = _connectionsThreads.begin(); | ||||||
| @@ -195,8 +203,6 @@ namespace ix | |||||||
|             if (thread.joinable()) thread.join(); |             if (thread.joinable()) thread.join(); | ||||||
|             it = _connectionsThreads.erase(it); |             it = _connectionsThreads.erase(it); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return _connectionsThreads.empty(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void SocketServer::run() |     void SocketServer::run() | ||||||
| @@ -208,12 +214,6 @@ namespace ix | |||||||
|         { |         { | ||||||
|             if (_stop) return; |             if (_stop) return; | ||||||
|  |  | ||||||
|             // Garbage collection to shutdown/join threads for closed connections. |  | ||||||
|             // We could run this in its own thread, so that we dont need to accept |  | ||||||
|             // a new connection to close a thread. |  | ||||||
|             // We could also use a condition variable to be notify when we need to do this |  | ||||||
|             closeTerminatedThreads(); |  | ||||||
|  |  | ||||||
|             // Use select to check whether a new connection is in progress |             // Use select to check whether a new connection is in progress | ||||||
|             fd_set rfds; |             fd_set rfds; | ||||||
|             struct timeval timeout; |             struct timeval timeout; | ||||||
| @@ -290,5 +290,30 @@ namespace ix | |||||||
|                                 connectionState))); |                                 connectionState))); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     size_t SocketServer::getConnectionsThreadsCount() | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lock(_connectionsThreadsMutex); | ||||||
|  |         return _connectionsThreads.size(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void SocketServer::runGC() | ||||||
|  |     { | ||||||
|  |         for (;;) | ||||||
|  |         { | ||||||
|  |             // Garbage collection to shutdown/join threads for closed connections. | ||||||
|  |             closeTerminatedThreads(); | ||||||
|  |  | ||||||
|  |             // We quit this thread if all connections are closed and we received | ||||||
|  |             // a stop request by setting _stopGc to true. | ||||||
|  |             if (_stopGc && getConnectionsThreadsCount() == 0) | ||||||
|  |             { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Sleep a little bit then keep cleaning up | ||||||
|  |             std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -74,6 +74,12 @@ namespace ix | |||||||
|         // background thread to wait for incoming connections |         // background thread to wait for incoming connections | ||||||
|         std::atomic<bool> _stop; |         std::atomic<bool> _stop; | ||||||
|         std::thread _thread; |         std::thread _thread; | ||||||
|  |         void run(); | ||||||
|  |  | ||||||
|  |         // background thread to cleanup (join) terminated threads | ||||||
|  |         std::atomic<bool> _stopGc; | ||||||
|  |         std::thread _gcThread; | ||||||
|  |         void runGC(); | ||||||
|  |  | ||||||
|         // the list of (connectionState, threads) for each connections |         // the list of (connectionState, threads) for each connections | ||||||
|         ConnectionThreads _connectionsThreads; |         ConnectionThreads _connectionsThreads; | ||||||
| @@ -87,13 +93,12 @@ namespace ix | |||||||
|         // the factory to create ConnectionState objects |         // the factory to create ConnectionState objects | ||||||
|         ConnectionStateFactory _connectionStateFactory; |         ConnectionStateFactory _connectionStateFactory; | ||||||
|  |  | ||||||
|         // Methods |  | ||||||
|         void run(); |  | ||||||
|         virtual void handleConnection(int fd, |         virtual void handleConnection(int fd, | ||||||
|                                       std::shared_ptr<ConnectionState> connectionState) = 0; |                                       std::shared_ptr<ConnectionState> connectionState) = 0; | ||||||
|         virtual size_t getConnectedClientsCount() = 0; |         virtual size_t getConnectedClientsCount() = 0; | ||||||
|  |  | ||||||
|         // Returns true if all connection threads are joined |         // Returns true if all connection threads are joined | ||||||
|         bool closeTerminatedThreads(); |         void closeTerminatedThreads(); | ||||||
|  |         size_t getConnectionsThreadsCount(); | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -212,9 +212,10 @@ namespace ix | |||||||
|         return getReadyState() == ReadyState::Closing; |         return getReadyState() == ReadyState::Closing; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void WebSocket::close() |     void WebSocket::close(uint16_t code, | ||||||
|  |                           const std::string& reason) | ||||||
|     { |     { | ||||||
|         _ws.close(); |         _ws.close(code, reason); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void WebSocket::checkConnection(bool firstConnectionAttempt) |     void WebSocket::checkConnection(bool firstConnectionAttempt) | ||||||
|   | |||||||
| @@ -112,7 +112,11 @@ namespace ix | |||||||
|         WebSocketSendInfo sendText(const std::string& text, |         WebSocketSendInfo sendText(const std::string& text, | ||||||
|                                    const OnProgressCallback& onProgressCallback = nullptr); |                                    const OnProgressCallback& onProgressCallback = nullptr); | ||||||
|         WebSocketSendInfo ping(const std::string& text); |         WebSocketSendInfo ping(const std::string& text); | ||||||
|         void close(); |  | ||||||
|  |         // A close frame can provide a code and a reason | ||||||
|  |         // FIXME: use constants | ||||||
|  |         void close(uint16_t code = 1000, | ||||||
|  |                    const std::string& reason = "Normal closure"); | ||||||
|  |  | ||||||
|         void setOnMessageCallback(const OnMessageCallback& callback); |         void setOnMessageCallback(const OnMessageCallback& callback); | ||||||
|         static void setTrafficTrackerCallback(const OnTrafficTrackerCallback& callback); |         static void setTrafficTrackerCallback(const OnTrafficTrackerCallback& callback); | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ project (ixwebsocket_unittest) | |||||||
|  |  | ||||||
| set (CMAKE_CXX_STANDARD 14) | set (CMAKE_CXX_STANDARD 14) | ||||||
|  |  | ||||||
| if (NOT WIN32) | if (UNIX) | ||||||
|   set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../third_party/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH}) |   set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../third_party/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH}) | ||||||
|   find_package(Sanitizers) |   find_package(Sanitizers) | ||||||
|   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") |   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") | ||||||
| @@ -38,19 +38,22 @@ set (SOURCES | |||||||
|   IXWebSocketPingTest.cpp |   IXWebSocketPingTest.cpp | ||||||
|   IXWebSocketTestConnectionDisconnection.cpp |   IXWebSocketTestConnectionDisconnection.cpp | ||||||
|   IXUrlParserTest.cpp |   IXUrlParserTest.cpp | ||||||
|  |   IXWebSocketServerTest.cpp | ||||||
|  |   IXWebSocketPingTest.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| # Some unittest don't work on windows yet | # Some unittest don't work on windows yet | ||||||
| if (NOT WIN32) | if (UNIX) | ||||||
|   list(APPEND SOURCES |   list(APPEND SOURCES | ||||||
|     IXWebSocketPingTimeoutTest.cpp |     # IXWebSocketPingTimeoutTest.cpp # This test isn't reliable # (multiple platforms), disabling in master | ||||||
|  |     # IXWebSocketCloseTest.cpp       #  | ||||||
|     cmd_websocket_chat.cpp |     cmd_websocket_chat.cpp | ||||||
|   ) |   ) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| add_executable(ixwebsocket_unittest ${SOURCES}) | add_executable(ixwebsocket_unittest ${SOURCES}) | ||||||
|  |  | ||||||
| if (NOT WIN32) | if (UNIX) | ||||||
|   add_sanitizers(ixwebsocket_unittest) |   add_sanitizers(ixwebsocket_unittest) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										407
									
								
								test/IXWebSocketCloseTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										407
									
								
								test/IXWebSocketCloseTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,407 @@ | |||||||
|  | /* | ||||||
|  |  *  IXWebSocketCloseTest.cpp | ||||||
|  |  *  Author: Alexandre Konieczny | ||||||
|  |  *  Copyright (c) 2019 Machine Zone. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <iostream> | ||||||
|  | #include <sstream> | ||||||
|  | #include <queue> | ||||||
|  | #include <ixwebsocket/IXWebSocket.h> | ||||||
|  | #include <ixwebsocket/IXWebSocketServer.h> | ||||||
|  |  | ||||||
|  | #include "IXTest.h" | ||||||
|  |  | ||||||
|  | #include "catch.hpp" | ||||||
|  |  | ||||||
|  | using namespace ix; | ||||||
|  |  | ||||||
|  | namespace | ||||||
|  | { | ||||||
|  |     class WebSocketClient | ||||||
|  |     { | ||||||
|  |         public: | ||||||
|  |             WebSocketClient(int port); | ||||||
|  |  | ||||||
|  |             void subscribe(const std::string& channel); | ||||||
|  |             void start(); | ||||||
|  |             void stop(); | ||||||
|  |             void stop(uint16_t code, const std::string& reason); | ||||||
|  |             bool isReady() const; | ||||||
|  |             void sendMessage(const std::string& text); | ||||||
|  |  | ||||||
|  |             uint16_t getCloseCode(); | ||||||
|  |             const std::string& getCloseReason(); | ||||||
|  |             bool getCloseRemote(); | ||||||
|  |  | ||||||
|  |         private: | ||||||
|  |             ix::WebSocket _webSocket; | ||||||
|  |             int _port; | ||||||
|  |  | ||||||
|  |             mutable std::mutex _mutexCloseData; | ||||||
|  |             uint16_t _closeCode; | ||||||
|  |             std::string _closeReason; | ||||||
|  |             bool _closeRemote; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     WebSocketClient::WebSocketClient(int port) | ||||||
|  |         : _port(port) | ||||||
|  |         , _closeCode(0) | ||||||
|  |         , _closeReason(std::string("")) | ||||||
|  |         , _closeRemote(false) | ||||||
|  |     { | ||||||
|  |         ; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool WebSocketClient::isReady() const | ||||||
|  |     { | ||||||
|  |         return _webSocket.getReadyState() == ix::ReadyState::Open; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     uint16_t WebSocketClient::getCloseCode() | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lck(_mutexCloseData); | ||||||
|  |          | ||||||
|  |         return _closeCode; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const std::string& WebSocketClient::getCloseReason() | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lck(_mutexCloseData); | ||||||
|  |  | ||||||
|  |         return _closeReason; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool WebSocketClient::getCloseRemote() | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lck(_mutexCloseData); | ||||||
|  |          | ||||||
|  |         return _closeRemote; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketClient::stop() | ||||||
|  |     { | ||||||
|  |         _webSocket.stop(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketClient::stop(uint16_t code, const std::string& reason) | ||||||
|  |     { | ||||||
|  |         _webSocket.close(code, reason); | ||||||
|  |         _webSocket.stop(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketClient::start() | ||||||
|  |     { | ||||||
|  |         std::string url; | ||||||
|  |         { | ||||||
|  |             std::stringstream ss; | ||||||
|  |             ss << "ws://localhost:" | ||||||
|  |                << _port | ||||||
|  |                << "/"; | ||||||
|  |  | ||||||
|  |             url = ss.str(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         _webSocket.setUrl(url); | ||||||
|  |  | ||||||
|  |         std::stringstream ss; | ||||||
|  |         log(std::string("Connecting to url: ") + url); | ||||||
|  |  | ||||||
|  |         _webSocket.setOnMessageCallback( | ||||||
|  |             [this](ix::WebSocketMessageType messageType, | ||||||
|  |                const std::string& str, | ||||||
|  |                size_t wireSize, | ||||||
|  |                const ix::WebSocketErrorInfo& error, | ||||||
|  |                const ix::WebSocketOpenInfo& openInfo, | ||||||
|  |                    const ix::WebSocketCloseInfo& closeInfo) | ||||||
|  |             { | ||||||
|  |                 std::stringstream ss; | ||||||
|  |                 if (messageType == ix::WebSocketMessageType::Open) | ||||||
|  |                 { | ||||||
|  |                     log("client connected"); | ||||||
|  |  | ||||||
|  |                     _webSocket.disableAutomaticReconnection(); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocketMessageType::Close) | ||||||
|  |                 { | ||||||
|  |                     log("client disconnected"); | ||||||
|  |  | ||||||
|  |                     std::lock_guard<std::mutex> lck(_mutexCloseData); | ||||||
|  |                      | ||||||
|  |                     _closeCode = closeInfo.code; | ||||||
|  |                     _closeReason = std::string(closeInfo.reason); | ||||||
|  |                     _closeRemote = closeInfo.remote; | ||||||
|  |                      | ||||||
|  |                     _webSocket.disableAutomaticReconnection(); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocketMessageType::Error) | ||||||
|  |                 { | ||||||
|  |                     ss << "Error ! " << error.reason; | ||||||
|  |                     log(ss.str()); | ||||||
|  |  | ||||||
|  |                     _webSocket.disableAutomaticReconnection(); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocketMessageType::Pong) | ||||||
|  |                 { | ||||||
|  |                     ss << "Received pong message " << str; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocketMessageType::Ping) | ||||||
|  |                 { | ||||||
|  |                     ss << "Received ping message " << str; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |                 else if (messageType == ix::WebSocketMessageType::Message) | ||||||
|  |                 { | ||||||
|  |                     ss << "Received message " << str; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     ss << "Invalid ix::WebSocketMessageType"; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |         _webSocket.start(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketClient::sendMessage(const std::string& text) | ||||||
|  |     { | ||||||
|  |         _webSocket.send(text); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool startServer(ix::WebSocketServer& server, | ||||||
|  |                      uint16_t& receivedCloseCode, | ||||||
|  |                      std::string& receivedCloseReason, | ||||||
|  |                      bool& receivedCloseRemote, | ||||||
|  |                      std::mutex& mutexWrite) | ||||||
|  |     { | ||||||
|  |         // A dev/null server | ||||||
|  |         server.setOnConnectionCallback( | ||||||
|  |             [&server, &receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](std::shared_ptr<ix::WebSocket> webSocket, | ||||||
|  |                                              std::shared_ptr<ConnectionState> connectionState) | ||||||
|  |             { | ||||||
|  |                 webSocket->setOnMessageCallback( | ||||||
|  |                     [webSocket, connectionState, &server, &receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](ix::WebSocketMessageType messageType, | ||||||
|  |                        const std::string& str, | ||||||
|  |                        size_t wireSize, | ||||||
|  |                        const ix::WebSocketErrorInfo& error, | ||||||
|  |                        const ix::WebSocketOpenInfo& openInfo, | ||||||
|  |                        const ix::WebSocketCloseInfo& closeInfo) | ||||||
|  |                     { | ||||||
|  |                         if (messageType == ix::WebSocketMessageType::Open) | ||||||
|  |                         { | ||||||
|  |                             Logger() << "New server connection"; | ||||||
|  |                             Logger() << "id: " << connectionState->getId(); | ||||||
|  |                             Logger() << "Uri: " << openInfo.uri; | ||||||
|  |                             Logger() << "Headers:"; | ||||||
|  |                             for (auto it : openInfo.headers) | ||||||
|  |                             { | ||||||
|  |                                 Logger() << it.first << ": " << it.second; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         else if (messageType == ix::WebSocketMessageType::Close) | ||||||
|  |                         { | ||||||
|  |                             log("Server closed connection"); | ||||||
|  |  | ||||||
|  |                             //Logger() << closeInfo.code; | ||||||
|  |                             //Logger() << closeInfo.reason; | ||||||
|  |                             //Logger() << closeInfo.remote; | ||||||
|  |  | ||||||
|  |                             std::lock_guard<std::mutex> lck(mutexWrite); | ||||||
|  |                              | ||||||
|  |                             receivedCloseCode = closeInfo.code; | ||||||
|  |                             receivedCloseReason = std::string(closeInfo.reason); | ||||||
|  |                             receivedCloseRemote = closeInfo.remote; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         auto res = server.listen(); | ||||||
|  |         if (!res.first) | ||||||
|  |         { | ||||||
|  |             log(res.second); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         server.start(); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_CASE("Websocket_client_close_default", "[close]") | ||||||
|  | { | ||||||
|  |     SECTION("Make sure that close code and reason was used and sent to server.") | ||||||
|  |     { | ||||||
|  |         ix::setupWebSocketTrafficTrackerCallback(); | ||||||
|  |  | ||||||
|  |         int port = getFreePort(); | ||||||
|  |         ix::WebSocketServer server(port); | ||||||
|  |          | ||||||
|  |         uint16_t serverReceivedCloseCode(0); | ||||||
|  |         bool serverReceivedCloseRemote(false); | ||||||
|  |         std::string serverReceivedCloseReason(""); | ||||||
|  |         std::mutex mutexWrite; | ||||||
|  |  | ||||||
|  |         REQUIRE(startServer(server, serverReceivedCloseCode, serverReceivedCloseReason, serverReceivedCloseRemote, mutexWrite)); | ||||||
|  |  | ||||||
|  |         std::string session = ix::generateSessionId(); | ||||||
|  |         WebSocketClient webSocketClient(port); | ||||||
|  |  | ||||||
|  |         webSocketClient.start(); | ||||||
|  |  | ||||||
|  |         // Wait for all chat instance to be ready | ||||||
|  |         while (true) | ||||||
|  |         { | ||||||
|  |             if (webSocketClient.isReady()) break; | ||||||
|  |             ix::msleep(10); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         REQUIRE(server.getClients().size() == 1); | ||||||
|  |  | ||||||
|  |         ix::msleep(100); | ||||||
|  |  | ||||||
|  |         webSocketClient.stop(); | ||||||
|  |  | ||||||
|  |         ix::msleep(200); | ||||||
|  |  | ||||||
|  |         // ensure client close is the same as values given | ||||||
|  |         REQUIRE(webSocketClient.getCloseCode() == 1000); | ||||||
|  |         REQUIRE(webSocketClient.getCloseReason() == "Normal closure"); | ||||||
|  |         REQUIRE(webSocketClient.getCloseRemote() == false); | ||||||
|  |  | ||||||
|  |         { | ||||||
|  |             std::lock_guard<std::mutex> lck(mutexWrite); | ||||||
|  |              | ||||||
|  |             // Here we read the code/reason received by the server, and ensure that remote is true | ||||||
|  |             REQUIRE(serverReceivedCloseCode == 1000); | ||||||
|  |             REQUIRE(serverReceivedCloseReason == "Normal closure"); | ||||||
|  |             REQUIRE(serverReceivedCloseRemote == true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Give us 1000ms for the server to notice that clients went away | ||||||
|  |         ix::msleep(1000); | ||||||
|  |         REQUIRE(server.getClients().size() == 0); | ||||||
|  |  | ||||||
|  |         ix::reportWebSocketTraffic(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_CASE("Websocket_client_close_params_given", "[close]") | ||||||
|  | { | ||||||
|  |     SECTION("Make sure that close code and reason was used and sent to server.") | ||||||
|  |     { | ||||||
|  |         ix::setupWebSocketTrafficTrackerCallback(); | ||||||
|  |  | ||||||
|  |         int port = getFreePort(); | ||||||
|  |         ix::WebSocketServer server(port); | ||||||
|  |          | ||||||
|  |         uint16_t serverReceivedCloseCode(0); | ||||||
|  |         bool serverReceivedCloseRemote(false); | ||||||
|  |         std::string serverReceivedCloseReason(""); | ||||||
|  |         std::mutex mutexWrite; | ||||||
|  |  | ||||||
|  |         REQUIRE(startServer(server, serverReceivedCloseCode, serverReceivedCloseReason, serverReceivedCloseRemote, mutexWrite)); | ||||||
|  |  | ||||||
|  |         std::string session = ix::generateSessionId(); | ||||||
|  |         WebSocketClient webSocketClient(port); | ||||||
|  |  | ||||||
|  |         webSocketClient.start(); | ||||||
|  |  | ||||||
|  |         // Wait for all chat instance to be ready | ||||||
|  |         while (true) | ||||||
|  |         { | ||||||
|  |             if (webSocketClient.isReady()) break; | ||||||
|  |             ix::msleep(10); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         REQUIRE(server.getClients().size() == 1); | ||||||
|  |  | ||||||
|  |         ix::msleep(100); | ||||||
|  |  | ||||||
|  |         webSocketClient.stop(4000, "My reason"); | ||||||
|  |  | ||||||
|  |         ix::msleep(500); | ||||||
|  |  | ||||||
|  |         // ensure client close is the same as values given | ||||||
|  |         REQUIRE(webSocketClient.getCloseCode() == 4000); | ||||||
|  |         REQUIRE(webSocketClient.getCloseReason() == "My reason"); | ||||||
|  |         REQUIRE(webSocketClient.getCloseRemote() == false); | ||||||
|  |  | ||||||
|  |         { | ||||||
|  |             std::lock_guard<std::mutex> lck(mutexWrite); | ||||||
|  |              | ||||||
|  |             // Here we read the code/reason received by the server, and ensure that remote is true | ||||||
|  |             REQUIRE(serverReceivedCloseCode == 4000); | ||||||
|  |             REQUIRE(serverReceivedCloseReason == "My reason"); | ||||||
|  |             REQUIRE(serverReceivedCloseRemote == true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Give us 1000ms for the server to notice that clients went away | ||||||
|  |         ix::msleep(1000); | ||||||
|  |         REQUIRE(server.getClients().size() == 0); | ||||||
|  |  | ||||||
|  |         ix::reportWebSocketTraffic(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_CASE("Websocket_server_close", "[close]") | ||||||
|  | { | ||||||
|  |     SECTION("Make sure that close code and reason was read from server.") | ||||||
|  |     { | ||||||
|  |         ix::setupWebSocketTrafficTrackerCallback(); | ||||||
|  |  | ||||||
|  |         int port = getFreePort(); | ||||||
|  |         ix::WebSocketServer server(port); | ||||||
|  |          | ||||||
|  |         uint16_t serverReceivedCloseCode(0); | ||||||
|  |         bool serverReceivedCloseRemote(false); | ||||||
|  |         std::string serverReceivedCloseReason(""); | ||||||
|  |         std::mutex mutexWrite; | ||||||
|  |  | ||||||
|  |         REQUIRE(startServer(server, serverReceivedCloseCode, serverReceivedCloseReason, serverReceivedCloseRemote, mutexWrite)); | ||||||
|  |  | ||||||
|  |         std::string session = ix::generateSessionId(); | ||||||
|  |         WebSocketClient webSocketClient(port); | ||||||
|  |  | ||||||
|  |         webSocketClient.start(); | ||||||
|  |  | ||||||
|  |         // Wait for all chat instance to be ready | ||||||
|  |         while (true) | ||||||
|  |         { | ||||||
|  |             if (webSocketClient.isReady()) break; | ||||||
|  |             ix::msleep(10); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         REQUIRE(server.getClients().size() == 1); | ||||||
|  |  | ||||||
|  |         ix::msleep(200); | ||||||
|  |  | ||||||
|  |         server.stop(); | ||||||
|  |  | ||||||
|  |         ix::msleep(500); | ||||||
|  |  | ||||||
|  |         // ensure client close is the same as values given | ||||||
|  |         REQUIRE(webSocketClient.getCloseCode() == 1000); | ||||||
|  |         REQUIRE(webSocketClient.getCloseReason() == "Normal closure"); | ||||||
|  |         REQUIRE(webSocketClient.getCloseRemote() == true); | ||||||
|  |  | ||||||
|  |         { | ||||||
|  |             std::lock_guard<std::mutex> lck(mutexWrite); | ||||||
|  |              | ||||||
|  |             // Here we read the code/reason received by the server, and ensure that remote is true | ||||||
|  |             REQUIRE(serverReceivedCloseCode == 1000); | ||||||
|  |             REQUIRE(serverReceivedCloseReason == "Normal closure"); | ||||||
|  |             REQUIRE(serverReceivedCloseRemote == false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Give us 1000ms for the server to notice that clients went away | ||||||
|  |         ix::msleep(1000); | ||||||
|  |         REQUIRE(server.getClients().size() == 0); | ||||||
|  |  | ||||||
|  |         ix::reportWebSocketTraffic(); | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user