Compare commits
	
		
			1 Commits
		
	
	
		
			feature/lu
			...
			feature/wi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					2cbe198497 | 
							
								
								
									
										30
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
								
							@@ -19,6 +19,15 @@ jobs:
 | 
			
		||||
    - 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
 | 
			
		||||
    - name: make test
 | 
			
		||||
      run: make test_tsan_openssl
 | 
			
		||||
 | 
			
		||||
  mac_tsan_mbedtls:
 | 
			
		||||
    runs-on: macOS-latest
 | 
			
		||||
    steps:
 | 
			
		||||
@@ -28,7 +37,7 @@ jobs:
 | 
			
		||||
    - name: make test
 | 
			
		||||
      run: make test_tsan_mbedtls
 | 
			
		||||
 | 
			
		||||
  windows_openssl:
 | 
			
		||||
  windows_no_tls:
 | 
			
		||||
    runs-on: windows-latest
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v1
 | 
			
		||||
@@ -36,8 +45,25 @@ jobs:
 | 
			
		||||
    - run: |
 | 
			
		||||
        mkdir build
 | 
			
		||||
        cd build
 | 
			
		||||
        cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 ..
 | 
			
		||||
        cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_TEST=1 ..
 | 
			
		||||
    - run: cmake --build build
 | 
			
		||||
    - run: |
 | 
			
		||||
        cd test
 | 
			
		||||
        ..\build\test\ixwebsocket_unittest.exe
 | 
			
		||||
 | 
			
		||||
      # windows_mbedtls:
 | 
			
		||||
      #   runs-on: windows-latest
 | 
			
		||||
      #   steps:
 | 
			
		||||
      #   - uses: actions/checkout@v1
 | 
			
		||||
      #   - uses: seanmiddleditch/gha-setup-vsdevenv@master
 | 
			
		||||
      #   - run: |
 | 
			
		||||
      #       vcpkg install zlib:x64-windows
 | 
			
		||||
      #       vcpkg install mbedtls:x64-windows
 | 
			
		||||
      #   - run: |
 | 
			
		||||
      #       mkdir build
 | 
			
		||||
      #       cd build
 | 
			
		||||
      #       cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_MBED_TLS=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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										111
									
								
								CMakeLists.txt
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								CMakeLists.txt
									
									
									
									
									
								
							@@ -44,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
 | 
			
		||||
@@ -79,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
 | 
			
		||||
@@ -92,6 +94,8 @@ set( IXWEBSOCKET_HEADERS
 | 
			
		||||
    ixwebsocket/IXWebSocketServer.h
 | 
			
		||||
    ixwebsocket/IXWebSocketTransport.h
 | 
			
		||||
    ixwebsocket/IXWebSocketVersion.h
 | 
			
		||||
    ixwebsocket/LUrlParser.h
 | 
			
		||||
    ixwebsocket/libwshandshake.hpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
if (UNIX)
 | 
			
		||||
@@ -109,33 +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()
 | 
			
		||||
    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()
 | 
			
		||||
 | 
			
		||||
@@ -150,45 +152,55 @@ if (USE_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)
 | 
			
		||||
    endif()
 | 
			
		||||
if (WIN32)
 | 
			
		||||
  target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi)
 | 
			
		||||
  add_definitions(-D_CRT_SECURE_NO_WARNINGS)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
    # This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL
 | 
			
		||||
    if (NOT OPENSSL_FOUND)
 | 
			
		||||
      find_package(OpenSSL REQUIRED)
 | 
			
		||||
    endif()
 | 
			
		||||
    message(STATUS "OpenSSL: " ${OPENSSL_VERSION})
 | 
			
		||||
if (UNIX)
 | 
			
		||||
  find_package(Threads)
 | 
			
		||||
  target_link_libraries(ixwebsocket ${CMAKE_THREAD_LIBS_INIT})
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
    add_definitions(${OPENSSL_DEFINITIONS})
 | 
			
		||||
    target_include_directories(ixwebsocket PUBLIC ${OPENSSL_INCLUDE_DIR})
 | 
			
		||||
    target_link_libraries(ixwebsocket ${OPENSSL_LIBRARIES})
 | 
			
		||||
  elseif (USE_MBED_TLS)
 | 
			
		||||
    message(STATUS "TLS configured to use mbedtls")
 | 
			
		||||
if (USE_TLS AND USE_OPEN_SSL)
 | 
			
		||||
 | 
			
		||||
  # 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 ${MBEDTLS_LIBRARIES})
 | 
			
		||||
  elseif (USE_SECURE_TRANSPORT)
 | 
			
		||||
    message(STATUS "TLS configured to use secure transport")
 | 
			
		||||
    target_link_libraries(ixwebsocket "-framework foundation" "-framework security")
 | 
			
		||||
  endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# This ZLIB_FOUND check is to help find a cmake manually configured zlib
 | 
			
		||||
if (NOT ZLIB_FOUND)
 | 
			
		||||
  find_package(ZLIB)
 | 
			
		||||
endif()
 | 
			
		||||
@@ -201,21 +213,6 @@ else()
 | 
			
		||||
  target_link_libraries(ixwebsocket zlibstatic)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (WIN32)
 | 
			
		||||
  target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi)
 | 
			
		||||
  add_definitions(-D_CRT_SECURE_NO_WARNINGS)
 | 
			
		||||
 | 
			
		||||
  if (USE_TLS)
 | 
			
		||||
    target_link_libraries(ixwebsocket Crypt32)
 | 
			
		||||
  endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (UNIX)
 | 
			
		||||
  find_package(Threads)
 | 
			
		||||
  target_link_libraries(ixwebsocket ${CMAKE_THREAD_LIBS_INIT})
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
set( IXWEBSOCKET_INCLUDE_DIRS
 | 
			
		||||
    ${CMAKE_CURRENT_SOURCE_DIR}
 | 
			
		||||
)
 | 
			
		||||
@@ -251,7 +248,3 @@ if (USE_WS OR USE_TEST)
 | 
			
		||||
      add_subdirectory(test)
 | 
			
		||||
  endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (USE_LUAROCKS)
 | 
			
		||||
    add_subdirectory(luarocks)
 | 
			
		||||
endif()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,74 +1,6 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
All changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## [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.
 | 
			
		||||
 
 | 
			
		||||
@@ -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 Windows and 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. On Windows with mbedtls the message will contain `error in handshake : X509 - Certificate verification failed, e.g. CRL, CA or signature check failed`.
 | 
			
		||||
* There is no convenient way to embed a ca cert.
 | 
			
		||||
* Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that.
 | 
			
		||||
* The server code is using select to detect incoming data, and creates one OS thread per connection. This is not as scalable as strategies using epoll or kqueue.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ Notes on how we can update the different packages for ixwebsocket.
 | 
			
		||||
 | 
			
		||||
Visit the [releases](https://github.com/machinezone/IXWebSocket/releases) page on Github. A tag must have been made first.
 | 
			
		||||
 | 
			
		||||
Download the latest entry.
 | 
			
		||||
Download the latest entry. 
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ cd /tmp
 | 
			
		||||
 
 | 
			
		||||
@@ -447,7 +447,7 @@ Additional TLS options can be configured by passing a `ix::SocketTLSOptions` ins
 | 
			
		||||
webSocket.setTLSOptions({
 | 
			
		||||
    .certFile = "path/to/cert/file.pem",
 | 
			
		||||
    .keyFile = "path/to/key/file.pem",
 | 
			
		||||
    .caFile = "path/to/trust/bundle/file.pem", // as a file, or in memory buffer in PEM format
 | 
			
		||||
    .caFile = "path/to/trust/bundle/file.pem",
 | 
			
		||||
    .tls = true // required in server mode
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
@@ -461,7 +461,6 @@ On a server, this is necessary for TLS support.
 | 
			
		||||
Specifying `caFile` configures the trusted roots bundle file (in PEM format) that will be used to verify peer certificates.
 | 
			
		||||
 - The special value of `SYSTEM` (the default) indicates that the system-configured trust bundle should be used; this is generally what you want when connecting to any publicly exposed API/server.
 | 
			
		||||
 - The special value of `NONE` can be used to disable peer verification; this is only recommended to rule out certificate verification when testing connectivity.
 | 
			
		||||
 - If the value contain the special value `-----BEGIN CERTIFICATE-----`, the value will be read from memory, and not from a file. This is convenient on platforms like Android where reading / writing to the file system can be challenging without proper permissions, or without knowing the location of a temp directory.
 | 
			
		||||
 | 
			
		||||
For a client, specifying `caFile` can be used if connecting to a server that uses a self-signed cert, or when using a custom CA in an internal environment.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,19 +4,15 @@
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
)
 | 
			
		||||
@@ -31,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,321 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXCobraBot.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXCobraBot.h"
 | 
			
		||||
 | 
			
		||||
#include "IXQueueManager.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 verbose,
 | 
			
		||||
                          size_t maxQueueSize,
 | 
			
		||||
                          bool useQueue,
 | 
			
		||||
                          bool enableHeartbeat,
 | 
			
		||||
                          int runtime)
 | 
			
		||||
    {
 | 
			
		||||
        ix::CobraConnection conn;
 | 
			
		||||
        conn.configure(config);
 | 
			
		||||
        conn.connect();
 | 
			
		||||
 | 
			
		||||
        Json::FastWriter jsonWriter;
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        QueueManager queueManager(maxQueueSize);
 | 
			
		||||
 | 
			
		||||
        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] {
 | 
			
		||||
            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");
 | 
			
		||||
                    exit(1);
 | 
			
		||||
                }
 | 
			
		||||
                state = currentState;
 | 
			
		||||
 | 
			
		||||
                auto duration = std::chrono::minutes(1);
 | 
			
		||||
                std::this_thread::sleep_for(duration);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CoreLogger::info("heartbeat thread done");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        std::thread t2(heartbeat);
 | 
			
		||||
 | 
			
		||||
        auto sender =
 | 
			
		||||
            [this, &queueManager, verbose, &sentCount, &stop, &throttled, &fatalCobraError] {
 | 
			
		||||
                while (true)
 | 
			
		||||
                {
 | 
			
		||||
                    auto data = queueManager.pop();
 | 
			
		||||
                    Json::Value msg = data.first;
 | 
			
		||||
                    std::string position = data.second;
 | 
			
		||||
 | 
			
		||||
                    if (stop) break;
 | 
			
		||||
                    if (msg.isNull()) continue;
 | 
			
		||||
 | 
			
		||||
                    if (_onBotMessageCallback &&
 | 
			
		||||
                        _onBotMessageCallback(msg, position, verbose, throttled, fatalCobraError))
 | 
			
		||||
                    {
 | 
			
		||||
                        // That might be too noisy
 | 
			
		||||
                        if (verbose)
 | 
			
		||||
                        {
 | 
			
		||||
                            CoreLogger::info("cobra bot: sending succesfull");
 | 
			
		||||
                        }
 | 
			
		||||
                        ++sentCount;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        CoreLogger::error("cobra bot: error sending");
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (stop) break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                CoreLogger::info("sender thread done");
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        std::thread t3(sender);
 | 
			
		||||
 | 
			
		||||
        std::string subscriptionPosition(position);
 | 
			
		||||
 | 
			
		||||
        conn.setEventCallback([this,
 | 
			
		||||
                               &conn,
 | 
			
		||||
                               &channel,
 | 
			
		||||
                               &filter,
 | 
			
		||||
                               &subscriptionPosition,
 | 
			
		||||
                               &jsonWriter,
 | 
			
		||||
                               verbose,
 | 
			
		||||
                               &throttled,
 | 
			
		||||
                               &receivedCount,
 | 
			
		||||
                               &fatalCobraError,
 | 
			
		||||
                               &useQueue,
 | 
			
		||||
                               &queueManager,
 | 
			
		||||
                               &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,
 | 
			
		||||
                                &jsonWriter,
 | 
			
		||||
                                verbose,
 | 
			
		||||
                                &throttled,
 | 
			
		||||
                                &receivedCount,
 | 
			
		||||
                                &queueManager,
 | 
			
		||||
                                &useQueue,
 | 
			
		||||
                                &subscriptionPosition,
 | 
			
		||||
                                &fatalCobraError,
 | 
			
		||||
                                &sentCount](const Json::Value& msg, const std::string& position) {
 | 
			
		||||
                                   if (verbose)
 | 
			
		||||
                                   {
 | 
			
		||||
                                       CoreLogger::info("Subscriber received message "
 | 
			
		||||
                                                        + position + " -> " + jsonWriter.write(msg));
 | 
			
		||||
                                   }
 | 
			
		||||
 | 
			
		||||
                                   subscriptionPosition = position;
 | 
			
		||||
 | 
			
		||||
                                   // If we cannot send to sentry fast enough, drop the message
 | 
			
		||||
                                   if (throttled)
 | 
			
		||||
                                   {
 | 
			
		||||
                                       return;
 | 
			
		||||
                                   }
 | 
			
		||||
 | 
			
		||||
                                   ++receivedCount;
 | 
			
		||||
 | 
			
		||||
                                   if (useQueue)
 | 
			
		||||
                                   {
 | 
			
		||||
                                       queueManager.add(msg, position);
 | 
			
		||||
                                   }
 | 
			
		||||
                                   else
 | 
			
		||||
                                   {
 | 
			
		||||
                                       if (_onBotMessageCallback &&
 | 
			
		||||
                                           _onBotMessageCallback(
 | 
			
		||||
                                               msg, position, verbose, throttled, fatalCobraError))
 | 
			
		||||
                                       {
 | 
			
		||||
                                           // That might be too noisy
 | 
			
		||||
                                           if (verbose)
 | 
			
		||||
                                           {
 | 
			
		||||
                                               CoreLogger::info("cobra bot: sending succesfull");
 | 
			
		||||
                                           }
 | 
			
		||||
                                           ++sentCount;
 | 
			
		||||
                                       }
 | 
			
		||||
                                       else
 | 
			
		||||
                                       {
 | 
			
		||||
                                           CoreLogger::error("cobra bot: error sending");
 | 
			
		||||
                                       }
 | 
			
		||||
                                   }
 | 
			
		||||
                               });
 | 
			
		||||
            }
 | 
			
		||||
            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();
 | 
			
		||||
 | 
			
		||||
        // sentry sender thread
 | 
			
		||||
        t3.join();
 | 
			
		||||
 | 
			
		||||
        return fatalCobraError ? -1 : (int64_t) sentCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void CobraBot::setOnBotMessageCallback(const OnBotMessageCallback& callback)
 | 
			
		||||
    {
 | 
			
		||||
        _onBotMessageCallback = callback;
 | 
			
		||||
    }
 | 
			
		||||
} // namespace ix
 | 
			
		||||
@@ -1,43 +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<bool(const Json::Value&,
 | 
			
		||||
                                                    const std::string&,
 | 
			
		||||
                                                    const bool verbose,
 | 
			
		||||
                                                    std::atomic<bool>&,
 | 
			
		||||
                                                    std::atomic<bool>&)>;
 | 
			
		||||
 | 
			
		||||
    class CobraBot
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        CobraBot() = default;
 | 
			
		||||
 | 
			
		||||
        int64_t run(const CobraConfig& config,
 | 
			
		||||
                    const std::string& channel,
 | 
			
		||||
                    const std::string& filter,
 | 
			
		||||
                    const std::string& position,
 | 
			
		||||
                    bool verbose,
 | 
			
		||||
                    size_t maxQueueSize,
 | 
			
		||||
                    bool useQueue,
 | 
			
		||||
                    bool enableHeartbeat,
 | 
			
		||||
                    int runtime);
 | 
			
		||||
 | 
			
		||||
        void setOnBotMessageCallback(const OnBotMessageCallback& callback);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        OnBotMessageCallback _onBotMessageCallback;
 | 
			
		||||
    };
 | 
			
		||||
} // namespace ix
 | 
			
		||||
