Compare commits
	
		
			2 Commits
		
	
	
		
			v9.5.7
			...
			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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										137
									
								
								CMakeLists.txt
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								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()
 | 
			
		||||
    # default to mbedtls on uwp (universal windows platform) if nothing is configured
 | 
			
		||||
    elseif (${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore")
 | 
			
		||||
      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()
 | 
			
		||||
    option(USE_MBED_TLS "Use Mbed TLS" OFF)
 | 
			
		||||
    option(USE_OPEN_SSL "Use OpenSSL" OFF)
 | 
			
		||||
 | 
			
		||||
    # default to mbedtls on windows if nothing is configured
 | 
			
		||||
    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,18 +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/
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
install(EXPORT ixwebsocket NAMESPACE ixwebsocket:: DESTINATION lib/cmake/ixwebsocket)
 | 
			
		||||
export(EXPORT ixwebsocket NAMESPACE ixwebsocket:: FILE ixwebsocketConfig.cmake)
 | 
			
		||||
 | 
			
		||||
if (USE_WS OR USE_TEST)
 | 
			
		||||
  add_subdirectory(ixcore)
 | 
			
		||||
  add_subdirectory(ixcrypto)
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +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)
 | 
			
		||||
- [dis-light](https://gitlab.com/HCInk/dis-light), a discord library with a node frontend.
 | 
			
		||||
- [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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,98 +1,6 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
All changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## [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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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,18 +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/IXCobraToSentryBot.h
 | 
			
		||||
    ixbots/IXCobraToStatsdBot.h
 | 
			
		||||
    ixbots/IXCobraToStdoutBot.h
 | 
			
		||||
    ixbots/IXQueueManager.h
 | 
			
		||||
    ixbots/IXStatsdClient.h
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -29,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,246 +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 CobraConfig& config,
 | 
			
		||||
                          const std::string& channel,
 | 
			
		||||
                          const std::string& filter,
 | 
			
		||||
                          const std::string& position,
 | 
			
		||||
                          bool enableHeartbeat,
 | 
			
		||||
                          int heartBeatTimeout,
 | 
			
		||||
                          int runtime)
 | 
			
		||||
    {
 | 
			
		||||
        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<bool> stop(false);
 | 
			
		||||
        std::atomic<bool> throttled(false);
 | 
			
		||||
        std::atomic<bool> fatalCobraError(false);
 | 
			
		||||
 | 
			
		||||
        auto timer = [&sentCount,
 | 
			
		||||
                      &receivedCount,
 | 
			
		||||
                      &sentCountTotal,
 | 
			
		||||
                      &receivedCountTotal,
 | 
			
		||||
                      &sentCountPerSecs,
 | 
			
		||||
                      &receivedCountPerSecs,
 | 
			
		||||
                      &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 - receivedCountTotal;
 | 
			
		||||
 | 
			
		||||
                receivedCountTotal += receivedCountPerSecs;
 | 
			
		||||
                sentCountTotal += sentCountPerSecs;
 | 
			
		||||
 | 
			
		||||
                auto duration = std::chrono::seconds(1);
 | 
			
		||||
                std::this_thread::sleep_for(duration);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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,
 | 
			
		||||
                               &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,
 | 
			
		||||
                               [this,
 | 
			
		||||
                                &throttled,
 | 
			
		||||
                                &receivedCount,
 | 
			
		||||
                                &subscriptionPosition,
 | 
			
		||||
                                &fatalCobraError,
 | 
			
		||||
                                &sentCount](const Json::Value& msg, const std::string& position) {
 | 
			
		||||
                                   subscriptionPosition = position;
 | 
			
		||||
 | 
			
		||||
                                   // If we cannot send to sentry fast enough, drop the message
 | 
			
		||||
                                   if (throttled)
 | 
			
		||||
                                   {
 | 
			
		||||
                                       return;
 | 
			
		||||
                                   }
 | 
			
		||||
 | 
			
		||||
                                   ++receivedCount;
 | 
			
		||||
 | 
			
		||||
                                   _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,41 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXCobraBot.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <ixcobra/IXCobraConfig.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 CobraConfig& config,
 | 
			
		||||
                    const std::string& channel,
 | 
			
		||||
                    const std::string& filter,
 | 
			
		||||
                    const std::string& position,
 | 
			
		||||
                    bool enableHeartbeat,
 | 
			
		||||
                    int heartBeatTimeout,
 | 
			
		||||
                    int runtime);
 | 
			
		||||
 | 
			
		||||
        void setOnBotMessageCallback(const OnBotMessageCallback& callback);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        OnBotMessageCallback _onBotMessageCallback;
 | 
			
		||||
    };
 | 
			
		||||
} // namespace ix
 | 
			
		||||
@@ -5,84 +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 CobraConfig& config,
 | 
			
		||||
                                const std::string& channel,
 | 
			
		||||
                                const std::string& filter,
 | 
			
		||||
                                const std::string& position,
 | 
			
		||||
                                SentryClient& sentryClient,
 | 
			
		||||
                                bool verbose,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                int heartBeatTimeout,
 | 
			
		||||
                                int runtime)
 | 
			
		||||
    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,
 | 
			
		||||
                       channel,
 | 
			
		||||
                       filter,
 | 
			
		||||
                       position,
 | 
			
		||||
                       enableHeartbeat,
 | 
			
		||||
                       heartBeatTimeout,
 | 
			
		||||
                       runtime);
 | 
			
		||||
        // 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,20 +5,20 @@
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <ixcobra/IXCobraConfig.h>
 | 
			
		||||
