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/**' | ||||
|  | ||||
| jobs: | ||||
|   linux: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v1 | ||||
|     - 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: | ||||
|   # | ||||
|   #   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_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 | ||||
|  | ||||
|     # Running the unittest does not work, the binary cannot be found | ||||
|     #- run: ../build/test/ixwebsocket_unittest.exe | ||||
|     # 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: | ||||
| -   repo: https://github.com/pre-commit/pre-commit-hooks | ||||
|     rev: v2.5.0 | ||||
|     rev: v2.3.0 | ||||
|     hooks: | ||||
|     -   id: check-yaml | ||||
|     -   id: end-of-file-fixer | ||||
|     -   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_library(JSONCPP_LIBRARY jsoncpp) | ||||
|  | ||||
| find_package_handle_standard_args(JsonCpp | ||||
| find_package_handle_standard_args(JSONCPP | ||||
|     FOUND_VAR | ||||
|       JSONCPP_FOUND | ||||
|     REQUIRED_VARS | ||||
|   | ||||
							
								
								
									
										140
									
								
								CMakeLists.txt
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								CMakeLists.txt
									
									
									
									
									
								
							| @@ -12,10 +12,6 @@ set (CMAKE_CXX_STANDARD 14) | ||||
| set (CXX_STANDARD_REQUIRED ON) | ||||
| set (CMAKE_CXX_EXTENSIONS OFF) | ||||
|  | ||||
| if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") | ||||
|   set(CMAKE_POSITION_INDEPENDENT_CODE ON) | ||||
| endif() | ||||
|  | ||||
| if (UNIX) | ||||
|   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") | ||||
| endif() | ||||
| @@ -48,11 +44,13 @@ set( IXWEBSOCKET_SOURCES | ||||
|     ixwebsocket/IXWebSocketCloseConstants.cpp | ||||
|     ixwebsocket/IXWebSocketHandshake.cpp | ||||
|     ixwebsocket/IXWebSocketHttpHeaders.cpp | ||||
|     ixwebsocket/IXWebSocketMessageQueue.cpp | ||||
|     ixwebsocket/IXWebSocketPerMessageDeflate.cpp | ||||
|     ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp | ||||
|     ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp | ||||
|     ixwebsocket/IXWebSocketServer.cpp | ||||
|     ixwebsocket/IXWebSocketTransport.cpp | ||||
|     ixwebsocket/LUrlParser.cpp | ||||
| ) | ||||
|  | ||||
| set( IXWEBSOCKET_HEADERS | ||||
| @@ -83,10 +81,10 @@ set( IXWEBSOCKET_HEADERS | ||||
|     ixwebsocket/IXWebSocketCloseInfo.h | ||||
|     ixwebsocket/IXWebSocketErrorInfo.h | ||||
|     ixwebsocket/IXWebSocketHandshake.h | ||||
|     ixwebsocket/IXWebSocketHandshakeKeyGen.h | ||||
|     ixwebsocket/IXWebSocketHttpHeaders.h | ||||
|     ixwebsocket/IXWebSocketInitResult.h | ||||
|     ixwebsocket/IXWebSocketMessage.h | ||||
|     ixwebsocket/IXWebSocketMessageQueue.h | ||||
|     ixwebsocket/IXWebSocketMessageType.h | ||||
|     ixwebsocket/IXWebSocketOpenInfo.h | ||||
|     ixwebsocket/IXWebSocketPerMessageDeflate.h | ||||
| @@ -96,6 +94,8 @@ set( IXWEBSOCKET_HEADERS | ||||
|     ixwebsocket/IXWebSocketServer.h | ||||
|     ixwebsocket/IXWebSocketTransport.h | ||||
|     ixwebsocket/IXWebSocketVersion.h | ||||
|     ixwebsocket/LUrlParser.h | ||||
|     ixwebsocket/libwshandshake.hpp | ||||
| ) | ||||
|  | ||||
| if (UNIX) | ||||
| @@ -113,38 +113,31 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") | ||||
|     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/freebsd/IXSetThreadName_freebsd.cpp) | ||||
| else() | ||||
|     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/linux/IXSetThreadName_linux.cpp) | ||||
|     list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptEventFd.cpp) | ||||
|     list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSelectInterruptEventFd.h) | ||||
| endif() | ||||
|  | ||||
| option(USE_TLS "Enable TLS support" FALSE) | ||||
|  | ||||
| if (USE_TLS) | ||||
|     # default to securetranport on Apple if nothing is configured | ||||
|     if (APPLE) | ||||
|       if (NOT USE_MBED_TLS AND NOT USE_OPEN_SSL) # unless we want something else | ||||
|         set(USE_SECURE_TRANSPORT ON) | ||||
|       endif() | ||||
|     option(USE_MBED_TLS "Use Mbed TLS" OFF) | ||||
|     option(USE_OPEN_SSL "Use OpenSSL" OFF) | ||||
|  | ||||
|     # default to mbedtls on windows if nothing is configured | ||||
|     elseif (WIN32) | ||||
|       if (NOT USE_OPEN_SSL) # unless we want something else | ||||
|         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() | ||||
|     if (WIN32 AND NOT USE_OPEN_SSL AND NOT USE_MBED_TLS) | ||||
|       option(USE_MBED_TLS "Use Mbed TLS" ON) | ||||
|     endif() | ||||
|  | ||||
|     if (USE_MBED_TLS) | ||||
|         list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketMbedTLS.h) | ||||
|         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_SOURCES ixwebsocket/IXSocketAppleSSL.cpp) | ||||
|     elseif (USE_OPEN_SSL) | ||||
|     else() | ||||
|         set(USE_OPEN_SSL ON) | ||||
|         list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSocketOpenSSL.h) | ||||
|         list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSocketOpenSSL.cpp) | ||||
|     else() | ||||
|         message(FATAL_ERROR "TLS Configuration error: unknown backend") | ||||
|     endif() | ||||
| endif() | ||||
|  | ||||
| @@ -153,82 +146,73 @@ add_library( ixwebsocket STATIC | ||||
|     ${IXWEBSOCKET_HEADERS} | ||||
| ) | ||||
|  | ||||
| add_library ( ixwebsocket::ixwebsocket ALIAS ixwebsocket ) | ||||
|  | ||||
| if (USE_TLS) | ||||
|     target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_TLS) | ||||
|     if (USE_MBED_TLS) | ||||
|         target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_MBED_TLS) | ||||
|     elseif (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() | ||||
|  | ||||
| if (USE_TLS) | ||||
|   if (USE_OPEN_SSL) | ||||
|     message(STATUS "TLS configured to use openssl") | ||||
| if (APPLE AND USE_TLS AND NOT USE_MBED_TLS AND NOT USE_OPEN_SSL) | ||||
|   target_link_libraries(ixwebsocket "-framework foundation" "-framework security") | ||||
| endif() | ||||
|  | ||||
|     # Help finding Homebrew's OpenSSL on macOS | ||||
|     if (APPLE) | ||||
|       set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/local/opt/openssl/lib) | ||||
|       set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /usr/local/opt/openssl/include) | ||||
| if (WIN32) | ||||
|   target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi) | ||||
|   add_definitions(-D_CRT_SECURE_NO_WARNINGS) | ||||
| endif() | ||||
|  | ||||
|       # This is for MacPort OpenSSL 1.0 | ||||
|       # set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /opt/local/lib/openssl-1.0) | ||||
|       # set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/local/include/openssl-1.0) | ||||
|     endif() | ||||
| if (UNIX) | ||||
|   find_package(Threads) | ||||
|   target_link_libraries(ixwebsocket ${CMAKE_THREAD_LIBS_INIT}) | ||||
| endif() | ||||
|  | ||||
|     # This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL | ||||
|     if (NOT OPENSSL_FOUND) | ||||
| 	    include(FindOpenSSL) | ||||
|     endif() | ||||
|     message(STATUS "OpenSSL: " ${OPENSSL_VERSION}) | ||||
| if (USE_TLS AND USE_OPEN_SSL) | ||||
|  | ||||
|     target_link_libraries(ixwebsocket PUBLIC OpenSSL::SSL OpenSSL::Crypto) | ||||
|   elseif (USE_MBED_TLS) | ||||
|     message(STATUS "TLS configured to use mbedtls") | ||||
|   # Help finding Homebrew's OpenSSL on macOS | ||||
|   if (APPLE) | ||||
|     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) | ||||
|     target_include_directories(ixwebsocket PUBLIC ${MBEDTLS_INCLUDE_DIRS}) | ||||
|     target_link_libraries(ixwebsocket PUBLIC ${MBEDTLS_LIBRARIES}) | ||||
|   elseif (USE_SECURE_TRANSPORT) | ||||
|     message(STATUS "TLS configured to use secure transport") | ||||
|     target_link_libraries(ixwebsocket PUBLIC "-framework foundation" "-framework security") | ||||
|     target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES}) | ||||
|   endif() | ||||
| endif() | ||||
|  | ||||
| # This ZLIB_FOUND check is to help find a cmake manually configured zlib | ||||
| if (NOT ZLIB_FOUND) | ||||
|   find_package(ZLIB) | ||||
| endif() | ||||
| if (ZLIB_FOUND) | ||||
|   include_directories(${ZLIB_INCLUDE_DIRS}) | ||||
|   target_link_libraries(ixwebsocket PUBLIC ${ZLIB_LIBRARIES}) | ||||
|   target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES}) | ||||
| else() | ||||
|   include_directories(third_party/zlib ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib) | ||||
|   add_subdirectory(third_party/zlib EXCLUDE_FROM_ALL) | ||||
|   target_link_libraries(ixwebsocket PRIVATE $<LINK_ONLY:zlibstatic>) | ||||
|   add_subdirectory(third_party/zlib) | ||||
|   target_link_libraries(ixwebsocket zlibstatic) | ||||
| 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 | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR} | ||||
| ) | ||||
| @@ -238,23 +222,15 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") | ||||
|     target_compile_options(ixwebsocket PRIVATE /MP) | ||||
| 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}") | ||||
|  | ||||
| install(TARGETS ixwebsocket EXPORT ixwebsocket | ||||
| 	ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib | ||||
| 	PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/include/ixwebsocket/ | ||||
| install(TARGETS ixwebsocket | ||||
|         ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib | ||||
|         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) | ||||
|   add_subdirectory(ixcore) | ||||
|   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. | ||||
|  | ||||
| - [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 \ | ||||
|     gcc g++ musl-dev linux-headers \ | ||||
|     cmake mbedtls-dev make zlib-dev ninja | ||||
|     cmake mbedtls-dev make zlib-dev | ||||
|  | ||||
| RUN addgroup -S app && \ | ||||
|     adduser -S -G app app && \ | ||||
|   | ||||
| @@ -1,118 +1,6 @@ | ||||
| # Changelog | ||||
| 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 | ||||
|  | ||||
| (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: | ||||
|  | ||||
| * `-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 | ||||
| * `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support (default on Windows) | ||||
| * `-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: | ||||
|  | ||||
| @@ -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. | ||||
|  | ||||
| ``` | ||||
| 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. | ||||
|   | ||||
| @@ -38,7 +38,8 @@ The regression test is running after each commit on github actions for multiple | ||||
|  | ||||
| ## 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. | ||||
| * 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. | ||||
|  | ||||
| Download the latest entry. | ||||
| Download the latest entry.  | ||||
|  | ||||
| ``` | ||||
| $ cd /tmp | ||||
|   | ||||
| @@ -447,7 +447,7 @@ Additional TLS options can be configured by passing a `ix::SocketTLSOptions` ins | ||||
| webSocket.setTLSOptions({ | ||||
|     .certFile = "path/to/cert/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 | ||||
| }); | ||||
| ``` | ||||
| @@ -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. | ||||
|  - 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. | ||||
|  - 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. | ||||
|  | ||||
|   | ||||
| @@ -4,19 +4,16 @@ | ||||
| # | ||||
|  | ||||
| set (IXBOTS_SOURCES | ||||
|     ixbots/IXCobraBot.cpp | ||||
|     ixbots/IXCobraToSentryBot.cpp | ||||
|     ixbots/IXCobraToStatsdBot.cpp | ||||
|     ixbots/IXCobraToStdoutBot.cpp | ||||
|     ixbots/IXQueueManager.cpp | ||||
|     ixbots/IXStatsdClient.cpp | ||||
| ) | ||||
|  | ||||
| set (IXBOTS_HEADERS | ||||
|     ixbots/IXCobraBot.h | ||||
|     ixbots/IXCobraBotConfig.h | ||||
|     ixbots/IXCobraToSentryBot.h | ||||
|     ixbots/IXCobraToStatsdBot.h | ||||
|     ixbots/IXCobraToStdoutBot.h | ||||
|     ixbots/IXQueueManager.h | ||||
|     ixbots/IXStatsdClient.h | ||||
| ) | ||||
|  | ||||
| @@ -30,6 +27,11 @@ if (NOT JSONCPP_FOUND) | ||||
|   set(JSONCPP_INCLUDE_DIRS ../third_party/jsoncpp) | ||||
| endif() | ||||
|  | ||||
| find_package(SpdLog) | ||||
| if (NOT SPDLOG_FOUND) | ||||
|   set(SPDLOG_INCLUDE_DIRS ../third_party/spdlog/include) | ||||
| endif() | ||||
|  | ||||
| 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 "IXCobraBot.h" | ||||
| #include <ixcobra/IXCobraConnection.h> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
| #include "IXQueueManager.h" | ||||
|  | ||||
| #include <chrono> | ||||
| #include <ixcobra/IXCobraConnection.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| #include <sstream> | ||||
| #include <thread> | ||||
| #include <vector> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     int64_t cobra_to_sentry_bot(const CobraBotConfig& config, | ||||
|                                 SentryClient& sentryClient, | ||||
|                                 bool verbose) | ||||
|     int cobra_to_sentry_bot(const CobraConfig& config, | ||||
|                             const std::string& channel, | ||||
|                             const std::string& filter, | ||||
|                             const std::string& position, | ||||
|                             SentryClient& sentryClient, | ||||
|                             bool verbose, | ||||
|                             bool strict, | ||||
|                             size_t maxQueueSize, | ||||
|                             bool enableHeartbeat, | ||||
|                             int runtime) | ||||
|     { | ||||
|         CobraBot bot; | ||||
|         bot.setOnBotMessageCallback([&sentryClient, &verbose](const Json::Value& msg, | ||||
|                                                     const std::string& /*position*/, | ||||
|                                                     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; | ||||
|                 } | ||||
|         ix::CobraConnection conn; | ||||
|         conn.configure(config); | ||||
|         conn.connect(); | ||||
|  | ||||
|                 if (response->statusCode == 200) | ||||
|                 { | ||||
|                     sentCount++; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode)); | ||||
|                     CoreLogger::error("Response: " + response->payload); | ||||
|         Json::FastWriter jsonWriter; | ||||
|         std::atomic<uint64_t> sentCount(0); | ||||
|         std::atomic<uint64_t> receivedCount(0); | ||||
|         std::atomic<bool> errorSending(false); | ||||
|         std::atomic<bool> stop(false); | ||||
|         std::atomic<bool> throttled(false); | ||||
|         std::atomic<bool> fatalCobraError(false); | ||||
|  | ||||
|                     // Error 429 Too Many Requests | ||||
|                     if (response->statusCode == 429) | ||||
|         QueueManager queueManager(maxQueueSize); | ||||
|  | ||||
|         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"]; | ||||
|                         std::stringstream ss; | ||||
|                         ss << retryAfter; | ||||
|                         int seconds; | ||||
|                         ss >> seconds; | ||||
|                         spdlog::warn("Null HTTP Response"); | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                         if (!ss.eof() || ss.fail()) | ||||
|                     if (verbose) | ||||
|                     { | ||||
|                         for (auto it : response->headers) | ||||
|                         { | ||||
|                             seconds = 30; | ||||
|                             CoreLogger::warn("Error parsing Retry-After header. " | ||||
|                                              "Using " + retryAfter + " for the sleep duration"); | ||||
|                             spdlog::info("{}: {}", it.first, it.second); | ||||
|                         } | ||||
|  | ||||
|                         CoreLogger::warn("Error 429 - Too Many Requests. ws will sleep " | ||||
|                                          "and retry after " + retryAfter + " seconds"); | ||||
|                         spdlog::info("Upload size: {}", response->uploadSize); | ||||
|                         spdlog::info("Download size: {}", response->downloadSize); | ||||
|  | ||||
|                         throttled = true; | ||||
|                         auto duration = std::chrono::seconds(seconds); | ||||
|                         std::this_thread::sleep_for(duration); | ||||
|                         throttled = false; | ||||
|                         spdlog::info("Status: {}", response->statusCode); | ||||
|                         if (response->errorCode != HttpErrorCode::Ok) | ||||
|                         { | ||||
|                             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 | ||||
|   | ||||
| @@ -5,14 +5,20 @@ | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include <cstdint> | ||||
| #include "IXCobraBotConfig.h" | ||||
| #include <ixcobra/IXCobraConfig.h> | ||||
| #include <ixsentry/IXSentryClient.h> | ||||
| #include <string> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     int64_t cobra_to_sentry_bot(const CobraBotConfig& config, | ||||
|                                 SentryClient& sentryClient, | ||||
|                                 bool verbose); | ||||
|     int cobra_to_sentry_bot(const CobraConfig& config, | ||||
|                             const std::string& channel, | ||||
|                             const std::string& filter, | ||||
|                             const std::string& position, | ||||
|                             SentryClient& sentryClient, | ||||
|                             bool verbose, | ||||
|                             bool strict, | ||||
|                             size_t maxQueueSize, | ||||
|                             bool enableHeartbeat, | ||||
|                             int runtime); | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -5,13 +5,16 @@ | ||||
|  */ | ||||
|  | ||||
| #include "IXCobraToStatsdBot.h" | ||||
|  | ||||
| #include "IXCobraBot.h" | ||||
| #include "IXQueueManager.h" | ||||
| #include "IXStatsdClient.h" | ||||
|  | ||||
| #include <atomic> | ||||
| #include <chrono> | ||||
| #include <condition_variable> | ||||
| #include <ixcobra/IXCobraConnection.h> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| #include <sstream> | ||||
| #include <thread> | ||||
| #include <vector> | ||||
|  | ||||
| namespace ix | ||||
| @@ -53,22 +56,84 @@ namespace ix | ||||
|         return val; | ||||
|     } | ||||
|  | ||||
|     int64_t cobra_to_statsd_bot(const ix::CobraBotConfig& config, | ||||
|                                 StatsdClient& statsdClient, | ||||
|                                 const std::string& fields, | ||||
|                                 const std::string& gauge, | ||||
|                                 const std::string& timer, | ||||
|                                 bool verbose) | ||||
|     int cobra_to_statsd_bot(const ix::CobraConfig& config, | ||||
|                             const std::string& channel, | ||||
|                             const std::string& filter, | ||||
|                             const std::string& position, | ||||
|                             StatsdClient& statsdClient, | ||||
|                             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); | ||||
|  | ||||
|         CobraBot bot; | ||||
|         bot.setOnBotMessageCallback( | ||||
|             [&statsdClient, &tokens, &gauge, &timer, &verbose](const Json::Value& msg, | ||||
|                                                      const std::string& /*position*/, | ||||
|                                                      std::atomic<bool>& /*throttled*/, | ||||
|                                                      std::atomic<bool>& fatalCobraError, | ||||
|                                                      std::atomic<uint64_t>& sentCount) -> void { | ||||
|         Json::FastWriter jsonWriter; | ||||
|         std::atomic<uint64_t> sentCount(0); | ||||
|         std::atomic<uint64_t> receivedCount(0); | ||||
|         std::atomic<bool> stop(false); | ||||
|         std::atomic<bool> fatalCobraError(false); | ||||
|  | ||||
|         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; | ||||
|                 for (auto&& attr : tokens) | ||||
|                 { | ||||
| @@ -109,14 +174,14 @@ namespace ix | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         CoreLogger::error("Gauge " + gauge + " is not a numeric type"); | ||||
|                         spdlog::error("Gauge {} is not a numberic type", gauge); | ||||
|                         fatalCobraError = true; | ||||
|                         return; | ||||
|                         break; | ||||
|                     } | ||||
|  | ||||
|                     if (verbose) | ||||
|                     { | ||||
|                         CoreLogger::info(id + " - " + attrName + " -> " + std::to_string(x)); | ||||
|                         spdlog::info("{} - {} -> {}", id, attrName, x); | ||||
|                     } | ||||
|  | ||||
|                     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 | ||||
|   | ||||
| @@ -5,18 +5,23 @@ | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include <cstdint> | ||||
| #include <ixcobra/IXCobraConfig.h> | ||||
| #include <ixbots/IXStatsdClient.h> | ||||
| #include "IXCobraBotConfig.h" | ||||
| #include <stddef.h> | ||||
| #include <string> | ||||
| #include <stddef.h> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     int64_t cobra_to_statsd_bot(const ix::CobraBotConfig& config, | ||||
|                                 StatsdClient& statsdClient, | ||||
|                                 const std::string& fields, | ||||
|                                 const std::string& gauge, | ||||
|                                 const std::string& timer, | ||||
|                                 bool verbose); | ||||
|     int cobra_to_statsd_bot(const ix::CobraConfig& config, | ||||
|                             const std::string& channel, | ||||
|                             const std::string& filter, | ||||
|                             const std::string& position, | ||||
|                             StatsdClient& statsdClient, | ||||
|                             const std::string& fields, | ||||
|                             const std::string& gauge, | ||||
|                             const std::string& timer, | ||||
|                             bool verbose, | ||||
|                             size_t maxQueueSize, | ||||
|                             bool enableHeartbeat, | ||||
|                             int runtime); | ||||
| } // 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 <iostream> | ||||
| #include <ixwebsocket/IXNetSystem.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <iostream> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     StatsdClient::StatsdClient(const std::string& host, int port, const std::string& prefix) | ||||
|         : _host(host) | ||||
|         , _port(port) | ||||
|         , _prefix(prefix) | ||||
|         , _stop(false) | ||||
|     StatsdClient::StatsdClient(const std::string& host, | ||||
|                                int port, | ||||
|                                const std::string& prefix) | ||||
|     : _host(host) | ||||
|       , _port(port) | ||||
|       , _prefix(prefix) | ||||
|       , _stop(false) | ||||
|     { | ||||
|         _thread = std::thread([this] { | ||||
|         _thread = std::thread([this] | ||||
|         { | ||||
|             while (!_stop) | ||||
|             { | ||||
|                 flushQueue(); | ||||
| @@ -116,8 +119,8 @@ namespace ix | ||||
|         cleanup(key); | ||||
|  | ||||
|         char buf[256]; | ||||
|         snprintf( | ||||
|             buf, sizeof(buf), "%s%s:%zd|%s\n", _prefix.c_str(), key.c_str(), value, type.c_str()); | ||||
|         snprintf(buf, sizeof(buf), "%s%s:%zd|%s\n", | ||||
|                  _prefix.c_str(), key.c_str(), value, type.c_str()); | ||||
|  | ||||
|         enqueue(buf); | ||||
|         return 0; | ||||
| @@ -139,7 +142,9 @@ namespace ix | ||||
|             auto ret = _socket.sendto(message); | ||||
|             if (ret != 0) | ||||
|             { | ||||
|                 std::cerr << "error: " << strerror(UdpSocket::getErrno()) << std::endl; | ||||
|                 std::cerr << "error: " | ||||
|                           << strerror(UdpSocket::getErrno()) | ||||
|                           << std::endl; | ||||
|             } | ||||
|             _queue.pop_front(); | ||||
|         } | ||||
|   | ||||
| @@ -6,20 +6,21 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <atomic> | ||||
| #include <deque> | ||||
| #include <ixwebsocket/IXUdpSocket.h> | ||||
| #include <mutex> | ||||
|  | ||||
| #include <string> | ||||
| #include <thread> | ||||
| #include <deque> | ||||
| #include <mutex> | ||||
| #include <atomic> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     class StatsdClient | ||||
|     { | ||||
|     public: | ||||
|         StatsdClient(const std::string& host = "127.0.0.1", | ||||
|                      int port = 8125, | ||||
|         StatsdClient(const std::string& host="127.0.0.1", | ||||
|                      int port=8125, | ||||
|                      const std::string& prefix = ""); | ||||
|         ~StatsdClient(); | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,6 @@ set (IXCOBRA_HEADERS | ||||
|     ixcobra/IXCobraMetricsThreadedPublisher.h | ||||
|     ixcobra/IXCobraMetricsPublisher.h | ||||
|     ixcobra/IXCobraConfig.h | ||||
|     ixcobra/IXCobraEventType.h | ||||
| ) | ||||
|  | ||||
| add_library(ixcobra STATIC | ||||
|   | ||||
| @@ -6,8 +6,8 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
| #include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h> | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|   | ||||
| @@ -5,17 +5,17 @@ | ||||
|  */ | ||||
|  | ||||
| #include "IXCobraConnection.h" | ||||
| #include <ixcrypto/IXHMac.h> | ||||
| #include <ixwebsocket/IXWebSocket.h> | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <cassert> | ||||
| #include <stdexcept> | ||||
| #include <cmath> | ||||
| #include <cassert> | ||||
| #include <cstring> | ||||
| #include <iostream> | ||||
| #include <ixcrypto/IXHMac.h> | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
| #include <ixwebsocket/IXWebSocket.h> | ||||
| #include <sstream> | ||||
| #include <stdexcept> | ||||
|  | ||||
|  | ||||
| namespace ix | ||||
| @@ -26,12 +26,12 @@ namespace ix | ||||
|     constexpr CobraConnection::MsgId CobraConnection::kInvalidMsgId; | ||||
|     constexpr int CobraConnection::kPingIntervalSecs; | ||||
|  | ||||
|     CobraConnection::CobraConnection() | ||||
|         : _webSocket(new WebSocket()) | ||||
|         , _publishMode(CobraConnection_PublishMode_Immediate) | ||||
|         , _authenticated(false) | ||||
|         , _eventCallback(nullptr) | ||||
|         , _id(1) | ||||
|     CobraConnection::CobraConnection() : | ||||
|         _webSocket(new WebSocket()), | ||||
|         _publishMode(CobraConnection_PublishMode_Immediate), | ||||
|         _authenticated(false), | ||||
|         _eventCallback(nullptr), | ||||
|         _id(1) | ||||
|     { | ||||
|         _pdu["action"] = "rtm/publish"; | ||||
|  | ||||
| @@ -87,7 +87,7 @@ namespace ix | ||||
|         _eventCallback = eventCallback; | ||||
|     } | ||||
|  | ||||
|     void CobraConnection::invokeEventCallback(ix::CobraEventType eventType, | ||||
|     void CobraConnection::invokeEventCallback(ix::CobraConnectionEventType eventType, | ||||
|                                               const std::string& errorMsg, | ||||
|                                               const WebSocketHttpHeaders& headers, | ||||
|                                               const std::string& subscriptionId, | ||||
| @@ -96,8 +96,7 @@ namespace ix | ||||
|         std::lock_guard<std::mutex> lock(_eventCallbackMutex); | ||||
|         if (_eventCallback) | ||||
|         { | ||||
|             _eventCallback( | ||||
|                 std::make_unique<CobraEvent>(eventType, errorMsg, headers, subscriptionId, msgId)); | ||||
|             _eventCallback(eventType, errorMsg, headers, subscriptionId, msgId); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -106,7 +105,7 @@ namespace ix | ||||
|     { | ||||
|         std::stringstream ss; | ||||
|         ss << errorMsg << " : received pdu => " << serializedPdu; | ||||
|         invokeEventCallback(ix::CobraEventType::Error, ss.str()); | ||||
|         invokeEventCallback(ix::CobraConnection_EventType_Error, ss.str()); | ||||
|     } | ||||
|  | ||||
|     void CobraConnection::disconnect() | ||||
| @@ -117,119 +116,126 @@ namespace ix | ||||
|  | ||||
|     void CobraConnection::initWebSocketOnMessageCallback() | ||||
|     { | ||||
|         _webSocket->setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) { | ||||
|             CobraConnection::invokeTrafficTrackerCallback(msg->wireSize, true); | ||||
|  | ||||
|             std::stringstream ss; | ||||
|             if (msg->type == ix::WebSocketMessageType::Open) | ||||
|         _webSocket->setOnMessageCallback( | ||||
|             [this](const ix::WebSocketMessagePtr& msg) | ||||
|             { | ||||
|                 invokeEventCallback(ix::CobraEventType::Open, std::string(), msg->openInfo.headers); | ||||
|                 sendHandshakeMessage(); | ||||
|             } | ||||
|             else if (msg->type == ix::WebSocketMessageType::Close) | ||||
|             { | ||||
|                 _authenticated = false; | ||||
|                 CobraConnection::invokeTrafficTrackerCallback(msg->wireSize, true); | ||||
|  | ||||
|                 std::stringstream ss; | ||||
|                 ss << "Close code " << msg->closeInfo.code; | ||||
|                 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)) | ||||
|                 if (msg->type == ix::WebSocketMessageType::Open) | ||||
|                 { | ||||
|                     invokeErrorCallback("Invalid json", msg->str); | ||||
|                     return; | ||||
|                     invokeEventCallback(ix::CobraConnection_EventType_Open, | ||||
|                                         std::string(), | ||||
|                                         msg->openInfo.headers); | ||||
|                     sendHandshakeMessage(); | ||||
|                 } | ||||
|  | ||||
|                 if (!data.isMember("action")) | ||||
|                 else if (msg->type == ix::WebSocketMessageType::Close) | ||||
|                 { | ||||
|                     invokeErrorCallback("Missing action", msg->str); | ||||
|                     return; | ||||
|                     _authenticated = false; | ||||
|  | ||||
|                     std::stringstream ss; | ||||
|                     ss << "Close code " << msg->closeInfo.code; | ||||
|                     ss << " reason " << msg->closeInfo.reason; | ||||
|                     invokeEventCallback(ix::CobraConnection_EventType_Closed, | ||||
|                                         ss.str()); | ||||
|                 } | ||||
|  | ||||
|                 auto action = data["action"].asString(); | ||||
|  | ||||
|                 if (action == "auth/handshake/ok") | ||||
|                 else if (msg->type == ix::WebSocketMessageType::Message) | ||||
|                 { | ||||
|                     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); | ||||
|                     } | ||||
|                 } | ||||
|                 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)) | ||||
|                     else if (action == "auth/authenticate/ok") | ||||
|                     { | ||||
|                         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)) | ||||
|                     { | ||||
|                         invokeErrorCallback("Error processing unsubscribe response", msg->str); | ||||
|                     } | ||||
|                     invokeEventCallback(ix::CobraConnection_EventType_Pong); | ||||
|                 } | ||||
|                 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; | ||||
|     } | ||||
|  | ||||
|     void CobraConnection::configure( | ||||
|         const std::string& appkey, | ||||
|         const std::string& endpoint, | ||||
|         const std::string& rolename, | ||||
|         const std::string& rolesecret, | ||||
|         const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions, | ||||
|         const SocketTLSOptions& socketTLSOptions) | ||||
|     void CobraConnection::configure(const std::string& appkey, | ||||
|                                     const std::string& endpoint, | ||||
|                                     const std::string& rolename, | ||||
|                                     const std::string& rolesecret, | ||||
|                                     const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions, | ||||
|                                     const SocketTLSOptions& socketTLSOptions) | ||||
|     { | ||||
|         _roleName = rolename; | ||||
|         _roleSecret = rolesecret; | ||||
| @@ -391,9 +396,8 @@ namespace ix | ||||
|  | ||||
|         if (!subscriptionId.isString()) return false; | ||||
|  | ||||
|         invokeEventCallback(ix::CobraEventType::Subscribed, | ||||
|                             std::string(), | ||||
|                             WebSocketHttpHeaders(), | ||||
|         invokeEventCallback(ix::CobraConnection_EventType_Subscribed, | ||||
|                             std::string(), WebSocketHttpHeaders(), | ||||
|                             subscriptionId.asString()); | ||||
|         return true; | ||||
|     } | ||||
| @@ -410,9 +414,8 @@ namespace ix | ||||
|  | ||||
|         if (!subscriptionId.isString()) return false; | ||||
|  | ||||
|         invokeEventCallback(ix::CobraEventType::UnSubscribed, | ||||
|                             std::string(), | ||||
|                             WebSocketHttpHeaders(), | ||||
|         invokeEventCallback(ix::CobraConnection_EventType_UnSubscribed, | ||||
|                             std::string(), WebSocketHttpHeaders(), | ||||
|                             subscriptionId.asString()); | ||||
|         return true; | ||||
|     } | ||||
| @@ -459,11 +462,9 @@ namespace ix | ||||
|  | ||||
|         uint64_t msgId = id.asUInt64(); | ||||
|  | ||||
|         invokeEventCallback(ix::CobraEventType::Published, | ||||
|                             std::string(), | ||||
|                             WebSocketHttpHeaders(), | ||||
|                             std::string(), | ||||
|                             msgId); | ||||
|         invokeEventCallback(ix::CobraConnection_EventType_Published, | ||||
|                             std::string(), WebSocketHttpHeaders(), | ||||
|                             std::string(), msgId); | ||||
|  | ||||
|         invokePublishTrackerCallback(false, true); | ||||
|  | ||||
| @@ -493,7 +494,9 @@ namespace ix | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|  | ||||
| @@ -659,7 +662,8 @@ namespace ix | ||||
|     bool CobraConnection::publishMessage(const std::string& serializedJson) | ||||
|     { | ||||
|         auto webSocketSendInfo = _webSocket->send(serializedJson); | ||||
|         CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false); | ||||
|         CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, | ||||
|                                                       false); | ||||
|         return webSocketSendInfo.success; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -6,19 +6,18 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "IXCobraConfig.h" | ||||
| #include "IXCobraEvent.h" | ||||
| #include "IXCobraEventType.h" | ||||
| #include <ixwebsocket/IXWebSocketHttpHeaders.h> | ||||
| #include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h> | ||||
| #include <json/json.h> | ||||
| #include <limits> | ||||
| #include <memory> | ||||
| #include <mutex> | ||||
| #include <queue> | ||||
| #include <string> | ||||
| #include <thread> | ||||
| #include <unordered_map> | ||||
| #include <limits> | ||||
|  | ||||
| #include "IXCobraConfig.h" | ||||
|  | ||||
| #ifdef max | ||||
| #undef max | ||||
| @@ -29,6 +28,21 @@ namespace ix | ||||
|     class WebSocket; | ||||
|     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 | ||||
|     { | ||||
|         CobraConnection_PublishMode_Immediate = 0, | ||||
| @@ -36,7 +50,11 @@ namespace ix | ||||
|     }; | ||||
|  | ||||
|     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 PublishTrackerCallback = std::function<void(bool sent, bool acked)>; | ||||
| @@ -120,9 +138,10 @@ namespace ix | ||||
|  | ||||
|         /// Prepare a message for transmission | ||||
|         /// (update the pdu, compute a msgId, serialize json to a string) | ||||
|         std::pair<CobraConnection::MsgId, std::string> prePublish(const Json::Value& channels, | ||||
|                                                                   const Json::Value& msg, | ||||
|                                                                   bool addToQueue); | ||||
|         std::pair<CobraConnection::MsgId, std::string> prePublish( | ||||
|             const Json::Value& channels, | ||||
|             const Json::Value& msg, | ||||
|             bool addToQueue); | ||||
|  | ||||
|         /// Attempt to send next message from the internal queue | ||||
|         bool publishNext(); | ||||
| @@ -152,7 +171,7 @@ namespace ix | ||||
|         static void invokePublishTrackerCallback(bool sent, bool acked); | ||||
|  | ||||
|         /// Invoke event callbacks | ||||
|         void invokeEventCallback(CobraEventType eventType, | ||||
|         void invokeEventCallback(CobraConnectionEventType eventType, | ||||
|                                  const std::string& errorMsg = std::string(), | ||||
|                                  const WebSocketHttpHeaders& headers = WebSocketHttpHeaders(), | ||||
|                                  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 <ixwebsocket/IXSocketTLSOptions.h> | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
| #include <stdexcept> | ||||
|  | ||||
|  | ||||
| @@ -17,8 +17,8 @@ namespace ix | ||||
|     const std::string CobraMetricsPublisher::kSetRateControlId = "sms_set_rate_control_id"; | ||||
|     const std::string CobraMetricsPublisher::kSetBlacklistId = "sms_set_blacklist_id"; | ||||
|  | ||||
|     CobraMetricsPublisher::CobraMetricsPublisher() | ||||
|         : _enabled(true) | ||||
|     CobraMetricsPublisher::CobraMetricsPublisher() : | ||||
|         _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 | ||||
|         _cobra_metrics_theaded_publisher.configure(config, channel); | ||||
| @@ -41,7 +42,7 @@ namespace ix | ||||
|     } | ||||
|  | ||||
|     void CobraMetricsPublisher::setGenericAttributes(const std::string& attrName, | ||||
|                                                      const Json::Value& value) | ||||
|                                                       const Json::Value& value) | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lock(_device_mutex); | ||||
|         _device[attrName] = value; | ||||
| @@ -106,7 +107,8 @@ namespace ix | ||||
|         auto last_update = _last_update.find(id); | ||||
|         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); | ||||
|     } | ||||
| @@ -121,7 +123,8 @@ namespace ix | ||||
|     { | ||||
|         auto now = std::chrono::system_clock::now(); | ||||
|         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; | ||||
|     } | ||||
| @@ -162,9 +165,10 @@ namespace ix | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     CobraConnection::MsgId CobraMetricsPublisher::push(const std::string& id, | ||||
|                                                        const Json::Value& data, | ||||
|                                                        bool shouldPushTest) | ||||
|     CobraConnection::MsgId CobraMetricsPublisher::push( | ||||
|         const std::string& id, | ||||
|         const Json::Value& data, | ||||
|         bool shouldPushTest) | ||||
|     { | ||||
|         if (shouldPushTest && !shouldPush(id)) return CobraConnection::kInvalidMsgId; | ||||
|  | ||||
|   | ||||
| @@ -40,7 +40,8 @@ namespace ix | ||||
|  | ||||
|         /// Configuration / set keys, etc... | ||||
|         /// 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. | ||||
|         /// 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 | ||||
|         /// 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. | ||||
|         CobraConnection::MsgId push(const std::string& id, | ||||
|                                     const Json::Value& data, | ||||
|                                     bool shouldPushTest = true); | ||||
|         CobraConnection::MsgId push(const std::string& id, const Json::Value& data, bool shouldPushTest = true); | ||||
|  | ||||
|         /// Interface used by lua. msg is a json encoded string. | ||||
|         CobraConnection::MsgId push(const std::string& id, | ||||
|                                     const std::string& data, | ||||
|                                     bool shouldPushTest = true); | ||||
|         CobraConnection::MsgId push(const std::string& id, const std::string& data, bool shouldPushTest = true); | ||||
|  | ||||
|         /// Tells whether a metric can be pushed. | ||||
|         /// 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); | ||||
|  | ||||
|         /// Set a unique id for the session. A uuid can be used. | ||||
|         void setSession(const std::string& session) | ||||
|         { | ||||
|             _session = session; | ||||
|         } | ||||
|         void setSession(const std::string& session) { _session = session; } | ||||
|  | ||||
|         /// Get the unique id used to identify the current session | ||||
|         const std::string& getSession() const | ||||
|         { | ||||
|             return _session; | ||||
|         } | ||||
|         const std::string& getSession() const { return _session; } | ||||
|  | ||||
|         /// Return the number of milliseconds since the epoch (~1970) | ||||
|         uint64_t getMillisecondsSinceEpoch() const; | ||||
|   | ||||
| @@ -5,77 +5,72 @@ | ||||
|  */ | ||||
|  | ||||
| #include "IXCobraMetricsThreadedPublisher.h" | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <cassert> | ||||
| #include <cmath> | ||||
| #include <iostream> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
| #include <ixwebsocket/IXSetThreadName.h> | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
| #include <sstream> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <stdexcept> | ||||
| #include <cmath> | ||||
| #include <cassert> | ||||
| #include <iostream> | ||||
| #include <sstream> | ||||
|  | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher() | ||||
|         : _stop(false) | ||||
|     CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher() : | ||||
|         _stop(false) | ||||
|     { | ||||
|         _cobra_connection.setEventCallback([](const CobraEventPtr& event) { | ||||
|             std::stringstream ss; | ||||
|  | ||||
|             if (event->type == ix::CobraEventType::Open) | ||||
|         _cobra_connection.setEventCallback( | ||||
|             [] | ||||
|             (ix::CobraConnectionEventType eventType, | ||||
|              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; | ||||
|                 } | ||||
|             } | ||||
|             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; | ||||
|             } | ||||
|                     ss << "Handshake headers" << std::endl; | ||||
|  | ||||
|             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, | ||||
|                                                   const std::string& channel) | ||||
|     { | ||||
|         CoreLogger::log(config.socketTLSOptions.getDescription().c_str()); | ||||
|         ix::IXCoreLogger::Log(config.socketTLSOptions.getDescription().c_str()); | ||||
|  | ||||
|         _channel = channel; | ||||
|         _cobra_connection.configure(config); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind) | ||||
| @@ -161,15 +157,13 @@ namespace ix | ||||
|                 { | ||||
|                     _cobra_connection.suspend(); | ||||
|                     continue; | ||||
|                 }; | ||||
|                 break; | ||||
|                 }; break; | ||||
|  | ||||
|                 case MessageKind::Resume: | ||||
|                 { | ||||
|                     _cobra_connection.resume(); | ||||
|                     continue; | ||||
|                 }; | ||||
|                 break; | ||||
|                 }; break; | ||||
|  | ||||
|                 case MessageKind::Message: | ||||
|                 { | ||||
| @@ -177,8 +171,7 @@ namespace ix | ||||
|                     { | ||||
|                         _cobra_connection.publishNext(); | ||||
|                     } | ||||
|                 }; | ||||
|                 break; | ||||
|                 }; break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -27,7 +27,8 @@ namespace ix | ||||
|         ~CobraMetricsThreadedPublisher(); | ||||
|  | ||||
|         /// 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 | ||||
|         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" | ||||
|  | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     // Default do a no-op logger | ||||
|     CoreLogger::LogFunc CoreLogger::_currentLogger = [](const char*, LogLevel) {}; | ||||
| // Default do nothing logger | ||||
| IXCoreLogger::LogFunc IXCoreLogger::_currentLogger = [](const char* /*msg*/){}; | ||||
|  | ||||
|     void CoreLogger::log(const char* msg, LogLevel level) | ||||
|     { | ||||
|         _currentLogger(msg, level); | ||||
|     } | ||||
| void IXCoreLogger::Log(const char* msg) | ||||
| { | ||||
|     _currentLogger(msg); | ||||
| } | ||||
|  | ||||
|     void CoreLogger::debug(const std::string& msg) | ||||
|     { | ||||
|         _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 | ||||
| } // ix | ||||
|   | ||||
| @@ -1,41 +1,15 @@ | ||||
| /* | ||||
|  *  IXCoreLogger.h | ||||
|  *  Author: Thomas Wells, Benjamin Sergeant | ||||
|  *  Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved. | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
| #include <functional> | ||||
| #include <string> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     enum class LogLevel | ||||
|     { | ||||
|         Debug = 0, | ||||
|         Info = 1, | ||||
|         Warning = 2, | ||||
|         Error = 3, | ||||
|         Critical = 4 | ||||
|     }; | ||||
|  | ||||
|     class CoreLogger | ||||
|     class IXCoreLogger | ||||
|     { | ||||
|     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 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; | ||||
|         } | ||||
|         static void setLogFunction(LogFunc& func) { _currentLogger = func; } | ||||
|  | ||||
|     private: | ||||
|         static LogFunc _currentLogger; | ||||
|   | ||||
| @@ -29,9 +29,10 @@ | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||||
|                                             "abcdefghijklmnopqrstuvwxyz" | ||||
|                                             "0123456789+/"; | ||||
|     static const std::string base64_chars = | ||||
|     "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||||
|     "abcdefghijklmnopqrstuvwxyz" | ||||
|     "0123456789+/"; | ||||
|  | ||||
|     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_4[4]; | ||||
|  | ||||
|         while (len--) | ||||
|         while(len--) | ||||
|         { | ||||
|             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[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[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]]; | ||||
|  | ||||
|                 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_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[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]]; | ||||
|  | ||||
|             while ((i++ < 3)) | ||||
|             while((i++ < 3)) | ||||
|                 ret += '='; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return ret; | ||||
| @@ -93,7 +95,7 @@ namespace ix | ||||
|  | ||||
|     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 j = 0; | ||||
|         int in_ = 0; | ||||
| @@ -101,42 +103,40 @@ namespace ix | ||||
|         std::string ret; | ||||
|         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_]; | ||||
|             in_++; | ||||
|             if (i == 4) | ||||
|             char_array_4[i++] = encoded_string[in_]; in_++; | ||||
|             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_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[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]; | ||||
|  | ||||
|                 i = 0; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (i) | ||||
|         if(i) | ||||
|         { | ||||
|             for (j = i; j < 4; j++) | ||||
|             for(j = i; j <4; j++) | ||||
|                 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_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[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; | ||||
|  | ||||
|             for (j = 0; (j < i - 1); j++) | ||||
|                 ret += char_array_3[j]; | ||||
|             for(j = 0; (j < i - 1); j++) ret += char_array_3[j]; | ||||
|         } | ||||
|  | ||||
|         return ret; | ||||
|     } | ||||
| } // namespace ix | ||||
| } | ||||
|   | ||||
| @@ -5,17 +5,16 @@ | ||||
|  */ | ||||
|  | ||||
| #include "IXHMac.h" | ||||
|  | ||||
| #include "IXBase64.h" | ||||
|  | ||||
| #if defined(IXCRYPTO_USE_MBED_TLS) | ||||
| #include <mbedtls/md.h> | ||||
| # include <mbedtls/md.h> | ||||
| #elif defined(__APPLE__) | ||||
| #include <CommonCrypto/CommonHMAC.h> | ||||
| # include <CommonCrypto/CommonHMAC.h> | ||||
| #elif defined(IXCRYPTO_USE_OPEN_SSL) | ||||
| #include <openssl/hmac.h> | ||||
| # include <openssl/hmac.h> | ||||
| #else | ||||
| #include <assert.h> | ||||
| # include <assert.h> | ||||
| #endif | ||||
|  | ||||
| namespace ix | ||||
| @@ -27,21 +26,19 @@ namespace ix | ||||
|  | ||||
| #if defined(IXCRYPTO_USE_MBED_TLS) | ||||
|         mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), | ||||
|                         (unsigned char*) key.c_str(), | ||||
|                         key.size(), | ||||
|                         (unsigned char*) data.c_str(), | ||||
|                         data.size(), | ||||
|                         (unsigned char*) &hash); | ||||
|                (unsigned char *) key.c_str(), key.size(), | ||||
|                (unsigned char *) data.c_str(), data.size(), | ||||
|                (unsigned char *) &hash); | ||||
| #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) | ||||
|         HMAC(EVP_md5(), | ||||
|              key.c_str(), | ||||
|              (int) key.size(), | ||||
|              (unsigned char*) data.c_str(), | ||||
|              (int) data.size(), | ||||
|              (unsigned char*) hash, | ||||
|              nullptr); | ||||
|              key.c_str(), (int) key.size(), | ||||
|              (unsigned char *) data.c_str(), (int) data.size(), | ||||
|              (unsigned char *) hash, nullptr); | ||||
| #else | ||||
|         assert(false && "hmac not implemented on this platform"); | ||||
| #endif | ||||
| @@ -50,4 +47,4 @@ namespace ix | ||||
|  | ||||
|         return base64_encode(hashString, (uint32_t) hashString.size()); | ||||
|     } | ||||
| } // namespace ix | ||||
| } | ||||
|   | ||||
| @@ -19,4 +19,4 @@ namespace ix | ||||
|  | ||||
|         return hashAddress; | ||||
|     } | ||||
| } // namespace ix | ||||
| } | ||||
|   | ||||
| @@ -16,23 +16,23 @@ | ||||
|  | ||||
| #include "IXUuid.h" | ||||
|  | ||||
| #include <iomanip> | ||||
| #include <random> | ||||
| #include <sstream> | ||||
| #include <string> | ||||
| #include <iomanip> | ||||
| #include <random> | ||||
|  | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     class Uuid | ||||
|     { | ||||
|     public: | ||||
|         Uuid(); | ||||
|         std::string toString() const; | ||||
|         public: | ||||
|             Uuid(); | ||||
|             std::string toString() const; | ||||
|  | ||||
|     private: | ||||
|         uint64_t _ab; | ||||
|         uint64_t _cd; | ||||
|         private: | ||||
|             uint64_t _ab; | ||||
|             uint64_t _cd; | ||||
|     }; | ||||
|  | ||||
|     Uuid::Uuid() | ||||
| @@ -60,7 +60,7 @@ namespace ix | ||||
|         ss << std::setw(8) << (a); | ||||
|         ss << std::setw(4) << (b >> 16); | ||||
|         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(8) << d; | ||||
|  | ||||
| @@ -72,4 +72,4 @@ namespace ix | ||||
|         Uuid id; | ||||
|         return id.toString(); | ||||
|     } | ||||
| } // namespace ix | ||||
| } | ||||
|   | ||||
| @@ -7,12 +7,12 @@ | ||||
| #include "IXSentryClient.h" | ||||
|  | ||||
| #include <chrono> | ||||
| #include <fstream> | ||||
| #include <iostream> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
| #include <fstream> | ||||
| #include <sstream> | ||||
| #include <ixwebsocket/IXWebSocketHttpHeaders.h> | ||||
| #include <ixwebsocket/IXWebSocketVersion.h> | ||||
| #include <sstream> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
|  | ||||
|  | ||||
| namespace ix | ||||
| @@ -64,8 +64,7 @@ namespace ix | ||||
|     std::string SentryClient::computeAuthHeader() | ||||
|     { | ||||
|         std::string securityHeader("Sentry sentry_version=5"); | ||||
|         securityHeader += ",sentry_client=ws/"; | ||||
|         securityHeader += std::string(IX_WEBSOCKET_VERSION); | ||||
|         securityHeader += ",sentry_client=ws/1.0.0"; | ||||
|         securityHeader += ",sentry_timestamp=" + std::to_string(SentryClient::getTimestamp()); | ||||
|         securityHeader += ",sentry_key=" + _publicKey; | ||||
|         securityHeader += ",sentry_secret=" + _secretKey; | ||||
| @@ -226,44 +225,44 @@ namespace ix | ||||
|         return _jsonWriter.write(payload); | ||||
|     } | ||||
|  | ||||
|     void SentryClient::send( | ||||
|         const Json::Value& msg, | ||||
|         bool verbose, | ||||
|         const OnResponseCallback& onResponseCallback) | ||||
|     std::pair<HttpResponsePtr, std::string> SentryClient::send(const Json::Value& msg, bool verbose) | ||||
|     { | ||||
|         auto args = _httpClient->createRequest(); | ||||
|         args->url = _url; | ||||
|         args->verb = HttpClient::kPost; | ||||
|         args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader(); | ||||
|         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->body = computePayload(msg); | ||||
|         args->logger = [](const std::string& msg) { ix::IXCoreLogger::Log(msg.c_str()); }; | ||||
|  | ||||
|         _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"); | ||||
|     std::string SentryClient::computeUrl(const std::string& project, const std::string& key) | ||||
|     { | ||||
|         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(); | ||||
|     } | ||||
|  | ||||
|     // | ||||
|     // curl -v -X POST -F upload_file_minidump=@ws/crash.dmp | ||||
|     // 'https://sentry.io/api/123456/minidump?sentry_key=12344567890' | ||||
|     // curl -v -X POST -F upload_file_minidump=@ws/crash.dmp 'https://sentry.io/api/123456/minidump?sentry_key=12344567890' | ||||
|     // | ||||
|     void SentryClient::uploadMinidump(const std::string& sentryMetadata, | ||||
|                                       const std::string& minidumpBytes, | ||||
|                                       const std::string& project, | ||||
|                                       const std::string& key, | ||||
|                                       bool verbose, | ||||
|                                       const OnResponseCallback& onResponseCallback) | ||||
|     void SentryClient::uploadMinidump( | ||||
|         const std::string& sentryMetadata, | ||||
|         const std::string& minidumpBytes, | ||||
|         const std::string& project, | ||||
|         const std::string& key, | ||||
|         bool verbose, | ||||
|         const OnResponseCallback& onResponseCallback) | ||||
|     { | ||||
|         std::string multipartBoundary = _httpClient->generateMultipartBoundary(); | ||||
|  | ||||
| @@ -274,7 +273,7 @@ namespace ix | ||||
|         args->followRedirects = true; | ||||
|         args->verbose = verbose; | ||||
|         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["upload_file_minidump"] = minidumpBytes; | ||||
| @@ -283,27 +282,7 @@ namespace ix | ||||
|         httpParameters["sentry"] = sentryMetadata; | ||||
|  | ||||
|         args->url = computeUrl(project, key); | ||||
|         args->body = _httpClient->serializeHttpFormDataParameters( | ||||
|             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); | ||||
|         args->body = _httpClient->serializeHttpFormDataParameters(multipartBoundary, httpFormDataParameters, httpParameters); | ||||
|  | ||||
|         _httpClient->performRequest(args, onResponseCallback); | ||||
|     } | ||||
|   | ||||
| @@ -10,8 +10,8 @@ | ||||
| #include <ixwebsocket/IXHttpClient.h> | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
| #include <json/json.h> | ||||
| #include <memory> | ||||
| #include <regex> | ||||
| #include <memory> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
| @@ -21,26 +21,20 @@ namespace ix | ||||
|         SentryClient(const std::string& dsn); | ||||
|         ~SentryClient() = default; | ||||
|  | ||||
|         void send(const Json::Value& msg, | ||||
|                   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); | ||||
|         std::pair<HttpResponsePtr, std::string> send(const Json::Value& msg, bool verbose); | ||||
|  | ||||
|         Json::Value parseLuaStackTrace(const std::string& stack); | ||||
|  | ||||
|         // Mostly for testing | ||||
|         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: | ||||
|         int64_t getTimestamp(); | ||||
|   | ||||
| @@ -6,10 +6,10 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
| #include <nlohmann/json.hpp> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
|  | ||||
| namespace snake | ||||
| { | ||||
|   | ||||
| @@ -28,7 +28,10 @@ namespace ix | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         CancellationRequest cancellationRequest = []() -> bool { return false; }; | ||||
|         CancellationRequest cancellationRequest = []() -> bool | ||||
|         { | ||||
|             return false; | ||||
|         }; | ||||
|  | ||||
|         std::string errMsg; | ||||
|         return _socket->connect(hostname, port, errMsg, cancellationRequest); | ||||
| @@ -249,8 +252,9 @@ namespace ix | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     std::string RedisClient::prepareXaddCommand(const std::string& stream, | ||||
|                                                 const std::string& message) | ||||
|     std::string RedisClient::prepareXaddCommand( | ||||
|         const std::string& stream, | ||||
|         const std::string& message) | ||||
|     { | ||||
|         std::stringstream ss; | ||||
|         ss << "*5\r\n"; | ||||
| @@ -324,9 +328,7 @@ namespace ix | ||||
|         return streamId; | ||||
|     } | ||||
|  | ||||
|     bool RedisClient::sendCommand(const std::string& commands, | ||||
|                                   int commandsCount, | ||||
|                                   std::string& errMsg) | ||||
|     bool RedisClient::sendCommand(const std::string& commands, int commandsCount, std::string& errMsg) | ||||
|     { | ||||
|         bool sent = _socket->writeBytes(commands, nullptr); | ||||
|         if (!sent) | ||||
|   | ||||
| @@ -8,10 +8,11 @@ | ||||
|  | ||||
| #include <atomic> | ||||
| #include <functional> | ||||
| #include <ixwebsocket/IXSocket.h> | ||||
| #include <memory> | ||||
| #include <string> | ||||
|  | ||||
| #include <ixwebsocket/IXSocket.h> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     class RedisClient | ||||
| @@ -38,11 +39,14 @@ namespace ix | ||||
|                        const OnRedisSubscribeCallback& callback); | ||||
|  | ||||
|         // XADD | ||||
|         std::string xadd(const std::string& channel, | ||||
|                          const std::string& message, | ||||
|                          std::string& errMsg); | ||||
|         std::string xadd( | ||||
|             const std::string& channel, | ||||
|             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); | ||||
|  | ||||
|   | ||||
| @@ -6,18 +6,17 @@ | ||||
|  | ||||
| #include "IXRedisServer.h" | ||||
|  | ||||
| #include <fstream> | ||||
| #include <ixwebsocket/IXCancellationRequest.h> | ||||
| #include <ixwebsocket/IXNetSystem.h> | ||||
| #include <ixwebsocket/IXSocket.h> | ||||
| #include <ixwebsocket/IXSocketConnect.h> | ||||
| #include <ixwebsocket/IXSocket.h> | ||||
| #include <ixwebsocket/IXCancellationRequest.h> | ||||
| #include <fstream> | ||||
| #include <sstream> | ||||
| #include <vector> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     RedisServer::RedisServer( | ||||
|         int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily) | ||||
|     RedisServer::RedisServer(int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily) | ||||
|         : SocketServer(port, host, backlog, maxConnections, addressFamily) | ||||
|         , _connectedClientsCount(0) | ||||
|         , _stopHandlingConnections(false) | ||||
| @@ -115,7 +114,8 @@ namespace ix | ||||
|         for (auto it : _subscribers) | ||||
|         { | ||||
|             std::stringstream ss; | ||||
|             ss << "Subscription id: " << it.first << " #subscribers: " << it.second.size(); | ||||
|             ss << "Subscription id: " << it.first | ||||
|                << " #subscribers: " << it.second.size(); | ||||
|  | ||||
|             logInfo(ss.str()); | ||||
|         } | ||||
| @@ -126,7 +126,8 @@ namespace ix | ||||
|         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; | ||||
|     } | ||||
| @@ -143,8 +144,9 @@ namespace ix | ||||
|         return ss.str(); | ||||
|     } | ||||
|  | ||||
|     bool RedisServer::parseRequest(std::unique_ptr<Socket>& socket, | ||||
|                                    std::vector<std::string>& tokens) | ||||
|     bool RedisServer::parseRequest( | ||||
|         std::unique_ptr<Socket>& socket, | ||||
|         std::vector<std::string>& tokens) | ||||
|     { | ||||
|         // Parse first line | ||||
|         auto cb = makeCancellationRequestWithTimeout(30, _stopHandlingConnections); | ||||
| @@ -188,8 +190,9 @@ namespace ix | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool RedisServer::handleCommand(std::unique_ptr<Socket>& socket, | ||||
|                                     const std::vector<std::string>& tokens) | ||||
|     bool RedisServer::handleCommand( | ||||
|         std::unique_ptr<Socket>& socket, | ||||
|         const std::vector<std::string>& tokens) | ||||
|     { | ||||
|         if (tokens.size() != 1) return false; | ||||
|  | ||||
| @@ -204,30 +207,31 @@ namespace ix | ||||
|         // | ||||
|         ss << "*6\r\n"; | ||||
|         ss << writeString("publish"); // 1 | ||||
|         ss << ":3\r\n";               // 2 | ||||
|         ss << "*0\r\n";               // 3 | ||||
|         ss << ":1\r\n";               // 4 | ||||
|         ss << ":2\r\n";               // 5 | ||||
|         ss << ":1\r\n";               // 6 | ||||
|         ss << ":3\r\n"; // 2 | ||||
|         ss << "*0\r\n"; // 3 | ||||
|         ss << ":1\r\n"; // 4 | ||||
|         ss << ":2\r\n"; // 5 | ||||
|         ss << ":1\r\n"; // 6 | ||||
|  | ||||
|         // | ||||
|         // subscribe | ||||
|         // | ||||
|         ss << "*6\r\n"; | ||||
|         ss << writeString("subscribe"); // 1 | ||||
|         ss << ":2\r\n";                 // 2 | ||||
|         ss << "*0\r\n";                 // 3 | ||||
|         ss << ":1\r\n";                 // 4 | ||||
|         ss << ":1\r\n";                 // 5 | ||||
|         ss << ":1\r\n";                 // 6 | ||||
|         ss << ":2\r\n"; // 2 | ||||
|         ss << "*0\r\n"; // 3 | ||||
|         ss << ":1\r\n"; // 4 | ||||
|         ss << ":1\r\n"; // 5 | ||||
|         ss << ":1\r\n"; // 6 | ||||
|  | ||||
|         socket->writeBytes(ss.str(), cb); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool RedisServer::handleSubscribe(std::unique_ptr<Socket>& socket, | ||||
|                                       const std::vector<std::string>& tokens) | ||||
|     bool RedisServer::handleSubscribe( | ||||
|         std::unique_ptr<Socket>& socket, | ||||
|         const std::vector<std::string>& tokens) | ||||
|     { | ||||
|         if (tokens.size() != 2) return false; | ||||
|  | ||||
| @@ -246,8 +250,9 @@ namespace ix | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool RedisServer::handlePublish(std::unique_ptr<Socket>& socket, | ||||
|                                     const std::vector<std::string>& tokens) | ||||
|     bool RedisServer::handlePublish( | ||||
|         std::unique_ptr<Socket>& socket, | ||||
|         const std::vector<std::string>& tokens) | ||||
|     { | ||||
|         if (tokens.size() != 3) return false; | ||||
|  | ||||
| @@ -276,7 +281,9 @@ namespace ix | ||||
|  | ||||
|         // return the number of clients that received the message. | ||||
|         std::stringstream ss; | ||||
|         ss << ":" << std::to_string(subscribers.size()) << "\r\n"; | ||||
|         ss << ":" | ||||
|            << std::to_string(subscribers.size()) | ||||
|            << "\r\n"; | ||||
|         socket->writeBytes(ss.str(), cb); | ||||
|  | ||||
|         return true; | ||||
|   | ||||
| @@ -6,13 +6,13 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "IXSocket.h" | ||||
| #include "IXSocketServer.h" | ||||
| #include "IXSocket.h" | ||||
| #include <functional> | ||||
| #include <map> | ||||
| #include <memory> | ||||
| #include <mutex> | ||||
| #include <set> | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include <thread> | ||||
| #include <utility> // pair | ||||
| @@ -50,14 +50,18 @@ namespace ix | ||||
|         bool startsWith(const std::string& str, const std::string& start); | ||||
|         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, | ||||
|                              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); | ||||
|     }; | ||||
|   | ||||
| @@ -10,9 +10,9 @@ | ||||
| #include "IXSnakeConnectionState.h" | ||||
| #include "nlohmann/json.hpp" | ||||
| #include <iostream> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
| #include <ixcrypto/IXHMac.h> | ||||
| #include <ixwebsocket/IXWebSocket.h> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
| #include <sstream> | ||||
|  | ||||
| namespace snake | ||||
| @@ -189,8 +189,7 @@ namespace snake | ||||
|             nlohmann::json response = { | ||||
|                 {"action", "rtm/subscription/data"}, | ||||
|                 {"id", id++}, | ||||
|                 {"body", | ||||
|                  {{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}}; | ||||
|                 {"body", {{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}}; | ||||
|  | ||||
|             ws->sendText(response.dump()); | ||||
|         }; | ||||
| @@ -198,7 +197,7 @@ namespace snake | ||||
|         auto responseCallback = [ws, pdu, &subscriptionId](const std::string& redisResponse) { | ||||
|             std::stringstream ss; | ||||
|             ss << "Redis Response: " << redisResponse << "..."; | ||||
|             ix::CoreLogger::log(ss.str().c_str()); | ||||
|             ix::IXCoreLogger::Log(ss.str().c_str()); | ||||
|  | ||||
|             // Success | ||||
|             nlohmann::json response = {{"action", "rtm/subscribe/ok"}, | ||||
| @@ -210,7 +209,7 @@ namespace snake | ||||
|         { | ||||
|             std::stringstream ss; | ||||
|             ss << "Subscribing to " << appChannel << "..."; | ||||
|             ix::CoreLogger::log(ss.str().c_str()); | ||||
|             ix::IXCoreLogger::Log(ss.str().c_str()); | ||||
|         } | ||||
|  | ||||
|         if (!redisClient.subscribe(appChannel, responseCallback, callback)) | ||||
| @@ -252,21 +251,7 @@ namespace snake | ||||
|                              const AppConfig& appConfig, | ||||
|                              const std::string& str) | ||||
|     { | ||||
|         nlohmann::json pdu; | ||||
|         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 pdu = nlohmann::json::parse(str); | ||||
|         auto action = pdu["action"]; | ||||
|  | ||||
|         if (action == "auth/handshake") | ||||
|   | ||||
| @@ -10,8 +10,8 @@ | ||||
| #include "IXSnakeConnectionState.h" | ||||
| #include "IXSnakeProtocol.h" | ||||
| #include <iostream> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
| #include <sstream> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
|  | ||||
|  | ||||
| namespace snake | ||||
| @@ -29,7 +29,7 @@ namespace snake | ||||
|  | ||||
|         std::stringstream ss; | ||||
|         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( | ||||
|                     [this, webSocket, state](const ix::WebSocketMessagePtr& msg) { | ||||
|                         std::stringstream ss; | ||||
|                         ix::LogLevel logLevel = ix::LogLevel::Debug; | ||||
|                         if (msg->type == ix::WebSocketMessageType::Open) | ||||
|                         { | ||||
|                             ss << "New connection" << std::endl; | ||||
| @@ -87,7 +86,6 @@ namespace snake | ||||
|                                                               _appConfig.redisPort)) | ||||
|                             { | ||||
|                                 ss << "Cannot connect to redis host" << std::endl; | ||||
|                                 logLevel = ix::LogLevel::Error; | ||||
|                             } | ||||
|                         } | ||||
|                         else if (msg->type == ix::WebSocketMessageType::Close) | ||||
| @@ -103,7 +101,6 @@ namespace snake | ||||
|                             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; | ||||
|                             logLevel = ix::LogLevel::Error; | ||||
|                         } | ||||
|                         else if (msg->type == ix::WebSocketMessageType::Fragment) | ||||
|                         { | ||||
| @@ -115,7 +112,7 @@ namespace snake | ||||
|                             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 <cassert> | ||||
| #include <chrono> | ||||
|  | ||||
| namespace ix | ||||
| @@ -14,8 +13,6 @@ namespace ix | ||||
|     CancellationRequest makeCancellationRequestWithTimeout( | ||||
|         int secs, std::atomic<bool>& requestInitCancellation) | ||||
|     { | ||||
|         assert(secs > 0); | ||||
|  | ||||
|         auto start = std::chrono::system_clock::now(); | ||||
|         auto timeout = std::chrono::seconds(secs); | ||||
|  | ||||
|   | ||||
| @@ -4,19 +4,6 @@ | ||||
|  *  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 "IXNetSystem.h" | ||||
|   | ||||
| @@ -78,12 +78,12 @@ namespace ix | ||||
|         WebSocketHttpHeaders extraHeaders; | ||||
|         std::string body; | ||||
|         std::string multipartBoundary; | ||||
|         int connectTimeout = 60; | ||||
|         int transferTimeout = 1800; | ||||
|         bool followRedirects = true; | ||||
|         int maxRedirects = 5; | ||||
|         bool verbose = false; | ||||
|         bool compress = true; | ||||
|         int connectTimeout; | ||||
|         int transferTimeout; | ||||
|         bool followRedirects; | ||||
|         int maxRedirects; | ||||
|         bool verbose; | ||||
|         bool compress; | ||||
|         Logger logger; | ||||
|         OnProgressCallback onProgressCallback; | ||||
|     }; | ||||
|   | ||||
| @@ -25,12 +25,10 @@ namespace ix | ||||
|     const std::string HttpClient::kHead = "HEAD"; | ||||
|     const std::string HttpClient::kDel = "DEL"; | ||||
|     const std::string HttpClient::kPut = "PUT"; | ||||
|     const std::string HttpClient::kPatch = "PATCH"; | ||||
|  | ||||
|     HttpClient::HttpClient(bool async) | ||||
|         : _async(async) | ||||
|         , _stop(false) | ||||
|         , _forceBody(false) | ||||
|     { | ||||
|         if (!_async) return; | ||||
|  | ||||
| @@ -51,11 +49,6 @@ namespace ix | ||||
|         _tlsOptions = tlsOptions; | ||||
|     } | ||||
|  | ||||
|     void HttpClient::setForceBody(bool value) | ||||
|     { | ||||
|         _forceBody = value; | ||||
|     } | ||||
|  | ||||
|     HttpRequestArgsPtr HttpClient::createRequest(const std::string& url, const std::string& verb) | ||||
|     { | ||||
|         auto request = std::make_shared<HttpRequestArgs>(); | ||||
| @@ -199,7 +192,7 @@ namespace ix | ||||
|             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"; | ||||
|  | ||||
| @@ -227,10 +220,11 @@ namespace ix | ||||
|  | ||||
|         std::string req(ss.str()); | ||||
|         std::string errMsg; | ||||
|         std::atomic<bool> requestInitCancellation(false); | ||||
|  | ||||
|         // Make a cancellation object dealing with connection timeout | ||||
|         auto isCancellationRequested = | ||||
|             makeCancellationRequestWithTimeout(args->connectTimeout, _stop); | ||||
|             makeCancellationRequestWithTimeout(args->connectTimeout, requestInitCancellation); | ||||
|  | ||||
|         bool success = _socket->connect(host, port, errMsg, isCancellationRequested); | ||||
|         if (!success) | ||||
| @@ -248,7 +242,8 @@ namespace ix | ||||
|         } | ||||
|  | ||||
|         // Make a new cancellation object dealing with transfer timeout | ||||
|         isCancellationRequested = makeCancellationRequestWithTimeout(args->transferTimeout, _stop); | ||||
|         isCancellationRequested = | ||||
|             makeCancellationRequestWithTimeout(args->transferTimeout, requestInitCancellation); | ||||
|  | ||||
|         if (args->verbose) | ||||
|         { | ||||
| @@ -567,20 +562,6 @@ namespace ix | ||||
|         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::ostringstream escaped; | ||||
|   | ||||
| @@ -46,19 +46,12 @@ namespace ix | ||||
|                             const std::string& body, | ||||
|                             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, | ||||
|                                 const std::string& verb, | ||||
|                                 const std::string& body, | ||||
|                                 HttpRequestArgsPtr args, | ||||
|                                 int redirects = 0); | ||||
|         void setForceBody(bool value); | ||||
|  | ||||
|         // Async API | ||||
|         HttpRequestArgsPtr createRequest(const std::string& url = std::string(), | ||||
|                                          const std::string& verb = HttpClient::kGet); | ||||
| @@ -85,7 +78,6 @@ namespace ix | ||||
|         const static std::string kHead; | ||||
|         const static std::string kDel; | ||||
|         const static std::string kPut; | ||||
|         const static std::string kPatch; | ||||
|  | ||||
|     private: | ||||
|         void log(const std::string& msg, HttpRequestArgsPtr args); | ||||
| @@ -94,6 +86,7 @@ namespace ix | ||||
|  | ||||
|         // Async API background thread runner | ||||
|         void run(); | ||||
|  | ||||
|         // Async API | ||||
|         bool _async; | ||||
|         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) | ||||
|  | ||||
|         SocketTLSOptions _tlsOptions; | ||||
|  | ||||
|         bool _forceBody; | ||||
|     }; | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -19,7 +19,6 @@ typedef unsigned long int nfds_t; | ||||
| #else | ||||
| #include <arpa/inet.h> | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <netdb.h> | ||||
| #include <netinet/in.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()) | ||||
|             { | ||||
|                 const std::string errorMsg("Cancellation Requested"); | ||||
|                 return std::make_pair(false, errorMsg); | ||||
|                 return std::make_pair(false, std::string()); | ||||
|             } | ||||
|  | ||||
|             size_t size = std::min(kChunkSize, length - output.size()); | ||||
| @@ -389,8 +388,7 @@ namespace ix | ||||
|             } | ||||
|             else if (ret <= 0 && !Socket::isWaitNeeded()) | ||||
|             { | ||||
|                 const std::string errorMsg("Recv Error"); | ||||
|                 return std::make_pair(false, errorMsg); | ||||
|                 return std::make_pair(false, std::string()); | ||||
|             } | ||||
|  | ||||
|             if (onProgressCallback) onProgressCallback((int) output.size(), (int) length); | ||||
| @@ -399,8 +397,7 @@ namespace ix | ||||
|             // This way we are not busy looping | ||||
|             if (isReadyToRead(1) == PollResultType::Error) | ||||
|             { | ||||
|                 const std::string errorMsg("Poll Error"); | ||||
|                 return std::make_pair(false, errorMsg); | ||||
|                 return std::make_pair(false, std::string()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,10 @@ | ||||
| /* | ||||
|  *  IXSocketAppleSSL.cpp | ||||
|  *  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. | ||||
|  */ | ||||
| #ifdef IXWEBSOCKET_USE_SECURE_TRANSPORT | ||||
|  | ||||
| #include "IXSocketAppleSSL.h" | ||||
|  | ||||
| #include "IXSocketConnect.h" | ||||
| @@ -309,5 +307,3 @@ namespace ix | ||||
|     } | ||||
|  | ||||
| } // namespace ix | ||||
|  | ||||
| #endif // IXWEBSOCKET_USE_SECURE_TRANSPORT | ||||
|   | ||||
| @@ -1,9 +1,8 @@ | ||||
| /* | ||||
|  *  IXSocketAppleSSL.h | ||||
|  *  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 | ||||
|  | ||||
| @@ -48,5 +47,3 @@ namespace ix | ||||
|     }; | ||||
|  | ||||
| } // namespace ix | ||||
|  | ||||
| #endif // IXWEBSOCKET_USE_SECURE_TRANSPORT | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| /* | ||||
|  *  IXSocketMbedTLS.cpp | ||||
|  *  Author: Benjamin Sergeant, Max Weisel | ||||
|  *  Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved. | ||||
|  *  Author: Benjamin Sergeant | ||||
|  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. | ||||
|  * | ||||
|  *  Some code taken from | ||||
|  *  https://github.com/rottor12/WsClientLib/blob/master/lib/src/WsClientLib.cpp | ||||
|  *  and mini_client.c example from mbedtls | ||||
|  */ | ||||
| #ifdef IXWEBSOCKET_USE_MBED_TLS | ||||
|  | ||||
| #include "IXSocketMbedTLS.h" | ||||
|  | ||||
| @@ -43,55 +42,6 @@ namespace ix | ||||
|         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) | ||||
|     { | ||||
|         initMBedTLS(); | ||||
| @@ -145,36 +95,18 @@ namespace ix | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // FIXME: should we call mbedtls_ssl_conf_verify ? | ||||
|             mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED); | ||||
|  | ||||
|             // FIXME: should we call mbedtls_ssl_conf_verify ? | ||||
|  | ||||
|             if (_tlsOptions.isUsingSystemDefaults()) | ||||
|             { | ||||
|                 if (!loadSystemCertificates(errMsg)) | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
|                 ; // FIXME | ||||
|             } | ||||
|             else | ||||
|             else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0) | ||||
|             { | ||||
|                 if (_tlsOptions.isUsingInMemoryCAs()) | ||||
|                 { | ||||
|                     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; | ||||
|                 } | ||||
|                 errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'"; | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL); | ||||
| @@ -348,5 +280,3 @@ namespace ix | ||||
|     } | ||||
|  | ||||
| } // namespace ix | ||||
|  | ||||
| #endif // IXWEBSOCKET_USE_MBED_TLS | ||||
|   | ||||
| @@ -1,9 +1,8 @@ | ||||
| /* | ||||
|  *  IXSocketMbedTLS.h | ||||
|  *  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 | ||||
|  | ||||
| @@ -52,9 +51,6 @@ namespace ix | ||||
|  | ||||
|         bool init(const std::string& host, bool isClient, std::string& errMsg); | ||||
|         void initMBedTLS(); | ||||
|         bool loadSystemCertificates(std::string& errMsg); | ||||
|     }; | ||||
|  | ||||
| } // namespace ix | ||||
|  | ||||
| #endif // IXWEBSOCKET_USE_MBED_TLS | ||||
|   | ||||
| @@ -1,11 +1,10 @@ | ||||
| /* | ||||
|  *  IXSocketOpenSSL.cpp | ||||
|  *  Author: Benjamin Sergeant, Matt DeBoer, Max Weisel | ||||
|  *  Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. | ||||
|  *  Author: Benjamin Sergeant, Matt DeBoer | ||||
|  *  Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. | ||||
|  * | ||||
|  *  Adapted from Satori SDK OpenSSL code. | ||||
|  */ | ||||
| #ifdef IXWEBSOCKET_USE_OPEN_SSL | ||||
|  | ||||
| #include "IXSocketOpenSSL.h" | ||||
|  | ||||
| @@ -33,8 +32,7 @@ namespace | ||||
|  | ||||
|         if (!systemStore) | ||||
|         { | ||||
|             errorMsg = "CertOpenStore failed with "; | ||||
|             errorMsg += std::to_string(GetLastError()); | ||||
|             errorMsg = "CertOpenStore failed with " errorMsg += std::to_string(GetLastError()); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
| @@ -85,8 +83,6 @@ namespace ix | ||||
|  | ||||
|     std::atomic<bool> SocketOpenSSL::_openSSLInitializationSuccessful(false); | ||||
|     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) | ||||
|         : Socket(fd) | ||||
| @@ -108,11 +104,6 @@ namespace ix | ||||
|         if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr)) return; | ||||
| #else | ||||
|         (void) OPENSSL_config(nullptr); | ||||
|  | ||||
|         if (CRYPTO_get_locking_callback() == nullptr) | ||||
|         { | ||||
|             CRYPTO_set_locking_callback(SocketOpenSSL::openSSLLockingCallback); | ||||
|         } | ||||
| #endif | ||||
|  | ||||
|         (void) OpenSSL_add_ssl_algorithms(); | ||||
| @@ -121,21 +112,6 @@ namespace ix | ||||
|         _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) | ||||
|     { | ||||
|         unsigned long e; | ||||
| @@ -217,66 +193,6 @@ namespace ix | ||||
|         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 | ||||
|      */ | ||||
| @@ -471,7 +387,7 @@ namespace ix | ||||
|             if (_tlsOptions.isUsingSystemDefaults()) | ||||
|             { | ||||
| #ifdef _WIN32 | ||||
|                 if (!loadWindowsSystemCertificates(_ssl_context, errMsg)) | ||||
|                 if (!loadWindowsSystemCertificates(_ssl_context)) | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
| @@ -485,32 +401,20 @@ namespace ix | ||||
|                 } | ||||
| #endif | ||||
|             } | ||||
|             else | ||||
|             else if (SSL_CTX_load_verify_locations( | ||||
|                          _ssl_context, _tlsOptions.caFile.c_str(), NULL) != 1) | ||||
|             { | ||||
|                 if (_tlsOptions.isUsingInMemoryCAs()) | ||||
|                 { | ||||
|                     // Load from memory | ||||
|                     openSSLAddCARootsFromString(_tlsOptions.caFile); | ||||
|                 } | ||||
|                 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); | ||||
|                 } | ||||
|                 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); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
| @@ -620,35 +524,26 @@ namespace ix | ||||
|                 } | ||||
|                 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 | ||||
|                         openSSLAddCARootsFromString(_tlsOptions.caFile); | ||||
|                         auto sslErr = ERR_get_error(); | ||||
|                         errMsg = "OpenSSL failed - SSL_load_client_CA_file('" + _tlsOptions.caFile + | ||||
|                                  "') failed: "; | ||||
|                         errMsg += ERR_error_string(sslErr, nullptr); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         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) | ||||
|                         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_load_client_CA_file('" + | ||||
|                                      _tlsOptions.caFile + "') failed: "; | ||||
|                             errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + | ||||
|                                      _tlsOptions.caFile + "\") failed: "; | ||||
|                             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 | ||||
|  | ||||
| #endif // IXWEBSOCKET_USE_OPEN_SSL | ||||
|   | ||||
| @@ -1,9 +1,8 @@ | ||||
| /* | ||||
|  *  IXSocketOpenSSL.h | ||||
|  *  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 | ||||
|  | ||||
| @@ -40,7 +39,6 @@ namespace ix | ||||
|         void openSSLInitialize(); | ||||
|         std::string getSSLError(int ret); | ||||
|         SSL_CTX* openSSLCreateContext(std::string& errMsg); | ||||
|         bool openSSLAddCARootsFromString(const std::string roots); | ||||
|         bool openSSLClientHandshake(const std::string& hostname, | ||||
|                                     std::string& errMsg, | ||||
|                                     const CancellationRequest& isCancellationRequested); | ||||
| @@ -49,9 +47,6 @@ namespace ix | ||||
|         bool handleTLSOptions(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_CTX* _ssl_context; | ||||
|         const SSL_METHOD* _ssl_method; | ||||
| @@ -61,9 +56,6 @@ namespace ix | ||||
|  | ||||
|         static std::once_flag _openSSLInitFlag; | ||||
|         static std::atomic<bool> _openSSLInitializationSuccessful; | ||||
|         static std::unique_ptr<std::mutex[]> _openSSLMutexes; | ||||
|     }; | ||||
|  | ||||
| } // namespace ix | ||||
|  | ||||
| #endif // IXWEBSOCKET_USE_OPEN_SSL | ||||
|   | ||||
| @@ -15,7 +15,6 @@ namespace ix | ||||
|     const char* kTLSCAFileUseSystemDefaults = "SYSTEM"; | ||||
|     const char* kTLSCAFileDisableVerify = "NONE"; | ||||
|     const char* kTLSCiphersUseDefault = "DEFAULT"; | ||||
|     const char* kTLSInMemoryMarker = "-----BEGIN CERTIFICATE-----"; | ||||
|  | ||||
|     bool SocketTLSOptions::isValid() const | ||||
|     { | ||||
| @@ -59,11 +58,6 @@ namespace ix | ||||
|         return caFile == kTLSCAFileUseSystemDefaults; | ||||
|     } | ||||
|  | ||||
|     bool SocketTLSOptions::isUsingInMemoryCAs() const | ||||
|     { | ||||
|         return caFile.find(kTLSInMemoryMarker) != std::string::npos; | ||||
|     } | ||||
|  | ||||
|     bool SocketTLSOptions::isPeerVerifyDisabled() const | ||||
|     { | ||||
|         return caFile == kTLSCAFileDisableVerify; | ||||
|   | ||||
| @@ -37,8 +37,6 @@ namespace ix | ||||
|  | ||||
|         bool isUsingSystemDefaults() const; | ||||
|  | ||||
|         bool isUsingInMemoryCAs() const; | ||||
|  | ||||
|         bool isPeerVerifyDisabled() const; | ||||
|  | ||||
|         bool isUsingDefaultCiphers() const; | ||||
|   | ||||
| @@ -44,18 +44,6 @@ namespace ix | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     bool UdpSocket::isWaitNeeded() | ||||
|     { | ||||
|         int err = getErrno(); | ||||
|  | ||||
|         if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS) | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void UdpSocket::closeSocket(int fd) | ||||
|     { | ||||
| #ifdef _WIN32 | ||||
| @@ -74,13 +62,6 @@ namespace ix | ||||
|             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)); | ||||
|         _server.sin_family = AF_INET; | ||||
|         _server.sin_port = htons(port); | ||||
| @@ -112,15 +93,4 @@ namespace ix | ||||
|         return (ssize_t)::sendto( | ||||
|             _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 | ||||
|   | ||||
| @@ -28,12 +28,9 @@ namespace ix | ||||
|         // Virtual methods | ||||
|         bool init(const std::string& host, int port, std::string& errMsg); | ||||
|         ssize_t sendto(const std::string& buffer); | ||||
|         ssize_t recvfrom(char* buffer, size_t length); | ||||
|  | ||||
|         void close(); | ||||
|  | ||||
|         static int getErrno(); | ||||
|         static bool isWaitNeeded(); | ||||
|         static void closeSocket(int fd); | ||||
|  | ||||
|     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 | ||||
|  *  Author: Benjamin Sergeant | ||||
|  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. | ||||
| @@ -31,308 +6,7 @@ | ||||
|  | ||||
| #include "IXUrlParser.h" | ||||
|  | ||||
| #include <algorithm> | ||||
| #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 | ||||
| #include "LUrlParser.h" | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
| @@ -343,7 +17,7 @@ namespace ix | ||||
|                           std::string& query, | ||||
|                           int& port) | ||||
|     { | ||||
|         clParseURL res = clParseURL::ParseURL(url); | ||||
|         LUrlParser::clParseURL res = LUrlParser::clParseURL::ParseURL(url); | ||||
|  | ||||
|         if (!res.IsValid()) | ||||
|         { | ||||
|   | ||||
| @@ -34,7 +34,7 @@ namespace ix | ||||
|         _ws.setOnCloseCallback( | ||||
|             [this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) { | ||||
|                 _onMessageCallback( | ||||
|                     std::make_unique<WebSocketMessage>(WebSocketMessageType::Close, | ||||
|                     std::make_shared<WebSocketMessage>(WebSocketMessageType::Close, | ||||
|                                                        "", | ||||
|                                                        wireSize, | ||||
|                                                        WebSocketErrorInfo(), | ||||
| @@ -193,7 +193,7 @@ namespace ix | ||||
|             return status; | ||||
|         } | ||||
|  | ||||
|         _onMessageCallback(std::make_unique<WebSocketMessage>( | ||||
|         _onMessageCallback(std::make_shared<WebSocketMessage>( | ||||
|             WebSocketMessageType::Open, | ||||
|             "", | ||||
|             0, | ||||
| @@ -225,7 +225,7 @@ namespace ix | ||||
|         } | ||||
|  | ||||
|         _onMessageCallback( | ||||
|             std::make_unique<WebSocketMessage>(WebSocketMessageType::Open, | ||||
|             std::make_shared<WebSocketMessage>(WebSocketMessageType::Open, | ||||
|                                                "", | ||||
|                                                0, | ||||
|                                                WebSocketErrorInfo(), | ||||
| @@ -310,7 +310,7 @@ namespace ix | ||||
|                 connectErr.reason = status.errorStr; | ||||
|                 connectErr.http_status = status.http_status; | ||||
|  | ||||
|                 _onMessageCallback(std::make_unique<WebSocketMessage>(WebSocketMessageType::Error, | ||||
|                 _onMessageCallback(std::make_shared<WebSocketMessage>(WebSocketMessageType::Error, | ||||
|                                                                       "", | ||||
|                                                                       0, | ||||
|                                                                       connectErr, | ||||
| @@ -386,7 +386,7 @@ namespace ix | ||||
|  | ||||
|                     bool binary = messageKind == WebSocketTransport::MessageKind::MSG_BINARY; | ||||
|  | ||||
|                     _onMessageCallback(std::make_unique<WebSocketMessage>(webSocketMessageType, | ||||
|                     _onMessageCallback(std::make_shared<WebSocketMessage>(webSocketMessageType, | ||||
|                                                                           msg, | ||||
|                                                                           wireSize, | ||||
|                                                                           webSocketErrorInfo, | ||||
|   | ||||
| @@ -6,9 +6,6 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <cstdint> | ||||
| #include <string> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     struct WebSocketCloseInfo | ||||
|   | ||||
| @@ -6,7 +6,6 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <cstdint> | ||||
| #include <string> | ||||
|  | ||||
| namespace ix | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| #include "IXSocketConnect.h" | ||||
| #include "IXUrlParser.h" | ||||
| #include "IXUserAgent.h" | ||||
| #include "IXWebSocketHandshakeKeyGen.h" | ||||
| #include "libwshandshake.hpp" | ||||
| #include <algorithm> | ||||
| #include <iostream> | ||||
| #include <random> | ||||
| @@ -133,7 +133,7 @@ namespace ix | ||||
|  | ||||
|         for (auto& it : extraHeaders) | ||||
|         { | ||||
|             ss << it.first << ": " << it.second << "\r\n"; | ||||
|             ss << it.first << ":" << it.second << "\r\n"; | ||||
|         } | ||||
|  | ||||
|         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 | ||||
|     { | ||||
|         WebSocketMessageType type; | ||||
|         const std::string& str; | ||||
|         std::string str; | ||||
|         size_t wireSize; | ||||
|         WebSocketErrorInfo errorInfo; | ||||
|         WebSocketOpenInfo openInfo; | ||||
| @@ -34,7 +34,7 @@ namespace ix | ||||
|                          WebSocketCloseInfo c, | ||||
|                          bool b = false) | ||||
|             : type(t) | ||||
|             , str(s) | ||||
|             , str(std::move(s)) | ||||
|             , wireSize(w) | ||||
|             , errorInfo(e) | ||||
|             , openInfo(o) | ||||
| @@ -45,5 +45,5 @@ namespace ix | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     using WebSocketMessagePtr = std::unique_ptr<WebSocketMessage>; | ||||
|     using WebSocketMessagePtr = std::shared_ptr<WebSocketMessage>; | ||||
| } // 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 | ||||
|  | ||||
| #include "IXWebSocketHttpHeaders.h" | ||||
| #include <cstdint> | ||||
| #include <string> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     struct WebSocketOpenInfo | ||||
|   | ||||
| @@ -87,9 +87,6 @@ namespace ix | ||||
|         // | ||||
|         size_t output; | ||||
|  | ||||
|         // Clear output | ||||
|         out.clear(); | ||||
|  | ||||
|         if (in.empty()) | ||||
|         { | ||||
|             // See issue #167 | ||||
| @@ -177,9 +174,6 @@ namespace ix | ||||
|         _inflateState.avail_in = (uInt) inFixed.size(); | ||||
|         _inflateState.next_in = (unsigned char*) (const_cast<char*>(inFixed.data())); | ||||
|  | ||||
|         // Clear output | ||||
|         out.clear(); | ||||
|  | ||||
|         do | ||||
|         { | ||||
|             _inflateState.avail_out = (uInt) _compressBufferSize; | ||||
|   | ||||
| @@ -62,7 +62,7 @@ namespace ix | ||||
|     WebSocketTransport::WebSocketTransport() | ||||
|         : _useMask(true) | ||||
|         , _blockingSend(false) | ||||
|         , _receivedMessageCompressed(false) | ||||
|         , _compressedMessage(false) | ||||
|         , _readyState(ReadyState::CLOSED) | ||||
|         , _closeCode(WebSocketCloseConstants::kInternalErrorCode) | ||||
|         , _closeReason(WebSocketCloseConstants::kInternalErrorMessage) | ||||
| @@ -74,7 +74,6 @@ namespace ix | ||||
|         , _enablePong(kDefaultEnablePong) | ||||
|         , _pingIntervalSecs(kDefaultPingIntervalSecs) | ||||
|         , _pongReceived(false) | ||||
|         , _pingCount(0) | ||||
|         , _lastSendPingTimePoint(std::chrono::steady_clock::now()) | ||||
|     { | ||||
|         _readbuf.resize(kChunkSize); | ||||
| @@ -222,8 +221,7 @@ namespace ix | ||||
|     { | ||||
|         _pongReceived = false; | ||||
|         std::stringstream ss; | ||||
|         ss << kPingMessage << "::" << _pingIntervalSecs << "s" | ||||
|            << "::" << _pingCount++; | ||||
|         ss << kPingMessage << "::" << _pingIntervalSecs << "s"; | ||||
|         return sendPing(ss.str()); | ||||
|     } | ||||
|  | ||||
| @@ -495,7 +493,7 @@ namespace ix | ||||
|                                                  ? MessageKind::MSG_TEXT | ||||
|                                                  : MessageKind::MSG_BINARY; | ||||
|  | ||||
|                     _receivedMessageCompressed = _enablePerMessageDeflate && ws.rsv1; | ||||
|                     _compressedMessage = _enablePerMessageDeflate && ws.rsv1; | ||||
|  | ||||
|                     // Continuation message needs to follow a non-fin TEXT or BINARY message | ||||
|                     if (!_chunks.empty()) | ||||
| @@ -517,12 +515,10 @@ namespace ix | ||||
|                 // | ||||
|                 if (ws.fin && _chunks.empty()) | ||||
|                 { | ||||
|                     emitMessage(_fragmentedMessageKind, | ||||
|                                 frameData, | ||||
|                                 _receivedMessageCompressed, | ||||
|                                 onMessageCallback); | ||||
|                     emitMessage( | ||||
|                         _fragmentedMessageKind, frameData, _compressedMessage, onMessageCallback); | ||||
|  | ||||
|                     _receivedMessageCompressed = false; | ||||
|                     _compressedMessage = false; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
| @@ -539,11 +535,11 @@ namespace ix | ||||
|                     { | ||||
|                         emitMessage(_fragmentedMessageKind, | ||||
|                                     getMergedChunks(), | ||||
|                                     _receivedMessageCompressed, | ||||
|                                     _compressedMessage, | ||||
|                                     onMessageCallback); | ||||
|  | ||||
|                         _chunks.clear(); | ||||
|                         _receivedMessageCompressed = false; | ||||
|                         _compressedMessage = false; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
| @@ -716,16 +712,17 @@ namespace ix | ||||
|         // When the RSV1 bit is 1 it means the message is compressed | ||||
|         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, | ||||
|                       WebSocketCloseConstants::kInvalidFramePayloadDataMessage); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 onMessageCallback(_decompressedMessage, wireSize, !success, messageKind); | ||||
|                 onMessageCallback(decompressedMessage, wireSize, !success, messageKind); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
| @@ -762,6 +759,7 @@ namespace ix | ||||
|  | ||||
|         size_t payloadSize = message.size(); | ||||
|         size_t wireSize = message.size(); | ||||
|         std::string compressedMessage; | ||||
|         bool compressionError = false; | ||||
|  | ||||
|         std::string::const_iterator message_begin = message.begin(); | ||||
| @@ -769,7 +767,7 @@ namespace ix | ||||
|  | ||||
|         if (compress) | ||||
|         { | ||||
|             if (!_perMessageDeflate->compress(message, _compressedMessage)) | ||||
|             if (!_perMessageDeflate->compress(message, compressedMessage)) | ||||
|             { | ||||
|                 bool success = false; | ||||
|                 compressionError = true; | ||||
| @@ -778,10 +776,10 @@ namespace ix | ||||
|                 return WebSocketSendInfo(success, compressionError, payloadSize, wireSize); | ||||
|             } | ||||
|             compressionError = false; | ||||
|             wireSize = _compressedMessage.size(); | ||||
|             wireSize = compressedMessage.size(); | ||||
|  | ||||
|             message_begin = _compressedMessage.begin(); | ||||
|             message_end = _compressedMessage.end(); | ||||
|             message_begin = compressedMessage.begin(); | ||||
|             message_end = compressedMessage.end(); | ||||
|         } | ||||
|  | ||||
|         { | ||||
|   | ||||
| @@ -165,7 +165,7 @@ namespace ix | ||||
|         MessageKind _fragmentedMessageKind; | ||||
|  | ||||
|         // Ditto for whether a message is compressed | ||||
|         bool _receivedMessageCompressed; | ||||
|         bool _compressedMessage; | ||||
|  | ||||
|         // Fragments are 32K long | ||||
|         static constexpr size_t kChunkSize = 1 << 15; | ||||
| @@ -189,9 +189,6 @@ namespace ix | ||||
|         WebSocketPerMessageDeflateOptions _perMessageDeflateOptions; | ||||
|         std::atomic<bool> _enablePerMessageDeflate; | ||||
|  | ||||
|         std::string _decompressedMessage; | ||||
|         std::string _compressedMessage; | ||||
|  | ||||
|         // Used to control TLS connection behavior | ||||
|         SocketTLSOptions _socketTLSOptions; | ||||
|  | ||||
| @@ -212,7 +209,6 @@ namespace ix | ||||
|  | ||||
|         static const int kDefaultPingIntervalSecs; | ||||
|         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 | ||||
|         mutable std::mutex _lastSendPingTimePointMutex; | ||||
|   | ||||
| @@ -6,4 +6,4 @@ | ||||
|  | ||||
| #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 | ||||
| # | ||||
| 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 | ||||
| # server side ?) and I can't work-around it easily, so we're using mbedtls on | ||||
| # Linux for the SSL backend, which works great. | ||||
| 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: | ||||
| 	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=heartbeat make 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) | ||||
| 	(cd test ; python2.7 run.py -r) | ||||
|  | ||||
| @@ -120,12 +116,10 @@ test_ubsan: | ||||
| 	(cd build/test ; ln -sf Debug/ixwebsocket_unittest) | ||||
| 	(cd test ; python2.7 run.py -r) | ||||
|  | ||||
| test_asan: build_test_asan | ||||
| 	(cd test ; python2.7 run.py -r) | ||||
|  | ||||
| build_test_asan: | ||||
| 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) | ||||
| 	(cd build/test ; ln -sf Debug/ixwebsocket_unittest) | ||||
| 	(cd test ; python2.7 run.py -r) | ||||
|  | ||||
| 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) | ||||
| @@ -148,7 +142,7 @@ test_tsan_mbedtls: | ||||
| 	(cd test ; python2.7 run.py -r) | ||||
|  | ||||
| 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 | ||||
| 	(cd test ; python2.7 run.py -r) | ||||
|   | ||||
| @@ -66,7 +66,13 @@ if (UNIX) | ||||
|     IXCobraMetricsPublisherTest.cpp | ||||
|     IXCobraToSentryBotTest.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() | ||||
|  | ||||
|   | ||||
| @@ -180,40 +180,44 @@ namespace | ||||
|         _conn.configure(_cobraConfig); | ||||
|         _conn.connect(); | ||||
|  | ||||
|         _conn.setEventCallback([this, channel](const CobraEventPtr& event) { | ||||
|             if (event->type == ix::CobraEventType::Open) | ||||
|         _conn.setEventCallback([this, channel](ix::CobraConnectionEventType eventType, | ||||
|                                                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); | ||||
|                 for (auto&& it : event->headers) | ||||
|                 for (auto&& it : headers) | ||||
|                 { | ||||
|                     log("Headers " + it.first + " " + it.second); | ||||
|                 } | ||||
|             } | ||||
|             else if (event->type == ix::CobraEventType::Authenticated) | ||||
|             else if (eventType == ix::CobraConnection_EventType_Authenticated) | ||||
|             { | ||||
|                 log("Subscriber authenticated: " + _user); | ||||
|                 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); | ||||
|             } | ||||
|             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; | ||||
|             } | ||||
|             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); | ||||
|         _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 | ||||
|  | ||||
|   | ||||
| @@ -54,24 +54,24 @@ namespace | ||||
|         conn.configure(config); | ||||
|         conn.connect(); | ||||
|  | ||||
|         conn.setEventCallback([&conn, &channel](const CobraEventPtr& event) { | ||||
|             if (event->type == ix::CobraEventType::Open) | ||||
|         conn.setEventCallback([&conn, &channel](ix::CobraConnectionEventType eventType, | ||||
|                                                 const std::string& errMsg, | ||||
|                                                 const ix::WebSocketHttpHeaders& headers, | ||||
|                                                 const std::string& subscriptionId, | ||||
|                                                 CobraConnection::MsgId msgId) { | ||||
|             if (eventType == ix::CobraConnection_EventType_Open) | ||||
|             { | ||||
|                 TLogger() << "Subscriber connected:"; | ||||
|                 for (auto&& it : event->headers) | ||||
|                 for (auto&& it : headers) | ||||
|                 { | ||||
|                     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) | ||||
|             { | ||||
|                 TLogger() << "Subscriber error:" << event->errMsg; | ||||
|             } | ||||
|             else if (event->type == ix::CobraEventType::Authenticated) | ||||
|             else if (eventType == ix::CobraConnection_EventType_Authenticated) | ||||
|             { | ||||
|                 log("Subscriber authenticated"); | ||||
|                 std::string filter; | ||||
| @@ -92,29 +92,29 @@ namespace | ||||
|                                    gMessageCount++; | ||||
|                                }); | ||||
|             } | ||||
|             else if (event->type == ix::CobraEventType::Subscribed) | ||||
|             else if (eventType == ix::CobraConnection_EventType_Subscribed) | ||||
|             { | ||||
|                 TLogger() << "Subscriber: subscribed to channel " << event->subscriptionId; | ||||
|                 if (event->subscriptionId == channel) | ||||
|                 TLogger() << "Subscriber: subscribed to channel " << subscriptionId; | ||||
|                 if (subscriptionId == channel) | ||||
|                 { | ||||
|                     gSubscriberConnectedAndSubscribed = true; | ||||
|                 } | ||||
|                 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; | ||||
|                 if (event->subscriptionId != channel) | ||||
|                 TLogger() << "Subscriber: ununexpected from channel " << subscriptionId; | ||||
|                 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); | ||||
|  | ||||
|         ix::CobraBotConfig cobraBotConfig; | ||||
|         cobraBotConfig.cobraConfig = config; | ||||
|         cobraBotConfig.channel = channel; | ||||
|         cobraBotConfig.runtime = 3; // Only run the bot for 3 seconds | ||||
|         cobraBotConfig.enableHeartbeat = false; | ||||
|         std::string filter; | ||||
|         std::string position("$"); | ||||
|         bool verbose = true; | ||||
|         bool strict = true; | ||||
|         size_t maxQueueSize = 10; | ||||
|         bool enableHeartbeat = false; | ||||
|  | ||||
|         // FIXME: try to get this working with https instead of http | ||||
|         //        to regress the TLS 1.3 OpenSSL bug | ||||
| @@ -158,7 +158,19 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]") | ||||
|         SentryClient sentryClient(dsn); | ||||
|         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 | ||||
|         // | ||||
|   | ||||
| @@ -87,11 +87,14 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]") | ||||
|  | ||||
|         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; | ||||
|         std::string filter; | ||||
|         std::string position("$"); | ||||
|         bool verbose = true; | ||||
|         size_t maxQueueSize = 10; | ||||
|         bool enableHeartbeat = false; | ||||
|  | ||||
|         // Only run the bot for 3 seconds | ||||
|         int runtime = 3; | ||||
|  | ||||
|         std::string hostname("127.0.0.1"); | ||||
|         // 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 gauge; | ||||
|         std::string timer; | ||||
|         bool verbose = true; | ||||
|  | ||||
|         int64_t sentCount = | ||||
|             ix::cobra_to_statsd_bot(cobraBotConfig, statsdClient, fields, gauge, timer, verbose); | ||||
|         int sentCount = ix::cobra_to_statsd_bot(config, | ||||
|                                                 channel, | ||||
|                                                 filter, | ||||
|                                                 position, | ||||
|                                                 statsdClient, | ||||
|                                                 fields, | ||||
|                                                 gauge, | ||||
|                                                 timer, | ||||
|                                                 verbose, | ||||
|                                                 maxQueueSize, | ||||
|                                                 enableHeartbeat, | ||||
|                                                 runtime); | ||||
|         // | ||||
|         // 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. | ||||
|  */ | ||||
|  | ||||
| // 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 <ixwebsocket/IXNetSystem.h> | ||||
|   | ||||
| @@ -17,11 +17,8 @@ | ||||
| #include <ixwebsocket/IXSelectInterruptFactory.h> | ||||
| #include <ixwebsocket/IXSetThreadName.h> | ||||
| #include <ixwebsocket/IXSocket.h> | ||||
| #include <ixwebsocket/IXSocketAppleSSL.h> | ||||
| #include <ixwebsocket/IXSocketConnect.h> | ||||
| #include <ixwebsocket/IXSocketFactory.h> | ||||
| #include <ixwebsocket/IXSocketMbedTLS.h> | ||||
| #include <ixwebsocket/IXSocketOpenSSL.h> | ||||
| #include <ixwebsocket/IXSocketServer.h> | ||||
| #include <ixwebsocket/IXUrlParser.h> | ||||
| #include <ixwebsocket/IXWebSocket.h> | ||||
| @@ -29,9 +26,9 @@ | ||||
| #include <ixwebsocket/IXWebSocketCloseInfo.h> | ||||
| #include <ixwebsocket/IXWebSocketErrorInfo.h> | ||||
| #include <ixwebsocket/IXWebSocketHandshake.h> | ||||
| #include <ixwebsocket/IXWebSocketHandshakeKeyGen.h> | ||||
| #include <ixwebsocket/IXWebSocketHttpHeaders.h> | ||||
| #include <ixwebsocket/IXWebSocketMessage.h> | ||||
| #include <ixwebsocket/IXWebSocketMessageQueue.h> | ||||
| #include <ixwebsocket/IXWebSocketMessageType.h> | ||||
| #include <ixwebsocket/IXWebSocketOpenInfo.h> | ||||
| #include <ixwebsocket/IXWebSocketPerMessageDeflate.h> | ||||
| @@ -40,6 +37,8 @@ | ||||
| #include <ixwebsocket/IXWebSocketSendInfo.h> | ||||
| #include <ixwebsocket/IXWebSocketServer.h> | ||||
| #include <ixwebsocket/IXWebSocketTransport.h> | ||||
| #include <ixwebsocket/LUrlParser.h> | ||||
| #include <ixwebsocket/libwshandshake.hpp> | ||||
|  | ||||
| using namespace ix; | ||||
|  | ||||
|   | ||||
| @@ -6,8 +6,8 @@ | ||||
|  | ||||
| #include "IXTest.h" | ||||
| #include "catch.hpp" | ||||
| #include "msgpack11.hpp" | ||||
| #include <iostream> | ||||
| #include "msgpack11.hpp" | ||||
| #include <ixwebsocket/IXSocket.h> | ||||
| #include <ixwebsocket/IXSocketFactory.h> | ||||
| #include <ixwebsocket/IXWebSocket.h> | ||||
| @@ -130,8 +130,7 @@ namespace | ||||
|             } | ||||
|             else if (msg->type == ix::WebSocketMessageType::Error) | ||||
|             { | ||||
|                 ss << "websocket_broadcast_client: " << _user << " Error ! " | ||||
|                    << msg->errorInfo.reason; | ||||
|                 ss << "websocket_broadcast_client: " << _user << " Error ! " << msg->errorInfo.reason; | ||||
|                 log(ss.str()); | ||||
|             } | ||||
|             else if (msg->type == ix::WebSocketMessageType::Ping) | ||||
| @@ -235,7 +234,7 @@ namespace | ||||
|         server.start(); | ||||
|         return true; | ||||
|     } | ||||
| } // namespace | ||||
| } // namespace ix | ||||
|  | ||||
| TEST_CASE("Websocket_broadcast_server", "[websocket_server]") | ||||
| { | ||||
| @@ -248,7 +247,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]") | ||||
|  | ||||
|         std::string session = ix::generateSessionId(); | ||||
|         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)); | ||||
|             chatClients.push_back(std::make_shared<WebSocketChat>(user, session, port)); | ||||
| @@ -260,7 +259,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]") | ||||
|         while (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(); | ||||
|             } | ||||
| @@ -270,7 +269,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]") | ||||
|  | ||||
|         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"); | ||||
|             } | ||||
| @@ -292,7 +291,7 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]") | ||||
|  | ||||
|         // Stop all clients | ||||
|         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); | ||||
|             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.start(); | ||||
|  | ||||
|         // Give us 3 seconds to connect | ||||
|         int attempts = 0; | ||||
|         while (!connected) | ||||
|         { | ||||
|             REQUIRE(attempts++ < 300); | ||||
|             REQUIRE(attempts++ < 10); | ||||
|             ix::msleep(10); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -14,41 +14,8 @@ int main(int argc, char* argv[]) | ||||
| { | ||||
|     ix::initNetSystem(); | ||||
|  | ||||
|     ix::CoreLogger::LogFunc logFunc = [](const char* msg, ix::LogLevel level) { | ||||
|         switch (level) | ||||
|         { | ||||
|             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); | ||||
|     ix::IXCoreLogger::LogFunc logFunc = [](const char* msg) { spdlog::info(msg); }; | ||||
|     ix::IXCoreLogger::setLogFunction(logFunc); | ||||
|  | ||||
|     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