Compare commits
	
		
			39 Commits
		
	
	
		
			v9.9.0
			...
			feature/zl
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 5036e338c7 | ||
|  | 80fb8cfb59 | ||
|  | 43c0ae0812 | ||
|  | dcc447ec4a | ||
|  | 5127094f0e | ||
|  | 2e3d625c1e | ||
|  | 029289413c | ||
|  | 4d51098c86 | ||
|  | c2b05af022 | ||
|  | e85f975ab0 | ||
|  | dc77d62a5d | ||
|  | 4f41f209a2 | ||
|  | 5940e53d77 | ||
|  | 22dffd5b7e | ||
|  | af2f31045d | ||
|  | 5daa59f9f3 | ||
|  | 2ea9d06a93 | ||
|  | 847fc142d1 | ||
|  | 0388459bd0 | ||
|  | 9a47ec1217 | ||
|  | 45a40c8640 | ||
|  | e34f1c30d6 | ||
|  | c14a4c0e3e | ||
|  | b146e93a3a | ||
|  | 9957ec9724 | ||
|  | 78a42f61bd | ||
|  | e78019dad6 | ||
|  | 0f026c5da2 | ||
|  | c26a2d5d39 | ||
|  | 2798886c0b | ||
|  | ffde283a4b | ||
|  | f7031d0d3e | ||
|  | 595e6c57df | ||
|  | 87709c201e | ||
|  | e70d83ace1 | ||
|  | ca829a3a98 | ||
|  | 26a1e63626 | ||
|  | c98959b895 | ||
|  | baf18648e9 | 
							
								
								
									
										13
									
								
								.github/workflows/unittest_linux.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.github/workflows/unittest_linux.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,13 +0,0 @@ | |||||||
| name: linux |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     paths-ignore: |  | ||||||
|     - 'docs/**' |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   linux: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - name: make test_make |  | ||||||
|       run: make test_make |  | ||||||
							
								
								
									
										15
									
								
								.github/workflows/unittest_mac_tsan_mbedtls.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.github/workflows/unittest_mac_tsan_mbedtls.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,15 +0,0 @@ | |||||||
| name: mac_tsan_mbedtls |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     paths-ignore: |  | ||||||
|     - 'docs/**' |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   mac_tsan_mbedtls: |  | ||||||
|     runs-on: macOS-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - name: install mbedtls |  | ||||||
|       run: brew install mbedtls |  | ||||||
|     - name: make test |  | ||||||
|       run: make test_tsan_mbedtls |  | ||||||
							
								
								
									
										15
									
								
								.github/workflows/unittest_mac_tsan_openssl.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.github/workflows/unittest_mac_tsan_openssl.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,15 +0,0 @@ | |||||||
| name: mac_tsan_openssl |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     paths-ignore: |  | ||||||
|     - 'docs/**' |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   mac_tsan_openssl: |  | ||||||
|     runs-on: macOS-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - name: install openssl |  | ||||||
|       run: brew install openssl@1.1 |  | ||||||
|     - name: make test |  | ||||||
|       run: make test_tsan_openssl |  | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| name: mac_tsan_sectransport |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     paths-ignore: |  | ||||||
|     - 'docs/**' |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   mac_tsan_sectransport: |  | ||||||
|     runs-on: macOS-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - name: make test_tsan |  | ||||||
|       run: make test_tsan |  | ||||||
							
								
								
									
										40
									
								
								.github/workflows/unittest_uwp.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								.github/workflows/unittest_uwp.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,40 +0,0 @@ | |||||||