#include <ixsentry/IXSentryClient.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    int64_t cobra_to_sentry_bot(const CobraConfig& config,
 | 
			
		||||
                                const std::string& channel,
 | 
			
		||||
                                const std::string& filter,
 | 
			
		||||
                                const std::string& position,
 | 
			
		||||
                                SentryClient& sentryClient,
 | 
			
		||||
                                bool verbose,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                int heartBeatTimeout,
 | 
			
		||||
                                int runtime);
 | 
			
		||||
    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,18 +56,18 @@ namespace ix
 | 
			
		||||
        return val;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int64_t 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,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                int heartBeatTimeout,
 | 
			
		||||
                                int runtime)
 | 
			
		||||
    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);
 | 
			
		||||
@@ -72,13 +75,65 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
                {
 | 
			
		||||
@@ -119,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())
 | 
			
		||||
@@ -139,15 +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,
 | 
			
		||||
                       channel,
 | 
			
		||||
                       filter,
 | 
			
		||||
                       position,
 | 
			
		||||
                       enableHeartbeat,
 | 
			
		||||
                       heartBeatTimeout,
 | 
			
		||||
                       runtime);
 | 
			
		||||
        // 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,24 +5,23 @@
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <ixbots/IXStatsdClient.h>
 | 
			
		||||
#include <ixcobra/IXCobraConfig.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <ixbots/IXStatsdClient.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    int64_t 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,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                int heartBeatTimeout,
 | 
			
		||||
                                int runtime);
 | 
			
		||||
    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,100 +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 CobraConfig& config,
 | 
			
		||||
                                const std::string& channel,
 | 
			
		||||
                                const std::string& filter,
 | 
			
		||||
                                const std::string& position,
 | 
			
		||||
                                bool fluentd,
 | 
			
		||||
                                bool quiet,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                int heartBeatTimeout,
 | 
			
		||||
                                int runtime)
 | 
			
		||||
    {
 | 
			
		||||
        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,
 | 
			
		||||
                       channel,
 | 
			
		||||
                       filter,
 | 
			
		||||
                       position,
 | 
			
		||||
                       enableHeartbeat,
 | 
			
		||||
                       heartBeatTimeout,
 | 
			
		||||
                       runtime);
 | 
			
		||||
    }
 | 
			
		||||
} // namespace ix
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXCobraToStdoutBot.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <ixcobra/IXCobraConfig.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    int64_t cobra_to_stdout_bot(const ix::CobraConfig& config,
 | 
			
		||||
                                const std::string& channel,
 | 
			
		||||
                                const std::string& filter,
 | 
			
		||||
                                const std::string& position,
 | 
			
		||||
                                bool fluentd,
 | 
			
		||||
                                bool quiet,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                int heartBeatTimeout,
 | 
			
		||||
                                int runtime);
 | 
			
		||||
} // 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"
 | 
			
		||||
 | 
			
		||||
@@ -104,26 +103,10 @@ namespace ix
 | 
			
		||||
            {
 | 
			
		||||
                ; // 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);
 | 
			
		||||
@@ -297,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
 | 
			
		||||
 | 
			
		||||
@@ -55,5 +54,3 @@ namespace ix
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
} // 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.5.7"
 | 
			
		||||
#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);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										14
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								makefile
									
									
									
									
									
								
							@@ -20,7 +20,7 @@ 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
 | 
			
		||||
