Compare commits
	
		
			2 Commits
		
	
	
		
			v9.6.2
			...
			feature/wi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ae3c94c4bb | ||
|  | fa88cbe268 | 
							
								
								
									
										75
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										75
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
								
							| @@ -5,82 +5,23 @@ on: | |||||||
|     - 'docs/**' |     - 'docs/**' | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   linux: |   # | ||||||
|     runs-on: ubuntu-latest |   #   Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg | ||||||
|     steps: |   # | ||||||
|     - uses: actions/checkout@v1 |   windows_openssl: | ||||||
|     - name: make test_make |  | ||||||
|       run: make test_make |  | ||||||
|  |  | ||||||
|   mac_tsan_sectransport: |  | ||||||
|     runs-on: macOS-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - name: make test_tsan |  | ||||||
|       run: make test_tsan |  | ||||||
|  |  | ||||||
|   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 |  | ||||||
|  |  | ||||||
|   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 |  | ||||||
|  |  | ||||||
|   windows: |  | ||||||
|     runs-on: windows-latest |     runs-on: windows-latest | ||||||
|     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 | ||||||
|  |         vcpkg install openssl:x64-windows | ||||||
|     - run: | |     - run: | | ||||||
|         mkdir build |         mkdir build | ||||||
|         cd build |         cd build | ||||||
|         cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 .. |         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 |     - run: cmake --build build | ||||||
|  |  | ||||||
|     # Running the unittest does not work, the binary cannot be found |     # Running the unittest does not work, the binary cannot be found | ||||||
|     #- run: ../build/test/ixwebsocket_unittest.exe |     #- run: ../build/test/ixwebsocket_unittest.exe | ||||||
|     # working-directory: test |     # working-directory: test | ||||||
|  |  | ||||||
|   uwp: |  | ||||||
|     runs-on: windows-latest |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v1 |  | ||||||
|     - uses: seanmiddleditch/gha-setup-vsdevenv@master |  | ||||||
|     - run: | |  | ||||||
|         mkdir build |  | ||||||
|         cd build |  | ||||||
|         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 |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,19 +0,0 @@ | |||||||
| name: Mark stale issues and pull requests |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   schedule: |  | ||||||
|   - cron: "0 0 * * *" |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   stale: |  | ||||||
|  |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|  |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/stale@v1 |  | ||||||
|       with: |  | ||||||
|         repo-token: ${{ secrets.GITHUB_TOKEN }} |  | ||||||
|         stale-issue-message: 'Stale issue message' |  | ||||||
|         stale-pr-message: 'Stale pull request message' |  | ||||||
|         stale-issue-label: 'no-issue-activity' |  | ||||||
|         stale-pr-label: 'no-pr-activity' |  | ||||||
| @@ -1,12 +1,7 @@ | |||||||
| repos: | repos: | ||||||
| -   repo: https://github.com/pre-commit/pre-commit-hooks | -   repo: https://github.com/pre-commit/pre-commit-hooks | ||||||
|     rev: v2.5.0 |     rev: v2.3.0 | ||||||
|     hooks: |     hooks: | ||||||
|     -   id: check-yaml |     -   id: check-yaml | ||||||
|     -   id: end-of-file-fixer |     -   id: end-of-file-fixer | ||||||
|     -   id: trailing-whitespace |     -   id: trailing-whitespace | ||||||
| -   repo: https://github.com/pocc/pre-commit-hooks |  | ||||||
|     rev: v1.1.1 |  | ||||||
|     hooks: |  | ||||||
|     -    id: clang-format |  | ||||||
|          args: [-i, -style=file] |  | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ include(FindPackageHandleStandardArgs) | |||||||
| find_path(JSONCPP_INCLUDE_DIRS json/json.h) | find_path(JSONCPP_INCLUDE_DIRS json/json.h) | ||||||
| find_library(JSONCPP_LIBRARY jsoncpp) | find_library(JSONCPP_LIBRARY jsoncpp) | ||||||
|  |  | ||||||
| find_package_handle_standard_args(JsonCpp | find_package_handle_standard_args(JSONCPP | ||||||
|     FOUND_VAR |     FOUND_VAR | ||||||
|       JSONCPP_FOUND |       JSONCPP_FOUND | ||||||
|     REQUIRED_VARS |     REQUIRED_VARS | ||||||
|   | |||||||
							
								
								
									
										140
									
								
								CMakeLists.txt
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								CMakeLists.txt
									
									
									
									
									
								
							| @@ -12,10 +12,6 @@ set (CMAKE_CXX_STANDARD 14) | |||||||
| set (CXX_STANDARD_REQUIRED ON) | set (CXX_STANDARD_REQUIRED ON) | ||||||
| set (CMAKE_CXX_EXTENSIONS OFF) | set (CMAKE_CXX_EXTENSIONS OFF) | ||||||
|  |  | ||||||
| if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") |  | ||||||
|   set(CMAKE_POSITION_INDEPENDENT_CODE ON) |  | ||||||
| endif() |  | ||||||
|  |  | ||||||
| if (UNIX) | if (UNIX) | ||||||
|   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") |   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") | ||||||
| endif() | endif() | ||||||
| @@ -48,11 +44,13 @@ set( IXWEBSOCKET_SOURCES | |||||||
|     ixwebsocket/IXWebSocketCloseConstants.cpp |     ixwebsocket/IXWebSocketCloseConstants.cpp | ||||||
|     ixwebsocket/IXWebSocketHandshake.cpp |     ixwebsocket/IXWebSocketHandshake.cpp | ||||||
|     ixwebsocket/IXWebSocketHttpHeaders.cpp |     ixwebsocket/IXWebSocketHttpHeaders.cpp | ||||||
|  |     ixwebsocket/IXWebSocketMessageQueue.cpp | ||||||
|     ixwebsocket/IXWebSocketPerMessageDeflate.cpp |     ixwebsocket/IXWebSocketPerMessageDeflate.cpp | ||||||
|     ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp |     ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp | ||||||
|     ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp |     ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp | ||||||
|     ixwebsocket/IXWebSocketServer.cpp |     ixwebsocket/IXWebSocketServer.cpp | ||||||
|     ixwebsocket/IXWebSocketTransport.cpp |     ixwebsocket/IXWebSocketTransport.cpp | ||||||
|  |     ixwebsocket/LUrlParser.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| set( IXWEBSOCKET_HEADERS | set( IXWEBSOCKET_HEADERS | ||||||
| @@ -83,10 +81,10 @@ set( IXWEBSOCKET_HEADERS | |||||||
|     ixwebsocket/IXWebSocketCloseInfo.h |     ixwebsocket/IXWebSocketCloseInfo.h | ||||||
|     ixwebsocket/IXWebSocketErrorInfo.h |     ixwebsocket/IXWebSocketErrorInfo.h | ||||||
|     ixwebsocket/IXWebSocketHandshake.h |     ixwebsocket/IXWebSocketHandshake.h | ||||||
|     ixwebsocket/IXWebSocketHandshakeKeyGen.h |  | ||||||
|     ixwebsocket/IXWebSocketHttpHeaders.h |     ixwebsocket/IXWebSocketHttpHeaders.h | ||||||
|     ixwebsocket/IXWebSocketInitResult.h |     ixwebsocket/IXWebSocketInitResult.h | ||||||
|     ixwebsocket/IXWebSocketMessage.h |     ixwebsocket/IXWebSocketMessage.h | ||||||
|  |     ixwebsocket/IXWebSocketMessageQueue.h | ||||||
|     ixwebsocket/IXWebSocketMessageType.h |     ixwebsocket/IXWebSocketMessageType.h | ||||||
|     ixwebsocket/IXWebSocketOpenInfo.h |     ixwebsocket/IXWebSocketOpenInfo.h | ||||||
|     ixwebsocket/IXWebSocketPerMessageDeflate.h |     ixwebsocket/IXWebSocketPerMessageDeflate.h | ||||||
| @@ -96,6 +94,8 @@ set( IXWEBSOCKET_HEADERS | |||||||
|     ixwebsocket/IXWebSocketServer.h |     ixwebsocket/IXWebSocketServer.h | ||||||
|     ixwebsocket/IXWebSocketTransport.h |     ixwebsocket/IXWebSocketTransport.h | ||||||
|     ixwebsocket/IXWebSocketVersion.h |     ixwebsocket/IXWebSocketVersion.h | ||||||
|  |     ixwebsocket/LUrlParser.h | ||||||
|  |     ixwebsocket/libwshandshake.hpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| if (UNIX) | if (UNIX) | ||||||
| @@ -113,38 +113,31 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") | |||||||
|     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/freebsd/IXSetThreadName_freebsd.cpp) |     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/freebsd/IXSetThreadName_freebsd.cpp) | ||||||
| else() | else() | ||||||
|     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/linux/IXSetThreadName_linux.cpp) |     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/linux/IXSetThreadName_linux.cpp) | ||||||
|  |     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptEventFd.cpp) | ||||||
|  |     list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSelectInterruptEventFd.h) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| option(USE_TLS "Enable TLS support" FALSE) | option(USE_TLS "Enable TLS support" FALSE) | ||||||
|  |  | ||||||
| if (USE_TLS) | if (USE_TLS) | ||||||
|     # default to securetranport on Apple if nothing is configured |     option(USE_MBED_TLS "Use Mbed TLS" OFF) | ||||||
|     if (APPLE) |     option(USE_OPEN_SSL "Use OpenSSL" OFF) | ||||||
|       if (NOT USE_MBED_TLS AND NOT USE_OPEN_SSL) # unless we want something else |  | ||||||
|         set(USE_SECURE_TRANSPORT ON) |  | ||||||
|       endif() |  | ||||||
|     # default to mbedtls on windows if nothing is configured |     # default to mbedtls on windows if nothing is configured | ||||||
|     elseif (WIN32) |     if (WIN32 AND NOT USE_OPEN_SSL AND NOT USE_MBED_TLS) | ||||||
|       if (NOT USE_OPEN_SSL) # unless we want something else |       option(USE_MBED_TLS "Use Mbed TLS" ON) | ||||||
|         set(USE_MBED_TLS ON) |  | ||||||
|       endif() |  | ||||||
|     else() # default to OpenSSL on all other platforms |  | ||||||
|       if (NOT USE_MBED_TLS) # Unless mbedtls is requested |  | ||||||
|         set(USE_OPEN_SSL ON) |  | ||||||
|       endif() |  | ||||||
|     endif() |     endif() | ||||||
|  |  | ||||||
|     if (USE_MBED_TLS) |     if (USE_MBED_TLS) | ||||||
|         list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketMbedTLS.h) |         list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketMbedTLS.h) | ||||||
|         list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketMbedTLS.cpp) |         list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketMbedTLS.cpp) | ||||||
|     elseif (USE_SECURE_TRANSPORT) |     elseif (APPLE AND NOT USE_OPEN_SSL) | ||||||
|         list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketAppleSSL.h) |         list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketAppleSSL.h) | ||||||
|         list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketAppleSSL.cpp) |         list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketAppleSSL.cpp) | ||||||
|     elseif (USE_OPEN_SSL) |     else() | ||||||
|  |         set(USE_OPEN_SSL ON) | ||||||
|         list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketOpenSSL.h) |         list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketOpenSSL.h) | ||||||
|         list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketOpenSSL.cpp) |         list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketOpenSSL.cpp) | ||||||
|     else() |  | ||||||
|         message(FATAL_ERROR "TLS Configuration error: unknown backend") |  | ||||||
|     endif() |     endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| @@ -153,82 +146,73 @@ add_library( ixwebsocket STATIC | |||||||
|     ${IXWEBSOCKET_HEADERS} |     ${IXWEBSOCKET_HEADERS} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| add_library ( ixwebsocket::ixwebsocket ALIAS ixwebsocket ) |  | ||||||
|  |  | ||||||
| if (USE_TLS) | if (USE_TLS) | ||||||
|     target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_TLS) |     target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_TLS) | ||||||
|     if (USE_MBED_TLS) |     if (USE_MBED_TLS) | ||||||
|         target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_MBED_TLS) |         target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_MBED_TLS) | ||||||
|     elseif (USE_OPEN_SSL) |     elseif (USE_OPEN_SSL) | ||||||
|         target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_OPEN_SSL) |         target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_OPEN_SSL) | ||||||
|     elseif (USE_SECURE_TRANSPORT) |  | ||||||
|         target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_SECURE_TRANSPORT) |  | ||||||
|     else() |  | ||||||
|         message(FATAL_ERROR "TLS Configuration error: unknown backend") |  | ||||||
|     endif() |     endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| if (USE_TLS) | if (APPLE AND USE_TLS AND NOT USE_MBED_TLS AND NOT USE_OPEN_SSL) | ||||||
|   if (USE_OPEN_SSL) |   target_link_libraries(ixwebsocket "-framework foundation" "-framework security") | ||||||
|     message(STATUS "TLS configured to use openssl") | endif() | ||||||
|  |  | ||||||
|     # Help finding Homebrew's OpenSSL on macOS | if (WIN32) | ||||||
|     if (APPLE) |   target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi) | ||||||
|       set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/local/opt/openssl/lib) |   add_definitions(-D_CRT_SECURE_NO_WARNINGS) | ||||||
|       set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /usr/local/opt/openssl/include) | endif() | ||||||
|  |  | ||||||
|       # This is for MacPort OpenSSL 1.0 | if (UNIX) | ||||||
|       # set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /opt/local/lib/openssl-1.0) |   find_package(Threads) | ||||||
|       # set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/local/include/openssl-1.0) |   target_link_libraries(ixwebsocket ${CMAKE_THREAD_LIBS_INIT}) | ||||||
|     endif() | endif() | ||||||
|  |  | ||||||
|     # This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL | if (USE_TLS AND USE_OPEN_SSL) | ||||||
|     if (NOT OPENSSL_FOUND) |  | ||||||
| 	    include(FindOpenSSL) |  | ||||||
|     endif() |  | ||||||
|     message(STATUS "OpenSSL: " ${OPENSSL_VERSION}) |  | ||||||
|  |  | ||||||
|     target_link_libraries(ixwebsocket PUBLIC OpenSSL::SSL OpenSSL::Crypto) |   # Help finding Homebrew's OpenSSL on macOS | ||||||
|   elseif (USE_MBED_TLS) |   if (APPLE) | ||||||
|     message(STATUS "TLS configured to use mbedtls") |     set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/local/opt/openssl/lib) | ||||||
|  |     set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /usr/local/opt/openssl/include) | ||||||
|  |   endif() | ||||||
|  |  | ||||||
|  |   if(NOT OPENSSL_FOUND) | ||||||
|  |     find_package(OpenSSL REQUIRED) | ||||||
|  |   endif() | ||||||
|  |   add_definitions(${OPENSSL_DEFINITIONS}) | ||||||
|  |   message(STATUS "OpenSSL: " ${OPENSSL_VERSION}) | ||||||
|  |   include_directories(${OPENSSL_INCLUDE_DIR}) | ||||||
|  |   target_link_libraries(ixwebsocket ${OPENSSL_LIBRARIES}) | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | if (USE_TLS AND USE_MBED_TLS) | ||||||
|  |   # FIXME I'm not too sure that this USE_VENDORED_THIRD_PARTY thing works | ||||||
|  |   if (USE_VENDORED_THIRD_PARTY) | ||||||
|  |     set (ENABLE_PROGRAMS OFF) | ||||||
|  |     add_subdirectory(third_party/mbedtls) | ||||||
|  |     include_directories(third_party/mbedtls/include) | ||||||
|  |  | ||||||
|  |     target_link_libraries(ixwebsocket mbedtls) | ||||||
|  |   else() | ||||||
|     find_package(MbedTLS REQUIRED) |     find_package(MbedTLS REQUIRED) | ||||||
|     target_include_directories(ixwebsocket PUBLIC ${MBEDTLS_INCLUDE_DIRS}) |     target_include_directories(ixwebsocket PUBLIC ${MBEDTLS_INCLUDE_DIRS}) | ||||||
|     target_link_libraries(ixwebsocket PUBLIC ${MBEDTLS_LIBRARIES}) |     target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES}) | ||||||
|   elseif (USE_SECURE_TRANSPORT) |  | ||||||
|     message(STATUS "TLS configured to use secure transport") |  | ||||||
|     target_link_libraries(ixwebsocket PUBLIC "-framework foundation" "-framework security") |  | ||||||
|   endif() |   endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| # This ZLIB_FOUND check is to help find a cmake manually configured zlib |  | ||||||
| if (NOT ZLIB_FOUND) | if (NOT ZLIB_FOUND) | ||||||
|   find_package(ZLIB) |   find_package(ZLIB) | ||||||
| endif() | endif() | ||||||
| if (ZLIB_FOUND) | if (ZLIB_FOUND) | ||||||
|   include_directories(${ZLIB_INCLUDE_DIRS}) |   include_directories(${ZLIB_INCLUDE_DIRS}) | ||||||
|   target_link_libraries(ixwebsocket PUBLIC ${ZLIB_LIBRARIES}) |   target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES}) | ||||||
| else() | else() | ||||||
|   include_directories(third_party/zlib ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib) |   include_directories(third_party/zlib ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib) | ||||||
|   add_subdirectory(third_party/zlib EXCLUDE_FROM_ALL) |   add_subdirectory(third_party/zlib) | ||||||
|   target_link_libraries(ixwebsocket PRIVATE $<LINK_ONLY:zlibstatic>) |   target_link_libraries(ixwebsocket zlibstatic) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| if (WIN32) |  | ||||||
|   target_link_libraries(ixwebsocket PUBLIC wsock32 ws2_32 shlwapi) |  | ||||||
|   add_definitions(-D_CRT_SECURE_NO_WARNINGS) |  | ||||||
|  |  | ||||||
|   if (USE_TLS) |  | ||||||
|     target_link_libraries(ixwebsocket PUBLIC Crypt32) |  | ||||||
|   endif() |  | ||||||
| endif() |  | ||||||
|  |  | ||||||
| if (UNIX) |  | ||||||
|   find_package(Threads) |  | ||||||
|   target_link_libraries(ixwebsocket PUBLIC ${CMAKE_THREAD_LIBS_INIT}) |  | ||||||
| endif() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| set( IXWEBSOCKET_INCLUDE_DIRS | set( IXWEBSOCKET_INCLUDE_DIRS | ||||||
|     ${CMAKE_CURRENT_SOURCE_DIR} |     ${CMAKE_CURRENT_SOURCE_DIR} | ||||||
| ) | ) | ||||||
| @@ -238,23 +222,15 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") | |||||||
|     target_compile_options(ixwebsocket PRIVATE /MP) |     target_compile_options(ixwebsocket PRIVATE /MP) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| target_include_directories(ixwebsocket PUBLIC $<BUILD_INTERFACE:${IXWEBSOCKET_INCLUDE_DIRS}> $<INSTALL_INTERFACE:include/ixwebsocket>) | target_include_directories(ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS}) | ||||||
|  |  | ||||||
| set_target_properties(ixwebsocket PROPERTIES PUBLIC_HEADER "${IXWEBSOCKET_HEADERS}") | set_target_properties(ixwebsocket PROPERTIES PUBLIC_HEADER "${IXWEBSOCKET_HEADERS}") | ||||||
|  |  | ||||||
| install(TARGETS ixwebsocket EXPORT ixwebsocket | install(TARGETS ixwebsocket | ||||||
| 	ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib |         ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib | ||||||
| 	PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/include/ixwebsocket/ |         PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/include/ixwebsocket/ | ||||||
| ) | ) | ||||||
|  |  | ||||||
| # This gets in the way of vcpkg in ways I do not know how to fix |  | ||||||
| # https://github.com/microsoft/vcpkg/pull/11030 |  | ||||||
| # Maybe using vcpkg_fixup_cmake_targets could fix it |  | ||||||
| if (INSTALL_CMAKE_FILE) |  | ||||||
|   install(EXPORT ixwebsocket NAMESPACE ixwebsocket:: DESTINATION lib/cmake/ixwebsocket) |  | ||||||
|   export(EXPORT ixwebsocket NAMESPACE ixwebsocket:: FILE ixwebsocketConfig.cmake) |  | ||||||
| endif() |  | ||||||
|  |  | ||||||
| if (USE_WS OR USE_TEST) | if (USE_WS OR USE_TEST) | ||||||
|   add_subdirectory(ixcore) |   add_subdirectory(ixcore) | ||||||
|   add_subdirectory(ixcrypto) |   add_subdirectory(ixcrypto) | ||||||
|   | |||||||
| @@ -45,7 +45,3 @@ IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version. | |||||||
| If your company or project is using this library, feel free to open an issue or PR to amend this list. | If your company or project is using this library, feel free to open an issue or PR to amend this list. | ||||||
|  |  | ||||||
| - [Machine Zone](https://www.mz.com) | - [Machine Zone](https://www.mz.com) | ||||||
| - [Tokio](https://gitlab.com/HCInk/tokio), a discord library focused on audio playback with node bindings. |  | ||||||
| - [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), a work in progress discord library |  | ||||||
| - [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod |  | ||||||
| - [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper |  | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ FROM alpine:3.11 as build | |||||||
|  |  | ||||||
| RUN apk add --no-cache \ | RUN apk add --no-cache \ | ||||||
|     gcc g++ musl-dev linux-headers \ |     gcc g++ musl-dev linux-headers \ | ||||||
|     cmake mbedtls-dev make zlib-dev ninja |     cmake mbedtls-dev make zlib-dev | ||||||
|  |  | ||||||
| RUN addgroup -S app && \ | RUN addgroup -S app && \ | ||||||
|     adduser -S -G app app && \ |     adduser -S -G app app && \ | ||||||
|   | |||||||
| @@ -1,118 +1,6 @@ | |||||||
| # Changelog | # Changelog | ||||||
| All changes to this project will be documented in this file. | All changes to this project will be documented in this file. | ||||||
|  |  | ||||||
| ## [9.6.2] - 2020-05-17 |  | ||||||
|  |  | ||||||
| (cmake) make install cmake files optional to not conflict with vcpkg |  | ||||||
|  |  | ||||||
| ## [9.6.1] - 2020-05-17 |  | ||||||
|  |  | ||||||
| (windows + tls) mbedtls is the default windows tls backend + add ability to load system certificates with mbdetls on windows |  | ||||||
|  |  | ||||||
| ## [9.6.0] - 2020-05-12 |  | ||||||
|  |  | ||||||
| (ixbots) add options to limit how many messages per minute should be processed |  | ||||||
|  |  | ||||||
| ## [9.5.9] - 2020-05-12 |  | ||||||
|  |  | ||||||
| (ixbots) add new class to configure a bot to simplify passing options around |  | ||||||
|  |  | ||||||
| ## [9.5.8] - 2020-05-08 |  | ||||||
|  |  | ||||||
| (openssl tls) (openssl < 1.1) logic inversion - crypto locking callback are not registered properly |  | ||||||
|  |  | ||||||
| ## [9.5.7] - 2020-05-08 |  | ||||||
|  |  | ||||||
| (cmake) default TLS back to mbedtls on Windows Universal Platform |  | ||||||
|  |  | ||||||
| ## [9.5.6] - 2020-05-06 |  | ||||||
|  |  | ||||||
| (cobra bots) add a --heartbeat_timeout option to specify when the bot should terminate because no events are received |  | ||||||
|  |  | ||||||
| ## [9.5.5] - 2020-05-06 |  | ||||||
|  |  | ||||||
| (openssl tls) when OpenSSL is older than 1.1, register the crypto locking callback to be thread safe. Should fix lots of CI failures |  | ||||||
|  |  | ||||||
| ## [9.5.4] - 2020-05-04 |  | ||||||
|  |  | ||||||
| (cobra bots) do not use a queue to store messages pending processing, let the bot handle queuing |  | ||||||
|  |  | ||||||
| ## [9.5.3] - 2020-04-29 |  | ||||||
|  |  | ||||||
| (http client) better current request cancellation support when the HttpClient destructor is invoked (see #189) |  | ||||||
|  |  | ||||||
| ## [9.5.2] - 2020-04-27 |  | ||||||
|  |  | ||||||
| (cmake) fix cmake broken tls option parsing |  | ||||||
|  |  | ||||||
| ## [9.5.1] - 2020-04-27 |  | ||||||
|  |  | ||||||
| (http client) Set default values for most HttpRequestArgs struct members (fix #185) |  | ||||||
|  |  | ||||||
| ## [9.5.0] - 2020-04-25 |  | ||||||
|  |  | ||||||
| (ssl) Default to OpenSSL on Windows, since it can load the system certificates by default |  | ||||||
|  |  | ||||||
| ## [9.4.1] - 2020-04-25 |  | ||||||
|  |  | ||||||
| (header) Add a space between header name and header value since most http parsers expects it, although it it not required. Cf #184 and #155 |  | ||||||
|  |  | ||||||
| ## [9.4.0] - 2020-04-24 |  | ||||||
|  |  | ||||||
| (ssl) Add support for supplying SSL CA from memory, for OpenSSL and MbedTLS backends |  | ||||||
|  |  | ||||||
| ## [9.3.3] - 2020-04-17 |  | ||||||
|  |  | ||||||
| (ixbots) display sent/receive message, per seconds as accumulated |  | ||||||
|  |  | ||||||
| ## [9.3.2] - 2020-04-17 |  | ||||||
|  |  | ||||||
| (ws) add a --logfile option to configure all logs to go to a file |  | ||||||
|  |  | ||||||
| ## [9.3.1] - 2020-04-16 |  | ||||||
|  |  | ||||||
| (cobra bots) add a utility class to factor out the common bots features (heartbeat) and move all bots to used it + convert cobra_subscribe to be a bot and add a unittest for it |  | ||||||
|  |  | ||||||
| ## [9.3.0] - 2020-04-15 |  | ||||||
|  |  | ||||||
| (websocket) add a positive number to the heartbeat message sent, incremented each time the heartbeat is sent |  | ||||||
|  |  | ||||||
| ## [9.2.9] - 2020-04-15 |  | ||||||
|  |  | ||||||
| (ixcobra) change cobra event callback to use a struct instead of several objects, which is more flexible/extensible |  | ||||||
|  |  | ||||||
| ## [9.2.8] - 2020-04-15 |  | ||||||
|  |  | ||||||
| (ixcobra) make CobraConnection_EventType an enum class (CobraEventType) |  | ||||||
|  |  | ||||||
| ## [9.2.7] - 2020-04-14 |  | ||||||
|  |  | ||||||
| (ixsentry) add a library method to upload a payload directly to sentry |  | ||||||
|  |  | ||||||
| ## [9.2.6] - 2020-04-14 |  | ||||||
|  |  | ||||||
| (ixcobra) snake server / handle invalid incoming json messages + cobra subscriber in fluentd mode insert a created_at timestamp entry |  | ||||||
|  |  | ||||||
| ## [9.2.5] - 2020-04-13 |  | ||||||
|  |  | ||||||
| (websocket) WebSocketMessagePtr is a unique_ptr instead of a shared_ptr |  | ||||||
|  |  | ||||||
| ## [9.2.4] - 2020-04-13 |  | ||||||
|  |  | ||||||
| (websocket) use persistent member variable as temp variables to encode/decode zlib messages in order to reduce transient allocations |  | ||||||
|  |  | ||||||
| ## [9.2.3] - 2020-04-13 |  | ||||||
|  |  | ||||||
| (ws) add a --runtime option to ws cobra_subscribe to optionally limit how much time it will run |  | ||||||
|  |  | ||||||
| ## [9.2.2] - 2020-04-04 |  | ||||||
|  |  | ||||||
| (third_party deps) fix #177, update bundled spdlog to 1.6.0 |  | ||||||
|  |  | ||||||
| ## [9.2.1] - 2020-04-04 |  | ||||||
|  |  | ||||||
| (windows) when using OpenSSL, the system store is used to populate the cacert. No need to ship a cacert.pem file with your app. |  | ||||||
|  |  | ||||||
| ## [9.2.0] - 2020-04-04 | ## [9.2.0] - 2020-04-04 | ||||||
|  |  | ||||||
| (windows) ci: windows build with TLS (mbedtls) + verify that we can be build with OpenSSL | (windows) ci: windows build with TLS (mbedtls) + verify that we can be build with OpenSSL | ||||||
|   | |||||||
| @@ -18,12 +18,10 @@ There is a unittest which can be executed by typing `make test`. | |||||||
| Options for building: | Options for building: | ||||||
|  |  | ||||||
| * `-DUSE_TLS=1` will enable TLS support | * `-DUSE_TLS=1` will enable TLS support | ||||||
| * `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows) | * `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support (default on Windows) | ||||||
| * `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support |  | ||||||
| * `-DUSE_WS=1` will build the ws interactive command line tool | * `-DUSE_WS=1` will build the ws interactive command line tool | ||||||
| * `-DUSE_TEST=1` will build the unittest |  | ||||||
|  |  | ||||||
| If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file (not maintained much though) or rather the [github actions](https://github.com/machinezone/IXWebSocket/blob/master/.github/workflows/ccpp.yml#L40) which have instructions for building dependencies. | If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file that has instructions for building dependencies. | ||||||
|  |  | ||||||
| It is also possible to externally include the project, so that everything is fetched over the wire when you build like so: | It is also possible to externally include the project, so that everything is fetched over the wire when you build like so: | ||||||
|  |  | ||||||
| @@ -61,7 +59,7 @@ Note that the version listed here might not be the latest one. See Bintray or th | |||||||
| There is a Dockerfile for running the unittest on Linux, and to run the `ws` tool. It is also available on the docker registry. | There is a Dockerfile for running the unittest on Linux, and to run the `ws` tool. It is also available on the docker registry. | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
| docker run docker.pkg.github.com/machinezone/ixwebsocket/ws:latest --help | docker run bsergean/ws | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| To use docker-compose you must make a docker container first. | To use docker-compose you must make a docker container first. | ||||||
|   | |||||||
| @@ -38,7 +38,8 @@ The regression test is running after each commit on github actions for multiple | |||||||
|  |  | ||||||
| ## Limitations | ## Limitations | ||||||
|  |  | ||||||
| * On some configuration (mostly Android) certificate validation needs to be setup so that SocketTLSOptions.caFile point to a pem file, such as the one distributed by Firefox. Unless that setup is done connecting to a wss endpoint will display an error. With mbedtls the message will contain `error in handshake : X509 - Certificate verification failed, e.g. CRL, CA or signature check failed`. | * On Android, or when using MbedTLS certificate validation needs to be setup so that SocketTLSOptions.caFile point to a pem file, such as the one distributed by [Firefox](https://curl.haxx.se/docs/caextract.html). Unless that setup is done connecting to a wss endpoint will display an error. On Windows with mbedtls the message will contain `error in handshake : X509 - Certificate verification failed, e.g. CRL, CA or signature check failed`. | ||||||
|  | * There is no convenient way to embed a ca cert. | ||||||
| * Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that. | * Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that. | ||||||
| * The server code is using select to detect incoming data, and creates one OS thread per connection. This is not as scalable as strategies using epoll or kqueue. | * The server code is using select to detect incoming data, and creates one OS thread per connection. This is not as scalable as strategies using epoll or kqueue. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ Notes on how we can update the different packages for ixwebsocket. | |||||||
|  |  | ||||||
| Visit the [releases](https://github.com/machinezone/IXWebSocket/releases) page on Github. A tag must have been made first. | Visit the [releases](https://github.com/machinezone/IXWebSocket/releases) page on Github. A tag must have been made first. | ||||||
|  |  | ||||||
| Download the latest entry. | Download the latest entry.  | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
| $ cd /tmp | $ cd /tmp | ||||||
|   | |||||||
| @@ -447,7 +447,7 @@ Additional TLS options can be configured by passing a `ix::SocketTLSOptions` ins | |||||||
| webSocket.setTLSOptions({ | webSocket.setTLSOptions({ | ||||||
|     .certFile = "path/to/cert/file.pem", |     .certFile = "path/to/cert/file.pem", | ||||||
|     .keyFile = "path/to/key/file.pem", |     .keyFile = "path/to/key/file.pem", | ||||||
|     .caFile = "path/to/trust/bundle/file.pem", // as a file, or in memory buffer in PEM format |     .caFile = "path/to/trust/bundle/file.pem", | ||||||
|     .tls = true // required in server mode |     .tls = true // required in server mode | ||||||
| }); | }); | ||||||
| ``` | ``` | ||||||
| @@ -461,7 +461,6 @@ On a server, this is necessary for TLS support. | |||||||
| Specifying `caFile` configures the trusted roots bundle file (in PEM format) that will be used to verify peer certificates. | Specifying `caFile` configures the trusted roots bundle file (in PEM format) that will be used to verify peer certificates. | ||||||
|  - The special value of `SYSTEM` (the default) indicates that the system-configured trust bundle should be used; this is generally what you want when connecting to any publicly exposed API/server. |  - The special value of `SYSTEM` (the default) indicates that the system-configured trust bundle should be used; this is generally what you want when connecting to any publicly exposed API/server. | ||||||
|  - The special value of `NONE` can be used to disable peer verification; this is only recommended to rule out certificate verification when testing connectivity. |  - The special value of `NONE` can be used to disable peer verification; this is only recommended to rule out certificate verification when testing connectivity. | ||||||
|  - If the value contain the special value `-----BEGIN CERTIFICATE-----`, the value will be read from memory, and not from a file. This is convenient on platforms like Android where reading / writing to the file system can be challenging without proper permissions, or without knowing the location of a temp directory. |  | ||||||
|  |  | ||||||
| For a client, specifying `caFile` can be used if connecting to a server that uses a self-signed cert, or when using a custom CA in an internal environment. | For a client, specifying `caFile` can be used if connecting to a server that uses a self-signed cert, or when using a custom CA in an internal environment. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,19 +4,16 @@ | |||||||
| # | # | ||||||
|  |  | ||||||
| set (IXBOTS_SOURCES | set (IXBOTS_SOURCES | ||||||
|     ixbots/IXCobraBot.cpp |  | ||||||
|     ixbots/IXCobraToSentryBot.cpp |     ixbots/IXCobraToSentryBot.cpp | ||||||
|     ixbots/IXCobraToStatsdBot.cpp |     ixbots/IXCobraToStatsdBot.cpp | ||||||
|     ixbots/IXCobraToStdoutBot.cpp |     ixbots/IXQueueManager.cpp | ||||||
|     ixbots/IXStatsdClient.cpp |     ixbots/IXStatsdClient.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| set (IXBOTS_HEADERS | set (IXBOTS_HEADERS | ||||||
|     ixbots/IXCobraBot.h |  | ||||||
|     ixbots/IXCobraBotConfig.h |  | ||||||
|     ixbots/IXCobraToSentryBot.h |     ixbots/IXCobraToSentryBot.h | ||||||
|     ixbots/IXCobraToStatsdBot.h |     ixbots/IXCobraToStatsdBot.h | ||||||
|     ixbots/IXCobraToStdoutBot.h |     ixbots/IXQueueManager.h | ||||||
|     ixbots/IXStatsdClient.h |     ixbots/IXStatsdClient.h | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -30,6 +27,11 @@ if (NOT JSONCPP_FOUND) | |||||||
|   set(JSONCPP_INCLUDE_DIRS ../third_party/jsoncpp) |   set(JSONCPP_INCLUDE_DIRS ../third_party/jsoncpp) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | find_package(SpdLog) | ||||||
|  | if (NOT SPDLOG_FOUND) | ||||||
|  |   set(SPDLOG_INCLUDE_DIRS ../third_party/spdlog/include) | ||||||
|  | endif() | ||||||
|  |  | ||||||
| set(IXBOTS_INCLUDE_DIRS | set(IXBOTS_INCLUDE_DIRS | ||||||
|     . |     . | ||||||
|     .. |     .. | ||||||
|   | |||||||
| @@ -1,268 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXCobraBot.cpp |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "IXCobraBot.h" |  | ||||||
|  |  | ||||||
| #include <ixcobra/IXCobraConnection.h> |  | ||||||
| #include <ixcore/utils/IXCoreLogger.h> |  | ||||||
|  |  | ||||||
| #include <algorithm> |  | ||||||
| #include <chrono> |  | ||||||
| #include <sstream> |  | ||||||
| #include <thread> |  | ||||||
| #include <vector> |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     int64_t CobraBot::run(const CobraBotConfig& botConfig) |  | ||||||
|     { |  | ||||||
|         auto config = botConfig.cobraConfig; |  | ||||||
|         auto channel = botConfig.channel; |  | ||||||
|         auto filter = botConfig.filter; |  | ||||||
|         auto position = botConfig.position; |  | ||||||
|         auto enableHeartbeat = botConfig.enableHeartbeat; |  | ||||||
|         auto heartBeatTimeout = botConfig.heartBeatTimeout; |  | ||||||
|         auto runtime = botConfig.runtime; |  | ||||||
|         auto maxEventsPerMinute = botConfig.maxEventsPerMinute; |  | ||||||
|         auto limitReceivedEvents = botConfig.limitReceivedEvents; |  | ||||||
|  |  | ||||||
|         ix::CobraConnection conn; |  | ||||||
|         conn.configure(config); |  | ||||||
|         conn.connect(); |  | ||||||
|  |  | ||||||
|         std::atomic<uint64_t> sentCount(0); |  | ||||||
|         std::atomic<uint64_t> receivedCount(0); |  | ||||||
|         uint64_t sentCountTotal(0); |  | ||||||
|         uint64_t receivedCountTotal(0); |  | ||||||
|         uint64_t sentCountPerSecs(0); |  | ||||||
|         uint64_t receivedCountPerSecs(0); |  | ||||||
|         std::atomic<int> receivedCountPerMinutes(0); |  | ||||||
|         std::atomic<bool> stop(false); |  | ||||||
|         std::atomic<bool> throttled(false); |  | ||||||
|         std::atomic<bool> fatalCobraError(false); |  | ||||||
|         int minuteCounter = 0; |  | ||||||
|  |  | ||||||
|         auto timer = [&sentCount, |  | ||||||
|                       &receivedCount, |  | ||||||
|                       &sentCountTotal, |  | ||||||
|                       &receivedCountTotal, |  | ||||||
|                       &sentCountPerSecs, |  | ||||||
|                       &receivedCountPerSecs, |  | ||||||
|                       &receivedCountPerMinutes, |  | ||||||
|                       &minuteCounter, |  | ||||||
|                       &stop] { |  | ||||||
|             while (!stop) |  | ||||||
|             { |  | ||||||
|                 // |  | ||||||
|                 // We cannot write to sentCount and receivedCount |  | ||||||
|                 // as those are used externally, so we need to introduce |  | ||||||
|                 // our own counters |  | ||||||
|                 // |  | ||||||
|                 std::stringstream ss; |  | ||||||
|                 ss << "messages received " |  | ||||||
|                    << receivedCountPerSecs |  | ||||||
|                    << " " |  | ||||||
|                    << receivedCountTotal |  | ||||||
|                    << " sent "  |  | ||||||
|                    << sentCountPerSecs |  | ||||||
|                    << " " |  | ||||||
|                    << sentCountTotal; |  | ||||||
|                 CoreLogger::info(ss.str()); |  | ||||||
|  |  | ||||||
|                 receivedCountPerSecs = receivedCount - receivedCountTotal; |  | ||||||
|                 sentCountPerSecs = sentCount - sentCountTotal; |  | ||||||
|  |  | ||||||
|                 receivedCountTotal += receivedCountPerSecs; |  | ||||||
|                 sentCountTotal += sentCountPerSecs; |  | ||||||
|  |  | ||||||
|                 auto duration = std::chrono::seconds(1); |  | ||||||
|                 std::this_thread::sleep_for(duration); |  | ||||||
|  |  | ||||||
|                 if (minuteCounter++ == 60) |  | ||||||
|                 { |  | ||||||
|                     receivedCountPerMinutes = 0; |  | ||||||
|                     minuteCounter = 0; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             CoreLogger::info("timer thread done"); |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         std::thread t1(timer); |  | ||||||
|  |  | ||||||
|         auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat, &heartBeatTimeout, &fatalCobraError] { |  | ||||||
|             std::string state("na"); |  | ||||||
|  |  | ||||||
|             if (!enableHeartbeat) return; |  | ||||||
|  |  | ||||||
|             while (!stop) |  | ||||||
|             { |  | ||||||
|                 std::stringstream ss; |  | ||||||
|                 ss << "messages received " << receivedCount; |  | ||||||
|                 ss << "messages sent " << sentCount; |  | ||||||
|  |  | ||||||
|                 std::string currentState = ss.str(); |  | ||||||
|  |  | ||||||
|                 if (currentState == state) |  | ||||||
|                 { |  | ||||||
|                     CoreLogger::error("no messages received or sent for 1 minute, exiting"); |  | ||||||
|                     fatalCobraError = true; |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 state = currentState; |  | ||||||
|  |  | ||||||
|                 auto duration = std::chrono::seconds(heartBeatTimeout); |  | ||||||
|                 std::this_thread::sleep_for(duration); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             CoreLogger::info("heartbeat thread done"); |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         std::thread t2(heartbeat); |  | ||||||
|  |  | ||||||
|         std::string subscriptionPosition(position); |  | ||||||
|  |  | ||||||
|         conn.setEventCallback([this, |  | ||||||
|                                &conn, |  | ||||||
|                                &channel, |  | ||||||
|                                &filter, |  | ||||||
|                                &subscriptionPosition, |  | ||||||
|                                &throttled, |  | ||||||
|                                &receivedCount, |  | ||||||
|                                &receivedCountPerMinutes, |  | ||||||
|                                maxEventsPerMinute, |  | ||||||
|                                limitReceivedEvents, |  | ||||||
|                                &fatalCobraError, |  | ||||||
|                                &sentCount](const CobraEventPtr& event) { |  | ||||||
|             if (event->type == ix::CobraEventType::Open) |  | ||||||
|             { |  | ||||||
|                 CoreLogger::info("Subscriber connected"); |  | ||||||
|  |  | ||||||
|                 for (auto&& it : event->headers) |  | ||||||
|                 { |  | ||||||
|                     CoreLogger::info(it.first + "::" + it.second); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Closed) |  | ||||||
|             { |  | ||||||
|                 CoreLogger::info("Subscriber closed: {}" + event->errMsg); |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Authenticated) |  | ||||||
|             { |  | ||||||
|                 CoreLogger::info("Subscriber authenticated"); |  | ||||||
|                 CoreLogger::info("Subscribing to " + channel); |  | ||||||
|                 CoreLogger::info("Subscribing at position " + subscriptionPosition); |  | ||||||
|                 CoreLogger::info("Subscribing with filter " + filter); |  | ||||||
|                 conn.subscribe(channel, filter, subscriptionPosition, |  | ||||||
|                     [&sentCount, &receivedCountPerMinutes, |  | ||||||
|                      maxEventsPerMinute, limitReceivedEvents, |  | ||||||
|                      &throttled, &receivedCount, |  | ||||||
|                      &subscriptionPosition, &fatalCobraError, |  | ||||||
|                      this](const Json::Value& msg, const std::string& position) { |  | ||||||
|                         subscriptionPosition = position; |  | ||||||
|                         ++receivedCount; |  | ||||||
|  |  | ||||||
|                         ++receivedCountPerMinutes; |  | ||||||
|                         if (limitReceivedEvents) |  | ||||||
|                         { |  | ||||||
|                             if (receivedCountPerMinutes > maxEventsPerMinute) |  | ||||||
|                             { |  | ||||||
|                                 return; |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         // If we cannot send to sentry fast enough, drop the message |  | ||||||
|                         if (throttled) |  | ||||||
|                         { |  | ||||||
|                             return; |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         _onBotMessageCallback( |  | ||||||
|                             msg, position, throttled, |  | ||||||
|                             fatalCobraError, sentCount); |  | ||||||
|                     }); |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Subscribed) |  | ||||||
|             { |  | ||||||
|                 CoreLogger::info("Subscriber: subscribed to channel " + event->subscriptionId); |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::UnSubscribed) |  | ||||||
|             { |  | ||||||
|                 CoreLogger::info("Subscriber: unsubscribed from channel " + event->subscriptionId); |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Error) |  | ||||||
|             { |  | ||||||
|                 CoreLogger::error("Subscriber: error " + event->errMsg); |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Published) |  | ||||||
|             { |  | ||||||
|                 CoreLogger::error("Published message hacked: " + std::to_string(event->msgId)); |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Pong) |  | ||||||
|             { |  | ||||||
|                 CoreLogger::info("Received websocket pong: " + event->errMsg); |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::HandshakeError) |  | ||||||
|             { |  | ||||||
|                 CoreLogger::error("Subscriber: Handshake error: " + event->errMsg); |  | ||||||
|                 fatalCobraError = true; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::AuthenticationError) |  | ||||||
|             { |  | ||||||
|                 CoreLogger::error("Subscriber: Authentication error: " + event->errMsg); |  | ||||||
|                 fatalCobraError = true; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::SubscriptionError) |  | ||||||
|             { |  | ||||||
|                 CoreLogger::error("Subscriber: Subscription error: " + event->errMsg); |  | ||||||
|                 fatalCobraError = true; |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         // Run forever |  | ||||||
|         if (runtime == -1) |  | ||||||
|         { |  | ||||||
|             while (true) |  | ||||||
|             { |  | ||||||
|                 auto duration = std::chrono::seconds(1); |  | ||||||
|                 std::this_thread::sleep_for(duration); |  | ||||||
|  |  | ||||||
|                 if (fatalCobraError) break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         // Run for a duration, used by unittesting now |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             for (int i = 0; i < runtime; ++i) |  | ||||||
|             { |  | ||||||
|                 auto duration = std::chrono::seconds(1); |  | ||||||
|                 std::this_thread::sleep_for(duration); |  | ||||||
|  |  | ||||||
|                 if (fatalCobraError) break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // |  | ||||||
|         // Cleanup. |  | ||||||
|         // join all the bg threads and stop them. |  | ||||||
|         // |  | ||||||
|         conn.disconnect(); |  | ||||||
|         stop = true; |  | ||||||
|  |  | ||||||
|         // progress thread |  | ||||||
|         t1.join(); |  | ||||||
|  |  | ||||||
|         // heartbeat thread |  | ||||||
|         if (t2.joinable()) t2.join(); |  | ||||||
|  |  | ||||||
|         return fatalCobraError ? -1 : (int64_t) sentCount; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void CobraBot::setOnBotMessageCallback(const OnBotMessageCallback& callback) |  | ||||||
|     { |  | ||||||
|         _onBotMessageCallback = callback; |  | ||||||
|     } |  | ||||||
| } // namespace ix |  | ||||||
| @@ -1,34 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXCobraBot.h |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <atomic> |  | ||||||
| #include <functional> |  | ||||||
| #include "IXCobraBotConfig.h" |  | ||||||
| #include <json/json.h> |  | ||||||
| #include <stddef.h> |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     using OnBotMessageCallback = std::function<void(const Json::Value&, |  | ||||||
|                                                     const std::string&, |  | ||||||
|                                                     std::atomic<bool>&, |  | ||||||
|                                                     std::atomic<bool>&, |  | ||||||
|                                                     std::atomic<uint64_t>&)>; |  | ||||||
|  |  | ||||||
|     class CobraBot |  | ||||||
|     { |  | ||||||
|     public: |  | ||||||
|         CobraBot() = default; |  | ||||||
|  |  | ||||||
|         int64_t run(const CobraBotConfig& botConfig); |  | ||||||
|         void setOnBotMessageCallback(const OnBotMessageCallback& callback); |  | ||||||
|  |  | ||||||
|     private: |  | ||||||
|         OnBotMessageCallback _onBotMessageCallback; |  | ||||||
|     }; |  | ||||||
| } // namespace ix |  | ||||||
| @@ -1,31 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXCobraBotConfig.h |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <string> |  | ||||||
| #include <limits> |  | ||||||
| #include <ixcobra/IXCobraConfig.h> |  | ||||||
|  |  | ||||||
| #ifdef max |  | ||||||
| #undef max |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     struct CobraBotConfig |  | ||||||
|     { |  | ||||||
|         CobraConfig cobraConfig; |  | ||||||
|         std::string channel; |  | ||||||
|         std::string filter; |  | ||||||
|         std::string position = std::string("$"); |  | ||||||
|         bool enableHeartbeat = true; |  | ||||||
|         int heartBeatTimeout = 60; |  | ||||||
|         int runtime = -1; |  | ||||||
|         int maxEventsPerMinute = std::numeric_limits<int>::max(); |  | ||||||
|         bool limitReceivedEvents = false; |  | ||||||
|     }; |  | ||||||
| } // namespace ix |  | ||||||
| @@ -5,72 +5,301 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "IXCobraToSentryBot.h" | #include "IXCobraToSentryBot.h" | ||||||
|  | #include "IXQueueManager.h" | ||||||
| #include "IXCobraBot.h" |  | ||||||
| #include <ixcobra/IXCobraConnection.h> |  | ||||||
| #include <ixcore/utils/IXCoreLogger.h> |  | ||||||
|  |  | ||||||
| #include <chrono> | #include <chrono> | ||||||
|  | #include <ixcobra/IXCobraConnection.h> | ||||||
|  | #include <spdlog/spdlog.h> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  | #include <thread> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     int64_t cobra_to_sentry_bot(const CobraBotConfig& config, |     int cobra_to_sentry_bot(const CobraConfig& config, | ||||||
|                                 SentryClient& sentryClient, |                             const std::string& channel, | ||||||
|                                 bool verbose) |                             const std::string& filter, | ||||||
|  |                             const std::string& position, | ||||||
|  |                             SentryClient& sentryClient, | ||||||
|  |                             bool verbose, | ||||||
|  |                             bool strict, | ||||||
|  |                             size_t maxQueueSize, | ||||||
|  |                             bool enableHeartbeat, | ||||||
|  |                             int runtime) | ||||||
|     { |     { | ||||||
|         CobraBot bot; |         ix::CobraConnection conn; | ||||||
|         bot.setOnBotMessageCallback([&sentryClient, &verbose](const Json::Value& msg, |         conn.configure(config); | ||||||
|                                                     const std::string& /*position*/, |         conn.connect(); | ||||||
|                                                     std::atomic<bool>& throttled, |  | ||||||
|                                                     std::atomic<bool>& /*fatalCobraError*/, |  | ||||||
|                                                     std::atomic<uint64_t>& sentCount) -> void { |  | ||||||
|             sentryClient.send(msg, verbose, |  | ||||||
|                 [&sentCount, &throttled](const HttpResponsePtr& response) { |  | ||||||
|                 if (!response) |  | ||||||
|                 { |  | ||||||
|                     CoreLogger::warn("Null HTTP Response"); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if (response->statusCode == 200) |         Json::FastWriter jsonWriter; | ||||||
|                 { |         std::atomic<uint64_t> sentCount(0); | ||||||
|                     sentCount++; |         std::atomic<uint64_t> receivedCount(0); | ||||||
|                 } |         std::atomic<bool> errorSending(false); | ||||||
|                 else |         std::atomic<bool> stop(false); | ||||||
|                 { |         std::atomic<bool> throttled(false); | ||||||
|                     CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode)); |         std::atomic<bool> fatalCobraError(false); | ||||||
|                     CoreLogger::error("Response: " + response->payload); |  | ||||||
|  |  | ||||||
|                     // Error 429 Too Many Requests |         QueueManager queueManager(maxQueueSize); | ||||||
|                     if (response->statusCode == 429) |  | ||||||
|  |         auto timer = [&sentCount, &receivedCount, &stop] { | ||||||
|  |             while (!stop) | ||||||
|  |             { | ||||||
|  |                 spdlog::info("messages received {} sent {}", receivedCount, sentCount); | ||||||
|  |  | ||||||
|  |                 auto duration = std::chrono::seconds(1); | ||||||
|  |                 std::this_thread::sleep_for(duration); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             spdlog::info("timer thread done"); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         std::thread t1(timer); | ||||||
|  |  | ||||||
|  |         auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] { | ||||||
|  |             std::string state("na"); | ||||||
|  |  | ||||||
|  |             if (!enableHeartbeat) return; | ||||||
|  |  | ||||||
|  |             while (!stop) | ||||||
|  |             { | ||||||
|  |                 std::stringstream ss; | ||||||
|  |                 ss << "messages received " << receivedCount; | ||||||
|  |                 ss << "messages sent " << sentCount; | ||||||
|  |  | ||||||
|  |                 std::string currentState = ss.str(); | ||||||
|  |  | ||||||
|  |                 if (currentState == state) | ||||||
|  |                 { | ||||||
|  |                     spdlog::error("no messages received or sent for 1 minute, exiting"); | ||||||
|  |                     exit(1); | ||||||
|  |                 } | ||||||
|  |                 state = currentState; | ||||||
|  |  | ||||||
|  |                 auto duration = std::chrono::minutes(1); | ||||||
|  |                 std::this_thread::sleep_for(duration); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             spdlog::info("heartbeat thread done"); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         std::thread t2(heartbeat); | ||||||
|  |  | ||||||
|  |         auto sentrySender = | ||||||
|  |             [&queueManager, verbose, &errorSending, &sentCount, &stop, &throttled, &sentryClient] { | ||||||
|  |  | ||||||
|  |                 while (true) | ||||||
|  |                 { | ||||||
|  |                     Json::Value msg = queueManager.pop(); | ||||||
|  |  | ||||||
|  |                     if (stop) break; | ||||||
|  |                     if (msg.isNull()) continue; | ||||||
|  |  | ||||||
|  |                     auto ret = sentryClient.send(msg, verbose); | ||||||
|  |                     HttpResponsePtr response = ret.first; | ||||||
|  |  | ||||||
|  |                     if (!response) | ||||||
|                     { |                     { | ||||||
|                         auto retryAfter = response->headers["Retry-After"]; |                         spdlog::warn("Null HTTP Response"); | ||||||
|                         std::stringstream ss; |                         continue; | ||||||
|                         ss << retryAfter; |                     } | ||||||
|                         int seconds; |  | ||||||
|                         ss >> seconds; |  | ||||||
|  |  | ||||||
|                         if (!ss.eof() || ss.fail()) |                     if (verbose) | ||||||
|  |                     { | ||||||
|  |                         for (auto it : response->headers) | ||||||
|                         { |                         { | ||||||
|                             seconds = 30; |                             spdlog::info("{}: {}", it.first, it.second); | ||||||
|                             CoreLogger::warn("Error parsing Retry-After header. " |  | ||||||
|                                              "Using " + retryAfter + " for the sleep duration"); |  | ||||||
|                         } |                         } | ||||||
|  |  | ||||||
|                         CoreLogger::warn("Error 429 - Too Many Requests. ws will sleep " |                         spdlog::info("Upload size: {}", response->uploadSize); | ||||||
|                                          "and retry after " + retryAfter + " seconds"); |                         spdlog::info("Download size: {}", response->downloadSize); | ||||||
|  |  | ||||||
|                         throttled = true; |                         spdlog::info("Status: {}", response->statusCode); | ||||||
|                         auto duration = std::chrono::seconds(seconds); |                         if (response->errorCode != HttpErrorCode::Ok) | ||||||
|                         std::this_thread::sleep_for(duration); |                         { | ||||||
|                         throttled = false; |                             spdlog::info("error message: {}", response->errorMsg); | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         if (response->headers["Content-Type"] != "application/octet-stream") | ||||||
|  |                         { | ||||||
|  |                             spdlog::info("payload: {}", response->payload); | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|  |                     if (response->statusCode != 200) | ||||||
|  |                     { | ||||||
|  |                         spdlog::error("Error sending data to sentry: {}", response->statusCode); | ||||||
|  |                         spdlog::error("Body: {}", ret.second); | ||||||
|  |                         spdlog::error("Response: {}", response->payload); | ||||||
|  |                         errorSending = true; | ||||||
|  |  | ||||||
|  |                         // Error 429 Too Many Requests | ||||||
|  |                         if (response->statusCode == 429) | ||||||
|  |                         { | ||||||
|  |                             auto retryAfter = response->headers["Retry-After"]; | ||||||
|  |                             std::stringstream ss; | ||||||
|  |                             ss << retryAfter; | ||||||
|  |                             int seconds; | ||||||
|  |                             ss >> seconds; | ||||||
|  |  | ||||||
|  |                             if (!ss.eof() || ss.fail()) | ||||||
|  |                             { | ||||||
|  |                                 seconds = 30; | ||||||
|  |                                 spdlog::warn("Error parsing Retry-After header. " | ||||||
|  |                                              "Using {} for the sleep duration", | ||||||
|  |                                              seconds); | ||||||
|  |                             } | ||||||
|  |  | ||||||
|  |                             spdlog::warn("Error 429 - Too Many Requests. ws will sleep " | ||||||
|  |                                          "and retry after {} seconds", | ||||||
|  |                                          retryAfter); | ||||||
|  |  | ||||||
|  |                             throttled = true; | ||||||
|  |                             auto duration = std::chrono::seconds(seconds); | ||||||
|  |                             std::this_thread::sleep_for(duration); | ||||||
|  |                             throttled = false; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         ++sentCount; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (stop) break; | ||||||
|                 } |                 } | ||||||
|             }); |  | ||||||
|  |                 spdlog::info("sentrySender thread done"); | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |         std::thread t3(sentrySender); | ||||||
|  |  | ||||||
|  |         conn.setEventCallback([&conn, | ||||||
|  |                                &channel, | ||||||
|  |                                &filter, | ||||||
|  |                                &position, | ||||||
|  |                                &jsonWriter, | ||||||
|  |                                verbose, | ||||||
|  |                                &throttled, | ||||||
|  |                                &receivedCount, | ||||||
|  |                                &fatalCobraError, | ||||||
|  |                                &queueManager](ix::CobraConnectionEventType eventType, | ||||||
|  |                                               const std::string& errMsg, | ||||||
|  |                                               const ix::WebSocketHttpHeaders& headers, | ||||||
|  |                                               const std::string& subscriptionId, | ||||||
|  |                                               CobraConnection::MsgId msgId) { | ||||||
|  |             if (eventType == ix::CobraConnection_EventType_Open) | ||||||
|  |             { | ||||||
|  |                 spdlog::info("Subscriber connected"); | ||||||
|  |  | ||||||
|  |                 for (auto it : headers) | ||||||
|  |                 { | ||||||
|  |                     spdlog::info("{}: {}", it.first, it.second); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (eventType == ix::CobraConnection_EventType_Closed) | ||||||
|  |             { | ||||||
|  |                 spdlog::info("Subscriber closed"); | ||||||
|  |             } | ||||||
|  |             else if (eventType == ix::CobraConnection_EventType_Authenticated) | ||||||
|  |             { | ||||||
|  |                 spdlog::info("Subscriber authenticated"); | ||||||
|  |                 conn.subscribe(channel, | ||||||
|  |                                filter, | ||||||
|  |                                position, | ||||||
|  |                                [&jsonWriter, verbose, &throttled, &receivedCount, &queueManager]( | ||||||
|  |                                    const Json::Value& msg, const std::string& position) { | ||||||
|  |                                    if (verbose) | ||||||
|  |                                    { | ||||||
|  |                                        spdlog::info("Subscriber received message {} -> {}", position, jsonWriter.write(msg)); | ||||||
|  |                                    } | ||||||
|  |  | ||||||
|  |                                    // If we cannot send to sentry fast enough, drop the message | ||||||
|  |                                    if (throttled) | ||||||
|  |                                    { | ||||||
|  |                                        return; | ||||||
|  |                                    } | ||||||
|  |  | ||||||
|  |                                    ++receivedCount; | ||||||
|  |                                    queueManager.add(msg); | ||||||
|  |                                }); | ||||||
|  |             } | ||||||
|  |             else if (eventType == ix::CobraConnection_EventType_Subscribed) | ||||||
|  |             { | ||||||
|  |                 spdlog::info("Subscriber: subscribed to channel {}", subscriptionId); | ||||||
|  |             } | ||||||
|  |             else if (eventType == ix::CobraConnection_EventType_UnSubscribed) | ||||||
|  |             { | ||||||
|  |                 spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId); | ||||||
|  |             } | ||||||
|  |             else if (eventType == ix::CobraConnection_EventType_Error) | ||||||
|  |             { | ||||||
|  |                 spdlog::error("Subscriber: error {}", errMsg); | ||||||
|  |             } | ||||||
|  |             else if (eventType == ix::CobraConnection_EventType_Published) | ||||||
|  |             { | ||||||
|  |                 spdlog::error("Published message hacked: {}", msgId); | ||||||
|  |             } | ||||||
|  |             else if (eventType == ix::CobraConnection_EventType_Pong) | ||||||
|  |             { | ||||||
|  |                 spdlog::info("Received websocket pong"); | ||||||
|  |             } | ||||||
|  |             else if (eventType == ix::CobraConnection_EventType_Handshake_Error) | ||||||
|  |             { | ||||||
|  |                 spdlog::error("Subscriber: Handshake error: {}", errMsg); | ||||||
|  |                 fatalCobraError = true; | ||||||
|  |             } | ||||||
|  |             else if (eventType == ix::CobraConnection_EventType_Authentication_Error) | ||||||
|  |             { | ||||||
|  |                 spdlog::error("Subscriber: Authentication error: {}", errMsg); | ||||||
|  |                 fatalCobraError = true; | ||||||
|  |             } | ||||||
|  |             else if (eventType == ix::CobraConnection_EventType_Subscription_Error) | ||||||
|  |             { | ||||||
|  |                 spdlog::error("Subscriber: Subscription error: {}", errMsg); | ||||||
|  |                 fatalCobraError = true; | ||||||
|  |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         return bot.run(config); |         // Run forever | ||||||
|  |         if (runtime == -1) | ||||||
|  |         { | ||||||
|  |             while (true) | ||||||
|  |             { | ||||||
|  |                 auto duration = std::chrono::seconds(1); | ||||||
|  |                 std::this_thread::sleep_for(duration); | ||||||
|  |  | ||||||
|  |                 if (strict && errorSending) break; | ||||||
|  |                 if (fatalCobraError) break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         // Run for a duration, used by unittesting now | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             for (int i = 0 ; i < runtime; ++i) | ||||||
|  |             { | ||||||
|  |                 auto duration = std::chrono::seconds(1); | ||||||
|  |                 std::this_thread::sleep_for(duration); | ||||||
|  |  | ||||||
|  |                 if (strict && errorSending) break; | ||||||
|  |                 if (fatalCobraError) break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // | ||||||
|  |         // Cleanup. | ||||||
|  |         // join all the bg threads and stop them. | ||||||
|  |         // | ||||||
|  |         conn.disconnect(); | ||||||
|  |         stop = true; | ||||||
|  |  | ||||||
|  |         // progress thread | ||||||
|  |         t1.join(); | ||||||
|  |  | ||||||
|  |         // heartbeat thread | ||||||
|  |         if (t2.joinable()) t2.join(); | ||||||
|  |  | ||||||
|  |         // sentry sender thread | ||||||
|  |         t3.join(); | ||||||
|  |  | ||||||
|  |         return ((strict && errorSending) || fatalCobraError) ? -1 : (int) sentCount; | ||||||
|     } |     } | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -5,14 +5,20 @@ | |||||||
|  */ |  */ | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <cstdint> | #include <ixcobra/IXCobraConfig.h> | ||||||
| #include "IXCobraBotConfig.h" |  | ||||||
| #include <ixsentry/IXSentryClient.h> | #include <ixsentry/IXSentryClient.h> | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     int64_t cobra_to_sentry_bot(const CobraBotConfig& config, |     int cobra_to_sentry_bot(const CobraConfig& config, | ||||||
|                                 SentryClient& sentryClient, |                             const std::string& channel, | ||||||
|                                 bool verbose); |                             const std::string& filter, | ||||||
|  |                             const std::string& position, | ||||||
|  |                             SentryClient& sentryClient, | ||||||
|  |                             bool verbose, | ||||||
|  |                             bool strict, | ||||||
|  |                             size_t maxQueueSize, | ||||||
|  |                             bool enableHeartbeat, | ||||||
|  |                             int runtime); | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -5,13 +5,16 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "IXCobraToStatsdBot.h" | #include "IXCobraToStatsdBot.h" | ||||||
|  | #include "IXQueueManager.h" | ||||||
| #include "IXCobraBot.h" |  | ||||||
| #include "IXStatsdClient.h" | #include "IXStatsdClient.h" | ||||||
|  |  | ||||||
|  | #include <atomic> | ||||||
| #include <chrono> | #include <chrono> | ||||||
|  | #include <condition_variable> | ||||||
| #include <ixcobra/IXCobraConnection.h> | #include <ixcobra/IXCobraConnection.h> | ||||||
| #include <ixcore/utils/IXCoreLogger.h> | #include <spdlog/spdlog.h> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  | #include <thread> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| @@ -53,22 +56,84 @@ namespace ix | |||||||
|         return val; |         return val; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int64_t cobra_to_statsd_bot(const ix::CobraBotConfig& config, |     int cobra_to_statsd_bot(const ix::CobraConfig& config, | ||||||
|                                 StatsdClient& statsdClient, |                             const std::string& channel, | ||||||
|                                 const std::string& fields, |                             const std::string& filter, | ||||||
|                                 const std::string& gauge, |                             const std::string& position, | ||||||
|                                 const std::string& timer, |                             StatsdClient& statsdClient, | ||||||
|                                 bool verbose) |                             const std::string& fields, | ||||||
|  |                             const std::string& gauge, | ||||||
|  |                             const std::string& timer, | ||||||
|  |                             bool verbose, | ||||||
|  |                             size_t maxQueueSize, | ||||||
|  |                             bool enableHeartbeat, | ||||||
|  |                             int runtime) | ||||||
|     { |     { | ||||||
|  |         ix::CobraConnection conn; | ||||||
|  |         conn.configure(config); | ||||||
|  |         conn.connect(); | ||||||
|  |  | ||||||
|         auto tokens = parseFields(fields); |         auto tokens = parseFields(fields); | ||||||
|  |  | ||||||
|         CobraBot bot; |         Json::FastWriter jsonWriter; | ||||||
|         bot.setOnBotMessageCallback( |         std::atomic<uint64_t> sentCount(0); | ||||||
|             [&statsdClient, &tokens, &gauge, &timer, &verbose](const Json::Value& msg, |         std::atomic<uint64_t> receivedCount(0); | ||||||
|                                                      const std::string& /*position*/, |         std::atomic<bool> stop(false); | ||||||
|                                                      std::atomic<bool>& /*throttled*/, |         std::atomic<bool> fatalCobraError(false); | ||||||
|                                                      std::atomic<bool>& fatalCobraError, |  | ||||||
|                                                      std::atomic<uint64_t>& sentCount) -> void { |         QueueManager queueManager(maxQueueSize); | ||||||
|  |  | ||||||
|  |         auto progress = [&sentCount, &receivedCount, &stop] { | ||||||
|  |             while (!stop) | ||||||
|  |             { | ||||||
|  |                 spdlog::info("messages received {} sent {}", receivedCount, sentCount); | ||||||
|  |  | ||||||
|  |                 auto duration = std::chrono::seconds(1); | ||||||
|  |                 std::this_thread::sleep_for(duration); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             spdlog::info("timer thread done"); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         std::thread t1(progress); | ||||||
|  |  | ||||||
|  |         auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] { | ||||||
|  |             std::string state("na"); | ||||||
|  |  | ||||||
|  |             if (!enableHeartbeat) return; | ||||||
|  |  | ||||||
|  |             while (!stop) | ||||||
|  |             { | ||||||
|  |                 std::stringstream ss; | ||||||
|  |                 ss << "messages received " << receivedCount; | ||||||
|  |                 ss << "messages sent " << sentCount; | ||||||
|  |  | ||||||
|  |                 std::string currentState = ss.str(); | ||||||
|  |  | ||||||
|  |                 if (currentState == state) | ||||||
|  |                 { | ||||||
|  |                     spdlog::error("no messages received or sent for 1 minute, exiting"); | ||||||
|  |                     exit(1); | ||||||
|  |                 } | ||||||
|  |                 state = currentState; | ||||||
|  |  | ||||||
|  |                 auto duration = std::chrono::minutes(1); | ||||||
|  |                 std::this_thread::sleep_for(duration); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             spdlog::info("heartbeat thread done"); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         std::thread t2(heartbeat); | ||||||
|  |  | ||||||
|  |         auto statsdSender = [&statsdClient, &queueManager, &sentCount, &tokens, &stop, &gauge, &timer, &fatalCobraError, &verbose] { | ||||||
|  |             while (true) | ||||||
|  |             { | ||||||
|  |                 Json::Value msg = queueManager.pop(); | ||||||
|  |  | ||||||
|  |                 if (stop) return; | ||||||
|  |                 if (msg.isNull()) continue; | ||||||
|  |  | ||||||
|                 std::string id; |                 std::string id; | ||||||
|                 for (auto&& attr : tokens) |                 for (auto&& attr : tokens) | ||||||
|                 { |                 { | ||||||
| @@ -109,14 +174,14 @@ namespace ix | |||||||
|                     } |                     } | ||||||
|                     else |                     else | ||||||
|                     { |                     { | ||||||
|                         CoreLogger::error("Gauge " + gauge + " is not a numeric type"); |                         spdlog::error("Gauge {} is not a numberic type", gauge); | ||||||
|                         fatalCobraError = true; |                         fatalCobraError = true; | ||||||
|                         return; |                         break; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     if (verbose) |                     if (verbose) | ||||||
|                     { |                     { | ||||||
|                         CoreLogger::info(id + " - " + attrName + " -> " + std::to_string(x)); |                         spdlog::info("{} - {} -> {}", id, attrName, x); | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     if (!gauge.empty()) |                     if (!gauge.empty()) | ||||||
| @@ -129,9 +194,127 @@ namespace ix | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 sentCount++; |                 sentCount += 1; | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         std::thread t3(statsdSender); | ||||||
|  |  | ||||||
|  |         conn.setEventCallback( | ||||||
|  |             [&conn, &channel, &filter, &position, &jsonWriter, verbose, &queueManager, &receivedCount, &fatalCobraError]( | ||||||
|  |                 ix::CobraConnectionEventType eventType, | ||||||
|  |                 const std::string& errMsg, | ||||||
|  |                 const ix::WebSocketHttpHeaders& headers, | ||||||
|  |                 const std::string& subscriptionId, | ||||||
|  |                 CobraConnection::MsgId msgId) { | ||||||
|  |                 if (eventType == ix::CobraConnection_EventType_Open) | ||||||
|  |                 { | ||||||
|  |                     spdlog::info("Subscriber connected"); | ||||||
|  |  | ||||||
|  |                     for (auto it : headers) | ||||||
|  |                     { | ||||||
|  |                         spdlog::info("{}: {}", it.first, it.second); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if (eventType == ix::CobraConnection_EventType_Closed) | ||||||
|  |                 { | ||||||
|  |                     spdlog::info("Subscriber closed"); | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Authenticated) | ||||||
|  |                 { | ||||||
|  |                     spdlog::info("Subscriber authenticated"); | ||||||
|  |                     conn.subscribe(channel, | ||||||
|  |                                    filter, | ||||||
|  |                                    position, | ||||||
|  |                                    [&jsonWriter, &queueManager, verbose, &receivedCount]( | ||||||
|  |                                        const Json::Value& msg, const std::string& position) { | ||||||
|  |                                        if (verbose) | ||||||
|  |                                        { | ||||||
|  |                                            spdlog::info("Subscriber received message {} -> {}", position, jsonWriter.write(msg)); | ||||||
|  |                                        } | ||||||
|  |  | ||||||
|  |                                        receivedCount++; | ||||||
|  |  | ||||||
|  |                                        ++receivedCount; | ||||||
|  |                                        queueManager.add(msg); | ||||||
|  |                                    }); | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Subscribed) | ||||||
|  |                 { | ||||||
|  |                     spdlog::info("Subscriber: subscribed to channel {}", subscriptionId); | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_UnSubscribed) | ||||||
|  |                 { | ||||||
|  |                     spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId); | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Error) | ||||||
|  |                 { | ||||||
|  |                     spdlog::error("Subscriber: error {}", errMsg); | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Published) | ||||||
|  |                 { | ||||||
|  |                     spdlog::error("Published message hacked: {}", msgId); | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Pong) | ||||||
|  |                 { | ||||||
|  |                     spdlog::info("Received websocket pong"); | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Handshake_Error) | ||||||
|  |                 { | ||||||
|  |                     spdlog::error("Subscriber: Handshake error: {}", errMsg); | ||||||
|  |                     fatalCobraError = true; | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Authentication_Error) | ||||||
|  |                 { | ||||||
|  |                     spdlog::error("Subscriber: Authentication error: {}", errMsg); | ||||||
|  |                     fatalCobraError = true; | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Subscription_Error) | ||||||
|  |                 { | ||||||
|  |                     spdlog::error("Subscriber: Subscription error: {}", errMsg); | ||||||
|  |                     fatalCobraError = true; | ||||||
|  |                 } | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|         return bot.run(config); |         // Run forever | ||||||
|  |         if (runtime == -1) | ||||||
|  |         { | ||||||
|  |             while (true) | ||||||
|  |             { | ||||||
|  |                 auto duration = std::chrono::seconds(1); | ||||||
|  |                 std::this_thread::sleep_for(duration); | ||||||
|  |  | ||||||
|  |                 if (fatalCobraError) break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         // Run for a duration, used by unittesting now | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             for (int i = 0 ; i < runtime; ++i) | ||||||
|  |             { | ||||||
|  |                 auto duration = std::chrono::seconds(1); | ||||||
|  |                 std::this_thread::sleep_for(duration); | ||||||
|  |  | ||||||
|  |                 if (fatalCobraError) break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // | ||||||
|  |         // Cleanup. | ||||||
|  |         // join all the bg threads and stop them. | ||||||
|  |         // | ||||||
|  |         conn.disconnect(); | ||||||
|  |         stop = true; | ||||||
|  |  | ||||||
|  |         // progress thread | ||||||
|  |         t1.join(); | ||||||
|  |  | ||||||
|  |         // heartbeat thread | ||||||
|  |         if (t2.joinable()) t2.join(); | ||||||
|  |  | ||||||
|  |         // statsd sender thread | ||||||
|  |         t3.join(); | ||||||
|  |  | ||||||
|  |         return fatalCobraError ? -1 : (int) sentCount; | ||||||
|     } |     } | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -5,18 +5,23 @@ | |||||||
|  */ |  */ | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <cstdint> | #include <ixcobra/IXCobraConfig.h> | ||||||
| #include <ixbots/IXStatsdClient.h> | #include <ixbots/IXStatsdClient.h> | ||||||
| #include "IXCobraBotConfig.h" |  | ||||||
| #include <stddef.h> |  | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <stddef.h> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     int64_t cobra_to_statsd_bot(const ix::CobraBotConfig& config, |     int cobra_to_statsd_bot(const ix::CobraConfig& config, | ||||||
|                                 StatsdClient& statsdClient, |                             const std::string& channel, | ||||||
|                                 const std::string& fields, |                             const std::string& filter, | ||||||
|                                 const std::string& gauge, |                             const std::string& position, | ||||||
|                                 const std::string& timer, |                             StatsdClient& statsdClient, | ||||||
|                                 bool verbose); |                             const std::string& fields, | ||||||
|  |                             const std::string& gauge, | ||||||
|  |                             const std::string& timer, | ||||||
|  |                             bool verbose, | ||||||
|  |                             size_t maxQueueSize, | ||||||
|  |                             bool enableHeartbeat, | ||||||
|  |                             int runtime); | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -1,88 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXCobraToStdoutBot.cpp |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "IXCobraToStdoutBot.h" |  | ||||||
|  |  | ||||||
| #include "IXCobraBot.h" |  | ||||||
| #include <chrono> |  | ||||||
| #include <iostream> |  | ||||||
| #include <sstream> |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     using StreamWriterPtr = std::unique_ptr<Json::StreamWriter>; |  | ||||||
|  |  | ||||||
|     StreamWriterPtr makeStreamWriter() |  | ||||||
|     { |  | ||||||
|         Json::StreamWriterBuilder builder; |  | ||||||
|         builder["commentStyle"] = "None"; |  | ||||||
|         builder["indentation"] = ""; // will make the JSON object compact |  | ||||||
|         std::unique_ptr<Json::StreamWriter> jsonWriter(builder.newStreamWriter()); |  | ||||||
|         return jsonWriter; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     std::string timeSinceEpoch() |  | ||||||
|     { |  | ||||||
|         std::chrono::system_clock::time_point tp = std::chrono::system_clock::now(); |  | ||||||
|         std::chrono::system_clock::duration dtn = tp.time_since_epoch(); |  | ||||||
|  |  | ||||||
|         std::stringstream ss; |  | ||||||
|         ss << dtn.count() * std::chrono::system_clock::period::num / |  | ||||||
|                   std::chrono::system_clock::period::den; |  | ||||||
|         return ss.str(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void writeToStdout(bool fluentd, |  | ||||||
|                        const StreamWriterPtr& jsonWriter, |  | ||||||
|                        const Json::Value& msg, |  | ||||||
|                        const std::string& position) |  | ||||||
|     { |  | ||||||
|         Json::Value enveloppe; |  | ||||||
|         if (fluentd) |  | ||||||
|         { |  | ||||||
|             enveloppe["producer"] = "cobra"; |  | ||||||
|             enveloppe["consumer"] = "fluentd"; |  | ||||||
|  |  | ||||||
|             Json::Value nestedMessage(msg); |  | ||||||
|             nestedMessage["position"] = position; |  | ||||||
|             nestedMessage["created_at"] = timeSinceEpoch(); |  | ||||||
|             enveloppe["message"] = nestedMessage; |  | ||||||
|  |  | ||||||
|             jsonWriter->write(enveloppe, &std::cout); |  | ||||||
|             std::cout << std::endl; // add lf and flush |  | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             enveloppe = msg; |  | ||||||
|             std::cout << position << " "; |  | ||||||
|             jsonWriter->write(enveloppe, &std::cout); |  | ||||||
|             std::cout << std::endl; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     int64_t cobra_to_stdout_bot(const ix::CobraBotConfig& config, |  | ||||||
|                                 bool fluentd, |  | ||||||
|                                 bool quiet) |  | ||||||
|     { |  | ||||||
|         CobraBot bot; |  | ||||||
|         auto jsonWriter = makeStreamWriter(); |  | ||||||
|  |  | ||||||
|         bot.setOnBotMessageCallback( |  | ||||||
|             [&fluentd, &quiet, &jsonWriter](const Json::Value& msg, |  | ||||||
|                                             const std::string& position, |  | ||||||
|                                             std::atomic<bool>& /*throttled*/, |  | ||||||
|                                             std::atomic<bool>& /*fatalCobraError*/, |  | ||||||
|                                             std::atomic<uint64_t>& sentCount) -> void { |  | ||||||
|                 if (!quiet) |  | ||||||
|                 { |  | ||||||
|                     writeToStdout(fluentd, jsonWriter, msg, position); |  | ||||||
|                 } |  | ||||||
|                 sentCount++; |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|         return bot.run(config); |  | ||||||
|     } |  | ||||||
| } // namespace ix |  | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXCobraToStdoutBot.h |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <cstdint> |  | ||||||
| #include "IXCobraBotConfig.h" |  | ||||||
| #include <stddef.h> |  | ||||||
| #include <string> |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     int64_t cobra_to_stdout_bot(const ix::CobraBotConfig& config, |  | ||||||
|                                 bool fluentd, |  | ||||||
|                                 bool quiet); |  | ||||||
| } // namespace ix |  | ||||||
							
								
								
									
										66
									
								
								ixbots/ixbots/IXQueueManager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								ixbots/ixbots/IXQueueManager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | /* | ||||||
|  |  *  IXQueueManager.cpp | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "IXQueueManager.h" | ||||||
|  | #include <vector> | ||||||
|  | #include <algorithm> | ||||||
|  |  | ||||||
|  | namespace ix | ||||||
|  | { | ||||||
|  |     Json::Value QueueManager::pop() | ||||||
|  |     { | ||||||
|  |         std::unique_lock<std::mutex> lock(_mutex); | ||||||
|  |  | ||||||
|  |         if (_queues.empty()) | ||||||
|  |         { | ||||||
|  |             Json::Value val; | ||||||
|  |             return val; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         std::vector<std::string> games; | ||||||
|  |         for (auto it : _queues) | ||||||
|  |         { | ||||||
|  |             games.push_back(it.first); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         std::random_shuffle(games.begin(), games.end()); | ||||||
|  |         std::string game = games[0]; | ||||||
|  |  | ||||||
|  |         auto duration = std::chrono::seconds(1); | ||||||
|  |         _condition.wait_for(lock, duration); | ||||||
|  |  | ||||||
|  |         if (_queues[game].empty()) | ||||||
|  |         { | ||||||
|  |             Json::Value val; | ||||||
|  |             return val; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         auto msg = _queues[game].front(); | ||||||
|  |         _queues[game].pop(); | ||||||
|  |         return msg; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void QueueManager::add(Json::Value msg) | ||||||
|  |     { | ||||||
|  |         std::unique_lock<std::mutex> lock(_mutex); | ||||||
|  |  | ||||||
|  |         std::string game; | ||||||
|  |         if (msg.isMember("device") && msg["device"].isMember("game")) | ||||||
|  |         { | ||||||
|  |             game = msg["device"]["game"].asString(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (game.empty()) return; | ||||||
|  |  | ||||||
|  |         // if the sending is not fast enough there is no point | ||||||
|  |         // in queuing too many events. | ||||||
|  |         if (_queues[game].size() < _maxQueueSize) | ||||||
|  |         { | ||||||
|  |             _queues[game].push(msg); | ||||||
|  |             _condition.notify_one(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								ixbots/ixbots/IXQueueManager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								ixbots/ixbots/IXQueueManager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | /* | ||||||
|  |  *  IXQueueManager.h | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <json/json.h> | ||||||
|  | #include <mutex> | ||||||
|  | #include <condition_variable> | ||||||
|  | #include <queue> | ||||||
|  | #include <map> | ||||||
|  |  | ||||||
|  | namespace ix | ||||||
|  | { | ||||||
|  |     class QueueManager | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         QueueManager(size_t maxQueueSize) | ||||||
|  |             : _maxQueueSize(maxQueueSize) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Json::Value pop(); | ||||||
|  |         void add(Json::Value msg); | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         std::map<std::string, std::queue<Json::Value>> _queues; | ||||||
|  |         std::mutex _mutex; | ||||||
|  |         std::condition_variable _condition; | ||||||
|  |         size_t _maxQueueSize; | ||||||
|  |     }; | ||||||
|  | } | ||||||
| @@ -39,21 +39,24 @@ | |||||||
|  |  | ||||||
| #include "IXStatsdClient.h" | #include "IXStatsdClient.h" | ||||||
|  |  | ||||||
| #include <iostream> |  | ||||||
| #include <ixwebsocket/IXNetSystem.h> | #include <ixwebsocket/IXNetSystem.h> | ||||||
| #include <stdio.h> |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <iostream> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     StatsdClient::StatsdClient(const std::string& host, int port, const std::string& prefix) |     StatsdClient::StatsdClient(const std::string& host, | ||||||
|         : _host(host) |                                int port, | ||||||
|         , _port(port) |                                const std::string& prefix) | ||||||
|         , _prefix(prefix) |     : _host(host) | ||||||
|         , _stop(false) |       , _port(port) | ||||||
|  |       , _prefix(prefix) | ||||||
|  |       , _stop(false) | ||||||
|     { |     { | ||||||
|         _thread = std::thread([this] { |         _thread = std::thread([this] | ||||||
|  |         { | ||||||
|             while (!_stop) |             while (!_stop) | ||||||
|             { |             { | ||||||
|                 flushQueue(); |                 flushQueue(); | ||||||
| @@ -116,8 +119,8 @@ namespace ix | |||||||
|         cleanup(key); |         cleanup(key); | ||||||
|  |  | ||||||
|         char buf[256]; |         char buf[256]; | ||||||
|         snprintf( |         snprintf(buf, sizeof(buf), "%s%s:%zd|%s\n", | ||||||
|             buf, sizeof(buf), "%s%s:%zd|%s\n", _prefix.c_str(), key.c_str(), value, type.c_str()); |                  _prefix.c_str(), key.c_str(), value, type.c_str()); | ||||||
|  |  | ||||||
|         enqueue(buf); |         enqueue(buf); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -139,7 +142,9 @@ namespace ix | |||||||
|             auto ret = _socket.sendto(message); |             auto ret = _socket.sendto(message); | ||||||
|             if (ret != 0) |             if (ret != 0) | ||||||
|             { |             { | ||||||
|                 std::cerr << "error: " << strerror(UdpSocket::getErrno()) << std::endl; |                 std::cerr << "error: " | ||||||
|  |                           << strerror(UdpSocket::getErrno()) | ||||||
|  |                           << std::endl; | ||||||
|             } |             } | ||||||
|             _queue.pop_front(); |             _queue.pop_front(); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -6,20 +6,21 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <atomic> |  | ||||||
| #include <deque> |  | ||||||
| #include <ixwebsocket/IXUdpSocket.h> | #include <ixwebsocket/IXUdpSocket.h> | ||||||
| #include <mutex> |  | ||||||
| #include <string> | #include <string> | ||||||
| #include <thread> | #include <thread> | ||||||
|  | #include <deque> | ||||||
|  | #include <mutex> | ||||||
|  | #include <atomic> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     class StatsdClient |     class StatsdClient | ||||||
|     { |     { | ||||||
|     public: |     public: | ||||||
|         StatsdClient(const std::string& host = "127.0.0.1", |         StatsdClient(const std::string& host="127.0.0.1", | ||||||
|                      int port = 8125, |                      int port=8125, | ||||||
|                      const std::string& prefix = ""); |                      const std::string& prefix = ""); | ||||||
|         ~StatsdClient(); |         ~StatsdClient(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,6 @@ set (IXCOBRA_HEADERS | |||||||
|     ixcobra/IXCobraMetricsThreadedPublisher.h |     ixcobra/IXCobraMetricsThreadedPublisher.h | ||||||
|     ixcobra/IXCobraMetricsPublisher.h |     ixcobra/IXCobraMetricsPublisher.h | ||||||
|     ixcobra/IXCobraConfig.h |     ixcobra/IXCobraConfig.h | ||||||
|     ixcobra/IXCobraEventType.h |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| add_library(ixcobra STATIC | add_library(ixcobra STATIC | ||||||
|   | |||||||
| @@ -6,8 +6,8 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <ixwebsocket/IXSocketTLSOptions.h> |  | ||||||
| #include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h> | #include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h> | ||||||
|  | #include <ixwebsocket/IXSocketTLSOptions.h> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|   | |||||||
| @@ -5,17 +5,17 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "IXCobraConnection.h" | #include "IXCobraConnection.h" | ||||||
|  | #include <ixcrypto/IXHMac.h> | ||||||
|  | #include <ixwebsocket/IXWebSocket.h> | ||||||
|  | #include <ixwebsocket/IXSocketTLSOptions.h> | ||||||
|  |  | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <cassert> | #include <stdexcept> | ||||||
| #include <cmath> | #include <cmath> | ||||||
|  | #include <cassert> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <ixcrypto/IXHMac.h> |  | ||||||
| #include <ixwebsocket/IXSocketTLSOptions.h> |  | ||||||
| #include <ixwebsocket/IXWebSocket.h> |  | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <stdexcept> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| @@ -26,12 +26,12 @@ namespace ix | |||||||
|     constexpr CobraConnection::MsgId CobraConnection::kInvalidMsgId; |     constexpr CobraConnection::MsgId CobraConnection::kInvalidMsgId; | ||||||
|     constexpr int CobraConnection::kPingIntervalSecs; |     constexpr int CobraConnection::kPingIntervalSecs; | ||||||
|  |  | ||||||
|     CobraConnection::CobraConnection() |     CobraConnection::CobraConnection() : | ||||||
|         : _webSocket(new WebSocket()) |         _webSocket(new WebSocket()), | ||||||
|         , _publishMode(CobraConnection_PublishMode_Immediate) |         _publishMode(CobraConnection_PublishMode_Immediate), | ||||||
|         , _authenticated(false) |         _authenticated(false), | ||||||
|         , _eventCallback(nullptr) |         _eventCallback(nullptr), | ||||||
|         , _id(1) |         _id(1) | ||||||
|     { |     { | ||||||
|         _pdu["action"] = "rtm/publish"; |         _pdu["action"] = "rtm/publish"; | ||||||
|  |  | ||||||
| @@ -87,7 +87,7 @@ namespace ix | |||||||
|         _eventCallback = eventCallback; |         _eventCallback = eventCallback; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void CobraConnection::invokeEventCallback(ix::CobraEventType eventType, |     void CobraConnection::invokeEventCallback(ix::CobraConnectionEventType eventType, | ||||||
|                                               const std::string& errorMsg, |                                               const std::string& errorMsg, | ||||||
|                                               const WebSocketHttpHeaders& headers, |                                               const WebSocketHttpHeaders& headers, | ||||||
|                                               const std::string& subscriptionId, |                                               const std::string& subscriptionId, | ||||||
| @@ -96,8 +96,7 @@ namespace ix | |||||||
|         std::lock_guard<std::mutex> lock(_eventCallbackMutex); |         std::lock_guard<std::mutex> lock(_eventCallbackMutex); | ||||||
|         if (_eventCallback) |         if (_eventCallback) | ||||||
|         { |         { | ||||||
|             _eventCallback( |             _eventCallback(eventType, errorMsg, headers, subscriptionId, msgId); | ||||||
|                 std::make_unique<CobraEvent>(eventType, errorMsg, headers, subscriptionId, msgId)); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -106,7 +105,7 @@ namespace ix | |||||||
|     { |     { | ||||||
|         std::stringstream ss; |         std::stringstream ss; | ||||||
|         ss << errorMsg << " : received pdu => " << serializedPdu; |         ss << errorMsg << " : received pdu => " << serializedPdu; | ||||||
|         invokeEventCallback(ix::CobraEventType::Error, ss.str()); |         invokeEventCallback(ix::CobraConnection_EventType_Error, ss.str()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void CobraConnection::disconnect() |     void CobraConnection::disconnect() | ||||||
| @@ -117,119 +116,126 @@ namespace ix | |||||||
|  |  | ||||||
|     void CobraConnection::initWebSocketOnMessageCallback() |     void CobraConnection::initWebSocketOnMessageCallback() | ||||||
|     { |     { | ||||||
|         _webSocket->setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) { |         _webSocket->setOnMessageCallback( | ||||||
|             CobraConnection::invokeTrafficTrackerCallback(msg->wireSize, true); |             [this](const ix::WebSocketMessagePtr& msg) | ||||||
|  |  | ||||||
|             std::stringstream ss; |  | ||||||
|             if (msg->type == ix::WebSocketMessageType::Open) |  | ||||||
|             { |             { | ||||||
|                 invokeEventCallback(ix::CobraEventType::Open, std::string(), msg->openInfo.headers); |                 CobraConnection::invokeTrafficTrackerCallback(msg->wireSize, true); | ||||||
|                 sendHandshakeMessage(); |  | ||||||
|             } |  | ||||||
|             else if (msg->type == ix::WebSocketMessageType::Close) |  | ||||||
|             { |  | ||||||
|                 _authenticated = false; |  | ||||||
|  |  | ||||||
|                 std::stringstream ss; |                 std::stringstream ss; | ||||||
|                 ss << "Close code " << msg->closeInfo.code; |                 if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|                 ss << " reason " << msg->closeInfo.reason; |  | ||||||
|                 invokeEventCallback(ix::CobraEventType::Closed, ss.str()); |  | ||||||
|             } |  | ||||||
|             else if (msg->type == ix::WebSocketMessageType::Message) |  | ||||||
|             { |  | ||||||
|                 Json::Value data; |  | ||||||
|                 Json::Reader reader; |  | ||||||
|                 if (!reader.parse(msg->str, data)) |  | ||||||
|                 { |                 { | ||||||
|                     invokeErrorCallback("Invalid json", msg->str); |                     invokeEventCallback(ix::CobraConnection_EventType_Open, | ||||||
|                     return; |                                         std::string(), | ||||||
|  |                                         msg->openInfo.headers); | ||||||
|  |                     sendHandshakeMessage(); | ||||||
|                 } |                 } | ||||||
|  |                 else if (msg->type == ix::WebSocketMessageType::Close) | ||||||
|                 if (!data.isMember("action")) |  | ||||||
|                 { |                 { | ||||||
|                     invokeErrorCallback("Missing action", msg->str); |                     _authenticated = false; | ||||||
|                     return; |  | ||||||
|  |                     std::stringstream ss; | ||||||
|  |                     ss << "Close code " << msg->closeInfo.code; | ||||||
|  |                     ss << " reason " << msg->closeInfo.reason; | ||||||
|  |                     invokeEventCallback(ix::CobraConnection_EventType_Closed, | ||||||
|  |                                         ss.str()); | ||||||
|                 } |                 } | ||||||
|  |                 else if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|                 auto action = data["action"].asString(); |  | ||||||
|  |  | ||||||
|                 if (action == "auth/handshake/ok") |  | ||||||
|                 { |                 { | ||||||
|                     if (!handleHandshakeResponse(data)) |                     Json::Value data; | ||||||
|  |                     Json::Reader reader; | ||||||
|  |                     if (!reader.parse(msg->str, data)) | ||||||
|                     { |                     { | ||||||
|                         invokeErrorCallback("Error extracting nonce from handshake response", |                         invokeErrorCallback("Invalid json", msg->str); | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (!data.isMember("action")) | ||||||
|  |                     { | ||||||
|  |                         invokeErrorCallback("Missing action", msg->str); | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     auto action = data["action"].asString(); | ||||||
|  |  | ||||||
|  |                     if (action == "auth/handshake/ok") | ||||||
|  |                     { | ||||||
|  |                         if (!handleHandshakeResponse(data)) | ||||||
|  |                         { | ||||||
|  |                             invokeErrorCallback("Error extracting nonce from handshake response", msg->str); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     else if (action == "auth/handshake/error") | ||||||
|  |                     { | ||||||
|  |                         invokeEventCallback(ix::CobraConnection_EventType_Handshake_Error, | ||||||
|                                             msg->str); |                                             msg->str); | ||||||
|                     } |                     } | ||||||
|                 } |                     else if (action == "auth/authenticate/ok") | ||||||
|                 else if (action == "auth/handshake/error") |  | ||||||
|                 { |  | ||||||
|                     invokeEventCallback(ix::CobraEventType::HandshakeError, msg->str); |  | ||||||
|                 } |  | ||||||
|                 else if (action == "auth/authenticate/ok") |  | ||||||
|                 { |  | ||||||
|                     _authenticated = true; |  | ||||||
|                     invokeEventCallback(ix::CobraEventType::Authenticated); |  | ||||||
|                     flushQueue(); |  | ||||||
|                 } |  | ||||||
|                 else if (action == "auth/authenticate/error") |  | ||||||
|                 { |  | ||||||
|                     invokeEventCallback(ix::CobraEventType::AuthenticationError, msg->str); |  | ||||||
|                 } |  | ||||||
|                 else if (action == "rtm/subscription/data") |  | ||||||
|                 { |  | ||||||
|                     handleSubscriptionData(data); |  | ||||||
|                 } |  | ||||||
|                 else if (action == "rtm/subscribe/ok") |  | ||||||
|                 { |  | ||||||
|                     if (!handleSubscriptionResponse(data)) |  | ||||||
|                     { |                     { | ||||||
|                         invokeErrorCallback("Error processing subscribe response", msg->str); |                         _authenticated = true; | ||||||
|  |                         invokeEventCallback(ix::CobraConnection_EventType_Authenticated); | ||||||
|  |                         flushQueue(); | ||||||
|  |                     } | ||||||
|  |                     else if (action == "auth/authenticate/error") | ||||||
|  |                     { | ||||||
|  |                         invokeEventCallback(ix::CobraConnection_EventType_Authentication_Error, | ||||||
|  |                                             msg->str); | ||||||
|  |                     } | ||||||
|  |                     else if (action == "rtm/subscription/data") | ||||||
|  |                     { | ||||||
|  |                         handleSubscriptionData(data); | ||||||
|  |                     } | ||||||
|  |                     else if (action == "rtm/subscribe/ok") | ||||||
|  |                     { | ||||||
|  |                         if (!handleSubscriptionResponse(data)) | ||||||
|  |                         { | ||||||
|  |                             invokeErrorCallback("Error processing subscribe response", msg->str); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     else if (action == "rtm/subscribe/error") | ||||||
|  |                     { | ||||||
|  |                         invokeEventCallback(ix::CobraConnection_EventType_Subscription_Error, | ||||||
|  |                                             msg->str); | ||||||
|  |                     } | ||||||
|  |                     else if (action == "rtm/unsubscribe/ok") | ||||||
|  |                     { | ||||||
|  |                         if (!handleUnsubscriptionResponse(data)) | ||||||
|  |                         { | ||||||
|  |                             invokeErrorCallback("Error processing unsubscribe response", msg->str); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     else if (action == "rtm/unsubscribe/error") | ||||||
|  |                     { | ||||||
|  |                         invokeErrorCallback("Unsubscription error", msg->str); | ||||||
|  |                     } | ||||||
|  |                     else if (action == "rtm/publish/ok") | ||||||
|  |                     { | ||||||
|  |                         if (!handlePublishResponse(data)) | ||||||
|  |                         { | ||||||
|  |                             invokeErrorCallback("Error processing publish response", msg->str); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     else if (action == "rtm/publish/error") | ||||||
|  |                     { | ||||||
|  |                         invokeErrorCallback("Publish error", msg->str); | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         invokeErrorCallback("Un-handled message type", msg->str); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 else if (action == "rtm/subscribe/error") |                 else if (msg->type == ix::WebSocketMessageType::Error) | ||||||
|                 { |                 { | ||||||
|                     invokeEventCallback(ix::CobraEventType::SubscriptionError, msg->str); |                     std::stringstream ss; | ||||||
|  |                     ss << "Connection error: " << msg->errorInfo.reason      << std::endl; | ||||||
|  |                     ss << "#retries: "         << msg->errorInfo.retries     << std::endl; | ||||||
|  |                     ss << "Wait time(ms): "    << msg->errorInfo.wait_time   << std::endl; | ||||||
|  |                     ss << "HTTP Status: "      << msg->errorInfo.http_status << std::endl; | ||||||
|  |                     invokeErrorCallback(ss.str(), std::string()); | ||||||
|                 } |                 } | ||||||
|                 else if (action == "rtm/unsubscribe/ok") |                 else if (msg->type == ix::WebSocketMessageType::Pong) | ||||||
|                 { |                 { | ||||||
|                     if (!handleUnsubscriptionResponse(data)) |                     invokeEventCallback(ix::CobraConnection_EventType_Pong); | ||||||
|                     { |  | ||||||
|                         invokeErrorCallback("Error processing unsubscribe response", msg->str); |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|                 else if (action == "rtm/unsubscribe/error") |  | ||||||
|                 { |  | ||||||
|                     invokeErrorCallback("Unsubscription error", msg->str); |  | ||||||
|                 } |  | ||||||
|                 else if (action == "rtm/publish/ok") |  | ||||||
|                 { |  | ||||||
|                     if (!handlePublishResponse(data)) |  | ||||||
|                     { |  | ||||||
|                         invokeErrorCallback("Error processing publish response", msg->str); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 else if (action == "rtm/publish/error") |  | ||||||
|                 { |  | ||||||
|                     invokeErrorCallback("Publish error", msg->str); |  | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                 { |  | ||||||
|                     invokeErrorCallback("Un-handled message type", msg->str); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else if (msg->type == ix::WebSocketMessageType::Error) |  | ||||||
|             { |  | ||||||
|                 std::stringstream ss; |  | ||||||
|                 ss << "Connection error: " << msg->errorInfo.reason << std::endl; |  | ||||||
|                 ss << "#retries: " << msg->errorInfo.retries << std::endl; |  | ||||||
|                 ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl; |  | ||||||
|                 ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl; |  | ||||||
|                 invokeErrorCallback(ss.str(), std::string()); |  | ||||||
|             } |  | ||||||
|             else if (msg->type == ix::WebSocketMessageType::Pong) |  | ||||||
|             { |  | ||||||
|                 invokeEventCallback(ix::CobraEventType::Pong, msg->str); |  | ||||||
|             } |  | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -243,13 +249,12 @@ namespace ix | |||||||
|         return _publishMode; |         return _publishMode; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void CobraConnection::configure( |     void CobraConnection::configure(const std::string& appkey, | ||||||
|         const std::string& appkey, |                                     const std::string& endpoint, | ||||||
|         const std::string& endpoint, |                                     const std::string& rolename, | ||||||
|         const std::string& rolename, |                                     const std::string& rolesecret, | ||||||
|         const std::string& rolesecret, |                                     const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions, | ||||||
|         const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions, |                                     const SocketTLSOptions& socketTLSOptions) | ||||||
|         const SocketTLSOptions& socketTLSOptions) |  | ||||||
|     { |     { | ||||||
|         _roleName = rolename; |         _roleName = rolename; | ||||||
|         _roleSecret = rolesecret; |         _roleSecret = rolesecret; | ||||||
| @@ -391,9 +396,8 @@ namespace ix | |||||||
|  |  | ||||||
|         if (!subscriptionId.isString()) return false; |         if (!subscriptionId.isString()) return false; | ||||||
|  |  | ||||||
|         invokeEventCallback(ix::CobraEventType::Subscribed, |         invokeEventCallback(ix::CobraConnection_EventType_Subscribed, | ||||||
|                             std::string(), |                             std::string(), WebSocketHttpHeaders(), | ||||||
|                             WebSocketHttpHeaders(), |  | ||||||
|                             subscriptionId.asString()); |                             subscriptionId.asString()); | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| @@ -410,9 +414,8 @@ namespace ix | |||||||
|  |  | ||||||
|         if (!subscriptionId.isString()) return false; |         if (!subscriptionId.isString()) return false; | ||||||
|  |  | ||||||
|         invokeEventCallback(ix::CobraEventType::UnSubscribed, |         invokeEventCallback(ix::CobraConnection_EventType_UnSubscribed, | ||||||
|                             std::string(), |                             std::string(), WebSocketHttpHeaders(), | ||||||
|                             WebSocketHttpHeaders(), |  | ||||||
|                             subscriptionId.asString()); |                             subscriptionId.asString()); | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| @@ -459,11 +462,9 @@ namespace ix | |||||||
|  |  | ||||||
|         uint64_t msgId = id.asUInt64(); |         uint64_t msgId = id.asUInt64(); | ||||||
|  |  | ||||||
|         invokeEventCallback(ix::CobraEventType::Published, |         invokeEventCallback(ix::CobraConnection_EventType_Published, | ||||||
|                             std::string(), |                             std::string(), WebSocketHttpHeaders(), | ||||||
|                             WebSocketHttpHeaders(), |                             std::string(), msgId); | ||||||
|                             std::string(), |  | ||||||
|                             msgId); |  | ||||||
|  |  | ||||||
|         invokePublishTrackerCallback(false, true); |         invokePublishTrackerCallback(false, true); | ||||||
|  |  | ||||||
| @@ -493,7 +494,9 @@ namespace ix | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::pair<CobraConnection::MsgId, std::string> CobraConnection::prePublish( |     std::pair<CobraConnection::MsgId, std::string> CobraConnection::prePublish( | ||||||
|         const Json::Value& channels, const Json::Value& msg, bool addToQueue) |         const Json::Value& channels, | ||||||
|  |         const Json::Value& msg, | ||||||
|  |         bool addToQueue) | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_prePublishMutex); |         std::lock_guard<std::mutex> lock(_prePublishMutex); | ||||||
|  |  | ||||||
| @@ -659,7 +662,8 @@ namespace ix | |||||||
|     bool CobraConnection::publishMessage(const std::string& serializedJson) |     bool CobraConnection::publishMessage(const std::string& serializedJson) | ||||||
|     { |     { | ||||||
|         auto webSocketSendInfo = _webSocket->send(serializedJson); |         auto webSocketSendInfo = _webSocket->send(serializedJson); | ||||||
|         CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false); |         CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, | ||||||
|  |                                                       false); | ||||||
|         return webSocketSendInfo.success; |         return webSocketSendInfo.success; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,19 +6,18 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "IXCobraConfig.h" |  | ||||||
| #include "IXCobraEvent.h" |  | ||||||
| #include "IXCobraEventType.h" |  | ||||||
| #include <ixwebsocket/IXWebSocketHttpHeaders.h> | #include <ixwebsocket/IXWebSocketHttpHeaders.h> | ||||||
| #include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h> | #include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h> | ||||||
| #include <json/json.h> | #include <json/json.h> | ||||||
| #include <limits> |  | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <queue> | #include <queue> | ||||||
| #include <string> | #include <string> | ||||||
| #include <thread> | #include <thread> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
|  | #include <limits> | ||||||
|  |  | ||||||
|  | #include "IXCobraConfig.h" | ||||||
|  |  | ||||||
| #ifdef max | #ifdef max | ||||||
| #undef max | #undef max | ||||||
| @@ -29,6 +28,21 @@ namespace ix | |||||||
|     class WebSocket; |     class WebSocket; | ||||||
|     struct SocketTLSOptions; |     struct SocketTLSOptions; | ||||||
|  |  | ||||||
|  |     enum CobraConnectionEventType | ||||||
|  |     { | ||||||
|  |         CobraConnection_EventType_Authenticated = 0, | ||||||
|  |         CobraConnection_EventType_Error = 1, | ||||||
|  |         CobraConnection_EventType_Open = 2, | ||||||
|  |         CobraConnection_EventType_Closed = 3, | ||||||
|  |         CobraConnection_EventType_Subscribed = 4, | ||||||
|  |         CobraConnection_EventType_UnSubscribed = 5, | ||||||
|  |         CobraConnection_EventType_Published = 6, | ||||||
|  |         CobraConnection_EventType_Pong = 7, | ||||||
|  |         CobraConnection_EventType_Handshake_Error = 8, | ||||||
|  |         CobraConnection_EventType_Authentication_Error = 9, | ||||||
|  |         CobraConnection_EventType_Subscription_Error = 10 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     enum CobraConnectionPublishMode |     enum CobraConnectionPublishMode | ||||||
|     { |     { | ||||||
|         CobraConnection_PublishMode_Immediate = 0, |         CobraConnection_PublishMode_Immediate = 0, | ||||||
| @@ -36,7 +50,11 @@ namespace ix | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     using SubscriptionCallback = std::function<void(const Json::Value&, const std::string&)>; |     using SubscriptionCallback = std::function<void(const Json::Value&, const std::string&)>; | ||||||
|     using EventCallback = std::function<void(const CobraEventPtr&)>; |     using EventCallback = std::function<void(CobraConnectionEventType, | ||||||
|  |                                              const std::string&, | ||||||
|  |                                              const WebSocketHttpHeaders&, | ||||||
|  |                                              const std::string&, | ||||||
|  |                                              uint64_t msgId)>; | ||||||
|  |  | ||||||
|     using TrafficTrackerCallback = std::function<void(size_t size, bool incoming)>; |     using TrafficTrackerCallback = std::function<void(size_t size, bool incoming)>; | ||||||
|     using PublishTrackerCallback = std::function<void(bool sent, bool acked)>; |     using PublishTrackerCallback = std::function<void(bool sent, bool acked)>; | ||||||
| @@ -120,9 +138,10 @@ namespace ix | |||||||
|  |  | ||||||
|         /// Prepare a message for transmission |         /// Prepare a message for transmission | ||||||
|         /// (update the pdu, compute a msgId, serialize json to a string) |         /// (update the pdu, compute a msgId, serialize json to a string) | ||||||
|         std::pair<CobraConnection::MsgId, std::string> prePublish(const Json::Value& channels, |         std::pair<CobraConnection::MsgId, std::string> prePublish( | ||||||
|                                                                   const Json::Value& msg, |             const Json::Value& channels, | ||||||
|                                                                   bool addToQueue); |             const Json::Value& msg, | ||||||
|  |             bool addToQueue); | ||||||
|  |  | ||||||
|         /// Attempt to send next message from the internal queue |         /// Attempt to send next message from the internal queue | ||||||
|         bool publishNext(); |         bool publishNext(); | ||||||
| @@ -152,7 +171,7 @@ namespace ix | |||||||
|         static void invokePublishTrackerCallback(bool sent, bool acked); |         static void invokePublishTrackerCallback(bool sent, bool acked); | ||||||
|  |  | ||||||
|         /// Invoke event callbacks |         /// Invoke event callbacks | ||||||
|         void invokeEventCallback(CobraEventType eventType, |         void invokeEventCallback(CobraConnectionEventType eventType, | ||||||
|                                  const std::string& errorMsg = std::string(), |                                  const std::string& errorMsg = std::string(), | ||||||
|                                  const WebSocketHttpHeaders& headers = WebSocketHttpHeaders(), |                                  const WebSocketHttpHeaders& headers = WebSocketHttpHeaders(), | ||||||
|                                  const std::string& subscriptionId = std::string(), |                                  const std::string& subscriptionId = std::string(), | ||||||
|   | |||||||
| @@ -1,41 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXCobraEvent.h |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include "IXCobraEventType.h" |  | ||||||
| #include <cstdint> |  | ||||||
| #include <ixwebsocket/IXWebSocketHttpHeaders.h> |  | ||||||
| #include <memory> |  | ||||||
| #include <string> |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     struct CobraEvent |  | ||||||
|     { |  | ||||||
|         ix::CobraEventType type; |  | ||||||
|         const std::string& errMsg; |  | ||||||
|         const ix::WebSocketHttpHeaders& headers; |  | ||||||
|         const std::string& subscriptionId; |  | ||||||
|         uint64_t msgId; // CobraConnection::MsgId |  | ||||||
|  |  | ||||||
|         CobraEvent(ix::CobraEventType t, |  | ||||||
|                    const std::string& e, |  | ||||||
|                    const ix::WebSocketHttpHeaders& h, |  | ||||||
|                    const std::string& s, |  | ||||||
|                    uint64_t m) |  | ||||||
|             : type(t) |  | ||||||
|             , errMsg(e) |  | ||||||
|             , headers(h) |  | ||||||
|             , subscriptionId(s) |  | ||||||
|             , msgId(m) |  | ||||||
|         { |  | ||||||
|             ; |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     using CobraEventPtr = std::unique_ptr<CobraEvent>; |  | ||||||
| } // namespace ix |  | ||||||
| @@ -1,25 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXCobraEventType.h |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| namespace ix |  | ||||||
| { |  | ||||||
|     enum class CobraEventType |  | ||||||
|     { |  | ||||||
|         Authenticated = 0, |  | ||||||
|         Error = 1, |  | ||||||
|         Open = 2, |  | ||||||
|         Closed = 3, |  | ||||||
|         Subscribed = 4, |  | ||||||
|         UnSubscribed = 5, |  | ||||||
|         Published = 6, |  | ||||||
|         Pong = 7, |  | ||||||
|         HandshakeError = 8, |  | ||||||
|         AuthenticationError = 9, |  | ||||||
|         SubscriptionError = 10 |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| @@ -5,9 +5,9 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "IXCobraMetricsPublisher.h" | #include "IXCobraMetricsPublisher.h" | ||||||
|  | #include <ixwebsocket/IXSocketTLSOptions.h> | ||||||
|  |  | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <ixwebsocket/IXSocketTLSOptions.h> |  | ||||||
| #include <stdexcept> | #include <stdexcept> | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -17,8 +17,8 @@ namespace ix | |||||||
|     const std::string CobraMetricsPublisher::kSetRateControlId = "sms_set_rate_control_id"; |     const std::string CobraMetricsPublisher::kSetRateControlId = "sms_set_rate_control_id"; | ||||||
|     const std::string CobraMetricsPublisher::kSetBlacklistId = "sms_set_blacklist_id"; |     const std::string CobraMetricsPublisher::kSetBlacklistId = "sms_set_blacklist_id"; | ||||||
|  |  | ||||||
|     CobraMetricsPublisher::CobraMetricsPublisher() |     CobraMetricsPublisher::CobraMetricsPublisher() : | ||||||
|         : _enabled(true) |         _enabled(true) | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -27,7 +27,8 @@ namespace ix | |||||||
|         ; |         ; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void CobraMetricsPublisher::configure(const CobraConfig& config, const std::string& channel) |     void CobraMetricsPublisher::configure(const CobraConfig& config, | ||||||
|  |                                           const std::string& channel) | ||||||
|     { |     { | ||||||
|         // Configure the satori connection and start its publish background thread |         // Configure the satori connection and start its publish background thread | ||||||
|         _cobra_metrics_theaded_publisher.configure(config, channel); |         _cobra_metrics_theaded_publisher.configure(config, channel); | ||||||
| @@ -41,7 +42,7 @@ namespace ix | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     void CobraMetricsPublisher::setGenericAttributes(const std::string& attrName, |     void CobraMetricsPublisher::setGenericAttributes(const std::string& attrName, | ||||||
|                                                      const Json::Value& value) |                                                       const Json::Value& value) | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_device_mutex); |         std::lock_guard<std::mutex> lock(_device_mutex); | ||||||
|         _device[attrName] = value; |         _device[attrName] = value; | ||||||
| @@ -106,7 +107,8 @@ namespace ix | |||||||
|         auto last_update = _last_update.find(id); |         auto last_update = _last_update.find(id); | ||||||
|         if (last_update == _last_update.end()) return false; |         if (last_update == _last_update.end()) return false; | ||||||
|  |  | ||||||
|         auto timeDeltaFromLastSend = std::chrono::steady_clock::now() - last_update->second; |         auto timeDeltaFromLastSend = | ||||||
|  |             std::chrono::steady_clock::now() - last_update->second; | ||||||
|  |  | ||||||
|         return timeDeltaFromLastSend < std::chrono::seconds(rate_control_it->second); |         return timeDeltaFromLastSend < std::chrono::seconds(rate_control_it->second); | ||||||
|     } |     } | ||||||
| @@ -121,7 +123,8 @@ namespace ix | |||||||
|     { |     { | ||||||
|         auto now = std::chrono::system_clock::now(); |         auto now = std::chrono::system_clock::now(); | ||||||
|         auto ms = |         auto ms = | ||||||
|             std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count(); |             std::chrono::duration_cast<std::chrono::milliseconds>( | ||||||
|  |                 now.time_since_epoch()).count(); | ||||||
|  |  | ||||||
|         return ms; |         return ms; | ||||||
|     } |     } | ||||||
| @@ -162,9 +165,10 @@ namespace ix | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     CobraConnection::MsgId CobraMetricsPublisher::push(const std::string& id, |     CobraConnection::MsgId CobraMetricsPublisher::push( | ||||||
|                                                        const Json::Value& data, |         const std::string& id, | ||||||
|                                                        bool shouldPushTest) |         const Json::Value& data, | ||||||
|  |         bool shouldPushTest) | ||||||
|     { |     { | ||||||
|         if (shouldPushTest && !shouldPush(id)) return CobraConnection::kInvalidMsgId; |         if (shouldPushTest && !shouldPush(id)) return CobraConnection::kInvalidMsgId; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -40,7 +40,8 @@ namespace ix | |||||||
|  |  | ||||||
|         /// Configuration / set keys, etc... |         /// Configuration / set keys, etc... | ||||||
|         /// All input data but the channel name is encrypted with rc4 |         /// All input data but the channel name is encrypted with rc4 | ||||||
|         void configure(const CobraConfig& config, const std::string& channel); |         void configure(const CobraConfig& config, | ||||||
|  |                        const std::string& channel); | ||||||
|  |  | ||||||
|         /// Setter for the list of blacklisted metrics ids. |         /// Setter for the list of blacklisted metrics ids. | ||||||
|         /// That list is sorted internally for fast lookups |         /// That list is sorted internally for fast lookups | ||||||
| @@ -67,14 +68,10 @@ namespace ix | |||||||
|         /// shouldPush method for places where we want to be as lightweight as possible when |         /// shouldPush method for places where we want to be as lightweight as possible when | ||||||
|         /// collecting metrics. When set to false, it is used so that we don't do double work when |         /// collecting metrics. When set to false, it is used so that we don't do double work when | ||||||
|         /// computing whether a metrics should be sent or not. |         /// computing whether a metrics should be sent or not. | ||||||
|         CobraConnection::MsgId push(const std::string& id, |         CobraConnection::MsgId push(const std::string& id, const Json::Value& data, bool shouldPushTest = true); | ||||||
|                                     const Json::Value& data, |  | ||||||
|                                     bool shouldPushTest = true); |  | ||||||
|  |  | ||||||
|         /// Interface used by lua. msg is a json encoded string. |         /// Interface used by lua. msg is a json encoded string. | ||||||
|         CobraConnection::MsgId push(const std::string& id, |         CobraConnection::MsgId push(const std::string& id, const std::string& data, bool shouldPushTest = true); | ||||||
|                                     const std::string& data, |  | ||||||
|                                     bool shouldPushTest = true); |  | ||||||
|  |  | ||||||
|         /// Tells whether a metric can be pushed. |         /// Tells whether a metric can be pushed. | ||||||
|         /// A metric can be pushed if it satisfies those conditions: |         /// A metric can be pushed if it satisfies those conditions: | ||||||
| @@ -92,16 +89,10 @@ namespace ix | |||||||
|         void setGenericAttributes(const std::string& attrName, const Json::Value& value); |         void setGenericAttributes(const std::string& attrName, const Json::Value& value); | ||||||
|  |  | ||||||
|         /// Set a unique id for the session. A uuid can be used. |         /// Set a unique id for the session. A uuid can be used. | ||||||
|         void setSession(const std::string& session) |         void setSession(const std::string& session) { _session = session; } | ||||||
|         { |  | ||||||
|             _session = session; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /// Get the unique id used to identify the current session |         /// Get the unique id used to identify the current session | ||||||
|         const std::string& getSession() const |         const std::string& getSession() const { return _session; } | ||||||
|         { |  | ||||||
|             return _session; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /// Return the number of milliseconds since the epoch (~1970) |         /// Return the number of milliseconds since the epoch (~1970) | ||||||
|         uint64_t getMillisecondsSinceEpoch() const; |         uint64_t getMillisecondsSinceEpoch() const; | ||||||
|   | |||||||
| @@ -5,77 +5,72 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "IXCobraMetricsThreadedPublisher.h" | #include "IXCobraMetricsThreadedPublisher.h" | ||||||
|  |  | ||||||
| #include <algorithm> |  | ||||||
| #include <cassert> |  | ||||||
| #include <cmath> |  | ||||||
| #include <iostream> |  | ||||||
| #include <ixcore/utils/IXCoreLogger.h> |  | ||||||
| #include <ixwebsocket/IXSetThreadName.h> | #include <ixwebsocket/IXSetThreadName.h> | ||||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | #include <ixwebsocket/IXSocketTLSOptions.h> | ||||||
| #include <sstream> | #include <ixcore/utils/IXCoreLogger.h> | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
| #include <stdexcept> | #include <stdexcept> | ||||||
|  | #include <cmath> | ||||||
|  | #include <cassert> | ||||||
|  | #include <iostream> | ||||||
|  | #include <sstream> | ||||||
|  |  | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher() |     CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher() : | ||||||
|         : _stop(false) |         _stop(false) | ||||||
|     { |     { | ||||||
|         _cobra_connection.setEventCallback([](const CobraEventPtr& event) { |         _cobra_connection.setEventCallback( | ||||||
|             std::stringstream ss; |             [] | ||||||
|  |             (ix::CobraConnectionEventType eventType, | ||||||
|             if (event->type == ix::CobraEventType::Open) |              const std::string& errMsg, | ||||||
|  |              const ix::WebSocketHttpHeaders& headers, | ||||||
|  |              const std::string& subscriptionId, | ||||||
|  |              CobraConnection::MsgId msgId) | ||||||
|             { |             { | ||||||
|                 ss << "Handshake headers" << std::endl; |                 std::stringstream ss; | ||||||
|  |  | ||||||
|                 for (auto&& it : event->headers) |                 if (eventType == ix::CobraConnection_EventType_Open) | ||||||
|                 { |                 { | ||||||
|                     ss << it.first << ": " << it.second << std::endl; |                     ss << "Handshake headers" << std::endl; | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Authenticated) |  | ||||||
|             { |  | ||||||
|                 ss << "Authenticated"; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Error) |  | ||||||
|             { |  | ||||||
|                 ss << "Error: " << event->errMsg; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Closed) |  | ||||||
|             { |  | ||||||
|                 ss << "Connection closed: " << event->errMsg; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Subscribed) |  | ||||||
|             { |  | ||||||
|                 ss << "Subscribed through subscription id: " << event->subscriptionId; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::UnSubscribed) |  | ||||||
|             { |  | ||||||
|                 ss << "Unsubscribed through subscription id: " << event->subscriptionId; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Published) |  | ||||||
|             { |  | ||||||
|                 ss << "Published message " << event->msgId << " acked"; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Pong) |  | ||||||
|             { |  | ||||||
|                 ss << "Received websocket pong"; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::HandshakeError) |  | ||||||
|             { |  | ||||||
|                 ss << "Handshake error: " << event->errMsg; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::AuthenticationError) |  | ||||||
|             { |  | ||||||
|                 ss << "Authentication error: " << event->errMsg; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::SubscriptionError) |  | ||||||
|             { |  | ||||||
|                 ss << "Subscription error: " << event->errMsg; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             CoreLogger::log(ss.str().c_str()); |                     for (auto it : headers) | ||||||
|  |                     { | ||||||
|  |                         ss << it.first << ": " << it.second << std::endl; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Authenticated) | ||||||
|  |                 { | ||||||
|  |                     ss << "Authenticated"; | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Error) | ||||||
|  |                 { | ||||||
|  |                     ss << "Error: " << errMsg; | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Closed) | ||||||
|  |                 { | ||||||
|  |                     ss << "Connection closed: " << errMsg; | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Subscribed) | ||||||
|  |                 { | ||||||
|  |                     ss << "Subscribed through subscription id: " << subscriptionId; | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_UnSubscribed) | ||||||
|  |                 { | ||||||
|  |                     ss << "Unsubscribed through subscription id: " << subscriptionId; | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Published) | ||||||
|  |                 { | ||||||
|  |                     ss << "Published message " << msgId << " acked"; | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == ix::CobraConnection_EventType_Pong) | ||||||
|  |                 { | ||||||
|  |                     ss << "Received websocket pong"; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 ix::IXCoreLogger::Log(ss.str().c_str()); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -100,10 +95,11 @@ namespace ix | |||||||
|     void CobraMetricsThreadedPublisher::configure(const CobraConfig& config, |     void CobraMetricsThreadedPublisher::configure(const CobraConfig& config, | ||||||
|                                                   const std::string& channel) |                                                   const std::string& channel) | ||||||
|     { |     { | ||||||
|         CoreLogger::log(config.socketTLSOptions.getDescription().c_str()); |         ix::IXCoreLogger::Log(config.socketTLSOptions.getDescription().c_str()); | ||||||
|  |  | ||||||
|         _channel = channel; |         _channel = channel; | ||||||
|         _cobra_connection.configure(config); |         _cobra_connection.configure(config); | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind) |     void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind) | ||||||
| @@ -161,15 +157,13 @@ namespace ix | |||||||
|                 { |                 { | ||||||
|                     _cobra_connection.suspend(); |                     _cobra_connection.suspend(); | ||||||
|                     continue; |                     continue; | ||||||
|                 }; |                 }; break; | ||||||
|                 break; |  | ||||||
|  |  | ||||||
|                 case MessageKind::Resume: |                 case MessageKind::Resume: | ||||||
|                 { |                 { | ||||||
|                     _cobra_connection.resume(); |                     _cobra_connection.resume(); | ||||||
|                     continue; |                     continue; | ||||||
|                 }; |                 }; break; | ||||||
|                 break; |  | ||||||
|  |  | ||||||
|                 case MessageKind::Message: |                 case MessageKind::Message: | ||||||
|                 { |                 { | ||||||
| @@ -177,8 +171,7 @@ namespace ix | |||||||
|                     { |                     { | ||||||
|                         _cobra_connection.publishNext(); |                         _cobra_connection.publishNext(); | ||||||
|                     } |                     } | ||||||
|                 }; |                 }; break; | ||||||
|                 break; |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -27,7 +27,8 @@ namespace ix | |||||||
|         ~CobraMetricsThreadedPublisher(); |         ~CobraMetricsThreadedPublisher(); | ||||||
|  |  | ||||||
|         /// Configuration / set keys, etc... |         /// Configuration / set keys, etc... | ||||||
|         void configure(const CobraConfig& config, const std::string& channel); |         void configure(const CobraConfig& config, | ||||||
|  |                        const std::string& channel); | ||||||
|  |  | ||||||
|         /// Start the worker thread, used for background publishing |         /// Start the worker thread, used for background publishing | ||||||
|         void start(); |         void start(); | ||||||
|   | |||||||
| @@ -1,44 +1,14 @@ | |||||||
| /* |  | ||||||
|  *  IXCoreLogger.cpp |  | ||||||
|  *  Author: Thomas Wells, Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "ixcore/utils/IXCoreLogger.h" | #include "ixcore/utils/IXCoreLogger.h" | ||||||
|  |  | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     // Default do a no-op logger | // Default do nothing logger | ||||||
|     CoreLogger::LogFunc CoreLogger::_currentLogger = [](const char*, LogLevel) {}; | IXCoreLogger::LogFunc IXCoreLogger::_currentLogger = [](const char* /*msg*/){}; | ||||||
|  |  | ||||||
|     void CoreLogger::log(const char* msg, LogLevel level) | void IXCoreLogger::Log(const char* msg) | ||||||
|     { | { | ||||||
|         _currentLogger(msg, level); |     _currentLogger(msg); | ||||||
|     } | } | ||||||
|  |  | ||||||
|     void CoreLogger::debug(const std::string& msg) | } // ix | ||||||
|     { |  | ||||||
|         _currentLogger(msg.c_str(), LogLevel::Debug); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void CoreLogger::info(const std::string& msg) |  | ||||||
|     { |  | ||||||
|         _currentLogger(msg.c_str(), LogLevel::Info); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void CoreLogger::warn(const std::string& msg) |  | ||||||
|     { |  | ||||||
|         _currentLogger(msg.c_str(), LogLevel::Warning); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void CoreLogger::error(const std::string& msg) |  | ||||||
|     { |  | ||||||
|         _currentLogger(msg.c_str(), LogLevel::Error); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void CoreLogger::critical(const std::string& msg) |  | ||||||
|     { |  | ||||||
|         _currentLogger(msg.c_str(), LogLevel::Critical); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } // namespace ix |  | ||||||
|   | |||||||
| @@ -1,41 +1,15 @@ | |||||||
| /* |  | ||||||
|  *  IXCoreLogger.h |  | ||||||
|  *  Author: Thomas Wells, Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <string> |  | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     enum class LogLevel |     class IXCoreLogger | ||||||
|     { |  | ||||||
|         Debug = 0, |  | ||||||
|         Info = 1, |  | ||||||
|         Warning = 2, |  | ||||||
|         Error = 3, |  | ||||||
|         Critical = 4 |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     class CoreLogger |  | ||||||
|     { |     { | ||||||
|     public: |     public: | ||||||
|         using LogFunc = std::function<void(const char*, LogLevel level)>; |         using LogFunc = std::function<void(const char*)>; | ||||||
|  |         static void Log(const char* msg); | ||||||
|  |  | ||||||
|         static void log(const char* msg, LogLevel level = LogLevel::Debug); |         static void setLogFunction(LogFunc& func) { _currentLogger = func; } | ||||||
|  |  | ||||||
|         static void debug(const std::string& msg); |  | ||||||
|         static void info(const std::string& msg); |  | ||||||
|         static void warn(const std::string& msg); |  | ||||||
|         static void error(const std::string& msg); |  | ||||||
|         static void critical(const std::string& msg); |  | ||||||
|  |  | ||||||
|         static void setLogFunction(LogFunc& func) |  | ||||||
|         { |  | ||||||
|             _currentLogger = func; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         static LogFunc _currentLogger; |         static LogFunc _currentLogger; | ||||||
|   | |||||||
| @@ -29,9 +29,10 @@ | |||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |     static const std::string base64_chars = | ||||||
|                                             "abcdefghijklmnopqrstuvwxyz" |     "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||||||
|                                             "0123456789+/"; |     "abcdefghijklmnopqrstuvwxyz" | ||||||
|  |     "0123456789+/"; | ||||||
|  |  | ||||||
|     std::string base64_encode(const std::string& data, size_t len) |     std::string base64_encode(const std::string& data, size_t len) | ||||||
|     { |     { | ||||||
| @@ -49,26 +50,26 @@ namespace ix | |||||||
|         unsigned char char_array_3[3]; |         unsigned char char_array_3[3]; | ||||||
|         unsigned char char_array_4[4]; |         unsigned char char_array_4[4]; | ||||||
|  |  | ||||||
|         while (len--) |         while(len--) | ||||||
|         { |         { | ||||||
|             char_array_3[i++] = *(bytes_to_encode++); |             char_array_3[i++] = *(bytes_to_encode++); | ||||||
|             if (i == 3) |             if(i == 3) | ||||||
|             { |             { | ||||||
|                 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; |                 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; | ||||||
|                 char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); |                 char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); | ||||||
|                 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); |                 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); | ||||||
|                 char_array_4[3] = char_array_3[2] & 0x3f; |                 char_array_4[3] = char_array_3[2] & 0x3f; | ||||||
|  |  | ||||||
|                 for (i = 0; (i < 4); i++) |                 for(i = 0; (i <4) ; i++) | ||||||
|                     ret += base64_chars[char_array_4[i]]; |                     ret += base64_chars[char_array_4[i]]; | ||||||
|  |  | ||||||
|                 i = 0; |                 i = 0; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (i) |         if(i) | ||||||
|         { |         { | ||||||
|             for (j = i; j < 3; j++) |             for(j = i; j < 3; j++) | ||||||
|                 char_array_3[j] = '\0'; |                 char_array_3[j] = '\0'; | ||||||
|  |  | ||||||
|             char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; |             char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; | ||||||
| @@ -76,11 +77,12 @@ namespace ix | |||||||
|             char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); |             char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); | ||||||
|             char_array_4[3] = char_array_3[2] & 0x3f; |             char_array_4[3] = char_array_3[2] & 0x3f; | ||||||
|  |  | ||||||
|             for (j = 0; (j < i + 1); j++) |             for(j = 0; (j < i + 1); j++) | ||||||
|                 ret += base64_chars[char_array_4[j]]; |                 ret += base64_chars[char_array_4[j]]; | ||||||
|  |  | ||||||
|             while ((i++ < 3)) |             while((i++ < 3)) | ||||||
|                 ret += '='; |                 ret += '='; | ||||||
|  |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return ret; |         return ret; | ||||||
| @@ -93,7 +95,7 @@ namespace ix | |||||||
|  |  | ||||||
|     std::string base64_decode(const std::string& encoded_string) |     std::string base64_decode(const std::string& encoded_string) | ||||||
|     { |     { | ||||||
|         int in_len = (int) encoded_string.size(); |         int in_len = (int)encoded_string.size(); | ||||||
|         int i = 0; |         int i = 0; | ||||||
|         int j = 0; |         int j = 0; | ||||||
|         int in_ = 0; |         int in_ = 0; | ||||||
| @@ -101,42 +103,40 @@ namespace ix | |||||||
|         std::string ret; |         std::string ret; | ||||||
|         ret.reserve(((in_len + 3) / 4) * 3); |         ret.reserve(((in_len + 3) / 4) * 3); | ||||||
|  |  | ||||||
|         while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) |         while(in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) | ||||||
|         { |         { | ||||||
|             char_array_4[i++] = encoded_string[in_]; |             char_array_4[i++] = encoded_string[in_]; in_++; | ||||||
|             in_++; |             if(i ==4) | ||||||
|             if (i == 4) |  | ||||||
|             { |             { | ||||||
|                 for (i = 0; i < 4; i++) |                 for(i = 0; i <4; i++) | ||||||
|                     char_array_4[i] = base64_chars.find(char_array_4[i]); |                     char_array_4[i] = base64_chars.find(char_array_4[i]); | ||||||
|  |  | ||||||
|                 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); |                 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); | ||||||
|                 char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); |                 char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); | ||||||
|                 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; |                 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; | ||||||
|  |  | ||||||
|                 for (i = 0; (i < 3); i++) |                 for(i = 0; (i < 3); i++) | ||||||
|                     ret += char_array_3[i]; |                     ret += char_array_3[i]; | ||||||
|  |  | ||||||
|                 i = 0; |                 i = 0; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (i) |         if(i) | ||||||
|         { |         { | ||||||
|             for (j = i; j < 4; j++) |             for(j = i; j <4; j++) | ||||||
|                 char_array_4[j] = 0; |                 char_array_4[j] = 0; | ||||||
|  |  | ||||||
|             for (j = 0; j < 4; j++) |             for(j = 0; j <4; j++) | ||||||
|                 char_array_4[j] = base64_chars.find(char_array_4[j]); |                 char_array_4[j] = base64_chars.find(char_array_4[j]); | ||||||
|  |  | ||||||
|             char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); |             char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); | ||||||
|             char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); |             char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); | ||||||
|             char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; |             char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; | ||||||
|  |  | ||||||
|             for (j = 0; (j < i - 1); j++) |             for(j = 0; (j < i - 1); j++) ret += char_array_3[j]; | ||||||
|                 ret += char_array_3[j]; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
| } // namespace ix | } | ||||||
|   | |||||||
| @@ -5,17 +5,16 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "IXHMac.h" | #include "IXHMac.h" | ||||||
|  |  | ||||||
| #include "IXBase64.h" | #include "IXBase64.h" | ||||||
|  |  | ||||||
| #if defined(IXCRYPTO_USE_MBED_TLS) | #if defined(IXCRYPTO_USE_MBED_TLS) | ||||||
| #include <mbedtls/md.h> | # include <mbedtls/md.h> | ||||||
| #elif defined(__APPLE__) | #elif defined(__APPLE__) | ||||||
| #include <CommonCrypto/CommonHMAC.h> | # include <CommonCrypto/CommonHMAC.h> | ||||||
| #elif defined(IXCRYPTO_USE_OPEN_SSL) | #elif defined(IXCRYPTO_USE_OPEN_SSL) | ||||||
| #include <openssl/hmac.h> | # include <openssl/hmac.h> | ||||||
| #else | #else | ||||||
| #include <assert.h> | # include <assert.h> | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| @@ -27,21 +26,19 @@ namespace ix | |||||||
|  |  | ||||||
| #if defined(IXCRYPTO_USE_MBED_TLS) | #if defined(IXCRYPTO_USE_MBED_TLS) | ||||||
|         mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), |         mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), | ||||||
|                         (unsigned char*) key.c_str(), |                (unsigned char *) key.c_str(), key.size(), | ||||||
|                         key.size(), |                (unsigned char *) data.c_str(), data.size(), | ||||||
|                         (unsigned char*) data.c_str(), |                (unsigned char *) &hash); | ||||||
|                         data.size(), |  | ||||||
|                         (unsigned char*) &hash); |  | ||||||
| #elif defined(__APPLE__) | #elif defined(__APPLE__) | ||||||
|         CCHmac(kCCHmacAlgMD5, key.c_str(), key.size(), data.c_str(), data.size(), &hash); |         CCHmac(kCCHmacAlgMD5, | ||||||
|  |                key.c_str(), key.size(), | ||||||
|  |                data.c_str(), data.size(), | ||||||
|  |                &hash); | ||||||
| #elif defined(IXCRYPTO_USE_OPEN_SSL) | #elif defined(IXCRYPTO_USE_OPEN_SSL) | ||||||
|         HMAC(EVP_md5(), |         HMAC(EVP_md5(), | ||||||
|              key.c_str(), |              key.c_str(), (int) key.size(), | ||||||
|              (int) key.size(), |              (unsigned char *) data.c_str(), (int) data.size(), | ||||||
|              (unsigned char*) data.c_str(), |              (unsigned char *) hash, nullptr); | ||||||
|              (int) data.size(), |  | ||||||
|              (unsigned char*) hash, |  | ||||||
|              nullptr); |  | ||||||
| #else | #else | ||||||
|         assert(false && "hmac not implemented on this platform"); |         assert(false && "hmac not implemented on this platform"); | ||||||
| #endif | #endif | ||||||
| @@ -50,4 +47,4 @@ namespace ix | |||||||
|  |  | ||||||
|         return base64_encode(hashString, (uint32_t) hashString.size()); |         return base64_encode(hashString, (uint32_t) hashString.size()); | ||||||
|     } |     } | ||||||
| } // namespace ix | } | ||||||
|   | |||||||
| @@ -19,4 +19,4 @@ namespace ix | |||||||
|  |  | ||||||
|         return hashAddress; |         return hashAddress; | ||||||
|     } |     } | ||||||
| } // namespace ix | } | ||||||
|   | |||||||
| @@ -16,23 +16,23 @@ | |||||||
|  |  | ||||||
| #include "IXUuid.h" | #include "IXUuid.h" | ||||||
|  |  | ||||||
| #include <iomanip> |  | ||||||
| #include <random> |  | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <iomanip> | ||||||
|  | #include <random> | ||||||
|  |  | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     class Uuid |     class Uuid | ||||||
|     { |     { | ||||||
|     public: |         public: | ||||||
|         Uuid(); |             Uuid(); | ||||||
|         std::string toString() const; |             std::string toString() const; | ||||||
|  |  | ||||||
|     private: |         private: | ||||||
|         uint64_t _ab; |             uint64_t _ab; | ||||||
|         uint64_t _cd; |             uint64_t _cd; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     Uuid::Uuid() |     Uuid::Uuid() | ||||||
| @@ -60,7 +60,7 @@ namespace ix | |||||||
|         ss << std::setw(8) << (a); |         ss << std::setw(8) << (a); | ||||||
|         ss << std::setw(4) << (b >> 16); |         ss << std::setw(4) << (b >> 16); | ||||||
|         ss << std::setw(4) << (b & 0xFFFF); |         ss << std::setw(4) << (b & 0xFFFF); | ||||||
|         ss << std::setw(4) << (c >> 16); |         ss << std::setw(4) << (c >> 16 ); | ||||||
|         ss << std::setw(4) << (c & 0xFFFF); |         ss << std::setw(4) << (c & 0xFFFF); | ||||||
|         ss << std::setw(8) << d; |         ss << std::setw(8) << d; | ||||||
|  |  | ||||||
| @@ -72,4 +72,4 @@ namespace ix | |||||||
|         Uuid id; |         Uuid id; | ||||||
|         return id.toString(); |         return id.toString(); | ||||||
|     } |     } | ||||||
| } // namespace ix | } | ||||||
|   | |||||||
| @@ -7,12 +7,12 @@ | |||||||
| #include "IXSentryClient.h" | #include "IXSentryClient.h" | ||||||
|  |  | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <fstream> |  | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <ixcore/utils/IXCoreLogger.h> | #include <fstream> | ||||||
|  | #include <sstream> | ||||||
| #include <ixwebsocket/IXWebSocketHttpHeaders.h> | #include <ixwebsocket/IXWebSocketHttpHeaders.h> | ||||||
| #include <ixwebsocket/IXWebSocketVersion.h> | #include <ixwebsocket/IXWebSocketVersion.h> | ||||||
| #include <sstream> | #include <ixcore/utils/IXCoreLogger.h> | ||||||
|  |  | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| @@ -64,8 +64,7 @@ namespace ix | |||||||
|     std::string SentryClient::computeAuthHeader() |     std::string SentryClient::computeAuthHeader() | ||||||
|     { |     { | ||||||
|         std::string securityHeader("Sentry sentry_version=5"); |         std::string securityHeader("Sentry sentry_version=5"); | ||||||
|         securityHeader += ",sentry_client=ws/"; |         securityHeader += ",sentry_client=ws/1.0.0"; | ||||||
|         securityHeader += std::string(IX_WEBSOCKET_VERSION); |  | ||||||
|         securityHeader += ",sentry_timestamp=" + std::to_string(SentryClient::getTimestamp()); |         securityHeader += ",sentry_timestamp=" + std::to_string(SentryClient::getTimestamp()); | ||||||
|         securityHeader += ",sentry_key=" + _publicKey; |         securityHeader += ",sentry_key=" + _publicKey; | ||||||
|         securityHeader += ",sentry_secret=" + _secretKey; |         securityHeader += ",sentry_secret=" + _secretKey; | ||||||
| @@ -226,44 +225,44 @@ namespace ix | |||||||
|         return _jsonWriter.write(payload); |         return _jsonWriter.write(payload); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void SentryClient::send( |     std::pair<HttpResponsePtr, std::string> SentryClient::send(const Json::Value& msg, bool verbose) | ||||||
|         const Json::Value& msg, |  | ||||||
|         bool verbose, |  | ||||||
|         const OnResponseCallback& onResponseCallback) |  | ||||||
|     { |     { | ||||||
|         auto args = _httpClient->createRequest(); |         auto args = _httpClient->createRequest(); | ||||||
|         args->url = _url; |  | ||||||
|         args->verb = HttpClient::kPost; |  | ||||||
|         args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader(); |         args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader(); | ||||||
|         args->connectTimeout = 60; |         args->connectTimeout = 60; | ||||||
|         args->transferTimeout = 5 * 60; |         args->transferTimeout = 5 * 60; | ||||||
|         args->followRedirects = true; |         args->followRedirects = true; | ||||||
|         args->verbose = verbose; |         args->verbose = verbose; | ||||||
|         args->logger = [](const std::string& msg) { CoreLogger::log(msg.c_str()); }; |         args->logger = [](const std::string& msg) { ix::IXCoreLogger::Log(msg.c_str()); }; | ||||||
|         args->body = computePayload(msg); |  | ||||||
|  |  | ||||||
|         _httpClient->performRequest(args, onResponseCallback); |         std::string body = computePayload(msg); | ||||||
|  |         HttpResponsePtr response = _httpClient->post(_url, body, args); | ||||||
|  |  | ||||||
|  |         return std::make_pair(response, body); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // https://sentry.io/api/12345/minidump?sentry_key=abcdefgh"); |     // https://sentry.io/api/12345/minidump?sentry_key=abcdefgh"); | ||||||
|     std::string SentryClient::computeUrl(const std::string& project, const std::string& key) |     std::string SentryClient::computeUrl(const std::string& project, const std::string& key) | ||||||
|     { |     { | ||||||
|         std::stringstream ss; |         std::stringstream ss; | ||||||
|         ss << "https://sentry.io/api/" << project << "/minidump?sentry_key=" << key; |         ss << "https://sentry.io/api/" | ||||||
|  |            << project | ||||||
|  |            << "/minidump?sentry_key=" | ||||||
|  |            << key; | ||||||
|  |  | ||||||
|         return ss.str(); |         return ss.str(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |     // | ||||||
|     // curl -v -X POST -F upload_file_minidump=@ws/crash.dmp |     // curl -v -X POST -F upload_file_minidump=@ws/crash.dmp 'https://sentry.io/api/123456/minidump?sentry_key=12344567890' | ||||||
|     // 'https://sentry.io/api/123456/minidump?sentry_key=12344567890' |  | ||||||
|     // |     // | ||||||
|     void SentryClient::uploadMinidump(const std::string& sentryMetadata, |     void SentryClient::uploadMinidump( | ||||||
|                                       const std::string& minidumpBytes, |         const std::string& sentryMetadata, | ||||||
|                                       const std::string& project, |         const std::string& minidumpBytes, | ||||||
|                                       const std::string& key, |         const std::string& project, | ||||||
|                                       bool verbose, |         const std::string& key, | ||||||
|                                       const OnResponseCallback& onResponseCallback) |         bool verbose, | ||||||
|  |         const OnResponseCallback& onResponseCallback) | ||||||
|     { |     { | ||||||
|         std::string multipartBoundary = _httpClient->generateMultipartBoundary(); |         std::string multipartBoundary = _httpClient->generateMultipartBoundary(); | ||||||
|  |  | ||||||
| @@ -274,7 +273,7 @@ namespace ix | |||||||
|         args->followRedirects = true; |         args->followRedirects = true; | ||||||
|         args->verbose = verbose; |         args->verbose = verbose; | ||||||
|         args->multipartBoundary = multipartBoundary; |         args->multipartBoundary = multipartBoundary; | ||||||
|         args->logger = [](const std::string& msg) { CoreLogger::log(msg.c_str()); }; |         args->logger = [](const std::string& msg) { ix::IXCoreLogger::Log(msg.c_str()); }; | ||||||
|  |  | ||||||
|         HttpFormDataParameters httpFormDataParameters; |         HttpFormDataParameters httpFormDataParameters; | ||||||
|         httpFormDataParameters["upload_file_minidump"] = minidumpBytes; |         httpFormDataParameters["upload_file_minidump"] = minidumpBytes; | ||||||
| @@ -283,27 +282,7 @@ namespace ix | |||||||
|         httpParameters["sentry"] = sentryMetadata; |         httpParameters["sentry"] = sentryMetadata; | ||||||
|  |  | ||||||
|         args->url = computeUrl(project, key); |         args->url = computeUrl(project, key); | ||||||
|         args->body = _httpClient->serializeHttpFormDataParameters( |         args->body = _httpClient->serializeHttpFormDataParameters(multipartBoundary, httpFormDataParameters, httpParameters); | ||||||
|             multipartBoundary, httpFormDataParameters, httpParameters); |  | ||||||
|  |  | ||||||
|         _httpClient->performRequest(args, onResponseCallback); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void SentryClient::uploadPayload(const Json::Value& payload, |  | ||||||
|                                      bool verbose, |  | ||||||
|                                      const OnResponseCallback& onResponseCallback) |  | ||||||
|     { |  | ||||||
|         auto args = _httpClient->createRequest(); |  | ||||||
|         args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader(); |  | ||||||
|         args->verb = HttpClient::kPost; |  | ||||||
|         args->connectTimeout = 60; |  | ||||||
|         args->transferTimeout = 5 * 60; |  | ||||||
|         args->followRedirects = true; |  | ||||||
|         args->verbose = verbose; |  | ||||||
|         args->logger = [](const std::string& msg) { CoreLogger::log(msg.c_str()); }; |  | ||||||
|  |  | ||||||
|         args->url = _url; |  | ||||||
|         args->body = _jsonWriter.write(payload); |  | ||||||
|  |  | ||||||
|         _httpClient->performRequest(args, onResponseCallback); |         _httpClient->performRequest(args, onResponseCallback); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -10,8 +10,8 @@ | |||||||
| #include <ixwebsocket/IXHttpClient.h> | #include <ixwebsocket/IXHttpClient.h> | ||||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | #include <ixwebsocket/IXSocketTLSOptions.h> | ||||||
| #include <json/json.h> | #include <json/json.h> | ||||||
| #include <memory> |  | ||||||
| #include <regex> | #include <regex> | ||||||
|  | #include <memory> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
| @@ -21,26 +21,20 @@ namespace ix | |||||||
|         SentryClient(const std::string& dsn); |         SentryClient(const std::string& dsn); | ||||||
|         ~SentryClient() = default; |         ~SentryClient() = default; | ||||||
|  |  | ||||||
|         void send(const Json::Value& msg, |         std::pair<HttpResponsePtr, std::string> send(const Json::Value& msg, bool verbose); | ||||||
|                   bool verbose, |  | ||||||
|                   const OnResponseCallback& onResponseCallback); |  | ||||||
|  |  | ||||||
|         void uploadMinidump(const std::string& sentryMetadata, |  | ||||||
|                             const std::string& minidumpBytes, |  | ||||||
|                             const std::string& project, |  | ||||||
|                             const std::string& key, |  | ||||||
|                             bool verbose, |  | ||||||
|                             const OnResponseCallback& onResponseCallback); |  | ||||||
|  |  | ||||||
|         void uploadPayload(const Json::Value& payload, |  | ||||||
|                            bool verbose, |  | ||||||
|                            const OnResponseCallback& onResponseCallback); |  | ||||||
|  |  | ||||||
|         Json::Value parseLuaStackTrace(const std::string& stack); |         Json::Value parseLuaStackTrace(const std::string& stack); | ||||||
|  |  | ||||||
|         // Mostly for testing |         // Mostly for testing | ||||||
|         void setTLSOptions(const SocketTLSOptions& tlsOptions); |         void setTLSOptions(const SocketTLSOptions& tlsOptions); | ||||||
|  |  | ||||||
|  |         void uploadMinidump( | ||||||
|  |             const std::string& sentryMetadata, | ||||||
|  |             const std::string& minidumpBytes, | ||||||
|  |             const std::string& project, | ||||||
|  |             const std::string& key, | ||||||
|  |             bool verbose, | ||||||
|  |             const OnResponseCallback& onResponseCallback); | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         int64_t getTimestamp(); |         int64_t getTimestamp(); | ||||||
|   | |||||||
| @@ -6,10 +6,10 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <ixwebsocket/IXSocketTLSOptions.h> |  | ||||||
| #include <nlohmann/json.hpp> | #include <nlohmann/json.hpp> | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include <ixwebsocket/IXSocketTLSOptions.h> | ||||||
|  |  | ||||||
| namespace snake | namespace snake | ||||||
| { | { | ||||||
|   | |||||||
| @@ -28,7 +28,10 @@ namespace ix | |||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         CancellationRequest cancellationRequest = []() -> bool { return false; }; |         CancellationRequest cancellationRequest = []() -> bool | ||||||
|  |         { | ||||||
|  |             return false; | ||||||
|  |         }; | ||||||
|  |  | ||||||
|         std::string errMsg; |         std::string errMsg; | ||||||
|         return _socket->connect(hostname, port, errMsg, cancellationRequest); |         return _socket->connect(hostname, port, errMsg, cancellationRequest); | ||||||
| @@ -249,8 +252,9 @@ namespace ix | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::string RedisClient::prepareXaddCommand(const std::string& stream, |     std::string RedisClient::prepareXaddCommand( | ||||||
|                                                 const std::string& message) |         const std::string& stream, | ||||||
|  |         const std::string& message) | ||||||
|     { |     { | ||||||
|         std::stringstream ss; |         std::stringstream ss; | ||||||
|         ss << "*5\r\n"; |         ss << "*5\r\n"; | ||||||
| @@ -324,9 +328,7 @@ namespace ix | |||||||
|         return streamId; |         return streamId; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool RedisClient::sendCommand(const std::string& commands, |     bool RedisClient::sendCommand(const std::string& commands, int commandsCount, std::string& errMsg) | ||||||
|                                   int commandsCount, |  | ||||||
|                                   std::string& errMsg) |  | ||||||
|     { |     { | ||||||
|         bool sent = _socket->writeBytes(commands, nullptr); |         bool sent = _socket->writeBytes(commands, nullptr); | ||||||
|         if (!sent) |         if (!sent) | ||||||
|   | |||||||
| @@ -8,10 +8,11 @@ | |||||||
|  |  | ||||||
| #include <atomic> | #include <atomic> | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <ixwebsocket/IXSocket.h> |  | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
|  | #include <ixwebsocket/IXSocket.h> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     class RedisClient |     class RedisClient | ||||||
| @@ -38,11 +39,14 @@ namespace ix | |||||||
|                        const OnRedisSubscribeCallback& callback); |                        const OnRedisSubscribeCallback& callback); | ||||||
|  |  | ||||||
|         // XADD |         // XADD | ||||||
|         std::string xadd(const std::string& channel, |         std::string xadd( | ||||||
|                          const std::string& message, |             const std::string& channel, | ||||||
|                          std::string& errMsg); |             const std::string& message, | ||||||
|  |             std::string& errMsg); | ||||||
|  |  | ||||||
|         std::string prepareXaddCommand(const std::string& stream, const std::string& message); |         std::string prepareXaddCommand( | ||||||
|  |             const std::string& stream, | ||||||
|  |             const std::string& message); | ||||||
|  |  | ||||||
|         std::string readXaddReply(std::string& errMsg); |         std::string readXaddReply(std::string& errMsg); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,18 +6,17 @@ | |||||||
|  |  | ||||||
| #include "IXRedisServer.h" | #include "IXRedisServer.h" | ||||||
|  |  | ||||||
| #include <fstream> |  | ||||||
| #include <ixwebsocket/IXCancellationRequest.h> |  | ||||||
| #include <ixwebsocket/IXNetSystem.h> | #include <ixwebsocket/IXNetSystem.h> | ||||||
| #include <ixwebsocket/IXSocket.h> |  | ||||||
| #include <ixwebsocket/IXSocketConnect.h> | #include <ixwebsocket/IXSocketConnect.h> | ||||||
|  | #include <ixwebsocket/IXSocket.h> | ||||||
|  | #include <ixwebsocket/IXCancellationRequest.h> | ||||||
|  | #include <fstream> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     RedisServer::RedisServer( |     RedisServer::RedisServer(int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily) | ||||||
|         int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily) |  | ||||||
|         : SocketServer(port, host, backlog, maxConnections, addressFamily) |         : SocketServer(port, host, backlog, maxConnections, addressFamily) | ||||||
|         , _connectedClientsCount(0) |         , _connectedClientsCount(0) | ||||||
|         , _stopHandlingConnections(false) |         , _stopHandlingConnections(false) | ||||||
| @@ -115,7 +114,8 @@ namespace ix | |||||||
|         for (auto it : _subscribers) |         for (auto it : _subscribers) | ||||||
|         { |         { | ||||||
|             std::stringstream ss; |             std::stringstream ss; | ||||||
|             ss << "Subscription id: " << it.first << " #subscribers: " << it.second.size(); |             ss << "Subscription id: " << it.first | ||||||
|  |                << " #subscribers: " << it.second.size(); | ||||||
|  |  | ||||||
|             logInfo(ss.str()); |             logInfo(ss.str()); | ||||||
|         } |         } | ||||||
| @@ -126,7 +126,8 @@ namespace ix | |||||||
|         return _connectedClientsCount; |         return _connectedClientsCount; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool RedisServer::startsWith(const std::string& str, const std::string& start) |     bool RedisServer::startsWith(const std::string& str, | ||||||
|  |                                  const std::string& start) | ||||||
|     { |     { | ||||||
|         return str.compare(0, start.length(), start) == 0; |         return str.compare(0, start.length(), start) == 0; | ||||||
|     } |     } | ||||||
| @@ -143,8 +144,9 @@ namespace ix | |||||||
|         return ss.str(); |         return ss.str(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool RedisServer::parseRequest(std::unique_ptr<Socket>& socket, |     bool RedisServer::parseRequest( | ||||||
|                                    std::vector<std::string>& tokens) |         std::unique_ptr<Socket>& socket, | ||||||
|  |         std::vector<std::string>& tokens) | ||||||
|     { |     { | ||||||
|         // Parse first line |         // Parse first line | ||||||
|         auto cb = makeCancellationRequestWithTimeout(30, _stopHandlingConnections); |         auto cb = makeCancellationRequestWithTimeout(30, _stopHandlingConnections); | ||||||
| @@ -188,8 +190,9 @@ namespace ix | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool RedisServer::handleCommand(std::unique_ptr<Socket>& socket, |     bool RedisServer::handleCommand( | ||||||
|                                     const std::vector<std::string>& tokens) |         std::unique_ptr<Socket>& socket, | ||||||
|  |         const std::vector<std::string>& tokens) | ||||||
|     { |     { | ||||||
|         if (tokens.size() != 1) return false; |         if (tokens.size() != 1) return false; | ||||||
|  |  | ||||||
| @@ -204,30 +207,31 @@ namespace ix | |||||||
|         // |         // | ||||||
|         ss << "*6\r\n"; |         ss << "*6\r\n"; | ||||||
|         ss << writeString("publish"); // 1 |         ss << writeString("publish"); // 1 | ||||||
|         ss << ":3\r\n";               // 2 |         ss << ":3\r\n"; // 2 | ||||||
|         ss << "*0\r\n";               // 3 |         ss << "*0\r\n"; // 3 | ||||||
|         ss << ":1\r\n";               // 4 |         ss << ":1\r\n"; // 4 | ||||||
|         ss << ":2\r\n";               // 5 |         ss << ":2\r\n"; // 5 | ||||||
|         ss << ":1\r\n";               // 6 |         ss << ":1\r\n"; // 6 | ||||||
|  |  | ||||||
|         // |         // | ||||||
|         // subscribe |         // subscribe | ||||||
|         // |         // | ||||||
|         ss << "*6\r\n"; |         ss << "*6\r\n"; | ||||||
|         ss << writeString("subscribe"); // 1 |         ss << writeString("subscribe"); // 1 | ||||||
|         ss << ":2\r\n";                 // 2 |         ss << ":2\r\n"; // 2 | ||||||
|         ss << "*0\r\n";                 // 3 |         ss << "*0\r\n"; // 3 | ||||||
|         ss << ":1\r\n";                 // 4 |         ss << ":1\r\n"; // 4 | ||||||
|         ss << ":1\r\n";                 // 5 |         ss << ":1\r\n"; // 5 | ||||||
|         ss << ":1\r\n";                 // 6 |         ss << ":1\r\n"; // 6 | ||||||
|  |  | ||||||
|         socket->writeBytes(ss.str(), cb); |         socket->writeBytes(ss.str(), cb); | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool RedisServer::handleSubscribe(std::unique_ptr<Socket>& socket, |     bool RedisServer::handleSubscribe( | ||||||
|                                       const std::vector<std::string>& tokens) |         std::unique_ptr<Socket>& socket, | ||||||
|  |         const std::vector<std::string>& tokens) | ||||||
|     { |     { | ||||||
|         if (tokens.size() != 2) return false; |         if (tokens.size() != 2) return false; | ||||||
|  |  | ||||||
| @@ -246,8 +250,9 @@ namespace ix | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool RedisServer::handlePublish(std::unique_ptr<Socket>& socket, |     bool RedisServer::handlePublish( | ||||||
|                                     const std::vector<std::string>& tokens) |         std::unique_ptr<Socket>& socket, | ||||||
|  |         const std::vector<std::string>& tokens) | ||||||
|     { |     { | ||||||
|         if (tokens.size() != 3) return false; |         if (tokens.size() != 3) return false; | ||||||
|  |  | ||||||
| @@ -276,7 +281,9 @@ namespace ix | |||||||
|  |  | ||||||
|         // return the number of clients that received the message. |         // return the number of clients that received the message. | ||||||
|         std::stringstream ss; |         std::stringstream ss; | ||||||
|         ss << ":" << std::to_string(subscribers.size()) << "\r\n"; |         ss << ":" | ||||||
|  |            << std::to_string(subscribers.size()) | ||||||
|  |            << "\r\n"; | ||||||
|         socket->writeBytes(ss.str(), cb); |         socket->writeBytes(ss.str(), cb); | ||||||
|  |  | ||||||
|         return true; |         return true; | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "IXSocket.h" |  | ||||||
| #include "IXSocketServer.h" | #include "IXSocketServer.h" | ||||||
|  | #include "IXSocket.h" | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <map> |  | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <set> | #include <set> | ||||||
|  | #include <map> | ||||||
| #include <string> | #include <string> | ||||||
| #include <thread> | #include <thread> | ||||||
| #include <utility> // pair | #include <utility> // pair | ||||||
| @@ -50,14 +50,18 @@ namespace ix | |||||||
|         bool startsWith(const std::string& str, const std::string& start); |         bool startsWith(const std::string& str, const std::string& start); | ||||||
|         std::string writeString(const std::string& str); |         std::string writeString(const std::string& str); | ||||||
|  |  | ||||||
|         bool parseRequest(std::unique_ptr<Socket>& socket, std::vector<std::string>& tokens); |         bool parseRequest( | ||||||
|  |             std::unique_ptr<Socket>& socket, | ||||||
|  |             std::vector<std::string>& tokens); | ||||||
|  |  | ||||||
|         bool handlePublish(std::unique_ptr<Socket>& socket, const std::vector<std::string>& tokens); |         bool handlePublish(std::unique_ptr<Socket>& socket, | ||||||
|  |                            const std::vector<std::string>& tokens); | ||||||
|  |  | ||||||
|         bool handleSubscribe(std::unique_ptr<Socket>& socket, |         bool handleSubscribe(std::unique_ptr<Socket>& socket, | ||||||
|                              const std::vector<std::string>& tokens); |                              const std::vector<std::string>& tokens); | ||||||
|  |  | ||||||
|         bool handleCommand(std::unique_ptr<Socket>& socket, const std::vector<std::string>& tokens); |         bool handleCommand(std::unique_ptr<Socket>& socket, | ||||||
|  |                            const std::vector<std::string>& tokens); | ||||||
|  |  | ||||||
|         void cleanupSubscribers(std::unique_ptr<Socket>& socket); |         void cleanupSubscribers(std::unique_ptr<Socket>& socket); | ||||||
|     }; |     }; | ||||||
|   | |||||||
| @@ -10,9 +10,9 @@ | |||||||
| #include "IXSnakeConnectionState.h" | #include "IXSnakeConnectionState.h" | ||||||
| #include "nlohmann/json.hpp" | #include "nlohmann/json.hpp" | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <ixcore/utils/IXCoreLogger.h> |  | ||||||
| #include <ixcrypto/IXHMac.h> | #include <ixcrypto/IXHMac.h> | ||||||
| #include <ixwebsocket/IXWebSocket.h> | #include <ixwebsocket/IXWebSocket.h> | ||||||
|  | #include <ixcore/utils/IXCoreLogger.h> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  |  | ||||||
| namespace snake | namespace snake | ||||||
| @@ -189,8 +189,7 @@ namespace snake | |||||||
|             nlohmann::json response = { |             nlohmann::json response = { | ||||||
|                 {"action", "rtm/subscription/data"}, |                 {"action", "rtm/subscription/data"}, | ||||||
|                 {"id", id++}, |                 {"id", id++}, | ||||||
|                 {"body", |                 {"body", {{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}}; | ||||||
|                  {{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}}; |  | ||||||
|  |  | ||||||
|             ws->sendText(response.dump()); |             ws->sendText(response.dump()); | ||||||
|         }; |         }; | ||||||
| @@ -198,7 +197,7 @@ namespace snake | |||||||
|         auto responseCallback = [ws, pdu, &subscriptionId](const std::string& redisResponse) { |         auto responseCallback = [ws, pdu, &subscriptionId](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::IXCoreLogger::Log(ss.str().c_str()); | ||||||
|  |  | ||||||
|             // Success |             // Success | ||||||
|             nlohmann::json response = {{"action", "rtm/subscribe/ok"}, |             nlohmann::json response = {{"action", "rtm/subscribe/ok"}, | ||||||
| @@ -210,7 +209,7 @@ namespace snake | |||||||
|         { |         { | ||||||
|             std::stringstream ss; |             std::stringstream ss; | ||||||
|             ss << "Subscribing to " << appChannel << "..."; |             ss << "Subscribing to " << appChannel << "..."; | ||||||
|             ix::CoreLogger::log(ss.str().c_str()); |             ix::IXCoreLogger::Log(ss.str().c_str()); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!redisClient.subscribe(appChannel, responseCallback, callback)) |         if (!redisClient.subscribe(appChannel, responseCallback, callback)) | ||||||
| @@ -252,21 +251,7 @@ namespace snake | |||||||
|                              const AppConfig& appConfig, |                              const AppConfig& appConfig, | ||||||
|                              const std::string& str) |                              const std::string& str) | ||||||
|     { |     { | ||||||
|         nlohmann::json pdu; |         auto pdu = nlohmann::json::parse(str); | ||||||
|         try |  | ||||||
|         { |  | ||||||
|             pdu = nlohmann::json::parse(str); |  | ||||||
|         } |  | ||||||
|         catch (const nlohmann::json::parse_error& e) |  | ||||||
|         { |  | ||||||
|             std::stringstream ss; |  | ||||||
|             ss << "malformed json pdu: " << e.what() << " -> " << str << ""; |  | ||||||
|  |  | ||||||
|             nlohmann::json response = {{"body", {{"error", "invalid_json"}, {"reason", ss.str()}}}}; |  | ||||||
|             ws->sendText(response.dump()); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         auto action = pdu["action"]; |         auto action = pdu["action"]; | ||||||
|  |  | ||||||
|         if (action == "auth/handshake") |         if (action == "auth/handshake") | ||||||
|   | |||||||
| @@ -10,8 +10,8 @@ | |||||||
| #include "IXSnakeConnectionState.h" | #include "IXSnakeConnectionState.h" | ||||||
| #include "IXSnakeProtocol.h" | #include "IXSnakeProtocol.h" | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <ixcore/utils/IXCoreLogger.h> |  | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  | #include <ixcore/utils/IXCoreLogger.h> | ||||||
|  |  | ||||||
|  |  | ||||||
| namespace snake | namespace snake | ||||||
| @@ -29,7 +29,7 @@ namespace snake | |||||||
|  |  | ||||||
|         std::stringstream ss; |         std::stringstream ss; | ||||||
|         ss << "Listening on " << appConfig.hostname << ":" << appConfig.port; |         ss << "Listening on " << appConfig.hostname << ":" << appConfig.port; | ||||||
|         ix::CoreLogger::log(ss.str().c_str()); |         ix::IXCoreLogger::Log(ss.str().c_str()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |     // | ||||||
| @@ -67,7 +67,6 @@ namespace snake | |||||||
|                 webSocket->setOnMessageCallback( |                 webSocket->setOnMessageCallback( | ||||||
|                     [this, webSocket, state](const ix::WebSocketMessagePtr& msg) { |                     [this, webSocket, state](const ix::WebSocketMessagePtr& msg) { | ||||||
|                         std::stringstream ss; |                         std::stringstream ss; | ||||||
|                         ix::LogLevel logLevel = ix::LogLevel::Debug; |  | ||||||
|                         if (msg->type == ix::WebSocketMessageType::Open) |                         if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|                         { |                         { | ||||||
|                             ss << "New connection" << std::endl; |                             ss << "New connection" << std::endl; | ||||||
| @@ -87,7 +86,6 @@ namespace snake | |||||||
|                                                               _appConfig.redisPort)) |                                                               _appConfig.redisPort)) | ||||||
|                             { |                             { | ||||||
|                                 ss << "Cannot connect to redis host" << std::endl; |                                 ss << "Cannot connect to redis host" << std::endl; | ||||||
|                                 logLevel = ix::LogLevel::Error; |  | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         else if (msg->type == ix::WebSocketMessageType::Close) |                         else if (msg->type == ix::WebSocketMessageType::Close) | ||||||
| @@ -103,7 +101,6 @@ namespace snake | |||||||
|                             ss << "#retries: " << msg->errorInfo.retries << std::endl; |                             ss << "#retries: " << msg->errorInfo.retries << std::endl; | ||||||
|                             ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl; |                             ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl; | ||||||
|                             ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl; |                             ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl; | ||||||
|                             logLevel = ix::LogLevel::Error; |  | ||||||
|                         } |                         } | ||||||
|                         else if (msg->type == ix::WebSocketMessageType::Fragment) |                         else if (msg->type == ix::WebSocketMessageType::Fragment) | ||||||
|                         { |                         { | ||||||
| @@ -115,7 +112,7 @@ namespace snake | |||||||
|                             processCobraMessage(state, webSocket, _appConfig, msg->str); |                             processCobraMessage(state, webSocket, _appConfig, msg->str); | ||||||
|                         } |                         } | ||||||
|  |  | ||||||
|                         ix::CoreLogger::log(ss.str().c_str(), logLevel); |                         ix::IXCoreLogger::Log(ss.str().c_str()); | ||||||
|                     }); |                     }); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ | |||||||
|  |  | ||||||
| #include "IXCancellationRequest.h" | #include "IXCancellationRequest.h" | ||||||
|  |  | ||||||
| #include <cassert> |  | ||||||
| #include <chrono> | #include <chrono> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| @@ -14,8 +13,6 @@ namespace ix | |||||||
|     CancellationRequest makeCancellationRequestWithTimeout( |     CancellationRequest makeCancellationRequestWithTimeout( | ||||||
|         int secs, std::atomic<bool>& requestInitCancellation) |         int secs, std::atomic<bool>& requestInitCancellation) | ||||||
|     { |     { | ||||||
|         assert(secs > 0); |  | ||||||
|  |  | ||||||
|         auto start = std::chrono::system_clock::now(); |         auto start = std::chrono::system_clock::now(); | ||||||
|         auto timeout = std::chrono::seconds(secs); |         auto timeout = std::chrono::seconds(secs); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,19 +4,6 @@ | |||||||
|  *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved. |  *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| // |  | ||||||
| // On Windows Universal Platform (uwp), gai_strerror defaults behavior is to returns wchar_t |  | ||||||
| // which is different from all other platforms. We want the non unicode version. |  | ||||||
| // See https://github.com/microsoft/vcpkg/pull/11030 |  | ||||||
| // We could do this in IXNetSystem.cpp but so far we are only using gai_strerror in here. |  | ||||||
| // |  | ||||||
| #ifdef _UNICODE |  | ||||||
| #undef _UNICODE |  | ||||||
| #endif |  | ||||||
| #ifdef UNICODE |  | ||||||
| #undef UNICODE |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include "IXDNSLookup.h" | #include "IXDNSLookup.h" | ||||||
|  |  | ||||||
| #include "IXNetSystem.h" | #include "IXNetSystem.h" | ||||||
|   | |||||||
| @@ -78,12 +78,12 @@ namespace ix | |||||||
|         WebSocketHttpHeaders extraHeaders; |         WebSocketHttpHeaders extraHeaders; | ||||||
|         std::string body; |         std::string body; | ||||||
|         std::string multipartBoundary; |         std::string multipartBoundary; | ||||||
|         int connectTimeout = 60; |         int connectTimeout; | ||||||
|         int transferTimeout = 1800; |         int transferTimeout; | ||||||
|         bool followRedirects = true; |         bool followRedirects; | ||||||
|         int maxRedirects = 5; |         int maxRedirects; | ||||||
|         bool verbose = false; |         bool verbose; | ||||||
|         bool compress = true; |         bool compress; | ||||||
|         Logger logger; |         Logger logger; | ||||||
|         OnProgressCallback onProgressCallback; |         OnProgressCallback onProgressCallback; | ||||||
|     }; |     }; | ||||||
|   | |||||||
| @@ -25,12 +25,10 @@ namespace ix | |||||||
|     const std::string HttpClient::kHead = "HEAD"; |     const std::string HttpClient::kHead = "HEAD"; | ||||||
|     const std::string HttpClient::kDel = "DEL"; |     const std::string HttpClient::kDel = "DEL"; | ||||||
|     const std::string HttpClient::kPut = "PUT"; |     const std::string HttpClient::kPut = "PUT"; | ||||||
|     const std::string HttpClient::kPatch = "PATCH"; |  | ||||||
|  |  | ||||||
|     HttpClient::HttpClient(bool async) |     HttpClient::HttpClient(bool async) | ||||||
|         : _async(async) |         : _async(async) | ||||||
|         , _stop(false) |         , _stop(false) | ||||||
|         , _forceBody(false) |  | ||||||
|     { |     { | ||||||
|         if (!_async) return; |         if (!_async) return; | ||||||
|  |  | ||||||
| @@ -51,11 +49,6 @@ namespace ix | |||||||
|         _tlsOptions = tlsOptions; |         _tlsOptions = tlsOptions; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void HttpClient::setForceBody(bool value) |  | ||||||
|     { |  | ||||||
|         _forceBody = value; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     HttpRequestArgsPtr HttpClient::createRequest(const std::string& url, const std::string& verb) |     HttpRequestArgsPtr HttpClient::createRequest(const std::string& url, const std::string& verb) | ||||||
|     { |     { | ||||||
|         auto request = std::make_shared<HttpRequestArgs>(); |         auto request = std::make_shared<HttpRequestArgs>(); | ||||||
| @@ -199,7 +192,7 @@ namespace ix | |||||||
|             ss << "User-Agent: " << userAgent() << "\r\n"; |             ss << "User-Agent: " << userAgent() << "\r\n"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (verb == kPost || verb == kPut || verb == kPatch || _forceBody) |         if (verb == kPost || verb == kPut) | ||||||
|         { |         { | ||||||
|             ss << "Content-Length: " << body.size() << "\r\n"; |             ss << "Content-Length: " << body.size() << "\r\n"; | ||||||
|  |  | ||||||
| @@ -227,10 +220,11 @@ namespace ix | |||||||
|  |  | ||||||
|         std::string req(ss.str()); |         std::string req(ss.str()); | ||||||
|         std::string errMsg; |         std::string errMsg; | ||||||
|  |         std::atomic<bool> requestInitCancellation(false); | ||||||
|  |  | ||||||
|         // Make a cancellation object dealing with connection timeout |         // Make a cancellation object dealing with connection timeout | ||||||
|         auto isCancellationRequested = |         auto isCancellationRequested = | ||||||
|             makeCancellationRequestWithTimeout(args->connectTimeout, _stop); |             makeCancellationRequestWithTimeout(args->connectTimeout, requestInitCancellation); | ||||||
|  |  | ||||||
|         bool success = _socket->connect(host, port, errMsg, isCancellationRequested); |         bool success = _socket->connect(host, port, errMsg, isCancellationRequested); | ||||||
|         if (!success) |         if (!success) | ||||||
| @@ -248,7 +242,8 @@ namespace ix | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Make a new cancellation object dealing with transfer timeout |         // Make a new cancellation object dealing with transfer timeout | ||||||
|         isCancellationRequested = makeCancellationRequestWithTimeout(args->transferTimeout, _stop); |         isCancellationRequested = | ||||||
|  |             makeCancellationRequestWithTimeout(args->transferTimeout, requestInitCancellation); | ||||||
|  |  | ||||||
|         if (args->verbose) |         if (args->verbose) | ||||||
|         { |         { | ||||||
| @@ -567,20 +562,6 @@ namespace ix | |||||||
|         return request(url, kPut, body, args); |         return request(url, kPut, body, args); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     HttpResponsePtr HttpClient::patch(const std::string& url, |  | ||||||
|                                       const HttpParameters& httpParameters, |  | ||||||
|                                       HttpRequestArgsPtr args) |  | ||||||
|     { |  | ||||||
|         return request(url, kPatch, serializeHttpParameters(httpParameters), args); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     HttpResponsePtr HttpClient::patch(const std::string& url, |  | ||||||
|                                       const std::string& body, |  | ||||||
|                                       const HttpRequestArgsPtr args) |  | ||||||
|     { |  | ||||||
|         return request(url, kPatch, body, args); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     std::string HttpClient::urlEncode(const std::string& value) |     std::string HttpClient::urlEncode(const std::string& value) | ||||||
|     { |     { | ||||||
|         std::ostringstream escaped; |         std::ostringstream escaped; | ||||||
|   | |||||||
| @@ -46,19 +46,12 @@ namespace ix | |||||||
|                             const std::string& body, |                             const std::string& body, | ||||||
|                             HttpRequestArgsPtr args); |                             HttpRequestArgsPtr args); | ||||||
|  |  | ||||||
|         HttpResponsePtr patch(const std::string& url, |  | ||||||
|                               const HttpParameters& httpParameters, |  | ||||||
|                               HttpRequestArgsPtr args); |  | ||||||
|         HttpResponsePtr patch(const std::string& url, |  | ||||||
|                               const std::string& body, |  | ||||||
|                               HttpRequestArgsPtr args); |  | ||||||
|  |  | ||||||
|         HttpResponsePtr request(const std::string& url, |         HttpResponsePtr request(const std::string& url, | ||||||
|                                 const std::string& verb, |                                 const std::string& verb, | ||||||
|                                 const std::string& body, |                                 const std::string& body, | ||||||
|                                 HttpRequestArgsPtr args, |                                 HttpRequestArgsPtr args, | ||||||
|                                 int redirects = 0); |                                 int redirects = 0); | ||||||
|         void setForceBody(bool value); |  | ||||||
|         // Async API |         // Async API | ||||||
|         HttpRequestArgsPtr createRequest(const std::string& url = std::string(), |         HttpRequestArgsPtr createRequest(const std::string& url = std::string(), | ||||||
|                                          const std::string& verb = HttpClient::kGet); |                                          const std::string& verb = HttpClient::kGet); | ||||||
| @@ -85,7 +78,6 @@ namespace ix | |||||||
|         const static std::string kHead; |         const static std::string kHead; | ||||||
|         const static std::string kDel; |         const static std::string kDel; | ||||||
|         const static std::string kPut; |         const static std::string kPut; | ||||||
|         const static std::string kPatch; |  | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         void log(const std::string& msg, HttpRequestArgsPtr args); |         void log(const std::string& msg, HttpRequestArgsPtr args); | ||||||
| @@ -94,6 +86,7 @@ namespace ix | |||||||
|  |  | ||||||
|         // Async API background thread runner |         // Async API background thread runner | ||||||
|         void run(); |         void run(); | ||||||
|  |  | ||||||
|         // Async API |         // Async API | ||||||
|         bool _async; |         bool _async; | ||||||
|         std::queue<std::pair<HttpRequestArgsPtr, OnResponseCallback>> _queue; |         std::queue<std::pair<HttpRequestArgsPtr, OnResponseCallback>> _queue; | ||||||
| @@ -106,7 +99,5 @@ namespace ix | |||||||
|         std::mutex _mutex; // to protect accessing the _socket (only one socket per client) |         std::mutex _mutex; // to protect accessing the _socket (only one socket per client) | ||||||
|  |  | ||||||
|         SocketTLSOptions _tlsOptions; |         SocketTLSOptions _tlsOptions; | ||||||
|  |  | ||||||
|         bool _forceBody; |  | ||||||
|     }; |     }; | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -19,7 +19,6 @@ typedef unsigned long int nfds_t; | |||||||
| #else | #else | ||||||
| #include <arpa/inet.h> | #include <arpa/inet.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <fcntl.h> |  | ||||||
| #include <netdb.h> | #include <netdb.h> | ||||||
| #include <netinet/in.h> | #include <netinet/in.h> | ||||||
| #include <netinet/ip.h> | #include <netinet/ip.h> | ||||||
|   | |||||||
							
								
								
									
										115
									
								
								ixwebsocket/IXSelectInterruptEventFd.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								ixwebsocket/IXSelectInterruptEventFd.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | |||||||
|  | /* | ||||||
|  |  *  IXSelectInterruptEventFd.cpp | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // On Linux we use eventd to wake up select. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // Linux/Android has a special type of virtual files. select(2) will react | ||||||
|  | // when reading/writing to those files, unlike closing sockets. | ||||||
|  | // | ||||||
|  | // https://linux.die.net/man/2/eventfd | ||||||
|  | // http://www.sourcexr.com/articles/2013/10/26/lightweight-inter-process-signaling-with-eventfd | ||||||
|  | // | ||||||
|  | // eventfd was added in Linux kernel 2.x, and our oldest Android (Kitkat 4.4) | ||||||
|  | // is on Kernel 3.x | ||||||
|  | // | ||||||
|  | // cf Android/Kernel table here | ||||||
|  | // https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel | ||||||
|  | // | ||||||
|  | // On macOS we use UNIX pipes to wake up select. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include "IXSelectInterruptEventFd.h" | ||||||
|  |  | ||||||
|  | #include <assert.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <sstream> | ||||||
|  | #include <string.h> // for strerror | ||||||
|  | #include <sys/eventfd.h> | ||||||
|  | #include <unistd.h> // for write | ||||||
|  |  | ||||||
|  | namespace ix | ||||||
|  | { | ||||||
|  |     SelectInterruptEventFd::SelectInterruptEventFd() | ||||||
|  |     { | ||||||
|  |         _eventfd = -1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SelectInterruptEventFd::~SelectInterruptEventFd() | ||||||
|  |     { | ||||||
|  |         ::close(_eventfd); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SelectInterruptEventFd::init(std::string& errorMsg) | ||||||
|  |     { | ||||||
|  |         // calling init twice is a programming error | ||||||
|  |         assert(_eventfd == -1); | ||||||
|  |  | ||||||
|  |         _eventfd = eventfd(0, 0); | ||||||
|  |         if (_eventfd < 0) | ||||||
|  |         { | ||||||
|  |             std::stringstream ss; | ||||||
|  |             ss << "SelectInterruptEventFd::init() failed in eventfd()" | ||||||
|  |                << " : " << strerror(errno); | ||||||
|  |             errorMsg = ss.str(); | ||||||
|  |  | ||||||
|  |             _eventfd = -1; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (fcntl(_eventfd, F_SETFL, O_NONBLOCK) == -1) | ||||||
|  |         { | ||||||
|  |             std::stringstream ss; | ||||||
|  |             ss << "SelectInterruptEventFd::init() failed in fcntl() call" | ||||||
|  |                << " : " << strerror(errno); | ||||||
|  |             errorMsg = ss.str(); | ||||||
|  |  | ||||||
|  |             _eventfd = -1; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SelectInterruptEventFd::notify(uint64_t value) | ||||||
|  |     { | ||||||
|  |         int fd = _eventfd; | ||||||
|  |  | ||||||
|  |         if (fd == -1) return false; | ||||||
|  |  | ||||||
|  |         // we should write 8 bytes for an uint64_t | ||||||
|  |         return write(fd, &value, sizeof(value)) == 8; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // TODO: return max uint64_t for errors ? | ||||||
|  |     uint64_t SelectInterruptEventFd::read() | ||||||
|  |     { | ||||||
|  |         int fd = _eventfd; | ||||||
|  |  | ||||||
|  |         uint64_t value = 0; | ||||||
|  |         ::read(fd, &value, sizeof(value)); | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SelectInterruptEventFd::clear() | ||||||
|  |     { | ||||||
|  |         if (_eventfd == -1) return false; | ||||||
|  |  | ||||||
|  |         // 0 is a special value ; select will not wake up | ||||||
|  |         uint64_t value = 0; | ||||||
|  |  | ||||||
|  |         // we should write 8 bytes for an uint64_t | ||||||
|  |         return write(_eventfd, &value, sizeof(value)) == 8; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int SelectInterruptEventFd::getFd() const | ||||||
|  |     { | ||||||
|  |         return _eventfd; | ||||||
|  |     } | ||||||
|  | } // namespace ix | ||||||
							
								
								
									
										31
									
								
								ixwebsocket/IXSelectInterruptEventFd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								ixwebsocket/IXSelectInterruptEventFd.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | /* | ||||||
|  |  *  IXSelectInterruptEventFd.h | ||||||
|  |  *  Author: Benjamin Sergeant | ||||||
|  |  *  Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "IXSelectInterrupt.h" | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | namespace ix | ||||||
|  | { | ||||||
|  |     class SelectInterruptEventFd final : public SelectInterrupt | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         SelectInterruptEventFd(); | ||||||
|  |         virtual ~SelectInterruptEventFd(); | ||||||
|  |  | ||||||
|  |         bool init(std::string& errorMsg) final; | ||||||
|  |  | ||||||
|  |         bool notify(uint64_t value) final; | ||||||
|  |         bool clear() final; | ||||||
|  |         uint64_t read() final; | ||||||
|  |         int getFd() const final; | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         int _eventfd; | ||||||
|  |     }; | ||||||
|  | } // namespace ix | ||||||
| @@ -376,8 +376,7 @@ namespace ix | |||||||
|         { |         { | ||||||
|             if (isCancellationRequested && isCancellationRequested()) |             if (isCancellationRequested && isCancellationRequested()) | ||||||
|             { |             { | ||||||
|                 const std::string errorMsg("Cancellation Requested"); |                 return std::make_pair(false, std::string()); | ||||||
|                 return std::make_pair(false, errorMsg); |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             size_t size = std::min(kChunkSize, length - output.size()); |             size_t size = std::min(kChunkSize, length - output.size()); | ||||||
| @@ -389,8 +388,7 @@ namespace ix | |||||||
|             } |             } | ||||||
|             else if (ret <= 0 && !Socket::isWaitNeeded()) |             else if (ret <= 0 && !Socket::isWaitNeeded()) | ||||||
|             { |             { | ||||||
|                 const std::string errorMsg("Recv Error"); |                 return std::make_pair(false, std::string()); | ||||||
|                 return std::make_pair(false, errorMsg); |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if (onProgressCallback) onProgressCallback((int) output.size(), (int) length); |             if (onProgressCallback) onProgressCallback((int) output.size(), (int) length); | ||||||
| @@ -399,8 +397,7 @@ namespace ix | |||||||
|             // This way we are not busy looping |             // This way we are not busy looping | ||||||
|             if (isReadyToRead(1) == PollResultType::Error) |             if (isReadyToRead(1) == PollResultType::Error) | ||||||
|             { |             { | ||||||
|                 const std::string errorMsg("Poll Error"); |                 return std::make_pair(false, std::string()); | ||||||
|                 return std::make_pair(false, errorMsg); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,12 +1,10 @@ | |||||||
| /* | /* | ||||||
|  *  IXSocketAppleSSL.cpp |  *  IXSocketAppleSSL.cpp | ||||||
|  *  Author: Benjamin Sergeant |  *  Author: Benjamin Sergeant | ||||||
|  *  Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. |  *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. | ||||||
|  * |  * | ||||||
|  *  Adapted from Satori SDK Apple SSL code. |  *  Adapted from Satori SDK Apple SSL code. | ||||||
|  */ |  */ | ||||||
| #ifdef IXWEBSOCKET_USE_SECURE_TRANSPORT |  | ||||||
|  |  | ||||||
| #include "IXSocketAppleSSL.h" | #include "IXSocketAppleSSL.h" | ||||||
|  |  | ||||||
| #include "IXSocketConnect.h" | #include "IXSocketConnect.h" | ||||||
| @@ -309,5 +307,3 @@ namespace ix | |||||||
|     } |     } | ||||||
|  |  | ||||||
| } // namespace ix | } // namespace ix | ||||||
|  |  | ||||||
| #endif // IXWEBSOCKET_USE_SECURE_TRANSPORT |  | ||||||
|   | |||||||
| @@ -1,9 +1,8 @@ | |||||||
| /* | /* | ||||||
|  *  IXSocketAppleSSL.h |  *  IXSocketAppleSSL.h | ||||||
|  *  Author: Benjamin Sergeant |  *  Author: Benjamin Sergeant | ||||||
|  *  Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. |  *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. | ||||||
|  */ |  */ | ||||||
| #ifdef IXWEBSOCKET_USE_SECURE_TRANSPORT |  | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| @@ -48,5 +47,3 @@ namespace ix | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
| } // namespace ix | } // namespace ix | ||||||
|  |  | ||||||
| #endif // IXWEBSOCKET_USE_SECURE_TRANSPORT |  | ||||||
|   | |||||||
| @@ -1,13 +1,12 @@ | |||||||
| /* | /* | ||||||
|  *  IXSocketMbedTLS.cpp |  *  IXSocketMbedTLS.cpp | ||||||
|  *  Author: Benjamin Sergeant, Max Weisel |  *  Author: Benjamin Sergeant | ||||||
|  *  Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved. |  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. | ||||||
|  * |  * | ||||||
|  *  Some code taken from |  *  Some code taken from | ||||||
|  *  https://github.com/rottor12/WsClientLib/blob/master/lib/src/WsClientLib.cpp |  *  https://github.com/rottor12/WsClientLib/blob/master/lib/src/WsClientLib.cpp | ||||||
|  *  and mini_client.c example from mbedtls |  *  and mini_client.c example from mbedtls | ||||||
|  */ |  */ | ||||||
| #ifdef IXWEBSOCKET_USE_MBED_TLS |  | ||||||
|  |  | ||||||
| #include "IXSocketMbedTLS.h" | #include "IXSocketMbedTLS.h" | ||||||
|  |  | ||||||
| @@ -43,55 +42,6 @@ namespace ix | |||||||
|         mbedtls_pk_init(&_pkey); |         mbedtls_pk_init(&_pkey); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool SocketMbedTLS::loadSystemCertificates(std::string& errorMsg) |  | ||||||
|     { |  | ||||||
| #ifdef _WIN32 |  | ||||||
|         DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG | |  | ||||||
|                       CERT_SYSTEM_STORE_CURRENT_USER; |  | ||||||
|         HCERTSTORE systemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root"); |  | ||||||
|  |  | ||||||
|         if (!systemStore) |  | ||||||
|         { |  | ||||||
|             errorMsg = "CertOpenStore failed with "; |  | ||||||
|             errorMsg += std::to_string(GetLastError()); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         PCCERT_CONTEXT certificateIterator = NULL; |  | ||||||
|  |  | ||||||
|         int certificateCount = 0; |  | ||||||
|         while (certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator)) |  | ||||||
|         { |  | ||||||
|             if (certificateIterator->dwCertEncodingType & X509_ASN_ENCODING) |  | ||||||
|             { |  | ||||||
|                 int ret = mbedtls_x509_crt_parse(&_cacert, |  | ||||||
|                                                  certificateIterator->pbCertEncoded, |  | ||||||
|                                                  certificateIterator->cbCertEncoded); |  | ||||||
|                 if (ret == 0) |  | ||||||
|                 { |  | ||||||
|                     ++certificateCount; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         CertFreeCertificateContext(certificateIterator); |  | ||||||
|         CertCloseStore(systemStore, 0); |  | ||||||
|  |  | ||||||
|         if (certificateCount == 0) |  | ||||||
|         { |  | ||||||
|             errorMsg = "No certificates found"; |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return true; |  | ||||||
| #else |  | ||||||
|         // On macOS we can query the system cert location from the keychain |  | ||||||
|         // On Linux we could try to fetch some local files based on the distribution |  | ||||||
|         // On Android we could use JNI to get to the system certs |  | ||||||
|         return false; |  | ||||||
| #endif |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bool SocketMbedTLS::init(const std::string& host, bool isClient, std::string& errMsg) |     bool SocketMbedTLS::init(const std::string& host, bool isClient, std::string& errMsg) | ||||||
|     { |     { | ||||||
|         initMBedTLS(); |         initMBedTLS(); | ||||||
| @@ -145,36 +95,18 @@ namespace ix | |||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             // FIXME: should we call mbedtls_ssl_conf_verify ? |  | ||||||
|             mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED); |             mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED); | ||||||
|  |  | ||||||
|  |             // FIXME: should we call mbedtls_ssl_conf_verify ? | ||||||
|  |  | ||||||
|             if (_tlsOptions.isUsingSystemDefaults()) |             if (_tlsOptions.isUsingSystemDefaults()) | ||||||
|             { |             { | ||||||
|                 if (!loadSystemCertificates(errMsg)) |                 ; // FIXME | ||||||
|                 { |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             else |             else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0) | ||||||
|             { |             { | ||||||
|                 if (_tlsOptions.isUsingInMemoryCAs()) |                 errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'"; | ||||||
|                 { |                 return false; | ||||||
|                     const char* buffer = _tlsOptions.caFile.c_str(); |  | ||||||
|                     size_t bufferSize = |  | ||||||
|                         _tlsOptions.caFile.size() + 1; // Needs to include null terminating |  | ||||||
|                                                        // character otherwise mbedtls will fail. |  | ||||||
|                     if (mbedtls_x509_crt_parse( |  | ||||||
|                             &_cacert, (const unsigned char*) buffer, bufferSize) < 0) |  | ||||||
|                     { |  | ||||||
|                         errMsg = "Cannot parse CA from memory."; |  | ||||||
|                         return false; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0) |  | ||||||
|                 { |  | ||||||
|                     errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'"; |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL); |             mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL); | ||||||
| @@ -348,5 +280,3 @@ namespace ix | |||||||
|     } |     } | ||||||
|  |  | ||||||
| } // namespace ix | } // namespace ix | ||||||
|  |  | ||||||
| #endif // IXWEBSOCKET_USE_MBED_TLS |  | ||||||
|   | |||||||
| @@ -1,9 +1,8 @@ | |||||||
| /* | /* | ||||||
|  *  IXSocketMbedTLS.h |  *  IXSocketMbedTLS.h | ||||||
|  *  Author: Benjamin Sergeant |  *  Author: Benjamin Sergeant | ||||||
|  *  Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved. |  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. | ||||||
|  */ |  */ | ||||||
| #ifdef IXWEBSOCKET_USE_MBED_TLS |  | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| @@ -52,9 +51,6 @@ namespace ix | |||||||
|  |  | ||||||
|         bool init(const std::string& host, bool isClient, std::string& errMsg); |         bool init(const std::string& host, bool isClient, std::string& errMsg); | ||||||
|         void initMBedTLS(); |         void initMBedTLS(); | ||||||
|         bool loadSystemCertificates(std::string& errMsg); |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| } // namespace ix | } // namespace ix | ||||||
|  |  | ||||||
| #endif // IXWEBSOCKET_USE_MBED_TLS |  | ||||||
|   | |||||||
| @@ -1,11 +1,10 @@ | |||||||
| /* | /* | ||||||
|  *  IXSocketOpenSSL.cpp |  *  IXSocketOpenSSL.cpp | ||||||
|  *  Author: Benjamin Sergeant, Matt DeBoer, Max Weisel |  *  Author: Benjamin Sergeant, Matt DeBoer | ||||||
|  *  Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. |  *  Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. | ||||||
|  * |  * | ||||||
|  *  Adapted from Satori SDK OpenSSL code. |  *  Adapted from Satori SDK OpenSSL code. | ||||||
|  */ |  */ | ||||||
| #ifdef IXWEBSOCKET_USE_OPEN_SSL |  | ||||||
|  |  | ||||||
| #include "IXSocketOpenSSL.h" | #include "IXSocketOpenSSL.h" | ||||||
|  |  | ||||||
| @@ -33,8 +32,7 @@ namespace | |||||||
|  |  | ||||||
|         if (!systemStore) |         if (!systemStore) | ||||||
|         { |         { | ||||||
|             errorMsg = "CertOpenStore failed with "; |             errorMsg = "CertOpenStore failed with " errorMsg += std::to_string(GetLastError()); | ||||||
|             errorMsg += std::to_string(GetLastError()); |  | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -85,8 +83,6 @@ namespace ix | |||||||
|  |  | ||||||
|     std::atomic<bool> SocketOpenSSL::_openSSLInitializationSuccessful(false); |     std::atomic<bool> SocketOpenSSL::_openSSLInitializationSuccessful(false); | ||||||
|     std::once_flag SocketOpenSSL::_openSSLInitFlag; |     std::once_flag SocketOpenSSL::_openSSLInitFlag; | ||||||
|     std::unique_ptr<std::mutex[]> SocketOpenSSL::_openSSLMutexes = |  | ||||||
|         std::make_unique<std::mutex[]>(CRYPTO_num_locks()); |  | ||||||
|  |  | ||||||
|     SocketOpenSSL::SocketOpenSSL(const SocketTLSOptions& tlsOptions, int fd) |     SocketOpenSSL::SocketOpenSSL(const SocketTLSOptions& tlsOptions, int fd) | ||||||
|         : Socket(fd) |         : Socket(fd) | ||||||
| @@ -108,11 +104,6 @@ namespace ix | |||||||
|         if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr)) return; |         if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr)) return; | ||||||
| #else | #else | ||||||
|         (void) OPENSSL_config(nullptr); |         (void) OPENSSL_config(nullptr); | ||||||
|  |  | ||||||
|         if (CRYPTO_get_locking_callback() == nullptr) |  | ||||||
|         { |  | ||||||
|             CRYPTO_set_locking_callback(SocketOpenSSL::openSSLLockingCallback); |  | ||||||
|         } |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|         (void) OpenSSL_add_ssl_algorithms(); |         (void) OpenSSL_add_ssl_algorithms(); | ||||||
| @@ -121,21 +112,6 @@ namespace ix | |||||||
|         _openSSLInitializationSuccessful = true; |         _openSSLInitializationSuccessful = true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void SocketOpenSSL::openSSLLockingCallback(int mode, |  | ||||||
|                                                int type, |  | ||||||
|                                                const char* /*file*/, |  | ||||||
|                                                int /*line*/) |  | ||||||
|     { |  | ||||||
|         if (mode & CRYPTO_LOCK) |  | ||||||
|         { |  | ||||||
|             _openSSLMutexes[type].lock(); |  | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             _openSSLMutexes[type].unlock(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     std::string SocketOpenSSL::getSSLError(int ret) |     std::string SocketOpenSSL::getSSLError(int ret) | ||||||
|     { |     { | ||||||
|         unsigned long e; |         unsigned long e; | ||||||
| @@ -217,66 +193,6 @@ namespace ix | |||||||
|         return ctx; |         return ctx; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool SocketOpenSSL::openSSLAddCARootsFromString(const std::string roots) |  | ||||||
|     { |  | ||||||
|         // Create certificate store |  | ||||||
|         X509_STORE* certificate_store = SSL_CTX_get_cert_store(_ssl_context); |  | ||||||
|         if (certificate_store == nullptr) return false; |  | ||||||
|  |  | ||||||
|         // Configure to allow intermediate certs |  | ||||||
|         X509_STORE_set_flags(certificate_store, |  | ||||||
|                              X509_V_FLAG_TRUSTED_FIRST | X509_V_FLAG_PARTIAL_CHAIN); |  | ||||||
|  |  | ||||||
|         // Create a new buffer and populate it with the roots |  | ||||||
|         BIO* buffer = BIO_new_mem_buf((void*) roots.c_str(), static_cast<int>(roots.length())); |  | ||||||
|         if (buffer == nullptr) return false; |  | ||||||
|  |  | ||||||
|         // Read each root in the buffer and add to the certificate store |  | ||||||
|         bool success = true; |  | ||||||
|         size_t number_of_roots = 0; |  | ||||||
|  |  | ||||||
|         while (true) |  | ||||||
|         { |  | ||||||
|             // Read the next root in the buffer |  | ||||||
|             X509* root = PEM_read_bio_X509_AUX(buffer, nullptr, nullptr, (void*) ""); |  | ||||||
|             if (root == nullptr) |  | ||||||
|             { |  | ||||||
|                 // No more certs left in the buffer, we're done. |  | ||||||
|                 ERR_clear_error(); |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Try adding the root to the certificate store |  | ||||||
|             ERR_clear_error(); |  | ||||||
|             if (!X509_STORE_add_cert(certificate_store, root)) |  | ||||||
|             { |  | ||||||
|                 // Failed to add. If the error is unrelated to the x509 lib or the cert already |  | ||||||
|                 // exists, we're safe to continue. |  | ||||||
|                 unsigned long error = ERR_get_error(); |  | ||||||
|                 if (ERR_GET_LIB(error) != ERR_LIB_X509 || |  | ||||||
|                     ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) |  | ||||||
|                 { |  | ||||||
|                     // Failed. Clean up and bail. |  | ||||||
|                     success = false; |  | ||||||
|                     X509_free(root); |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Clean up and loop |  | ||||||
|             X509_free(root); |  | ||||||
|             number_of_roots++; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Clean up buffer |  | ||||||
|         BIO_free(buffer); |  | ||||||
|  |  | ||||||
|         // Make sure we loaded at least one certificate. |  | ||||||
|         if (number_of_roots == 0) success = false; |  | ||||||
|  |  | ||||||
|         return success; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Check whether a hostname matches a pattern |      * Check whether a hostname matches a pattern | ||||||
|      */ |      */ | ||||||
| @@ -471,7 +387,7 @@ namespace ix | |||||||
|             if (_tlsOptions.isUsingSystemDefaults()) |             if (_tlsOptions.isUsingSystemDefaults()) | ||||||
|             { |             { | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|                 if (!loadWindowsSystemCertificates(_ssl_context, errMsg)) |                 if (!loadWindowsSystemCertificates(_ssl_context)) | ||||||
|                 { |                 { | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
| @@ -485,32 +401,20 @@ namespace ix | |||||||
|                 } |                 } | ||||||
| #endif | #endif | ||||||
|             } |             } | ||||||
|             else |             else if (SSL_CTX_load_verify_locations( | ||||||
|  |                          _ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1) | ||||||
|             { |             { | ||||||
|                 if (_tlsOptions.isUsingInMemoryCAs()) |                 auto sslErr = ERR_get_error(); | ||||||
|                 { |                 errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + _tlsOptions.caFile + | ||||||
|                     // Load from memory |                          "\") failed: "; | ||||||
|                     openSSLAddCARootsFromString(_tlsOptions.caFile); |                 errMsg += ERR_error_string(sslErr, nullptr); | ||||||
|                 } |                 return false; | ||||||
|                 else |  | ||||||
|                 { |  | ||||||
|                     if (SSL_CTX_load_verify_locations( |  | ||||||
|                             _ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1) |  | ||||||
|                     { |  | ||||||
|                         auto sslErr = ERR_get_error(); |  | ||||||
|                         errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + |  | ||||||
|                                  _tlsOptions.caFile + "\") failed: "; |  | ||||||
|                         errMsg += ERR_error_string(sslErr, nullptr); |  | ||||||
|                         return false; |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     SSL_CTX_set_verify( |  | ||||||
|                         _ssl_context, SSL_VERIFY_PEER, [](int preverify, X509_STORE_CTX*) -> int { |  | ||||||
|                             return preverify; |  | ||||||
|                         }); |  | ||||||
|                     SSL_CTX_set_verify_depth(_ssl_context, 4); |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             SSL_CTX_set_verify(_ssl_context, | ||||||
|  |                                SSL_VERIFY_PEER, | ||||||
|  |                                [](int preverify, X509_STORE_CTX*) -> int { return preverify; }); | ||||||
|  |             SSL_CTX_set_verify_depth(_ssl_context, 4); | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
| @@ -620,35 +524,26 @@ namespace ix | |||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     if (_tlsOptions.isUsingInMemoryCAs()) |                     const char* root_ca_file = _tlsOptions.caFile.c_str(); | ||||||
|  |                     STACK_OF(X509_NAME) * rootCAs; | ||||||
|  |                     rootCAs = SSL_load_client_CA_file(root_ca_file); | ||||||
|  |                     if (rootCAs == NULL) | ||||||
|                     { |                     { | ||||||
|                         // Load from memory |                         auto sslErr = ERR_get_error(); | ||||||
|                         openSSLAddCARootsFromString(_tlsOptions.caFile); |                         errMsg = "OpenSSL failed - SSL_load_client_CA_file('" + _tlsOptions.caFile + | ||||||
|  |                                  "') failed: "; | ||||||
|  |                         errMsg += ERR_error_string(sslErr, nullptr); | ||||||
|                     } |                     } | ||||||
|                     else |                     else | ||||||
|                     { |                     { | ||||||
|                         const char* root_ca_file = _tlsOptions.caFile.c_str(); |                         SSL_CTX_set_client_CA_list(_ssl_context, rootCAs); | ||||||
|                         STACK_OF(X509_NAME) * rootCAs; |                         if (SSL_CTX_load_verify_locations(_ssl_context, root_ca_file, nullptr) != 1) | ||||||
|                         rootCAs = SSL_load_client_CA_file(root_ca_file); |  | ||||||
|                         if (rootCAs == NULL) |  | ||||||
|                         { |                         { | ||||||
|                             auto sslErr = ERR_get_error(); |                             auto sslErr = ERR_get_error(); | ||||||
|                             errMsg = "OpenSSL failed - SSL_load_client_CA_file('" + |                             errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + | ||||||
|                                      _tlsOptions.caFile + "') failed: "; |                                      _tlsOptions.caFile + "\") failed: "; | ||||||
|                             errMsg += ERR_error_string(sslErr, nullptr); |                             errMsg += ERR_error_string(sslErr, nullptr); | ||||||
|                         } |                         } | ||||||
|                         else |  | ||||||
|                         { |  | ||||||
|                             SSL_CTX_set_client_CA_list(_ssl_context, rootCAs); |  | ||||||
|                             if (SSL_CTX_load_verify_locations( |  | ||||||
|                                     _ssl_context, root_ca_file, nullptr) != 1) |  | ||||||
|                             { |  | ||||||
|                                 auto sslErr = ERR_get_error(); |  | ||||||
|                                 errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + |  | ||||||
|                                          _tlsOptions.caFile + "\") failed: "; |  | ||||||
|                                 errMsg += ERR_error_string(sslErr, nullptr); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -835,5 +730,3 @@ namespace ix | |||||||
|     } |     } | ||||||
|  |  | ||||||
| } // namespace ix | } // namespace ix | ||||||
|  |  | ||||||
| #endif // IXWEBSOCKET_USE_OPEN_SSL |  | ||||||
|   | |||||||
| @@ -1,9 +1,8 @@ | |||||||
| /* | /* | ||||||
|  *  IXSocketOpenSSL.h |  *  IXSocketOpenSSL.h | ||||||
|  *  Author: Benjamin Sergeant, Matt DeBoer |  *  Author: Benjamin Sergeant, Matt DeBoer | ||||||
|  *  Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. |  *  Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. | ||||||
|  */ |  */ | ||||||
| #ifdef IXWEBSOCKET_USE_OPEN_SSL |  | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| @@ -40,7 +39,6 @@ namespace ix | |||||||
|         void openSSLInitialize(); |         void openSSLInitialize(); | ||||||
|         std::string getSSLError(int ret); |         std::string getSSLError(int ret); | ||||||
|         SSL_CTX* openSSLCreateContext(std::string& errMsg); |         SSL_CTX* openSSLCreateContext(std::string& errMsg); | ||||||
|         bool openSSLAddCARootsFromString(const std::string roots); |  | ||||||
|         bool openSSLClientHandshake(const std::string& hostname, |         bool openSSLClientHandshake(const std::string& hostname, | ||||||
|                                     std::string& errMsg, |                                     std::string& errMsg, | ||||||
|                                     const CancellationRequest& isCancellationRequested); |                                     const CancellationRequest& isCancellationRequested); | ||||||
| @@ -49,9 +47,6 @@ namespace ix | |||||||
|         bool handleTLSOptions(std::string& errMsg); |         bool handleTLSOptions(std::string& errMsg); | ||||||
|         bool openSSLServerHandshake(std::string& errMsg); |         bool openSSLServerHandshake(std::string& errMsg); | ||||||
|  |  | ||||||
|         // Required for OpenSSL < 1.1 |  | ||||||
|         static void openSSLLockingCallback(int mode, int type, const char* /*file*/, int /*line*/); |  | ||||||
|  |  | ||||||
|         SSL* _ssl_connection; |         SSL* _ssl_connection; | ||||||
|         SSL_CTX* _ssl_context; |         SSL_CTX* _ssl_context; | ||||||
|         const SSL_METHOD* _ssl_method; |         const SSL_METHOD* _ssl_method; | ||||||
| @@ -61,9 +56,6 @@ namespace ix | |||||||
|  |  | ||||||
|         static std::once_flag _openSSLInitFlag; |         static std::once_flag _openSSLInitFlag; | ||||||
|         static std::atomic<bool> _openSSLInitializationSuccessful; |         static std::atomic<bool> _openSSLInitializationSuccessful; | ||||||
|         static std::unique_ptr<std::mutex[]> _openSSLMutexes; |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| } // namespace ix | } // namespace ix | ||||||
|  |  | ||||||
| #endif // IXWEBSOCKET_USE_OPEN_SSL |  | ||||||
|   | |||||||
| @@ -15,7 +15,6 @@ namespace ix | |||||||
|     const char* kTLSCAFileUseSystemDefaults = "SYSTEM"; |     const char* kTLSCAFileUseSystemDefaults = "SYSTEM"; | ||||||
|     const char* kTLSCAFileDisableVerify = "NONE"; |     const char* kTLSCAFileDisableVerify = "NONE"; | ||||||
|     const char* kTLSCiphersUseDefault = "DEFAULT"; |     const char* kTLSCiphersUseDefault = "DEFAULT"; | ||||||
|     const char* kTLSInMemoryMarker = "-----BEGIN CERTIFICATE-----"; |  | ||||||
|  |  | ||||||
|     bool SocketTLSOptions::isValid() const |     bool SocketTLSOptions::isValid() const | ||||||
|     { |     { | ||||||
| @@ -59,11 +58,6 @@ namespace ix | |||||||
|         return caFile == kTLSCAFileUseSystemDefaults; |         return caFile == kTLSCAFileUseSystemDefaults; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool SocketTLSOptions::isUsingInMemoryCAs() const |  | ||||||
|     { |  | ||||||
|         return caFile.find(kTLSInMemoryMarker) != std::string::npos; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bool SocketTLSOptions::isPeerVerifyDisabled() const |     bool SocketTLSOptions::isPeerVerifyDisabled() const | ||||||
|     { |     { | ||||||
|         return caFile == kTLSCAFileDisableVerify; |         return caFile == kTLSCAFileDisableVerify; | ||||||
|   | |||||||
| @@ -37,8 +37,6 @@ namespace ix | |||||||
|  |  | ||||||
|         bool isUsingSystemDefaults() const; |         bool isUsingSystemDefaults() const; | ||||||
|  |  | ||||||
|         bool isUsingInMemoryCAs() const; |  | ||||||
|  |  | ||||||
|         bool isPeerVerifyDisabled() const; |         bool isPeerVerifyDisabled() const; | ||||||
|  |  | ||||||
|         bool isUsingDefaultCiphers() const; |         bool isUsingDefaultCiphers() const; | ||||||
|   | |||||||
| @@ -44,18 +44,6 @@ namespace ix | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bool UdpSocket::isWaitNeeded() |  | ||||||
|     { |  | ||||||
|         int err = getErrno(); |  | ||||||
|  |  | ||||||
|         if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS) |  | ||||||
|         { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void UdpSocket::closeSocket(int fd) |     void UdpSocket::closeSocket(int fd) | ||||||
|     { |     { | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| @@ -74,13 +62,6 @@ namespace ix | |||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| #ifdef _WIN32 |  | ||||||
|         unsigned long nonblocking = 1; |  | ||||||
|         ioctlsocket(_sockfd, FIONBIO, &nonblocking); |  | ||||||
| #else |  | ||||||
|         fcntl(_sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|         memset(&_server, 0, sizeof(_server)); |         memset(&_server, 0, sizeof(_server)); | ||||||
|         _server.sin_family = AF_INET; |         _server.sin_family = AF_INET; | ||||||
|         _server.sin_port = htons(port); |         _server.sin_port = htons(port); | ||||||
| @@ -112,15 +93,4 @@ namespace ix | |||||||
|         return (ssize_t)::sendto( |         return (ssize_t)::sendto( | ||||||
|             _sockfd, buffer.data(), buffer.size(), 0, (struct sockaddr*) &_server, sizeof(_server)); |             _sockfd, buffer.data(), buffer.size(), 0, (struct sockaddr*) &_server, sizeof(_server)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ssize_t UdpSocket::recvfrom(char* buffer, size_t length) |  | ||||||
|     { |  | ||||||
| #ifdef _WIN32 |  | ||||||
|         int addressLen = (int) sizeof(_server); |  | ||||||
| #else |  | ||||||
|         socklen_t addressLen = (socklen_t) sizeof(_server); |  | ||||||
| #endif |  | ||||||
|         return (ssize_t)::recvfrom( |  | ||||||
|             _sockfd, buffer, length, 0, (struct sockaddr*) &_server, &addressLen); |  | ||||||
|     } |  | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -28,12 +28,9 @@ namespace ix | |||||||
|         // Virtual methods |         // Virtual methods | ||||||
|         bool init(const std::string& host, int port, std::string& errMsg); |         bool init(const std::string& host, int port, std::string& errMsg); | ||||||
|         ssize_t sendto(const std::string& buffer); |         ssize_t sendto(const std::string& buffer); | ||||||
|         ssize_t recvfrom(char* buffer, size_t length); |  | ||||||
|  |  | ||||||
|         void close(); |         void close(); | ||||||
|  |  | ||||||
|         static int getErrno(); |         static int getErrno(); | ||||||
|         static bool isWaitNeeded(); |  | ||||||
|         static void closeSocket(int fd); |         static void closeSocket(int fd); | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|   | |||||||
| @@ -1,29 +1,4 @@ | |||||||
| /* | /* | ||||||
|  * Lightweight URL & URI parser (RFC 1738, RFC 3986) |  | ||||||
|  * https://github.com/corporateshark/LUrlParser |  | ||||||
|  * |  | ||||||
|  * The MIT License (MIT) |  | ||||||
|  * |  | ||||||
|  * Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com) |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in all |  | ||||||
|  * copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |  | ||||||
|  * SOFTWARE. |  | ||||||
|  * |  | ||||||
|  *  IXUrlParser.cpp |  *  IXUrlParser.cpp | ||||||
|  *  Author: Benjamin Sergeant |  *  Author: Benjamin Sergeant | ||||||
|  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. |  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. | ||||||
| @@ -31,308 +6,7 @@ | |||||||
|  |  | ||||||
| #include "IXUrlParser.h" | #include "IXUrlParser.h" | ||||||
|  |  | ||||||
| #include <algorithm> | #include "LUrlParser.h" | ||||||
| #include <cstring> |  | ||||||
|  |  | ||||||
| namespace |  | ||||||
| { |  | ||||||
|     enum LUrlParserError |  | ||||||
|     { |  | ||||||
|         LUrlParserError_Ok = 0, |  | ||||||
|         LUrlParserError_Uninitialized = 1, |  | ||||||
|         LUrlParserError_NoUrlCharacter = 2, |  | ||||||
|         LUrlParserError_InvalidSchemeName = 3, |  | ||||||
|         LUrlParserError_NoDoubleSlash = 4, |  | ||||||
|         LUrlParserError_NoAtSign = 5, |  | ||||||
|         LUrlParserError_UnexpectedEndOfLine = 6, |  | ||||||
|         LUrlParserError_NoSlash = 7, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     class clParseURL |  | ||||||
|     { |  | ||||||
|     public: |  | ||||||
|         LUrlParserError m_ErrorCode; |  | ||||||
|         std::string m_Scheme; |  | ||||||
|         std::string m_Host; |  | ||||||
|         std::string m_Port; |  | ||||||
|         std::string m_Path; |  | ||||||
|         std::string m_Query; |  | ||||||
|         std::string m_Fragment; |  | ||||||
|         std::string m_UserName; |  | ||||||
|         std::string m_Password; |  | ||||||
|  |  | ||||||
|         clParseURL() |  | ||||||
|             : m_ErrorCode(LUrlParserError_Uninitialized) |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /// return 'true' if the parsing was successful |  | ||||||
|         bool IsValid() const |  | ||||||
|         { |  | ||||||
|             return m_ErrorCode == LUrlParserError_Ok; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /// helper to convert the port number to int, return 'true' if the port is valid (within the |  | ||||||
|         /// 0..65535 range) |  | ||||||
|         bool GetPort(int* OutPort) const; |  | ||||||
|  |  | ||||||
|         /// parse the URL |  | ||||||
|         static clParseURL ParseURL(const std::string& URL); |  | ||||||
|  |  | ||||||
|     private: |  | ||||||
|         explicit clParseURL(LUrlParserError ErrorCode) |  | ||||||
|             : m_ErrorCode(ErrorCode) |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     static bool IsSchemeValid(const std::string& SchemeName) |  | ||||||
|     { |  | ||||||
|         for (auto c : SchemeName) |  | ||||||
|         { |  | ||||||
|             if (!isalpha(c) && c != '+' && c != '-' && c != '.') return false; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     bool clParseURL::GetPort(int* OutPort) const |  | ||||||
|     { |  | ||||||
|         if (!IsValid()) |  | ||||||
|         { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         int Port = atoi(m_Port.c_str()); |  | ||||||
|  |  | ||||||
|         if (Port <= 0 || Port > 65535) |  | ||||||
|         { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (OutPort) |  | ||||||
|         { |  | ||||||
|             *OutPort = Port; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // based on RFC 1738 and RFC 3986 |  | ||||||
|     clParseURL clParseURL::ParseURL(const std::string& URL) |  | ||||||
|     { |  | ||||||
|         clParseURL Result; |  | ||||||
|  |  | ||||||
|         const char* CurrentString = URL.c_str(); |  | ||||||
|  |  | ||||||
|         /* |  | ||||||
|          *	<scheme>:<scheme-specific-part> |  | ||||||
|          *	<scheme> := [a-z\+\-\.]+ |  | ||||||
|          *	For resiliency, programs interpreting URLs should treat upper case letters as |  | ||||||
|          *equivalent to lower case in scheme names |  | ||||||
|          */ |  | ||||||
|  |  | ||||||
|         // try to read scheme |  | ||||||
|         { |  | ||||||
|             const char* LocalString = strchr(CurrentString, ':'); |  | ||||||
|  |  | ||||||
|             if (!LocalString) |  | ||||||
|             { |  | ||||||
|                 return clParseURL(LUrlParserError_NoUrlCharacter); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // save the scheme name |  | ||||||
|             Result.m_Scheme = std::string(CurrentString, LocalString - CurrentString); |  | ||||||
|  |  | ||||||
|             if (!IsSchemeValid(Result.m_Scheme)) |  | ||||||
|             { |  | ||||||
|                 return clParseURL(LUrlParserError_InvalidSchemeName); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // scheme should be lowercase |  | ||||||
|             std::transform( |  | ||||||
|                 Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower); |  | ||||||
|  |  | ||||||
|             // skip ':' |  | ||||||
|             CurrentString = LocalString + 1; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         /* |  | ||||||
|          *	//<user>:<password>@<host>:<port>/<url-path> |  | ||||||
|          *	any ":", "@" and "/" must be normalized |  | ||||||
|          */ |  | ||||||
|  |  | ||||||
|         // skip "//" |  | ||||||
|         if (*CurrentString++ != '/') return clParseURL(LUrlParserError_NoDoubleSlash); |  | ||||||
|         if (*CurrentString++ != '/') return clParseURL(LUrlParserError_NoDoubleSlash); |  | ||||||
|  |  | ||||||
|         // check if the user name and password are specified |  | ||||||
|         bool bHasUserName = false; |  | ||||||
|  |  | ||||||
|         const char* LocalString = CurrentString; |  | ||||||
|  |  | ||||||
|         while (*LocalString) |  | ||||||
|         { |  | ||||||
|             if (*LocalString == '@') |  | ||||||
|             { |  | ||||||
|                 // user name and password are specified |  | ||||||
|                 bHasUserName = true; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             else if (*LocalString == '/') |  | ||||||
|             { |  | ||||||
|                 // end of <host>:<port> specification |  | ||||||
|                 bHasUserName = false; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             LocalString++; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // user name and password |  | ||||||
|         LocalString = CurrentString; |  | ||||||
|  |  | ||||||
|         if (bHasUserName) |  | ||||||
|         { |  | ||||||
|             // read user name |  | ||||||
|             while (*LocalString && *LocalString != ':' && *LocalString != '@') |  | ||||||
|                 LocalString++; |  | ||||||
|  |  | ||||||
|             Result.m_UserName = std::string(CurrentString, LocalString - CurrentString); |  | ||||||
|  |  | ||||||
|             // proceed with the current pointer |  | ||||||
|             CurrentString = LocalString; |  | ||||||
|  |  | ||||||
|             if (*CurrentString == ':') |  | ||||||
|             { |  | ||||||
|                 // skip ':' |  | ||||||
|                 CurrentString++; |  | ||||||
|  |  | ||||||
|                 // read password |  | ||||||
|                 LocalString = CurrentString; |  | ||||||
|  |  | ||||||
|                 while (*LocalString && *LocalString != '@') |  | ||||||
|                     LocalString++; |  | ||||||
|  |  | ||||||
|                 Result.m_Password = std::string(CurrentString, LocalString - CurrentString); |  | ||||||
|  |  | ||||||
|                 CurrentString = LocalString; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // skip '@' |  | ||||||
|             if (*CurrentString != '@') |  | ||||||
|             { |  | ||||||
|                 return clParseURL(LUrlParserError_NoAtSign); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             CurrentString++; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         bool bHasBracket = (*CurrentString == '['); |  | ||||||
|  |  | ||||||
|         // go ahead, read the host name |  | ||||||
|         LocalString = CurrentString; |  | ||||||
|  |  | ||||||
|         while (*LocalString) |  | ||||||
|         { |  | ||||||
|             if (bHasBracket && *LocalString == ']') |  | ||||||
|             { |  | ||||||
|                 // end of IPv6 address |  | ||||||
|                 LocalString++; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             else if (!bHasBracket && (*LocalString == ':' || *LocalString == '/')) |  | ||||||
|             { |  | ||||||
|                 // port number is specified |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             LocalString++; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Result.m_Host = std::string(CurrentString, LocalString - CurrentString); |  | ||||||
|  |  | ||||||
|         CurrentString = LocalString; |  | ||||||
|  |  | ||||||
|         // is port number specified? |  | ||||||
|         if (*CurrentString == ':') |  | ||||||
|         { |  | ||||||
|             CurrentString++; |  | ||||||
|  |  | ||||||
|             // read port number |  | ||||||
|             LocalString = CurrentString; |  | ||||||
|  |  | ||||||
|             while (*LocalString && *LocalString != '/') |  | ||||||
|                 LocalString++; |  | ||||||
|  |  | ||||||
|             Result.m_Port = std::string(CurrentString, LocalString - CurrentString); |  | ||||||
|  |  | ||||||
|             CurrentString = LocalString; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // end of string |  | ||||||
|         if (!*CurrentString) |  | ||||||
|         { |  | ||||||
|             Result.m_ErrorCode = LUrlParserError_Ok; |  | ||||||
|  |  | ||||||
|             return Result; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // skip '/' |  | ||||||
|         if (*CurrentString != '/') |  | ||||||
|         { |  | ||||||
|             return clParseURL(LUrlParserError_NoSlash); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         CurrentString++; |  | ||||||
|  |  | ||||||
|         // parse the path |  | ||||||
|         LocalString = CurrentString; |  | ||||||
|  |  | ||||||
|         while (*LocalString && *LocalString != '#' && *LocalString != '?') |  | ||||||
|             LocalString++; |  | ||||||
|  |  | ||||||
|         Result.m_Path = std::string(CurrentString, LocalString - CurrentString); |  | ||||||
|  |  | ||||||
|         CurrentString = LocalString; |  | ||||||
|  |  | ||||||
|         // check for query |  | ||||||
|         if (*CurrentString == '?') |  | ||||||
|         { |  | ||||||
|             // skip '?' |  | ||||||
|             CurrentString++; |  | ||||||
|  |  | ||||||
|             // read query |  | ||||||
|             LocalString = CurrentString; |  | ||||||
|  |  | ||||||
|             while (*LocalString && *LocalString != '#') |  | ||||||
|                 LocalString++; |  | ||||||
|  |  | ||||||
|             Result.m_Query = std::string(CurrentString, LocalString - CurrentString); |  | ||||||
|  |  | ||||||
|             CurrentString = LocalString; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // check for fragment |  | ||||||
|         if (*CurrentString == '#') |  | ||||||
|         { |  | ||||||
|             // skip '#' |  | ||||||
|             CurrentString++; |  | ||||||
|  |  | ||||||
|             // read fragment |  | ||||||
|             LocalString = CurrentString; |  | ||||||
|  |  | ||||||
|             while (*LocalString) |  | ||||||
|                 LocalString++; |  | ||||||
|  |  | ||||||
|             Result.m_Fragment = std::string(CurrentString, LocalString - CurrentString); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Result.m_ErrorCode = LUrlParserError_Ok; |  | ||||||
|  |  | ||||||
|         return Result; |  | ||||||
|     } |  | ||||||
| } // namespace |  | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
| @@ -343,7 +17,7 @@ namespace ix | |||||||
|                           std::string& query, |                           std::string& query, | ||||||
|                           int& port) |                           int& port) | ||||||
|     { |     { | ||||||
|         clParseURL res = clParseURL::ParseURL(url); |         LUrlParser::clParseURL res = LUrlParser::clParseURL::ParseURL(url); | ||||||
|  |  | ||||||
|         if (!res.IsValid()) |         if (!res.IsValid()) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ namespace ix | |||||||
|         _ws.setOnCloseCallback( |         _ws.setOnCloseCallback( | ||||||
|             [this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) { |             [this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) { | ||||||
|                 _onMessageCallback( |                 _onMessageCallback( | ||||||
|                     std::make_unique<WebSocketMessage>(WebSocketMessageType::Close, |                     std::make_shared<WebSocketMessage>(WebSocketMessageType::Close, | ||||||
|                                                        "", |                                                        "", | ||||||
|                                                        wireSize, |                                                        wireSize, | ||||||
|                                                        WebSocketErrorInfo(), |                                                        WebSocketErrorInfo(), | ||||||
| @@ -193,7 +193,7 @@ namespace ix | |||||||
|             return status; |             return status; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         _onMessageCallback(std::make_unique<WebSocketMessage>( |         _onMessageCallback(std::make_shared<WebSocketMessage>( | ||||||
|             WebSocketMessageType::Open, |             WebSocketMessageType::Open, | ||||||
|             "", |             "", | ||||||
|             0, |             0, | ||||||
| @@ -225,7 +225,7 @@ namespace ix | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         _onMessageCallback( |         _onMessageCallback( | ||||||
|             std::make_unique<WebSocketMessage>(WebSocketMessageType::Open, |             std::make_shared<WebSocketMessage>(WebSocketMessageType::Open, | ||||||
|                                                "", |                                                "", | ||||||
|                                                0, |                                                0, | ||||||
|                                                WebSocketErrorInfo(), |                                                WebSocketErrorInfo(), | ||||||
| @@ -310,7 +310,7 @@ namespace ix | |||||||
|                 connectErr.reason = status.errorStr; |                 connectErr.reason = status.errorStr; | ||||||
|                 connectErr.http_status = status.http_status; |                 connectErr.http_status = status.http_status; | ||||||
|  |  | ||||||
|                 _onMessageCallback(std::make_unique<WebSocketMessage>(WebSocketMessageType::Error, |                 _onMessageCallback(std::make_shared<WebSocketMessage>(WebSocketMessageType::Error, | ||||||
|                                                                       "", |                                                                       "", | ||||||
|                                                                       0, |                                                                       0, | ||||||
|                                                                       connectErr, |                                                                       connectErr, | ||||||
| @@ -386,7 +386,7 @@ namespace ix | |||||||
|  |  | ||||||
|                     bool binary = messageKind == WebSocketTransport::MessageKind::MSG_BINARY; |                     bool binary = messageKind == WebSocketTransport::MessageKind::MSG_BINARY; | ||||||
|  |  | ||||||
|                     _onMessageCallback(std::make_unique<WebSocketMessage>(webSocketMessageType, |                     _onMessageCallback(std::make_shared<WebSocketMessage>(webSocketMessageType, | ||||||
|                                                                           msg, |                                                                           msg, | ||||||
|                                                                           wireSize, |                                                                           wireSize, | ||||||
|                                                                           webSocketErrorInfo, |                                                                           webSocketErrorInfo, | ||||||
|   | |||||||
| @@ -6,9 +6,6 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <cstdint> |  | ||||||
| #include <string> |  | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     struct WebSocketCloseInfo |     struct WebSocketCloseInfo | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <cstdint> |  | ||||||
| #include <string> | #include <string> | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ | |||||||
| #include "IXSocketConnect.h" | #include "IXSocketConnect.h" | ||||||
| #include "IXUrlParser.h" | #include "IXUrlParser.h" | ||||||
| #include "IXUserAgent.h" | #include "IXUserAgent.h" | ||||||
| #include "IXWebSocketHandshakeKeyGen.h" | #include "libwshandshake.hpp" | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <random> | #include <random> | ||||||
| @@ -133,7 +133,7 @@ namespace ix | |||||||
|  |  | ||||||
|         for (auto& it : extraHeaders) |         for (auto& it : extraHeaders) | ||||||
|         { |         { | ||||||
|             ss << it.first << ": " << it.second << "\r\n"; |             ss << it.first << ":" << it.second << "\r\n"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (_enablePerMessageDeflate) |         if (_enablePerMessageDeflate) | ||||||
|   | |||||||
| @@ -1,171 +0,0 @@ | |||||||
| // Copyright (c) 2016 Alex Hultman and contributors |  | ||||||
|  |  | ||||||
| // This software is provided 'as-is', without any express or implied |  | ||||||
| // warranty. In no event will the authors be held liable for any damages |  | ||||||
| // arising from the use of this software. |  | ||||||
|  |  | ||||||
| // Permission is granted to anyone to use this software for any purpose, |  | ||||||
| // including commercial applications, and to alter it and redistribute it |  | ||||||
| // freely, subject to the following restrictions: |  | ||||||
|  |  | ||||||
| // 1. The origin of this software must not be misrepresented; you must not |  | ||||||
| //    claim that you wrote the original software. If you use this software |  | ||||||
| //    in a product, an acknowledgement in the product documentation would be |  | ||||||
| //    appreciated but is not required. |  | ||||||
| // 2. Altered source versions must be plainly marked as such, and must not be |  | ||||||
| //    misrepresented as being the original software. |  | ||||||
| // 3. This notice may not be removed or altered from any source distribution. |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <cstddef> |  | ||||||
| #include <cstdint> |  | ||||||
| #include <string.h> |  | ||||||
| #include <string> |  | ||||||
|  |  | ||||||
| class WebSocketHandshakeKeyGen |  | ||||||
| { |  | ||||||
|     template<int N, typename T> |  | ||||||
|     struct static_for |  | ||||||
|     { |  | ||||||
|         void operator()(uint32_t* a, uint32_t* b) |  | ||||||
|         { |  | ||||||
|             static_for<N - 1, T>()(a, b); |  | ||||||
|             T::template f<N - 1>(a, b); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     template<typename T> |  | ||||||
|     struct static_for<0, T> |  | ||||||
|     { |  | ||||||
|         void operator()(uint32_t* /*a*/, uint32_t* /*hash*/) |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     template<int state> |  | ||||||
|     struct Sha1Loop |  | ||||||
|     { |  | ||||||
|         static inline uint32_t rol(uint32_t value, size_t bits) |  | ||||||
|         { |  | ||||||
|             return (value << bits) | (value >> (32 - bits)); |  | ||||||
|         } |  | ||||||
|         static inline uint32_t blk(uint32_t b[16], size_t i) |  | ||||||
|         { |  | ||||||
|             return rol(b[(i + 13) & 15] ^ b[(i + 8) & 15] ^ b[(i + 2) & 15] ^ b[i], 1); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         template<int i> |  | ||||||
|         static inline void f(uint32_t* a, uint32_t* b) |  | ||||||
|         { |  | ||||||
|             switch (state) |  | ||||||
|             { |  | ||||||
|                 case 1: |  | ||||||
|                     a[i % 5] += |  | ||||||
|                         ((a[(3 + i) % 5] & (a[(2 + i) % 5] ^ a[(1 + i) % 5])) ^ a[(1 + i) % 5]) + |  | ||||||
|                         b[i] + 0x5a827999 + rol(a[(4 + i) % 5], 5); |  | ||||||
|                     a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); |  | ||||||
|                     break; |  | ||||||
|                 case 2: |  | ||||||
|                     b[i] = blk(b, i); |  | ||||||
|                     a[(1 + i) % 5] += |  | ||||||
|                         ((a[(4 + i) % 5] & (a[(3 + i) % 5] ^ a[(2 + i) % 5])) ^ a[(2 + i) % 5]) + |  | ||||||
|                         b[i] + 0x5a827999 + rol(a[(5 + i) % 5], 5); |  | ||||||
|                     a[(4 + i) % 5] = rol(a[(4 + i) % 5], 30); |  | ||||||
|                     break; |  | ||||||
|                 case 3: |  | ||||||
|                     b[(i + 4) % 16] = blk(b, (i + 4) % 16); |  | ||||||
|                     a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + |  | ||||||
|                                 b[(i + 4) % 16] + 0x6ed9eba1 + rol(a[(4 + i) % 5], 5); |  | ||||||
|                     a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); |  | ||||||
|                     break; |  | ||||||
|                 case 4: |  | ||||||
|                     b[(i + 8) % 16] = blk(b, (i + 8) % 16); |  | ||||||
|                     a[i % 5] += (((a[(3 + i) % 5] | a[(2 + i) % 5]) & a[(1 + i) % 5]) | |  | ||||||
|                                  (a[(3 + i) % 5] & a[(2 + i) % 5])) + |  | ||||||
|                                 b[(i + 8) % 16] + 0x8f1bbcdc + rol(a[(4 + i) % 5], 5); |  | ||||||
|                     a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); |  | ||||||
|                     break; |  | ||||||
|                 case 5: |  | ||||||
|                     b[(i + 12) % 16] = blk(b, (i + 12) % 16); |  | ||||||
|                     a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + |  | ||||||
|                                 b[(i + 12) % 16] + 0xca62c1d6 + rol(a[(4 + i) % 5], 5); |  | ||||||
|                     a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); |  | ||||||
|                     break; |  | ||||||
|                 case 6: b[i] += a[4 - i]; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     static inline void sha1(uint32_t hash[5], uint32_t b[16]) |  | ||||||
|     { |  | ||||||
|         uint32_t a[5] = {hash[4], hash[3], hash[2], hash[1], hash[0]}; |  | ||||||
|         static_for<16, Sha1Loop<1>>()(a, b); |  | ||||||
|         static_for<4, Sha1Loop<2>>()(a, b); |  | ||||||
|         static_for<20, Sha1Loop<3>>()(a, b); |  | ||||||
|         static_for<20, Sha1Loop<4>>()(a, b); |  | ||||||
|         static_for<20, Sha1Loop<5>>()(a, b); |  | ||||||
|         static_for<5, Sha1Loop<6>>()(a, hash); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static inline void base64(unsigned char* src, char* dst) |  | ||||||
|     { |  | ||||||
|         const char* b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |  | ||||||
|         for (int i = 0; i < 18; i += 3) |  | ||||||
|         { |  | ||||||
|             *dst++ = b64[(src[i] >> 2) & 63]; |  | ||||||
|             *dst++ = b64[((src[i] & 3) << 4) | ((src[i + 1] & 240) >> 4)]; |  | ||||||
|             *dst++ = b64[((src[i + 1] & 15) << 2) | ((src[i + 2] & 192) >> 6)]; |  | ||||||
|             *dst++ = b64[src[i + 2] & 63]; |  | ||||||
|         } |  | ||||||
|         *dst++ = b64[(src[18] >> 2) & 63]; |  | ||||||
|         *dst++ = b64[((src[18] & 3) << 4) | ((src[19] & 240) >> 4)]; |  | ||||||
|         *dst++ = b64[((src[19] & 15) << 2)]; |  | ||||||
|         *dst++ = '='; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| public: |  | ||||||
|     static inline void generate(const std::string& inputStr, char output[28]) |  | ||||||
|     { |  | ||||||
|         char input[25] = {}; |  | ||||||
|         strncpy(input, inputStr.c_str(), 25 - 1); |  | ||||||
|         input[25 - 1] = '\0'; |  | ||||||
|  |  | ||||||
|         uint32_t b_output[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0}; |  | ||||||
|         uint32_t b_input[16] = {0, |  | ||||||
|                                 0, |  | ||||||
|                                 0, |  | ||||||
|                                 0, |  | ||||||
|                                 0, |  | ||||||
|                                 0, |  | ||||||
|                                 0x32353845, |  | ||||||
|                                 0x41464135, |  | ||||||
|                                 0x2d453931, |  | ||||||
|                                 0x342d3437, |  | ||||||
|                                 0x44412d39, |  | ||||||
|                                 0x3543412d, |  | ||||||
|                                 0x43354142, |  | ||||||
|                                 0x30444338, |  | ||||||
|                                 0x35423131, |  | ||||||
|                                 0x80000000}; |  | ||||||
|  |  | ||||||
|         for (int i = 0; i < 6; i++) |  | ||||||
|         { |  | ||||||
|             b_input[i] = (input[4 * i + 3] & 0xff) | (input[4 * i + 2] & 0xff) << 8 | |  | ||||||
|                          (input[4 * i + 1] & 0xff) << 16 | (input[4 * i + 0] & 0xff) << 24; |  | ||||||
|         } |  | ||||||
|         sha1(b_output, b_input); |  | ||||||
|         uint32_t last_b[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480}; |  | ||||||
|         sha1(b_output, last_b); |  | ||||||
|         for (int i = 0; i < 5; i++) |  | ||||||
|         { |  | ||||||
|             uint32_t tmp = b_output[i]; |  | ||||||
|             char* bytes = (char*) &b_output[i]; |  | ||||||
|             bytes[3] = tmp & 0xff; |  | ||||||
|             bytes[2] = (tmp >> 8) & 0xff; |  | ||||||
|             bytes[1] = (tmp >> 16) & 0xff; |  | ||||||
|             bytes[0] = (tmp >> 24) & 0xff; |  | ||||||
|         } |  | ||||||
|         base64((unsigned char*) b_output, output); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| @@ -19,7 +19,7 @@ namespace ix | |||||||
|     struct WebSocketMessage |     struct WebSocketMessage | ||||||
|     { |     { | ||||||
|         WebSocketMessageType type; |         WebSocketMessageType type; | ||||||
|         const std::string& str; |         std::string str; | ||||||
|         size_t wireSize; |         size_t wireSize; | ||||||
|         WebSocketErrorInfo errorInfo; |         WebSocketErrorInfo errorInfo; | ||||||
|         WebSocketOpenInfo openInfo; |         WebSocketOpenInfo openInfo; | ||||||
| @@ -34,7 +34,7 @@ namespace ix | |||||||
|                          WebSocketCloseInfo c, |                          WebSocketCloseInfo c, | ||||||
|                          bool b = false) |                          bool b = false) | ||||||
|             : type(t) |             : type(t) | ||||||
|             , str(s) |             , str(std::move(s)) | ||||||
|             , wireSize(w) |             , wireSize(w) | ||||||
|             , errorInfo(e) |             , errorInfo(e) | ||||||
|             , openInfo(o) |             , openInfo(o) | ||||||
| @@ -45,5 +45,5 @@ namespace ix | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     using WebSocketMessagePtr = std::unique_ptr<WebSocketMessage>; |     using WebSocketMessagePtr = std::shared_ptr<WebSocketMessage>; | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
							
								
								
									
										86
									
								
								ixwebsocket/IXWebSocketMessageQueue.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								ixwebsocket/IXWebSocketMessageQueue.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | /* | ||||||
|  |  *  IXWebSocketMessageQueue.cpp | ||||||
|  |  *  Author: Korchynskyi Dmytro | ||||||
|  |  *  Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "IXWebSocketMessageQueue.h" | ||||||
|  |  | ||||||
|  | namespace ix | ||||||
|  | { | ||||||
|  |     WebSocketMessageQueue::WebSocketMessageQueue(WebSocket* websocket) | ||||||
|  |     { | ||||||
|  |         bindWebsocket(websocket); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     WebSocketMessageQueue::~WebSocketMessageQueue() | ||||||
|  |     { | ||||||
|  |         if (!_messages.empty()) | ||||||
|  |         { | ||||||
|  |             // not handled all messages | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bindWebsocket(nullptr); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketMessageQueue::bindWebsocket(WebSocket* websocket) | ||||||
|  |     { | ||||||
|  |         if (_websocket == websocket) return; | ||||||
|  |  | ||||||
|  |         // unbind old | ||||||
|  |         if (_websocket) | ||||||
|  |         { | ||||||
|  |             // set dummy callback just to avoid crash | ||||||
|  |             _websocket->setOnMessageCallback([](const WebSocketMessagePtr&) {}); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         _websocket = websocket; | ||||||
|  |  | ||||||
|  |         // bind new | ||||||
|  |         if (_websocket) | ||||||
|  |         { | ||||||
|  |             _websocket->setOnMessageCallback([this](const WebSocketMessagePtr& msg) { | ||||||
|  |                 std::lock_guard<std::mutex> lock(_messagesMutex); | ||||||
|  |                 _messages.emplace_back(std::move(msg)); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketMessageQueue::setOnMessageCallback(const OnMessageCallback& callback) | ||||||
|  |     { | ||||||
|  |         _onMessageUserCallback = callback; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketMessageQueue::setOnMessageCallback(OnMessageCallback&& callback) | ||||||
|  |     { | ||||||
|  |         _onMessageUserCallback = std::move(callback); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     WebSocketMessagePtr WebSocketMessageQueue::popMessage() | ||||||
|  |     { | ||||||
|  |         WebSocketMessagePtr message; | ||||||
|  |         std::lock_guard<std::mutex> lock(_messagesMutex); | ||||||
|  |  | ||||||
|  |         if (!_messages.empty()) | ||||||
|  |         { | ||||||
|  |             message = std::move(_messages.front()); | ||||||
|  |             _messages.pop_front(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return message; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void WebSocketMessageQueue::poll(int count) | ||||||
|  |     { | ||||||
|  |         if (!_onMessageUserCallback) return; | ||||||
|  |  | ||||||
|  |         WebSocketMessagePtr message; | ||||||
|  |  | ||||||
|  |         while (count > 0 && (message = popMessage())) | ||||||
|  |         { | ||||||
|  |             _onMessageUserCallback(message); | ||||||
|  |             --count; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } // namespace ix | ||||||
							
								
								
									
										41
									
								
								ixwebsocket/IXWebSocketMessageQueue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								ixwebsocket/IXWebSocketMessageQueue.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | /* | ||||||
|  |  *  IXWebSocketMessageQueue.h | ||||||
|  |  *  Author: Korchynskyi Dmytro | ||||||
|  |  *  Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "IXWebSocket.h" | ||||||
|  | #include <list> | ||||||
|  | #include <memory> | ||||||
|  | #include <thread> | ||||||
|  |  | ||||||
|  | namespace ix | ||||||
|  | { | ||||||
|  |     // | ||||||
|  |     // A helper class to dispatch websocket message callbacks in your thread. | ||||||
|  |     // | ||||||
|  |     class WebSocketMessageQueue | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         WebSocketMessageQueue(WebSocket* websocket = nullptr); | ||||||
|  |         ~WebSocketMessageQueue(); | ||||||
|  |  | ||||||
|  |         void bindWebsocket(WebSocket* websocket); | ||||||
|  |  | ||||||
|  |         void setOnMessageCallback(const OnMessageCallback& callback); | ||||||
|  |         void setOnMessageCallback(OnMessageCallback&& callback); | ||||||
|  |  | ||||||
|  |         void poll(int count = 512); | ||||||
|  |  | ||||||
|  |     protected: | ||||||
|  |         WebSocketMessagePtr popMessage(); | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         WebSocket* _websocket = nullptr; | ||||||
|  |         OnMessageCallback _onMessageUserCallback; | ||||||
|  |         std::mutex _messagesMutex; | ||||||
|  |         std::list<WebSocketMessagePtr> _messages; | ||||||
|  |     }; | ||||||
|  | } // namespace ix | ||||||
| @@ -6,10 +6,6 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "IXWebSocketHttpHeaders.h" |  | ||||||
| #include <cstdint> |  | ||||||
| #include <string> |  | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     struct WebSocketOpenInfo |     struct WebSocketOpenInfo | ||||||
|   | |||||||
| @@ -87,9 +87,6 @@ namespace ix | |||||||
|         // |         // | ||||||
|         size_t output; |         size_t output; | ||||||
|  |  | ||||||
|         // Clear output |  | ||||||
|         out.clear(); |  | ||||||
|  |  | ||||||
|         if (in.empty()) |         if (in.empty()) | ||||||
|         { |         { | ||||||
|             // See issue #167 |             // See issue #167 | ||||||
| @@ -177,9 +174,6 @@ namespace ix | |||||||
|         _inflateState.avail_in = (uInt) inFixed.size(); |         _inflateState.avail_in = (uInt) inFixed.size(); | ||||||
|         _inflateState.next_in = (unsigned char*) (const_cast<char*>(inFixed.data())); |         _inflateState.next_in = (unsigned char*) (const_cast<char*>(inFixed.data())); | ||||||
|  |  | ||||||
|         // Clear output |  | ||||||
|         out.clear(); |  | ||||||
|  |  | ||||||
|         do |         do | ||||||
|         { |         { | ||||||
|             _inflateState.avail_out = (uInt) _compressBufferSize; |             _inflateState.avail_out = (uInt) _compressBufferSize; | ||||||
|   | |||||||
| @@ -62,7 +62,7 @@ namespace ix | |||||||
|     WebSocketTransport::WebSocketTransport() |     WebSocketTransport::WebSocketTransport() | ||||||
|         : _useMask(true) |         : _useMask(true) | ||||||
|         , _blockingSend(false) |         , _blockingSend(false) | ||||||
|         , _receivedMessageCompressed(false) |         , _compressedMessage(false) | ||||||
|         , _readyState(ReadyState::CLOSED) |         , _readyState(ReadyState::CLOSED) | ||||||
|         , _closeCode(WebSocketCloseConstants::kInternalErrorCode) |         , _closeCode(WebSocketCloseConstants::kInternalErrorCode) | ||||||
|         , _closeReason(WebSocketCloseConstants::kInternalErrorMessage) |         , _closeReason(WebSocketCloseConstants::kInternalErrorMessage) | ||||||
| @@ -74,7 +74,6 @@ namespace ix | |||||||
|         , _enablePong(kDefaultEnablePong) |         , _enablePong(kDefaultEnablePong) | ||||||
|         , _pingIntervalSecs(kDefaultPingIntervalSecs) |         , _pingIntervalSecs(kDefaultPingIntervalSecs) | ||||||
|         , _pongReceived(false) |         , _pongReceived(false) | ||||||
|         , _pingCount(0) |  | ||||||
|         , _lastSendPingTimePoint(std::chrono::steady_clock::now()) |         , _lastSendPingTimePoint(std::chrono::steady_clock::now()) | ||||||
|     { |     { | ||||||
|         _readbuf.resize(kChunkSize); |         _readbuf.resize(kChunkSize); | ||||||
| @@ -222,8 +221,7 @@ namespace ix | |||||||
|     { |     { | ||||||
|         _pongReceived = false; |         _pongReceived = false; | ||||||
|         std::stringstream ss; |         std::stringstream ss; | ||||||
|         ss << kPingMessage << "::" << _pingIntervalSecs << "s" |         ss << kPingMessage << "::" << _pingIntervalSecs << "s"; | ||||||
|            << "::" << _pingCount++; |  | ||||||
|         return sendPing(ss.str()); |         return sendPing(ss.str()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -495,7 +493,7 @@ namespace ix | |||||||
|                                                  ? MessageKind::MSG_TEXT |                                                  ? MessageKind::MSG_TEXT | ||||||
|                                                  : MessageKind::MSG_BINARY; |                                                  : MessageKind::MSG_BINARY; | ||||||
|  |  | ||||||
|                     _receivedMessageCompressed = _enablePerMessageDeflate && ws.rsv1; |                     _compressedMessage = _enablePerMessageDeflate && ws.rsv1; | ||||||
|  |  | ||||||
|                     // Continuation message needs to follow a non-fin TEXT or BINARY message |                     // Continuation message needs to follow a non-fin TEXT or BINARY message | ||||||
|                     if (!_chunks.empty()) |                     if (!_chunks.empty()) | ||||||
| @@ -517,12 +515,10 @@ namespace ix | |||||||
|                 // |                 // | ||||||
|                 if (ws.fin && _chunks.empty()) |                 if (ws.fin && _chunks.empty()) | ||||||
|                 { |                 { | ||||||
|                     emitMessage(_fragmentedMessageKind, |                     emitMessage( | ||||||
|                                 frameData, |                         _fragmentedMessageKind, frameData, _compressedMessage, onMessageCallback); | ||||||
|                                 _receivedMessageCompressed, |  | ||||||
|                                 onMessageCallback); |  | ||||||
|  |  | ||||||
|                     _receivedMessageCompressed = false; |                     _compressedMessage = false; | ||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
| @@ -539,11 +535,11 @@ namespace ix | |||||||
|                     { |                     { | ||||||
|                         emitMessage(_fragmentedMessageKind, |                         emitMessage(_fragmentedMessageKind, | ||||||
|                                     getMergedChunks(), |                                     getMergedChunks(), | ||||||
|                                     _receivedMessageCompressed, |                                     _compressedMessage, | ||||||
|                                     onMessageCallback); |                                     onMessageCallback); | ||||||
|  |  | ||||||
|                         _chunks.clear(); |                         _chunks.clear(); | ||||||
|                         _receivedMessageCompressed = false; |                         _compressedMessage = false; | ||||||
|                     } |                     } | ||||||
|                     else |                     else | ||||||
|                     { |                     { | ||||||
| @@ -716,16 +712,17 @@ namespace ix | |||||||
|         // When the RSV1 bit is 1 it means the message is compressed |         // When the RSV1 bit is 1 it means the message is compressed | ||||||
|         if (compressedMessage && messageKind != MessageKind::FRAGMENT) |         if (compressedMessage && messageKind != MessageKind::FRAGMENT) | ||||||
|         { |         { | ||||||
|             bool success = _perMessageDeflate->decompress(message, _decompressedMessage); |             std::string decompressedMessage; | ||||||
|  |             bool success = _perMessageDeflate->decompress(message, decompressedMessage); | ||||||
|  |  | ||||||
|             if (messageKind == MessageKind::MSG_TEXT && !validateUtf8(_decompressedMessage)) |             if (messageKind == MessageKind::MSG_TEXT && !validateUtf8(decompressedMessage)) | ||||||
|             { |             { | ||||||
|                 close(WebSocketCloseConstants::kInvalidFramePayloadData, |                 close(WebSocketCloseConstants::kInvalidFramePayloadData, | ||||||
|                       WebSocketCloseConstants::kInvalidFramePayloadDataMessage); |                       WebSocketCloseConstants::kInvalidFramePayloadDataMessage); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
|                 onMessageCallback(_decompressedMessage, wireSize, !success, messageKind); |                 onMessageCallback(decompressedMessage, wireSize, !success, messageKind); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
| @@ -762,6 +759,7 @@ namespace ix | |||||||
|  |  | ||||||
|         size_t payloadSize = message.size(); |         size_t payloadSize = message.size(); | ||||||
|         size_t wireSize = message.size(); |         size_t wireSize = message.size(); | ||||||
|  |         std::string compressedMessage; | ||||||
|         bool compressionError = false; |         bool compressionError = false; | ||||||
|  |  | ||||||
|         std::string::const_iterator message_begin = message.begin(); |         std::string::const_iterator message_begin = message.begin(); | ||||||
| @@ -769,7 +767,7 @@ namespace ix | |||||||
|  |  | ||||||
|         if (compress) |         if (compress) | ||||||
|         { |         { | ||||||
|             if (!_perMessageDeflate->compress(message, _compressedMessage)) |             if (!_perMessageDeflate->compress(message, compressedMessage)) | ||||||
|             { |             { | ||||||
|                 bool success = false; |                 bool success = false; | ||||||
|                 compressionError = true; |                 compressionError = true; | ||||||
| @@ -778,10 +776,10 @@ namespace ix | |||||||
|                 return WebSocketSendInfo(success, compressionError, payloadSize, wireSize); |                 return WebSocketSendInfo(success, compressionError, payloadSize, wireSize); | ||||||
|             } |             } | ||||||
|             compressionError = false; |             compressionError = false; | ||||||
|             wireSize = _compressedMessage.size(); |             wireSize = compressedMessage.size(); | ||||||
|  |  | ||||||
|             message_begin = _compressedMessage.begin(); |             message_begin = compressedMessage.begin(); | ||||||
|             message_end = _compressedMessage.end(); |             message_end = compressedMessage.end(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -165,7 +165,7 @@ namespace ix | |||||||
|         MessageKind _fragmentedMessageKind; |         MessageKind _fragmentedMessageKind; | ||||||
|  |  | ||||||
|         // Ditto for whether a message is compressed |         // Ditto for whether a message is compressed | ||||||
|         bool _receivedMessageCompressed; |         bool _compressedMessage; | ||||||
|  |  | ||||||
|         // Fragments are 32K long |         // Fragments are 32K long | ||||||
|         static constexpr size_t kChunkSize = 1 << 15; |         static constexpr size_t kChunkSize = 1 << 15; | ||||||
| @@ -189,9 +189,6 @@ namespace ix | |||||||
|         WebSocketPerMessageDeflateOptions _perMessageDeflateOptions; |         WebSocketPerMessageDeflateOptions _perMessageDeflateOptions; | ||||||
|         std::atomic<bool> _enablePerMessageDeflate; |         std::atomic<bool> _enablePerMessageDeflate; | ||||||
|  |  | ||||||
|         std::string _decompressedMessage; |  | ||||||
|         std::string _compressedMessage; |  | ||||||
|  |  | ||||||
|         // Used to control TLS connection behavior |         // Used to control TLS connection behavior | ||||||
|         SocketTLSOptions _socketTLSOptions; |         SocketTLSOptions _socketTLSOptions; | ||||||
|  |  | ||||||
| @@ -212,7 +209,6 @@ namespace ix | |||||||
|  |  | ||||||
|         static const int kDefaultPingIntervalSecs; |         static const int kDefaultPingIntervalSecs; | ||||||
|         static const std::string kPingMessage; |         static const std::string kPingMessage; | ||||||
|         std::atomic<uint64_t> _pingCount; |  | ||||||
|  |  | ||||||
|         // We record when ping are being sent so that we can know when to send the next one |         // We record when ping are being sent so that we can know when to send the next one | ||||||
|         mutable std::mutex _lastSendPingTimePointMutex; |         mutable std::mutex _lastSendPingTimePointMutex; | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #define IX_WEBSOCKET_VERSION "9.6.2" | #define IX_WEBSOCKET_VERSION "9.2.0" | ||||||
|   | |||||||
							
								
								
									
										280
									
								
								ixwebsocket/LUrlParser.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								ixwebsocket/LUrlParser.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | |||||||
|  | /* | ||||||
|  |  * Lightweight URL & URI parser (RFC 1738, RFC 3986) | ||||||
|  |  * https://github.com/corporateshark/LUrlParser | ||||||
|  |  * | ||||||
|  |  * The MIT License (MIT) | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com) | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in all | ||||||
|  |  * copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  |  * SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "LUrlParser.h" | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cstring> | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
|  | // check if the scheme name is valid | ||||||
|  | static bool IsSchemeValid(const std::string& SchemeName) | ||||||
|  | { | ||||||
|  |     for (auto c : SchemeName) | ||||||
|  |     { | ||||||
|  |         if (!isalpha(c) && c != '+' && c != '-' && c != '.') return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool LUrlParser::clParseURL::GetPort(int* OutPort) const | ||||||
|  | { | ||||||
|  |     if (!IsValid()) | ||||||
|  |     { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int Port = atoi(m_Port.c_str()); | ||||||
|  |  | ||||||
|  |     if (Port <= 0 || Port > 65535) | ||||||
|  |     { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (OutPort) | ||||||
|  |     { | ||||||
|  |         *OutPort = Port; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // based on RFC 1738 and RFC 3986 | ||||||
|  | LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL(const std::string& URL) | ||||||
|  | { | ||||||
|  |     LUrlParser::clParseURL Result; | ||||||
|  |  | ||||||
|  |     const char* CurrentString = URL.c_str(); | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |      *	<scheme>:<scheme-specific-part> | ||||||
|  |      *	<scheme> := [a-z\+\-\.]+ | ||||||
|  |      *	For resiliency, programs interpreting URLs should treat upper case letters as equivalent to | ||||||
|  |      *lower case in scheme names | ||||||
|  |      */ | ||||||
|  |  | ||||||
|  |     // try to read scheme | ||||||
|  |     { | ||||||
|  |         const char* LocalString = strchr(CurrentString, ':'); | ||||||
|  |  | ||||||
|  |         if (!LocalString) | ||||||
|  |         { | ||||||
|  |             return clParseURL(LUrlParserError_NoUrlCharacter); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // save the scheme name | ||||||
|  |         Result.m_Scheme = std::string(CurrentString, LocalString - CurrentString); | ||||||
|  |  | ||||||
|  |         if (!IsSchemeValid(Result.m_Scheme)) | ||||||
|  |         { | ||||||
|  |             return clParseURL(LUrlParserError_InvalidSchemeName); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // scheme should be lowercase | ||||||
|  |         std::transform( | ||||||
|  |             Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower); | ||||||
|  |  | ||||||
|  |         // skip ':' | ||||||
|  |         CurrentString = LocalString + 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |      *	//<user>:<password>@<host>:<port>/<url-path> | ||||||
|  |      *	any ":", "@" and "/" must be normalized | ||||||
|  |      */ | ||||||
|  |  | ||||||
|  |     // skip "//" | ||||||
|  |     if (*CurrentString++ != '/') return clParseURL(LUrlParserError_NoDoubleSlash); | ||||||
|  |     if (*CurrentString++ != '/') return clParseURL(LUrlParserError_NoDoubleSlash); | ||||||
|  |  | ||||||
|  |     // check if the user name and password are specified | ||||||
|  |     bool bHasUserName = false; | ||||||
|  |  | ||||||
|  |     const char* LocalString = CurrentString; | ||||||
|  |  | ||||||
|  |     while (*LocalString) | ||||||
|  |     { | ||||||
|  |         if (*LocalString == '@') | ||||||
|  |         { | ||||||
|  |             // user name and password are specified | ||||||
|  |             bHasUserName = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         else if (*LocalString == '/') | ||||||
|  |         { | ||||||
|  |             // end of <host>:<port> specification | ||||||
|  |             bHasUserName = false; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         LocalString++; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // user name and password | ||||||
|  |     LocalString = CurrentString; | ||||||
|  |  | ||||||
|  |     if (bHasUserName) | ||||||
|  |     { | ||||||
|  |         // read user name | ||||||
|  |         while (*LocalString && *LocalString != ':' && *LocalString != '@') | ||||||
|  |             LocalString++; | ||||||
|  |  | ||||||
|  |         Result.m_UserName = std::string(CurrentString, LocalString - CurrentString); | ||||||
|  |  | ||||||
|  |         // proceed with the current pointer | ||||||
|  |         CurrentString = LocalString; | ||||||
|  |  | ||||||
|  |         if (*CurrentString == ':') | ||||||
|  |         { | ||||||
|  |             // skip ':' | ||||||
|  |             CurrentString++; | ||||||
|  |  | ||||||
|  |             // read password | ||||||
|  |             LocalString = CurrentString; | ||||||
|  |  | ||||||
|  |             while (*LocalString && *LocalString != '@') | ||||||
|  |                 LocalString++; | ||||||
|  |  | ||||||
|  |             Result.m_Password = std::string(CurrentString, LocalString - CurrentString); | ||||||
|  |  | ||||||
|  |             CurrentString = LocalString; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // skip '@' | ||||||
|  |         if (*CurrentString != '@') | ||||||
|  |         { | ||||||
|  |             return clParseURL(LUrlParserError_NoAtSign); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         CurrentString++; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool bHasBracket = (*CurrentString == '['); | ||||||
|  |  | ||||||
|  |     // go ahead, read the host name | ||||||
|  |     LocalString = CurrentString; | ||||||
|  |  | ||||||
|  |     while (*LocalString) | ||||||
|  |     { | ||||||
|  |         if (bHasBracket && *LocalString == ']') | ||||||
|  |         { | ||||||
|  |             // end of IPv6 address | ||||||
|  |             LocalString++; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         else if (!bHasBracket && (*LocalString == ':' || *LocalString == '/')) | ||||||
|  |         { | ||||||
|  |             // port number is specified | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         LocalString++; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Result.m_Host = std::string(CurrentString, LocalString - CurrentString); | ||||||
|  |  | ||||||
|  |     CurrentString = LocalString; | ||||||
|  |  | ||||||
|  |     // is port number specified? | ||||||
|  |     if (*CurrentString == ':') | ||||||
|  |     { | ||||||
|  |         CurrentString++; | ||||||
|  |  | ||||||
|  |         // read port number | ||||||
|  |         LocalString = CurrentString; | ||||||
|  |  | ||||||
|  |         while (*LocalString && *LocalString != '/') | ||||||
|  |             LocalString++; | ||||||
|  |  | ||||||
|  |         Result.m_Port = std::string(CurrentString, LocalString - CurrentString); | ||||||
|  |  | ||||||
|  |         CurrentString = LocalString; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // end of string | ||||||
|  |     if (!*CurrentString) | ||||||
|  |     { | ||||||
|  |         Result.m_ErrorCode = LUrlParserError_Ok; | ||||||
|  |  | ||||||
|  |         return Result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // skip '/' | ||||||
|  |     if (*CurrentString != '/') | ||||||
|  |     { | ||||||
|  |         return clParseURL(LUrlParserError_NoSlash); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     CurrentString++; | ||||||
|  |  | ||||||
|  |     // parse the path | ||||||
|  |     LocalString = CurrentString; | ||||||
|  |  | ||||||
|  |     while (*LocalString && *LocalString != '#' && *LocalString != '?') | ||||||
|  |         LocalString++; | ||||||
|  |  | ||||||
|  |     Result.m_Path = std::string(CurrentString, LocalString - CurrentString); | ||||||
|  |  | ||||||
|  |     CurrentString = LocalString; | ||||||
|  |  | ||||||
|  |     // check for query | ||||||
|  |     if (*CurrentString == '?') | ||||||
|  |     { | ||||||
|  |         // skip '?' | ||||||
|  |         CurrentString++; | ||||||
|  |  | ||||||
|  |         // read query | ||||||
|  |         LocalString = CurrentString; | ||||||
|  |  | ||||||
|  |         while (*LocalString && *LocalString != '#') | ||||||
|  |             LocalString++; | ||||||
|  |  | ||||||
|  |         Result.m_Query = std::string(CurrentString, LocalString - CurrentString); | ||||||
|  |  | ||||||
|  |         CurrentString = LocalString; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // check for fragment | ||||||
|  |     if (*CurrentString == '#') | ||||||
|  |     { | ||||||
|  |         // skip '#' | ||||||
|  |         CurrentString++; | ||||||
|  |  | ||||||
|  |         // read fragment | ||||||
|  |         LocalString = CurrentString; | ||||||
|  |  | ||||||
|  |         while (*LocalString) | ||||||
|  |             LocalString++; | ||||||
|  |  | ||||||
|  |         Result.m_Fragment = std::string(CurrentString, LocalString - CurrentString); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Result.m_ErrorCode = LUrlParserError_Ok; | ||||||
|  |  | ||||||
|  |     return Result; | ||||||
|  | } | ||||||
							
								
								
									
										84
									
								
								ixwebsocket/LUrlParser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								ixwebsocket/LUrlParser.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | /* | ||||||
|  |  * Lightweight URL & URI parser (RFC 1738, RFC 3986) | ||||||
|  |  * https://github.com/corporateshark/LUrlParser | ||||||
|  |  * | ||||||
|  |  * The MIT License (MIT) | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com) | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in all | ||||||
|  |  * copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  |  * SOFTWARE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | namespace LUrlParser | ||||||
|  | { | ||||||
|  |     enum LUrlParserError | ||||||
|  |     { | ||||||
|  |         LUrlParserError_Ok = 0, | ||||||
|  |         LUrlParserError_Uninitialized = 1, | ||||||
|  |         LUrlParserError_NoUrlCharacter = 2, | ||||||
|  |         LUrlParserError_InvalidSchemeName = 3, | ||||||
|  |         LUrlParserError_NoDoubleSlash = 4, | ||||||
|  |         LUrlParserError_NoAtSign = 5, | ||||||
|  |         LUrlParserError_UnexpectedEndOfLine = 6, | ||||||
|  |         LUrlParserError_NoSlash = 7, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     class clParseURL | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         LUrlParserError m_ErrorCode; | ||||||
|  |         std::string m_Scheme; | ||||||
|  |         std::string m_Host; | ||||||
|  |         std::string m_Port; | ||||||
|  |         std::string m_Path; | ||||||
|  |         std::string m_Query; | ||||||
|  |         std::string m_Fragment; | ||||||
|  |         std::string m_UserName; | ||||||
|  |         std::string m_Password; | ||||||
|  |  | ||||||
|  |         clParseURL() | ||||||
|  |             : m_ErrorCode(LUrlParserError_Uninitialized) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// return 'true' if the parsing was successful | ||||||
|  |         bool IsValid() const | ||||||
|  |         { | ||||||
|  |             return m_ErrorCode == LUrlParserError_Ok; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// helper to convert the port number to int, return 'true' if the port is valid (within the | ||||||
|  |         /// 0..65535 range) | ||||||
|  |         bool GetPort(int* OutPort) const; | ||||||
|  |  | ||||||
|  |         /// parse the URL | ||||||
|  |         static clParseURL ParseURL(const std::string& URL); | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         explicit clParseURL(LUrlParserError ErrorCode) | ||||||
|  |             : m_ErrorCode(ErrorCode) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  | } // namespace LUrlParser | ||||||
							
								
								
									
										135
									
								
								ixwebsocket/libwshandshake.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								ixwebsocket/libwshandshake.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | |||||||
|  | // Copyright (c) 2016 Alex Hultman and contributors | ||||||
|  |  | ||||||
|  | // This software is provided 'as-is', without any express or implied | ||||||
|  | // warranty. In no event will the authors be held liable for any damages | ||||||
|  | // arising from the use of this software. | ||||||
|  |  | ||||||
|  | // Permission is granted to anyone to use this software for any purpose, | ||||||
|  | // including commercial applications, and to alter it and redistribute it | ||||||
|  | // freely, subject to the following restrictions: | ||||||
|  |  | ||||||
|  | // 1. The origin of this software must not be misrepresented; you must not | ||||||
|  | //    claim that you wrote the original software. If you use this software | ||||||
|  | //    in a product, an acknowledgement in the product documentation would be | ||||||
|  | //    appreciated but is not required. | ||||||
|  | // 2. Altered source versions must be plainly marked as such, and must not be | ||||||
|  | //    misrepresented as being the original software. | ||||||
|  | // 3. This notice may not be removed or altered from any source distribution. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
|  | #include <cstddef> | ||||||
|  | #include <string> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | class WebSocketHandshakeKeyGen { | ||||||
|  |     template <int N, typename T> | ||||||
|  |     struct static_for { | ||||||
|  |         void operator()(uint32_t *a, uint32_t *b) { | ||||||
|  |             static_for<N - 1, T>()(a, b); | ||||||
|  |             T::template f<N - 1>(a, b); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     template <typename T> | ||||||
|  |     struct static_for<0, T> { | ||||||
|  |         void operator()(uint32_t * /*a*/, uint32_t * /*hash*/) {} | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     template <int state> | ||||||
|  |     struct Sha1Loop { | ||||||
|  |         static inline uint32_t rol(uint32_t value, size_t bits) {return (value << bits) | (value >> (32 - bits));} | ||||||
|  |         static inline uint32_t blk(uint32_t b[16], size_t i) { | ||||||
|  |             return rol(b[(i + 13) & 15] ^ b[(i + 8) & 15] ^ b[(i + 2) & 15] ^ b[i], 1); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         template <int i> | ||||||
|  |         static inline void f(uint32_t *a, uint32_t *b) { | ||||||
|  |             switch (state) { | ||||||
|  |             case 1: | ||||||
|  |                 a[i % 5] += ((a[(3 + i) % 5] & (a[(2 + i) % 5] ^ a[(1 + i) % 5])) ^ a[(1 + i) % 5]) + b[i] + 0x5a827999 + rol(a[(4 + i) % 5], 5); | ||||||
|  |                 a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); | ||||||
|  |                 break; | ||||||
|  |             case 2: | ||||||
|  |                 b[i] = blk(b, i); | ||||||
|  |                 a[(1 + i) % 5] += ((a[(4 + i) % 5] & (a[(3 + i) % 5] ^ a[(2 + i) % 5])) ^ a[(2 + i) % 5]) + b[i] + 0x5a827999 + rol(a[(5 + i) % 5], 5); | ||||||
|  |                 a[(4 + i) % 5] = rol(a[(4 + i) % 5], 30); | ||||||
|  |                 break; | ||||||
|  |             case 3: | ||||||
|  |                 b[(i + 4) % 16] = blk(b, (i + 4) % 16); | ||||||
|  |                 a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + b[(i + 4) % 16] + 0x6ed9eba1 + rol(a[(4 + i) % 5], 5); | ||||||
|  |                 a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); | ||||||
|  |                 break; | ||||||
|  |             case 4: | ||||||
|  |                 b[(i + 8) % 16] = blk(b, (i + 8) % 16); | ||||||
|  |                 a[i % 5] += (((a[(3 + i) % 5] | a[(2 + i) % 5]) & a[(1 + i) % 5]) | (a[(3 + i) % 5] & a[(2 + i) % 5])) + b[(i + 8) % 16] + 0x8f1bbcdc + rol(a[(4 + i) % 5], 5); | ||||||
|  |                 a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); | ||||||
|  |                 break; | ||||||
|  |             case 5: | ||||||
|  |                 b[(i + 12) % 16] = blk(b, (i + 12) % 16); | ||||||
|  |                 a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + b[(i + 12) % 16] + 0xca62c1d6 + rol(a[(4 + i) % 5], 5); | ||||||
|  |                 a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); | ||||||
|  |                 break; | ||||||
|  |             case 6: | ||||||
|  |                 b[i] += a[4 - i]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     static inline void sha1(uint32_t hash[5], uint32_t b[16]) { | ||||||
|  |         uint32_t a[5] = {hash[4], hash[3], hash[2], hash[1], hash[0]}; | ||||||
|  |         static_for<16, Sha1Loop<1>>()(a, b); | ||||||
|  |         static_for<4, Sha1Loop<2>>()(a, b); | ||||||
|  |         static_for<20, Sha1Loop<3>>()(a, b); | ||||||
|  |         static_for<20, Sha1Loop<4>>()(a, b); | ||||||
|  |         static_for<20, Sha1Loop<5>>()(a, b); | ||||||
|  |         static_for<5, Sha1Loop<6>>()(a, hash); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static inline void base64(unsigned char *src, char *dst) { | ||||||
|  |         const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||||||
|  |         for (int i = 0; i < 18; i += 3) { | ||||||
|  |             *dst++ = b64[(src[i] >> 2) & 63]; | ||||||
|  |             *dst++ = b64[((src[i] & 3) << 4) | ((src[i + 1] & 240) >> 4)]; | ||||||
|  |             *dst++ = b64[((src[i + 1] & 15) << 2) | ((src[i + 2] & 192) >> 6)]; | ||||||
|  |             *dst++ = b64[src[i + 2] & 63]; | ||||||
|  |         } | ||||||
|  |         *dst++ = b64[(src[18] >> 2) & 63]; | ||||||
|  |         *dst++ = b64[((src[18] & 3) << 4) | ((src[19] & 240) >> 4)]; | ||||||
|  |         *dst++ = b64[((src[19] & 15) << 2)]; | ||||||
|  |         *dst++ = '='; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |     static inline void generate(const std::string& inputStr, char output[28]) { | ||||||
|  |  | ||||||
|  |         char input[25] = {}; | ||||||
|  |         strncpy(input, inputStr.c_str(), 25 - 1); | ||||||
|  |         input[25 - 1] = '\0'; | ||||||
|  |  | ||||||
|  |         uint32_t b_output[5] = { | ||||||
|  |             0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 | ||||||
|  |         }; | ||||||
|  |         uint32_t b_input[16] = { | ||||||
|  |             0, 0, 0, 0, 0, 0, 0x32353845, 0x41464135, 0x2d453931, 0x342d3437, 0x44412d39, | ||||||
|  |             0x3543412d, 0x43354142, 0x30444338, 0x35423131, 0x80000000 | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         for (int i = 0; i < 6; i++) { | ||||||
|  |             b_input[i] = (input[4 * i + 3] & 0xff) | (input[4 * i + 2] & 0xff) << 8 | (input[4 * i + 1] & 0xff) << 16 | (input[4 * i + 0] & 0xff) << 24; | ||||||
|  |         } | ||||||
|  |         sha1(b_output, b_input); | ||||||
|  |         uint32_t last_b[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480}; | ||||||
|  |         sha1(b_output, last_b); | ||||||
|  |         for (int i = 0; i < 5; i++) { | ||||||
|  |             uint32_t tmp = b_output[i]; | ||||||
|  |             char *bytes = (char *) &b_output[i]; | ||||||
|  |             bytes[3] = tmp & 0xff; | ||||||
|  |             bytes[2] = (tmp >> 8) & 0xff; | ||||||
|  |             bytes[1] = (tmp >> 16) & 0xff; | ||||||
|  |             bytes[0] = (tmp >> 24) & 0xff; | ||||||
|  |         } | ||||||
|  |         base64((unsigned char *) b_output, output); | ||||||
|  |     } | ||||||
|  | }; | ||||||
							
								
								
									
										16
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								makefile
									
									
									
									
									
								
							| @@ -20,13 +20,13 @@ install: brew | |||||||
| # Release, Debug, MinSizeRel, RelWithDebInfo are the build types | # Release, Debug, MinSizeRel, RelWithDebInfo are the build types | ||||||
| # | # | ||||||
| brew: | brew: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install) | 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4 install) | ||||||
|  |  | ||||||
| # Docker default target. We've add problem with OpenSSL and TLS 1.3 (on the | # Docker default target. We've add problem with OpenSSL and TLS 1.3 (on the | ||||||
| # server side ?) and I can't work-around it easily, so we're using mbedtls on | # server side ?) and I can't work-around it easily, so we're using mbedtls on | ||||||
| # Linux for the SSL backend, which works great. | # Linux for the SSL backend, which works great. | ||||||
| ws_mbedtls_install: | ws_mbedtls_install: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; ninja install) | 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4 install) | ||||||
|  |  | ||||||
| ws: | ws: | ||||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j 4) | 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j 4) | ||||||
| @@ -103,10 +103,6 @@ test_server: | |||||||
| # env TEST=Websocket_chat make test | # env TEST=Websocket_chat make test | ||||||
| # env TEST=heartbeat make test | # env TEST=heartbeat make test | ||||||
| test: | test: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install) |  | ||||||
| 	(cd test ; python2.7 run.py -r) |  | ||||||
|  |  | ||||||
| test_make: |  | ||||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4) | 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4) | ||||||
| 	(cd test ; python2.7 run.py -r) | 	(cd test ; python2.7 run.py -r) | ||||||
|  |  | ||||||
| @@ -120,12 +116,10 @@ test_ubsan: | |||||||
| 	(cd build/test ; ln -sf Debug/ixwebsocket_unittest) | 	(cd build/test ; ln -sf Debug/ixwebsocket_unittest) | ||||||
| 	(cd test ; python2.7 run.py -r) | 	(cd test ; python2.7 run.py -r) | ||||||
|  |  | ||||||
| test_asan: build_test_asan | test_asan: | ||||||
| 	(cd test ; python2.7 run.py -r) |  | ||||||
|  |  | ||||||
| build_test_asan: |  | ||||||
| 	mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableAddressSanitizer YES) | 	mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableAddressSanitizer YES) | ||||||
| 	(cd build/test ; ln -sf Debug/ixwebsocket_unittest) | 	(cd build/test ; ln -sf Debug/ixwebsocket_unittest) | ||||||
|  | 	(cd test ; python2.7 run.py -r) | ||||||
|  |  | ||||||
| test_tsan_openssl: | test_tsan_openssl: | ||||||
| 	mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableThreadSanitizer YES) | 	mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableThreadSanitizer YES) | ||||||
| @@ -148,7 +142,7 @@ test_tsan_mbedtls: | |||||||
| 	(cd test ; python2.7 run.py -r) | 	(cd test ; python2.7 run.py -r) | ||||||
|  |  | ||||||
| build_test_openssl: | build_test_openssl: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; ninja install) | 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; make -j 4) | ||||||
|  |  | ||||||
| test_openssl: build_test_openssl | test_openssl: build_test_openssl | ||||||
| 	(cd test ; python2.7 run.py -r) | 	(cd test ; python2.7 run.py -r) | ||||||
|   | |||||||
| @@ -66,7 +66,13 @@ if (UNIX) | |||||||
|     IXCobraMetricsPublisherTest.cpp |     IXCobraMetricsPublisherTest.cpp | ||||||
|     IXCobraToSentryBotTest.cpp |     IXCobraToSentryBotTest.cpp | ||||||
|     IXCobraToStatsdBotTest.cpp |     IXCobraToStatsdBotTest.cpp | ||||||
|     IXCobraToStdoutBotTest.cpp |   ) | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | # Some unittest fail for dubious reason on Ubuntu Xenial with TSAN | ||||||
|  | if (MAC OR WIN32) | ||||||
|  |   list(APPEND SOURCES | ||||||
|  |     IXWebSocketMessageQTest.cpp | ||||||
|   ) |   ) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -180,40 +180,44 @@ namespace | |||||||
|         _conn.configure(_cobraConfig); |         _conn.configure(_cobraConfig); | ||||||
|         _conn.connect(); |         _conn.connect(); | ||||||
|  |  | ||||||
|         _conn.setEventCallback([this, channel](const CobraEventPtr& event) { |         _conn.setEventCallback([this, channel](ix::CobraConnectionEventType eventType, | ||||||
|             if (event->type == ix::CobraEventType::Open) |                                                const std::string& errMsg, | ||||||
|  |                                                const ix::WebSocketHttpHeaders& headers, | ||||||
|  |                                                const std::string& subscriptionId, | ||||||
|  |                                                CobraConnection::MsgId msgId) { | ||||||
|  |             if (eventType == ix::CobraConnection_EventType_Open) | ||||||
|             { |             { | ||||||
|                 log("Subscriber connected: " + _user); |                 log("Subscriber connected: " + _user); | ||||||
|                 for (auto&& it : event->headers) |                 for (auto&& it : headers) | ||||||
|                 { |                 { | ||||||
|                     log("Headers " + it.first + " " + it.second); |                     log("Headers " + it.first + " " + it.second); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::Authenticated) |             else if (eventType == ix::CobraConnection_EventType_Authenticated) | ||||||
|             { |             { | ||||||
|                 log("Subscriber authenticated: " + _user); |                 log("Subscriber authenticated: " + _user); | ||||||
|                 subscribe(channel); |                 subscribe(channel); | ||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::Error) |             else if (eventType == ix::CobraConnection_EventType_Error) | ||||||
|             { |             { | ||||||
|                 log(event->errMsg + _user); |                 log(errMsg + _user); | ||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::Closed) |             else if (eventType == ix::CobraConnection_EventType_Closed) | ||||||
|             { |             { | ||||||
|                 log("Connection closed: " + _user); |                 log("Connection closed: " + _user); | ||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::Subscribed) |             else if (eventType == ix::CobraConnection_EventType_Subscribed) | ||||||
|             { |             { | ||||||
|                 log("Subscription ok: " + _user + " subscription_id " + event->subscriptionId); |                 log("Subscription ok: " + _user + " subscription_id " + subscriptionId); | ||||||
|                 _connectedAndSubscribed = true; |                 _connectedAndSubscribed = true; | ||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::UnSubscribed) |             else if (eventType == ix::CobraConnection_EventType_UnSubscribed) | ||||||
|             { |             { | ||||||
|                 log("Unsubscription ok: " + _user + " subscription_id " + event->subscriptionId); |                 log("Unsubscription ok: " + _user + " subscription_id " + subscriptionId); | ||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::Published) |             else if (eventType == ix::CobraConnection_EventType_Published) | ||||||
|             { |             { | ||||||
|                 TLogger() << "Subscriber: published message acked: " << event->msgId; |                 TLogger() << "Subscriber: published message acked: " << msgId; | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
| @@ -244,7 +248,11 @@ namespace | |||||||
|         ix::msleep(50); |         ix::msleep(50); | ||||||
|         _conn.disconnect(); |         _conn.disconnect(); | ||||||
|  |  | ||||||
|         _conn.setEventCallback([](const CobraEventPtr& /*event*/) {}); |         _conn.setEventCallback([](ix::CobraConnectionEventType /*eventType*/, | ||||||
|  |                                   const std::string& /*errMsg*/, | ||||||
|  |                                   const ix::WebSocketHttpHeaders& /*headers*/, | ||||||
|  |                                   const std::string& /*subscriptionId*/, | ||||||
|  |                                   CobraConnection::MsgId /*msgId*/) { ; }); | ||||||
|     } |     } | ||||||
| } // namespace | } // namespace | ||||||
|  |  | ||||||
|   | |||||||
| @@ -54,24 +54,24 @@ namespace | |||||||
|         conn.configure(config); |         conn.configure(config); | ||||||
|         conn.connect(); |         conn.connect(); | ||||||
|  |  | ||||||
|         conn.setEventCallback([&conn, &channel](const CobraEventPtr& event) { |         conn.setEventCallback([&conn, &channel](ix::CobraConnectionEventType eventType, | ||||||
|             if (event->type == ix::CobraEventType::Open) |                                                 const std::string& errMsg, | ||||||
|  |                                                 const ix::WebSocketHttpHeaders& headers, | ||||||
|  |                                                 const std::string& subscriptionId, | ||||||
|  |                                                 CobraConnection::MsgId msgId) { | ||||||
|  |             if (eventType == ix::CobraConnection_EventType_Open) | ||||||
|             { |             { | ||||||
|                 TLogger() << "Subscriber connected:"; |                 TLogger() << "Subscriber connected:"; | ||||||
|                 for (auto&& it : event->headers) |                 for (auto&& it : headers) | ||||||
|                 { |                 { | ||||||
|                     log("Headers " + it.first + " " + it.second); |                     log("Headers " + it.first + " " + it.second); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::Closed) |             if (eventType == ix::CobraConnection_EventType_Error) | ||||||
|             { |             { | ||||||
|                 TLogger() << "Subscriber closed:" << event->errMsg; |                 TLogger() << "Subscriber error:" << errMsg; | ||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::Error) |             else if (eventType == ix::CobraConnection_EventType_Authenticated) | ||||||
|             { |  | ||||||
|                 TLogger() << "Subscriber error:" << event->errMsg; |  | ||||||
|             } |  | ||||||
|             else if (event->type == ix::CobraEventType::Authenticated) |  | ||||||
|             { |             { | ||||||
|                 log("Subscriber authenticated"); |                 log("Subscriber authenticated"); | ||||||
|                 std::string filter; |                 std::string filter; | ||||||
| @@ -92,29 +92,29 @@ namespace | |||||||
|                                    gMessageCount++; |                                    gMessageCount++; | ||||||
|                                }); |                                }); | ||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::Subscribed) |             else if (eventType == ix::CobraConnection_EventType_Subscribed) | ||||||
|             { |             { | ||||||
|                 TLogger() << "Subscriber: subscribed to channel " << event->subscriptionId; |                 TLogger() << "Subscriber: subscribed to channel " << subscriptionId; | ||||||
|                 if (event->subscriptionId == channel) |                 if (subscriptionId == channel) | ||||||
|                 { |                 { | ||||||
|                     gSubscriberConnectedAndSubscribed = true; |                     gSubscriberConnectedAndSubscribed = true; | ||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     TLogger() << "Subscriber: unexpected channel " << event->subscriptionId; |                     TLogger() << "Subscriber: unexpected channel " << subscriptionId; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::UnSubscribed) |             else if (eventType == ix::CobraConnection_EventType_UnSubscribed) | ||||||
|             { |             { | ||||||
|                 TLogger() << "Subscriber: ununexpected from channel " << event->subscriptionId; |                 TLogger() << "Subscriber: ununexpected from channel " << subscriptionId; | ||||||
|                 if (event->subscriptionId != channel) |                 if (subscriptionId != channel) | ||||||
|                 { |                 { | ||||||
|                     TLogger() << "Subscriber: unexpected channel " << event->subscriptionId; |                     TLogger() << "Subscriber: unexpected channel " << subscriptionId; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             else if (event->type == ix::CobraEventType::Published) |             else if (eventType == ix::CobraConnection_EventType_Published) | ||||||
|             { |             { | ||||||
|                 TLogger() << "Subscriber: published message acked: " << event->msgId; |                 TLogger() << "Subscriber: published message acked: " << msgId; | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -138,12 +138,12 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]") | |||||||
|  |  | ||||||
|         std::thread publisherThread(runPublisher, config, channel); |         std::thread publisherThread(runPublisher, config, channel); | ||||||
|  |  | ||||||
|         ix::CobraBotConfig cobraBotConfig; |         std::string filter; | ||||||
|         cobraBotConfig.cobraConfig = config; |         std::string position("$"); | ||||||
|         cobraBotConfig.channel = channel; |  | ||||||
|         cobraBotConfig.runtime = 3; // Only run the bot for 3 seconds |  | ||||||
|         cobraBotConfig.enableHeartbeat = false; |  | ||||||
|         bool verbose = true; |         bool verbose = true; | ||||||
|  |         bool strict = true; | ||||||
|  |         size_t maxQueueSize = 10; | ||||||
|  |         bool enableHeartbeat = false; | ||||||
|  |  | ||||||
|         // FIXME: try to get this working with https instead of http |         // FIXME: try to get this working with https instead of http | ||||||
|         //        to regress the TLS 1.3 OpenSSL bug |         //        to regress the TLS 1.3 OpenSSL bug | ||||||
| @@ -158,7 +158,19 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]") | |||||||
|         SentryClient sentryClient(dsn); |         SentryClient sentryClient(dsn); | ||||||
|         sentryClient.setTLSOptions(tlsOptionsClient); |         sentryClient.setTLSOptions(tlsOptionsClient); | ||||||
|  |  | ||||||
|         int64_t sentCount = cobra_to_sentry_bot(cobraBotConfig, sentryClient, verbose); |         // Only run the bot for 3 seconds | ||||||
|  |         int runtime = 3; | ||||||
|  |  | ||||||
|  |         int sentCount = cobra_to_sentry_bot(config, | ||||||
|  |                                             channel, | ||||||
|  |                                             filter, | ||||||
|  |                                             position, | ||||||
|  |                                             sentryClient, | ||||||
|  |                                             verbose, | ||||||
|  |                                             strict, | ||||||
|  |                                             maxQueueSize, | ||||||
|  |                                             enableHeartbeat, | ||||||
|  |                                             runtime); | ||||||
|         // |         // | ||||||
|         // We want at least 2 messages to be sent |         // We want at least 2 messages to be sent | ||||||
|         // |         // | ||||||
|   | |||||||
| @@ -87,11 +87,14 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]") | |||||||
|  |  | ||||||
|         std::thread publisherThread(runPublisher, config, channel); |         std::thread publisherThread(runPublisher, config, channel); | ||||||
|  |  | ||||||
|         ix::CobraBotConfig cobraBotConfig; |         std::string filter; | ||||||
|         cobraBotConfig.cobraConfig = config; |         std::string position("$"); | ||||||
|         cobraBotConfig.channel = channel; |         bool verbose = true; | ||||||
|         cobraBotConfig.runtime = 3; // Only run the bot for 3 seconds |         size_t maxQueueSize = 10; | ||||||
|         cobraBotConfig.enableHeartbeat = false; |         bool enableHeartbeat = false; | ||||||
|  |  | ||||||
|  |         // Only run the bot for 3 seconds | ||||||
|  |         int runtime = 3; | ||||||
|  |  | ||||||
|         std::string hostname("127.0.0.1"); |         std::string hostname("127.0.0.1"); | ||||||
|         // std::string hostname("www.google.com"); |         // std::string hostname("www.google.com"); | ||||||
| @@ -110,10 +113,19 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]") | |||||||
|         std::string fields("device.game\ndevice.os_name"); |         std::string fields("device.game\ndevice.os_name"); | ||||||
|         std::string gauge; |         std::string gauge; | ||||||
|         std::string timer; |         std::string timer; | ||||||
|         bool verbose = true; |  | ||||||
|  |  | ||||||
|         int64_t sentCount = |         int sentCount = ix::cobra_to_statsd_bot(config, | ||||||
|             ix::cobra_to_statsd_bot(cobraBotConfig, statsdClient, fields, gauge, timer, verbose); |                                                 channel, | ||||||
|  |                                                 filter, | ||||||
|  |                                                 position, | ||||||
|  |                                                 statsdClient, | ||||||
|  |                                                 fields, | ||||||
|  |                                                 gauge, | ||||||
|  |                                                 timer, | ||||||
|  |                                                 verbose, | ||||||
|  |                                                 maxQueueSize, | ||||||
|  |                                                 enableHeartbeat, | ||||||
|  |                                                 runtime); | ||||||
|         // |         // | ||||||
|         // We want at least 2 messages to be sent |         // We want at least 2 messages to be sent | ||||||
|         // |         // | ||||||
|   | |||||||
| @@ -1,115 +0,0 @@ | |||||||
| /* |  | ||||||
|  *  IXCobraToStdoutTest.cpp |  | ||||||
|  *  Author: Benjamin Sergeant |  | ||||||
|  *  Copyright (c) 2020 Machine Zone. All rights reserved. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include "IXTest.h" |  | ||||||
| #include "catch.hpp" |  | ||||||
| #include <chrono> |  | ||||||
| #include <iostream> |  | ||||||
| #include <ixbots/IXCobraToStdoutBot.h> |  | ||||||
| #include <ixcobra/IXCobraConnection.h> |  | ||||||
| #include <ixcobra/IXCobraMetricsPublisher.h> |  | ||||||
| #include <ixcrypto/IXUuid.h> |  | ||||||
| #include <ixsentry/IXSentryClient.h> |  | ||||||
| #include <ixsnake/IXRedisServer.h> |  | ||||||
| #include <ixsnake/IXSnakeServer.h> |  | ||||||
| #include <ixwebsocket/IXHttpServer.h> |  | ||||||
| #include <ixwebsocket/IXUserAgent.h> |  | ||||||
|  |  | ||||||
| using namespace ix; |  | ||||||
|  |  | ||||||
| namespace |  | ||||||
| { |  | ||||||
|     void runPublisher(const ix::CobraConfig& config, const std::string& channel) |  | ||||||
|     { |  | ||||||
|         ix::CobraMetricsPublisher cobraMetricsPublisher; |  | ||||||
|         cobraMetricsPublisher.configure(config, channel); |  | ||||||
|         cobraMetricsPublisher.setSession(uuid4()); |  | ||||||
|         cobraMetricsPublisher.enable(true); |  | ||||||
|  |  | ||||||
|         Json::Value msg; |  | ||||||
|         msg["fps"] = 60; |  | ||||||
|  |  | ||||||
|         cobraMetricsPublisher.setGenericAttributes("game", "ody"); |  | ||||||
|  |  | ||||||
|         // Wait a bit |  | ||||||
|         ix::msleep(500); |  | ||||||
|  |  | ||||||
|         // publish some messages |  | ||||||
|         cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #1) |  | ||||||
|         cobraMetricsPublisher.push("sms_metric_B_id", msg); // (msg #2) |  | ||||||
|         ix::msleep(500); |  | ||||||
|  |  | ||||||
|         cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #3) |  | ||||||
|         cobraMetricsPublisher.push("sms_metric_D_id", msg); // (msg #4) |  | ||||||
|         ix::msleep(500); |  | ||||||
|  |  | ||||||
|         cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #4) |  | ||||||
|         cobraMetricsPublisher.push("sms_metric_F_id", msg); // (msg #5) |  | ||||||
|         ix::msleep(500); |  | ||||||
|     } |  | ||||||
| } // namespace |  | ||||||
|  |  | ||||||
| TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]") |  | ||||||
| { |  | ||||||
|     SECTION("Exchange and count sent/received messages.") |  | ||||||
|     { |  | ||||||
|         int port = getFreePort(); |  | ||||||
|         snake::AppConfig appConfig = makeSnakeServerConfig(port, true); |  | ||||||
|  |  | ||||||
|         // Start a redis server |  | ||||||
|         ix::RedisServer redisServer(appConfig.redisPort); |  | ||||||
|         auto res = redisServer.listen(); |  | ||||||
|         REQUIRE(res.first); |  | ||||||
|         redisServer.start(); |  | ||||||
|  |  | ||||||
|         // Start a snake server |  | ||||||
|         snake::SnakeServer snakeServer(appConfig); |  | ||||||
|         snakeServer.run(); |  | ||||||
|  |  | ||||||
|         // Run the bot for a small amount of time |  | ||||||
|         std::string channel = ix::generateSessionId(); |  | ||||||
|         std::string appkey("FC2F10139A2BAc53BB72D9db967b024f"); |  | ||||||
|         std::string role = "_sub"; |  | ||||||
|         std::string secret = "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba"; |  | ||||||
|         std::string endpoint = makeCobraEndpoint(port, true); |  | ||||||
|  |  | ||||||
|         ix::CobraConfig config; |  | ||||||
|         config.endpoint = endpoint; |  | ||||||
|         config.appkey = appkey; |  | ||||||
|         config.rolename = role; |  | ||||||
|         config.rolesecret = secret; |  | ||||||
|         config.socketTLSOptions = makeClientTLSOptions(); |  | ||||||
|  |  | ||||||
|         std::thread publisherThread(runPublisher, config, channel); |  | ||||||
|  |  | ||||||
|         ix::CobraBotConfig cobraBotConfig; |  | ||||||
|         cobraBotConfig.cobraConfig = config; |  | ||||||
|         cobraBotConfig.channel = channel; |  | ||||||
|         cobraBotConfig.runtime = 3; // Only run the bot for 3 seconds |  | ||||||
|         cobraBotConfig.enableHeartbeat = false; |  | ||||||
|         bool quiet = false; |  | ||||||
|  |  | ||||||
|         // We could try to capture the output ... not sure how. |  | ||||||
|         bool fluentd = true; |  | ||||||
|  |  | ||||||
|         int64_t sentCount = ix::cobra_to_stdout_bot(cobraBotConfig, fluentd, quiet); |  | ||||||
|         // |  | ||||||
|         // We want at least 2 messages to be sent |  | ||||||
|         // |  | ||||||
|         REQUIRE(sentCount >= 2); |  | ||||||
|  |  | ||||||
|         // Give us 1s for all messages to be received |  | ||||||
|         ix::msleep(1000); |  | ||||||
|  |  | ||||||
|         spdlog::info("Stopping snake server..."); |  | ||||||
|         snakeServer.stop(); |  | ||||||
|  |  | ||||||
|         spdlog::info("Stopping redis server..."); |  | ||||||
|         redisServer.stop(); |  | ||||||
|  |  | ||||||
|         publisherThread.join(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -4,14 +4,6 @@ | |||||||
|  *  Copyright (c) 2019 Machine Zone. All rights reserved. |  *  Copyright (c) 2019 Machine Zone. All rights reserved. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| // Using inet_addr will trigger an error on uwp without this |  | ||||||
| // FIXME: use a different api |  | ||||||
| #ifdef _WIN32 |  | ||||||
| #ifndef _WINSOCK_DEPRECATED_NO_WARNINGS |  | ||||||
| #define _WINSOCK_DEPRECATED_NO_WARNINGS |  | ||||||
| #endif |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include "IXGetFreePort.h" | #include "IXGetFreePort.h" | ||||||
|  |  | ||||||
| #include <ixwebsocket/IXNetSystem.h> | #include <ixwebsocket/IXNetSystem.h> | ||||||
|   | |||||||
| @@ -17,11 +17,8 @@ | |||||||
| #include <ixwebsocket/IXSelectInterruptFactory.h> | #include <ixwebsocket/IXSelectInterruptFactory.h> | ||||||
| #include <ixwebsocket/IXSetThreadName.h> | #include <ixwebsocket/IXSetThreadName.h> | ||||||
| #include <ixwebsocket/IXSocket.h> | #include <ixwebsocket/IXSocket.h> | ||||||
| #include <ixwebsocket/IXSocketAppleSSL.h> |  | ||||||
| #include <ixwebsocket/IXSocketConnect.h> | #include <ixwebsocket/IXSocketConnect.h> | ||||||
| #include <ixwebsocket/IXSocketFactory.h> | #include <ixwebsocket/IXSocketFactory.h> | ||||||
| #include <ixwebsocket/IXSocketMbedTLS.h> |  | ||||||
| #include <ixwebsocket/IXSocketOpenSSL.h> |  | ||||||
| #include <ixwebsocket/IXSocketServer.h> | #include <ixwebsocket/IXSocketServer.h> | ||||||
| #include <ixwebsocket/IXUrlParser.h> | #include <ixwebsocket/IXUrlParser.h> | ||||||
| #include <ixwebsocket/IXWebSocket.h> | #include <ixwebsocket/IXWebSocket.h> | ||||||
| @@ -29,9 +26,9 @@ | |||||||
| #include <ixwebsocket/IXWebSocketCloseInfo.h> | #include <ixwebsocket/IXWebSocketCloseInfo.h> | ||||||
| #include <ixwebsocket/IXWebSocketErrorInfo.h> | #include <ixwebsocket/IXWebSocketErrorInfo.h> | ||||||
| #include <ixwebsocket/IXWebSocketHandshake.h> | #include <ixwebsocket/IXWebSocketHandshake.h> | ||||||
| #include <ixwebsocket/IXWebSocketHandshakeKeyGen.h> |  | ||||||
| #include <ixwebsocket/IXWebSocketHttpHeaders.h> | #include <ixwebsocket/IXWebSocketHttpHeaders.h> | ||||||
| #include <ixwebsocket/IXWebSocketMessage.h> | #include <ixwebsocket/IXWebSocketMessage.h> | ||||||
|  | #include <ixwebsocket/IXWebSocketMessageQueue.h> | ||||||
| #include <ixwebsocket/IXWebSocketMessageType.h> | #include <ixwebsocket/IXWebSocketMessageType.h> | ||||||
| #include <ixwebsocket/IXWebSocketOpenInfo.h> | #include <ixwebsocket/IXWebSocketOpenInfo.h> | ||||||
| #include <ixwebsocket/IXWebSocketPerMessageDeflate.h> | #include <ixwebsocket/IXWebSocketPerMessageDeflate.h> | ||||||
| @@ -40,6 +37,8 @@ | |||||||
| #include <ixwebsocket/IXWebSocketSendInfo.h> | #include <ixwebsocket/IXWebSocketSendInfo.h> | ||||||
| #include <ixwebsocket/IXWebSocketServer.h> | #include <ixwebsocket/IXWebSocketServer.h> | ||||||
| #include <ixwebsocket/IXWebSocketTransport.h> | #include <ixwebsocket/IXWebSocketTransport.h> | ||||||
|  | #include <ixwebsocket/LUrlParser.h> | ||||||
|  | #include <ixwebsocket/libwshandshake.hpp> | ||||||
|  |  | ||||||
| using namespace ix; | using namespace ix; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,8 +6,8 @@ | |||||||
|  |  | ||||||
| #include "IXTest.h" | #include "IXTest.h" | ||||||
| #include "catch.hpp" | #include "catch.hpp" | ||||||
| #include "msgpack11.hpp" |  | ||||||
| #include <iostream> | #include <iostream> | ||||||
|  | #include "msgpack11.hpp" | ||||||
| #include <ixwebsocket/IXSocket.h> | #include <ixwebsocket/IXSocket.h> | ||||||
| #include <ixwebsocket/IXSocketFactory.h> | #include <ixwebsocket/IXSocketFactory.h> | ||||||
| #include <ixwebsocket/IXWebSocket.h> | #include <ixwebsocket/IXWebSocket.h> | ||||||
| @@ -130,8 +130,7 @@ namespace | |||||||
|             } |             } | ||||||
|             else if (msg->type == ix::WebSocketMessageType::Error) |             else if (msg->type == ix::WebSocketMessageType::Error) | ||||||
|             { |             { | ||||||
|                 ss << "websocket_broadcast_client: " << _user << " Error ! " |                 ss << "websocket_broadcast_client: " << _user << " Error ! " << msg->errorInfo.reason; | ||||||
|                    << msg->errorInfo.reason; |  | ||||||
|                 log(ss.str()); |                 log(ss.str()); | ||||||
|             } |             } | ||||||
|             else if (msg->type == ix::WebSocketMessageType::Ping) |             else if (msg->type == ix::WebSocketMessageType::Ping) | ||||||
| @@ -235,7 +234,7 @@ namespace | |||||||
|         server.start(); |         server.start(); | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| } // namespace | } // namespace ix | ||||||
|  |  | ||||||
| TEST_CASE("Websocket_broadcast_server", "[websocket_server]") | TEST_CASE("Websocket_broadcast_server", "[websocket_server]") | ||||||
| { | { | ||||||
| @@ -248,7 +247,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]") | |||||||
|  |  | ||||||
|         std::string session = ix::generateSessionId(); |         std::string session = ix::generateSessionId(); | ||||||
|         std::vector<std::shared_ptr<WebSocketChat>> chatClients; |         std::vector<std::shared_ptr<WebSocketChat>> chatClients; | ||||||
|         for (int i = 0; i < 10; ++i) |         for (int i = 0 ; i < 10; ++i) | ||||||
|         { |         { | ||||||
|             std::string user("user_" + std::to_string(i)); |             std::string user("user_" + std::to_string(i)); | ||||||
|             chatClients.push_back(std::make_shared<WebSocketChat>(user, session, port)); |             chatClients.push_back(std::make_shared<WebSocketChat>(user, session, port)); | ||||||
| @@ -260,7 +259,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]") | |||||||
|         while (true) |         while (true) | ||||||
|         { |         { | ||||||
|             bool allReady = true; |             bool allReady = true; | ||||||
|             for (size_t i = 0; i < chatClients.size(); ++i) |             for (size_t i = 0 ; i < chatClients.size(); ++i) | ||||||
|             { |             { | ||||||
|                 allReady &= chatClients[i]->isReady(); |                 allReady &= chatClients[i]->isReady(); | ||||||
|             } |             } | ||||||
| @@ -270,7 +269,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]") | |||||||
|  |  | ||||||
|         for (int j = 0; j < 1000; j++) |         for (int j = 0; j < 1000; j++) | ||||||
|         { |         { | ||||||
|             for (size_t i = 0; i < chatClients.size(); ++i) |             for (size_t i = 0 ; i < chatClients.size(); ++i) | ||||||
|             { |             { | ||||||
|                 chatClients[i]->sendMessage("hello world"); |                 chatClients[i]->sendMessage("hello world"); | ||||||
|             } |             } | ||||||
| @@ -292,7 +291,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]") | |||||||
|  |  | ||||||
|         // Stop all clients |         // Stop all clients | ||||||
|         size_t messageCount = chatClients.size() * 50; |         size_t messageCount = chatClients.size() * 50; | ||||||
|         for (size_t i = 0; i < chatClients.size(); ++i) |         for (size_t i = 0 ; i < chatClients.size(); ++i) | ||||||
|         { |         { | ||||||
|             REQUIRE(chatClients[i]->getReceivedMessagesCount() >= messageCount); |             REQUIRE(chatClients[i]->getReceivedMessagesCount() >= messageCount); | ||||||
|             chatClients[i]->stop(); |             chatClients[i]->stop(); | ||||||
|   | |||||||
							
								
								
									
										178
									
								
								test/IXWebSocketMessageQTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								test/IXWebSocketMessageQTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,178 @@ | |||||||
|  | /* | ||||||
|  |  *  IXWebSocketMessageQTest.cpp | ||||||
|  |  *  Author: Korchynskyi Dmytro | ||||||
|  |  *  Copyright (c) 2019 Machine Zone. All rights reserved. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "IXTest.h" | ||||||
|  | #include "catch.hpp" | ||||||
|  | #include <ixwebsocket/IXWebSocket.h> | ||||||
|  | #include <ixwebsocket/IXWebSocketMessageQueue.h> | ||||||
|  | #include <ixwebsocket/IXWebSocketServer.h> | ||||||
|  | #include <thread> | ||||||
|  |  | ||||||
|  | using namespace ix; | ||||||
|  |  | ||||||
|  | namespace | ||||||
|  | { | ||||||
|  |     bool startServer(ix::WebSocketServer& server) | ||||||
|  |     { | ||||||
|  |         server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket, | ||||||
|  |                                                  std::shared_ptr<ConnectionState> connectionState) { | ||||||
|  |             webSocket->setOnMessageCallback( | ||||||
|  |                 [connectionState, &server](const WebSocketMessagePtr& msg) { | ||||||
|  |                     if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|  |                     { | ||||||
|  |                         TLogger() << "New connection"; | ||||||
|  |                         connectionState->computeId(); | ||||||
|  |                         TLogger() << "id: " << connectionState->getId(); | ||||||
|  |                         TLogger() << "Uri: " << msg->openInfo.uri; | ||||||
|  |                         TLogger() << "Headers:"; | ||||||
|  |                         for (auto&& it : msg->openInfo.headers) | ||||||
|  |                         { | ||||||
|  |                             TLogger() << it.first << ": " << it.second; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     else if (msg->type == ix::WebSocketMessageType::Close) | ||||||
|  |                     { | ||||||
|  |                         TLogger() << "Closed connection"; | ||||||
|  |                     } | ||||||
|  |                     else if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|  |                     { | ||||||
|  |                         TLogger() << "Message received: " << msg->str; | ||||||
|  |  | ||||||
|  |                         for (auto&& client : server.getClients()) | ||||||
|  |                         { | ||||||
|  |                             client->send(msg->str); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         auto res = server.listen(); | ||||||
|  |         if (!res.first) | ||||||
|  |         { | ||||||
|  |             TLogger() << res.second; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         server.start(); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     class MsgQTestClient | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         MsgQTestClient() | ||||||
|  |         { | ||||||
|  |             msgQ.bindWebsocket(&ws); | ||||||
|  |  | ||||||
|  |             msgQ.setOnMessageCallback([this](const WebSocketMessagePtr& msg) { | ||||||
|  |                 REQUIRE(mainThreadId == std::this_thread::get_id()); | ||||||
|  |  | ||||||
|  |                 std::stringstream ss; | ||||||
|  |                 if (msg->type == WebSocketMessageType::Open) | ||||||
|  |                 { | ||||||
|  |                     log("client connected"); | ||||||
|  |                     sendNextMessage(); | ||||||
|  |                 } | ||||||
|  |                 else if (msg->type == WebSocketMessageType::Close) | ||||||
|  |                 { | ||||||
|  |                     log("client disconnected"); | ||||||
|  |                 } | ||||||
|  |                 else if (msg->type == WebSocketMessageType::Error) | ||||||
|  |                 { | ||||||
|  |                     ss << "Error ! " << msg->errorInfo.reason; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                     testDone = true; | ||||||
|  |                 } | ||||||
|  |                 else if (msg->type == WebSocketMessageType::Pong) | ||||||
|  |                 { | ||||||
|  |                     ss << "Received pong message " << msg->str; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |                 else if (msg->type == WebSocketMessageType::Ping) | ||||||
|  |                 { | ||||||
|  |                     ss << "Received ping message " << msg->str; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                 } | ||||||
|  |                 else if (msg->type == WebSocketMessageType::Message) | ||||||
|  |                 { | ||||||
|  |                     REQUIRE(msg->str.compare("Hey dude!") == 0); | ||||||
|  |                     ++receivedCount; | ||||||
|  |                     ss << "Received message " << msg->str; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                     sendNextMessage(); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     ss << "Invalid WebSocketMessageType"; | ||||||
|  |                     log(ss.str()); | ||||||
|  |                     testDone = true; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         void sendNextMessage() | ||||||
|  |         { | ||||||
|  |             if (receivedCount >= 3) | ||||||
|  |             { | ||||||
|  |                 testDone = true; | ||||||
|  |                 succeeded = true; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 auto info = ws.sendText("Hey dude!"); | ||||||
|  |                 if (info.success) | ||||||
|  |                     log("sent message"); | ||||||
|  |                 else | ||||||
|  |                     log("send failed"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         void run(const std::string& url) | ||||||
|  |         { | ||||||
|  |             mainThreadId = std::this_thread::get_id(); | ||||||
|  |             testDone = false; | ||||||
|  |             receivedCount = 0; | ||||||
|  |  | ||||||
|  |             ws.setUrl(url); | ||||||
|  |             ws.start(); | ||||||
|  |  | ||||||
|  |             while (!testDone) | ||||||
|  |             { | ||||||
|  |                 msgQ.poll(); | ||||||
|  |                 msleep(50); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bool isSucceeded() const | ||||||
|  |         { | ||||||
|  |             return succeeded; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         WebSocket ws; | ||||||
|  |         WebSocketMessageQueue msgQ; | ||||||
|  |         bool testDone = false; | ||||||
|  |         uint32_t receivedCount = 0; | ||||||
|  |         std::thread::id mainThreadId; | ||||||
|  |         bool succeeded = false; | ||||||
|  |     }; | ||||||
|  | } // namespace | ||||||
|  |  | ||||||
|  | TEST_CASE("Websocket_message_queue", "[websocket_message_q]") | ||||||
|  | { | ||||||
|  |     SECTION("Send several messages") | ||||||
|  |     { | ||||||
|  |         int port = getFreePort(); | ||||||
|  |         WebSocketServer server(port); | ||||||
|  |         REQUIRE(startServer(server)); | ||||||
|  |  | ||||||
|  |         MsgQTestClient testClient; | ||||||
|  |         testClient.run("ws://127.0.0.1:" + std::to_string(port)); | ||||||
|  |         REQUIRE(testClient.isSucceeded()); | ||||||
|  |  | ||||||
|  |         server.stop(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -93,11 +93,10 @@ TEST_CASE("subprotocol", "[websocket_subprotocol]") | |||||||
|         webSocket.setUrl(url); |         webSocket.setUrl(url); | ||||||
|         webSocket.start(); |         webSocket.start(); | ||||||
|  |  | ||||||
|         // Give us 3 seconds to connect |  | ||||||
|         int attempts = 0; |         int attempts = 0; | ||||||
|         while (!connected) |         while (!connected) | ||||||
|         { |         { | ||||||
|             REQUIRE(attempts++ < 300); |             REQUIRE(attempts++ < 10); | ||||||
|             ix::msleep(10); |             ix::msleep(10); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,41 +14,8 @@ int main(int argc, char* argv[]) | |||||||
| { | { | ||||||
|     ix::initNetSystem(); |     ix::initNetSystem(); | ||||||
|  |  | ||||||
|     ix::CoreLogger::LogFunc logFunc = [](const char* msg, ix::LogLevel level) { |     ix::IXCoreLogger::LogFunc logFunc = [](const char* msg) { spdlog::info(msg); }; | ||||||
|         switch (level) |     ix::IXCoreLogger::setLogFunction(logFunc); | ||||||
|         { |  | ||||||
|             case ix::LogLevel::Debug: |  | ||||||
|             { |  | ||||||
|                 spdlog::debug(msg); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|             case ix::LogLevel::Info: |  | ||||||
|             { |  | ||||||
|                 spdlog::info(msg); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|             case ix::LogLevel::Warning: |  | ||||||
|             { |  | ||||||
|                 spdlog::warn(msg); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|             case ix::LogLevel::Error: |  | ||||||
|             { |  | ||||||
|                 spdlog::error(msg); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|             case ix::LogLevel::Critical: |  | ||||||
|             { |  | ||||||
|                 spdlog::critical(msg); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|     ix::CoreLogger::setLogFunction(logFunc); |  | ||||||
|  |  | ||||||
|     int result = Catch::Session().run(argc, argv); |     int result = Catch::Session().run(argc, argv); | ||||||
|  |  | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user