| name: uwp |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     paths-ignore: |  | ||||||
|     - 'docs/**' |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   uwp: |  | ||||||
|     runs-on: windows-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - uses: seanmiddleditch/gha-setup-vsdevenv@master |  | ||||||
|     - run: | |  | ||||||
|         vcpkg install zlib:x64-uwp |  | ||||||
|     - run: | |  | ||||||
|         mkdir build |  | ||||||
|         cd build |  | ||||||
|         cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DUSE_TEST=1 .. |  | ||||||
|     - run: cmake --build build |  | ||||||
|  |  | ||||||
| # |  | ||||||
| #   Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg |  | ||||||
| # |  | ||||||
| #   windows_openssl: |  | ||||||
| #     runs-on: windows-latest |  | ||||||
| #     steps: |  | ||||||
| #     - uses: actions/checkout@v1 |  | ||||||
| #     - uses: seanmiddleditch/gha-setup-vsdevenv@master |  | ||||||
| #     - run: | |  | ||||||
| #         vcpkg install zlib:x64-windows |  | ||||||
| #         vcpkg install openssl:x64-windows |  | ||||||
| #     - run: | |  | ||||||
| #         mkdir build |  | ||||||
| #         cd build |  | ||||||
| #         cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_OPEN_SSL=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. |  | ||||||
| #     - run: cmake --build build |  | ||||||
| #  |  | ||||||
| #     # Running the unittest does not work, the binary cannot be found |  | ||||||
| #     #- run: ../build/test/ixwebsocket_unittest.exe |  | ||||||
| #     # working-directory: test |  | ||||||
							
								
								
									
										6
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							| @@ -10,10 +10,10 @@ jobs: | |||||||
|     steps: |     steps: | ||||||
|     - uses: actions/checkout@v1 |     - uses: actions/checkout@v1 | ||||||
|     - uses: seanmiddleditch/gha-setup-vsdevenv@master |     - uses: seanmiddleditch/gha-setup-vsdevenv@master | ||||||
|     - run: | |  | ||||||
|         vcpkg install zlib:x64-windows |  | ||||||
|     - run: | |     - run: | | ||||||
|         mkdir build |         mkdir build | ||||||
|         cd build |         cd build | ||||||
|         cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 .. |         cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 .. | ||||||
|     - run: cmake --build build |     - run: cmake --build build | ||||||
|  |     - run: ../build/test/ixwebsocket_unittest.exe | ||||||
|  |       working-directory: test | ||||||
|   | |||||||
| @@ -30,12 +30,15 @@ set( IXWEBSOCKET_SOURCES | |||||||
|     ixwebsocket/IXConnectionState.cpp |     ixwebsocket/IXConnectionState.cpp | ||||||
|     ixwebsocket/IXDNSLookup.cpp |     ixwebsocket/IXDNSLookup.cpp | ||||||
|     ixwebsocket/IXExponentialBackoff.cpp |     ixwebsocket/IXExponentialBackoff.cpp | ||||||
|  |     ixwebsocket/IXGetFreePort.cpp | ||||||
|     ixwebsocket/IXHttp.cpp |     ixwebsocket/IXHttp.cpp | ||||||
|     ixwebsocket/IXHttpClient.cpp |     ixwebsocket/IXHttpClient.cpp | ||||||
|     ixwebsocket/IXHttpServer.cpp |     ixwebsocket/IXHttpServer.cpp | ||||||
|     ixwebsocket/IXNetSystem.cpp |     ixwebsocket/IXNetSystem.cpp | ||||||
|     ixwebsocket/IXSelectInterrupt.cpp |     ixwebsocket/IXSelectInterrupt.cpp | ||||||
|     ixwebsocket/IXSelectInterruptFactory.cpp |     ixwebsocket/IXSelectInterruptFactory.cpp | ||||||
|  |     ixwebsocket/IXSelectInterruptPipe.cpp | ||||||
|  |     ixwebsocket/IXSetThreadName.cpp | ||||||
|     ixwebsocket/IXSocket.cpp |     ixwebsocket/IXSocket.cpp | ||||||
|     ixwebsocket/IXSocketConnect.cpp |     ixwebsocket/IXSocketConnect.cpp | ||||||
|     ixwebsocket/IXSocketFactory.cpp |     ixwebsocket/IXSocketFactory.cpp | ||||||
| @@ -51,6 +54,7 @@ set( IXWEBSOCKET_SOURCES | |||||||
|     ixwebsocket/IXWebSocketPerMessageDeflate.cpp |     ixwebsocket/IXWebSocketPerMessageDeflate.cpp | ||||||
|     ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp |     ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp | ||||||
|     ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp |     ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp | ||||||
|  |     ixwebsocket/IXWebSocketProxyServer.cpp | ||||||
|     ixwebsocket/IXWebSocketServer.cpp |     ixwebsocket/IXWebSocketServer.cpp | ||||||
|     ixwebsocket/IXWebSocketTransport.cpp |     ixwebsocket/IXWebSocketTransport.cpp | ||||||
| ) | ) | ||||||
| @@ -62,6 +66,7 @@ set( IXWEBSOCKET_HEADERS | |||||||
|     ixwebsocket/IXConnectionState.h |     ixwebsocket/IXConnectionState.h | ||||||
|     ixwebsocket/IXDNSLookup.h |     ixwebsocket/IXDNSLookup.h | ||||||
|     ixwebsocket/IXExponentialBackoff.h |     ixwebsocket/IXExponentialBackoff.h | ||||||
|  |     ixwebsocket/IXGetFreePort.h | ||||||
|     ixwebsocket/IXHttp.h |     ixwebsocket/IXHttp.h | ||||||
|     ixwebsocket/IXHttpClient.h |     ixwebsocket/IXHttpClient.h | ||||||
|     ixwebsocket/IXHttpServer.h |     ixwebsocket/IXHttpServer.h | ||||||
| @@ -69,6 +74,7 @@ set( IXWEBSOCKET_HEADERS | |||||||
|     ixwebsocket/IXProgressCallback.h |     ixwebsocket/IXProgressCallback.h | ||||||
|     ixwebsocket/IXSelectInterrupt.h |     ixwebsocket/IXSelectInterrupt.h | ||||||
|     ixwebsocket/IXSelectInterruptFactory.h |     ixwebsocket/IXSelectInterruptFactory.h | ||||||
|  |     ixwebsocket/IXSelectInterruptPipe.h | ||||||
|     ixwebsocket/IXSetThreadName.h |     ixwebsocket/IXSetThreadName.h | ||||||
|     ixwebsocket/IXSocket.h |     ixwebsocket/IXSocket.h | ||||||
|     ixwebsocket/IXSocketConnect.h |     ixwebsocket/IXSocketConnect.h | ||||||
| @@ -93,29 +99,13 @@ set( IXWEBSOCKET_HEADERS | |||||||
|     ixwebsocket/IXWebSocketPerMessageDeflate.h |     ixwebsocket/IXWebSocketPerMessageDeflate.h | ||||||
|     ixwebsocket/IXWebSocketPerMessageDeflateCodec.h |     ixwebsocket/IXWebSocketPerMessageDeflateCodec.h | ||||||
|     ixwebsocket/IXWebSocketPerMessageDeflateOptions.h |     ixwebsocket/IXWebSocketPerMessageDeflateOptions.h | ||||||
|  |     ixwebsocket/IXWebSocketProxyServer.h | ||||||
|     ixwebsocket/IXWebSocketSendInfo.h |     ixwebsocket/IXWebSocketSendInfo.h | ||||||
|     ixwebsocket/IXWebSocketServer.h |     ixwebsocket/IXWebSocketServer.h | ||||||
|     ixwebsocket/IXWebSocketTransport.h |     ixwebsocket/IXWebSocketTransport.h | ||||||
|     ixwebsocket/IXWebSocketVersion.h |     ixwebsocket/IXWebSocketVersion.h | ||||||
| ) | ) | ||||||
|  |  | ||||||
| if (UNIX) |  | ||||||
|     # Linux, Mac, iOS, Android |  | ||||||
|     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptPipe.cpp ) |  | ||||||
|     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptPipe.h ) |  | ||||||
| endif() |  | ||||||
|  |  | ||||||
| # Platform specific code |  | ||||||
| if (APPLE) |  | ||||||
|     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/apple/IXSetThreadName_apple.cpp) |  | ||||||
| elseif (WIN32) |  | ||||||
|     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/windows/IXSetThreadName_windows.cpp) |  | ||||||
| elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") |  | ||||||
|     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/freebsd/IXSetThreadName_freebsd.cpp) |  | ||||||
| else() |  | ||||||
|     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/linux/IXSetThreadName_linux.cpp) |  | ||||||
| endif() |  | ||||||
|  |  | ||||||
| option(USE_TLS "Enable TLS support" FALSE) | option(USE_TLS "Enable TLS support" FALSE) | ||||||
|  |  | ||||||
| if (USE_TLS) | if (USE_TLS) | ||||||
| @@ -200,11 +190,19 @@ if (USE_TLS) | |||||||
|   endif() |   endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | option(USE_ZLIB "Enable zlib support" TRUE) | ||||||
|  |  | ||||||
|  | if (USE_ZLIB) | ||||||
|   # Use ZLIB_ROOT CMake variable if you need to use your own zlib |   # Use ZLIB_ROOT CMake variable if you need to use your own zlib | ||||||
| find_package(ZLIB REQUIRED) |   find_package(ZLIB) | ||||||
|  |   if (ZLIB_FOUND) | ||||||
|     include_directories(${ZLIB_INCLUDE_DIRS}) |     include_directories(${ZLIB_INCLUDE_DIRS}) | ||||||
|     target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES}) |     target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES}) | ||||||
|  |  | ||||||
|  |     target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB) | ||||||
|  |   endif() | ||||||
|  | endif() | ||||||
|  |  | ||||||
| if (WIN32) | if (WIN32) | ||||||
|   target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi) |   target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi) | ||||||
|   add_definitions(-D_CRT_SECURE_NO_WARNINGS) |   add_definitions(-D_CRT_SECURE_NO_WARNINGS) | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								docker/Dockerfile.ubuntu_groovy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								docker/Dockerfile.ubuntu_groovy
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | # Build time | ||||||
|  | FROM ubuntu:groovy as build | ||||||
|  |  | ||||||
|  | ENV DEBIAN_FRONTEND noninteractive | ||||||
|  | RUN apt-get update | ||||||
|  |  | ||||||
|  | RUN apt-get -y install g++ libssl-dev libz-dev make python ninja-build | ||||||
|  | RUN apt-get -y install cmake | ||||||
|  | RUN apt-get -y install gdb | ||||||
|  |  | ||||||
|  | COPY . /opt | ||||||
|  | WORKDIR /opt | ||||||
|  |  | ||||||
|  | # | ||||||
|  | # To use the container interactively for debugging/building | ||||||
|  | # 1. Build with | ||||||
|  | #    CMD ["ls"] | ||||||
|  | # 2. Run with | ||||||
|  | #    docker run --entrypoint sh -it docker-game-eng-dev.addsrv.com/ws:9.10.6  | ||||||
|  | # | ||||||
|  |  | ||||||
|  | RUN ["make", "test"] | ||||||
|  | # CMD ["ls"] | ||||||
| @@ -1,6 +1,70 @@ | |||||||
| # Changelog | # Changelog | ||||||
| All changes to this project will be documented in this file. | All changes to this project will be documented in this file. | ||||||
|  |  | ||||||
|  | ## [10.1.1] - 2020-07-29 | ||||||
|  |  | ||||||
|  | (websocket client) onProgressCallback not called for short messages on a websocket (fix #233) | ||||||
|  |  | ||||||
|  | ## [10.1.0] - 2020-07-29 | ||||||
|  |  | ||||||
|  | (websocket client) heartbeat is not sent at the requested frequency (fix #232) | ||||||
|  |  | ||||||
|  | ## [10.0.3] - 2020-07-28 | ||||||
|  |  | ||||||
|  | compiler warning fixes | ||||||
|  |  | ||||||
|  | ## [10.0.2] - 2020-07-28 | ||||||
|  |  | ||||||
|  | (ixcobra) CobraConnection: unsubscribe from all subscriptions when disconnecting | ||||||
|  |  | ||||||
|  | ## [10.0.1] - 2020-07-27 | ||||||
|  |  | ||||||
|  | (socket utility) move ix::getFreePort to ixwebsocket library | ||||||
|  |  | ||||||
|  | ## [10.0.0] - 2020-07-25 | ||||||
|  |  | ||||||
|  | (ixwebsocket server) change legacy api with 2 nested callbacks, so that the first api takes a weak_ptr<WebSocket> as its first argument | ||||||
|  |  | ||||||
|  | ## [9.10.7] - 2020-07-25 | ||||||
|  |  | ||||||
|  | (ixwebsocket) add WebSocketProxyServer, from ws. Still need to make the interface better. | ||||||
|  |  | ||||||
|  | ## [9.10.6] - 2020-07-24 | ||||||
|  |  | ||||||
|  | (ws) port broadcast_server sub-command to the new server API | ||||||
|  |  | ||||||
|  | ## [9.10.5] - 2020-07-24 | ||||||
|  |  | ||||||
|  | (unittest) port most unittests to the new server API | ||||||
|  |  | ||||||
|  | ## [9.10.3] - 2020-07-24 | ||||||
|  |  | ||||||
|  | (ws) port ws transfer to the new server API | ||||||
|  |  | ||||||
|  | ## [9.10.2] - 2020-07-24 | ||||||
|  |  | ||||||
|  | (websocket client) reset WebSocketTransport onClose callback in the WebSocket destructor | ||||||
|  |  | ||||||
|  | ## [9.10.1] - 2020-07-24 | ||||||
|  |  | ||||||
|  | (websocket server) reset client websocket callback when the connection is closed | ||||||
|  |  | ||||||
|  | ## [9.10.0] - 2020-07-23 | ||||||
|  |  | ||||||
|  | (websocket server) add a new simpler API to handle client connections / that API does not trigger a memory leak while the previous one did | ||||||
|  |  | ||||||
|  | ## [9.9.3] - 2020-07-17 | ||||||
|  |  | ||||||
|  | (build) merge platform specific files which were used to have different implementations for setting a thread name into a single file, to make it easier to include every source files and build the ixwebsocket library (fix #226) | ||||||
|  |  | ||||||
|  | ## [9.9.2] - 2020-07-10 | ||||||
|  |  | ||||||
|  | (socket server) bump default max connection count from 32 to 128 | ||||||
|  |  | ||||||
|  | ## [9.9.1] - 2020-07-10 | ||||||
|  |  | ||||||
|  | (snake) implement super simple stream sql expression support in snake server | ||||||
|  |  | ||||||
| ## [9.9.0] - 2020-07-08 | ## [9.9.0] - 2020-07-08 | ||||||
|  |  | ||||||
| (socket+websocket+http+redis+snake servers) expose the remote ip and remote port when a new connection is made | (socket+websocket+http+redis+snake servers) expose the remote ip and remote port when a new connection is made | ||||||
|   | |||||||
| @@ -246,6 +246,10 @@ uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries(); | |||||||
|  |  | ||||||
| ## WebSocket server API | ## WebSocket server API | ||||||
|  |  | ||||||
|  | ### Legacy api | ||||||
|  |  | ||||||
|  | This api was actually changed to take a weak_ptr<WebSocket> as the first argument to setOnConnectionCallback ; previously it would take a shared_ptr<WebSocket> which was creating cycles and then memory leaks problems. | ||||||
|  |  | ||||||
| ```cpp | ```cpp | ||||||
| #include <ixwebsocket/IXWebSocketServer.h> | #include <ixwebsocket/IXWebSocketServer.h> | ||||||
|  |  | ||||||
| @@ -256,13 +260,16 @@ uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries(); | |||||||
| ix::WebSocketServer server(port); | ix::WebSocketServer server(port); | ||||||
|  |  | ||||||
| server.setOnConnectionCallback( | server.setOnConnectionCallback( | ||||||
|     [&server](std::shared_ptr<WebSocket> webSocket, |     [&server](std::weak_ptr<WebSocket> webSocket, | ||||||
|               std::shared_ptr<ConnectionState> connectionState, |               std::shared_ptr<ConnectionState> connectionState, | ||||||
|               std::unique_ptr<ConnectionInfo> connectionInfo) |               std::unique_ptr<ConnectionInfo> connectionInfo) | ||||||
|     { |     { | ||||||
|         std::cout << "Remote ip: " << connectionInfo->remoteIp << std::endl; |         std::cout << "Remote ip: " << connectionInfo->remoteIp << std::endl; | ||||||
|  |  | ||||||
|         webSocket->setOnMessageCallback( |         auto ws = webSocket.lock(); | ||||||
|  |         if (ws) | ||||||
|  |         { | ||||||
|  |             ws->setOnMessageCallback( | ||||||
|                 [webSocket, connectionState, &server](const ix::WebSocketMessagePtr msg) |                 [webSocket, connectionState, &server](const ix::WebSocketMessagePtr msg) | ||||||
|                 { |                 { | ||||||
|                     if (msg->type == ix::WebSocketMessageType::Open) |                     if (msg->type == ix::WebSocketMessageType::Open) | ||||||
| @@ -290,7 +297,12 @@ server.setOnConnectionCallback( | |||||||
|                         // All connected clients are available in an std::set. See the broadcast cpp example. |                         // All connected clients are available in an std::set. See the broadcast cpp example. | ||||||
|                         // Second parameter tells whether we are sending the message in binary or text mode. |                         // Second parameter tells whether we are sending the message in binary or text mode. | ||||||
|                         // Here we send it in the same mode as it was received. |                         // Here we send it in the same mode as it was received. | ||||||
|                     webSocket->send(msg->str, msg->binary); |                         auto ws = webSocket.lock(); | ||||||
|  |                         if (ws) | ||||||
|  |                         { | ||||||
|  |                             ws->send(msg->str, msg->binary); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         ); |         ); | ||||||
| @@ -312,6 +324,74 @@ server.wait(); | |||||||
|  |  | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  | ### New api | ||||||
|  |  | ||||||
|  | The new API does not require to use 2 nested callbacks, which is a bit annoying. The real fix is that there was a memory leak due to a shared_ptr cycle, due to passing down a shared_ptr<WebSocket> down to the callbacks. | ||||||
|  |  | ||||||
|  | The webSocket reference is guaranteed to be always valid ; by design the callback will never be invoked with a null webSocket object. | ||||||
|  |  | ||||||
|  | ```cpp | ||||||
|  | #include <ixwebsocket/IXWebSocketServer.h> | ||||||
|  |  | ||||||
|  | ... | ||||||
|  |  | ||||||
|  | // Run a server on localhost at a given port. | ||||||
|  | // Bound host name, max connections and listen backlog can also be passed in as parameters. | ||||||
|  | ix::WebSocketServer server(port); | ||||||
|  |  | ||||||
|  | server.setOnClientMessageCallback(std::shared_ptr<ConnectionState> connectionState, | ||||||
|  |                                   ConnectionInfo& connectionInfo, | ||||||
|  |                                   WebSocket& webSocket, | ||||||
|  |                                   const WebSocketMessagePtr& msg) | ||||||
|  | { | ||||||
|  |     // The ConnectionInfo object contains information about the connection, | ||||||
|  |     // at this point only the client ip address and the port. | ||||||
|  |     std::cout << "Remote ip: " << connectionInfo.remoteIp << std::endl; | ||||||
|  |  | ||||||
|  |     if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|  |     { | ||||||
|  |         std::cout << "New connection" << std::endl; | ||||||
|  |  | ||||||
|  |         // A connection state object is available, and has a default id | ||||||
|  |         // You can subclass ConnectionState and pass an alternate factory | ||||||
|  |         // to override it. It is useful if you want to store custom | ||||||
|  |         // attributes per connection (authenticated bool flag, attributes, etc...) | ||||||
|  |         std::cout << "id: " << connectionState->getId() << std::endl; | ||||||
|  |  | ||||||
|  |         // The uri the client did connect to. | ||||||
|  |         std::cout << "Uri: " << msg->openInfo.uri << std::endl; | ||||||
|  |  | ||||||
|  |         std::cout << "Headers:" << std::endl; | ||||||
|  |         for (auto it : msg->openInfo.headers) | ||||||
|  |         { | ||||||
|  |             std::cout << it.first << ": " << it.second << std::endl; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|  |     { | ||||||
|  |         // For an echo server, we just send back to the client whatever was received by the server | ||||||
|  |         // All connected clients are available in an std::set. See the broadcast cpp example. | ||||||
|  |         // Second parameter tells whether we are sending the message in binary or text mode. | ||||||
|  |         // Here we send it in the same mode as it was received. | ||||||
|  |         webSocket.send(msg->str, msg->binary); | ||||||
|  |     } | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | auto res = server.listen(); | ||||||
|  | if (!res.first) | ||||||
|  | { | ||||||
|  |     // Error handling | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Run the server in the background. Server can be stoped by calling server.stop() | ||||||
|  | server.start(); | ||||||
|  |  | ||||||
|  | // Block until server.stop() is called. | ||||||
|  | server.wait(); | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  |  | ||||||
| ## HTTP client API | ## HTTP client API | ||||||
|  |  | ||||||
| ```cpp | ```cpp | ||||||
|   | |||||||
| @@ -111,6 +111,12 @@ namespace ix | |||||||
|  |  | ||||||
|     void CobraConnection::disconnect() |     void CobraConnection::disconnect() | ||||||
|     { |     { | ||||||
|  |         auto subscriptionIds = getSubscriptionsIds(); | ||||||
|  |         for (auto&& subscriptionId : subscriptionIds) | ||||||
|  |         { | ||||||
|  |             unsubscribe(subscriptionId); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         _authenticated = false; |         _authenticated = false; | ||||||
|         _webSocket->stop(); |         _webSocket->stop(); | ||||||
|     } |     } | ||||||
| @@ -614,6 +620,18 @@ namespace ix | |||||||
|         _webSocket->send(pdu.toStyledString()); |         _webSocket->send(pdu.toStyledString()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     std::vector<std::string> CobraConnection::getSubscriptionsIds() | ||||||
|  |     { | ||||||
|  |         std::vector<std::string> subscriptionIds; | ||||||
|  |         std::lock_guard<std::mutex> lock(_cbsMutex); | ||||||
|  |  | ||||||
|  |         for (auto&& it : _cbs) | ||||||
|  |         { | ||||||
|  |             subscriptionIds.push_back(it.first); | ||||||
|  |         } | ||||||
|  |         return subscriptionIds; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // |     // | ||||||
|     // Enqueue strategy drops old messages when we are at full capacity |     // Enqueue strategy drops old messages when we are at full capacity | ||||||
|     // |     // | ||||||
|   | |||||||
| @@ -163,6 +163,9 @@ namespace ix | |||||||
|         /// Tells whether the internal queue is empty or not |         /// Tells whether the internal queue is empty or not | ||||||
|         bool isQueueEmpty(); |         bool isQueueEmpty(); | ||||||
|  |  | ||||||
|  |         /// Retrieve all subscriptions ids | ||||||
|  |         std::vector<std::string> getSubscriptionsIds(); | ||||||
|  |  | ||||||
|         /// |         /// | ||||||
|         /// Member variables |         /// Member variables | ||||||
|         /// |         /// | ||||||
|   | |||||||
| @@ -7,12 +7,14 @@ set (IXSNAKE_SOURCES | |||||||
|     ixsnake/IXSnakeServer.cpp |     ixsnake/IXSnakeServer.cpp | ||||||
|     ixsnake/IXSnakeProtocol.cpp |     ixsnake/IXSnakeProtocol.cpp | ||||||
|     ixsnake/IXAppConfig.cpp |     ixsnake/IXAppConfig.cpp | ||||||
|  |     ixsnake/IXStreamSql.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| set (IXSNAKE_HEADERS | set (IXSNAKE_HEADERS | ||||||
|     ixsnake/IXSnakeServer.h |     ixsnake/IXSnakeServer.h | ||||||
|     ixsnake/IXSnakeProtocol.h |     ixsnake/IXSnakeProtocol.h | ||||||
|     ixsnake/IXAppConfig.h |     ixsnake/IXAppConfig.h | ||||||
|  |     ixsnake/IXStreamSql.h | ||||||
| ) | ) | ||||||
|  |  | ||||||
| add_library(ixsnake STATIC | add_library(ixsnake STATIC | ||||||
|   | |||||||
| @@ -26,6 +26,12 @@ namespace snake | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         auto roles = appConfig.apps[appkey]["roles"]; |         auto roles = appConfig.apps[appkey]["roles"]; | ||||||
|  |         if (roles.count(role) == 0) | ||||||
|  |         { | ||||||
|  |             std::cerr << "Missing role " << role << std::endl; | ||||||
|  |             return std::string(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         auto channel = roles[role]["secret"]; |         auto channel = roles[role]["secret"]; | ||||||
|         return channel; |         return channel; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -33,6 +33,9 @@ namespace snake | |||||||
|         // Misc |         // Misc | ||||||
|         bool verbose; |         bool verbose; | ||||||
|         bool disablePong; |         bool disablePong; | ||||||
|  |  | ||||||
|  |         // If non empty, every published message gets republished to a given channel | ||||||
|  |         std::string republishChannel; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     bool isAppKeyValid(const AppConfig& appConfig, std::string appkey); |     bool isAppKeyValid(const AppConfig& appConfig, std::string appkey); | ||||||
|   | |||||||
| @@ -7,15 +7,21 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <ixredis/IXRedisClient.h> | #include <ixredis/IXRedisClient.h> | ||||||
| #include <future> | #include <thread> | ||||||
| #include <ixwebsocket/IXConnectionState.h> | #include <ixwebsocket/IXConnectionState.h> | ||||||
| #include <string> | #include <string> | ||||||
|  | #include "IXStreamSql.h" | ||||||
|  |  | ||||||
| namespace snake | namespace snake | ||||||
| { | { | ||||||
|     class SnakeConnectionState : public ix::ConnectionState |     class SnakeConnectionState : public ix::ConnectionState | ||||||
|     { |     { | ||||||
|     public: |     public: | ||||||
|  |         virtual ~SnakeConnectionState() | ||||||
|  |         { | ||||||
|  |             stopSubScriptionThread(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         std::string getNonce() |         std::string getNonce() | ||||||
|         { |         { | ||||||
|             return _nonce; |             return _nonce; | ||||||
| @@ -30,6 +36,7 @@ namespace snake | |||||||
|         { |         { | ||||||
|             return _appkey; |             return _appkey; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         void setAppkey(const std::string& appkey) |         void setAppkey(const std::string& appkey) | ||||||
|         { |         { | ||||||
|             _appkey = appkey; |             _appkey = appkey; | ||||||
| @@ -39,6 +46,7 @@ namespace snake | |||||||
|         { |         { | ||||||
|             return _role; |             return _role; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         void setRole(const std::string& role) |         void setRole(const std::string& role) | ||||||
|         { |         { | ||||||
|             _role = role; |             _role = role; | ||||||
| @@ -49,7 +57,24 @@ namespace snake | |||||||
|             return _redisClient; |             return _redisClient; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         std::future<void> fut; |         void stopSubScriptionThread() | ||||||
|  |         { | ||||||
|  |             if (subscriptionThread.joinable()) | ||||||
|  |             { | ||||||
|  |                 subscriptionRedisClient.stop(); | ||||||
|  |                 subscriptionThread.join(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // We could make those accessible through methods | ||||||
|  |         std::thread subscriptionThread; | ||||||
|  |         std::string appChannel; | ||||||
|  |         std::string subscriptionId; | ||||||
|  |         uint64_t id; | ||||||
|  |         std::unique_ptr<StreamSql> streamSql; | ||||||
|  |         ix::RedisClient subscriptionRedisClient; | ||||||
|  |         ix::RedisClient::OnRedisSubscribeResponseCallback onRedisSubscribeResponseCallback; | ||||||
|  |         ix::RedisClient::OnRedisSubscribeCallback onRedisSubscribeCallback; | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         std::string _nonce; |         std::string _nonce; | ||||||
|   | |||||||
| @@ -18,21 +18,22 @@ | |||||||
| namespace snake | namespace snake | ||||||
| { | { | ||||||
|     void handleError(const std::string& action, |     void handleError(const std::string& action, | ||||||
|                      std::shared_ptr<ix::WebSocket> ws, |                      ix::WebSocket& ws, | ||||||
|                      nlohmann::json pdu, |                      uint64_t pduId, | ||||||
|                      const std::string& errMsg) |                      const std::string& errMsg) | ||||||
|     { |     { | ||||||
|         std::string actionError(action); |         std::string actionError(action); | ||||||
|         actionError += "/error"; |         actionError += "/error"; | ||||||
|  |  | ||||||
|         nlohmann::json response = { |         nlohmann::json response = { | ||||||
|             {"action", actionError}, {"id", pdu.value("id", 1)}, {"body", {{"reason", errMsg}}}}; |             {"action", actionError}, {"id", pduId}, {"body", {{"reason", errMsg}}}}; | ||||||
|         ws->sendText(response.dump()); |         ws.sendText(response.dump()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void handleHandshake(std::shared_ptr<SnakeConnectionState> state, |     void handleHandshake(std::shared_ptr<SnakeConnectionState> state, | ||||||
|                          std::shared_ptr<ix::WebSocket> ws, |                          ix::WebSocket& ws, | ||||||
|                          const nlohmann::json& pdu) |                          const nlohmann::json& pdu, | ||||||
|  |                          uint64_t pduId) | ||||||
|     { |     { | ||||||
|         std::string role = pdu["body"]["data"]["role"]; |         std::string role = pdu["body"]["data"]["role"]; | ||||||
|  |  | ||||||
| @@ -41,7 +42,7 @@ namespace snake | |||||||
|  |  | ||||||
|         nlohmann::json response = { |         nlohmann::json response = { | ||||||
|             {"action", "auth/handshake/ok"}, |             {"action", "auth/handshake/ok"}, | ||||||
|             {"id", pdu.value("id", 1)}, |             {"id", pduId}, | ||||||
|             {"body", |             {"body", | ||||||
|              { |              { | ||||||
|                  {"data", {{"nonce", state->getNonce()}, {"connection_id", state->getId()}}}, |                  {"data", {{"nonce", state->getNonce()}, {"connection_id", state->getId()}}}, | ||||||
| @@ -49,13 +50,14 @@ namespace snake | |||||||
|  |  | ||||||
|         auto serializedResponse = response.dump(); |         auto serializedResponse = response.dump(); | ||||||
|  |  | ||||||
|         ws->sendText(serializedResponse); |         ws.sendText(serializedResponse); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void handleAuth(std::shared_ptr<SnakeConnectionState> state, |     void handleAuth(std::shared_ptr<SnakeConnectionState> state, | ||||||
|                     std::shared_ptr<ix::WebSocket> ws, |                     ix::WebSocket& ws, | ||||||
|                     const AppConfig& appConfig, |                     const AppConfig& appConfig, | ||||||
|                     const nlohmann::json& pdu) |                     const nlohmann::json& pdu, | ||||||
|  |                     uint64_t pduId) | ||||||
|     { |     { | ||||||
|         auto secret = getRoleSecret(appConfig, state->appkey(), state->role()); |         auto secret = getRoleSecret(appConfig, state->appkey(), state->role()); | ||||||
|  |  | ||||||
| @@ -63,9 +65,9 @@ namespace snake | |||||||
|         { |         { | ||||||
|             nlohmann::json response = { |             nlohmann::json response = { | ||||||
|                 {"action", "auth/authenticate/error"}, |                 {"action", "auth/authenticate/error"}, | ||||||
|                 {"id", pdu.value("id", 1)}, |                 {"id", pduId}, | ||||||
|                 {"body", {{"error", "authentication_failed"}, {"reason", "invalid secret"}}}}; |                 {"body", {{"error", "authentication_failed"}, {"reason", "invalid secret"}}}}; | ||||||
|             ws->sendText(response.dump()); |             ws.sendText(response.dump()); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -79,19 +81,21 @@ namespace snake | |||||||
|                 {"action", "auth/authenticate/error"}, |                 {"action", "auth/authenticate/error"}, | ||||||
|                 {"id", pdu.value("id", 1)}, |                 {"id", pdu.value("id", 1)}, | ||||||
|                 {"body", {{"error", "authentication_failed"}, {"reason", "invalid hash"}}}}; |                 {"body", {{"error", "authentication_failed"}, {"reason", "invalid hash"}}}}; | ||||||
|             ws->sendText(response.dump()); |             ws.sendText(response.dump()); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         nlohmann::json response = { |         nlohmann::json response = { | ||||||
|             {"action", "auth/authenticate/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}}; |             {"action", "auth/authenticate/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}}; | ||||||
|  |  | ||||||
|         ws->sendText(response.dump()); |         ws.sendText(response.dump()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void handlePublish(std::shared_ptr<SnakeConnectionState> state, |     void handlePublish(std::shared_ptr<SnakeConnectionState> state, | ||||||
|                        std::shared_ptr<ix::WebSocket> ws, |                        ix::WebSocket& ws, | ||||||
|                        const nlohmann::json& pdu) |                        const AppConfig& appConfig, | ||||||
|  |                        const nlohmann::json& pdu, | ||||||
|  |                        uint64_t pduId) | ||||||
|     { |     { | ||||||
|         std::vector<std::string> channels; |         std::vector<std::string> channels; | ||||||
|  |  | ||||||
| @@ -111,10 +115,16 @@ namespace snake | |||||||
|         { |         { | ||||||
|             std::stringstream ss; |             std::stringstream ss; | ||||||
|             ss << "Missing channels or channel field in publish data"; |             ss << "Missing channels or channel field in publish data"; | ||||||
|             handleError("rtm/publish", ws, pdu, ss.str()); |             handleError("rtm/publish", ws, pduId, ss.str()); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // add an extra channel if the config has one specified | ||||||
|  |         if (!appConfig.republishChannel.empty()) | ||||||
|  |         { | ||||||
|  |             channels.push_back(appConfig.republishChannel); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         for (auto&& channel : channels) |         for (auto&& channel : channels) | ||||||
|         { |         { | ||||||
|             std::stringstream ss; |             std::stringstream ss; | ||||||
| @@ -125,7 +135,7 @@ namespace snake | |||||||
|             { |             { | ||||||
|                 std::stringstream ss; |                 std::stringstream ss; | ||||||
|                 ss << "Cannot publish to redis host " << errMsg; |                 ss << "Cannot publish to redis host " << errMsg; | ||||||
|                 handleError("rtm/publish", ws, pdu, ss.str()); |                 handleError("rtm/publish", ws, pduId, ss.str()); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -133,26 +143,27 @@ namespace snake | |||||||
|         nlohmann::json response = { |         nlohmann::json response = { | ||||||
|             {"action", "rtm/publish/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}}; |             {"action", "rtm/publish/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}}; | ||||||
|  |  | ||||||
|         ws->sendText(response.dump()); |         ws.sendText(response.dump()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |     // | ||||||
|     // FIXME: this is not cancellable. We should be able to cancel the redis subscription |     // FIXME: this is not cancellable. We should be able to cancel the redis subscription | ||||||
|     // |     // | ||||||
|     void handleRedisSubscription(std::shared_ptr<SnakeConnectionState> state, |     void handleSubscribe(std::shared_ptr<SnakeConnectionState> state, | ||||||
|                                  std::shared_ptr<ix::WebSocket> ws, |                          ix::WebSocket& ws, | ||||||
|                          const AppConfig& appConfig, |                          const AppConfig& appConfig, | ||||||
|                                  const nlohmann::json& pdu) |                          const nlohmann::json& pdu, | ||||||
|  |                          uint64_t pduId) | ||||||
|     { |     { | ||||||
|         std::string channel = pdu["body"]["channel"]; |         std::string channel = pdu["body"]["channel"]; | ||||||
|         std::string subscriptionId = channel; |         state->subscriptionId = channel; | ||||||
|  |  | ||||||
|         std::stringstream ss; |         std::stringstream ss; | ||||||
|         ss << state->appkey() << "::" << channel; |         ss << state->appkey() << "::" << channel; | ||||||
|  |  | ||||||
|         std::string appChannel(ss.str()); |         state->appChannel = ss.str(); | ||||||
|  |  | ||||||
|         ix::RedisClient redisClient; |         ix::RedisClient& redisClient = state->subscriptionRedisClient; | ||||||
|         int port = appConfig.redisPort; |         int port = appConfig.redisPort; | ||||||
|  |  | ||||||
|         auto urls = appConfig.redisHosts; |         auto urls = appConfig.redisHosts; | ||||||
| @@ -163,7 +174,7 @@ namespace snake | |||||||
|         { |         { | ||||||
|             std::stringstream ss; |             std::stringstream ss; | ||||||
|             ss << "Cannot connect to redis host " << hostname << ":" << port; |             ss << "Cannot connect to redis host " << hostname << ":" << port; | ||||||
|             handleError("rtm/subscribe", ws, pdu, ss.str()); |             handleError("rtm/subscribe", ws, pduId, ss.str()); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -175,80 +186,90 @@ namespace snake | |||||||
|             { |             { | ||||||
|                 std::stringstream ss; |                 std::stringstream ss; | ||||||
|                 ss << "Cannot authenticated to redis"; |                 ss << "Cannot authenticated to redis"; | ||||||
|                 handleError("rtm/subscribe", ws, pdu, ss.str()); |                 handleError("rtm/subscribe", ws, pduId, ss.str()); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         int id = 0; |         std::string filterStr; | ||||||
|         auto callback = [ws, &id, &subscriptionId](const std::string& messageStr) { |         if (pdu["body"].find("filter") != pdu["body"].end()) | ||||||
|  |         { | ||||||
|  |             std::string filterStr = pdu["body"]["filter"]; | ||||||
|  |         } | ||||||
|  |         state->streamSql = std::make_unique<StreamSql>(filterStr); | ||||||
|  |         state->id = 0; | ||||||
|  |         state->onRedisSubscribeCallback = [&ws, state](const std::string& messageStr) { | ||||||
|             auto msg = nlohmann::json::parse(messageStr); |             auto msg = nlohmann::json::parse(messageStr); | ||||||
|  |  | ||||||
|             msg = msg["body"]["message"]; |             msg = msg["body"]["message"]; | ||||||
|  |  | ||||||
|  |             if (state->streamSql->valid() && !state->streamSql->match(msg)) | ||||||
|  |             { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             nlohmann::json response = { |             nlohmann::json response = { | ||||||
|                 {"action", "rtm/subscription/data"}, |                 {"action", "rtm/subscription/data"}, | ||||||
|                 {"id", id++}, |                 {"id", state->id++}, | ||||||
|                 {"body", |                 {"body", | ||||||
|                  {{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}}; |                  {{"subscription_id", state->subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}}; | ||||||
|  |  | ||||||
|             ws->sendText(response.dump()); |             ws.sendText(response.dump()); | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         auto responseCallback = [ws, pdu, &subscriptionId](const std::string& redisResponse) { |         state->onRedisSubscribeResponseCallback = [&ws, state, pduId](const std::string& redisResponse) { | ||||||
|             std::stringstream ss; |             std::stringstream ss; | ||||||
|             ss << "Redis Response: " << redisResponse << "..."; |             ss << "Redis Response: " << redisResponse << "..."; | ||||||
|             ix::CoreLogger::log(ss.str().c_str()); |             ix::CoreLogger::log(ss.str().c_str()); | ||||||
|  |  | ||||||
|             // Success |             // Success | ||||||
|             nlohmann::json response = {{"action", "rtm/subscribe/ok"}, |             nlohmann::json response = {{"action", "rtm/subscribe/ok"}, | ||||||
|                                        {"id", pdu.value("id", 1)}, |                                        {"id", pduId}, | ||||||
|                                        {"body", {{"subscription_id", subscriptionId}}}}; |                                        {"body", {{"subscription_id", state->subscriptionId}}}}; | ||||||
|             ws->sendText(response.dump()); |             ws.sendText(response.dump()); | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         { |         { | ||||||
|             std::stringstream ss; |             std::stringstream ss; | ||||||
|             ss << "Subscribing to " << appChannel << "..."; |             ss << "Subscribing to " << state->appChannel << "..."; | ||||||
|             ix::CoreLogger::log(ss.str().c_str()); |             ix::CoreLogger::log(ss.str().c_str()); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!redisClient.subscribe(appChannel, responseCallback, callback)) |         auto subscription = [&redisClient, state, &ws, pduId] | ||||||
|  |         { | ||||||
|  |             if (!redisClient.subscribe(state->appChannel,  | ||||||
|  |                                        state->onRedisSubscribeResponseCallback, | ||||||
|  |                                        state->onRedisSubscribeCallback)) | ||||||
|             { |             { | ||||||
|                 std::stringstream ss; |                 std::stringstream ss; | ||||||
|             ss << "Error subscribing to channel " << appChannel; |                 ss << "Error subscribing to channel " << state->appChannel; | ||||||
|             handleError("rtm/subscribe", ws, pdu, ss.str()); |                 handleError("rtm/subscribe", ws, pduId, ss.str()); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|     } |         }; | ||||||
|  |  | ||||||
|     void handleSubscribe(std::shared_ptr<SnakeConnectionState> state, |         state->subscriptionThread = std::thread(subscription); | ||||||
|                          std::shared_ptr<ix::WebSocket> ws, |  | ||||||
|                          const AppConfig& appConfig, |  | ||||||
|                          const nlohmann::json& pdu) |  | ||||||
|     { |  | ||||||
|         state->fut = |  | ||||||
|             std::async(std::launch::async, handleRedisSubscription, state, ws, appConfig, pdu); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void handleUnSubscribe(std::shared_ptr<SnakeConnectionState> state, |     void handleUnSubscribe(std::shared_ptr<SnakeConnectionState> state, | ||||||
|                            std::shared_ptr<ix::WebSocket> ws, |                            ix::WebSocket& ws, | ||||||
|                            const nlohmann::json& pdu) |                            const nlohmann::json& pdu, | ||||||
|  |                            uint64_t pduId) | ||||||
|     { |     { | ||||||
|         // extract subscription_id |         // extract subscription_id | ||||||
|         auto body = pdu["body"]; |         auto body = pdu["body"]; | ||||||
|         auto subscriptionId = body["subscription_id"]; |         auto subscriptionId = body["subscription_id"]; | ||||||
|  |  | ||||||
|         state->redisClient().stop(); |         state->stopSubScriptionThread(); | ||||||
|  |  | ||||||
|         nlohmann::json response = {{"action", "rtm/unsubscribe/ok"}, |         nlohmann::json response = {{"action", "rtm/unsubscribe/ok"}, | ||||||
|                                    {"id", pdu.value("id", 1)}, |                                    {"id", pduId}, | ||||||
|                                    {"body", {{"subscription_id", subscriptionId}}}}; |                                    {"body", {{"subscription_id", subscriptionId}}}}; | ||||||
|         ws->sendText(response.dump()); |         ws.sendText(response.dump()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void processCobraMessage(std::shared_ptr<SnakeConnectionState> state, |     void processCobraMessage(std::shared_ptr<SnakeConnectionState> state, | ||||||
|                              std::shared_ptr<ix::WebSocket> ws, |                              ix::WebSocket& ws, | ||||||
|                              const AppConfig& appConfig, |                              const AppConfig& appConfig, | ||||||
|                              const std::string& str) |                              const std::string& str) | ||||||
|     { |     { | ||||||
| @@ -263,31 +284,32 @@ namespace snake | |||||||
|             ss << "malformed json pdu: " << e.what() << " -> " << str << ""; |             ss << "malformed json pdu: " << e.what() << " -> " << str << ""; | ||||||
|  |  | ||||||
|             nlohmann::json response = {{"body", {{"error", "invalid_json"}, {"reason", ss.str()}}}}; |             nlohmann::json response = {{"body", {{"error", "invalid_json"}, {"reason", ss.str()}}}}; | ||||||
|             ws->sendText(response.dump()); |             ws.sendText(response.dump()); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         auto action = pdu["action"]; |         auto action = pdu["action"]; | ||||||
|  |         uint64_t pduId = pdu.value("id", 1); | ||||||
|  |  | ||||||
|         if (action == "auth/handshake") |         if (action == "auth/handshake") | ||||||
|         { |         { | ||||||
|             handleHandshake(state, ws, pdu); |             handleHandshake(state, ws, pdu, pduId); | ||||||
|         } |         } | ||||||
|         else if (action == "auth/authenticate") |         else if (action == "auth/authenticate") | ||||||
|         { |         { | ||||||
|             handleAuth(state, ws, appConfig, pdu); |             handleAuth(state, ws, appConfig, pdu, pduId); | ||||||
|         } |         } | ||||||
|         else if (action == "rtm/publish") |         else if (action == "rtm/publish") | ||||||
|         { |         { | ||||||
|             handlePublish(state, ws, pdu); |             handlePublish(state, ws, appConfig, pdu, pduId); | ||||||
|         } |         } | ||||||
|         else if (action == "rtm/subscribe") |         else if (action == "rtm/subscribe") | ||||||
|         { |         { | ||||||
|             handleSubscribe(state, ws, appConfig, pdu); |             handleSubscribe(state, ws, appConfig, pdu, pduId); | ||||||
|         } |         } | ||||||
|         else if (action == "rtm/unsubscribe") |         else if (action == "rtm/unsubscribe") | ||||||
|         { |         { | ||||||
|             handleUnSubscribe(state, ws, pdu); |             handleUnSubscribe(state, ws, pdu, pduId); | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ namespace snake | |||||||
|     struct AppConfig; |     struct AppConfig; | ||||||
|  |  | ||||||
|     void processCobraMessage(std::shared_ptr<SnakeConnectionState> state, |     void processCobraMessage(std::shared_ptr<SnakeConnectionState> state, | ||||||
|                              std::shared_ptr<ix::WebSocket> ws, |                              ix::WebSocket& ws, | ||||||
|                              const AppConfig& appConfig, |                              const AppConfig& appConfig, | ||||||
|                              const std::string& str); |                              const std::string& str); | ||||||
| } // namespace snake | } // namespace snake | ||||||
|   | |||||||
| @@ -59,16 +59,17 @@ namespace snake | |||||||
|         }; |         }; | ||||||
|         _server.setConnectionStateFactory(factory); |         _server.setConnectionStateFactory(factory); | ||||||
|  |  | ||||||
|         _server.setOnConnectionCallback( |         _server.setOnClientMessageCallback( | ||||||
|             [this](std::shared_ptr<ix::WebSocket> webSocket, |             [this](std::shared_ptr<ix::ConnectionState> connectionState, | ||||||
|                    std::shared_ptr<ix::ConnectionState> connectionState, |                    ix::ConnectionInfo& connectionInfo, | ||||||
|                    std::unique_ptr<ix::ConnectionInfo> connectionInfo) { |                    ix::WebSocket& webSocket, | ||||||
|  |                    const ix::WebSocketMessagePtr& msg) { | ||||||
|                 auto state = std::dynamic_pointer_cast<SnakeConnectionState>(connectionState); |                 auto state = std::dynamic_pointer_cast<SnakeConnectionState>(connectionState); | ||||||
|                 auto remoteIp = connectionInfo->remoteIp; |                 auto remoteIp = connectionInfo.remoteIp; | ||||||
|              |              | ||||||
|                 webSocket->setOnMessageCallback( |  | ||||||
|                     [this, webSocket, state, remoteIp](const ix::WebSocketMessagePtr& msg) { |  | ||||||
|                 std::stringstream ss; |                 std::stringstream ss; | ||||||
|  |                 ss << "[" << state->getId() << "] "; | ||||||
|  |  | ||||||
|                 ix::LogLevel logLevel = ix::LogLevel::Debug; |                 ix::LogLevel logLevel = ix::LogLevel::Debug; | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Open) |                 if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|                 { |                 { | ||||||
| @@ -114,13 +115,12 @@ namespace snake | |||||||
|                 } |                 } | ||||||
|                 else if (msg->type == ix::WebSocketMessageType::Message) |                 else if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|                 { |                 { | ||||||
|                             ss << "Received " << msg->wireSize << " bytes" << std::endl; |                     ss << "Received " << msg->wireSize << " bytes" << " " << msg->str << std::endl; | ||||||
|                     processCobraMessage(state, webSocket, _appConfig, msg->str); |                     processCobraMessage(state, webSocket, _appConfig, msg->str); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 ix::CoreLogger::log(ss.str().c_str(), logLevel); |                 ix::CoreLogger::log(ss.str().c_str(), logLevel); | ||||||
|         }); |         }); | ||||||
|             }); |  | ||||||
|  |  | ||||||
|         auto res = _server.listen(); |         auto res = _server.listen(); | ||||||
|         if (!res.first) |         if (!res.first) | ||||||
|   | |||||||
							
								
								
									
										63
									
								
								ixsnake/ixsnake/IXStreamSql.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								ixsnake/ixsnake/IXStreamSql.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | /* | ||||||
|  |  *  IXStreamSql.cpp | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved. | ||||||
|  |  * | ||||||
|  |  *  Super simple hacked up version of a stream sql expression, | ||||||
|  |  *  that only supports non nested field evaluation | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "IXStreamSql.h" | ||||||
|  | #include <sstream> | ||||||
|  | #include <iostream> | ||||||
|  |  | ||||||
|  | namespace snake | ||||||
|  | { | ||||||
|  |     StreamSql::StreamSql(const std::string& sqlFilter) | ||||||
|  |         : _valid(false) | ||||||
|  |     { | ||||||
|  |         std::string token; | ||||||
|  |         std::stringstream tokenStream(sqlFilter); | ||||||
|  |         std::vector<std::string> tokens; | ||||||
|  |  | ||||||
|  |         // Split by ' ' | ||||||
|  |         while (std::getline(tokenStream, token, ' ')) | ||||||
|  |         { | ||||||
|  |             tokens.push_back(token); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         _valid = tokens.size() == 8; | ||||||
|  |         if (!_valid) return; | ||||||
|  |  | ||||||
|  |         _field = tokens[5]; | ||||||
|  |         _operator = tokens[6]; | ||||||
|  |         _value = tokens[7]; | ||||||
|  |  | ||||||
|  |         // remove single quotes | ||||||
|  |         _value = _value.substr(1, _value.size() - 2); | ||||||
|  |  | ||||||
|  |         if (_operator == "LIKE") | ||||||
|  |         { | ||||||
|  |             _value = _value.substr(1, _value.size() - 2); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool StreamSql::valid() const | ||||||
|  |     { | ||||||
|  |         return _valid; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool StreamSql::match(const nlohmann::json& msg) | ||||||
|  |     { | ||||||
|  |         if (!_valid) return false; | ||||||
|  |  | ||||||
|  |         if (msg.find(_field) == msg.end()) | ||||||
|  |         { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         std::string value = msg[_field]; | ||||||
|  |         return value == _value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } // namespace snake | ||||||
							
								
								
									
										29
									
								
								ixsnake/ixsnake/IXStreamSql.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								ixsnake/ixsnake/IXStreamSql.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | /* | ||||||
|  |  *  IXStreamSql.h | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  | #include "nlohmann/json.hpp" | ||||||
|  |  | ||||||
|  | namespace snake | ||||||
|  | { | ||||||
|  |     class StreamSql | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         StreamSql(const std::string& sqlFilter = std::string()); | ||||||
|  |         ~StreamSql() = default; | ||||||
|  |  | ||||||
|  |         bool match(const nlohmann::json& msg); | ||||||
|  |         bool valid() const; | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         std::string _field; | ||||||
|  |         std::string _operator; | ||||||
|  |         std::string _value; | ||||||
|  |         bool _valid; | ||||||
|  |     }; | ||||||
|  | } | ||||||
| @@ -16,7 +16,10 @@ | |||||||
| #include <random> | #include <random> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
| #include <zlib.h> | #include <zlib.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
| @@ -174,11 +177,13 @@ namespace ix | |||||||
|         ss << verb << " " << path << " HTTP/1.1\r\n"; |         ss << verb << " " << path << " HTTP/1.1\r\n"; | ||||||
|         ss << "Host: " << host << "\r\n"; |         ss << "Host: " << host << "\r\n"; | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         if (args->compress) |         if (args->compress) | ||||||
|         { |         { | ||||||
|             ss << "Accept-Encoding: gzip" |             ss << "Accept-Encoding: gzip" | ||||||
|                << "\r\n"; |                << "\r\n"; | ||||||
|         } |         } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|         // Append extra headers |         // Append extra headers | ||||||
|         for (auto&& it : args->extraHeaders) |         for (auto&& it : args->extraHeaders) | ||||||
| @@ -495,6 +500,7 @@ namespace ix | |||||||
|  |  | ||||||
|         downloadSize = payload.size(); |         downloadSize = payload.size(); | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         // If the content was compressed with gzip, decode it |         // If the content was compressed with gzip, decode it | ||||||
|         if (headers["Content-Encoding"] == "gzip") |         if (headers["Content-Encoding"] == "gzip") | ||||||
|         { |         { | ||||||
| @@ -513,6 +519,7 @@ namespace ix | |||||||
|             } |             } | ||||||
|             payload = decompressedPayload; |             payload = decompressedPayload; | ||||||
|         } |         } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|         return std::make_shared<HttpResponse>(code, |         return std::make_shared<HttpResponse>(code, | ||||||
|                                               description, |                                               description, | ||||||
| @@ -672,6 +679,7 @@ namespace ix | |||||||
|         return ss.str(); |         return ss.str(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|     bool HttpClient::gzipInflate(const std::string& in, std::string& out) |     bool HttpClient::gzipInflate(const std::string& in, std::string& out) | ||||||
|     { |     { | ||||||
|         z_stream inflateState; |         z_stream inflateState; | ||||||
| @@ -716,6 +724,7 @@ namespace ix | |||||||
|         inflateEnd(&inflateState); |         inflateEnd(&inflateState); | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args) |     void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -90,7 +90,9 @@ namespace ix | |||||||
|     private: |     private: | ||||||
|         void log(const std::string& msg, HttpRequestArgsPtr args); |         void log(const std::string& msg, HttpRequestArgsPtr args); | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         bool gzipInflate(const std::string& in, std::string& out); |         bool gzipInflate(const std::string& in, std::string& out); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|         // Async API background thread runner |         // Async API background thread runner | ||||||
|         void run(); |         void run(); | ||||||
|   | |||||||
| @@ -13,7 +13,10 @@ | |||||||
| #include <fstream> | #include <fstream> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
| #include <zlib.h> | #include <zlib.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
| namespace | namespace | ||||||
| { | { | ||||||
| @@ -41,6 +44,7 @@ namespace | |||||||
|         return std::make_pair(res.first, std::string(vec.begin(), vec.end())); |         return std::make_pair(res.first, std::string(vec.begin(), vec.end())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|     std::string gzipCompress(const std::string& str) |     std::string gzipCompress(const std::string& str) | ||||||
|     { |     { | ||||||
|         z_stream zs; // z_stream is zlib's control structure |         z_stream zs; // z_stream is zlib's control structure | ||||||
| @@ -83,6 +87,7 @@ namespace | |||||||
|  |  | ||||||
|         return outstring; |         return outstring; | ||||||
|     } |     } | ||||||
|  | #endif | ||||||
| } // namespace | } // namespace | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| @@ -125,9 +130,8 @@ namespace ix | |||||||
|  |  | ||||||
|         if (std::get<0>(ret)) |         if (std::get<0>(ret)) | ||||||
|         { |         { | ||||||
|             auto response = _onConnectionCallback(std::get<2>(ret), |             auto response = | ||||||
|                                                   connectionState, |                 _onConnectionCallback(std::get<2>(ret), connectionState, std::move(connectionInfo)); | ||||||
|                                                   std::move(connectionInfo)); |  | ||||||
|             if (!Http::sendResponse(response, socket)) |             if (!Http::sendResponse(response, socket)) | ||||||
|             { |             { | ||||||
|                 logError("Cannot send response"); |                 logError("Cannot send response"); | ||||||
| @@ -169,12 +173,14 @@ namespace ix | |||||||
|  |  | ||||||
|                 std::string content = res.second; |                 std::string content = res.second; | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|                 std::string acceptEncoding = request->headers["Accept-encoding"]; |                 std::string acceptEncoding = request->headers["Accept-encoding"]; | ||||||
|                 if (acceptEncoding == "*" || acceptEncoding.find("gzip") != std::string::npos) |                 if (acceptEncoding == "*" || acceptEncoding.find("gzip") != std::string::npos) | ||||||
|                 { |                 { | ||||||
|                     content = gzipCompress(content); |                     content = gzipCompress(content); | ||||||
|                     headers["Content-Encoding"] = "gzip"; |                     headers["Content-Encoding"] = "gzip"; | ||||||
|                 } |                 } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|                 // Log request |                 // Log request | ||||||
|                 std::stringstream ss; |                 std::stringstream ss; | ||||||
| @@ -203,8 +209,7 @@ namespace ix | |||||||
|         // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections |         // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections | ||||||
|         // |         // | ||||||
|         setOnConnectionCallback( |         setOnConnectionCallback( | ||||||
|             [this, |             [this, redirectUrl](HttpRequestPtr request, | ||||||
|              redirectUrl](HttpRequestPtr request, |  | ||||||
|                                 std::shared_ptr<ConnectionState> /*connectionState*/, |                                 std::shared_ptr<ConnectionState> /*connectionState*/, | ||||||
|                                 std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr { |                                 std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr { | ||||||
|                 WebSocketHttpHeaders headers; |                 WebSocketHttpHeaders headers; | ||||||
|   | |||||||
| @@ -5,8 +5,10 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| // | // | ||||||
| // On macOS we use UNIX pipes to wake up select. | // On UNIX we use pipes to wake up select. There is no way to do that | ||||||
|  | // on Windows so this file is compiled out on Windows. | ||||||
| // | // | ||||||
|  | #ifndef _WIN32 | ||||||
|  |  | ||||||
| #include "IXSelectInterruptPipe.h" | #include "IXSelectInterruptPipe.h" | ||||||
|  |  | ||||||
| @@ -144,3 +146,5 @@ namespace ix | |||||||
|         return _fildes[kPipeReadIndex]; |         return _fildes[kPipeReadIndex]; | ||||||
|     } |     } | ||||||
| } // namespace ix | } // namespace ix | ||||||
|  |  | ||||||
|  | #endif // !_WIN32 | ||||||
|   | |||||||
							
								
								
									
										81
									
								
								ixwebsocket/IXSetThreadName.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								ixwebsocket/IXSetThreadName.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | /* | ||||||
|  |  *  IXSetThreadName.cpp | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2018 2020 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  | #include "IXSetThreadName.h" | ||||||
|  |  | ||||||
|  | // unix systems | ||||||
|  | #if defined(__APPLE__) || defined(__linux__) || defined(BSD) | ||||||
|  | #include <pthread.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | // freebsd needs this header as well | ||||||
|  | #if defined(BSD) | ||||||
|  | #include <pthread_np.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | // Windows | ||||||
|  | #ifdef _WIN32 | ||||||
|  | #include <Windows.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace ix | ||||||
|  | { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     const DWORD MS_VC_EXCEPTION = 0x406D1388; | ||||||
|  |  | ||||||
|  | #pragma pack(push, 8) | ||||||
|  |     typedef struct tagTHREADNAME_INFO | ||||||
|  |     { | ||||||
|  |         DWORD dwType;     // Must be 0x1000. | ||||||
|  |         LPCSTR szName;    // Pointer to name (in user addr space). | ||||||
|  |         DWORD dwThreadID; // Thread ID (-1=caller thread). | ||||||
|  |         DWORD dwFlags;    // Reserved for future use, must be zero. | ||||||
|  |     } THREADNAME_INFO; | ||||||
|  | #pragma pack(pop) | ||||||
|  |  | ||||||
|  |     void SetThreadName(DWORD dwThreadID, const char* threadName) | ||||||
|  |     { | ||||||
|  |         THREADNAME_INFO info; | ||||||
|  |         info.dwType = 0x1000; | ||||||
|  |         info.szName = threadName; | ||||||
|  |         info.dwThreadID = dwThreadID; | ||||||
|  |         info.dwFlags = 0; | ||||||
|  |  | ||||||
|  |         __try | ||||||
|  |         { | ||||||
|  |             RaiseException( | ||||||
|  |                 MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*) &info); | ||||||
|  |         } | ||||||
|  |         __except (EXCEPTION_EXECUTE_HANDLER) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     void setThreadName(const std::string& name) | ||||||
|  |     { | ||||||
|  | #if defined(__APPLE__) | ||||||
|  |         // | ||||||
|  |         // Apple reserves 16 bytes for its thread names | ||||||
|  |         // Notice that the Apple version of pthread_setname_np | ||||||
|  |         // does not take a pthread_t argument | ||||||
|  |         // | ||||||
|  |         pthread_setname_np(name.substr(0, 63).c_str()); | ||||||
|  | #elif defined(__linux__) | ||||||
|  |         // | ||||||
|  |         // Linux only reserves 16 bytes for its thread names | ||||||
|  |         // See prctl and PR_SET_NAME property in | ||||||
|  |         // http://man7.org/linux/man-pages/man2/prctl.2.html | ||||||
|  |         // | ||||||
|  |         pthread_setname_np(pthread_self(), name.substr(0, 15).c_str()); | ||||||
|  | #elif defined(_WIN32) | ||||||
|  |         SetThreadName(-1, name.c_str()); | ||||||
|  | #elif defined(BSD) | ||||||
|  |         pthread_set_name_np(pthread_self(), name.substr(0, 15).c_str()); | ||||||
|  | #else | ||||||
|  |         // ... assert here ? | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  | } // namespace ix | ||||||
| @@ -22,7 +22,7 @@ namespace ix | |||||||
|     const int SocketServer::kDefaultPort(8080); |     const int SocketServer::kDefaultPort(8080); | ||||||
|     const std::string SocketServer::kDefaultHost("127.0.0.1"); |     const std::string SocketServer::kDefaultHost("127.0.0.1"); | ||||||
|     const int SocketServer::kDefaultTcpBacklog(5); |     const int SocketServer::kDefaultTcpBacklog(5); | ||||||
|     const size_t SocketServer::kDefaultMaxConnections(32); |     const size_t SocketServer::kDefaultMaxConnections(128); | ||||||
|     const int SocketServer::kDefaultAddressFamily(AF_INET); |     const int SocketServer::kDefaultAddressFamily(AF_INET); | ||||||
|  |  | ||||||
|     SocketServer::SocketServer( |     SocketServer::SocketServer( | ||||||
| @@ -379,10 +379,13 @@ namespace ix | |||||||
|  |  | ||||||
|             // Launch the handleConnection work asynchronously in its own thread. |             // Launch the handleConnection work asynchronously in its own thread. | ||||||
|             std::lock_guard<std::mutex> lock(_connectionsThreadsMutex); |             std::lock_guard<std::mutex> lock(_connectionsThreadsMutex); | ||||||
|             _connectionsThreads.push_back(std::make_pair( |             _connectionsThreads.push_back( | ||||||
|  |                 std::make_pair(connectionState, | ||||||
|  |                                std::thread(&SocketServer::handleConnection, | ||||||
|  |                                            this, | ||||||
|  |                                            std::move(socket), | ||||||
|                                            connectionState, |                                            connectionState, | ||||||
|                 std::thread( |                                            std::move(connectionInfo)))); | ||||||
|                     &SocketServer::handleConnection, this, std::move(socket), connectionState, std::move(connectionInfo)))); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,7 +8,9 @@ | |||||||
|  |  | ||||||
| #include "IXWebSocketVersion.h" | #include "IXWebSocketVersion.h" | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
| #include <zlib.h> | #include <zlib.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
| // Platform name | // Platform name | ||||||
| #if defined(_WIN32) | #if defined(_WIN32) | ||||||
| @@ -77,8 +79,10 @@ namespace ix | |||||||
|         ss << " nossl"; |         ss << " nossl"; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         // Zlib version |         // Zlib version | ||||||
|         ss << " zlib " << ZLIB_VERSION; |         ss << " zlib " << ZLIB_VERSION; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|         return ss.str(); |         return ss.str(); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -46,6 +46,7 @@ namespace ix | |||||||
|     WebSocket::~WebSocket() |     WebSocket::~WebSocket() | ||||||
|     { |     { | ||||||
|         stop(); |         stop(); | ||||||
|  |         _ws.setOnCloseCallback(nullptr); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void WebSocket::setUrl(const std::string& url) |     void WebSocket::setUrl(const std::string& url) | ||||||
|   | |||||||
| @@ -28,21 +28,26 @@ namespace ix | |||||||
|     WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor() |     WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor() | ||||||
|         : _compressBufferSize(kBufferSize) |         : _compressBufferSize(kBufferSize) | ||||||
|     { |     { | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         memset(&_deflateState, 0, sizeof(_deflateState)); |         memset(&_deflateState, 0, sizeof(_deflateState)); | ||||||
|  |  | ||||||
|         _deflateState.zalloc = Z_NULL; |         _deflateState.zalloc = Z_NULL; | ||||||
|         _deflateState.zfree = Z_NULL; |         _deflateState.zfree = Z_NULL; | ||||||
|         _deflateState.opaque = Z_NULL; |         _deflateState.opaque = Z_NULL; | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     WebSocketPerMessageDeflateCompressor::~WebSocketPerMessageDeflateCompressor() |     WebSocketPerMessageDeflateCompressor::~WebSocketPerMessageDeflateCompressor() | ||||||
|     { |     { | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         deflateEnd(&_deflateState); |         deflateEnd(&_deflateState); | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits, |     bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits, | ||||||
|                                                     bool clientNoContextTakeOver) |                                                     bool clientNoContextTakeOver) | ||||||
|     { |     { | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         int ret = deflateInit2(&_deflateState, |         int ret = deflateInit2(&_deflateState, | ||||||
|                                Z_DEFAULT_COMPRESSION, |                                Z_DEFAULT_COMPRESSION, | ||||||
|                                Z_DEFLATED, |                                Z_DEFLATED, | ||||||
| @@ -57,6 +62,9 @@ namespace ix | |||||||
|         _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH; |         _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH; | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
|  | #else | ||||||
|  |         return false; | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     template<typename T> |     template<typename T> | ||||||
| @@ -96,6 +104,7 @@ namespace ix | |||||||
|     template<typename T, typename S> |     template<typename T, typename S> | ||||||
|     bool WebSocketPerMessageDeflateCompressor::compressData(const T& in, S& out) |     bool WebSocketPerMessageDeflateCompressor::compressData(const T& in, S& out) | ||||||
|     { |     { | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         // |         // | ||||||
|         // 7.2.1.  Compression |         // 7.2.1.  Compression | ||||||
|         // |         // | ||||||
| @@ -152,6 +161,9 @@ namespace ix | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
|  | #else | ||||||
|  |         return false; | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |     // | ||||||
| @@ -160,6 +172,7 @@ namespace ix | |||||||
|     WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor() |     WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor() | ||||||
|         : _compressBufferSize(kBufferSize) |         : _compressBufferSize(kBufferSize) | ||||||
|     { |     { | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         memset(&_inflateState, 0, sizeof(_inflateState)); |         memset(&_inflateState, 0, sizeof(_inflateState)); | ||||||
|  |  | ||||||
|         _inflateState.zalloc = Z_NULL; |         _inflateState.zalloc = Z_NULL; | ||||||
| @@ -167,16 +180,20 @@ namespace ix | |||||||
|         _inflateState.opaque = Z_NULL; |         _inflateState.opaque = Z_NULL; | ||||||
|         _inflateState.avail_in = 0; |         _inflateState.avail_in = 0; | ||||||
|         _inflateState.next_in = Z_NULL; |         _inflateState.next_in = Z_NULL; | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     WebSocketPerMessageDeflateDecompressor::~WebSocketPerMessageDeflateDecompressor() |     WebSocketPerMessageDeflateDecompressor::~WebSocketPerMessageDeflateDecompressor() | ||||||
|     { |     { | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         inflateEnd(&_inflateState); |         inflateEnd(&_inflateState); | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits, |     bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits, | ||||||
|                                                       bool clientNoContextTakeOver) |                                                       bool clientNoContextTakeOver) | ||||||
|     { |     { | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         int ret = inflateInit2(&_inflateState, -1 * inflateBits); |         int ret = inflateInit2(&_inflateState, -1 * inflateBits); | ||||||
|  |  | ||||||
|         if (ret != Z_OK) return false; |         if (ret != Z_OK) return false; | ||||||
| @@ -186,10 +203,14 @@ namespace ix | |||||||
|         _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH; |         _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH; | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
|  | #else | ||||||
|  |         return false; | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in, std::string& out) |     bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in, std::string& out) | ||||||
|     { |     { | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         // |         // | ||||||
|         // 7.2.2.  Decompression |         // 7.2.2.  Decompression | ||||||
|         // |         // | ||||||
| @@ -226,5 +247,8 @@ namespace ix | |||||||
|         } while (_inflateState.avail_out == 0); |         } while (_inflateState.avail_out == 0); | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
|  | #else | ||||||
|  |         return false; | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -6,7 +6,9 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
| #include "zlib.h" | #include "zlib.h" | ||||||
|  | #endif | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| @@ -34,7 +36,10 @@ namespace ix | |||||||
|         int _flush; |         int _flush; | ||||||
|         size_t _compressBufferSize; |         size_t _compressBufferSize; | ||||||
|         std::unique_ptr<unsigned char[]> _compressBuffer; |         std::unique_ptr<unsigned char[]> _compressBuffer; | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         z_stream _deflateState; |         z_stream _deflateState; | ||||||
|  | #endif | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     class WebSocketPerMessageDeflateDecompressor |     class WebSocketPerMessageDeflateDecompressor | ||||||
| @@ -50,7 +55,10 @@ namespace ix | |||||||
|         int _flush; |         int _flush; | ||||||
|         size_t _compressBufferSize; |         size_t _compressBufferSize; | ||||||
|         std::unique_ptr<unsigned char[]> _compressBuffer; |         std::unique_ptr<unsigned char[]> _compressBuffer; | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         z_stream _inflateState; |         z_stream _inflateState; | ||||||
|  | #endif | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -61,6 +61,7 @@ namespace ix | |||||||
|         _clientMaxWindowBits = kDefaultClientMaxWindowBits; |         _clientMaxWindowBits = kDefaultClientMaxWindowBits; | ||||||
|         _serverMaxWindowBits = kDefaultServerMaxWindowBits; |         _serverMaxWindowBits = kDefaultServerMaxWindowBits; | ||||||
|  |  | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         // Split by ; |         // Split by ; | ||||||
|         std::string token; |         std::string token; | ||||||
|         std::stringstream tokenStream(extension); |         std::stringstream tokenStream(extension); | ||||||
| @@ -112,6 +113,7 @@ namespace ix | |||||||
|                 sanitizeClientMaxWindowBits(); |                 sanitizeClientMaxWindowBits(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void WebSocketPerMessageDeflateOptions::sanitizeClientMaxWindowBits() |     void WebSocketPerMessageDeflateOptions::sanitizeClientMaxWindowBits() | ||||||
| @@ -126,6 +128,7 @@ namespace ix | |||||||
|  |  | ||||||
|     std::string WebSocketPerMessageDeflateOptions::generateHeader() |     std::string WebSocketPerMessageDeflateOptions::generateHeader() | ||||||
|     { |     { | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         std::stringstream ss; |         std::stringstream ss; | ||||||
|         ss << "Sec-WebSocket-Extensions: permessage-deflate"; |         ss << "Sec-WebSocket-Extensions: permessage-deflate"; | ||||||
|  |  | ||||||
| @@ -138,11 +141,18 @@ namespace ix | |||||||
|         ss << "\r\n"; |         ss << "\r\n"; | ||||||
|  |  | ||||||
|         return ss.str(); |         return ss.str(); | ||||||
|  | #else | ||||||
|  |         return std::string(); | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool WebSocketPerMessageDeflateOptions::enabled() const |     bool WebSocketPerMessageDeflateOptions::enabled() const | ||||||
|     { |     { | ||||||
|  | #ifdef IXWEBSOCKET_USE_ZLIB | ||||||
|         return _enabled; |         return _enabled; | ||||||
|  | #else | ||||||
|  |         return false; | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool WebSocketPerMessageDeflateOptions::getClientNoContextTakeover() const |     bool WebSocketPerMessageDeflateOptions::getClientNoContextTakeover() const | ||||||
|   | |||||||
							
								
								
									
										123
									
								
								ixwebsocket/IXWebSocketProxyServer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								ixwebsocket/IXWebSocketProxyServer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | |||||||
|  | /* | ||||||
|  |  *  IXWebSocketProxyServer.cpp | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "IXWebSocketProxyServer.h" | ||||||
|  |  | ||||||
|  | #include "IXWebSocketServer.h" | ||||||
|  | #include <sstream> | ||||||
|  |  | ||||||
|  | namespace ix | ||||||
|  | { | ||||||
|  |     class ProxyConnectionState : public ix::ConnectionState | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         ProxyConnectionState() | ||||||
|  |             : _connected(false) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         ix::WebSocket& webSocket() | ||||||
|  |         { | ||||||
|  |             return _serverWebSocket; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bool isConnected() | ||||||
|  |         { | ||||||
|  |             return _connected; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         void setConnected() | ||||||
|  |         { | ||||||
|  |             _connected = true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         ix::WebSocket _serverWebSocket; | ||||||
|  |         bool _connected; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     int websocket_proxy_server_main(int port, | ||||||
|  |                                     const std::string& hostname, | ||||||
|  |                                     const ix::SocketTLSOptions& tlsOptions, | ||||||
|  |                                     const std::string& remoteUrl, | ||||||
|  |                                     bool /*verbose*/) | ||||||
|  |     { | ||||||
|  |         ix::WebSocketServer server(port, hostname); | ||||||
|  |         server.setTLSOptions(tlsOptions); | ||||||
|  |  | ||||||
|  |         auto factory = []() -> std::shared_ptr<ix::ConnectionState> { | ||||||
|  |             return std::make_shared<ProxyConnectionState>(); | ||||||
|  |         }; | ||||||
|  |         server.setConnectionStateFactory(factory); | ||||||
|  |  | ||||||
|  |         server.setOnConnectionCallback([remoteUrl](std::weak_ptr<ix::WebSocket> webSocket, | ||||||
|  |                                                    std::shared_ptr<ConnectionState> connectionState, | ||||||
|  |                                                    std::unique_ptr<ConnectionInfo> connectionInfo) { | ||||||
|  |             auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState); | ||||||
|  |             auto remoteIp = connectionInfo->remoteIp; | ||||||
|  |  | ||||||
|  |             // Server connection | ||||||
|  |             state->webSocket().setOnMessageCallback( | ||||||
|  |                 [webSocket, state, remoteIp](const WebSocketMessagePtr& msg) { | ||||||
|  |                     if (msg->type == ix::WebSocketMessageType::Close) | ||||||
|  |                     { | ||||||
|  |                         state->setTerminated(); | ||||||
|  |                     } | ||||||
|  |                     else if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|  |                     { | ||||||
|  |                         auto ws = webSocket.lock(); | ||||||
|  |                         if (ws) | ||||||
|  |                         { | ||||||
|  |                             ws->send(msg->str, msg->binary); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |             // Client connection | ||||||
|  |             auto ws = webSocket.lock(); | ||||||
|  |             if (ws) | ||||||
|  |             { | ||||||
|  |                 ws->setOnMessageCallback([state, remoteUrl](const WebSocketMessagePtr& msg) { | ||||||
|  |                     if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|  |                     { | ||||||
|  |                         // Connect to the 'real' server | ||||||
|  |                         std::string url(remoteUrl); | ||||||
|  |                         url += msg->openInfo.uri; | ||||||
|  |                         state->webSocket().setUrl(url); | ||||||
|  |                         state->webSocket().disableAutomaticReconnection(); | ||||||
|  |                         state->webSocket().start(); | ||||||
|  |  | ||||||
|  |                         // we should sleep here for a bit until we've established the | ||||||
|  |                         // connection with the remote server | ||||||
|  |                         while (state->webSocket().getReadyState() != ReadyState::Open) | ||||||
|  |                         { | ||||||
|  |                             std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     else if (msg->type == ix::WebSocketMessageType::Close) | ||||||
|  |                     { | ||||||
|  |                         state->webSocket().close(msg->closeInfo.code, msg->closeInfo.reason); | ||||||
|  |                     } | ||||||
|  |                     else if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|  |                     { | ||||||
|  |                         state->webSocket().send(msg->str, msg->binary); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         auto res = server.listen(); | ||||||
|  |         if (!res.first) | ||||||
|  |         { | ||||||
|  |             return 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         server.start(); | ||||||
|  |         server.wait(); | ||||||
|  |  | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } // namespace ix | ||||||
							
								
								
									
										20
									
								
								ixwebsocket/IXWebSocketProxyServer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								ixwebsocket/IXWebSocketProxyServer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | /* | ||||||
|  |  *  IXWebSocketProxyServer.h | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "IXSocketTLSOptions.h" | ||||||
|  | #include <cstdint> | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | namespace ix | ||||||
|  | { | ||||||
|  |     int websocket_proxy_server_main(int port, | ||||||
|  |                                     const std::string& hostname, | ||||||
|  |                                     const ix::SocketTLSOptions& tlsOptions, | ||||||
|  |                                     const std::string& remoteUrl, | ||||||
|  |                                     bool verbose); | ||||||
|  | } // namespace ix | ||||||
| @@ -71,6 +71,11 @@ namespace ix | |||||||
|         _onConnectionCallback = callback; |         _onConnectionCallback = callback; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void WebSocketServer::setOnClientMessageCallback(const OnClientMessageCallback& callback) | ||||||
|  |     { | ||||||
|  |         _onClientMessageCallback = callback; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     void WebSocketServer::handleConnection(std::unique_ptr<Socket> socket, |     void WebSocketServer::handleConnection(std::unique_ptr<Socket> socket, | ||||||
|                                            std::shared_ptr<ConnectionState> connectionState, |                                            std::shared_ptr<ConnectionState> connectionState, | ||||||
|                                            std::unique_ptr<ConnectionInfo> connectionInfo) |                                            std::unique_ptr<ConnectionInfo> connectionInfo) | ||||||
| @@ -78,7 +83,26 @@ namespace ix | |||||||
|         setThreadName("WebSocketServer::" + connectionState->getId()); |         setThreadName("WebSocketServer::" + connectionState->getId()); | ||||||
|  |  | ||||||
|         auto webSocket = std::make_shared<WebSocket>(); |         auto webSocket = std::make_shared<WebSocket>(); | ||||||
|  |         if (_onConnectionCallback) | ||||||
|  |         { | ||||||
|             _onConnectionCallback(webSocket, connectionState, std::move(connectionInfo)); |             _onConnectionCallback(webSocket, connectionState, std::move(connectionInfo)); | ||||||
|  |         } | ||||||
|  |         else if (_onClientMessageCallback) | ||||||
|  |         { | ||||||
|  |             webSocket->setOnMessageCallback( | ||||||
|  |                 [this, &ws = *webSocket.get(), connectionState, &ci = *connectionInfo.get()]( | ||||||
|  |                     const WebSocketMessagePtr& msg) { | ||||||
|  |                     _onClientMessageCallback(connectionState, ci, ws, msg); | ||||||
|  |                 }); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             logError( | ||||||
|  |                 "WebSocketServer Application developer error: No server callback is registerered."); | ||||||
|  |             logError("Missing call to setOnConnectionCallback or setOnClientMessageCallback."); | ||||||
|  |             connectionState->setTerminated(); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         webSocket->disableAutomaticReconnection(); |         webSocket->disableAutomaticReconnection(); | ||||||
|  |  | ||||||
| @@ -112,6 +136,8 @@ namespace ix | |||||||
|             logError(ss.str()); |             logError(ss.str()); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         webSocket->setOnMessageCallback(nullptr); | ||||||
|  |  | ||||||
|         // Remove this client from our client set |         // Remove this client from our client set | ||||||
|         { |         { | ||||||
|             std::lock_guard<std::mutex> lock(_clientsMutex); |             std::lock_guard<std::mutex> lock(_clientsMutex); | ||||||
|   | |||||||
| @@ -23,9 +23,15 @@ namespace ix | |||||||
|     { |     { | ||||||
|     public: |     public: | ||||||
|         using OnConnectionCallback = |         using OnConnectionCallback = | ||||||
|             std::function<void(std::shared_ptr<WebSocket>, std::shared_ptr<ConnectionState>, |             std::function<void(std::weak_ptr<WebSocket>, | ||||||
|  |                                std::shared_ptr<ConnectionState>, | ||||||
|                                std::unique_ptr<ConnectionInfo> connectionInfo)>; |                                std::unique_ptr<ConnectionInfo> connectionInfo)>; | ||||||
|  |  | ||||||
|  |         using OnClientMessageCallback = std::function<void(std::shared_ptr<ConnectionState>, | ||||||
|  |                                                            ConnectionInfo&, | ||||||
|  |                                                            WebSocket&, | ||||||
|  |                                                            const WebSocketMessagePtr&)>; | ||||||
|  |  | ||||||
|         WebSocketServer(int port = SocketServer::kDefaultPort, |         WebSocketServer(int port = SocketServer::kDefaultPort, | ||||||
|                         const std::string& host = SocketServer::kDefaultHost, |                         const std::string& host = SocketServer::kDefaultHost, | ||||||
|                         int backlog = SocketServer::kDefaultTcpBacklog, |                         int backlog = SocketServer::kDefaultTcpBacklog, | ||||||
| @@ -40,6 +46,7 @@ namespace ix | |||||||
|         void disablePerMessageDeflate(); |         void disablePerMessageDeflate(); | ||||||
|  |  | ||||||
|         void setOnConnectionCallback(const OnConnectionCallback& callback); |         void setOnConnectionCallback(const OnConnectionCallback& callback); | ||||||
|  |         void setOnClientMessageCallback(const OnClientMessageCallback& callback); | ||||||
|  |  | ||||||
|         // Get all the connected clients |         // Get all the connected clients | ||||||
|         std::set<std::shared_ptr<WebSocket>> getClients(); |         std::set<std::shared_ptr<WebSocket>> getClients(); | ||||||
| @@ -53,6 +60,7 @@ namespace ix | |||||||
|         bool _enablePerMessageDeflate; |         bool _enablePerMessageDeflate; | ||||||
|  |  | ||||||
|         OnConnectionCallback _onConnectionCallback; |         OnConnectionCallback _onConnectionCallback; | ||||||
|  |         OnClientMessageCallback _onClientMessageCallback; | ||||||
|  |  | ||||||
|         std::mutex _clientsMutex; |         std::mutex _clientsMutex; | ||||||
|         std::set<std::shared_ptr<WebSocket>> _clients; |         std::set<std::shared_ptr<WebSocket>> _clients; | ||||||
|   | |||||||
| @@ -65,7 +65,6 @@ namespace ix | |||||||
|         , _receivedMessageCompressed(false) |         , _receivedMessageCompressed(false) | ||||||
|         , _readyState(ReadyState::CLOSED) |         , _readyState(ReadyState::CLOSED) | ||||||
|         , _closeCode(WebSocketCloseConstants::kInternalErrorCode) |         , _closeCode(WebSocketCloseConstants::kInternalErrorCode) | ||||||
|         , _closeReason(WebSocketCloseConstants::kInternalErrorMessage) |  | ||||||
|         , _closeWireSize(0) |         , _closeWireSize(0) | ||||||
|         , _closeRemote(false) |         , _closeRemote(false) | ||||||
|         , _enablePerMessageDeflate(false) |         , _enablePerMessageDeflate(false) | ||||||
| @@ -77,6 +76,7 @@ namespace ix | |||||||
|         , _pingCount(0) |         , _pingCount(0) | ||||||
|         , _lastSendPingTimePoint(std::chrono::steady_clock::now()) |         , _lastSendPingTimePoint(std::chrono::steady_clock::now()) | ||||||
|     { |     { | ||||||
|  |         setCloseReason(WebSocketCloseConstants::kInternalErrorMessage); | ||||||
|         _readbuf.resize(kChunkSize); |         _readbuf.resize(kChunkSize); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -179,10 +179,12 @@ namespace ix | |||||||
|  |  | ||||||
|         if (readyState == ReadyState::CLOSED) |         if (readyState == ReadyState::CLOSED) | ||||||
|         { |         { | ||||||
|             std::lock_guard<std::mutex> lock(_closeDataMutex); |             if (_onCloseCallback) | ||||||
|             _onCloseCallback(_closeCode, _closeReason, _closeWireSize, _closeRemote); |             { | ||||||
|  |                 _onCloseCallback(_closeCode, getCloseReason(), _closeWireSize, _closeRemote); | ||||||
|  |             } | ||||||
|  |             setCloseReason(WebSocketCloseConstants::kInternalErrorMessage); | ||||||
|             _closeCode = WebSocketCloseConstants::kInternalErrorCode; |             _closeCode = WebSocketCloseConstants::kInternalErrorCode; | ||||||
|             _closeReason = WebSocketCloseConstants::kInternalErrorMessage; |  | ||||||
|             _closeWireSize = 0; |             _closeWireSize = 0; | ||||||
|             _closeRemote = false; |             _closeRemote = false; | ||||||
|         } |         } | ||||||
| @@ -261,9 +263,10 @@ namespace ix | |||||||
|         { |         { | ||||||
|             // compute lasting delay to wait for next ping / timeout, if at least one set |             // compute lasting delay to wait for next ping / timeout, if at least one set | ||||||
|             auto now = std::chrono::steady_clock::now(); |             auto now = std::chrono::steady_clock::now(); | ||||||
|             lastingTimeoutDelayInMs = (int) std::chrono::duration_cast<std::chrono::milliseconds>( |             int timeSinceLastPingMs = (int) std::chrono::duration_cast<std::chrono::milliseconds>( | ||||||
|                                           now - _lastSendPingTimePoint) |                                           now - _lastSendPingTimePoint) | ||||||
|                                           .count(); |                                           .count(); | ||||||
|  |             lastingTimeoutDelayInMs = (1000 * _pingIntervalSecs) - timeSinceLastPingMs; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| @@ -639,11 +642,7 @@ namespace ix | |||||||
|                 { |                 { | ||||||
|                     // we got the CLOSE frame answer from our close, so we can close the connection |                     // we got the CLOSE frame answer from our close, so we can close the connection | ||||||
|                     // if the code/reason are the same |                     // if the code/reason are the same | ||||||
|                     bool identicalReason; |                     bool identicalReason = _closeCode == code && getCloseReason() == reason; | ||||||
|                     { |  | ||||||
|                         std::lock_guard<std::mutex> lock(_closeDataMutex); |  | ||||||
|                         identicalReason = _closeCode == code && _closeReason == reason; |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     if (identicalReason) |                     if (identicalReason) | ||||||
|                     { |                     { | ||||||
| @@ -797,6 +796,11 @@ namespace ix | |||||||
|         if (wireSize < kChunkSize) |         if (wireSize < kChunkSize) | ||||||
|         { |         { | ||||||
|             success = sendFragment(type, true, message_begin, message_end, compress); |             success = sendFragment(type, true, message_begin, message_end, compress); | ||||||
|  |  | ||||||
|  |             if (onProgressCallback) | ||||||
|  |             { | ||||||
|  |                 onProgressCallback(0, 1); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
| @@ -1081,13 +1085,10 @@ namespace ix | |||||||
|     { |     { | ||||||
|         closeSocket(); |         closeSocket(); | ||||||
|  |  | ||||||
|         { |         setCloseReason(reason); | ||||||
|             std::lock_guard<std::mutex> lock(_closeDataMutex); |  | ||||||
|         _closeCode = code; |         _closeCode = code; | ||||||
|             _closeReason = reason; |  | ||||||
|         _closeWireSize = closeWireSize; |         _closeWireSize = closeWireSize; | ||||||
|         _closeRemote = remote; |         _closeRemote = remote; | ||||||
|         } |  | ||||||
|  |  | ||||||
|         setReadyState(ReadyState::CLOSED); |         setReadyState(ReadyState::CLOSED); | ||||||
|         _requestInitCancellation = false; |         _requestInitCancellation = false; | ||||||
| @@ -1107,13 +1108,11 @@ namespace ix | |||||||
|             closeWireSize = reason.size(); |             closeWireSize = reason.size(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         { |         setCloseReason(reason); | ||||||
|             std::lock_guard<std::mutex> lock(_closeDataMutex); |  | ||||||
|         _closeCode = code; |         _closeCode = code; | ||||||
|             _closeReason = reason; |  | ||||||
|         _closeWireSize = closeWireSize; |         _closeWireSize = closeWireSize; | ||||||
|         _closeRemote = remote; |         _closeRemote = remote; | ||||||
|         } |  | ||||||
|         { |         { | ||||||
|             std::lock_guard<std::mutex> lock(_closingTimePointMutex); |             std::lock_guard<std::mutex> lock(_closingTimePointMutex); | ||||||
|             _closingTimePoint = std::chrono::steady_clock::now(); |             _closingTimePoint = std::chrono::steady_clock::now(); | ||||||
| @@ -1158,4 +1157,15 @@ namespace ix | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void WebSocketTransport::setCloseReason(const std::string& reason) | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lock(_closeReasonMutex); | ||||||
|  |         _closeReason = reason; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const std::string& WebSocketTransport::getCloseReason() const | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lock(_closeReasonMutex); | ||||||
|  |         return _closeReason; | ||||||
|  |     } | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -178,11 +178,11 @@ namespace ix | |||||||
|         std::atomic<ReadyState> _readyState; |         std::atomic<ReadyState> _readyState; | ||||||
|  |  | ||||||
|         OnCloseCallback _onCloseCallback; |         OnCloseCallback _onCloseCallback; | ||||||
|         uint16_t _closeCode; |  | ||||||
|         std::string _closeReason; |         std::string _closeReason; | ||||||
|         size_t _closeWireSize; |         mutable std::mutex _closeReasonMutex; | ||||||
|         bool _closeRemote; |         std::atomic<uint16_t> _closeCode; | ||||||
|         mutable std::mutex _closeDataMutex; |         std::atomic<size_t> _closeWireSize; | ||||||
|  |         std::atomic<bool> _closeRemote; | ||||||
|  |  | ||||||
|         // Data used for Per Message Deflate compression (with zlib) |         // Data used for Per Message Deflate compression (with zlib) | ||||||
|         WebSocketPerMessageDeflatePtr _perMessageDeflate; |         WebSocketPerMessageDeflatePtr _perMessageDeflate; | ||||||
| @@ -267,5 +267,8 @@ namespace ix | |||||||
|         void unmaskReceiveBuffer(const wsheader_type& ws); |         void unmaskReceiveBuffer(const wsheader_type& ws); | ||||||
|  |  | ||||||
|         std::string getMergedChunks() const; |         std::string getMergedChunks() const; | ||||||
|  |  | ||||||
|  |         void setCloseReason(const std::string& reason); | ||||||
|  |         const std::string& getCloseReason() const; | ||||||
|     }; |     }; | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #define IX_WEBSOCKET_VERSION "9.9.0" | #define IX_WEBSOCKET_VERSION "10.1.1" | ||||||
|   | |||||||
| @@ -1,20 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXSetThreadName_apple.cpp |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
| #include "../IXSetThreadName.h" |  | ||||||
| #include <pthread.h> |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     void setThreadName(const std::string& name) |  | ||||||
|     { |  | ||||||
|         // |  | ||||||
|         // Apple reserves 16 bytes for its thread names |  | ||||||
|         // Notice that the Apple version of pthread_setname_np |  | ||||||
|         // does not take a pthread_t argument |  | ||||||
|         // |  | ||||||
|         pthread_setname_np(name.substr(0, 63).c_str()); |  | ||||||
|     } |  | ||||||
| } // namespace ix |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXSetThreadName_freebsd.cpp |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
| #include "../IXSetThreadName.h" |  | ||||||
| #include <pthread.h> |  | ||||||
| #include <pthread_np.h> |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     void setThreadName(const std::string& name) |  | ||||||
|     { |  | ||||||
|         pthread_set_name_np(pthread_self(), name.substr(0, 15).c_str()); |  | ||||||
|     } |  | ||||||
| } // namespace ix |  | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXSetThreadName_linux.cpp |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
| #include "../IXSetThreadName.h" |  | ||||||
| #include <pthread.h> |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     void setThreadName(const std::string& name) |  | ||||||
|     { |  | ||||||
|         // |  | ||||||
|         // Linux only reserves 16 bytes for its thread names |  | ||||||
|         // See prctl and PR_SET_NAME property in |  | ||||||
|         // http://man7.org/linux/man-pages/man2/prctl.2.html |  | ||||||
|         // |  | ||||||
|         pthread_setname_np(pthread_self(), name.substr(0, 15).c_str()); |  | ||||||
|     } |  | ||||||
| } // namespace ix |  | ||||||
| @@ -1,46 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXSetThreadName_windows.cpp |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
| #include "../IXSetThreadName.h" |  | ||||||
|  |  | ||||||
| #include <Windows.h> |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     const DWORD MS_VC_EXCEPTION = 0x406D1388; |  | ||||||
|  |  | ||||||
| #pragma pack(push, 8) |  | ||||||
|     typedef struct tagTHREADNAME_INFO |  | ||||||
|     { |  | ||||||
|         DWORD dwType;     // Must be 0x1000. |  | ||||||
|         LPCSTR szName;    // Pointer to name (in user addr space). |  | ||||||
|         DWORD dwThreadID; // Thread ID (-1=caller thread). |  | ||||||
|         DWORD dwFlags;    // Reserved for future use, must be zero. |  | ||||||
|     } THREADNAME_INFO; |  | ||||||
| #pragma pack(pop) |  | ||||||
|  |  | ||||||
|     void SetThreadName(DWORD dwThreadID, const char* threadName) |  | ||||||
|     { |  | ||||||
|         THREADNAME_INFO info; |  | ||||||
|         info.dwType = 0x1000; |  | ||||||
|         info.szName = threadName; |  | ||||||
|         info.dwThreadID = dwThreadID; |  | ||||||
|         info.dwFlags = 0; |  | ||||||
|  |  | ||||||
|         __try |  | ||||||
|         { |  | ||||||
|             RaiseException( |  | ||||||
|                 MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*) &info); |  | ||||||
|         } |  | ||||||
|         __except (EXCEPTION_EXECUTE_HANDLER) |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void setThreadName(const std::string& name) |  | ||||||
|     { |  | ||||||
|         SetThreadName(-1, name.c_str()); |  | ||||||
|     } |  | ||||||
| } // namespace ix |  | ||||||
							
								
								
									
										15
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								makefile
									
									
									
									
									
								
							| @@ -34,7 +34,7 @@ ws: | |||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install) | ||||||
|  |  | ||||||
| ws_install: | ws_install: | ||||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && make -j 4 install) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_ZLIB=0 -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install) | ||||||
|  |  | ||||||
| ws_openssl: | ws_openssl: | ||||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4) | 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4) | ||||||
| @@ -174,7 +174,7 @@ autobahn_report: | |||||||
| 	cp -rvf ~/sandbox/reports/clients/* ../bsergean.github.io/IXWebSocket/autobahn/ | 	cp -rvf ~/sandbox/reports/clients/* ../bsergean.github.io/IXWebSocket/autobahn/ | ||||||
|  |  | ||||||
| httpd: | httpd: | ||||||
| 	clang++ --std=c++14 --stdlib=libc++ httpd.cpp \ | 	clang++ --std=c++14 --stdlib=libc++ -o ixhttpd httpd.cpp \ | ||||||
| 		ixwebsocket/IXSelectInterruptFactory.cpp \ | 		ixwebsocket/IXSelectInterruptFactory.cpp \ | ||||||
| 		ixwebsocket/IXCancellationRequest.cpp \ | 		ixwebsocket/IXCancellationRequest.cpp \ | ||||||
| 		ixwebsocket/IXSocketTLSOptions.cpp \ | 		ixwebsocket/IXSocketTLSOptions.cpp \ | ||||||
| @@ -193,11 +193,11 @@ httpd: | |||||||
| 		ixwebsocket/IXConnectionState.cpp \ | 		ixwebsocket/IXConnectionState.cpp \ | ||||||
| 		ixwebsocket/IXUrlParser.cpp \ | 		ixwebsocket/IXUrlParser.cpp \ | ||||||
| 		ixwebsocket/IXSelectInterrupt.cpp \ | 		ixwebsocket/IXSelectInterrupt.cpp \ | ||||||
| 		ixwebsocket/apple/IXSetThreadName_apple.cpp \ | 		ixwebsocket/IXSetThreadName.cpp \ | ||||||
| 		-lz | 		-lz | ||||||
|  |  | ||||||
| httpd_linux: | httpd_linux: | ||||||
| 	g++ --std=c++11 -o ixhttpd httpd.cpp -Iixwebsocket \ | 	g++ --std=c++14 -o ixhttpd httpd.cpp -Iixwebsocket \ | ||||||
| 		ixwebsocket/IXSelectInterruptFactory.cpp \ | 		ixwebsocket/IXSelectInterruptFactory.cpp \ | ||||||
| 		ixwebsocket/IXCancellationRequest.cpp \ | 		ixwebsocket/IXCancellationRequest.cpp \ | ||||||
| 		ixwebsocket/IXSocketTLSOptions.cpp \ | 		ixwebsocket/IXSocketTLSOptions.cpp \ | ||||||
| @@ -216,7 +216,7 @@ httpd_linux: | |||||||
| 		ixwebsocket/IXConnectionState.cpp \ | 		ixwebsocket/IXConnectionState.cpp \ | ||||||
| 		ixwebsocket/IXUrlParser.cpp \ | 		ixwebsocket/IXUrlParser.cpp \ | ||||||
| 		ixwebsocket/IXSelectInterrupt.cpp \ | 		ixwebsocket/IXSelectInterrupt.cpp \ | ||||||
| 		ixwebsocket/linux/IXSetThreadName_linux.cpp \ | 		ixwebsocket/IXSetThreadName.cpp \ | ||||||
| 		-lz -lpthread | 		-lz -lpthread | ||||||
| 	cp -f ixhttpd /usr/local/bin | 	cp -f ixhttpd /usr/local/bin | ||||||
|  |  | ||||||
| @@ -238,9 +238,12 @@ install_cmake_for_linux: | |||||||
| doc: | doc: | ||||||
| 	mkdocs gh-deploy | 	mkdocs gh-deploy | ||||||
|  |  | ||||||
| change: | change: format | ||||||
| 	vim ixwebsocket/IXWebSocketVersion.h docs/CHANGELOG.md | 	vim ixwebsocket/IXWebSocketVersion.h docs/CHANGELOG.md | ||||||
|  |  | ||||||
|  | commit: | ||||||
|  | 	git commit -am "`sh tools/extract_latest_change.sh`" | ||||||
|  |  | ||||||
| .PHONY: test | .PHONY: test | ||||||
| .PHONY: build | .PHONY: build | ||||||
| .PHONY: ws | .PHONY: ws | ||||||
|   | |||||||
| @@ -37,11 +37,11 @@ set (SOURCES | |||||||
|  |  | ||||||
|   test_runner.cpp |   test_runner.cpp | ||||||
|   IXTest.cpp |   IXTest.cpp | ||||||
|   IXGetFreePort.cpp |  | ||||||
|   ../third_party/msgpack11/msgpack11.cpp |   ../third_party/msgpack11/msgpack11.cpp | ||||||
|  |  | ||||||
|   IXSocketTest.cpp |   IXSocketTest.cpp | ||||||
|   IXSocketConnectTest.cpp |   IXSocketConnectTest.cpp | ||||||
|  |   # IXWebSocketLeakTest.cpp # commented until we have a fix for #224 | ||||||
|   IXWebSocketServerTest.cpp |   IXWebSocketServerTest.cpp | ||||||
|   IXWebSocketTestConnectionDisconnection.cpp |   IXWebSocketTestConnectionDisconnection.cpp | ||||||
|   IXUrlParserTest.cpp |   IXUrlParserTest.cpp | ||||||
| @@ -56,6 +56,7 @@ set (SOURCES | |||||||
|   IXWebSocketChatTest.cpp |   IXWebSocketChatTest.cpp | ||||||
|   IXWebSocketBroadcastTest.cpp |   IXWebSocketBroadcastTest.cpp | ||||||
|   IXWebSocketPerMessageDeflateCompressorTest.cpp |   IXWebSocketPerMessageDeflateCompressorTest.cpp | ||||||
|  |   IXStreamSqlTest.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| # Some unittest don't work on windows yet | # Some unittest don't work on windows yet | ||||||
|   | |||||||
| @@ -108,7 +108,7 @@ namespace | |||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::UnSubscribed) |             else if (event->type == ix::CobraEventType::UnSubscribed) | ||||||
|             { |             { | ||||||
|                 TLogger() << "Subscriber: ununexpected from channel " << event->subscriptionId; |                 TLogger() << "Subscriber: unsubscribed from channel " << event->subscriptionId; | ||||||
|                 if (event->subscriptionId != channel) |                 if (event->subscriptionId != channel) | ||||||
|                 { |                 { | ||||||
|                     TLogger() << "Subscriber: unexpected channel " << event->subscriptionId; |                     TLogger() << "Subscriber: unexpected channel " << event->subscriptionId; | ||||||
|   | |||||||
| @@ -92,6 +92,9 @@ TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]") | |||||||
|         cobraBotConfig.enableHeartbeat = false; |         cobraBotConfig.enableHeartbeat = false; | ||||||
|         bool quiet = false; |         bool quiet = false; | ||||||
|  |  | ||||||
|  |         cobraBotConfig.filter = | ||||||
|  |             std::string("select * from `") + channel + "` where id = 'sms_metric_A_id'"; | ||||||
|  |  | ||||||
|         // We could try to capture the output ... not sure how. |         // We could try to capture the output ... not sure how. | ||||||
|         bool fluentd = true; |         bool fluentd = true; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,9 +4,9 @@ | |||||||
|  *  Copyright (c) 2019 Machine Zone. All rights reserved. |  *  Copyright (c) 2019 Machine Zone. All rights reserved. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "IXGetFreePort.h" |  | ||||||
| #include "catch.hpp" | #include "catch.hpp" | ||||||
| #include <iostream> | #include <iostream> | ||||||
|  | #include <ixwebsocket/IXGetFreePort.h> | ||||||
| #include <ixwebsocket/IXHttpClient.h> | #include <ixwebsocket/IXHttpClient.h> | ||||||
| #include <ixwebsocket/IXHttpServer.h> | #include <ixwebsocket/IXHttpServer.h> | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								test/IXStreamSqlTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								test/IXStreamSqlTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | /* | ||||||
|  |  *  IXStreamSqlTest.cpp | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2020 Machine Zone. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "IXTest.h" | ||||||
|  | #include "catch.hpp" | ||||||
|  | #include <iostream> | ||||||
|  | #include <ixsnake/IXStreamSql.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | using namespace ix; | ||||||
|  |  | ||||||
|  | namespace ix | ||||||
|  | { | ||||||
|  |     TEST_CASE("stream_sql", "[streamsql]") | ||||||
|  |     { | ||||||
|  |         SECTION("expression A") | ||||||
|  |         { | ||||||
|  |             snake::StreamSql streamSql( | ||||||
|  |                 "select * from subscriber_republished_v1_neo where session LIKE '%123456%'"); | ||||||
|  |  | ||||||
|  |             nlohmann::json msg = {{"session", "123456"}, {"id", "foo_id"}, {"timestamp", 12}}; | ||||||
|  |  | ||||||
|  |             CHECK(streamSql.match(msg)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         SECTION("expression A") | ||||||
|  |         { | ||||||
|  |             snake::StreamSql streamSql("select * from `subscriber_republished_v1_neo` where " | ||||||
|  |                                        "session = '30091320ed8d4e50b758f8409b83bed7'"); | ||||||
|  |  | ||||||
|  |             nlohmann::json msg = {{"session", "30091320ed8d4e50b758f8409b83bed7"}, | ||||||
|  |                                   {"id", "foo_id"}, | ||||||
|  |                                   {"timestamp", 12}}; | ||||||
|  |  | ||||||
|  |             CHECK(streamSql.match(msg)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } // namespace ix | ||||||
| @@ -84,12 +84,12 @@ namespace ix | |||||||
|  |  | ||||||
|     bool startWebSocketEchoServer(ix::WebSocketServer& server) |     bool startWebSocketEchoServer(ix::WebSocketServer& server) | ||||||
|     { |     { | ||||||
|         server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket, |         server.setOnClientMessageCallback( | ||||||
|                                                  std::shared_ptr<ConnectionState> connectionState, |             [&server](std::shared_ptr<ConnectionState> /*connectionState*/, | ||||||
|                                                  std::unique_ptr<ConnectionInfo> connectionInfo) { |                       ConnectionInfo& connectionInfo, | ||||||
|             auto remoteIp = connectionInfo->remoteIp; |                       WebSocket& webSocket, | ||||||
|             webSocket->setOnMessageCallback( |                       const ix::WebSocketMessagePtr& msg) { | ||||||
|                 [webSocket, connectionState, remoteIp, &server](const ix::WebSocketMessagePtr& msg) { |                 auto remoteIp = connectionInfo.remoteIp; | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Open) |                 if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|                 { |                 { | ||||||
|                     TLogger() << "New connection"; |                     TLogger() << "New connection"; | ||||||
| @@ -109,14 +109,13 @@ namespace ix | |||||||
|                 { |                 { | ||||||
|                     for (auto&& client : server.getClients()) |                     for (auto&& client : server.getClients()) | ||||||
|                     { |                     { | ||||||
|                             if (client != webSocket) |                         if (client.get() != &webSocket) | ||||||
|                         { |                         { | ||||||
|                             client->send(msg->str, msg->binary); |                             client->send(msg->str, msg->binary); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         auto res = server.listen(); |         auto res = server.listen(); | ||||||
|         if (!res.first) |         if (!res.first) | ||||||
|   | |||||||
| @@ -6,9 +6,9 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "IXGetFreePort.h" |  | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <ixsnake/IXAppConfig.h> | #include <ixsnake/IXAppConfig.h> | ||||||
|  | #include <ixwebsocket/IXGetFreePort.h> | ||||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | #include <ixwebsocket/IXSocketTLSOptions.h> | ||||||
| #include <ixwebsocket/IXWebSocketServer.h> | #include <ixwebsocket/IXWebSocketServer.h> | ||||||
| #include <mutex> | #include <mutex> | ||||||
|   | |||||||
| @@ -189,13 +189,14 @@ namespace | |||||||
|         bool preferTLS = true; |         bool preferTLS = true; | ||||||
|         server.setTLSOptions(makeServerTLSOptions(preferTLS)); |         server.setTLSOptions(makeServerTLSOptions(preferTLS)); | ||||||
|  |  | ||||||
|         server.setOnConnectionCallback([&server, &connectionId]( |         server.setOnClientMessageCallback( | ||||||
|                                            std::shared_ptr<ix::WebSocket> webSocket, |             [&server, &connectionId](std::shared_ptr<ConnectionState> connectionState, | ||||||
|                                            std::shared_ptr<ConnectionState> connectionState, |                                      ConnectionInfo& connectionInfo, | ||||||
|                                            std::unique_ptr<ConnectionInfo> connectionInfo) { |                                      WebSocket& webSocket, | ||||||
|             auto remoteIp = connectionInfo->remoteIp; |  | ||||||
|             webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &connectionId, &server]( |  | ||||||
|                                      const ix::WebSocketMessagePtr& msg) { |                                      const ix::WebSocketMessagePtr& msg) { | ||||||
|  |                 auto remoteIp = connectionInfo.remoteIp; | ||||||
|  |  | ||||||
|  |  | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Open) |                 if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|                 { |                 { | ||||||
|                     TLogger() << "New connection"; |                     TLogger() << "New connection"; | ||||||
| @@ -219,14 +220,13 @@ namespace | |||||||
|                 { |                 { | ||||||
|                     for (auto&& client : server.getClients()) |                     for (auto&& client : server.getClients()) | ||||||
|                     { |                     { | ||||||
|                         if (client != webSocket) |                         if (client.get() != &webSocket) | ||||||
|                         { |                         { | ||||||
|                             client->send(msg->str, msg->binary); |                             client->send(msg->str, msg->binary); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         auto res = server.listen(); |         auto res = server.listen(); | ||||||
|         if (!res.first) |         if (!res.first) | ||||||
|   | |||||||
| @@ -193,13 +193,12 @@ namespace | |||||||
|  |  | ||||||
|     bool startServer(ix::WebSocketServer& server) |     bool startServer(ix::WebSocketServer& server) | ||||||
|     { |     { | ||||||
|         server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket, |         server.setOnClientMessageCallback( | ||||||
|                                                  std::shared_ptr<ConnectionState> connectionState, |             [&server](std::shared_ptr<ConnectionState> connectionState, | ||||||
|  |                       ConnectionInfo& connectionInfo, | ||||||
|                                                  std::unique_ptr<ConnectionInfo> connectionInfo) { |                       WebSocket& webSocket, | ||||||
|             auto remoteIp = connectionInfo->remoteIp; |                       const ix::WebSocketMessagePtr& msg) { | ||||||
|             webSocket->setOnMessageCallback( |                 auto remoteIp = connectionInfo.remoteIp; | ||||||
|                 [webSocket, connectionState, remoteIp, &server](const ix::WebSocketMessagePtr& msg) { |  | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Open) |                 if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|                 { |                 { | ||||||
|                     TLogger() << "New connection"; |                     TLogger() << "New connection"; | ||||||
| @@ -220,14 +219,13 @@ namespace | |||||||
|                 { |                 { | ||||||
|                     for (auto&& client : server.getClients()) |                     for (auto&& client : server.getClients()) | ||||||
|                     { |                     { | ||||||
|                             if (client != webSocket) |                         if (client.get() != &webSocket) | ||||||
|                         { |                         { | ||||||
|                             client->sendBinary(msg->str); |                             client->sendBinary(msg->str); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         auto res = server.listen(); |         auto res = server.listen(); | ||||||
|         if (!res.first) |         if (!res.first) | ||||||
|   | |||||||
| @@ -168,19 +168,13 @@ namespace | |||||||
|                      std::mutex& mutexWrite) |                      std::mutex& mutexWrite) | ||||||
|     { |     { | ||||||
|         // A dev/null server |         // A dev/null server | ||||||
|         server.setOnConnectionCallback( |         server.setOnClientMessageCallback( | ||||||
|             [&receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite]( |             [&receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite]( | ||||||
|                 std::shared_ptr<ix::WebSocket> webSocket, |  | ||||||
|                 std::shared_ptr<ConnectionState> connectionState, |                 std::shared_ptr<ConnectionState> connectionState, | ||||||
|                 std::unique_ptr<ConnectionInfo> connectionInfo) { |                 ConnectionInfo& connectionInfo, | ||||||
|                 auto remoteIp = connectionInfo->remoteIp; |                 WebSocket& /*webSocket*/, | ||||||
|                 webSocket->setOnMessageCallback([webSocket, |                 const ix::WebSocketMessagePtr& msg) { | ||||||
|                                                  connectionState, |                 auto remoteIp = connectionInfo.remoteIp; | ||||||
|                                                  remoteIp, |  | ||||||
|                                                  &receivedCloseCode, |  | ||||||
|                                                  &receivedCloseReason, |  | ||||||
|                                                  &receivedCloseRemote, |  | ||||||
|                                                  &mutexWrite](const ix::WebSocketMessagePtr& msg) { |  | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Open) |                 if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|                 { |                 { | ||||||
|                     TLogger() << "New server connection"; |                     TLogger() << "New server connection"; | ||||||
| @@ -207,7 +201,6 @@ namespace | |||||||
|                     receivedCloseRemote = msg->closeInfo.remote; |                     receivedCloseRemote = msg->closeInfo.remote; | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|             }); |  | ||||||
|  |  | ||||||
|         auto res = server.listen(); |         auto res = server.listen(); | ||||||
|         if (!res.first) |         if (!res.first) | ||||||
|   | |||||||
							
								
								
									
										183
									
								
								test/IXWebSocketLeakTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								test/IXWebSocketLeakTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,183 @@ | |||||||
|  | /* | ||||||
|  |  *  IXWebSocketServer.cpp | ||||||
|  |  *  Author: Benjamin Sergeant, @marcelkauf | ||||||
|  |  *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "IXTest.h" | ||||||
|  | #include "catch.hpp" | ||||||
|  | #include <ixwebsocket/IXWebSocket.h> | ||||||
|  | #include <ixwebsocket/IXWebSocketServer.h> | ||||||
|  | #include <memory> | ||||||
|  | #include <sstream> | ||||||
|  |  | ||||||
|  | using namespace ix; | ||||||
|  |  | ||||||
|  | namespace | ||||||
|  | { | ||||||
|  |     class WebSocketClient | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         WebSocketClient(int port); | ||||||
|  |         void start(); | ||||||
|  |         void stop(); | ||||||
|  |         bool isReady() const; | ||||||
|  |         bool hasConnectionError() const; | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         ix::WebSocket _webSocket; | ||||||
|  |         int _port; | ||||||
|  |         std::atomic<bool> _connectionError; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     WebSocketClient::WebSocketClient(int port) | ||||||
|  |         : _port(port) | ||||||
|  |         , _connectionError(false) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool WebSocketClient::hasConnectionError() const | ||||||
|  |     { | ||||||
|  |         return _connectionError; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool WebSocketClient::isReady() const | ||||||
|  |     { | ||||||
|  |         return _webSocket.getReadyState() == ix::ReadyState::Open; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketClient::stop() | ||||||
|  |     { | ||||||
|  |         _webSocket.stop(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketClient::start() | ||||||
|  |     { | ||||||
|  |         std::string url; | ||||||
|  |         { | ||||||
|  |             std::stringstream ss; | ||||||
|  |             ss << "ws://localhost:" << _port << "/"; | ||||||
|  |  | ||||||
|  |             url = ss.str(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         _webSocket.setUrl(url); | ||||||
|  |         _webSocket.disableAutomaticReconnection(); | ||||||
|  |  | ||||||
|  |         std::stringstream ss; | ||||||
|  |         log(std::string("Connecting to url: ") + url); | ||||||
|  |  | ||||||
|  |         _webSocket.setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) { | ||||||
|  |             std::stringstream ss; | ||||||
|  |             if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|  |             { | ||||||
|  |                 log("client connected"); | ||||||
|  |             } | ||||||
|  |             else if (msg->type == ix::WebSocketMessageType::Close) | ||||||
|  |             { | ||||||
|  |                 log("client disconnected"); | ||||||
|  |             } | ||||||
|  |             else if (msg->type == ix::WebSocketMessageType::Error) | ||||||
|  |             { | ||||||
|  |                 _connectionError = true; | ||||||
|  |                 log("error"); | ||||||
|  |             } | ||||||
|  |             else if (msg->type == ix::WebSocketMessageType::Pong) | ||||||
|  |             { | ||||||
|  |                 log("pong"); | ||||||
|  |             } | ||||||
|  |             else if (msg->type == ix::WebSocketMessageType::Ping) | ||||||
|  |             { | ||||||
|  |                 log("ping"); | ||||||
|  |             } | ||||||
|  |             else if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|  |             { | ||||||
|  |                 log("message"); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 log("invalid type"); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         _webSocket.start(); | ||||||
|  |     } | ||||||
|  | } // namespace | ||||||
|  |  | ||||||
|  | TEST_CASE("Websocket leak test") | ||||||
|  | { | ||||||
|  |     SECTION("Websocket destructor is called when closing the connection.") | ||||||
|  |     { | ||||||
|  |         // stores the server websocket in order to check the use_count | ||||||
|  |         std::shared_ptr<WebSocket> webSocketPtr; | ||||||
|  |  | ||||||
|  |         { | ||||||
|  |             int port = getFreePort(); | ||||||
|  |             WebSocketServer server(port); | ||||||
|  |  | ||||||
|  |             server.setOnConnectionCallback( | ||||||
|  |                 [&webSocketPtr](std::shared_ptr<ix::WebSocket> webSocket, | ||||||
|  |                                 std::shared_ptr<ConnectionState> connectionState, | ||||||
|  |                                 std::unique_ptr<ConnectionInfo> connectionInfo) { | ||||||
|  |                     // original ptr in WebSocketServer::handleConnection and the callback argument | ||||||
|  |                     REQUIRE(webSocket.use_count() == 2); | ||||||
|  |                     webSocket->setOnMessageCallback([&webSocketPtr, webSocket, connectionState]( | ||||||
|  |                                                         const ix::WebSocketMessagePtr& msg) { | ||||||
|  |                         if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|  |                         { | ||||||
|  |                             log(std::string("New connection id: ") + connectionState->getId()); | ||||||
|  |                             // original ptr in WebSocketServer::handleConnection, captured ptr of | ||||||
|  |                             // this callback, and ptr in WebSocketServer::_clients | ||||||
|  |                             REQUIRE(webSocket.use_count() == 3); | ||||||
|  |                             webSocketPtr = std::shared_ptr<WebSocket>(webSocket); | ||||||
|  |                             REQUIRE(webSocket.use_count() == 4); | ||||||
|  |                         } | ||||||
|  |                         else if (msg->type == ix::WebSocketMessageType::Close) | ||||||
|  |                         { | ||||||
|  |                             log(std::string("Client closed connection id: ") + | ||||||
|  |                                 connectionState->getId()); | ||||||
|  |                         } | ||||||
|  |                         else | ||||||
|  |                         { | ||||||
|  |                             log(std::string(msg->str)); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                     // original ptr in WebSocketServer::handleConnection, argument of this callback, | ||||||
|  |                     // and captured ptr in websocket callback | ||||||
|  |                     REQUIRE(webSocket.use_count() == 3); | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |             server.listen(); | ||||||
|  |             server.start(); | ||||||
|  |  | ||||||
|  |             WebSocketClient webSocketClient(port); | ||||||
|  |             webSocketClient.start(); | ||||||
|  |  | ||||||
|  |             while (true) | ||||||
|  |             { | ||||||
|  |                 REQUIRE(!webSocketClient.hasConnectionError()); | ||||||
|  |                 if (webSocketClient.isReady()) break; | ||||||
|  |                 ix::msleep(10); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             REQUIRE(server.getClients().size() == 1); | ||||||
|  |             // same value as in Open-handler above | ||||||
|  |             REQUIRE(webSocketPtr.use_count() == 4); | ||||||
|  |  | ||||||
|  |             ix::msleep(500); | ||||||
|  |             webSocketClient.stop(); | ||||||
|  |             ix::msleep(500); | ||||||
|  |             REQUIRE(server.getClients().size() == 0); | ||||||
|  |  | ||||||
|  |             // websocket should only be referenced by webSocketPtr but is still used by the | ||||||
|  |             // websocket callback | ||||||
|  |             REQUIRE(webSocketPtr.use_count() == 1); | ||||||
|  |             webSocketPtr->setOnMessageCallback(nullptr); | ||||||
|  |             // websocket should only be referenced by webSocketPtr | ||||||
|  |             REQUIRE(webSocketPtr.use_count() == 1); | ||||||
|  |             server.stop(); | ||||||
|  |         } | ||||||
|  |         // websocket should only be referenced by webSocketPtr | ||||||
|  |         REQUIRE(webSocketPtr.use_count() == 1); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -33,13 +33,14 @@ namespace ix | |||||||
|         }; |         }; | ||||||
|         server.setConnectionStateFactory(factory); |         server.setConnectionStateFactory(factory); | ||||||
|  |  | ||||||
|         server.setOnConnectionCallback([&server, &connectionId]( |         server.setOnClientMessageCallback( | ||||||
|                                            std::shared_ptr<ix::WebSocket> webSocket, |             [&server, &connectionId](std::shared_ptr<ConnectionState> connectionState, | ||||||
|                                            std::shared_ptr<ConnectionState> connectionState, |                                      ConnectionInfo& connectionInfo, | ||||||
|                                            std::unique_ptr<ConnectionInfo> connectionInfo) { |                                      WebSocket& webSocket, | ||||||
|             auto remoteIp = connectionInfo->remoteIp; |  | ||||||
|             webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &connectionId, &server]( |  | ||||||
|                                      const ix::WebSocketMessagePtr& msg) { |                                      const ix::WebSocketMessagePtr& msg) { | ||||||
|  |                 auto remoteIp = connectionInfo.remoteIp; | ||||||
|  |  | ||||||
|  |  | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Open) |                 if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|                 { |                 { | ||||||
|                     TLogger() << "New connection"; |                     TLogger() << "New connection"; | ||||||
| @@ -63,14 +64,13 @@ namespace ix | |||||||
|                 { |                 { | ||||||
|                     for (auto&& client : server.getClients()) |                     for (auto&& client : server.getClients()) | ||||||
|                     { |                     { | ||||||
|                         if (client != webSocket) |                         if (client.get() != &webSocket) | ||||||
|                         { |                         { | ||||||
|                             client->send(msg->str, msg->binary); |                             client->send(msg->str, msg->binary); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         auto res = server.listen(); |         auto res = server.listen(); | ||||||
|         if (!res.first) |         if (!res.first) | ||||||
|   | |||||||
| @@ -16,13 +16,12 @@ using namespace ix; | |||||||
|  |  | ||||||
| bool startServer(ix::WebSocketServer& server, std::string& subProtocols) | bool startServer(ix::WebSocketServer& server, std::string& subProtocols) | ||||||
| { | { | ||||||
|     server.setOnConnectionCallback( |     server.setOnClientMessageCallback( | ||||||
|         [&server, &subProtocols](std::shared_ptr<ix::WebSocket> webSocket, |         [&server, &subProtocols](std::shared_ptr<ConnectionState> connectionState, | ||||||
|                                  std::shared_ptr<ConnectionState> connectionState, |                                  ConnectionInfo& connectionInfo, | ||||||
|                                  std::unique_ptr<ConnectionInfo> connectionInfo) { |                                  WebSocket& webSocket, | ||||||
|             auto remoteIp = connectionInfo->remoteIp; |  | ||||||
|             webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &server, &subProtocols]( |  | ||||||
|                                  const ix::WebSocketMessagePtr& msg) { |                                  const ix::WebSocketMessagePtr& msg) { | ||||||
|  |             auto remoteIp = connectionInfo.remoteIp; | ||||||
|             if (msg->type == ix::WebSocketMessageType::Open) |             if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|             { |             { | ||||||
|                 TLogger() << "New connection"; |                 TLogger() << "New connection"; | ||||||
| @@ -45,14 +44,13 @@ bool startServer(ix::WebSocketServer& server, std::string& subProtocols) | |||||||
|             { |             { | ||||||
|                 for (auto&& client : server.getClients()) |                 for (auto&& client : server.getClients()) | ||||||
|                 { |                 { | ||||||
|                         if (client != webSocket) |                     if (client.get() != &webSocket) | ||||||
|                     { |                     { | ||||||
|                         client->sendBinary(msg->str); |                         client->sendBinary(msg->str); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|         }); |  | ||||||
|  |  | ||||||
|     auto res = server.listen(); |     auto res = server.listen(); | ||||||
|     if (!res.first) |     if (!res.first) | ||||||
|   | |||||||
| @@ -10,10 +10,18 @@ | |||||||
| #include <ixwebsocket/IXNetSystem.h> | #include <ixwebsocket/IXNetSystem.h> | ||||||
| #include <spdlog/spdlog.h> | #include <spdlog/spdlog.h> | ||||||
|  |  | ||||||
|  | #ifndef _WIN32 | ||||||
|  | #include <signal.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
| int main(int argc, char* argv[]) | int main(int argc, char* argv[]) | ||||||
| { | { | ||||||
|     ix::initNetSystem(); |     ix::initNetSystem(); | ||||||
|  |  | ||||||
|  | #ifndef _WIN32 | ||||||
|  |     signal(SIGPIPE, SIG_IGN); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     ix::CoreLogger::LogFunc logFunc = [](const char* msg, ix::LogLevel level) { |     ix::CoreLogger::LogFunc logFunc = [](const char* msg, ix::LogLevel level) { | ||||||
|         switch (level) |         switch (level) | ||||||
|         { |         { | ||||||
| @@ -49,6 +57,7 @@ int main(int argc, char* argv[]) | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     ix::CoreLogger::setLogFunction(logFunc); |     ix::CoreLogger::setLogFunction(logFunc); | ||||||
|  |     spdlog::set_level(spdlog::level::debug); | ||||||
|  |  | ||||||
|     int result = Catch::Session().run(argc, argv); |     int result = Catch::Session().run(argc, argv); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								tools/extract_latest_change.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tools/extract_latest_change.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | #!/bin/sh | ||||||
|  |  | ||||||
|  | grep -A 3 '^##' docs/CHANGELOG.md | head -n 3 | tail -n 1 | ||||||
| @@ -20,7 +20,6 @@ option(USE_TLS "Add TLS support" ON) | |||||||
|  |  | ||||||
| include_directories(ws .) | include_directories(ws .) | ||||||
| include_directories(ws ..) | include_directories(ws ..) | ||||||
| include_directories(ws ../third_party) |  | ||||||
| include_directories(ws ../third_party/spdlog/include) | include_directories(ws ../third_party/spdlog/include) | ||||||
| include_directories(ws ../third_party/cpp-linenoise) | include_directories(ws ../third_party/cpp-linenoise) | ||||||
|  |  | ||||||
| @@ -66,7 +65,6 @@ add_executable(ws | |||||||
|   ws_cobra_publish.cpp |   ws_cobra_publish.cpp | ||||||
|   ws_httpd.cpp |   ws_httpd.cpp | ||||||
|   ws_autobahn.cpp |   ws_autobahn.cpp | ||||||
|   ws_proxy_server.cpp |  | ||||||
|   ws_sentry_minidump_upload.cpp |   ws_sentry_minidump_upload.cpp | ||||||
|   ws_dns_lookup.cpp |   ws_dns_lookup.cpp | ||||||
|   ws.cpp) |   ws.cpp) | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ function cleanup_and_exit { | |||||||
| } | } | ||||||
|  |  | ||||||
| WITH_TLS=${WITH_TLS:-0} | WITH_TLS=${WITH_TLS:-0} | ||||||
|  | BLOCKS=${BLOCKS:-20000} | ||||||
|  |  | ||||||
| rm -rf /tmp/ws_test | rm -rf /tmp/ws_test | ||||||
| mkdir -p /tmp/ws_test | mkdir -p /tmp/ws_test | ||||||
| @@ -57,7 +58,7 @@ ws receive "${protocol}127.0.0.1:8090" ${delay} --pidfile /tmp/ws_test/pidfile.r | |||||||
|  |  | ||||||
| mkdir -p /tmp/ws_test/send | mkdir -p /tmp/ws_test/send | ||||||
| cd /tmp/ws_test/send | cd /tmp/ws_test/send | ||||||
| dd if=/dev/urandom of=/tmp/ws_test/send/20M_file count=20000 bs=1024 | dd if=/dev/urandom of=/tmp/ws_test/send/20M_file count=$BLOCKS bs=1024 | ||||||
|  |  | ||||||
| # Start the sender job | # Start the sender job | ||||||
| ws send ${client_tls} --pidfile /tmp/ws_test/pidfile.send "${protocol}127.0.0.1:8090" /tmp/ws_test/send/20M_file | ws send ${client_tls} --pidfile /tmp/ws_test/pidfile.send "${protocol}127.0.0.1:8090" /tmp/ws_test/send/20M_file | ||||||
|   | |||||||
							
								
								
									
										45
									
								
								ws/ws.cpp
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								ws/ws.cpp
									
									
									
									
									
								
							| @@ -22,6 +22,7 @@ | |||||||
| #include <ixwebsocket/IXNetSystem.h> | #include <ixwebsocket/IXNetSystem.h> | ||||||
| #include <ixwebsocket/IXSocket.h> | #include <ixwebsocket/IXSocket.h> | ||||||
| #include <ixwebsocket/IXUserAgent.h> | #include <ixwebsocket/IXUserAgent.h> | ||||||
|  | #include <ixwebsocket/IXWebSocketProxyServer.h> | ||||||
| #include <spdlog/sinks/basic_file_sink.h> | #include <spdlog/sinks/basic_file_sink.h> | ||||||
| #include <spdlog/spdlog.h> | #include <spdlog/spdlog.h> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| @@ -74,6 +75,7 @@ int main(int argc, char** argv) | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     ix::CoreLogger::setLogFunction(logFunc); |     ix::CoreLogger::setLogFunction(logFunc); | ||||||
|  |     spdlog::set_level(spdlog::level::debug); | ||||||
|  |  | ||||||
| #ifndef _WIN32 | #ifndef _WIN32 | ||||||
|     signal(SIGPIPE, SIG_IGN); |     signal(SIGPIPE, SIG_IGN); | ||||||
| @@ -122,6 +124,7 @@ int main(int argc, char** argv) | |||||||
|     std::string key; |     std::string key; | ||||||
|     std::string logfile; |     std::string logfile; | ||||||
|     std::string scriptPath; |     std::string scriptPath; | ||||||
|  |     std::string republishChannel; | ||||||
|     ix::SocketTLSOptions tlsOptions; |     ix::SocketTLSOptions tlsOptions; | ||||||
|     ix::CobraConfig cobraConfig; |     ix::CobraConfig cobraConfig; | ||||||
|     ix::CobraBotConfig cobraBotConfig; |     ix::CobraBotConfig cobraBotConfig; | ||||||
| @@ -391,6 +394,7 @@ int main(int argc, char** argv) | |||||||
|     snakeApp->add_option("--redis_password", redisPassword, "Redis password"); |     snakeApp->add_option("--redis_password", redisPassword, "Redis password"); | ||||||
|     snakeApp->add_option("--apps_config_path", appsConfigPath, "Path to auth data") |     snakeApp->add_option("--apps_config_path", appsConfigPath, "Path to auth data") | ||||||
|         ->check(CLI::ExistingPath); |         ->check(CLI::ExistingPath); | ||||||
|  |     snakeApp->add_option("--republish_channel", republishChannel, "Republish channel"); | ||||||
|     snakeApp->add_flag("-v", verbose, "Verbose"); |     snakeApp->add_flag("-v", verbose, "Verbose"); | ||||||
|     snakeApp->add_flag("-d", disablePong, "Disable Pongs"); |     snakeApp->add_flag("-d", disablePong, "Disable Pongs"); | ||||||
|     addTLSOptions(snakeApp); |     addTLSOptions(snakeApp); | ||||||
| @@ -482,7 +486,24 @@ int main(int argc, char** argv) | |||||||
|     cobraBotConfig.cobraConfig.socketTLSOptions = tlsOptions; |     cobraBotConfig.cobraConfig.socketTLSOptions = tlsOptions; | ||||||
|  |  | ||||||
|     int ret = 1; |     int ret = 1; | ||||||
|     if (app.got_subcommand("transfer")) |     if (app.got_subcommand("connect")) | ||||||
|  |     { | ||||||
|  |         ret = ix::ws_connect_main(url, | ||||||
|  |                                   headers, | ||||||
|  |                                   disableAutomaticReconnection, | ||||||
|  |                                   disablePerMessageDeflate, | ||||||
|  |                                   binaryMode, | ||||||
|  |                                   maxWaitBetweenReconnectionRetries, | ||||||
|  |                                   tlsOptions, | ||||||
|  |                                   subprotocol, | ||||||
|  |                                   pingIntervalSecs); | ||||||
|  |     } | ||||||
|  |     else if (app.got_subcommand("echo_server")) | ||||||
|  |     { | ||||||
|  |         ret = ix::ws_echo_server_main( | ||||||
|  |             port, greetings, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong); | ||||||
|  |     } | ||||||
|  |     else if (app.got_subcommand("transfer")) | ||||||
|     { |     { | ||||||
|         ret = ix::ws_transfer_main(port, hostname, tlsOptions); |         ret = ix::ws_transfer_main(port, hostname, tlsOptions); | ||||||
|     } |     } | ||||||
| @@ -495,27 +516,10 @@ int main(int argc, char** argv) | |||||||
|         bool enablePerMessageDeflate = false; |         bool enablePerMessageDeflate = false; | ||||||
|         ret = ix::ws_receive_main(url, enablePerMessageDeflate, delayMs, tlsOptions); |         ret = ix::ws_receive_main(url, enablePerMessageDeflate, delayMs, tlsOptions); | ||||||
|     } |     } | ||||||
|     else if (app.got_subcommand("connect")) |  | ||||||
|     { |  | ||||||
|         ret = ix::ws_connect_main(url, |  | ||||||
|                                   headers, |  | ||||||
|                                   disableAutomaticReconnection, |  | ||||||
|                                   disablePerMessageDeflate, |  | ||||||
|                                   binaryMode, |  | ||||||
|                                   maxWaitBetweenReconnectionRetries, |  | ||||||
|                                   tlsOptions, |  | ||||||
|                                   subprotocol, |  | ||||||
|                                   pingIntervalSecs); |  | ||||||
|     } |  | ||||||
|     else if (app.got_subcommand("chat")) |     else if (app.got_subcommand("chat")) | ||||||
|     { |     { | ||||||
|         ret = ix::ws_chat_main(url, user); |         ret = ix::ws_chat_main(url, user); | ||||||
|     } |     } | ||||||
|     else if (app.got_subcommand("echo_server")) |  | ||||||
|     { |  | ||||||
|         ret = ix::ws_echo_server_main( |  | ||||||
|             port, greetings, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong); |  | ||||||
|     } |  | ||||||
|     else if (app.got_subcommand("broadcast_server")) |     else if (app.got_subcommand("broadcast_server")) | ||||||
|     { |     { | ||||||
|         ret = ix::ws_broadcast_server_main(port, hostname, tlsOptions); |         ret = ix::ws_broadcast_server_main(port, hostname, tlsOptions); | ||||||
| @@ -637,7 +641,8 @@ int main(int argc, char** argv) | |||||||
|                                 verbose, |                                 verbose, | ||||||
|                                 appsConfigPath, |                                 appsConfigPath, | ||||||
|                                 tlsOptions, |                                 tlsOptions, | ||||||
|                                 disablePong); |                                 disablePong, | ||||||
|  |                                 republishChannel); | ||||||
|     } |     } | ||||||
|     else if (app.got_subcommand("httpd")) |     else if (app.got_subcommand("httpd")) | ||||||
|     { |     { | ||||||
| @@ -653,7 +658,7 @@ int main(int argc, char** argv) | |||||||
|     } |     } | ||||||
|     else if (app.got_subcommand("proxy_server")) |     else if (app.got_subcommand("proxy_server")) | ||||||
|     { |     { | ||||||
|         ret = ix::ws_proxy_server_main(port, hostname, tlsOptions, remoteHost, verbose); |         ret = ix::websocket_proxy_server_main(port, hostname, tlsOptions, remoteHost, verbose); | ||||||
|     } |     } | ||||||
|     else if (app.got_subcommand("upload_minidump")) |     else if (app.got_subcommand("upload_minidump")) | ||||||
|     { |     { | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								ws/ws.h
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								ws/ws.h
									
									
									
									
									
								
							| @@ -103,7 +103,8 @@ namespace ix | |||||||
|                       bool verbose, |                       bool verbose, | ||||||
|                       const std::string& appsConfigPath, |                       const std::string& appsConfigPath, | ||||||
|                       const ix::SocketTLSOptions& tlsOptions, |                       const ix::SocketTLSOptions& tlsOptions, | ||||||
|                       bool disablePong); |                       bool disablePong, | ||||||
|  |                       const std::string& republishChannel); | ||||||
|  |  | ||||||
|     int ws_httpd_main(int port, |     int ws_httpd_main(int port, | ||||||
|                       const std::string& hostname, |                       const std::string& hostname, | ||||||
| @@ -115,12 +116,6 @@ namespace ix | |||||||
|  |  | ||||||
|     int ws_redis_server_main(int port, const std::string& hostname); |     int ws_redis_server_main(int port, const std::string& hostname); | ||||||
|  |  | ||||||
|     int ws_proxy_server_main(int port, |  | ||||||
|                              const std::string& hostname, |  | ||||||
|                              const ix::SocketTLSOptions& tlsOptions, |  | ||||||
|                              const std::string& remoteHost, |  | ||||||
|                              bool verbose); |  | ||||||
|  |  | ||||||
|     int ws_sentry_minidump_upload(const std::string& metadataPath, |     int ws_sentry_minidump_upload(const std::string& metadataPath, | ||||||
|                                   const std::string& minidump, |                                   const std::string& minidump, | ||||||
|                                   const std::string& project, |                                   const std::string& project, | ||||||
|   | |||||||
| @@ -20,12 +20,12 @@ namespace ix | |||||||
|         ix::WebSocketServer server(port, hostname); |         ix::WebSocketServer server(port, hostname); | ||||||
|         server.setTLSOptions(tlsOptions); |         server.setTLSOptions(tlsOptions); | ||||||
|  |  | ||||||
|         server.setOnConnectionCallback([&server](std::shared_ptr<WebSocket> webSocket, |         server.setOnClientMessageCallback( | ||||||
|                                                  std::shared_ptr<ConnectionState> connectionState, |             [&server](std::shared_ptr<ConnectionState> connectionState, | ||||||
|                                                  std::unique_ptr<ConnectionInfo> connectionInfo) { |                       ConnectionInfo& connectionInfo, | ||||||
|             auto remoteIp = connectionInfo->remoteIp; |                       WebSocket& webSocket, | ||||||
|             webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &server]( |  | ||||||
|                       const WebSocketMessagePtr& msg) { |                       const WebSocketMessagePtr& msg) { | ||||||
|  |                 auto remoteIp = connectionInfo.remoteIp; | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Open) |                 if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|                 { |                 { | ||||||
|                     spdlog::info("New connection"); |                     spdlog::info("New connection"); | ||||||
| @@ -63,7 +63,7 @@ namespace ix | |||||||
|  |  | ||||||
|                     for (auto&& client : server.getClients()) |                     for (auto&& client : server.getClients()) | ||||||
|                     { |                     { | ||||||
|                         if (client != webSocket) |                         if (client.get() != &webSocket) | ||||||
|                         { |                         { | ||||||
|                             client->send(msg->str, msg->binary, [](int current, int total) -> bool { |                             client->send(msg->str, msg->binary, [](int current, int total) -> bool { | ||||||
|                                 spdlog::info("Step {} out of {}", current, total); |                                 spdlog::info("Step {} out of {}", current, total); | ||||||
| @@ -82,7 +82,6 @@ namespace ix | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         auto res = server.listen(); |         auto res = server.listen(); | ||||||
|         if (!res.first) |         if (!res.first) | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ | |||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <fstream> | #include <fstream> | ||||||
| #include <ixcobra/IXCobraMetricsPublisher.h> | #include <ixcobra/IXCobraMetricsPublisher.h> | ||||||
| #include <jsoncpp/json/json.h> |  | ||||||
| #include <spdlog/spdlog.h> | #include <spdlog/spdlog.h> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <thread> | #include <thread> | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ | |||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <fstream> | #include <fstream> | ||||||
| #include <ixcobra/IXCobraMetricsPublisher.h> | #include <ixcobra/IXCobraMetricsPublisher.h> | ||||||
| #include <jsoncpp/json/json.h> |  | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <spdlog/spdlog.h> | #include <spdlog/spdlog.h> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|   | |||||||
| @@ -200,7 +200,7 @@ namespace ix | |||||||
|             } |             } | ||||||
|             else if (msg->type == ix::WebSocketMessageType::Pong) |             else if (msg->type == ix::WebSocketMessageType::Pong) | ||||||
|             { |             { | ||||||
|                 spdlog::info("Received pong"); |                 spdlog::info("Received pong {}", msg->str); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
|   | |||||||
| @@ -42,13 +42,12 @@ namespace ix | |||||||
|             server.disablePong(); |             server.disablePong(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         server.setOnConnectionCallback( |         server.setOnClientMessageCallback( | ||||||
|             [greetings](std::shared_ptr<ix::WebSocket> webSocket, |             [greetings](std::shared_ptr<ConnectionState> connectionState, | ||||||
|                         std::shared_ptr<ConnectionState> connectionState, |                         ConnectionInfo& connectionInfo, | ||||||
|                         std::unique_ptr<ConnectionInfo> connectionInfo) { |                         WebSocket& webSocket, | ||||||
|                 auto remoteIp = connectionInfo->remoteIp; |                         const WebSocketMessagePtr& msg) { | ||||||
|                 webSocket->setOnMessageCallback( |                 auto remoteIp = connectionInfo.remoteIp; | ||||||
|                     [webSocket, connectionState, remoteIp, greetings](const WebSocketMessagePtr& msg) { |  | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Open) |                 if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|                 { |                 { | ||||||
|                     spdlog::info("New connection"); |                     spdlog::info("New connection"); | ||||||
| @@ -63,7 +62,7 @@ namespace ix | |||||||
|  |  | ||||||
|                     if (greetings) |                     if (greetings) | ||||||
|                     { |                     { | ||||||
|                                 webSocket->sendText("Welcome !"); |                         webSocket.sendText("Welcome !"); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 else if (msg->type == ix::WebSocketMessageType::Close) |                 else if (msg->type == ix::WebSocketMessageType::Close) | ||||||
| @@ -83,10 +82,9 @@ namespace ix | |||||||
|                 else if (msg->type == ix::WebSocketMessageType::Message) |                 else if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|                 { |                 { | ||||||
|                     spdlog::info("Received {} bytes", msg->wireSize); |                     spdlog::info("Received {} bytes", msg->wireSize); | ||||||
|                             webSocket->send(msg->str, msg->binary); |                     webSocket.send(msg->str, msg->binary); | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|             }); |  | ||||||
|  |  | ||||||
|         auto res = server.listen(); |         auto res = server.listen(); | ||||||
|         if (!res.first) |         if (!res.first) | ||||||
|   | |||||||
| @@ -1,176 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  ws_proxy_server.cpp |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <ixwebsocket/IXWebSocketServer.h> |  | ||||||
| #include <spdlog/spdlog.h> |  | ||||||
| #include <sstream> |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     class ProxyConnectionState : public ix::ConnectionState |  | ||||||
|     { |  | ||||||
|     public: |  | ||||||
|         ProxyConnectionState() |  | ||||||
|             : _connected(false) |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ix::WebSocket& webSocket() |  | ||||||
|         { |  | ||||||
|             return _serverWebSocket; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         bool isConnected() |  | ||||||
|         { |  | ||||||
|             return _connected; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         void setConnected() |  | ||||||
|         { |  | ||||||
|             _connected = true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     private: |  | ||||||
|         ix::WebSocket _serverWebSocket; |  | ||||||
|         bool _connected; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     int ws_proxy_server_main(int port, |  | ||||||
|                              const std::string& hostname, |  | ||||||
|                              const ix::SocketTLSOptions& tlsOptions, |  | ||||||
|                              const std::string& remoteUrl, |  | ||||||
|                              bool verbose) |  | ||||||
|     { |  | ||||||
|         spdlog::info("Listening on {}:{}", hostname, port); |  | ||||||
|  |  | ||||||
|         ix::WebSocketServer server(port, hostname); |  | ||||||
|         server.setTLSOptions(tlsOptions); |  | ||||||
|  |  | ||||||
|         auto factory = []() -> std::shared_ptr<ix::ConnectionState> { |  | ||||||
|             return std::make_shared<ProxyConnectionState>(); |  | ||||||
|         }; |  | ||||||
|         server.setConnectionStateFactory(factory); |  | ||||||
|  |  | ||||||
|         server.setOnConnectionCallback([remoteUrl, |  | ||||||
|                                         verbose](std::shared_ptr<ix::WebSocket> webSocket, |  | ||||||
|                                                  std::shared_ptr<ConnectionState> connectionState, |  | ||||||
|                                                  std::unique_ptr<ConnectionInfo> connectionInfo) { |  | ||||||
|             auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState); |  | ||||||
|             auto remoteIp = connectionInfo->remoteIp; |  | ||||||
|  |  | ||||||
|             // Server connection |  | ||||||
|             state->webSocket().setOnMessageCallback([webSocket, state, remoteIp, verbose]( |  | ||||||
|                                                         const WebSocketMessagePtr& msg) { |  | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Open) |  | ||||||
|                 { |  | ||||||
|                     spdlog::info("New connection to remote server"); |  | ||||||
|                     spdlog::info("remote ip: {}", remoteIp); |  | ||||||
|                     spdlog::info("id: {}", state->getId()); |  | ||||||
|                     spdlog::info("Uri: {}", msg->openInfo.uri); |  | ||||||
|                     spdlog::info("Headers:"); |  | ||||||
|                     for (auto it : msg->openInfo.headers) |  | ||||||
|                     { |  | ||||||
|                         spdlog::info("{}: {}", it.first, it.second); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 else if (msg->type == ix::WebSocketMessageType::Close) |  | ||||||
|                 { |  | ||||||
|                     spdlog::info("Closed remote server connection: client id {} code {} reason {}", |  | ||||||
|                                  state->getId(), |  | ||||||
|                                  msg->closeInfo.code, |  | ||||||
|                                  msg->closeInfo.reason); |  | ||||||
|                     state->setTerminated(); |  | ||||||
|                 } |  | ||||||
|                 else if (msg->type == ix::WebSocketMessageType::Error) |  | ||||||
|                 { |  | ||||||
|                     spdlog::error("Connection error: {}", msg->errorInfo.reason); |  | ||||||
|                     spdlog::error("#retries: {}", msg->errorInfo.retries); |  | ||||||
|                     spdlog::error("Wait time(ms): {}", msg->errorInfo.wait_time); |  | ||||||
|                     spdlog::error("HTTP Status: {}", msg->errorInfo.http_status); |  | ||||||
|                 } |  | ||||||
|                 else if (msg->type == ix::WebSocketMessageType::Message) |  | ||||||
|                 { |  | ||||||
|                     spdlog::info("Received {} bytes from server", msg->wireSize); |  | ||||||
|                     if (verbose) |  | ||||||
|                     { |  | ||||||
|                         spdlog::info("payload {}", msg->str); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     webSocket->send(msg->str, msg->binary); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             // Client connection |  | ||||||
|             webSocket->setOnMessageCallback( |  | ||||||
|                 [state, remoteUrl, verbose](const WebSocketMessagePtr& msg) { |  | ||||||
|                     if (msg->type == ix::WebSocketMessageType::Open) |  | ||||||
|                     { |  | ||||||
|                         spdlog::info("New connection from client"); |  | ||||||
|                         spdlog::info("id: {}", state->getId()); |  | ||||||
|                         spdlog::info("Uri: {}", msg->openInfo.uri); |  | ||||||
|                         spdlog::info("Headers:"); |  | ||||||
|                         for (auto it : msg->openInfo.headers) |  | ||||||
|                         { |  | ||||||
|                             spdlog::info("{}: {}", it.first, it.second); |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         // Connect to the 'real' server |  | ||||||
|                         std::string url(remoteUrl); |  | ||||||
|                         url += msg->openInfo.uri; |  | ||||||
|                         state->webSocket().setUrl(url); |  | ||||||
|                         state->webSocket().disableAutomaticReconnection(); |  | ||||||
|                         state->webSocket().start(); |  | ||||||
|  |  | ||||||
|                         // we should sleep here for a bit until we've established the |  | ||||||
|                         // connection with the remote server |  | ||||||
|                         while (state->webSocket().getReadyState() != ReadyState::Open) |  | ||||||
|                         { |  | ||||||
|                             spdlog::info("waiting for server connection establishment"); |  | ||||||
|                             std::this_thread::sleep_for(std::chrono::milliseconds(10)); |  | ||||||
|                         } |  | ||||||
|                         spdlog::info("server connection established"); |  | ||||||
|                     } |  | ||||||
|                     else if (msg->type == ix::WebSocketMessageType::Close) |  | ||||||
|                     { |  | ||||||
|                         spdlog::info("Closed client connection: client id {} code {} reason {}", |  | ||||||
|                                      state->getId(), |  | ||||||
|                                      msg->closeInfo.code, |  | ||||||
|                                      msg->closeInfo.reason); |  | ||||||
|                         state->webSocket().close(msg->closeInfo.code, msg->closeInfo.reason); |  | ||||||
|                     } |  | ||||||
|                     else if (msg->type == ix::WebSocketMessageType::Error) |  | ||||||
|                     { |  | ||||||
|                         spdlog::error("Connection error: {}", msg->errorInfo.reason); |  | ||||||
|                         spdlog::error("#retries: {}", msg->errorInfo.retries); |  | ||||||
|                         spdlog::error("Wait time(ms): {}", msg->errorInfo.wait_time); |  | ||||||
|                         spdlog::error("HTTP Status: {}", msg->errorInfo.http_status); |  | ||||||
|                     } |  | ||||||
|                     else if (msg->type == ix::WebSocketMessageType::Message) |  | ||||||
|                     { |  | ||||||
|                         spdlog::info("Received {} bytes from client", msg->wireSize); |  | ||||||
|                         if (verbose) |  | ||||||
|                         { |  | ||||||
|                             spdlog::info("payload {}", msg->str); |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         state->webSocket().send(msg->str, msg->binary); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         auto res = server.listen(); |  | ||||||
|         if (!res.first) |  | ||||||
|         { |  | ||||||
|             spdlog::info(res.second); |  | ||||||
|             return 1; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         server.start(); |  | ||||||
|         server.wait(); |  | ||||||
|  |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| } // namespace ix |  | ||||||
| @@ -6,7 +6,6 @@ | |||||||
|  |  | ||||||
| #include <fstream> | #include <fstream> | ||||||
| #include <ixsentry/IXSentryClient.h> | #include <ixsentry/IXSentryClient.h> | ||||||
| #include <jsoncpp/json/json.h> |  | ||||||
| #include <spdlog/spdlog.h> | #include <spdlog/spdlog.h> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -45,7 +45,8 @@ namespace ix | |||||||
|                       bool verbose, |                       bool verbose, | ||||||
|                       const std::string& appsConfigPath, |                       const std::string& appsConfigPath, | ||||||
|                       const SocketTLSOptions& socketTLSOptions, |                       const SocketTLSOptions& socketTLSOptions, | ||||||
|                       bool disablePong) |                       bool disablePong, | ||||||
|  |                       const std::string& republishChannel) | ||||||
|     { |     { | ||||||
|         snake::AppConfig appConfig; |         snake::AppConfig appConfig; | ||||||
|         appConfig.port = port; |         appConfig.port = port; | ||||||
| @@ -55,6 +56,7 @@ namespace ix | |||||||
|         appConfig.redisPassword = redisPassword; |         appConfig.redisPassword = redisPassword; | ||||||
|         appConfig.socketTLSOptions = socketTLSOptions; |         appConfig.socketTLSOptions = socketTLSOptions; | ||||||
|         appConfig.disablePong = disablePong; |         appConfig.disablePong = disablePong; | ||||||
|  |         appConfig.republishChannel = republishChannel; | ||||||
|  |  | ||||||
|         // Parse config file |         // Parse config file | ||||||
|         auto str = readAsString(appsConfigPath); |         auto str = readAsString(appsConfigPath); | ||||||
|   | |||||||
| @@ -19,12 +19,12 @@ namespace ix | |||||||
|         ix::WebSocketServer server(port, hostname); |         ix::WebSocketServer server(port, hostname); | ||||||
|         server.setTLSOptions(tlsOptions); |         server.setTLSOptions(tlsOptions); | ||||||
|  |  | ||||||
|         server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket, |         server.setOnClientMessageCallback( | ||||||
|                                                  std::shared_ptr<ConnectionState> connectionState, |             [&server](std::shared_ptr<ConnectionState> connectionState, | ||||||
|                                                  std::unique_ptr<ConnectionInfo> connectionInfo) { |                       ConnectionInfo& connectionInfo, | ||||||
|             auto remoteIp = connectionInfo->remoteIp; |                       WebSocket& webSocket, | ||||||
|             webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &server]( |  | ||||||
|                       const WebSocketMessagePtr& msg) { |                       const WebSocketMessagePtr& msg) { | ||||||
|  |                 auto remoteIp = connectionInfo.remoteIp; | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Open) |                 if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|                 { |                 { | ||||||
|                     spdlog::info("ws_transfer: New connection"); |                     spdlog::info("ws_transfer: New connection"); | ||||||
| @@ -43,7 +43,7 @@ namespace ix | |||||||
|                                  connectionState->getId(), |                                  connectionState->getId(), | ||||||
|                                  msg->closeInfo.code, |                                  msg->closeInfo.code, | ||||||
|                                  msg->closeInfo.reason); |                                  msg->closeInfo.reason); | ||||||
|                     auto remaining = server.getClients().erase(webSocket); |                     auto remaining = server.getClients().size() - 1; | ||||||
|                     spdlog::info("ws_transfer: {} remaining clients", remaining); |                     spdlog::info("ws_transfer: {} remaining clients", remaining); | ||||||
|                 } |                 } | ||||||
|                 else if (msg->type == ix::WebSocketMessageType::Error) |                 else if (msg->type == ix::WebSocketMessageType::Error) | ||||||
| @@ -65,7 +65,7 @@ namespace ix | |||||||
|                     size_t receivers = 0; |                     size_t receivers = 0; | ||||||
|                     for (auto&& client : server.getClients()) |                     for (auto&& client : server.getClients()) | ||||||
|                     { |                     { | ||||||
|                         if (client != webSocket) |                         if (client.get() != &webSocket) | ||||||
|                         { |                         { | ||||||
|                             auto readyState = client->getReadyState(); |                             auto readyState = client->getReadyState(); | ||||||
|                             auto id = connectionState->getId(); |                             auto id = connectionState->getId(); | ||||||
| @@ -119,7 +119,6 @@ namespace ix | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         auto res = server.listen(); |         auto res = server.listen(); | ||||||
|         if (!res.first) |         if (!res.first) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user