@@ -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;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -141,8 +141,9 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
 | 
			
		||||
        std::string filter;
 | 
			
		||||
        std::string position("$");
 | 
			
		||||
        bool verbose = true;
 | 
			
		||||
        bool strict = true;
 | 
			
		||||
        size_t maxQueueSize = 10;
 | 
			
		||||
        bool enableHeartbeat = false;
 | 
			
		||||
        int heartBeatTimeout = 60;
 | 
			
		||||
 | 
			
		||||
        // FIXME: try to get this working with https instead of http
 | 
			
		||||
        //        to regress the TLS 1.3 OpenSSL bug
 | 
			
		||||
@@ -160,15 +161,16 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
 | 
			
		||||
        // Only run the bot for 3 seconds
 | 
			
		||||
        int runtime = 3;
 | 
			
		||||
 | 
			
		||||
        int64_t sentCount = cobra_to_sentry_bot(config,
 | 
			
		||||
                                                channel,
 | 
			
		||||
                                                filter,
 | 
			
		||||
                                                position,
 | 
			
		||||
                                                sentryClient,
 | 
			
		||||
                                                verbose,
 | 
			
		||||
                                                enableHeartbeat,
 | 
			
		||||
                                                heartBeatTimeout,
 | 
			
		||||
                                                runtime);
 | 
			
		||||
        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
 | 
			
		||||
        //
 | 
			
		||||
 
 | 
			
		||||
@@ -90,8 +90,8 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]")
 | 
			
		||||
        std::string filter;
 | 
			
		||||
        std::string position("$");
 | 
			
		||||
        bool verbose = true;
 | 
			
		||||
        size_t maxQueueSize = 10;
 | 
			
		||||
        bool enableHeartbeat = false;
 | 
			
		||||
        int heartBeatTimeout = 60;
 | 
			
		||||
 | 
			
		||||
        // Only run the bot for 3 seconds
 | 
			
		||||
        int runtime = 3;
 | 
			
		||||
