Compare commits
	
		
			4 Commits
		
	
	
		
			v7.6.5
			...
			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