@@ -5,113 +5,301 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXCobraToSentryBot.h"
 | 
			
		||||
 | 
			
		||||
#include "IXCobraBot.h"
 | 
			
		||||
#include "IXQueueManager.h"
 | 
			
		||||
#include <ixcobra/IXCobraConnection.h>
 | 
			
		||||
#include <ixcore/utils/IXCoreLogger.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,
 | 
			
		||||
                                size_t maxQueueSize,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                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](const Json::Value& msg,
 | 
			
		||||
                                                    const std::string& /*position*/,
 | 
			
		||||
                                                    const bool verbose,
 | 
			
		||||
                                                    std::atomic<bool>& throttled,
 | 
			
		||||
                                                    std::atomic<bool> &
 | 
			
		||||
                                                    /*fatalCobraError*/) -> bool {
 | 
			
		||||
            auto ret = sentryClient.send(msg, verbose);
 | 
			
		||||
            HttpResponsePtr response = ret.first;
 | 
			
		||||
        ix::CobraConnection conn;
 | 
			
		||||
        conn.configure(config);
 | 
			
		||||
        conn.connect();
 | 
			
		||||
 | 
			
		||||
            if (!response)
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        QueueManager queueManager(maxQueueSize);
 | 
			
		||||
 | 
			
		||||
        auto timer = [&sentCount, &receivedCount, &stop] {
 | 
			
		||||
            while (!stop)
 | 
			
		||||
            {
 | 
			
		||||
                CoreLogger::warn("Null HTTP Response");
 | 
			
		||||
                return false;
 | 
			
		||||
                spdlog::info("messages received {} sent {}", receivedCount, sentCount);
 | 
			
		||||
 | 
			
		||||
                auto duration = std::chrono::seconds(1);
 | 
			
		||||
                std::this_thread::sleep_for(duration);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (verbose)
 | 
			
		||||
            spdlog::info("timer thread done");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        std::thread t1(timer);
 | 
			
		||||
 | 
			
		||||
        auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat] {
 | 
			
		||||
            std::string state("na");
 | 
			
		||||
 | 
			
		||||
            if (!enableHeartbeat) return;
 | 
			
		||||
 | 
			
		||||
            while (!stop)
 | 
			
		||||
            {
 | 
			
		||||
                for (auto it : response->headers)
 | 
			
		||||
                {
 | 
			
		||||
                    CoreLogger::info(it.first + ": " + it.second);
 | 
			
		||||
                }
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                ss << "messages received " << receivedCount;
 | 
			
		||||
                ss << "messages sent " << sentCount;
 | 
			
		||||
 | 
			
		||||
                CoreLogger::info("Upload size: " + std::to_string(response->uploadSize));
 | 
			
		||||
                CoreLogger::info("Download size: " + std::to_string(response->downloadSize));
 | 
			
		||||
                std::string currentState = ss.str();
 | 
			
		||||
 | 
			
		||||
                CoreLogger::info("Status: " + std::to_string(response->statusCode));
 | 
			
		||||
                if (response->errorCode != HttpErrorCode::Ok)
 | 
			
		||||
                if (currentState == state)
 | 
			
		||||
                {
 | 
			
		||||
                    CoreLogger::info("error message: " + response->errorMsg);
 | 
			
		||||
                    spdlog::error("no messages received or sent for 1 minute, exiting");
 | 
			
		||||
                    exit(1);
 | 
			
		||||
                }
 | 
			
		||||
                state = currentState;
 | 
			
		||||
 | 
			
		||||
                if (response->headers["Content-Type"] != "application/octet-stream")
 | 
			
		||||
                {
 | 
			
		||||
                    CoreLogger::info("payload: " + response->payload);
 | 
			
		||||
                }
 | 
			
		||||
                auto duration = std::chrono::minutes(1);
 | 
			
		||||
                std::this_thread::sleep_for(duration);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool success = response->statusCode == 200;
 | 
			
		||||
            spdlog::info("heartbeat thread done");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
            if (!success)
 | 
			
		||||
            {
 | 
			
		||||
                CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode));
 | 
			
		||||
                CoreLogger::error("Body: " + ret.second);
 | 
			
		||||
                CoreLogger::error("Response: " + response->payload);
 | 
			
		||||
        std::thread t2(heartbeat);
 | 
			
		||||
 | 
			
		||||
                // Error 429 Too Many Requests
 | 
			
		||||
                if (response->statusCode == 429)
 | 
			
		||||
        auto sentrySender =
 | 
			
		||||
            [&queueManager, verbose, &errorSending, &sentCount, &stop, &throttled, &sentryClient] {
 | 
			
		||||
 | 
			
		||||
                while (true)
 | 
			
		||||
                {
 | 
			
		||||
                    auto retryAfter = response->headers["Retry-After"];
 | 
			
		||||
                    std::stringstream ss;
 | 
			
		||||
                    ss << retryAfter;
 | 
			
		||||
                    int seconds;
 | 
			
		||||
                    ss >> seconds;
 | 
			
		||||
                    Json::Value msg = queueManager.pop();
 | 
			
		||||
 | 
			
		||||
                    if (!ss.eof() || ss.fail())
 | 
			
		||||
                    if (stop) break;
 | 
			
		||||
                    if (msg.isNull()) continue;
 | 
			
		||||
 | 
			
		||||
                    auto ret = sentryClient.send(msg, verbose);
 | 
			
		||||
                    HttpResponsePtr response = ret.first;
 | 
			
		||||
 | 
			
		||||
                    if (!response)
 | 
			
		||||
                    {
 | 
			
		||||
                        seconds = 30;
 | 
			
		||||
                        CoreLogger::warn("Error parsing Retry-After header. "
 | 
			
		||||
                                         "Using " + retryAfter + " for the sleep duration");
 | 
			
		||||
                        spdlog::warn("Null HTTP Response");
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    CoreLogger::warn("Error 429 - Too Many Requests. ws will sleep "
 | 
			
		||||
                                     "and retry after " + retryAfter + " seconds");
 | 
			
		||||
                    if (verbose)
 | 
			
		||||
                    {
 | 
			
		||||
                        for (auto it : response->headers)
 | 
			
		||||
                        {
 | 
			
		||||
                            spdlog::info("{}: {}", it.first, it.second);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    throttled = true;
 | 
			
		||||
                    auto duration = std::chrono::seconds(seconds);
 | 
			
		||||
                    std::this_thread::sleep_for(duration);
 | 
			
		||||
                    throttled = false;
 | 
			
		||||
                        spdlog::info("Upload size: {}", response->uploadSize);
 | 
			
		||||
                        spdlog::info("Download size: {}", response->downloadSize);
 | 
			
		||||
 | 
			
		||||
                        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));
 | 
			
		||||
                                   }
 | 
			
		||||
 | 
			
		||||
            return success;
 | 
			
		||||
                                   // 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;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        bool useQueue = true;
 | 
			
		||||
        // Run forever
 | 
			
		||||
        if (runtime == -1)
 | 
			
		||||
        {
 | 
			
		||||
            while (true)
 | 
			
		||||
            {
 | 
			
		||||
                auto duration = std::chrono::seconds(1);
 | 
			
		||||
                std::this_thread::sleep_for(duration);
 | 
			
		||||
 | 
			
		||||
        return bot.run(config,
 | 
			
		||||
                       channel,
 | 
			
		||||
                       filter,
 | 
			
		||||
                       position,
 | 
			
		||||
                       verbose,
 | 
			
		||||
                       maxQueueSize,
 | 
			
		||||
                       useQueue,
 | 
			
		||||
                       enableHeartbeat,
 | 
			
		||||
                       runtime);
 | 
			
		||||
                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,
 | 
			
		||||
                                size_t maxQueueSize,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                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,14 +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
 | 
			
		||||
@@ -54,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,
 | 
			
		||||
                                size_t maxQueueSize,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                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);
 | 
			
		||||
@@ -73,13 +75,65 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        auto tokens = parseFields(fields);
 | 
			
		||||
 | 
			
		||||
        CobraBot bot;
 | 
			
		||||
        bot.setOnBotMessageCallback(
 | 
			
		||||
            [&statsdClient, &tokens, &gauge, &timer](const Json::Value& msg,
 | 
			
		||||
                                                     const std::string& /*position*/,
 | 
			
		||||
                                                     const bool verbose,
 | 
			
		||||
                                                     std::atomic<bool>& /*throttled*/,
 | 
			
		||||
                                                     std::atomic<bool>& fatalCobraError) -> bool {
 | 
			
		||||
        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)
 | 
			
		||||
                {
 | 
			
		||||
@@ -120,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 false;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (verbose)
 | 
			
		||||
                    {
 | 
			
		||||
                        CoreLogger::info(id + " - " + attrName + " -> " + std::to_string(x));
 | 
			
		||||
                        spdlog::info("{} - {} -> {}", id, attrName, x);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (!gauge.empty())
 | 
			
		||||
@@ -140,19 +194,127 @@ namespace ix
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
                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;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        bool useQueue = true;
 | 
			
		||||
        // Run forever
 | 
			
		||||
        if (runtime == -1)
 | 
			
		||||
        {
 | 
			
		||||
            while (true)
 | 
			
		||||
            {
 | 
			
		||||
                auto duration = std::chrono::seconds(1);
 | 
			
		||||
                std::this_thread::sleep_for(duration);
 | 
			
		||||
 | 
			
		||||
        return bot.run(config,
 | 
			
		||||
                       channel,
 | 
			
		||||
                       filter,
 | 
			
		||||
                       position,
 | 
			
		||||
                       verbose,
 | 
			
		||||
                       maxQueueSize,
 | 
			
		||||
                       useQueue,
 | 
			
		||||
                       enableHeartbeat,
 | 
			
		||||
                       runtime);
 | 
			
		||||
                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,
 | 
			
		||||
                                size_t maxQueueSize,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                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,107 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXCobraToStdoutBot.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXCobraToStdoutBot.h"
 | 
			
		||||
 | 
			
		||||
#include "IXCobraBot.h"
 | 
			
		||||
#include "IXQueueManager.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 verbose,
 | 
			
		||||
                                size_t maxQueueSize,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                int runtime)
 | 
			
		||||
    {
 | 
			
		||||
        CobraBot bot;
 | 
			
		||||
        auto jsonWriter = makeStreamWriter();
 | 
			
		||||
 | 
			
		||||
        bot.setOnBotMessageCallback(
 | 
			
		||||
            [&fluentd, &quiet, &jsonWriter](const Json::Value& msg,
 | 
			
		||||
                                            const std::string& position,
 | 
			
		||||
                                            const bool /*verbose*/,
 | 
			
		||||
                                            std::atomic<bool>& /*throttled*/,
 | 
			
		||||
                                            std::atomic<bool> &
 | 
			
		||||
                                            /*fatalCobraError*/) -> bool {
 | 
			
		||||
                if (!quiet)
 | 
			
		||||
                {
 | 
			
		||||
                    writeToStdout(fluentd, jsonWriter, msg, position);
 | 
			
		||||
                }
 | 
			
		||||
                return true;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        bool useQueue = false;
 | 
			
		||||
 | 
			
		||||
        return bot.run(config,
 | 
			
		||||
                       channel,
 | 
			
		||||
                       filter,
 | 
			
		||||
                       position,
 | 
			
		||||
                       verbose,
 | 
			
		||||
                       maxQueueSize,
 | 
			
		||||
                       useQueue,
 | 
			
		||||
                       enableHeartbeat,
 | 
			
		||||
                       runtime);
 | 
			
		||||
    }
 | 
			
		||||
} // namespace ix
 | 
			
		||||
@@ -1,25 +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 verbose,
 | 
			
		||||
                                size_t maxQueueSize,
 | 
			
		||||
                                bool enableHeartbeat,
 | 
			
		||||
                                int runtime);
 | 
			
		||||
} // namespace ix
 | 
			
		||||
@@ -5,20 +5,19 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXQueueManager.h"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    std::pair<Json::Value, std::string> QueueManager::pop()
 | 
			
		||||
    Json::Value QueueManager::pop()
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> lock(_mutex);
 | 
			
		||||
 | 
			
		||||
        if (_queues.empty())
 | 
			
		||||
        {
 | 
			
		||||
            Json::Value val;
 | 
			
		||||
            return std::make_pair(val, std::string());
 | 
			
		||||
            return val;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::vector<std::string> games;
 | 
			
		||||
@@ -36,7 +35,7 @@ namespace ix
 | 
			
		||||
        if (_queues[game].empty())
 | 
			
		||||
        {
 | 
			
		||||
            Json::Value val;
 | 
			
		||||
            return std::make_pair(val, std::string());
 | 
			
		||||
            return val;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto msg = _queues[game].front();
 | 
			
		||||
@@ -44,7 +43,7 @@ namespace ix
 | 
			
		||||
        return msg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void QueueManager::add(const Json::Value& msg, const std::string& position)
 | 
			
		||||
    void QueueManager::add(Json::Value msg)
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> lock(_mutex);
 | 
			
		||||
 | 
			
		||||
@@ -60,8 +59,8 @@ namespace ix
 | 
			
		||||
        // in queuing too many events.
 | 
			
		||||
        if (_queues[game].size() < _maxQueueSize)
 | 
			
		||||
        {
 | 
			
		||||
            _queues[game].push(std::make_pair(msg, position));
 | 
			
		||||
            _queues[game].push(msg);
 | 
			
		||||
            _condition.notify_one();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
} // namespace ix
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,12 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <json/json.h>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <json/json.h>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
@@ -23,13 +23,13 @@ namespace ix
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::pair<Json::Value, std::string> pop();
 | 
			
		||||
        void add(const Json::Value& msg, const std::string& position);
 | 
			
		||||
        Json::Value pop();
 | 
			
		||||
        void add(Json::Value msg);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        std::map<std::string, std::queue<std::pair<Json::Value, std::string>>> _queues;
 | 
			
		||||
        std::map<std::string, std::queue<Json::Value>> _queues;
 | 
			
		||||
        std::mutex _mutex;
 | 
			
		||||
        std::condition_variable _condition;
 | 
			
		||||
        size_t _maxQueueSize;
 | 
			
		||||
    };
 | 
			
		||||
} // namespace ix
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -234,7 +233,7 @@ namespace ix
 | 
			
		||||
        args->transferTimeout = 5 * 60;
 | 
			
		||||
        args->followRedirects = true;
 | 
			
		||||
        args->verbose = verbose;
 | 
			
		||||
        args->logger = [](const std::string& msg) { CoreLogger::log(msg.c_str()); };
 | 
			
		||||
        args->logger = [](const std::string& msg) { ix::IXCoreLogger::Log(msg.c_str()); };
 | 
			
		||||
 | 
			
		||||
        std::string body = computePayload(msg);
 | 
			
		||||
        HttpResponsePtr response = _httpClient->post(_url, body, args);
 | 
			
		||||
@@ -246,21 +245,24 @@ namespace ix
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
@@ -271,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;
 | 
			
		||||
@@ -280,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
 | 
			
		||||
{
 | 
			
		||||
@@ -28,16 +28,13 @@ namespace ix
 | 
			
		||||
        // 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);
 | 
			
		||||
 | 
			
		||||
        void uploadPayload(const Json::Value& payload,
 | 
			
		||||
                           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);
 | 
			
		||||
 | 
			
		||||
    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());
 | 
			
		||||
                    });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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"
 | 
			
		||||
 | 
			
		||||
@@ -195,66 +194,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
 | 
			
		||||
     */
 | 
			
		||||
@@ -463,32 +402,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
 | 
			
		||||
        {
 | 
			
		||||
@@ -598,35 +525,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);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@@ -813,5 +731,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);
 | 
			
		||||
@@ -61,5 +59,3 @@ namespace ix
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
} // 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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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.2"
 | 
			
		||||
#define IX_WEBSOCKET_VERSION "9.2.1"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
# Author: Benjamin Sergeant
 | 
			
		||||
# Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
cmake_minimum_required (VERSION 3.4.1)
 | 
			
		||||
project (luarocks)
 | 
			
		||||
 | 
			
		||||
# There's -Weverything too for clang
 | 
			
		||||
if (NOT WIN32)
 | 
			
		||||
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
set (CMAKE_CXX_STANDARD 14)
 | 
			
		||||
 | 
			
		||||
option(USE_TLS "Add TLS support" ON)
 | 
			
		||||
 | 
			
		||||
include_directories(luarocks .)
 | 
			
		||||
 | 
			
		||||
add_executable(luarocks main.cpp)
 | 
			
		||||
 | 
			
		||||
include(FindLua)
 | 
			
		||||
find_package(lua REQUIRED)
 | 
			
		||||
target_link_libraries(luarocks ${LUA_LIBRARIES})
 | 
			
		||||
target_include_directories(luarocks PRIVATE ${LUA_INCLUDE_DIR})
 | 
			
		||||
 | 
			
		||||
# library with the most dependencies come first
 | 
			
		||||
target_link_libraries(luarocks ixwebsocket)
 | 
			
		||||
 | 
			
		||||
install(TARGETS luarocks RUNTIME DESTINATION bin)
 | 
			
		||||
@@ -1,163 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  LuaWebSocket.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
extern "C"
 | 
			
		||||
{
 | 
			
		||||
    #include "lua.h"
 | 
			
		||||
    #include "lauxlib.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include "luawrapper.hpp"
 | 
			
		||||
#include <ixwebsocket/IXWebSocket.h>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class LuaWebSocket
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
            LuaWebSocket()
 | 
			
		||||
            {
 | 
			
		||||
                _webSocket.setOnMessageCallback([this](const WebSocketMessagePtr& msg) {
 | 
			
		||||
                    if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
                    {
 | 
			
		||||
                        std::lock_guard<std::mutex> lock(_queueMutex);
 | 
			
		||||
                        _queue.push(msg->str);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void setUrl(const std::string& url) { _webSocket.setUrl(url); }
 | 
			
		||||
            const std::string& getUrl() { return _webSocket.getUrl(); }
 | 
			
		||||
            void start() { _webSocket.start(); }
 | 
			
		||||
            void send(const std::string& msg) { _webSocket.send(msg); }
 | 
			
		||||
 | 
			
		||||
            const std::string& getMessage()
 | 
			
		||||
            {
 | 
			
		||||
                std::lock_guard<std::mutex> lock(_queueMutex);
 | 
			
		||||
                return _queue.front();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool hasMessage()
 | 
			
		||||
            {
 | 
			
		||||
                std::lock_guard<std::mutex> lock(_queueMutex);
 | 
			
		||||
                return !_queue.empty();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void popMessage()
 | 
			
		||||
            {
 | 
			
		||||
                std::lock_guard<std::mutex> lock(_queueMutex);
 | 
			
		||||
                _queue.pop();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            WebSocket _webSocket;
 | 
			
		||||
 | 
			
		||||
            std::queue<std::string> _queue;
 | 
			
		||||
            std::mutex _queueMutex;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    LuaWebSocket* WebSocket_new(lua_State* /*L*/)
 | 
			
		||||
    {
 | 
			
		||||
        LuaWebSocket* webSocket = new LuaWebSocket();
 | 
			
		||||
        return webSocket;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int WebSocket_getUrl(lua_State* L)
 | 
			
		||||
    {
 | 
			
		||||
        LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
 | 
			
		||||
        lua_pushstring(L, luaWebSocket->getUrl().c_str());
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int WebSocket_setUrl(lua_State* L)
 | 
			
		||||
    {
 | 
			
		||||
        LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
 | 
			
		||||
        const char* url = luaL_checkstring(L, 2);
 | 
			
		||||
        luaWebSocket->setUrl(url);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int WebSocket_start(lua_State* L)
 | 
			
		||||
    {
 | 
			
		||||
        LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
 | 
			
		||||
        luaWebSocket->start();
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int WebSocket_send(lua_State* L)
 | 
			
		||||
    {
 | 
			
		||||
        LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
 | 
			
		||||
        const char* msg = luaL_checkstring(L, 2);
 | 
			
		||||
        luaWebSocket->send(msg);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int WebSocket_getMessage(lua_State* L)
 | 
			
		||||
    {
 | 
			
		||||
        LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
 | 
			
		||||
        lua_pushstring(L, luaWebSocket->getMessage().c_str());
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int WebSocket_hasMessage(lua_State* L)
 | 
			
		||||
    {
 | 
			
		||||
        LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
 | 
			
		||||
        lua_pushboolean(L, luaWebSocket->hasMessage());
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int WebSocket_popMessage(lua_State* L)
 | 
			
		||||
    {
 | 
			
		||||
        LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
 | 
			
		||||
        luaWebSocket->popMessage();
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // FIXME: This should be a static method, or be part of a different module
 | 
			
		||||
    int WebSocket_sleep(lua_State* L)
 | 
			
		||||
    {
 | 
			
		||||
        // LuaWebSocket* luaWebSocket = luaW_check<LuaWebSocket>(L, 1);
 | 
			
		||||
        auto duration = luaL_checkinteger(L, 2);
 | 
			
		||||
        std::this_thread::sleep_for(std::chrono::milliseconds(duration));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static luaL_Reg WebSocket_table[] = {
 | 
			
		||||
        { NULL, NULL }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    static luaL_Reg WebSocket_metatable[] = {
 | 
			
		||||
        { "getUrl", WebSocket_getUrl },
 | 
			
		||||
        { "setUrl", WebSocket_setUrl },
 | 
			
		||||
        { "start", WebSocket_start },
 | 
			
		||||
        { "send", WebSocket_send },
 | 
			
		||||
        { "getMessage", WebSocket_getMessage },
 | 
			
		||||
        { "hasMessage", WebSocket_hasMessage },
 | 
			
		||||
        { "popMessage", WebSocket_popMessage },
 | 
			
		||||
        { "sleep", WebSocket_sleep },
 | 
			
		||||
        { NULL, NULL }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    int luaopen_WebSocket(lua_State* L)
 | 
			
		||||
    {
 | 
			
		||||
        luaW_register<LuaWebSocket>(L,
 | 
			
		||||
            "WebSocket",
 | 
			
		||||
            WebSocket_table,
 | 
			
		||||
            WebSocket_metatable,
 | 
			
		||||
            WebSocket_new
 | 
			
		||||
            );
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,56 +0,0 @@
 | 
			
		||||
#ifndef PLAYER_HPP
 | 
			
		||||
#define PLAYER_HPP
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
class Player
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    Player(const char* name, unsigned int health) :
 | 
			
		||||
        m_Name(name),
 | 
			
		||||
        m_Health(health)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void info()
 | 
			
		||||
    {
 | 
			
		||||
        std::cout << m_Name << " have " << m_Health << " HP" << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void say(const char* text)
 | 
			
		||||
    {
 | 
			
		||||
        std::cout << m_Name << ": " << text << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void heal(Player* target)
 | 
			
		||||
    {
 | 
			
		||||
        target->setHealth(100);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const char* getName()
 | 
			
		||||
    {
 | 
			
		||||
        return m_Name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unsigned int getHealth()
 | 
			
		||||
    {
 | 
			
		||||
        return m_Health;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool setHealth(unsigned int health)
 | 
			
		||||
    {
 | 
			
		||||
        if (health >= 0 && health <= 100)
 | 
			
		||||
        {
 | 
			
		||||
            m_Health = health;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    const char* m_Name;
 | 
			
		||||
    unsigned int m_Health;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // PLAYER_HPP
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
Wrapper based on https://github.com/LuaxY/cpp-lua
 | 
			
		||||
 | 
			
		||||
Examples to build C++
 | 
			
		||||
https://github.com/siffiejoe/lua-fltk4lua/blob/master/fltk4lua-scm-0.rockspec
 | 
			
		||||
https://github.com/lua4web/refser/blob/master/rockspecs/refser-0.2-1.rockspec
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
--
 | 
			
		||||
-- make luarocks && (cd luarocks ; ../build/luarocks/luarocks)
 | 
			
		||||
--
 | 
			
		||||
-- ... git push test with ssh key
 | 
			
		||||
--
 | 
			
		||||
local webSocket = WebSocket.new()
 | 
			
		||||
 | 
			
		||||
webSocket:setUrl("ws://localhost:8008")
 | 
			
		||||
print("Url: " .. webSocket:getUrl())
 | 
			
		||||
 | 
			
		||||
-- Start the background thread
 | 
			
		||||
webSocket:start()
 | 
			
		||||
 | 
			
		||||
local i = 0
 | 
			
		||||
 | 
			
		||||
while true
 | 
			
		||||
do
 | 
			
		||||
    print("Sending message...")
 | 
			
		||||
    webSocket:send("msg_" .. tostring(i));
 | 
			
		||||
    i = i + 1
 | 
			
		||||
 | 
			
		||||
    print("Waiting 1s...")
 | 
			
		||||
    webSocket:sleep(1000)
 | 
			
		||||
 | 
			
		||||
    if webSocket:hasMessage() then
 | 
			
		||||
        local msg = webSocket:getMessage()
 | 
			
		||||
        print("Received: " .. msg)
 | 
			
		||||
        webSocket:popMessage()
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  functions.hpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
int lua_info(lua_State* /*L*/)
 | 
			
		||||
{
 | 
			
		||||
    std::cout << "C++ Lua v0.1" << std::endl << std::endl;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,709 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2010-2013 Alexander Ames
 | 
			
		||||
 * Alexander.Ames@gmail.com
 | 
			
		||||
 * See Copyright Notice at the end of this file
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// API Summary:
 | 
			
		||||
//
 | 
			
		||||
// LuaWrapper is a library designed to help bridge the gab between Lua and
 | 
			
		||||
// C++. It is designed to be small (a single header file), simple, fast,
 | 
			
		||||
// and typesafe. It has no external dependencies, and does not need to be
 | 
			
		||||
// precompiled; the header can simply be dropped into a project and used
 | 
			
		||||
// immediately. It even supports class inheritance to a certain degree. Objects
 | 
			
		||||
// can be created in either Lua or C++, and passed back and forth.
 | 
			
		||||
//
 | 
			
		||||
// The main functions of interest are the following:
 | 
			
		||||
//  luaW_is<T>
 | 
			
		||||
//  luaW_to<T>
 | 
			
		||||
//  luaW_check<T>
 | 
			
		||||
//  luaW_push<T>
 | 
			
		||||
//  luaW_register<T>
 | 
			
		||||
//  luaW_setfuncs<T>
 | 
			
		||||
//  luaW_extend<T, U>
 | 
			
		||||
//  luaW_hold<T>
 | 
			
		||||
//  luaW_release<T>
 | 
			
		||||
//
 | 
			
		||||
// These functions allow you to manipulate arbitrary classes just like you
 | 
			
		||||
// would the primitive types (e.g. numbers or strings). If you are familiar
 | 
			
		||||
// with the normal Lua API the behavior of these functions should be very
 | 
			
		||||
// intuative.
 | 
			
		||||
//
 | 
			
		||||
// For more information see the README and the comments below
 | 
			
		||||
 | 
			
		||||
#ifndef LUA_WRAPPER_H_
 | 
			
		||||
#define LUA_WRAPPER_H_
 | 
			
		||||
 | 
			
		||||
// If you are linking against Lua compiled in C++, define LUAW_NO_EXTERN_C
 | 
			
		||||
#ifndef LUAW_NO_EXTERN_C
 | 
			
		||||
extern "C"
 | 
			
		||||
{
 | 
			
		||||
#endif // LUAW_NO_EXTERN_C
 | 
			
		||||
 | 
			
		||||
#include "lua.h"
 | 
			
		||||
#include "lauxlib.h"
 | 
			
		||||
    
 | 
			
		||||
#ifndef LUAW_NO_EXTERN_C
 | 
			
		||||
}
 | 
			
		||||
#endif // LUAW_NO_EXTERN_C
 | 
			
		||||
 | 
			
		||||
#define LUAW_POSTCTOR_KEY "__postctor"
 | 
			
		||||
#define LUAW_EXTENDS_KEY "__extends"
 | 
			
		||||
#define LUAW_STORAGE_KEY "storage"
 | 
			
		||||
#define LUAW_CACHE_KEY "cache"
 | 
			
		||||
#define LUAW_CACHE_METATABLE_KEY "cachemetatable"
 | 
			
		||||
#define LUAW_HOLDS_KEY "holds"
 | 
			
		||||
#define LUAW_WRAPPER_KEY "LuaWrapper"
 | 
			
		||||
 | 
			
		||||
// A simple utility function to adjust a given index
 | 
			
		||||
// Useful for when a parameter index needs to be adjusted
 | 
			
		||||
// after pushing or popping things off the stack
 | 
			
		||||
inline int luaW_correctindex(lua_State* /*L*/, int index, int correction)
 | 
			
		||||
{
 | 
			
		||||
    return index < 0 ? index - correction : index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// These are the default allocator and deallocator. If you would prefer an
 | 
			
		||||
// alternative option, you may select a different function when registering
 | 
			
		||||
// your class.
 | 
			
		||||
template <typename T>
 | 
			
		||||
T* luaW_defaultallocator(lua_State*)
 | 
			
		||||
{
 | 
			
		||||
    return new T();
 | 
			
		||||
}
 | 
			
		||||
template <typename T>
 | 
			
		||||
void luaW_defaultdeallocator(lua_State*, T* obj)
 | 
			
		||||
{
 | 
			
		||||
    delete obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The identifier function is responsible for pushing a value unique to each
 | 
			
		||||
// object on to the stack. Most of the time, this can simply be the address
 | 
			
		||||
// of the pointer, but sometimes that is not adaquate. For example, if you
 | 
			
		||||
// are using shared_ptr you would need to push the address of the object the
 | 
			
		||||
// shared_ptr represents, rather than the address of the shared_ptr itself.
 | 
			
		||||
template <typename T>
 | 
			
		||||
void luaW_defaultidentifier(lua_State* L, T* obj)
 | 
			
		||||
{
 | 
			
		||||
    lua_pushlightuserdata(L, obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This class is what is used by LuaWrapper to contain the userdata. data
 | 
			
		||||
// stores a pointer to the object itself, and cast is used to cast toward the
 | 
			
		||||
// base class if there is one and it is necessary. Rather than use RTTI and
 | 
			
		||||
// typid to compare types, I use the clever trick of using the cast to compare
 | 
			
		||||
// types. Because there is at most one cast per type, I can use it to identify
 | 
			
		||||
// when and object is the type I want. This is only used internally.
 | 
			
		||||
struct luaW_Userdata
 | 
			
		||||
{
 | 
			
		||||
    luaW_Userdata(void* vptr = NULL, luaW_Userdata (*udcast)(const luaW_Userdata&) = NULL)
 | 
			
		||||
        : data(vptr), cast(udcast) {}
 | 
			
		||||
    void* data;
 | 
			
		||||
    luaW_Userdata (*cast)(const luaW_Userdata&);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// This class cannot actually to be instantiated. It is used only hold the
 | 
			
		||||
// table name and other information.
 | 
			
		||||
template <typename T>
 | 
			
		||||
class LuaWrapper
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    static const char* classname;
 | 
			
		||||
    static void (*identifier)(lua_State*, T*);
 | 
			
		||||
    static T* (*allocator)(lua_State*);
 | 
			
		||||
    static void (*deallocator)(lua_State*, T*);
 | 
			
		||||
    static luaW_Userdata (*cast)(const luaW_Userdata&);
 | 
			
		||||
private:
 | 
			
		||||
    LuaWrapper();
 | 
			
		||||
};
 | 
			
		||||
template <typename T> const char* LuaWrapper<T>::classname;
 | 
			
		||||
template <typename T> void (*LuaWrapper<T>::identifier)(lua_State*, T*);
 | 
			
		||||
template <typename T> T* (*LuaWrapper<T>::allocator)(lua_State*);
 | 
			
		||||
template <typename T> void (*LuaWrapper<T>::deallocator)(lua_State*, T*);
 | 
			
		||||
template <typename T> luaW_Userdata (*LuaWrapper<T>::cast)(const luaW_Userdata&);
 | 
			
		||||
 | 
			
		||||
// Cast from an object of type T to an object of type U. This template
 | 
			
		||||
// function is instantiated by calling luaW_extend<T, U>(L). This is only used
 | 
			
		||||
// internally.
 | 
			
		||||
template <typename T, typename U>
 | 
			
		||||
luaW_Userdata luaW_cast(const luaW_Userdata& obj)
 | 
			
		||||
{
 | 
			
		||||
    return luaW_Userdata(static_cast<U*>(static_cast<T*>(obj.data)), LuaWrapper<U>::cast);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U>
 | 
			
		||||
void luaW_identify(lua_State* L, T* obj)
 | 
			
		||||
{
 | 
			
		||||
    LuaWrapper<U>::identifier(L, static_cast<U*>(obj));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void luaW_wrapperfield(lua_State* L, const char* field)
 | 
			
		||||
{
 | 
			
		||||
    lua_getfield(L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
 | 
			
		||||
    lua_getfield(L, -1, field); // ... LuaWrapper LuaWrapper.field
 | 
			
		||||
    lua_getfield(L, -1, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.field LuaWrapper.field.class
 | 
			
		||||
    lua_replace(L, -3); // ... LuaWrapper.field.class LuaWrapper.field
 | 
			
		||||
    lua_pop(L, 1); // ... LuaWrapper.field.class
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Analogous to lua_is(boolean|string|*)
 | 
			
		||||
//
 | 
			
		||||
// Returns 1 if the value at the given acceptable index is of type T (or if
 | 
			
		||||
// strict is false, convertable to type T) and 0 otherwise.
 | 
			
		||||
template <typename T>
 | 
			
		||||
bool luaW_is(lua_State *L, int index, bool strict = false)
 | 
			
		||||
{
 | 
			
		||||
    bool equal = false;// lua_isnil(L, index);
 | 
			
		||||
    if (!equal && lua_isuserdata(L, index) && lua_getmetatable(L, index))
 | 
			
		||||
    {
 | 
			
		||||
        // ... ud ... udmt
 | 
			
		||||
        luaL_getmetatable(L, LuaWrapper<T>::classname); // ... ud ... udmt Tmt
 | 
			
		||||
        equal = lua_rawequal(L, -1, -2) != 0;
 | 
			
		||||
        if (!equal && !strict)
 | 
			
		||||
        {
 | 
			
		||||
            lua_getfield(L, -2, LUAW_EXTENDS_KEY); // ... ud ... udmt Tmt udmt.extends
 | 
			
		||||
            for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
 | 
			
		||||
            {
 | 
			
		||||
                // ... ud ... udmt Tmt udmt.extends k v
 | 
			
		||||
                equal = lua_rawequal(L, -1, -4) != 0;
 | 
			
		||||
                if (equal)
 | 
			
		||||
                {
 | 
			
		||||
                    lua_pop(L, 2); // ... ud ... udmt Tmt udmt.extends
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            lua_pop(L, 1); // ... ud ... udmt Tmt
 | 
			
		||||
        }
 | 
			
		||||
        lua_pop(L, 2); // ... ud ...
 | 
			
		||||
    }
 | 
			
		||||
    return equal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Analogous to lua_to(boolean|string|*)
 | 
			
		||||
//
 | 
			
		||||
// Converts the given acceptable index to a T*. That value must be of (or
 | 
			
		||||
// convertable to) type T; otherwise, returns NULL.
 | 
			
		||||
template <typename T>
 | 
			
		||||
T* luaW_to(lua_State* L, int index, bool strict = false)
 | 
			
		||||
{
 | 
			
		||||
    if (luaW_is<T>(L, index, strict))
 | 
			
		||||
    {
 | 
			
		||||
        luaW_Userdata* pud = static_cast<luaW_Userdata*>(lua_touserdata(L, index));
 | 
			
		||||
        luaW_Userdata ud;
 | 
			
		||||
        while (!strict && LuaWrapper<T>::cast != pud->cast)
 | 
			
		||||
        {
 | 
			
		||||
            ud = pud->cast(*pud);
 | 
			
		||||
            pud = &ud;
 | 
			
		||||
        }
 | 
			
		||||
        return static_cast<T*>(pud->data);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Analogous to luaL_check(boolean|string|*)
 | 
			
		||||
//
 | 
			
		||||
// Converts the given acceptable index to a T*. That value must be of (or
 | 
			
		||||
// convertable to) type T; otherwise, an error is raised.
 | 
			
		||||
template <typename T>
 | 
			
		||||
T* luaW_check(lua_State* L, int index, bool strict = false)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = NULL;
 | 
			
		||||
    if (luaW_is<T>(L, index, strict))
 | 
			
		||||
    {
 | 
			
		||||
        luaW_Userdata* pud = (luaW_Userdata*)lua_touserdata(L, index);
 | 
			
		||||
        luaW_Userdata ud;
 | 
			
		||||
        while (!strict && LuaWrapper<T>::cast != pud->cast)
 | 
			
		||||
        {
 | 
			
		||||
            ud = pud->cast(*pud);
 | 
			
		||||
            pud = &ud;
 | 
			
		||||
        }
 | 
			
		||||
        obj = (T*)pud->data;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        const char *msg = lua_pushfstring(L, "%s expected, got %s", LuaWrapper<T>::classname, luaL_typename(L, index));
 | 
			
		||||
        luaL_argerror(L, index, msg);
 | 
			
		||||
    }
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
T* luaW_opt(lua_State* L, int index, T* fallback = NULL, bool strict = false)
 | 
			
		||||
{
 | 
			
		||||
    if (lua_isnil(L, index))
 | 
			
		||||
        return fallback;
 | 
			
		||||
    else
 | 
			
		||||
        return luaW_check<T>(L, index, strict);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Analogous to lua_push(boolean|string|*)
 | 
			
		||||
//
 | 
			
		||||
// Pushes a userdata of type T onto the stack. If this object already exists in
 | 
			
		||||
// the Lua environment, it will assign the existing storage table to it.
 | 
			
		||||
// Otherwise, a new storage table will be created for it.
 | 
			
		||||
template <typename T>
 | 
			
		||||
void luaW_push(lua_State* L, T* obj)
 | 
			
		||||
{
 | 
			
		||||
    if (obj)
 | 
			
		||||
    {
 | 
			
		||||
        LuaWrapper<T>::identifier(L, obj); // ... id
 | 
			
		||||
        luaW_wrapperfield<T>(L, LUAW_CACHE_KEY); // ... id cache
 | 
			
		||||
        lua_pushvalue(L, -2); // ... id cache id
 | 
			
		||||
        lua_gettable(L, -2); // ... id cache obj
 | 
			
		||||
        if (lua_isnil(L, -1))
 | 
			
		||||
        {
 | 
			
		||||
            // Create the new luaW_userdata and place it in the cache
 | 
			
		||||
            lua_pop(L, 1); // ... id cache
 | 
			
		||||
            lua_insert(L, -2); // ... cache id
 | 
			
		||||
            luaW_Userdata* ud = static_cast<luaW_Userdata*>(lua_newuserdata(L, sizeof(luaW_Userdata))); // ... cache id obj
 | 
			
		||||
            ud->data = obj;
 | 
			
		||||
            ud->cast = LuaWrapper<T>::cast;
 | 
			
		||||
            lua_pushvalue(L, -1); // ... cache id obj obj
 | 
			
		||||
            lua_insert(L, -4); // ... obj cache id obj
 | 
			
		||||
            lua_settable(L, -3); // ... obj cache
 | 
			
		||||
 | 
			
		||||
            luaL_getmetatable(L, LuaWrapper<T>::classname); // ... obj cache mt
 | 
			
		||||
            lua_setmetatable(L, -3); // ... obj cache
 | 
			
		||||
 | 
			
		||||
            lua_pop(L, 1); // ... obj
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            lua_replace(L, -3); // ... obj cache
 | 
			
		||||
            lua_pop(L, 1); // ... obj
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        lua_pushnil(L);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Instructs LuaWrapper that it owns the userdata, and can manage its memory.
 | 
			
		||||
// When all references to the object are removed, Lua is free to garbage
 | 
			
		||||
// collect it and delete the object.
 | 
			
		||||
//
 | 
			
		||||
// Returns true if luaW_hold took hold of the object, and false if it was
 | 
			
		||||
// already held
 | 
			
		||||
template <typename T>
 | 
			
		||||
bool luaW_hold(lua_State* L, T* obj)
 | 
			
		||||
{
 | 
			
		||||
    luaW_wrapperfield<T>(L, LUAW_HOLDS_KEY); // ... holds
 | 
			
		||||
    LuaWrapper<T>::identifier(L, obj); // ... holds id
 | 
			
		||||
    lua_pushvalue(L, -1); // ... holds id id
 | 
			
		||||
    lua_gettable(L, -3); // ... holds id hold
 | 
			
		||||
    // If it's not held, hold it
 | 
			
		||||
    if (!lua_toboolean(L, -1))
 | 
			
		||||
    {
 | 
			
		||||
        // Apply hold boolean
 | 
			
		||||
        lua_pop(L, 1); // ... holds id
 | 
			
		||||
        lua_pushboolean(L, true); // ... holds id true
 | 
			
		||||
        lua_settable(L, -3); // ... holds
 | 
			
		||||
        lua_pop(L, 1); // ...
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    lua_pop(L, 3); // ...
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Releases LuaWrapper's hold on an object. This allows the user to remove
 | 
			
		||||
// all references to an object in Lua and ensure that Lua will not attempt to
 | 
			
		||||
// garbage collect it.
 | 
			
		||||
//
 | 
			
		||||
// This function takes the index of the identifier for an object rather than
 | 
			
		||||
// the object itself. This is because needs to be able to run after the object
 | 
			
		||||
// has already been deallocated. A wrapper is provided for when it is more
 | 
			
		||||
// convenient to pass in the object directly.
 | 
			
		||||
template <typename T>
 | 
			
		||||
void luaW_release(lua_State* L, int index)
 | 
			
		||||
{
 | 
			
		||||
    luaW_wrapperfield<T>(L, LUAW_HOLDS_KEY); // ... id ... holds
 | 
			
		||||
    lua_pushvalue(L, luaW_correctindex(L, index, 1)); // ... id ... holds id
 | 
			
		||||
    lua_pushnil(L); // ... id ... holds id nil
 | 
			
		||||
    lua_settable(L, -3); // ... id ... holds
 | 
			
		||||
    lua_pop(L, 1); // ... id ...
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
void luaW_release(lua_State* L, T* obj)
 | 
			
		||||
{
 | 
			
		||||
    LuaWrapper<T>::identifier(L, obj); // ... id
 | 
			
		||||
    luaW_release<T>(L, -1); // ... id
 | 
			
		||||
    lua_pop(L, 1); // ...
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function is called from Lua, not C++
 | 
			
		||||
//
 | 
			
		||||
// Calls the lua post-constructor (LUAW_POSTCTOR_KEY or "__postctor") on a
 | 
			
		||||
// userdata. Assumes the userdata is on top of the stack, and numargs arguments
 | 
			
		||||
// are below it. This runs the LUAW_POSTCTOR_KEY function on T's metatable,
 | 
			
		||||
// using the object as the first argument and whatever else is below it as
 | 
			
		||||
// the rest of the arguments This exists to allow types to adjust values in
 | 
			
		||||
// thier storage table, which can not be created until after the constructor is
 | 
			
		||||
// called.
 | 
			
		||||
template <typename T>
 | 
			
		||||
void luaW_postconstructor(lua_State* L, int numargs)
 | 
			
		||||
{
 | 
			
		||||
    // ... args... ud
 | 
			
		||||
    lua_getfield(L, -1, LUAW_POSTCTOR_KEY); // ... args... ud ud.__postctor
 | 
			
		||||
    if (lua_type(L, -1) == LUA_TFUNCTION)
 | 
			
		||||
    {
 | 
			
		||||
        lua_pushvalue(L, -2); // ... args... ud ud.__postctor ud
 | 
			
		||||
        lua_insert(L, -3 - numargs); // ... ud args... ud ud.__postctor
 | 
			
		||||
        lua_insert(L, -3 - numargs); // ... ud.__postctor ud args... ud
 | 
			
		||||
        lua_insert(L, -3 - numargs); // ... ud ud.__postctor ud args...
 | 
			
		||||
        lua_call(L, numargs + 1, 0); // ... ud
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        lua_pop(L, 1); // ... ud
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function is generally called from Lua, not C++
 | 
			
		||||
//
 | 
			
		||||
// Creates an object of type T using the constructor and subsequently calls the
 | 
			
		||||
// post-constructor on it.
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline int luaW_new(lua_State* L, int args)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = LuaWrapper<T>::allocator(L);
 | 
			
		||||
    luaW_push<T>(L, obj);
 | 
			
		||||
    luaW_hold<T>(L, obj);
 | 
			
		||||
    luaW_postconstructor<T>(L, args);
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
int luaW_new(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    return luaW_new<T>(L, lua_gettop(L));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function is called from Lua, not C++
 | 
			
		||||
//
 | 
			
		||||
// The default metamethod to call when indexing into lua userdata representing
 | 
			
		||||
// an object of type T. This will first check the userdata's environment table
 | 
			
		||||
// and if it's not found there it will check the metatable. This is done so
 | 
			
		||||
// individual userdata can be treated as a table, and can hold thier own
 | 
			
		||||
// values.
 | 
			
		||||
template <typename T>
 | 
			
		||||
int luaW_index(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    // obj key
 | 
			
		||||
    T* obj = luaW_to<T>(L, 1);
 | 
			
		||||
    luaW_wrapperfield<T>(L, LUAW_STORAGE_KEY); // obj key storage
 | 
			
		||||
    LuaWrapper<T>::identifier(L, obj); // obj key storage id
 | 
			
		||||
    lua_gettable(L, -2); // obj key storage store
 | 
			
		||||
 | 
			
		||||
    // Check if storage table exists
 | 
			
		||||
    if (!lua_isnil(L, -1))
 | 
			
		||||
    {
 | 
			
		||||
        lua_pushvalue(L, -3); // obj key storage store key
 | 
			
		||||
        lua_gettable(L, -2); // obj key storage store store[k]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If either there is no storage table or the key wasn't found
 | 
			
		||||
    // then fall back to the metatable
 | 
			
		||||
    if (lua_isnil(L, -1))
 | 
			
		||||
    {
 | 
			
		||||
        lua_settop(L, 2); // obj key
 | 
			
		||||
        lua_getmetatable(L, -2); // obj key mt
 | 
			
		||||
        lua_pushvalue(L, -2); // obj key mt k
 | 
			
		||||
        lua_gettable(L, -2); // obj key mt mt[k]
 | 
			
		||||
    }
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function is called from Lua, not C++
 | 
			
		||||
//
 | 
			
		||||
// The default metamethod to call when creating a new index on lua userdata
 | 
			
		||||
// representing an object of type T. This will index into the the userdata's
 | 
			
		||||
// environment table that it keeps for personal storage. This is done so
 | 
			
		||||
// individual userdata can be treated as a table, and can hold thier own
 | 
			
		||||
// values.
 | 
			
		||||
template <typename T>
 | 
			
		||||
int luaW_newindex(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    // obj key value
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    luaW_wrapperfield<T>(L, LUAW_STORAGE_KEY); // obj key value storage
 | 
			
		||||
    LuaWrapper<T>::identifier(L, obj); // obj key value storage id
 | 
			
		||||
    lua_pushvalue(L, -1); // obj key value storage id id
 | 
			
		||||
    lua_gettable(L, -3); // obj key value storage id store
 | 
			
		||||
 | 
			
		||||
    // Add the storage table if there isn't one already
 | 
			
		||||
    if (lua_isnil(L, -1))
 | 
			
		||||
    {
 | 
			
		||||
        lua_pop(L, 1); // obj key value storage id
 | 
			
		||||
        lua_newtable(L); // obj key value storage id store
 | 
			
		||||
        lua_pushvalue(L, -1); // obj key value storage id store store
 | 
			
		||||
        lua_insert(L, -3); // obj key value storage store id store
 | 
			
		||||
        lua_settable(L, -4); // obj key value storage store
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lua_pushvalue(L, 2); // obj key value ... store key
 | 
			
		||||
    lua_pushvalue(L, 3); // obj key value ... store key value
 | 
			
		||||
    lua_settable(L, -3); // obj key value ... store
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function is called from Lua, not C++
 | 
			
		||||
//
 | 
			
		||||
// The __gc metamethod handles cleaning up userdata. The userdata's reference
 | 
			
		||||
// count is decremented and if this is the final reference to the userdata its
 | 
			
		||||
// environment table is nil'd and pointer deleted with the destructor callback.
 | 
			
		||||
template <typename T>
 | 
			
		||||
int luaW_gc(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    // obj
 | 
			
		||||
    T* obj = luaW_to<T>(L, 1);
 | 
			
		||||
    LuaWrapper<T>::identifier(L, obj); // obj key value storage id
 | 
			
		||||
    luaW_wrapperfield<T>(L, LUAW_HOLDS_KEY); // obj id counts count holds
 | 
			
		||||
    lua_pushvalue(L, 2); // obj id counts count holds id
 | 
			
		||||
    lua_gettable(L, -2); // obj id counts count holds hold
 | 
			
		||||
    if (lua_toboolean(L, -1) && LuaWrapper<T>::deallocator)
 | 
			
		||||
    {
 | 
			
		||||
        LuaWrapper<T>::deallocator(L, obj);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    luaW_wrapperfield<T>(L, LUAW_STORAGE_KEY); // obj id counts count holds hold storage
 | 
			
		||||
    lua_pushvalue(L, 2); // obj id counts count holds hold storage id
 | 
			
		||||
    lua_pushnil(L); // obj id counts count holds hold storage id nil
 | 
			
		||||
    lua_settable(L, -3); // obj id counts count holds hold storage
 | 
			
		||||
 | 
			
		||||
    luaW_release<T>(L, 2);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Thakes two tables and registers them with Lua to the table on the top of the
 | 
			
		||||
// stack. 
 | 
			
		||||
//
 | 
			
		||||
// This function is only called from LuaWrapper internally. 
 | 
			
		||||
inline void luaW_registerfuncs(lua_State* L, const luaL_Reg defaulttable[], const luaL_Reg table[])
 | 
			
		||||
{
 | 
			
		||||
    // ... T
 | 
			
		||||
#if LUA_VERSION_NUM > 501
 | 
			
		||||
    if (defaulttable)
 | 
			
		||||
        luaL_setfuncs(L, defaulttable, 0); // ... T
 | 
			
		||||
    if (table)
 | 
			
		||||
        luaL_setfuncs(L, table, 0); // ... T
 | 
			
		||||
#else
 | 
			
		||||
    if (defaulttable)
 | 
			
		||||
        luaL_register(L, NULL, defaulttable); // ... T
 | 
			
		||||
    if (table)
 | 
			
		||||
        luaL_register(L, NULL, table); // ... T
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Initializes the LuaWrapper tables used to track internal state. 
 | 
			
		||||
//
 | 
			
		||||
// This function is only called from LuaWrapper internally. 
 | 
			
		||||
inline void luaW_initialize(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    // Ensure that the LuaWrapper table is set up
 | 
			
		||||
    lua_getfield(L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
 | 
			
		||||
    if (lua_isnil(L, -1))
 | 
			
		||||
    {
 | 
			
		||||
        lua_newtable(L); // ... nil {}
 | 
			
		||||
        lua_pushvalue(L, -1); // ... nil {} {}
 | 
			
		||||
        lua_setfield(L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... nil LuaWrapper
 | 
			
		||||
        
 | 
			
		||||
        // Create a storage table        
 | 
			
		||||
        lua_newtable(L); // ... LuaWrapper nil {}
 | 
			
		||||
        lua_setfield(L, -2, LUAW_STORAGE_KEY); // ... nil LuaWrapper
 | 
			
		||||
        
 | 
			
		||||
        // Create a holds table
 | 
			
		||||
        lua_newtable(L); // ... LuaWrapper {}
 | 
			
		||||
        lua_setfield(L, -2, LUAW_HOLDS_KEY); // ... nil LuaWrapper
 | 
			
		||||
 | 
			
		||||
        // Create a cache table, with weak values so that the userdata will not
 | 
			
		||||
        // be ref counted
 | 
			
		||||
        lua_newtable(L); // ... nil LuaWrapper {}
 | 
			
		||||
        lua_setfield(L, -2, LUAW_CACHE_KEY); // ... nil LuaWrapper
 | 
			
		||||
        
 | 
			
		||||
        lua_newtable(L); // ... nil LuaWrapper {}
 | 
			
		||||
        lua_pushstring(L, "v"); // ... nil LuaWrapper {} "v"
 | 
			
		||||
        lua_setfield(L, -2, "__mode"); // ... nil LuaWrapper {}
 | 
			
		||||
        lua_setfield(L, -2, LUAW_CACHE_METATABLE_KEY); // ... nil LuaWrapper
 | 
			
		||||
        
 | 
			
		||||
        lua_pop(L, 1); // ... nil
 | 
			
		||||
    }
 | 
			
		||||
    lua_pop(L, 1); // ...
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Run luaW_register or luaW_setfuncs to create a table and metatable for your
 | 
			
		||||
// class.  These functions create a table with filled with the function from
 | 
			
		||||
// the table argument in addition to the functions new and build (This is
 | 
			
		||||
// generally for things you think of as static methods in C++). The given
 | 
			
		||||
// metatable argument becomes a metatable for each object of your class. These
 | 
			
		||||
// can be thought of as member functions or methods.
 | 
			
		||||
//
 | 
			
		||||
// You may also supply constructors and destructors for classes that do not
 | 
			
		||||
// have a default constructor or that require special set up or tear down. You
 | 
			
		||||
// may specify NULL as the constructor, which means that you will not be able
 | 
			
		||||
// to call the new function on your class table. You will need to manually push
 | 
			
		||||
// objects from C++. By default, the default constructor is used to create
 | 
			
		||||
// objects and a simple call to delete is used to destroy them.
 | 
			
		||||
//
 | 
			
		||||
// By default LuaWrapper uses the address of C++ object to identify unique
 | 
			
		||||
// objects. In some cases this is not desired, such as in the case of
 | 
			
		||||
// shared_ptrs. Two shared_ptrs may themselves have unique locations in memory
 | 
			
		||||
// but still represent the same object. For cases like that, you may specify an
 | 
			
		||||
// identifier function which is responsible for pushing a key representing your
 | 
			
		||||
// object on to the stack.
 | 
			
		||||
//
 | 
			
		||||
// luaW_register will set table as the new value of the global of the given
 | 
			
		||||
// name. luaW_setfuncs is identical to luaW_register, but it does not set the
 | 
			
		||||
// table globally.  As with luaL_register and luaL_setfuncs, both funcstions
 | 
			
		||||
// leave the new table on the top of the stack.
 | 
			
		||||
template <typename T>
 | 
			
		||||
void luaW_setfuncs(lua_State* L, const char* classname, const luaL_Reg* table, const luaL_Reg* metatable, T* (*allocator)(lua_State*) = luaW_defaultallocator<T>, void (*deallocator)(lua_State*, T*) = luaW_defaultdeallocator<T>, void (*identifier)(lua_State*, T*) = luaW_defaultidentifier<T>)
 | 
			
		||||
{
 | 
			
		||||
    luaW_initialize(L);
 | 
			
		||||
 | 
			
		||||
    LuaWrapper<T>::classname = classname;
 | 
			
		||||
    LuaWrapper<T>::identifier = identifier;
 | 
			
		||||
    LuaWrapper<T>::allocator = allocator;
 | 
			
		||||
    LuaWrapper<T>::deallocator = deallocator;
 | 
			
		||||
 | 
			
		||||
    const luaL_Reg defaulttable[] =
 | 
			
		||||
    {
 | 
			
		||||
        { "new", luaW_new<T> },
 | 
			
		||||
        { NULL, NULL }
 | 
			
		||||
    };
 | 
			
		||||
    const luaL_Reg defaultmetatable[] = 
 | 
			
		||||
    { 
 | 
			
		||||
        { "__index", luaW_index<T> }, 
 | 
			
		||||
        { "__newindex", luaW_newindex<T> }, 
 | 
			
		||||
        { "__gc", luaW_gc<T> }, 
 | 
			
		||||
        { NULL, NULL } 
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Set up per-type tables
 | 
			
		||||
    lua_getfield(L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
 | 
			
		||||
    
 | 
			
		||||
    lua_getfield(L, -1, LUAW_STORAGE_KEY); // ... LuaWrapper LuaWrapper.storage
 | 
			
		||||
    lua_newtable(L); // ... LuaWrapper LuaWrapper.storage {}
 | 
			
		||||
    lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.storage
 | 
			
		||||
    lua_pop(L, 1); // ... LuaWrapper
 | 
			
		||||
 | 
			
		||||
    lua_getfield(L, -1, LUAW_HOLDS_KEY); // ... LuaWrapper LuaWrapper.holds
 | 
			
		||||
    lua_newtable(L); // ... LuaWrapper LuaWrapper.holds {}
 | 
			
		||||
    lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.holds
 | 
			
		||||
    lua_pop(L, 1); // ... LuaWrapper
 | 
			
		||||
 | 
			
		||||
    lua_getfield(L, -1, LUAW_CACHE_KEY); // ... LuaWrapper LuaWrapper.cache
 | 
			
		||||
    lua_newtable(L); // ... LuaWrapper LuaWrapper.cache {}
 | 
			
		||||
    luaW_wrapperfield<T>(L, LUAW_CACHE_METATABLE_KEY); // ... LuaWrapper LuaWrapper.cache {} cmt
 | 
			
		||||
    lua_setmetatable(L, -2); // ... LuaWrapper LuaWrapper.cache {}
 | 
			
		||||
    lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.cache
 | 
			
		||||
        
 | 
			
		||||
    lua_pop(L, 2); // ...
 | 
			
		||||
    
 | 
			
		||||
    // Open table
 | 
			
		||||
    lua_newtable(L); // ... T
 | 
			
		||||
    luaW_registerfuncs(L, allocator ? defaulttable : NULL, table); // ... T
 | 
			
		||||
 | 
			
		||||
    // Open metatable, set up extends table
 | 
			
		||||
    luaL_newmetatable(L, classname); // ... T mt
 | 
			
		||||
    lua_newtable(L); // ... T mt {}
 | 
			
		||||
    lua_setfield(L, -2, LUAW_EXTENDS_KEY); // ... T mt
 | 
			
		||||
    luaW_registerfuncs(L, defaultmetatable, metatable); // ... T mt
 | 
			
		||||
    lua_setfield(L, -2, "metatable"); // ... T
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
void luaW_register(lua_State* L, const char* classname, const luaL_Reg* table, const luaL_Reg* metatable, T* (*allocator)(lua_State*) = luaW_defaultallocator<T>, void (*deallocator)(lua_State*, T*) = luaW_defaultdeallocator<T>, void (*identifier)(lua_State*, T*) = luaW_defaultidentifier<T>)
 | 
			
		||||
{
 | 
			
		||||
    luaW_setfuncs(L, classname, table, metatable, allocator, deallocator, identifier); // ... T
 | 
			
		||||
    lua_pushvalue(L, -1); // ... T T
 | 
			
		||||
    lua_setglobal(L, classname); // ... T
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// luaW_extend is used to declare that class T inherits from class U. All
 | 
			
		||||
// functions in the base class will be available to the derived class (except
 | 
			
		||||
// when they share a function name, in which case the derived class's function
 | 
			
		||||
// wins). This also allows luaW_to<T> to cast your object apropriately, as
 | 
			
		||||
// casts straight through a void pointer do not work.
 | 
			
		||||
template <typename T, typename U>
 | 
			
		||||
void luaW_extend(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    if(!LuaWrapper<T>::classname)
 | 
			
		||||
        luaL_error(L, "attempting to call extend on a type that has not been registered");
 | 
			
		||||
 | 
			
		||||
    if(!LuaWrapper<U>::classname)
 | 
			
		||||
        luaL_error(L, "attempting to extend %s by a type that has not been registered", LuaWrapper<T>::classname);
 | 
			
		||||
 | 
			
		||||
    LuaWrapper<T>::cast = luaW_cast<T, U>;
 | 
			
		||||
    LuaWrapper<T>::identifier = luaW_identify<T, U>;
 | 
			
		||||
 | 
			
		||||
    luaL_getmetatable(L, LuaWrapper<T>::classname); // mt
 | 
			
		||||
    luaL_getmetatable(L, LuaWrapper<U>::classname); // mt emt
 | 
			
		||||
 | 
			
		||||
    // Point T's metatable __index at U's metatable for inheritance
 | 
			
		||||
    lua_newtable(L); // mt emt {}
 | 
			
		||||
    lua_pushvalue(L, -2); // mt emt {} emt
 | 
			
		||||
    lua_setfield(L, -2, "__index"); // mt emt {}
 | 
			
		||||
    lua_setmetatable(L, -3); // mt emt
 | 
			
		||||
 | 
			
		||||
    // Set up per-type tables to point at parent type
 | 
			
		||||
    lua_getfield(L, LUA_REGISTRYINDEX, LUAW_WRAPPER_KEY); // ... LuaWrapper
 | 
			
		||||
    
 | 
			
		||||
    lua_getfield(L, -1, LUAW_STORAGE_KEY); // ... LuaWrapper LuaWrapper.storage
 | 
			
		||||
    lua_getfield(L, -1, LuaWrapper<U>::classname); // ... LuaWrapper LuaWrapper.storage U
 | 
			
		||||
    lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.storage
 | 
			
		||||
    lua_pop(L, 1); // ... LuaWrapper
 | 
			
		||||
 | 
			
		||||
    lua_getfield(L, -1, LUAW_HOLDS_KEY); // ... LuaWrapper LuaWrapper.holds
 | 
			
		||||
    lua_getfield(L, -1, LuaWrapper<U>::classname); // ... LuaWrapper LuaWrapper.holds U
 | 
			
		||||
    lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.holds
 | 
			
		||||
    lua_pop(L, 1); // ... LuaWrapper
 | 
			
		||||
 | 
			
		||||
    lua_getfield(L, -1, LUAW_CACHE_KEY); // ... LuaWrapper LuaWrapper.cache
 | 
			
		||||
    lua_getfield(L, -1, LuaWrapper<U>::classname); // ... LuaWrapper LuaWrapper.cache U
 | 
			
		||||
    lua_setfield(L, -2, LuaWrapper<T>::classname); // ... LuaWrapper LuaWrapper.cache
 | 
			
		||||
        
 | 
			
		||||
    lua_pop(L, 2); // ...
 | 
			
		||||
    
 | 
			
		||||
    // Make a list of all types that inherit from U, for type checking
 | 
			
		||||
    lua_getfield(L, -2, LUAW_EXTENDS_KEY); // mt emt mt.extends
 | 
			
		||||
    lua_pushvalue(L, -2); // mt emt mt.extends emt
 | 
			
		||||
    lua_setfield(L, -2, LuaWrapper<U>::classname); // mt emt mt.extends
 | 
			
		||||
    lua_getfield(L, -2, LUAW_EXTENDS_KEY); // mt emt mt.extends emt.extends
 | 
			
		||||
    for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
 | 
			
		||||
    {
 | 
			
		||||
        // mt emt mt.extends emt.extends k v
 | 
			
		||||
        lua_pushvalue(L, -2); // mt emt mt.extends emt.extends k v k
 | 
			
		||||
        lua_pushvalue(L, -2); // mt emt mt.extends emt.extends k v k
 | 
			
		||||
        lua_rawset(L, -6); // mt emt mt.extends emt.extends k v
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lua_pop(L, 4); // mt emt
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2010-2013 Alexander Ames
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#endif // LUA_WRAPPER_H_
 | 
			
		||||
@@ -1,714 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2010-2013 Alexander Ames
 | 
			
		||||
 * Alexander.Ames@gmail.com
 | 
			
		||||
 * See Copyright Notice at the end of this file
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// API Summary:
 | 
			
		||||
//
 | 
			
		||||
// This is a set of utility functions that add to the functionalit of
 | 
			
		||||
// LuaWrapper. Over time I found myself repeating a few patterns, such as
 | 
			
		||||
// writing many trvial getter and setter functions, and wanting pass ownership
 | 
			
		||||
// of one object to another and have the Lua script properly handle that case.
 | 
			
		||||
//
 | 
			
		||||
// This file contains the additional functions that I've added but that do
 | 
			
		||||
// not really belong in the core API.
 | 
			
		||||
 | 
			
		||||
#ifndef LUAWRAPPERUTILS_HPP_
 | 
			
		||||
#define LUAWRAPPERUTILS_HPP_
 | 
			
		||||
 | 
			
		||||
#include "luawrapper.hpp"
 | 
			
		||||
 | 
			
		||||
#ifndef LUAW_NO_CXX11
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef LUAW_STD
 | 
			
		||||
#define LUAW_STD std
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
//
 | 
			
		||||
// A set of templated luaL_check and lua_push functions for use in the getters
 | 
			
		||||
// and setters below
 | 
			
		||||
//
 | 
			
		||||
// It is often useful to override luaU_check, luaU_to and/or luaU_push to
 | 
			
		||||
// operate on your own simple types rather than register your type with
 | 
			
		||||
// LuaWrapper, especially with small objects.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
template<typename U, typename = void>
 | 
			
		||||
struct luaU_Impl
 | 
			
		||||
{
 | 
			
		||||
    static U    luaU_check(lua_State* L, int      index);
 | 
			
		||||
    static U    luaU_to   (lua_State* L, int      index);
 | 
			
		||||
    static void luaU_push (lua_State* L, const U& value);
 | 
			
		||||
    static void luaU_push (lua_State* L,       U& value);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename U> U    luaU_check(lua_State* L, int      index) { return luaU_Impl<U>::luaU_check(L, index); }
 | 
			
		||||
template<typename U> U    luaU_to   (lua_State* L, int      index) { return luaU_Impl<U>::luaU_to   (L, index); }
 | 
			
		||||
template<typename U> void luaU_push (lua_State* L, const U& value) {        luaU_Impl<U>::luaU_push (L, value); }
 | 
			
		||||
template<typename U> void luaU_push (lua_State* L,       U& value) {        luaU_Impl<U>::luaU_push (L, value); }
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
//
 | 
			
		||||
// This is slightly different than the previous three functions in that you
 | 
			
		||||
// shouldn't need to write your own version of it, since it uses luaU_check
 | 
			
		||||
// automatically.
 | 
			
		||||
//
 | 
			
		||||
template<typename U>
 | 
			
		||||
U luaU_opt(lua_State* L, int index, const U& fallback = U())
 | 
			
		||||
{
 | 
			
		||||
    if (lua_isnil(L, index))
 | 
			
		||||
        return fallback;
 | 
			
		||||
    else
 | 
			
		||||
        return luaU_Impl<U>::luaU_check(L, index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
struct luaU_Impl<bool>
 | 
			
		||||
{
 | 
			
		||||
    static bool luaU_check(lua_State* L, int         index) { return lua_toboolean  (L, index) != 0; }
 | 
			
		||||
    static bool luaU_to   (lua_State* L, int         index) { return lua_toboolean  (L, index) != 0; }
 | 
			
		||||
    static void luaU_push (lua_State* L, const bool& value) {        lua_pushboolean(L, value); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
struct luaU_Impl<const char*>
 | 
			
		||||
{
 | 
			
		||||
    static const char* luaU_check(lua_State* L, int                index) { return luaL_checkstring(L, index); }
 | 
			
		||||
    static const char* luaU_to   (lua_State* L, int                index) { return lua_tostring    (L, index); }
 | 
			
		||||
    static void        luaU_push (lua_State* L, const char* const& value) {        lua_pushstring  (L, value); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
struct luaU_Impl<unsigned int>
 | 
			
		||||
{
 | 
			
		||||
    static unsigned int luaU_check(lua_State* L, int                 index) { return static_cast<unsigned int>(luaL_checkinteger(L, index)); }
 | 
			
		||||
    static unsigned int luaU_to   (lua_State* L, int                 index) { return static_cast<unsigned int>(lua_tointeger    (L, index)); }
 | 
			
		||||
    static void         luaU_push (lua_State* L, const unsigned int& value) {                                  lua_pushinteger  (L, value); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
struct luaU_Impl<int>
 | 
			
		||||
{
 | 
			
		||||
    static int  luaU_check(lua_State* L, int        index) { return static_cast<int>(luaL_checkinteger(L, index)); }
 | 
			
		||||
    static int  luaU_to   (lua_State* L, int        index) { return static_cast<int>(lua_tointeger    (L, index)); }
 | 
			
		||||
    static void luaU_push (lua_State* L, const int& value) {                         lua_pushinteger  (L, value); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
struct luaU_Impl<unsigned char>
 | 
			
		||||
{
 | 
			
		||||
    static unsigned char luaU_check(lua_State* L, int                  index) { return static_cast<unsigned char>(luaL_checkinteger(L, index)); }
 | 
			
		||||
    static unsigned char luaU_to   (lua_State* L, int                  index) { return static_cast<unsigned char>(lua_tointeger    (L, index)); }
 | 
			
		||||
    static void          luaU_push (lua_State* L, const unsigned char& value) {                                   lua_pushinteger  (L, value); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
struct luaU_Impl<char>
 | 
			
		||||
{
 | 
			
		||||
    static char luaU_check(lua_State* L, int         index) { return static_cast<char>(luaL_checkinteger(L, index)); }
 | 
			
		||||
    static char luaU_to   (lua_State* L, int         index) { return static_cast<char>(lua_tointeger    (L, index)); }
 | 
			
		||||
    static void luaU_push (lua_State* L, const char& value) {                          lua_pushinteger  (L, value); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
struct luaU_Impl<float>
 | 
			
		||||
{
 | 
			
		||||
    static float luaU_check(lua_State* L, int          index) { return static_cast<float>(luaL_checknumber(L, index)); }
 | 
			
		||||
    static float luaU_to   (lua_State* L, int          index) { return static_cast<float>(lua_tonumber    (L, index)); }
 | 
			
		||||
    static void  luaU_push (lua_State* L, const float& value) {                           lua_pushnumber  (L, value); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
struct luaU_Impl<double>
 | 
			
		||||
{
 | 
			
		||||
    static double luaU_check(lua_State* L, int           index) { return static_cast<double>(luaL_checknumber(L, index)); }
 | 
			
		||||
    static double luaU_to   (lua_State* L, int           index) { return static_cast<double>(lua_tonumber    (L, index)); }
 | 
			
		||||
    static void   luaU_push (lua_State* L, const double& value) {                            lua_pushnumber  (L, value); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifndef LUAW_NO_CXX11
 | 
			
		||||
template<typename T>
 | 
			
		||||
struct luaU_Impl<T, typename LUAW_STD::enable_if<LUAW_STD::is_enum<T>::value>::type>
 | 
			
		||||
{
 | 
			
		||||
    static T    luaU_check(lua_State* L, int      index) { return static_cast<T>(luaL_checkinteger  (L, index)); }
 | 
			
		||||
    static T    luaU_to   (lua_State* L, int      index) { return static_cast<T>(lua_tointeger      (L, index)); }
 | 
			
		||||
    static void luaU_push (lua_State* L, const T& value) {        lua_pushnumber(L, static_cast<int>(value   )); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename T>
 | 
			
		||||
struct luaU_Impl<T*, typename LUAW_STD::enable_if<LUAW_STD::is_class<T>::value>::type>
 | 
			
		||||
{
 | 
			
		||||
    static T*   luaU_check( lua_State* L, int index) { return luaW_check<T>(L, index); }
 | 
			
		||||
    static T*   luaU_to   ( lua_State* L, int index) { return luaW_to   <T>(L, index); }
 | 
			
		||||
    static void luaU_push ( lua_State* L, T*& value) {        luaW_push <T>(L, value); }
 | 
			
		||||
    static void luaU_push ( lua_State* L, T*  value) {        luaW_push <T>(L, value); }
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
//
 | 
			
		||||
// These are just some functions I've always felt should exist
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
template <typename U>
 | 
			
		||||
inline U luaU_getfield(lua_State* L, int index, const char* field)
 | 
			
		||||
{
 | 
			
		||||
#ifndef LUAW_NO_CXX11
 | 
			
		||||
    static_assert(!std::is_same<U, const char*>::value, 
 | 
			
		||||
        "luaU_getfield is not safe to use on const char*'s. (The string will be popped from the stack.)");
 | 
			
		||||
#endif
 | 
			
		||||
    lua_getfield(L, index, field);
 | 
			
		||||
    U val = luaU_to<U>(L, -1);
 | 
			
		||||
    lua_pop(L, 1);
 | 
			
		||||
    return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename U>
 | 
			
		||||
inline U luaU_checkfield(lua_State* L, int index, const char* field)
 | 
			
		||||
{
 | 
			
		||||
#ifndef LUAW_NO_CXX11
 | 
			
		||||
    static_assert(!std::is_same<U, const char*>::value, 
 | 
			
		||||
        "luaU_checkfield is not safe to use on const char*'s. (The string will be popped from the stack.)");
 | 
			
		||||
#endif
 | 
			
		||||
    lua_getfield(L, index, field);
 | 
			
		||||
    U val = luaU_check<U>(L, -1);
 | 
			
		||||
    lua_pop(L, 1);
 | 
			
		||||
    return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename U>
 | 
			
		||||
inline void luaU_setfield(lua_State* L, int index, const char* field, U val)
 | 
			
		||||
{
 | 
			
		||||
    luaU_push<U>(L, val);
 | 
			
		||||
    lua_setfield(L, luaW_correctindex(L, index, 1), field);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
//
 | 
			
		||||
// A set of trivial getter and setter templates. These templates are designed
 | 
			
		||||
// to call trivial getters or setters.
 | 
			
		||||
//
 | 
			
		||||
// There are four forms:
 | 
			
		||||
//  1. Getting or setting a public member variable that is of a primitive type
 | 
			
		||||
//  2. Getting or setting a public member variable that is a pointer to an
 | 
			
		||||
//     object
 | 
			
		||||
//  3. Getting or setting a private member variable that is of a primitive type
 | 
			
		||||
//     through a getter or setter
 | 
			
		||||
//  3. Getting or setting a private member variable that is is a pointer to an
 | 
			
		||||
//     object through a getter or setter
 | 
			
		||||
//
 | 
			
		||||
// The interface to all of them is the same however. In addition to plain
 | 
			
		||||
// getter and setter functions, there is a getset which does both - if an
 | 
			
		||||
// argument is supplied it attempts to set the value, and in either case it
 | 
			
		||||
// returns the value. In your lua table declaration in C++ rather than write
 | 
			
		||||
// individiual wrappers for each getter and setter you may do the following:
 | 
			
		||||
//
 | 
			
		||||
// static luaL_reg Foo_metatable[] =
 | 
			
		||||
// {
 | 
			
		||||
//     { "GetBar", luaU_get<Foo, bool, &Widget::GetBar> },
 | 
			
		||||
//     { "SetBar", luaU_set<Foo, bool, &Widget::SetBar> },
 | 
			
		||||
//     { "Bar", luaU_getset<Foo, bool, &Widget::GetBar, &Widget::SetBar> },
 | 
			
		||||
//     { NULL, NULL }
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
// Getters and setters must have one of the following signatures:
 | 
			
		||||
//    void T::Setter(U value);
 | 
			
		||||
//    void T::Setter(U* value);
 | 
			
		||||
//    void T::Setter(const U& value);
 | 
			
		||||
//    U Getter() const;
 | 
			
		||||
//    U* Getter() const;
 | 
			
		||||
//
 | 
			
		||||
// In this example, the variable Foo::bar is private and so it is accessed
 | 
			
		||||
// through getter and setter functions. If Foo::bar were public, it could be
 | 
			
		||||
// accessed directly, like so:
 | 
			
		||||
//
 | 
			
		||||
// static luaL_reg Foo_metatable[] =
 | 
			
		||||
// {
 | 
			
		||||
//     { "GetBar", luaU_get<Foo, bool, &Widget::bar> },
 | 
			
		||||
//     { "SetBar", luaU_set<Foo, bool, &Widget::bar> },
 | 
			
		||||
//     { "Bar", luaU_getset<Foo, bool, &Widget::bar> },
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
// In a Lua script, you can now use foo:GetBar(), foo:SetBar() and foo:Bar()
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U T::*Member>
 | 
			
		||||
int luaU_get(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    luaU_push<U>(L, obj->*Member);
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U* T::*Member>
 | 
			
		||||
int luaU_get(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    luaW_push<U>(L, obj->*Member);
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U (T::*Getter)() const>
 | 
			
		||||
int luaU_get(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    luaU_push<U>(L, (obj->*Getter)());
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, const U& (T::*Getter)() const>
 | 
			
		||||
int luaU_get(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    luaU_push<U>(L, (obj->*Getter)());
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U* (T::*Getter)() const>
 | 
			
		||||
int luaU_get(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    luaW_push<U>(L, (obj->*Getter)());
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U T::*Member>
 | 
			
		||||
int luaU_set(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj)
 | 
			
		||||
        obj->*Member = luaU_check<U>(L, 2);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U* T::*Member>
 | 
			
		||||
int luaU_set(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj)
 | 
			
		||||
    {
 | 
			
		||||
        U* member = luaW_opt<U>(L, 2);
 | 
			
		||||
        obj->*Member = member;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, const U* T::*Member>
 | 
			
		||||
int luaU_set(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj)
 | 
			
		||||
    {
 | 
			
		||||
        U* member = luaW_opt<U>(L, 2);
 | 
			
		||||
        obj->*Member = member;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, const U* T::*Member>
 | 
			
		||||
int luaU_setandrelease(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj)
 | 
			
		||||
    {
 | 
			
		||||
        U* member = luaW_opt<U>(L, 2);
 | 
			
		||||
        obj->*Member = member;
 | 
			
		||||
        if (member)
 | 
			
		||||
            luaW_release<U>(L, member);
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, void (T::*Setter)(U)>
 | 
			
		||||
int luaU_set(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj)
 | 
			
		||||
        (obj->*Setter)(luaU_check<U>(L, 2));
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, void (T::*Setter)(const U&)>
 | 
			
		||||
int luaU_set(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj)
 | 
			
		||||
        (obj->*Setter)(luaU_check<U>(L, 2));
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, void (T::*Setter)(U*)>
 | 
			
		||||
int luaU_set(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj)
 | 
			
		||||
    {
 | 
			
		||||
        U* member = luaW_opt<U>(L, 2);
 | 
			
		||||
        (obj->*Setter)(member);
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, void (T::*Setter)(U*)>
 | 
			
		||||
int luaU_setandrelease(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj)
 | 
			
		||||
    {
 | 
			
		||||
        U* member = luaW_opt<U>(L, 2);
 | 
			
		||||
        (obj->*Setter)(member);
 | 
			
		||||
        if (member)
 | 
			
		||||
            luaW_release<U>(L, member);
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U T::*Member>
 | 
			
		||||
int luaU_getset(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj && lua_gettop(L) >= 2)
 | 
			
		||||
    {
 | 
			
		||||
        obj->*Member = luaU_check<U>(L, 2);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        luaU_push<U>(L, obj->*Member);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U* T::*Member>
 | 
			
		||||
int luaU_getset(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj && lua_gettop(L) >= 2)
 | 
			
		||||
    {
 | 
			
		||||
        U* member = luaW_opt<U>(L, 2);
 | 
			
		||||
        obj->*Member = member;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        luaW_push<U>(L, obj->*Member);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U* T::*Member>
 | 
			
		||||
int luaU_getsetandrelease(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj && lua_gettop(L) >= 2)
 | 
			
		||||
    {
 | 
			
		||||
        U* member = luaW_opt<U>(L, 2);
 | 
			
		||||
        obj->*Member = member;
 | 
			
		||||
        if (member)
 | 
			
		||||
            luaW_release<U>(L, member);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        luaW_push<U>(L, obj->*Member);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U (T::*Getter)() const, void (T::*Setter)(U)>
 | 
			
		||||
int luaU_getset(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj && lua_gettop(L) >= 2)
 | 
			
		||||
    {
 | 
			
		||||
        (obj->*Setter)(luaU_check<U>(L, 2));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        luaU_push<U>(L, (obj->*Getter)());
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U (T::*Getter)() const, void (T::*Setter)(const U&)>
 | 
			
		||||
int luaU_getset(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj && lua_gettop(L) >= 2)
 | 
			
		||||
    {
 | 
			
		||||
        (obj->*Setter)(luaU_check<U>(L, 2));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        luaU_push<U>(L, (obj->*Getter)());
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, const U& (T::*Getter)() const, void (T::*Setter)(const U&)>
 | 
			
		||||
int luaU_getset(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj && lua_gettop(L) >= 2)
 | 
			
		||||
    {
 | 
			
		||||
        (obj->*Setter)(luaU_check<U>(L, 2));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        luaU_push<U>(L, (obj->*Getter)());
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U* (T::*Getter)() const, void (T::*Setter)(U*)>
 | 
			
		||||
int luaU_getset(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj && lua_gettop(L) >= 2)
 | 
			
		||||
    {
 | 
			
		||||
        U* member = luaW_opt<U>(L, 2);
 | 
			
		||||
        (obj->*Setter)(member);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        luaW_push<U>(L, (obj->*Getter)());
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename U, U* (T::*Getter)() const, void (T::*Setter)(U*)>
 | 
			
		||||
int luaU_getsetandrelease(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    T* obj = luaW_check<T>(L, 1);
 | 
			
		||||
    if (obj && lua_gettop(L) >= 2)
 | 
			
		||||
    {
 | 
			
		||||
        U* member = luaW_opt<U>(L, 2);
 | 
			
		||||
        (obj->*Setter)(member);
 | 
			
		||||
        if (member)
 | 
			
		||||
            luaW_release<U>(L, member);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        luaW_push<U>(L, (obj->*Getter)());
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if !defined(_WIN32) && !defined(LUAW_NO_CXX11)
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
//
 | 
			
		||||
// luaU_func is a special macro that expands into a simple function wrapper.
 | 
			
		||||
// Unlike the getter setters above, you merely need to name the function you
 | 
			
		||||
// would like to wrap.
 | 
			
		||||
//
 | 
			
		||||
// For example,
 | 
			
		||||
//
 | 
			
		||||
// struct Foo
 | 
			
		||||
// {
 | 
			
		||||
//     int DoSomething(int, const char*);
 | 
			
		||||
// };
 | 
			
		||||
//
 | 
			
		||||
// static luaL_reg Foo_metatable[] =
 | 
			
		||||
// {
 | 
			
		||||
//     { "DoSomething", luaU_func(&Foo::DoSomething) },
 | 
			
		||||
//     { NULL, NULL }
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
// This macro will expand based on the function signature of Foo::DoSomething
 | 
			
		||||
// In this example, it would expand into the following wrapper:
 | 
			
		||||
//
 | 
			
		||||
//     luaU_push(luaW_check<T>(L, 1)->DoSomething(luaU_check<int>(L, 2), luaU_check<const char*>(L, 3)));
 | 
			
		||||
//     return 1;
 | 
			
		||||
//
 | 
			
		||||
// In this example there is only one member function called DoSomething. In some 
 | 
			
		||||
// cases there may be multiple overrides for a function. For those cases, an 
 | 
			
		||||
// additional macro has been provided, luaU_funcsig, which takes the entire
 | 
			
		||||
// function signature. The arguments to the macro reflect the order they would
 | 
			
		||||
// appear in the function signature: return type, type name, function name, and
 | 
			
		||||
// finally a list of all the argument types. For example:
 | 
			
		||||
//
 | 
			
		||||
// struct Foo
 | 
			
		||||
// {
 | 
			
		||||
//     int DoSomething (const char*);
 | 
			
		||||
//     int DoSomething (const char*, int);
 | 
			
		||||
// };
 | 
			
		||||
//
 | 
			
		||||
// const struct luaL_Reg Foo_metatable[] =
 | 
			
		||||
// {
 | 
			
		||||
//     {"DoSomething1", luaU_funcsig(int, Foo, DoSomething, const char*)) },
 | 
			
		||||
//     {"DoSomething1", luaU_funcsig(int, Foo, DoSomething, const char*, int)) },
 | 
			
		||||
//     { NULL, NULL }
 | 
			
		||||
// };
 | 
			
		||||
//
 | 
			
		||||
// These macros and it's underlying templates are somewhat experimental and some
 | 
			
		||||
// refinements are probably needed.  There are cases where it does not
 | 
			
		||||
// currently work and I expect some changes can be made to refine its behavior.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#define luaU_func(memberfunc) &luaU_FuncWrapper<decltype(memberfunc),memberfunc>::call
 | 
			
		||||
#define luaU_funcsig(returntype, type, funcname, ...) luaU_func(static_cast<returntype (type::*)(__VA_ARGS__)>(&type::funcname))
 | 
			
		||||
 | 
			
		||||
template<int... ints> struct luaU_IntPack { };
 | 
			
		||||
template<int start, int count, int... tail> struct luaU_MakeIntRangeType { typedef typename luaU_MakeIntRangeType<start, count-1, start+count-1, tail...>::type type; };
 | 
			
		||||
template<int start, int... tail> struct luaU_MakeIntRangeType<start, 0, tail...> { typedef luaU_IntPack<tail...> type; };
 | 
			
		||||
template<int start, int count> inline typename luaU_MakeIntRangeType<start, count>::type luaU_makeIntRange() { return typename luaU_MakeIntRangeType<start, count>::type(); }
 | 
			
		||||
template<class MemFunPtrType, MemFunPtrType MemberFunc> struct luaU_FuncWrapper;
 | 
			
		||||
 | 
			
		||||
template<class T, class ReturnType, class... Args, ReturnType(T::*MemberFunc)(Args...)>
 | 
			
		||||
struct luaU_FuncWrapper<ReturnType (T::*)(Args...), MemberFunc>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    static int call(lua_State* L)
 | 
			
		||||
    {
 | 
			
		||||
        return callImpl(L, luaU_makeIntRange<2,sizeof...(Args)>());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    template<int... indices>
 | 
			
		||||
    static int callImpl(lua_State* L, luaU_IntPack<indices...>)
 | 
			
		||||
    {
 | 
			
		||||
        luaU_push<ReturnType>(L, (luaW_check<T>(L, 1)->*MemberFunc)(luaU_check<Args>(L, indices)...));
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
//
 | 
			
		||||
// Calls the copy constructor for an object of type T.
 | 
			
		||||
// Arguments may be passed in, in case they're needed for the postconstructor
 | 
			
		||||
//
 | 
			
		||||
// e.g.
 | 
			
		||||
//
 | 
			
		||||
// C++:
 | 
			
		||||
// luaL_reg Foo_Metatable[] =
 | 
			
		||||
// {
 | 
			
		||||
//     { "clone", luaU_clone<Foo> },
 | 
			
		||||
//     { NULL, NULL }
 | 
			
		||||
// };
 | 
			
		||||
//
 | 
			
		||||
// Lua:
 | 
			
		||||
// foo = Foo.new()
 | 
			
		||||
// foo2 = foo:clone()
 | 
			
		||||
//
 | 
			
		||||
template <typename T>
 | 
			
		||||
int luaU_clone(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    // obj ...
 | 
			
		||||
    T* obj = new T(*luaW_check<T>(L, 1));
 | 
			
		||||
    lua_remove(L, 1); // ...
 | 
			
		||||
    int numargs = lua_gettop(L);
 | 
			
		||||
    luaW_push<T>(L, obj); // ... clone
 | 
			
		||||
    luaW_hold<T>(L, obj);
 | 
			
		||||
    luaW_postconstructor<T>(L, numargs);
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
//
 | 
			
		||||
// luaU_build is intended to be used to initialize many values by passing in a
 | 
			
		||||
// table. They keys of the table are used as function names, and values are
 | 
			
		||||
// used as arguments to the function. This is intended to be used on functions
 | 
			
		||||
// that are simple setters.
 | 
			
		||||
//
 | 
			
		||||
// For example, if luaU_build is set as the post constructor, you can
 | 
			
		||||
// initialize an object as so:
 | 
			
		||||
//
 | 
			
		||||
// f = Foo.new
 | 
			
		||||
// {
 | 
			
		||||
//     X = 10;
 | 
			
		||||
//     Y = 20;
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
// After the object is constructed, luaU_build will do the equivalent of
 | 
			
		||||
// calling f:X(10) and f:Y(20).
 | 
			
		||||
//
 | 
			
		||||
template <typename T>
 | 
			
		||||
int luaU_build(lua_State* L)
 | 
			
		||||
{
 | 
			
		||||
    // obj {}
 | 
			
		||||
    lua_insert(L, -2); // {} obj
 | 
			
		||||
    if (lua_type(L, 1) == LUA_TTABLE)
 | 
			
		||||
    {
 | 
			
		||||
        for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1))
 | 
			
		||||
        {
 | 
			
		||||
            // {} obj k v
 | 
			
		||||
            lua_pushvalue(L, -2); // {} obj k v k
 | 
			
		||||
            lua_gettable(L, -4); // {} obj k v ud[k]
 | 
			
		||||
            lua_pushvalue(L, -4); // {} obj k v ud[k] ud
 | 
			
		||||
            lua_pushvalue(L, -3); // {} obj k v ud[k] ud v
 | 
			
		||||
            lua_call(L, 2, 0); // {} obj k v
 | 
			
		||||
        }
 | 
			
		||||
        // {} ud
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
//
 | 
			
		||||
// Takes the object of type T at the top of the stack and stores it in on a
 | 
			
		||||
// table with the name storagetable, on the table at the specified index.
 | 
			
		||||
//
 | 
			
		||||
// You may manually call luaW_hold and luaW_release to handle pointer
 | 
			
		||||
// ownership, but it is often easier to simply store a Lua userdata on a table
 | 
			
		||||
// that is owned by its parent. This ensures that your object will not be
 | 
			
		||||
// prematurely freed, and that it can only be destoryed after its parent.
 | 
			
		||||
//
 | 
			
		||||
// e.g.
 | 
			
		||||
//
 | 
			
		||||
// Foo* parent = luaW_check<Foo>(L, 1);
 | 
			
		||||
// Bar* child = luaW_check<Bar>(L, 2);
 | 
			
		||||
// if (parent && child)
 | 
			
		||||
// {
 | 
			
		||||
//     luaU_store<Bar>(L, 1, "Children");
 | 
			
		||||
//     parent->AddChild(child);
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
template <typename T>
 | 
			
		||||
void luaU_store(lua_State* L, int index, const char* storagetable, const char* key = NULL)
 | 
			
		||||
{
 | 
			
		||||
    // ... store ... obj
 | 
			
		||||
    lua_getfield(L, index, storagetable); // ... store ... obj store.storagetable
 | 
			
		||||
    if (key)
 | 
			
		||||
        lua_pushstring(L, key); // ... store ... obj store.storagetable key
 | 
			
		||||
    else
 | 
			
		||||
        LuaWrapper<T>::identifier(L, luaW_to<T>(L, -2)); // ... store ... obj store.storagetable key
 | 
			
		||||
    lua_pushvalue(L, -3); // ... store ... obj store.storagetable key obj
 | 
			
		||||
    lua_settable(L, -3); // ... store ... obj store.storagetable
 | 
			
		||||
    lua_pop(L, 1); // ... store ... obj
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2010-2011 Alexander Ames
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#endif // LUAWRAPPERUTILS_HPP_
 | 
			
		||||
@@ -1,64 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  main.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
extern "C"
 | 
			
		||||
{
 | 
			
		||||
    #include "lua.h"
 | 
			
		||||
    #include "lualib.h"
 | 
			
		||||
    #include "lauxlib.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include "functions.hpp"
 | 
			
		||||
#include "LuaWebSocket.h"
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
    lua_State* L = luaL_newstate();
 | 
			
		||||
    luaL_openlibs(L);
 | 
			
		||||
 | 
			
		||||
    // Functions
 | 
			
		||||
    lua_register(L, "info", lua_info);
 | 
			
		||||
 | 
			
		||||
    // Objets
 | 
			
		||||
    ix::luaopen_WebSocket(L);
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // Simple version does little error handling
 | 
			
		||||
    // luaL_dofile(L, "echo_client.lua");
 | 
			
		||||
    //
 | 
			
		||||
    std::string luaFile("echo_client.lua");
 | 
			
		||||
    int loadStatus = luaL_loadfile(L, luaFile.c_str());
 | 
			
		||||
    if (loadStatus)
 | 
			
		||||
    {
 | 
			
		||||
        std::cerr << "Error loading " << luaFile << std::endl;
 | 
			
		||||
        std::cerr << lua_tostring(L, -1) << std::endl;
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        std::cout << "loaded " << luaFile << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // Capture lua errors
 | 
			
		||||
    //
 | 
			
		||||
    try
 | 
			
		||||
    {
 | 
			
		||||
        lua_call(L, 0, 0);
 | 
			
		||||
        lua_close(L);
 | 
			
		||||
    }
 | 
			
		||||
    catch (const std::runtime_error& ex)
 | 
			
		||||
    {
 | 
			
		||||
        lua_close(L);
 | 
			
		||||
        std::cerr << ex.what() << std::endl;
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								makefile
									
									
									
									
									
								
							@@ -20,7 +20,7 @@ install: brew
 | 
			
		||||
# Release, Debug, MinSizeRel, RelWithDebInfo are the build types
 | 
			
		||||
#
 | 
			
		||||
brew:
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4 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
 | 
			
		||||
@@ -116,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)
 | 
			
		||||
@@ -159,9 +157,6 @@ test_no_ssl:
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TEST=1 .. ; make -j 4)
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
 | 
			
		||||
luarocks:
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_LUAROCKS=1 .. ; ninja)
 | 
			
		||||
 | 
			
		||||
ws_test: ws
 | 
			
		||||
	(cd ws ; env DEBUG=1 PATH=../ws/build:$$PATH bash test_ws.sh)
 | 
			
		||||
 | 
			
		||||
@@ -188,4 +183,3 @@ doc:
 | 
			
		||||
.PHONY: test
 | 
			
		||||
.PHONY: build
 | 
			
		||||
.PHONY: ws
 | 
			
		||||
.PHONY: luarocks
 | 
			
		||||
 
 | 
			
		||||
@@ -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,6 +141,7 @@ 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;
 | 
			
		||||
 | 
			
		||||
@@ -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,
 | 
			
		||||
                                                maxQueueSize,
 | 
			
		||||
                                                enableHeartbeat,
 | 
			
		||||
                                                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
 | 
			
		||||
        //
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
                                                    maxQueueSize,
 | 
			
		||||
                                                    enableHeartbeat,
 | 
			
		||||
                                                    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,127 +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 verbose = true;
 | 
			
		||||
        bool quiet = false;
 | 
			
		||||
        size_t maxQueueSize = 10;
 | 
			
		||||
        bool enableHeartbeat = false;
 | 
			
		||||
 | 
			
		||||
        // 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,
 | 
			
		||||
                                                    verbose,
 | 
			
		||||
                                                    maxQueueSize,
 | 
			
		||||
                                                    enableHeartbeat,
 | 
			
		||||
                                                    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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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