@@ -114,18 +114,18 @@ TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]")
 | 
			
		||||
        std::string gauge;
 | 
			
		||||
        std::string timer;
 | 
			
		||||
 | 
			
		||||
        int64_t sentCount = ix::cobra_to_statsd_bot(config,
 | 
			
		||||
                                                    channel,
 | 
			
		||||
                                                    filter,
 | 
			
		||||
                                                    position,
 | 
			
		||||
                                                    statsdClient,
 | 
			
		||||
                                                    fields,
 | 
			
		||||
                                                    gauge,
 | 
			
		||||
                                                    timer,
 | 
			
		||||
                                                    verbose,
 | 
			
		||||
                                                    enableHeartbeat,
 | 
			
		||||
                                                    heartBeatTimeout,
 | 
			
		||||
                                                    runtime);
 | 
			
		||||
        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,125 +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);
 | 
			
		||||
 | 
			
		||||
        std::string filter;
 | 
			
		||||
        std::string position("$");
 | 
			
		||||
        bool quiet = false;
 | 
			
		||||
        bool enableHeartbeat = false;
 | 
			
		||||
        int heartBeatTimeout = 60;
 | 
			
		||||
 | 
			
		||||
        // Only run the bot for 3 seconds
 | 
			
		||||
        int runtime = 3;
 | 
			
		||||
 | 
			
		||||
        // We could try to capture the output ... not sure how.
 | 
			
		||||
        bool fluentd = true;
 | 
			
		||||
 | 
			
		||||
        int64_t sentCount = ix::cobra_to_stdout_bot(config,
 | 
			
		||||
                                                    channel,
 | 
			
		||||
                                                    filter,
 | 
			
		||||
                                                    position,
 | 
			
		||||
                                                    fluentd,
 | 
			
		||||
                                                    quiet,
 | 
			
		||||
                                                    enableHeartbeat,
 | 
			
		||||
                                                    heartBeatTimeout,
 | 
			
		||||
                                                    runtime);
 | 
			
		||||
        //
 | 
			
		||||
        // 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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -96,7 +96,7 @@ TEST_CASE("subprotocol", "[websocket_subprotocol]")
 | 
			
		||||
        int attempts = 0;
 | 
			
		||||
        while (!connected)
 | 
			
		||||
        {
 | 
			
		||||
            REQUIRE(attempts++ < 100);
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								third_party/.clang-format
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								third_party/.clang-format
									
									
									
									
										vendored
									
									
								
							@@ -1,2 +1,4 @@
 | 
			
		||||
DisableFormat: true
 | 
			
		||||
SortIncludes: false
 | 
			
		||||
{
 | 
			
		||||
    "DisableFormat": true,
 | 
			
		||||
    "SortIncludes": false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								third_party/mbedtls/.github/issue_template.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								third_party/mbedtls/.github/issue_template.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
Note: This is just a template, so feel free to use/remove the unnecessary things
 | 
			
		||||
 | 
			
		||||
### Description
 | 
			
		||||
- Type: Bug | Enhancement\Feature Request | Question
 | 
			
		||||
- Priority: Blocker | Major | Minor
 | 
			
		||||
 | 
			
		||||
---------------------------------------------------------------
 | 
			
		||||
## Bug
 | 
			
		||||
 | 
			
		||||
**OS**
 | 
			
		||||
Mbed OS|linux|windows|
 | 
			
		||||
 | 
			
		||||
**mbed TLS build:**
 | 
			
		||||
Version: x.x.x or git commit id
 | 
			
		||||
OS version: x.x.x
 | 
			
		||||
Configuration: please attach config.h file where possible
 | 
			
		||||
Compiler and options (if you used a pre-built binary, please indicate how you obtained it):
 | 
			
		||||
Additional environment information:
 | 
			
		||||
 | 
			
		||||
**Peer device TLS stack and version**
 | 
			
		||||
OpenSSL|GnuTls|Chrome|NSS(Firefox)|SecureChannel (IIS/Internet Explorer/Edge)|Other
 | 
			
		||||
Version:
 | 
			
		||||
 | 
			
		||||
**Expected behavior**
 | 
			
		||||
 | 
			
		||||
**Actual behavior**
 | 
			
		||||
 | 
			
		||||
**Steps to reproduce**
 | 
			
		||||
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
## Enhancement\Feature Request
 | 
			
		||||
 | 
			
		||||
**Justification - why does the library need this feature?**
 | 
			
		||||
 | 
			
		||||
**Suggested enhancement**
 | 
			
		||||
 | 
			
		||||
-----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
## Question
 | 
			
		||||
 | 
			
		||||
**Please first check for answers in the [Mbed TLS knowledge Base](https://tls.mbed.org/kb), and preferably file an issue in the [Mbed TLS support forum](https://forums.mbed.com/c/mbed-tls)**
 | 
			
		||||
							
								
								
									
										39
									
								
								third_party/mbedtls/.github/pull_request_template.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								third_party/mbedtls/.github/pull_request_template.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
Notes:
 | 
			
		||||
* Pull requests cannot be accepted until:
 | 
			
		||||
-  The submitter has [accepted the online agreement here with a click through](https://developer.mbed.org/contributor_agreement/)
 | 
			
		||||
   or for companies or those that do not wish to create an mbed account, a slightly different agreement can be found [here](https://www.mbed.com/en/about-mbed/contributor-license-agreements/)
 | 
			
		||||
- The PR follows the [mbed TLS coding standards](https://tls.mbed.org/kb/development/mbedtls-coding-standards)
 | 
			
		||||
* This is just a template, so feel free to use/remove the unnecessary things
 | 
			
		||||
## Description
 | 
			
		||||
A few sentences describing the overall goals of the pull request's commits.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Status
 | 
			
		||||
**READY/IN DEVELOPMENT/HOLD**
 | 
			
		||||
 | 
			
		||||
## Requires Backporting
 | 
			
		||||
When there is a bug fix, it should be backported to all maintained and supported branches.
 | 
			
		||||
Changes do not have to be backported if:
 | 
			
		||||
- This PR is a new feature\enhancement
 | 
			
		||||
- This PR contains changes in the API. If this is true, and there is a need for the fix to be backported, the fix should be handled differently in the legacy branch
 | 
			
		||||
 | 
			
		||||
Yes | NO
 | 
			
		||||
Which branch?
 | 
			
		||||
 | 
			
		||||
## Migrations
 | 
			
		||||
If there is any API change, what's the incentive and logic for it.
 | 
			
		||||
 | 
			
		||||
YES | NO
 | 
			
		||||
 | 
			
		||||
## Additional comments
 | 
			
		||||
Any additional information that could be of interest
 | 
			
		||||
 | 
			
		||||
## Todos
 | 
			
		||||
- [ ] Tests
 | 
			
		||||
- [ ] Documentation
 | 
			
		||||
- [ ] Changelog updated
 | 
			
		||||
- [ ] Backported
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Steps to test or reproduce
 | 
			
		||||
Outline the steps to test or reproduce the PR here.
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user