Compare commits
	
		
			86 Commits
		
	
	
		
			feature/ci
			...
			bsergean-p
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 317e3c44bf | ||
|  | 61b90251af | ||
|  | 851157f252 | ||
|  | 69541cf750 | ||
|  | 1e166eee57 | ||
|  | 6860b2d09d | ||
|  | 9bbd1f1b30 | ||
|  | 18c2b69633 | ||
|  | 178f218374 | ||
|  | 6a1aa27b5f | ||
|  | e7f89ae529 | ||
|  | cdeaf8e2be | ||
|  | dbafa0aa07 | ||
|  | 3baf59a031 | ||
|  | b5804c2082 | ||
|  | 30bcddb99f | ||
|  | 47fd04e210 | ||
|  | 4f5b0c4f07 | ||
|  | c2d497abc5 | ||
|  | bbe2ae6dd3 | ||
|  | 26897b2425 | ||
|  | e3c98a03cc | ||
|  | 97fedf9482 | ||
|  | ae187c0e98 | ||
|  | 0f21a20fe3 | ||
|  | 54db6ec8bb | ||
|  | 0e0a748037 | ||
|  | 3b19b0eeca | ||
|  | dbfe3104e8 | ||
|  | 68fd8c20d6 | ||
|  | d932af8568 | ||
|  | 3add6d4c2e | ||
|  | 0d7fb05567 | ||
|  | bf1747ef18 | ||
|  | 5c9c05caff | ||
|  | 2573ca151b | ||
|  | c5b5fa82be | ||
|  | 80dff08304 | ||
|  | 24c2eae3d7 | ||
|  | 449c5fa138 | ||
|  | b6234ff908 | ||
|  | d26664fccc | ||
|  | def0243d6d | ||
|  | 1410797d6f | ||
|  | 2670187fe0 | ||
|  | 95359461d7 | ||
|  | 4d7b149649 | ||
|  | b29a37ce76 | ||
|  | 9a4dfb40da | ||
|  | c4c344518d | ||
|  | d706a4a73e | ||
|  | 88970604e3 | ||
|  | 7fee54464e | ||
|  | 1c7634d075 | ||
|  | 99f9556aa9 | ||
|  | 39b2a3d6df | ||
|  | 056b02a494 | ||
|  | 48166a9a72 | ||
|  | b36a2d1faa | ||
|  | 968cc5c1c4 | ||
|  | 0813eb1788 | ||
|  | cadb8336f2 | ||
|  | 7fd782f72f | ||
|  | 85bcdaaec3 | ||
|  | 461641f3d0 | ||
|  | 2d65c27d11 | ||
|  | 6a7785d9d9 | ||
|  | 78a670e0c8 | ||
|  | e63ac69ec6 | ||
|  | afa15d6dcf | ||
|  | 432a202c07 | ||
|  | d609370a85 | ||
|  | bbe3a766f4 | ||
|  | 09d3520b66 | ||
|  | f090c7659b | ||
|  | 7c195219cd | ||
|  | d739662a7c | ||
|  | e7f7e470e2 | ||
|  | d239738ec6 | ||
|  | c61975bf75 | ||
|  | 39cc0ed32f | ||
|  | 22c3a7264e | ||
|  | ee5a2eb46e | ||
|  | f6e34e4b34 | ||
|  | d0359a1764 | ||
|  | 8910ebcc3c | 
							
								
								
									
										2
									
								
								.github/workflows/mkdocs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/mkdocs.yml
									
									
									
									
										vendored
									
									
								
							| @@ -21,5 +21,7 @@ jobs: | |||||||
|         pip install pygments |         pip install pygments | ||||||
|     - name: Build doc |     - name: Build doc | ||||||
|       run: | |       run: | | ||||||
|  |         git clean -dfx . | ||||||
|  |         git fetch | ||||||
|         git pull |         git pull | ||||||
|         mkdocs gh-deploy |         mkdocs gh-deploy | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/unittest_linux.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/unittest_linux.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   push: |   push: | ||||||
|     paths-ignore: |     paths-ignore: | ||||||
|     - 'docs/**' |     - 'docs/**' | ||||||
|  |   pull_request: | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   linux: |   linux: | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/unittest_linux_asan.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/unittest_linux_asan.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   push: |   push: | ||||||
|     paths-ignore: |     paths-ignore: | ||||||
|     - 'docs/**' |     - 'docs/**' | ||||||
|  |   pull_request: | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   linux: |   linux: | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ on: | |||||||
|   push: |   push: | ||||||
|     paths-ignore: |     paths-ignore: | ||||||
|     - 'docs/**' |     - 'docs/**' | ||||||
|  |   pull_request: | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   mac_tsan_mbedtls: |   mac_tsan_mbedtls: | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ on: | |||||||
|   push: |   push: | ||||||
|     paths-ignore: |     paths-ignore: | ||||||
|     - 'docs/**' |     - 'docs/**' | ||||||
|  |   pull_request: | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   mac_tsan_openssl: |   mac_tsan_openssl: | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ on: | |||||||
|   push: |   push: | ||||||
|     paths-ignore: |     paths-ignore: | ||||||
|     - 'docs/**' |     - 'docs/**' | ||||||
|  |   pull_request: | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   mac_tsan_sectransport: |   mac_tsan_sectransport: | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								.github/workflows/unittest_uwp.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/unittest_uwp.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   push: |   push: | ||||||
|     paths-ignore: |     paths-ignore: | ||||||
|     - 'docs/**' |     - 'docs/**' | ||||||
|  |   pull_request: | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   uwp: |   uwp: | ||||||
| @@ -10,11 +11,17 @@ jobs: | |||||||
|     steps: |     steps: | ||||||
|     - uses: actions/checkout@v1 |     - uses: actions/checkout@v1 | ||||||
|     - uses: seanmiddleditch/gha-setup-vsdevenv@master |     - uses: seanmiddleditch/gha-setup-vsdevenv@master | ||||||
|  |     - uses: seanmiddleditch/gha-setup-ninja@master | ||||||
|     - run: | |     - run: | | ||||||
|         mkdir build |         mkdir build | ||||||
|         cd build |         cd build | ||||||
|         cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DUSE_TEST=1 -DUSE_ZLIB=0 .. |         cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_C_COMPILER=cl.exe -DUSE_TEST=1 -DUSE_ZLIB=0 .. | ||||||
|     - run: cmake --build build |     - run: | | ||||||
|  |         cd build | ||||||
|  |         ninja | ||||||
|  |     - run: | | ||||||
|  |         cd build | ||||||
|  |         ninja test | ||||||
|  |  | ||||||
| # | # | ||||||
| #   Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg | #   Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   push: |   push: | ||||||
|     paths-ignore: |     paths-ignore: | ||||||
|     - 'docs/**' |     - 'docs/**' | ||||||
|  |   pull_request: | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   windows: |   windows: | ||||||
| @@ -10,11 +11,17 @@ jobs: | |||||||
|     steps: |     steps: | ||||||
|     - uses: actions/checkout@v1 |     - uses: actions/checkout@v1 | ||||||
|     - uses: seanmiddleditch/gha-setup-vsdevenv@master |     - uses: seanmiddleditch/gha-setup-vsdevenv@master | ||||||
|  |     - uses: seanmiddleditch/gha-setup-ninja@master | ||||||
|     - run: | |     - run: | | ||||||
|         mkdir build |         mkdir build | ||||||
|         cd build |         cd build | ||||||
|         cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 .. |         cmake -GNinja -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_C_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=OFF -DBUILD_SHARED_LIBS=OFF .. | ||||||
|     - run: cmake --build build |     - run: | | ||||||
|  |         cd build | ||||||
|  |         ninja | ||||||
|  |     - run: | | ||||||
|  |         cd build | ||||||
|  |         ninja test | ||||||
|  |  | ||||||
| #- run: ../build/test/ixwebsocket_unittest.exe | #- run: ../build/test/ixwebsocket_unittest.exe | ||||||
| # working-directory: test | # working-directory: test | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								.github/workflows/unittest_windows_gcc.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								.github/workflows/unittest_windows_gcc.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | name: windows_gcc | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     paths-ignore: | ||||||
|  |     - 'docs/**' | ||||||
|  |   pull_request: | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   windows: | ||||||
|  |     runs-on: windows-latest | ||||||
|  |     steps: | ||||||
|  |     - uses: actions/checkout@v1 | ||||||
|  |     - uses: seanmiddleditch/gha-setup-ninja@master | ||||||
|  |     - uses: egor-tensin/setup-mingw@v2 | ||||||
|  |     - run: | | ||||||
|  |         mkdir build | ||||||
|  |         cd build | ||||||
|  |         cmake -GNinja -DCMAKE_CXX_COMPILER=c++ -DCMAKE_C_COMPILER=cc -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 -DCMAKE_UNITY_BUILD=ON .. | ||||||
|  |     - run: | | ||||||
|  |         cd build | ||||||
|  |         ninja | ||||||
|  |     - run: | | ||||||
|  |         cd build | ||||||
|  |         ctest -V | ||||||
|  |         # ninja test | ||||||
|  |  | ||||||
|  | #- run: ../build/test/ixwebsocket_unittest.exe | ||||||
|  | # working-directory: test | ||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -7,3 +7,4 @@ ws/.certs/ | |||||||
| ws/.srl | ws/.srl | ||||||
| ixhttpd | ixhttpd | ||||||
| makefile | makefile | ||||||
|  | a.out | ||||||
|   | |||||||
| @@ -1,5 +1,8 @@ | |||||||
| find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h) | find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h) | ||||||
|  |  | ||||||
|  | # mbedtls-3.0 changed headers files, and we need to ifdef'out a few things | ||||||
|  | find_path(MBEDTLS_VERSION_GREATER_THAN_3 mbedtls/build_info.h) | ||||||
|  |  | ||||||
| find_library(MBEDTLS_LIBRARY mbedtls) | find_library(MBEDTLS_LIBRARY mbedtls) | ||||||
| find_library(MBEDX509_LIBRARY mbedx509) | find_library(MBEDX509_LIBRARY mbedx509) | ||||||
| find_library(MBEDCRYPTO_LIBRARY mbedcrypto) | find_library(MBEDCRYPTO_LIBRARY mbedcrypto) | ||||||
|   | |||||||
| @@ -12,6 +12,8 @@ set (CMAKE_CXX_STANDARD 11) | |||||||
| set (CXX_STANDARD_REQUIRED ON) | set (CXX_STANDARD_REQUIRED ON) | ||||||
| set (CMAKE_CXX_EXTENSIONS OFF) | set (CMAKE_CXX_EXTENSIONS OFF) | ||||||
|  |  | ||||||
|  | option (BUILD_DEMO OFF) | ||||||
|  |  | ||||||
| if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") | if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") | ||||||
|   set(CMAKE_POSITION_INDEPENDENT_CODE ON) |   set(CMAKE_POSITION_INDEPENDENT_CODE ON) | ||||||
| endif() | endif() | ||||||
| @@ -86,6 +88,7 @@ set( IXWEBSOCKET_HEADERS | |||||||
|     ixwebsocket/IXSocketTLSOptions.h |     ixwebsocket/IXSocketTLSOptions.h | ||||||
|     ixwebsocket/IXStrCaseCompare.h |     ixwebsocket/IXStrCaseCompare.h | ||||||
|     ixwebsocket/IXUdpSocket.h |     ixwebsocket/IXUdpSocket.h | ||||||
|  |     ixwebsocket/IXUniquePtr.h | ||||||
|     ixwebsocket/IXUrlParser.h |     ixwebsocket/IXUrlParser.h | ||||||
|     ixwebsocket/IXUuid.h |     ixwebsocket/IXUuid.h | ||||||
|     ixwebsocket/IXUtf8Validator.h |     ixwebsocket/IXUtf8Validator.h | ||||||
| @@ -111,6 +114,7 @@ set( IXWEBSOCKET_HEADERS | |||||||
|     ixwebsocket/IXWebSocketVersion.h |     ixwebsocket/IXWebSocketVersion.h | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | option(BUILD_SHARED_LIBS "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)" OFF) | ||||||
| option(USE_TLS "Enable TLS support" FALSE) | option(USE_TLS "Enable TLS support" FALSE) | ||||||
|  |  | ||||||
| if (USE_TLS) | if (USE_TLS) | ||||||
| @@ -144,7 +148,7 @@ if (USE_TLS) | |||||||
|     endif() |     endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| add_library( ixwebsocket STATIC | add_library( ixwebsocket | ||||||
|     ${IXWEBSOCKET_SOURCES} |     ${IXWEBSOCKET_SOURCES} | ||||||
|     ${IXWEBSOCKET_HEADERS} |     ${IXWEBSOCKET_HEADERS} | ||||||
| ) | ) | ||||||
| @@ -176,31 +180,43 @@ if (USE_TLS) | |||||||
|       # set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/local/include/openssl-1.0) |       # set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/local/include/openssl-1.0) | ||||||
|     endif() |     endif() | ||||||
|  |  | ||||||
|     # Use OPENSSL_ROOT_DIR CMake variable if you need to use your own openssl |     # This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL | ||||||
|  |     if (NOT OPENSSL_FOUND) | ||||||
|       find_package(OpenSSL REQUIRED) |       find_package(OpenSSL REQUIRED) | ||||||
|  |     endif() | ||||||
|     message(STATUS "OpenSSL: " ${OPENSSL_VERSION}) |     message(STATUS "OpenSSL: " ${OPENSSL_VERSION}) | ||||||
|  |  | ||||||
|     add_definitions(${OPENSSL_DEFINITIONS}) |     add_definitions(${OPENSSL_DEFINITIONS}) | ||||||
|     target_include_directories(ixwebsocket PUBLIC ${OPENSSL_INCLUDE_DIR}) |     target_include_directories(ixwebsocket PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>) | ||||||
|     target_link_libraries(ixwebsocket ${OPENSSL_LIBRARIES}) |     target_link_libraries(ixwebsocket ${OPENSSL_LIBRARIES}) | ||||||
|   elseif (USE_MBED_TLS) |   elseif (USE_MBED_TLS) | ||||||
|     message(STATUS "TLS configured to use mbedtls") |     message(STATUS "TLS configured to use mbedtls") | ||||||
|  |  | ||||||
|  |     # This MBEDTLS_FOUND check is to help find a cmake manually configured MbedTLS | ||||||
|  |     if (NOT MBEDTLS_FOUND) | ||||||
|       find_package(MbedTLS REQUIRED) |       find_package(MbedTLS REQUIRED) | ||||||
|     target_include_directories(ixwebsocket PUBLIC ${MBEDTLS_INCLUDE_DIRS}) |        | ||||||
|  |       if (MBEDTLS_VERSION_GREATER_THAN_3) | ||||||
|  |         target_compile_definitions(ixwebsocket PRIVATE IXWEBSOCKET_USE_MBED_TLS_MIN_VERSION_3) | ||||||
|  |       endif() | ||||||
|  |        | ||||||
|  |     endif() | ||||||
|  |     target_include_directories(ixwebsocket PUBLIC $<BUILD_INTERFACE:${MBEDTLS_INCLUDE_DIRS}>) | ||||||
|     target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES}) |     target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES}) | ||||||
|   elseif (USE_SECURE_TRANSPORT) |   elseif (USE_SECURE_TRANSPORT) | ||||||
|     message(STATUS "TLS configured to use secure transport") |     message(STATUS "TLS configured to use secure transport") | ||||||
|     target_link_libraries(ixwebsocket "-framework foundation" "-framework security") |     target_link_libraries(ixwebsocket "-framework Foundation" "-framework Security") | ||||||
|   endif() |   endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| option(USE_ZLIB "Enable zlib support" TRUE) | option(USE_ZLIB "Enable zlib support" TRUE) | ||||||
|  |  | ||||||
| if (USE_ZLIB) | if (USE_ZLIB) | ||||||
|   # Use ZLIB_ROOT CMake variable if you need to use your own zlib |   # This ZLIB_FOUND check is to help find a cmake manually configured zlib | ||||||
|  |   if (NOT ZLIB_FOUND) | ||||||
|     find_package(ZLIB REQUIRED) |     find_package(ZLIB REQUIRED) | ||||||
|   include_directories(${ZLIB_INCLUDE_DIRS}) |   endif() | ||||||
|  |   target_include_directories(ixwebsocket PUBLIC $<BUILD_INTERFACE:${ZLIB_INCLUDE_DIRS}>) | ||||||
|   target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES}) |   target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES}) | ||||||
|  |  | ||||||
|   target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB) |   target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB) | ||||||
| @@ -238,23 +254,29 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") | |||||||
|     target_compile_options(ixwebsocket PRIVATE /MP) |     target_compile_options(ixwebsocket PRIVATE /MP) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | include(GNUInstallDirs) | ||||||
|  |  | ||||||
| target_include_directories(ixwebsocket PUBLIC | target_include_directories(ixwebsocket PUBLIC | ||||||
|   $<BUILD_INTERFACE:${IXWEBSOCKET_INCLUDE_DIRS}/> |   $<BUILD_INTERFACE:${IXWEBSOCKET_INCLUDE_DIRS}/> | ||||||
|   $<INSTALL_INTERFACE:include/ixwebsocket> |   $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket> | ||||||
| ) | ) | ||||||
|  |  | ||||||
| set_target_properties(ixwebsocket PROPERTIES PUBLIC_HEADER "${IXWEBSOCKET_HEADERS}") | set_target_properties(ixwebsocket PROPERTIES PUBLIC_HEADER "${IXWEBSOCKET_HEADERS}") | ||||||
|  |  | ||||||
|  | option(IXWEBSOCKET_INSTALL "Install IXWebSocket" TRUE) | ||||||
|  |  | ||||||
|  | if (IXWEBSOCKET_INSTALL) | ||||||
|   install(TARGETS ixwebsocket |   install(TARGETS ixwebsocket | ||||||
|           EXPORT ixwebsocket |           EXPORT ixwebsocket | ||||||
|         ARCHIVE DESTINATION lib |           ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} | ||||||
|         PUBLIC_HEADER DESTINATION include/ixwebsocket/ |           PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket/ | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
|   install(EXPORT ixwebsocket |   install(EXPORT ixwebsocket | ||||||
|           FILE ixwebsocket-config.cmake |           FILE ixwebsocket-config.cmake | ||||||
|           NAMESPACE ixwebsocket:: |           NAMESPACE ixwebsocket:: | ||||||
|         DESTINATION lib/cmake/ixwebsocket) |           DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket) | ||||||
|  | endif() | ||||||
|  |  | ||||||
| if (USE_WS OR USE_TEST) | if (USE_WS OR USE_TEST) | ||||||
|   include(FetchContent) |   include(FetchContent) | ||||||
| @@ -273,3 +295,8 @@ if (USE_WS OR USE_TEST) | |||||||
|     add_subdirectory(test) |     add_subdirectory(test) | ||||||
|   endif() |   endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | if (BUILD_DEMO)  | ||||||
|  |   add_executable(demo main.cpp) | ||||||
|  |   target_link_libraries(demo ixwebsocket)  | ||||||
|  | endif() | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								README.md
									
									
									
									
									
								
							| @@ -15,13 +15,16 @@ A bad security bug affecting users compiling with SSL enabled and OpenSSL as the | |||||||
|  *  Super simple standalone example. See ws folder, unittest and doc/usage.md for more. |  *  Super simple standalone example. See ws folder, unittest and doc/usage.md for more. | ||||||
|  * |  * | ||||||
|  *  On macOS |  *  On macOS | ||||||
|  *  $ mkdir -p build ; cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install |  *  $ mkdir -p build ; (cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install) | ||||||
|  *  $ clang++ --std=c++14 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation |  *  $ clang++ --std=c++11 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation | ||||||
|  *  $ ./a.out |  *  $ ./a.out | ||||||
|  |  * | ||||||
|  |  *  Or use cmake -DBUILD_DEMO=ON option for other platforms | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <ixwebsocket/IXNetSystem.h> | #include <ixwebsocket/IXNetSystem.h> | ||||||
| #include <ixwebsocket/IXWebSocket.h> | #include <ixwebsocket/IXWebSocket.h> | ||||||
|  | #include <ixwebsocket/IXUserAgent.h> | ||||||
| #include <iostream> | #include <iostream> | ||||||
|  |  | ||||||
| int main() | int main() | ||||||
| @@ -32,6 +35,8 @@ int main() | |||||||
|     // Our websocket object |     // Our websocket object | ||||||
|     ix::WebSocket webSocket; |     ix::WebSocket webSocket; | ||||||
|  |  | ||||||
|  |     // Connect to a server with encryption | ||||||
|  |     // See https://machinezone.github.io/IXWebSocket/usage/#tls-support-and-configuration | ||||||
|     std::string url("wss://echo.websocket.org"); |     std::string url("wss://echo.websocket.org"); | ||||||
|     webSocket.setUrl(url); |     webSocket.setUrl(url); | ||||||
|  |  | ||||||
| @@ -44,10 +49,18 @@ int main() | |||||||
|             if (msg->type == ix::WebSocketMessageType::Message) |             if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|             { |             { | ||||||
|                 std::cout << "received message: " << msg->str << std::endl; |                 std::cout << "received message: " << msg->str << std::endl; | ||||||
|  |                 std::cout << "> " << std::flush; | ||||||
|             } |             } | ||||||
|             else if (msg->type == ix::WebSocketMessageType::Open) |             else if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|             { |             { | ||||||
|                 std::cout << "Connection established" << std::endl; |                 std::cout << "Connection established" << std::endl; | ||||||
|  |                 std::cout << "> " << std::flush; | ||||||
|  |             } | ||||||
|  |             else if (msg->type == ix::WebSocketMessageType::Error) | ||||||
|  |             { | ||||||
|  |                 // Maybe SSL is not configured properly | ||||||
|  |                 std::cout << "Connection error: " << msg->errorInfo.reason << std::endl; | ||||||
|  |                 std::cout << "> " << std::flush; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     ); |     ); | ||||||
| @@ -58,13 +71,16 @@ int main() | |||||||
|     // Send a message to the server (default to TEXT mode) |     // Send a message to the server (default to TEXT mode) | ||||||
|     webSocket.send("hello world"); |     webSocket.send("hello world"); | ||||||
|  |  | ||||||
|     while (true) |     // Display a prompt | ||||||
|     { |  | ||||||
|         std::string text; |  | ||||||
|     std::cout << "> " << std::flush; |     std::cout << "> " << std::flush; | ||||||
|         std::getline(std::cin, text); |  | ||||||
|  |  | ||||||
|  |     std::string text; | ||||||
|  |     // Read text from the console and send messages in text mode. | ||||||
|  |     // Exit with Ctrl-D on Unix or Ctrl-Z on Windows. | ||||||
|  |     while (std::getline(std::cin, text)) | ||||||
|  |     { | ||||||
|         webSocket.send(text); |         webSocket.send(text); | ||||||
|  |         std::cout << "> " << std::flush; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| @@ -77,6 +93,8 @@ IXWebSocket is actively being developed, check out the [changelog](https://machi | |||||||
|  |  | ||||||
| IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version. See the current [test results](https://bsergean.github.io/autobahn/reports/clients/index.html). Some tests are still failing in the server code. | IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version. See the current [test results](https://bsergean.github.io/autobahn/reports/clients/index.html). Some tests are still failing in the server code. | ||||||
|  |  | ||||||
|  | Starting with the 11.0.8 release, IXWebSocket should be fully C++11 compatible. | ||||||
|  |  | ||||||
| ## Users | ## Users | ||||||
|  |  | ||||||
| If your company or project is using this library, feel free to open an issue or PR to amend this list. | If your company or project is using this library, feel free to open an issue or PR to amend this list. | ||||||
| @@ -87,6 +105,7 @@ If your company or project is using this library, feel free to open an issue or | |||||||
| - [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod | - [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod | ||||||
| - [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper | - [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper | ||||||
| - [discord.cpp](https://github.com/luccanunes/discord.cpp), a discord library for making bots | - [discord.cpp](https://github.com/luccanunes/discord.cpp), a discord library for making bots | ||||||
|  | - [Teleport](http://teleportconnect.com/), Teleport is your own personal remote robot avatar | ||||||
|  |  | ||||||
| ## Alternative libraries | ## Alternative libraries | ||||||
|  |  | ||||||
| @@ -96,6 +115,7 @@ There are plenty of great websocket libraries out there, which might work for yo | |||||||
| * [beast](https://github.com/boostorg/beast) - C++ | * [beast](https://github.com/boostorg/beast) - C++ | ||||||
| * [libwebsockets](https://libwebsockets.org/) - C | * [libwebsockets](https://libwebsockets.org/) - C | ||||||
| * [µWebSockets](https://github.com/uNetworking/uWebSockets) - C | * [µWebSockets](https://github.com/uNetworking/uWebSockets) - C | ||||||
|  | * [wslay](https://github.com/tatsuhiro-t/wslay) - C | ||||||
|  |  | ||||||
| [uvweb](https://github.com/bsergean/uvweb) is a library written by the IXWebSocket author which is built on top of [uvw](https://github.com/skypjack/uvw), which is a C++ wrapper for [libuv](https://libuv.org/). It has more dependencies and does not support SSL at this point, but it can be used to open multiple connections within a single OS thread thanks to libuv. | [uvweb](https://github.com/bsergean/uvweb) is a library written by the IXWebSocket author which is built on top of [uvw](https://github.com/skypjack/uvw), which is a C++ wrapper for [libuv](https://libuv.org/). It has more dependencies and does not support SSL at this point, but it can be used to open multiple connections within a single OS thread thanks to libuv. | ||||||
|  |  | ||||||
| @@ -112,6 +132,11 @@ To check the performance of a websocket library, you can look at the [autoroute] | |||||||
| | Windows           | Disabled          | None              | [![Build2][5]][0] | | | Windows           | Disabled          | None              | [![Build2][5]][0] | | ||||||
| | UWP               | Disabled          | None              | [![Build2][6]][0] | | | UWP               | Disabled          | None              | [![Build2][6]][0] | | ||||||
| | Linux             | OpenSSL           | Address Sanitizer | [![Build2][7]][0] | | | Linux             | OpenSSL           | Address Sanitizer | [![Build2][7]][0] | | ||||||
|  | | Mingw             | Disabled          | None              | [![Build2][8]][0] | | ||||||
|  |  | ||||||
|  | * ASAN fails on Linux because of a known problem, we need a  | ||||||
|  | * Some tests are disabled on Windows/UWP because of a pathing problem | ||||||
|  | * TLS and ZLIB are disabled on Windows/UWP because enabling make the CI run takes a lot of time, for setting up vcpkg. | ||||||
|  |  | ||||||
| [0]: https://github.com/machinezone/IXWebSocket | [0]: https://github.com/machinezone/IXWebSocket | ||||||
| [1]: https://github.com/machinezone/IXWebSocket/workflows/linux/badge.svg | [1]: https://github.com/machinezone/IXWebSocket/workflows/linux/badge.svg | ||||||
| @@ -121,4 +146,5 @@ To check the performance of a websocket library, you can look at the [autoroute] | |||||||
| [5]: https://github.com/machinezone/IXWebSocket/workflows/windows/badge.svg | [5]: https://github.com/machinezone/IXWebSocket/workflows/windows/badge.svg | ||||||
| [6]: https://github.com/machinezone/IXWebSocket/workflows/uwp/badge.svg | [6]: https://github.com/machinezone/IXWebSocket/workflows/uwp/badge.svg | ||||||
| [7]: https://github.com/machinezone/IXWebSocket/workflows/linux_asan/badge.svg | [7]: https://github.com/machinezone/IXWebSocket/workflows/linux_asan/badge.svg | ||||||
|  | [8]: https://github.com/machinezone/IXWebSocket/workflows/unittest_windows_gcc/badge.svg | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,82 @@ | |||||||
|  |  | ||||||
| All changes to this project will be documented in this file. | All changes to this project will be documented in this file. | ||||||
|  |  | ||||||
|  | ## [11.3.1] - 2021-10-22 | ||||||
|  |  | ||||||
|  | (library/cmake) Compatible with MbedTLS 3 + fix a bug on Windows where the incorrect remote port is computed (#320) | ||||||
|  |  | ||||||
|  | ## [11.3.0] - 2021-09-20 | ||||||
|  |  | ||||||
|  | (library/cmake) Only find OpenSSL, MbedTLS, zlib if they have not already been found, make CMake install optional (#317) + Use GNUInstallDirs in cmake (#318) | ||||||
|  |  | ||||||
|  | ## [11.2.10] - 2021-07-27 | ||||||
|  |  | ||||||
|  | (ws) bump CLI command line parsing library from 1.8 to 2.0 | ||||||
|  |  | ||||||
|  | ## [11.2.9] - 2021-06-08 | ||||||
|  |  | ||||||
|  | (ws) ws connect has a -g option to gzip decompress messages for API such as the websocket Huobi Global. | ||||||
|  |  | ||||||
|  | ## [11.2.8] - 2021-06-03 | ||||||
|  |  | ||||||
|  | (websocket client + server) WebSocketMessage class tweak to fix unsafe patterns | ||||||
|  |  | ||||||
|  | ## [11.2.7] - 2021-05-27 | ||||||
|  |  | ||||||
|  | (websocket server) Handle and accept firefox browser special upgrade value (keep-alive, Upgrade) | ||||||
|  |  | ||||||
|  | ## [11.2.6] - 2021-05-18 | ||||||
|  |  | ||||||
|  | (Windows) move EINVAL (re)definition from IXSocket.h to IXNetSystem.h (fix #289) | ||||||
|  |  | ||||||
|  | ## [11.2.5] - 2021-04-04 | ||||||
|  |  | ||||||
|  | (http client) DEL is not an HTTP method name, but DELETE is | ||||||
|  |  | ||||||
|  | ## [11.2.4] - 2021-03-25 | ||||||
|  |  | ||||||
|  | (cmake) install IXUniquePtr.h | ||||||
|  |  | ||||||
|  | ## [11.2.3] - 2021-03-24 | ||||||
|  |  | ||||||
|  | (ssl + windows) missing include for CertOpenStore function | ||||||
|  |  | ||||||
|  | ## [11.2.2] - 2021-03-23 | ||||||
|  |  | ||||||
|  | (ixwebsocket) version bump | ||||||
|  |  | ||||||
|  | ## [11.2.1] - 2021-03-23 | ||||||
|  |  | ||||||
|  | (ixwebsocket) version bump | ||||||
|  |  | ||||||
|  | ## [11.2.0] - 2021-03-23 | ||||||
|  |  | ||||||
|  | (ixwebsocket) correct mingw support (gcc on windows) | ||||||
|  |  | ||||||
|  | ## [11.1.4] - 2021-03-23 | ||||||
|  |  | ||||||
|  | (ixwebsocket) add getMinWaitBetweenReconnectionRetries | ||||||
|  |  | ||||||
|  | ## [11.1.3] - 2021-03-23 | ||||||
|  |  | ||||||
|  | (ixwebsocket) New option to set the min wait between reconnection attempts. Still default to 1ms. (setMinWaitBetweenReconnectionRetries). | ||||||
|  |  | ||||||
|  | ## [11.1.2] - 2021-03-22 | ||||||
|  |  | ||||||
|  | (ws) initialize maxWaitBetweenReconnectionRetries to a non zero value ; a zero value was causing spurious reconnections attempts | ||||||
|  |  | ||||||
|  | ## [11.1.1] - 2021-03-20 | ||||||
|  |  | ||||||
|  | (cmake) Library can be built as a static or a dynamic library, controlled with BUILD_SHARED_LIBS. Default to static library | ||||||
|  |  | ||||||
|  | ## [11.1.0] - 2021-03-16 | ||||||
|  |  | ||||||
|  | (ixwebsocket) Use LEAN_AND_MEAN Windows define to help with undefined link error when building a DLL. Support websocket server disablePerMessageDeflate option correctly. | ||||||
|  |  | ||||||
|  | ## [11.0.9] - 2021-03-07 | ||||||
|  |  | ||||||
|  | (ixwebsocket) Expose setHandshakeTimeout method | ||||||
|  |  | ||||||
| ## [11.0.8] - 2020-12-25 | ## [11.0.8] - 2020-12-25 | ||||||
|  |  | ||||||
| (ws) trim ws dependencies no more ixcrypto and ixcore deps | (ws) trim ws dependencies no more ixcrypto and ixcore deps | ||||||
|   | |||||||
| @@ -17,13 +17,13 @@ There is a unittest which can be executed by typing `make test`. | |||||||
|  |  | ||||||
| Options for building: | Options for building: | ||||||
|  |  | ||||||
|  | * `-DBUILD_SHARED_LIBS=ON` will build the unittest as a shared libary instead of a static library, which is the default | ||||||
| * `-DUSE_ZLIB=1` will enable zlib support, required for http client + server + websocket per message deflate extension | * `-DUSE_ZLIB=1` will enable zlib support, required for http client + server + websocket per message deflate extension | ||||||
| * `-DUSE_TLS=1` will enable TLS support | * `-DUSE_TLS=1` will enable TLS support | ||||||
| * `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows) | * `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows). When using a custom version of openssl (say a prebuilt version, odd runtime problems can happens, as in #319, and special cmake trickery will be required (see this [comment](https://github.com/machinezone/IXWebSocket/issues/175#issuecomment-620231032)) | ||||||
| * `-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 | ||||||
| * `-DUSE_WS=1` will build the ws interactive command line tool | * `-DUSE_WS=1` will build the ws interactive command line tool | ||||||
| * `-DUSE_TEST=1` will build the unittest | * `-DUSE_TEST=1` will build the unittest | ||||||
| * `-DUSE_PYTHON=1` will use Python3 for cobra bots, require Python3 to be installed. |  | ||||||
|  |  | ||||||
| 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/unittest_windows.yml) 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 (not maintained much though) or rather the [github actions](https://github.com/machinezone/IXWebSocket/blob/master/.github/workflows/unittest_windows.yml) which have instructions for building dependencies. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -256,11 +256,24 @@ Wait time(ms): 6400 | |||||||
| Wait time(ms): 10000 | Wait time(ms): 10000 | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| The waiting time is capped by default at 10s between 2 attempts, but that value can be changed and queried. | The waiting time is capped by default at 10s between 2 attempts, but that value | ||||||
|  | can be changed and queried. The minimum waiting time can also be set. | ||||||
|  |  | ||||||
| ```cpp | ```cpp | ||||||
| webSocket.setMaxWaitBetweenReconnectionRetries(5 * 1000); // 5000ms = 5s | webSocket.setMaxWaitBetweenReconnectionRetries(5 * 1000); // 5000ms = 5s | ||||||
| uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries(); | uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries(); | ||||||
|  |  | ||||||
|  | webSocket.setMinWaitBetweenReconnectionRetries(1000); // 1000ms = 1s | ||||||
|  | uint32_t m = webSocket.getMinWaitBetweenReconnectionRetries(); | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Handshake timeout | ||||||
|  |  | ||||||
|  | You can control how long to wait until timing out while waiting for the websocket handshake to be performed. | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | int handshakeTimeoutSecs = 1; | ||||||
|  | setHandshakeTimeout(handshakeTimeoutSecs); | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ## WebSocket server API | ## WebSocket server API | ||||||
| @@ -334,6 +347,10 @@ if (!res.first) | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Per message deflate connection is enabled by default. It can be disabled | ||||||
|  | // which might be helpful when running on low power devices such as a Rasbery Pi | ||||||
|  | server.disablePerMessageDeflate(); | ||||||
|  |  | ||||||
| // Run the server in the background. Server can be stoped by calling server.stop() | // Run the server in the background. Server can be stoped by calling server.stop() | ||||||
| server.start(); | server.start(); | ||||||
|  |  | ||||||
| @@ -357,13 +374,10 @@ The webSocket reference is guaranteed to be always valid ; by design the callbac | |||||||
| // Bound host name, max connections and listen backlog can also be passed in as parameters. | // Bound host name, max connections and listen backlog can also be passed in as parameters. | ||||||
| ix::WebSocketServer server(port); | ix::WebSocketServer server(port); | ||||||
|  |  | ||||||
| server.setOnClientMessageCallback(std::shared_ptr<ConnectionState> connectionState, | server.setOnClientMessageCallback([](std::shared_ptr<ix::ConnectionState> connectionState, ix::WebSocket & webSocket, const ix::WebSocketMessagePtr & msg) { | ||||||
|                                   WebSocket& webSocket, |  | ||||||
|                                   const WebSocketMessagePtr& msg) |  | ||||||
| { |  | ||||||
|     // The ConnectionState object contains information about the connection, |     // The ConnectionState object contains information about the connection, | ||||||
|     // at this point only the client ip address and the port. |     // at this point only the client ip address and the port. | ||||||
|     std::cout << "Remote ip: " << connectionState->getRemoteIp(); |     std::cout << "Remote ip: " << connectionState->getRemoteIp() << std::endl; | ||||||
|  |  | ||||||
|     if (msg->type == ix::WebSocketMessageType::Open) |     if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|     { |     { | ||||||
| @@ -381,7 +395,7 @@ server.setOnClientMessageCallback(std::shared_ptr<ConnectionState> connectionSta | |||||||
|         std::cout << "Headers:" << std::endl; |         std::cout << "Headers:" << std::endl; | ||||||
|         for (auto it : msg->openInfo.headers) |         for (auto it : msg->openInfo.headers) | ||||||
|         { |         { | ||||||
|             std::cout << it.first << ": " << it.second << std::endl; |             std::cout << "\t" << it.first << ": " << it.second << std::endl; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     else if (msg->type == ix::WebSocketMessageType::Message) |     else if (msg->type == ix::WebSocketMessageType::Message) | ||||||
| @@ -390,9 +404,11 @@ server.setOnClientMessageCallback(std::shared_ptr<ConnectionState> connectionSta | |||||||
|         // All connected clients are available in an std::set. See the broadcast cpp example. |         // All connected clients are available in an std::set. See the broadcast cpp example. | ||||||
|         // Second parameter tells whether we are sending the message in binary or text mode. |         // Second parameter tells whether we are sending the message in binary or text mode. | ||||||
|         // Here we send it in the same mode as it was received. |         // Here we send it in the same mode as it was received. | ||||||
|  |         std::cout << "Received: " << msg->str << std::endl; | ||||||
|  |  | ||||||
|         webSocket.send(msg->str, msg->binary); |         webSocket.send(msg->str, msg->binary); | ||||||
|     } |     } | ||||||
| ); | }); | ||||||
|  |  | ||||||
| auto res = server.listen(); | auto res = server.listen(); | ||||||
| if (!res.first) | if (!res.first) | ||||||
| @@ -401,6 +417,10 @@ if (!res.first) | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Per message deflate connection is enabled by default. It can be disabled | ||||||
|  | // which might be helpful when running on low power devices such as a Rasbery Pi | ||||||
|  | server.disablePerMessageDeflate(); | ||||||
|  |  | ||||||
| // Run the server in the background. Server can be stoped by calling server.stop() | // Run the server in the background. Server can be stoped by calling server.stop() | ||||||
| server.start(); | server.start(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,6 +24,12 @@ | |||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <thread> | #include <thread> | ||||||
|  |  | ||||||
|  | // mingw build quirks | ||||||
|  | #if defined(_WIN32) && defined(__GNUC__) | ||||||
|  | #define AI_NUMERICSERV NI_NUMERICSERV | ||||||
|  | #define AI_ADDRCONFIG LUP_ADDRCONFIG | ||||||
|  | #endif | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     const int64_t DNSLookup::kDefaultWait = 1; // ms |     const int64_t DNSLookup::kDefaultWait = 1; // ms | ||||||
|   | |||||||
| @@ -10,16 +10,22 @@ | |||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count, |     uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount, | ||||||
|                                             uint32_t maxWaitBetweenReconnectionRetries) |                                             uint32_t maxWaitBetweenReconnectionRetries, | ||||||
|  |                                             uint32_t minWaitBetweenReconnectionRetries) | ||||||
|     { |     { | ||||||
|         uint32_t wait_time = (retry_count < 26) ? (std::pow(2, retry_count) * 100) : 0; |         uint32_t waitTime = (retryCount < 26) ? (std::pow(2, retryCount) * 100) : 0; | ||||||
|  |  | ||||||
|         if (wait_time > maxWaitBetweenReconnectionRetries || wait_time == 0) |         if (waitTime < minWaitBetweenReconnectionRetries) | ||||||
|         { |         { | ||||||
|             wait_time = maxWaitBetweenReconnectionRetries; |             waitTime = minWaitBetweenReconnectionRetries; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return wait_time; |         if (waitTime > maxWaitBetweenReconnectionRetries || waitTime == 0) | ||||||
|  |         { | ||||||
|  |             waitTime = maxWaitBetweenReconnectionRetries; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return waitTime; | ||||||
|     } |     } | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ | |||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count, |     uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount, | ||||||
|                                             uint32_t maxWaitBetweenReconnectionRetries); |                                             uint32_t maxWaitBetweenReconnectionRetries, | ||||||
|  |                                             uint32_t minWaitBetweenReconnectionRetries); | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -137,7 +137,7 @@ namespace ix | |||||||
|             { |             { | ||||||
|                 contentLength = std::stoi(headers["Content-Length"]); |                 contentLength = std::stoi(headers["Content-Length"]); | ||||||
|             } |             } | ||||||
|             catch (std::exception) |             catch (const std::exception&) | ||||||
|             { |             { | ||||||
|                 return std::make_tuple( |                 return std::make_tuple( | ||||||
|                     false, "Error parsing HTTP Header 'Content-Length'", httpRequest); |                     false, "Error parsing HTTP Header 'Content-Length'", httpRequest); | ||||||
|   | |||||||
| @@ -20,10 +20,11 @@ | |||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|  |     // https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods | ||||||
|     const std::string HttpClient::kPost = "POST"; |     const std::string HttpClient::kPost = "POST"; | ||||||
|     const std::string HttpClient::kGet = "GET"; |     const std::string HttpClient::kGet = "GET"; | ||||||
|     const std::string HttpClient::kHead = "HEAD"; |     const std::string HttpClient::kHead = "HEAD"; | ||||||
|     const std::string HttpClient::kDel = "DEL"; |     const std::string HttpClient::kDelete = "DELETE"; | ||||||
|     const std::string HttpClient::kPut = "PUT"; |     const std::string HttpClient::kPut = "PUT"; | ||||||
|     const std::string HttpClient::kPatch = "PATCH"; |     const std::string HttpClient::kPatch = "PATCH"; | ||||||
|  |  | ||||||
| @@ -189,14 +190,14 @@ namespace ix | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Set a default Accept header if none is present |         // Set a default Accept header if none is present | ||||||
|         if (headers.find("Accept") == headers.end()) |         if (args->extraHeaders.find("Accept") == args->extraHeaders.end()) | ||||||
|         { |         { | ||||||
|             ss << "Accept: */*" |             ss << "Accept: */*" | ||||||
|                << "\r\n"; |                << "\r\n"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Set a default User agent if none is present |         // Set a default User agent if none is present | ||||||
|         if (headers.find("User-Agent") == headers.end()) |         if (args->extraHeaders.find("User-Agent") == args->extraHeaders.end()) | ||||||
|         { |         { | ||||||
|             ss << "User-Agent: " << userAgent() << "\r\n"; |             ss << "User-Agent: " << userAgent() << "\r\n"; | ||||||
|         } |         } | ||||||
| @@ -557,9 +558,9 @@ namespace ix | |||||||
|         return request(url, kHead, std::string(), args); |         return request(url, kHead, std::string(), args); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     HttpResponsePtr HttpClient::del(const std::string& url, HttpRequestArgsPtr args) |     HttpResponsePtr HttpClient::Delete(const std::string& url, HttpRequestArgsPtr args) | ||||||
|     { |     { | ||||||
|         return request(url, kDel, std::string(), args); |         return request(url, kDelete, std::string(), args); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     HttpResponsePtr HttpClient::request(const std::string& url, |     HttpResponsePtr HttpClient::request(const std::string& url, | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ namespace ix | |||||||
|  |  | ||||||
|         HttpResponsePtr get(const std::string& url, HttpRequestArgsPtr args); |         HttpResponsePtr get(const std::string& url, HttpRequestArgsPtr args); | ||||||
|         HttpResponsePtr head(const std::string& url, HttpRequestArgsPtr args); |         HttpResponsePtr head(const std::string& url, HttpRequestArgsPtr args); | ||||||
|         HttpResponsePtr del(const std::string& url, HttpRequestArgsPtr args); |         HttpResponsePtr Delete(const std::string& url, HttpRequestArgsPtr args); | ||||||
|  |  | ||||||
|         HttpResponsePtr post(const std::string& url, |         HttpResponsePtr post(const std::string& url, | ||||||
|                              const HttpParameters& httpParameters, |                              const HttpParameters& httpParameters, | ||||||
| @@ -94,7 +94,7 @@ namespace ix | |||||||
|         const static std::string kPost; |         const static std::string kPost; | ||||||
|         const static std::string kGet; |         const static std::string kGet; | ||||||
|         const static std::string kHead; |         const static std::string kHead; | ||||||
|         const static std::string kDel; |         const static std::string kDelete; | ||||||
|         const static std::string kPut; |         const static std::string kPut; | ||||||
|         const static std::string kPatch; |         const static std::string kPatch; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -124,4 +124,158 @@ namespace ix | |||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // | ||||||
|  |     // mingw does not have inet_ntop, which were taken as is from the musl C library. | ||||||
|  |     // | ||||||
|  |     const char* inet_ntop(int af, const void* a0, char* s, socklen_t l) | ||||||
|  |     { | ||||||
|  | #if defined(_WIN32) && defined(__GNUC__) | ||||||
|  |         const unsigned char* a = (const unsigned char*) a0; | ||||||
|  |         int i, j, max, best; | ||||||
|  |         char buf[100]; | ||||||
|  |  | ||||||
|  |         switch (af) | ||||||
|  |         { | ||||||
|  |             case AF_INET: | ||||||
|  |                 if (snprintf(s, l, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]) < l) return s; | ||||||
|  |                 break; | ||||||
|  |             case AF_INET6: | ||||||
|  |                 if (memcmp(a, "\0\0\0\0\0\0\0\0\0\0\377\377", 12)) | ||||||
|  |                     snprintf(buf, | ||||||
|  |                              sizeof buf, | ||||||
|  |                              "%x:%x:%x:%x:%x:%x:%x:%x", | ||||||
|  |                              256 * a[0] + a[1], | ||||||
|  |                              256 * a[2] + a[3], | ||||||
|  |                              256 * a[4] + a[5], | ||||||
|  |                              256 * a[6] + a[7], | ||||||
|  |                              256 * a[8] + a[9], | ||||||
|  |                              256 * a[10] + a[11], | ||||||
|  |                              256 * a[12] + a[13], | ||||||
|  |                              256 * a[14] + a[15]); | ||||||
|  |                 else | ||||||
|  |                     snprintf(buf, | ||||||
|  |                              sizeof buf, | ||||||
|  |                              "%x:%x:%x:%x:%x:%x:%d.%d.%d.%d", | ||||||
|  |                              256 * a[0] + a[1], | ||||||
|  |                              256 * a[2] + a[3], | ||||||
|  |                              256 * a[4] + a[5], | ||||||
|  |                              256 * a[6] + a[7], | ||||||
|  |                              256 * a[8] + a[9], | ||||||
|  |                              256 * a[10] + a[11], | ||||||
|  |                              a[12], | ||||||
|  |                              a[13], | ||||||
|  |                              a[14], | ||||||
|  |                              a[15]); | ||||||
|  |                 /* Replace longest /(^0|:)[:0]{2,}/ with "::" */ | ||||||
|  |                 for (i = best = 0, max = 2; buf[i]; i++) | ||||||
|  |                 { | ||||||
|  |                     if (i && buf[i] != ':') continue; | ||||||
|  |                     j = strspn(buf + i, ":0"); | ||||||
|  |                     if (j > max) best = i, max = j; | ||||||
|  |                 } | ||||||
|  |                 if (max > 3) | ||||||
|  |                 { | ||||||
|  |                     buf[best] = buf[best + 1] = ':'; | ||||||
|  |                     memmove(buf + best + 2, buf + best + max, i - best - max + 1); | ||||||
|  |                 } | ||||||
|  |                 if (strlen(buf) < l) | ||||||
|  |                 { | ||||||
|  |                     strcpy(s, buf); | ||||||
|  |                     return s; | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             default: errno = EAFNOSUPPORT; return 0; | ||||||
|  |         } | ||||||
|  |         errno = ENOSPC; | ||||||
|  |         return 0; | ||||||
|  | #else | ||||||
|  |         return ::inet_ntop(af, a0, s, l); | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #if defined(_WIN32) && defined(__GNUC__) | ||||||
|  |     static int hexval(unsigned c) | ||||||
|  |     { | ||||||
|  |         if (c - '0' < 10) return c - '0'; | ||||||
|  |         c |= 32; | ||||||
|  |         if (c - 'a' < 6) return c - 'a' + 10; | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     // | ||||||
|  |     // mingw does not have inet_pton, which were taken as is from the musl C library. | ||||||
|  |     // | ||||||
|  |     int inet_pton(int af, const char* s, void* a0) | ||||||
|  |     { | ||||||
|  | #if defined(_WIN32) && defined(__GNUC__) | ||||||
|  |         uint16_t ip[8]; | ||||||
|  |         unsigned char* a = (unsigned char*) a0; | ||||||
|  |         int i, j, v, d, brk = -1, need_v4 = 0; | ||||||
|  |  | ||||||
|  |         if (af == AF_INET) | ||||||
|  |         { | ||||||
|  |             for (i = 0; i < 4; i++) | ||||||
|  |             { | ||||||
|  |                 for (v = j = 0; j < 3 && isdigit(s[j]); j++) | ||||||
|  |                     v = 10 * v + s[j] - '0'; | ||||||
|  |                 if (j == 0 || (j > 1 && s[0] == '0') || v > 255) return 0; | ||||||
|  |                 a[i] = v; | ||||||
|  |                 if (s[j] == 0 && i == 3) return 1; | ||||||
|  |                 if (s[j] != '.') return 0; | ||||||
|  |                 s += j + 1; | ||||||
|  |             } | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         else if (af != AF_INET6) | ||||||
|  |         { | ||||||
|  |             errno = EAFNOSUPPORT; | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (*s == ':' && *++s != ':') return 0; | ||||||
|  |  | ||||||
|  |         for (i = 0;; i++) | ||||||
|  |         { | ||||||
|  |             if (s[0] == ':' && brk < 0) | ||||||
|  |             { | ||||||
|  |                 brk = i; | ||||||
|  |                 ip[i & 7] = 0; | ||||||
|  |                 if (!*++s) break; | ||||||
|  |                 if (i == 7) return 0; | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             for (v = j = 0; j < 4 && (d = hexval(s[j])) >= 0; j++) | ||||||
|  |                 v = 16 * v + d; | ||||||
|  |             if (j == 0) return 0; | ||||||
|  |             ip[i & 7] = v; | ||||||
|  |             if (!s[j] && (brk >= 0 || i == 7)) break; | ||||||
|  |             if (i == 7) return 0; | ||||||
|  |             if (s[j] != ':') | ||||||
|  |             { | ||||||
|  |                 if (s[j] != '.' || (i < 6 && brk < 0)) return 0; | ||||||
|  |                 need_v4 = 1; | ||||||
|  |                 i++; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             s += j + 1; | ||||||
|  |         } | ||||||
|  |         if (brk >= 0) | ||||||
|  |         { | ||||||
|  |             memmove(ip + brk + 7 - i, ip + brk, 2 * (i + 1 - brk)); | ||||||
|  |             for (j = 0; j < 7 - i; j++) | ||||||
|  |                 ip[brk + j] = 0; | ||||||
|  |         } | ||||||
|  |         for (j = 0; j < 8; j++) | ||||||
|  |         { | ||||||
|  |             *a++ = ip[j] >> 8; | ||||||
|  |             *a++ = ip[j]; | ||||||
|  |         } | ||||||
|  |         if (need_v4 && inet_pton(AF_INET, (const char*) s, a - 4) <= 0) return 0; | ||||||
|  |         return 1; | ||||||
|  | #else | ||||||
|  |         return ::inet_pton(af, s, a0); | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -7,15 +7,49 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|  |  | ||||||
|  | #ifndef WIN32_LEAN_AND_MEAN | ||||||
|  | #define WIN32_LEAN_AND_MEAN | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #include <WS2tcpip.h> | #include <WS2tcpip.h> | ||||||
| #include <WinSock2.h> | #include <WinSock2.h> | ||||||
| #include <basetsd.h> | #include <basetsd.h> | ||||||
| #include <io.h> | #include <io.h> | ||||||
| #include <ws2def.h> | #include <ws2def.h> | ||||||
|  |  | ||||||
|  | #undef EWOULDBLOCK | ||||||
|  | #undef EAGAIN | ||||||
|  | #undef EINPROGRESS | ||||||
|  | #undef EBADF | ||||||
|  | #undef EINVAL | ||||||
|  |  | ||||||
|  | // map to WSA error codes | ||||||
|  | #define EWOULDBLOCK WSAEWOULDBLOCK | ||||||
|  | #define EAGAIN WSATRY_AGAIN | ||||||
|  | #define EINPROGRESS WSAEINPROGRESS | ||||||
|  | #define EBADF WSAEBADF | ||||||
|  | #define EINVAL WSAEINVAL | ||||||
|  |  | ||||||
| // Define our own poll on Windows, as a wrapper on top of select | // Define our own poll on Windows, as a wrapper on top of select | ||||||
| typedef unsigned long int nfds_t; | typedef unsigned long int nfds_t; | ||||||
|  |  | ||||||
|  | // mingw does not know about poll so mock it | ||||||
|  | #if defined(__GNUC__) | ||||||
|  | struct pollfd | ||||||
|  | { | ||||||
|  |     int fd;        /* file descriptor */ | ||||||
|  |     short events;  /* requested events */ | ||||||
|  |     short revents; /* returned events */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define POLLIN 0x001   /* There is data to read.  */ | ||||||
|  | #define POLLOUT 0x004  /* Writing now will not block.  */ | ||||||
|  | #define POLLERR 0x008  /* Error condition.  */ | ||||||
|  | #define POLLHUP 0x010  /* Hung up.  */ | ||||||
|  | #define POLLNVAL 0x020 /* Invalid polling request.  */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #else | #else | ||||||
| #include <arpa/inet.h> | #include <arpa/inet.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| @@ -44,4 +78,7 @@ namespace ix | |||||||
|     bool uninitNetSystem(); |     bool uninitNetSystem(); | ||||||
|  |  | ||||||
|     int poll(struct pollfd* fds, nfds_t nfds, int timeout); |     int poll(struct pollfd* fds, nfds_t nfds, int timeout); | ||||||
|  |  | ||||||
|  |     const char* inet_ntop(int af, const void* src, char* dst, socklen_t size); | ||||||
|  |     int inet_pton(int af, const char* src, void* dst); | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -37,6 +37,7 @@ namespace ix | |||||||
|  |  | ||||||
|     void SetThreadName(DWORD dwThreadID, const char* threadName) |     void SetThreadName(DWORD dwThreadID, const char* threadName) | ||||||
|     { |     { | ||||||
|  | #ifndef __GNUC__ | ||||||
|         THREADNAME_INFO info; |         THREADNAME_INFO info; | ||||||
|         info.dwType = 0x1000; |         info.dwType = 0x1000; | ||||||
|         info.szName = threadName; |         info.szName = threadName; | ||||||
| @@ -51,6 +52,7 @@ namespace ix | |||||||
|         __except (EXCEPTION_EXECUTE_HANDLER) |         __except (EXCEPTION_EXECUTE_HANDLER) | ||||||
|         { |         { | ||||||
|         } |         } | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,20 +15,6 @@ | |||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| #include <BaseTsd.h> | #include <BaseTsd.h> | ||||||
| typedef SSIZE_T ssize_t; | typedef SSIZE_T ssize_t; | ||||||
|  |  | ||||||
| #undef EWOULDBLOCK |  | ||||||
| #undef EAGAIN |  | ||||||
| #undef EINPROGRESS |  | ||||||
| #undef EBADF |  | ||||||
| #undef EINVAL |  | ||||||
|  |  | ||||||
| // map to WSA error codes |  | ||||||
| #define EWOULDBLOCK WSAEWOULDBLOCK |  | ||||||
| #define EAGAIN WSATRY_AGAIN |  | ||||||
| #define EINPROGRESS WSAEINPROGRESS |  | ||||||
| #define EBADF WSAEBADF |  | ||||||
| #define EINVAL WSAEINVAL |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #include "IXCancellationRequest.h" | #include "IXCancellationRequest.h" | ||||||
|   | |||||||
| @@ -16,6 +16,11 @@ | |||||||
| #include "IXSocketConnect.h" | #include "IXSocketConnect.h" | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  | // For manipulating the certificate store | ||||||
|  | #include <wincrypt.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd) |     SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd) | ||||||
| @@ -127,7 +132,11 @@ namespace ix | |||||||
|                 errMsg = "Cannot parse cert file '" + _tlsOptions.certFile + "'"; |                 errMsg = "Cannot parse cert file '" + _tlsOptions.certFile + "'"; | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|  | #ifdef IXWEBSOCKET_USE_MBED_TLS_MIN_VERSION_3 | ||||||
|  |             if (mbedtls_pk_parse_keyfile(&_pkey, _tlsOptions.keyFile.c_str(), "", mbedtls_ctr_drbg_random, &_ctr_drbg) < 0) | ||||||
|  | #else | ||||||
|             if (mbedtls_pk_parse_keyfile(&_pkey, _tlsOptions.keyFile.c_str(), "") < 0) |             if (mbedtls_pk_parse_keyfile(&_pkey, _tlsOptions.keyFile.c_str(), "") < 0) | ||||||
|  | #endif | ||||||
|             { |             { | ||||||
|                 errMsg = "Cannot parse key file '" + _tlsOptions.keyFile + "'"; |                 errMsg = "Cannot parse key file '" + _tlsOptions.keyFile + "'"; | ||||||
|                 return false; |                 return false; | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ | |||||||
| #include <mbedtls/debug.h> | #include <mbedtls/debug.h> | ||||||
| #include <mbedtls/entropy.h> | #include <mbedtls/entropy.h> | ||||||
| #include <mbedtls/error.h> | #include <mbedtls/error.h> | ||||||
| #include <mbedtls/net.h> | #include <mbedtls/net_sockets.h> | ||||||
| #include <mbedtls/platform.h> | #include <mbedtls/platform.h> | ||||||
| #include <mbedtls/x509.h> | #include <mbedtls/x509.h> | ||||||
| #include <mbedtls/x509_crt.h> | #include <mbedtls/x509_crt.h> | ||||||
|   | |||||||
| @@ -24,6 +24,11 @@ | |||||||
| #endif | #endif | ||||||
| #define socketerrno errno | #define socketerrno errno | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  | // For manipulating the certificate store | ||||||
|  | #include <wincrypt.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| namespace | namespace | ||||||
| { | { | ||||||
|   | |||||||
| @@ -104,7 +104,7 @@ namespace ix | |||||||
|             server.sin_family = _addressFamily; |             server.sin_family = _addressFamily; | ||||||
|             server.sin_port = htons(_port); |             server.sin_port = htons(_port); | ||||||
|  |  | ||||||
|             if (inet_pton(_addressFamily, _host.c_str(), &server.sin_addr.s_addr) <= 0) |             if (ix::inet_pton(_addressFamily, _host.c_str(), &server.sin_addr.s_addr) <= 0) | ||||||
|             { |             { | ||||||
|                 std::stringstream ss; |                 std::stringstream ss; | ||||||
|                 ss << "SocketServer::listen() error calling inet_pton " |                 ss << "SocketServer::listen() error calling inet_pton " | ||||||
| @@ -133,7 +133,7 @@ namespace ix | |||||||
|             server.sin6_family = _addressFamily; |             server.sin6_family = _addressFamily; | ||||||
|             server.sin6_port = htons(_port); |             server.sin6_port = htons(_port); | ||||||
|  |  | ||||||
|             if (inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= 0) |             if (ix::inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= 0) | ||||||
|             { |             { | ||||||
|                 std::stringstream ss; |                 std::stringstream ss; | ||||||
|                 ss << "SocketServer::listen() error calling inet_pton " |                 ss << "SocketServer::listen() error calling inet_pton " | ||||||
| @@ -338,7 +338,7 @@ namespace ix | |||||||
|             if (_addressFamily == AF_INET) |             if (_addressFamily == AF_INET) | ||||||
|             { |             { | ||||||
|                 char remoteIp4[INET_ADDRSTRLEN]; |                 char remoteIp4[INET_ADDRSTRLEN]; | ||||||
|                 if (inet_ntop(AF_INET, &client.sin_addr, remoteIp4, INET_ADDRSTRLEN) == nullptr) |                 if (ix::inet_ntop(AF_INET, &client.sin_addr, remoteIp4, INET_ADDRSTRLEN) == nullptr) | ||||||
|                 { |                 { | ||||||
|                     int err = Socket::getErrno(); |                     int err = Socket::getErrno(); | ||||||
|                     std::stringstream ss; |                     std::stringstream ss; | ||||||
| @@ -357,7 +357,8 @@ namespace ix | |||||||
|             else // AF_INET6 |             else // AF_INET6 | ||||||
|             { |             { | ||||||
|                 char remoteIp6[INET6_ADDRSTRLEN]; |                 char remoteIp6[INET6_ADDRSTRLEN]; | ||||||
|                 if (inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) == nullptr) |                 if (ix::inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) == | ||||||
|  |                     nullptr) | ||||||
|                 { |                 { | ||||||
|                     int err = Socket::getErrno(); |                     int err = Socket::getErrno(); | ||||||
|                     std::stringstream ss; |                     std::stringstream ss; | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ namespace ix | |||||||
|     bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char& c1, |     bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char& c1, | ||||||
|                                                         const unsigned char& c2) const |                                                         const unsigned char& c2) const | ||||||
|     { |     { | ||||||
| #ifdef _WIN32 | #if defined(_WIN32) && !defined(__GNUC__) | ||||||
|         return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale()); |         return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale()); | ||||||
| #else | #else | ||||||
|         return std::tolower(c1) < std::tolower(c2); |         return std::tolower(c1) < std::tolower(c2); | ||||||
|   | |||||||
| @@ -15,6 +15,12 @@ | |||||||
| #include <cmath> | #include <cmath> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | namespace | ||||||
|  | { | ||||||
|  |     const std::string emptyMsg; | ||||||
|  | } // namespace | ||||||
|  |  | ||||||
|  |  | ||||||
| namespace ix | namespace ix | ||||||
| { | { | ||||||
|     OnTrafficTrackerCallback WebSocket::_onTrafficTrackerCallback = nullptr; |     OnTrafficTrackerCallback WebSocket::_onTrafficTrackerCallback = nullptr; | ||||||
| @@ -22,12 +28,14 @@ namespace ix | |||||||
|     const int WebSocket::kDefaultPingIntervalSecs(-1); |     const int WebSocket::kDefaultPingIntervalSecs(-1); | ||||||
|     const bool WebSocket::kDefaultEnablePong(true); |     const bool WebSocket::kDefaultEnablePong(true); | ||||||
|     const uint32_t WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s |     const uint32_t WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s | ||||||
|  |     const uint32_t WebSocket::kDefaultMinWaitBetweenReconnectionRetries(1);         // 1 ms | ||||||
|  |  | ||||||
|     WebSocket::WebSocket() |     WebSocket::WebSocket() | ||||||
|         : _onMessageCallback(OnMessageCallback()) |         : _onMessageCallback(OnMessageCallback()) | ||||||
|         , _stop(false) |         , _stop(false) | ||||||
|         , _automaticReconnection(true) |         , _automaticReconnection(true) | ||||||
|         , _maxWaitBetweenReconnectionRetries(kDefaultMaxWaitBetweenReconnectionRetries) |         , _maxWaitBetweenReconnectionRetries(kDefaultMaxWaitBetweenReconnectionRetries) | ||||||
|  |         , _minWaitBetweenReconnectionRetries(kDefaultMinWaitBetweenReconnectionRetries) | ||||||
|         , _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs) |         , _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs) | ||||||
|         , _enablePong(kDefaultEnablePong) |         , _enablePong(kDefaultEnablePong) | ||||||
|         , _pingIntervalSecs(kDefaultPingIntervalSecs) |         , _pingIntervalSecs(kDefaultPingIntervalSecs) | ||||||
| @@ -36,7 +44,7 @@ namespace ix | |||||||
|             [this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) { |             [this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) { | ||||||
|                 _onMessageCallback( |                 _onMessageCallback( | ||||||
|                     ix::make_unique<WebSocketMessage>(WebSocketMessageType::Close, |                     ix::make_unique<WebSocketMessage>(WebSocketMessageType::Close, | ||||||
|                                                       "", |                                                       emptyMsg, | ||||||
|                                                       wireSize, |                                                       wireSize, | ||||||
|                                                       WebSocketErrorInfo(), |                                                       WebSocketErrorInfo(), | ||||||
|                                                       WebSocketOpenInfo(), |                                                       WebSocketOpenInfo(), | ||||||
| @@ -56,13 +64,18 @@ namespace ix | |||||||
|         _url = url; |         _url = url; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void WebSocket::setHandshakeTimeout(int handshakeTimeoutSecs) | ||||||
|  |     { | ||||||
|  |         _handshakeTimeoutSecs = handshakeTimeoutSecs; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     void WebSocket::setExtraHeaders(const WebSocketHttpHeaders& headers) |     void WebSocket::setExtraHeaders(const WebSocketHttpHeaders& headers) | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_configMutex); |         std::lock_guard<std::mutex> lock(_configMutex); | ||||||
|         _extraHeaders = headers; |         _extraHeaders = headers; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const std::string& WebSocket::getUrl() const |     const std::string WebSocket::getUrl() const | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_configMutex); |         std::lock_guard<std::mutex> lock(_configMutex); | ||||||
|         return _url; |         return _url; | ||||||
| @@ -81,7 +94,7 @@ namespace ix | |||||||
|         _socketTLSOptions = socketTLSOptions; |         _socketTLSOptions = socketTLSOptions; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const WebSocketPerMessageDeflateOptions& WebSocket::getPerMessageDeflateOptions() const |     const WebSocketPerMessageDeflateOptions WebSocket::getPerMessageDeflateOptions() const | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_configMutex); |         std::lock_guard<std::mutex> lock(_configMutex); | ||||||
|         return _perMessageDeflateOptions; |         return _perMessageDeflateOptions; | ||||||
| @@ -131,12 +144,24 @@ namespace ix | |||||||
|         _maxWaitBetweenReconnectionRetries = maxWaitBetweenReconnectionRetries; |         _maxWaitBetweenReconnectionRetries = maxWaitBetweenReconnectionRetries; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void WebSocket::setMinWaitBetweenReconnectionRetries(uint32_t minWaitBetweenReconnectionRetries) | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lock(_configMutex); | ||||||
|  |         _minWaitBetweenReconnectionRetries = minWaitBetweenReconnectionRetries; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     uint32_t WebSocket::getMaxWaitBetweenReconnectionRetries() const |     uint32_t WebSocket::getMaxWaitBetweenReconnectionRetries() const | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_configMutex); |         std::lock_guard<std::mutex> lock(_configMutex); | ||||||
|         return _maxWaitBetweenReconnectionRetries; |         return _maxWaitBetweenReconnectionRetries; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     uint32_t WebSocket::getMinWaitBetweenReconnectionRetries() const | ||||||
|  |     { | ||||||
|  |         std::lock_guard<std::mutex> lock(_configMutex); | ||||||
|  |         return _minWaitBetweenReconnectionRetries; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     void WebSocket::start() |     void WebSocket::start() | ||||||
|     { |     { | ||||||
|         if (_thread.joinable()) return; // we've already been started |         if (_thread.joinable()) return; // we've already been started | ||||||
| @@ -198,7 +223,7 @@ namespace ix | |||||||
|  |  | ||||||
|         _onMessageCallback(ix::make_unique<WebSocketMessage>( |         _onMessageCallback(ix::make_unique<WebSocketMessage>( | ||||||
|             WebSocketMessageType::Open, |             WebSocketMessageType::Open, | ||||||
|             "", |             emptyMsg, | ||||||
|             0, |             0, | ||||||
|             WebSocketErrorInfo(), |             WebSocketErrorInfo(), | ||||||
|             WebSocketOpenInfo(status.uri, status.headers, status.protocol), |             WebSocketOpenInfo(status.uri, status.headers, status.protocol), | ||||||
| @@ -213,7 +238,9 @@ namespace ix | |||||||
|         return status; |         return status; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs) |     WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, | ||||||
|  |                                                    int timeoutSecs, | ||||||
|  |                                                    bool enablePerMessageDeflate) | ||||||
|     { |     { | ||||||
|         { |         { | ||||||
|             std::lock_guard<std::mutex> lock(_configMutex); |             std::lock_guard<std::mutex> lock(_configMutex); | ||||||
| @@ -221,7 +248,8 @@ namespace ix | |||||||
|                 _perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs); |                 _perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         WebSocketInitResult status = _ws.connectToSocket(std::move(socket), timeoutSecs); |         WebSocketInitResult status = | ||||||
|  |             _ws.connectToSocket(std::move(socket), timeoutSecs, enablePerMessageDeflate); | ||||||
|         if (!status.success) |         if (!status.success) | ||||||
|         { |         { | ||||||
|             return status; |             return status; | ||||||
| @@ -229,7 +257,7 @@ namespace ix | |||||||
|  |  | ||||||
|         _onMessageCallback( |         _onMessageCallback( | ||||||
|             ix::make_unique<WebSocketMessage>(WebSocketMessageType::Open, |             ix::make_unique<WebSocketMessage>(WebSocketMessageType::Open, | ||||||
|                                               "", |                                               emptyMsg, | ||||||
|                                               0, |                                               0, | ||||||
|                                               WebSocketErrorInfo(), |                                               WebSocketErrorInfo(), | ||||||
|                                               WebSocketOpenInfo(status.uri, status.headers), |                                               WebSocketOpenInfo(status.uri, status.headers), | ||||||
| @@ -303,8 +331,10 @@ namespace ix | |||||||
|  |  | ||||||
|                 if (_automaticReconnection) |                 if (_automaticReconnection) | ||||||
|                 { |                 { | ||||||
|                     duration = millis(calculateRetryWaitMilliseconds( |                     duration = | ||||||
|                         retries++, _maxWaitBetweenReconnectionRetries)); |                         millis(calculateRetryWaitMilliseconds(retries++, | ||||||
|  |                                                               _maxWaitBetweenReconnectionRetries, | ||||||
|  |                                                               _minWaitBetweenReconnectionRetries)); | ||||||
|  |  | ||||||
|                     connectErr.wait_time = duration.count(); |                     connectErr.wait_time = duration.count(); | ||||||
|                     connectErr.retries = retries; |                     connectErr.retries = retries; | ||||||
| @@ -314,7 +344,7 @@ namespace ix | |||||||
|                 connectErr.http_status = status.http_status; |                 connectErr.http_status = status.http_status; | ||||||
|  |  | ||||||
|                 _onMessageCallback(ix::make_unique<WebSocketMessage>(WebSocketMessageType::Error, |                 _onMessageCallback(ix::make_unique<WebSocketMessage>(WebSocketMessageType::Error, | ||||||
|                                                                      "", |                                                                      emptyMsg, | ||||||
|                                                                      0, |                                                                      0, | ||||||
|                                                                      connectErr, |                                                                      connectErr, | ||||||
|                                                                      WebSocketOpenInfo(), |                                                                      WebSocketOpenInfo(), | ||||||
|   | |||||||
| @@ -58,6 +58,7 @@ namespace ix | |||||||
|         void enablePerMessageDeflate(); |         void enablePerMessageDeflate(); | ||||||
|         void disablePerMessageDeflate(); |         void disablePerMessageDeflate(); | ||||||
|         void addSubProtocol(const std::string& subProtocol); |         void addSubProtocol(const std::string& subProtocol); | ||||||
|  |         void setHandshakeTimeout(int handshakeTimeoutSecs); | ||||||
|  |  | ||||||
|         // Run asynchronously, by calling start and stop. |         // Run asynchronously, by calling start and stop. | ||||||
|         void start(); |         void start(); | ||||||
| @@ -91,8 +92,8 @@ namespace ix | |||||||
|         ReadyState getReadyState() const; |         ReadyState getReadyState() const; | ||||||
|         static std::string readyStateToString(ReadyState readyState); |         static std::string readyStateToString(ReadyState readyState); | ||||||
|  |  | ||||||
|         const std::string& getUrl() const; |         const std::string getUrl() const; | ||||||
|         const WebSocketPerMessageDeflateOptions& getPerMessageDeflateOptions() const; |         const WebSocketPerMessageDeflateOptions getPerMessageDeflateOptions() const; | ||||||
|         int getPingInterval() const; |         int getPingInterval() const; | ||||||
|         size_t bufferedAmount() const; |         size_t bufferedAmount() const; | ||||||
|  |  | ||||||
| @@ -100,7 +101,9 @@ namespace ix | |||||||
|         void disableAutomaticReconnection(); |         void disableAutomaticReconnection(); | ||||||
|         bool isAutomaticReconnectionEnabled() const; |         bool isAutomaticReconnectionEnabled() const; | ||||||
|         void setMaxWaitBetweenReconnectionRetries(uint32_t maxWaitBetweenReconnectionRetries); |         void setMaxWaitBetweenReconnectionRetries(uint32_t maxWaitBetweenReconnectionRetries); | ||||||
|  |         void setMinWaitBetweenReconnectionRetries(uint32_t minWaitBetweenReconnectionRetries); | ||||||
|         uint32_t getMaxWaitBetweenReconnectionRetries() const; |         uint32_t getMaxWaitBetweenReconnectionRetries() const; | ||||||
|  |         uint32_t getMinWaitBetweenReconnectionRetries() const; | ||||||
|         const std::vector<std::string>& getSubProtocols(); |         const std::vector<std::string>& getSubProtocols(); | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
| @@ -114,7 +117,9 @@ namespace ix | |||||||
|         static void invokeTrafficTrackerCallback(size_t size, bool incoming); |         static void invokeTrafficTrackerCallback(size_t size, bool incoming); | ||||||
|  |  | ||||||
|         // Server |         // Server | ||||||
|         WebSocketInitResult connectToSocket(std::unique_ptr<Socket>, int timeoutSecs); |         WebSocketInitResult connectToSocket(std::unique_ptr<Socket>, | ||||||
|  |                                             int timeoutSecs, | ||||||
|  |                                             bool enablePerMessageDeflate); | ||||||
|  |  | ||||||
|         WebSocketTransport _ws; |         WebSocketTransport _ws; | ||||||
|  |  | ||||||
| @@ -137,7 +142,9 @@ namespace ix | |||||||
|         // Automatic reconnection |         // Automatic reconnection | ||||||
|         std::atomic<bool> _automaticReconnection; |         std::atomic<bool> _automaticReconnection; | ||||||
|         static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries; |         static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries; | ||||||
|  |         static const uint32_t kDefaultMinWaitBetweenReconnectionRetries; | ||||||
|         uint32_t _maxWaitBetweenReconnectionRetries; |         uint32_t _maxWaitBetweenReconnectionRetries; | ||||||
|  |         uint32_t _minWaitBetweenReconnectionRetries; | ||||||
|  |  | ||||||
|         // Make the sleeping in the automatic reconnection cancellable |         // Make the sleeping in the automatic reconnection cancellable | ||||||
|         std::mutex _sleepMutex; |         std::mutex _sleepMutex; | ||||||
|   | |||||||
| @@ -204,6 +204,9 @@ namespace ix | |||||||
|         // Check the value of the connection field |         // Check the value of the connection field | ||||||
|         // Some websocket servers (Go/Gorilla?) send lowercase values for the |         // Some websocket servers (Go/Gorilla?) send lowercase values for the | ||||||
|         // connection header, so do a case insensitive comparison |         // connection header, so do a case insensitive comparison | ||||||
|  |         // | ||||||
|  |         // See https://github.com/apache/thrift/commit/7c4bdf9914fcba6c89e0f69ae48b9675578f084a | ||||||
|  |         // | ||||||
|         if (!insensitiveStringCompare(headers["connection"], "Upgrade")) |         if (!insensitiveStringCompare(headers["connection"], "Upgrade")) | ||||||
|         { |         { | ||||||
|             std::stringstream ss; |             std::stringstream ss; | ||||||
| @@ -241,7 +244,8 @@ namespace ix | |||||||
|         return WebSocketInitResult(true, status, "", headers, path); |         return WebSocketInitResult(true, status, "", headers, path); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs) |     WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs, | ||||||
|  |                                                             bool enablePerMessageDeflate) | ||||||
|     { |     { | ||||||
|         _requestInitCancellation = false; |         _requestInitCancellation = false; | ||||||
|  |  | ||||||
| @@ -295,7 +299,8 @@ namespace ix | |||||||
|             return sendErrorResponse(400, "Missing Upgrade header"); |             return sendErrorResponse(400, "Missing Upgrade header"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!insensitiveStringCompare(headers["upgrade"], "WebSocket")) |         if (!insensitiveStringCompare(headers["upgrade"], "WebSocket") && | ||||||
|  |             headers["Upgrade"] != "keep-alive, Upgrade") // special case for firefox | ||||||
|         { |         { | ||||||
|             return sendErrorResponse(400, |             return sendErrorResponse(400, | ||||||
|                                      "Invalid Upgrade header, " |                                      "Invalid Upgrade header, " | ||||||
| @@ -338,7 +343,7 @@ namespace ix | |||||||
|         WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(header); |         WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(header); | ||||||
|  |  | ||||||
|         // If the client has requested that extension, |         // If the client has requested that extension, | ||||||
|         if (webSocketPerMessageDeflateOptions.enabled()) |         if (webSocketPerMessageDeflateOptions.enabled() && enablePerMessageDeflate) | ||||||
|         { |         { | ||||||
|             _enablePerMessageDeflate = true; |             _enablePerMessageDeflate = true; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ namespace ix | |||||||
|                                             int port, |                                             int port, | ||||||
|                                             int timeoutSecs); |                                             int timeoutSecs); | ||||||
|  |  | ||||||
|         WebSocketInitResult serverHandshake(int timeoutSecs); |         WebSocketInitResult serverHandshake(int timeoutSecs, bool enablePerMessageDeflate); | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|         std::string genRandomString(const int len); |         std::string genRandomString(const int len); | ||||||
|   | |||||||
| @@ -42,6 +42,18 @@ namespace ix | |||||||
|         { |         { | ||||||
|             ; |             ; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * @brief Deleted overload to prevent binding `str` to a temporary, which would cause | ||||||
|  |          * undefined behavior since class members don't extend lifetime beyond the constructor call. | ||||||
|  |          */ | ||||||
|  |         WebSocketMessage(WebSocketMessageType t, | ||||||
|  |                          std::string&& s, | ||||||
|  |                          size_t w, | ||||||
|  |                          WebSocketErrorInfo e, | ||||||
|  |                          WebSocketOpenInfo o, | ||||||
|  |                          WebSocketCloseInfo c, | ||||||
|  |                          bool b = false) = delete; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     using WebSocketMessagePtr = std::unique_ptr<WebSocketMessage>; |     using WebSocketMessagePtr = std::unique_ptr<WebSocketMessage>; | ||||||
|   | |||||||
| @@ -14,12 +14,12 @@ namespace ix | |||||||
| { | { | ||||||
|     /// Default values as defined in the RFC |     /// Default values as defined in the RFC | ||||||
|     const uint8_t WebSocketPerMessageDeflateOptions::kDefaultServerMaxWindowBits = 15; |     const uint8_t WebSocketPerMessageDeflateOptions::kDefaultServerMaxWindowBits = 15; | ||||||
|     static const int minServerMaxWindowBits = 8; |     static const uint8_t minServerMaxWindowBits = 8; | ||||||
|     static const int maxServerMaxWindowBits = 15; |     static const uint8_t maxServerMaxWindowBits = 15; | ||||||
|  |  | ||||||
|     const uint8_t WebSocketPerMessageDeflateOptions::kDefaultClientMaxWindowBits = 15; |     const uint8_t WebSocketPerMessageDeflateOptions::kDefaultClientMaxWindowBits = 15; | ||||||
|     static const int minClientMaxWindowBits = 8; |     static const uint8_t minClientMaxWindowBits = 8; | ||||||
|     static const int maxClientMaxWindowBits = 15; |     static const uint8_t maxClientMaxWindowBits = 15; | ||||||
|  |  | ||||||
|     WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions( |     WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions( | ||||||
|         bool enabled, |         bool enabled, | ||||||
| @@ -85,11 +85,7 @@ namespace ix | |||||||
|  |  | ||||||
|             if (startsWith(token, "server_max_window_bits=")) |             if (startsWith(token, "server_max_window_bits=")) | ||||||
|             { |             { | ||||||
|                 std::string val = token.substr(token.find_last_of("=") + 1); |                 uint8_t x = strtol(token.substr(token.find_last_of("=") + 1).c_str(), nullptr, 10); | ||||||
|                 std::stringstream ss; |  | ||||||
|                 ss << val; |  | ||||||
|                 int x; |  | ||||||
|                 ss >> x; |  | ||||||
|  |  | ||||||
|                 // Sanitize values to be in the proper range [8, 15] in |                 // Sanitize values to be in the proper range [8, 15] in | ||||||
|                 // case a server would give us bogus values |                 // case a server would give us bogus values | ||||||
| @@ -99,11 +95,7 @@ namespace ix | |||||||
|  |  | ||||||
|             if (startsWith(token, "client_max_window_bits=")) |             if (startsWith(token, "client_max_window_bits=")) | ||||||
|             { |             { | ||||||
|                 std::string val = token.substr(token.find_last_of("=") + 1); |                 uint8_t x = strtol(token.substr(token.find_last_of("=") + 1).c_str(), nullptr, 10); | ||||||
|                 std::stringstream ss; |  | ||||||
|                 ss << val; |  | ||||||
|                 int x; |  | ||||||
|                 ss >> x; |  | ||||||
|  |  | ||||||
|                 // Sanitize values to be in the proper range [8, 15] in |                 // Sanitize values to be in the proper range [8, 15] in | ||||||
|                 // case a server would give us bogus values |                 // case a server would give us bogus values | ||||||
|   | |||||||
| @@ -39,8 +39,8 @@ namespace ix | |||||||
|         bool _enabled; |         bool _enabled; | ||||||
|         bool _clientNoContextTakeover; |         bool _clientNoContextTakeover; | ||||||
|         bool _serverNoContextTakeover; |         bool _serverNoContextTakeover; | ||||||
|         int _clientMaxWindowBits; |         uint8_t _clientMaxWindowBits; | ||||||
|         int _serverMaxWindowBits; |         uint8_t _serverMaxWindowBits; | ||||||
|  |  | ||||||
|         void sanitizeClientMaxWindowBits(); |         void sanitizeClientMaxWindowBits(); | ||||||
|     }; |     }; | ||||||
|   | |||||||
| @@ -97,9 +97,10 @@ namespace ix | |||||||
|         } |         } | ||||||
|         else if (_onClientMessageCallback) |         else if (_onClientMessageCallback) | ||||||
|         { |         { | ||||||
|  |             WebSocket* webSocketRawPtr = webSocket.get(); | ||||||
|             webSocket->setOnMessageCallback( |             webSocket->setOnMessageCallback( | ||||||
|                 [this, &ws = *webSocket.get(), connectionState](const WebSocketMessagePtr& msg) { |                 [this, webSocketRawPtr, connectionState](const WebSocketMessagePtr& msg) { | ||||||
|                     _onClientMessageCallback(connectionState, ws, msg); |                     _onClientMessageCallback(connectionState, *webSocketRawPtr, msg); | ||||||
|                 }); |                 }); | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
| @@ -128,7 +129,8 @@ namespace ix | |||||||
|             _clients.insert(webSocket); |             _clients.insert(webSocket); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         auto status = webSocket->connectToSocket(std::move(socket), _handshakeTimeoutSecs); |         auto status = webSocket->connectToSocket( | ||||||
|  |             std::move(socket), _handshakeTimeoutSecs, _enablePerMessageDeflate); | ||||||
|         if (status.success) |         if (status.success) | ||||||
|         { |         { | ||||||
|             // Process incoming messages and execute callbacks |             // Process incoming messages and execute callbacks | ||||||
| @@ -168,4 +170,45 @@ namespace ix | |||||||
|         std::lock_guard<std::mutex> lock(_clientsMutex); |         std::lock_guard<std::mutex> lock(_clientsMutex); | ||||||
|         return _clients.size(); |         return _clients.size(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // | ||||||
|  |     // Classic servers | ||||||
|  |     // | ||||||
|  |     void WebSocketServer::makeBroadcastServer() | ||||||
|  |     { | ||||||
|  |         setOnClientMessageCallback([this](std::shared_ptr<ConnectionState> connectionState, | ||||||
|  |                                           WebSocket& webSocket, | ||||||
|  |                                           const WebSocketMessagePtr& msg) { | ||||||
|  |             auto remoteIp = connectionState->getRemoteIp(); | ||||||
|  |             if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|  |             { | ||||||
|  |                 for (auto&& client : getClients()) | ||||||
|  |                 { | ||||||
|  |                     if (client.get() != &webSocket) | ||||||
|  |                     { | ||||||
|  |                         client->send(msg->str, msg->binary); | ||||||
|  |  | ||||||
|  |                         // Make sure the OS send buffer is flushed before moving on | ||||||
|  |                         do | ||||||
|  |                         { | ||||||
|  |                             std::chrono::duration<double, std::milli> duration(500); | ||||||
|  |                             std::this_thread::sleep_for(duration); | ||||||
|  |                         } while (client->bufferedAmount() != 0); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool WebSocketServer::listenAndStart() | ||||||
|  |     { | ||||||
|  |         auto res = listen(); | ||||||
|  |         if (!res.first) | ||||||
|  |         { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         start(); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
| } // namespace ix | } // namespace ix | ||||||
|   | |||||||
| @@ -47,6 +47,9 @@ namespace ix | |||||||
|         // Get all the connected clients |         // Get all the connected clients | ||||||
|         std::set<std::shared_ptr<WebSocket>> getClients(); |         std::set<std::shared_ptr<WebSocket>> getClients(); | ||||||
|  |  | ||||||
|  |         void makeBroadcastServer(); | ||||||
|  |         bool listenAndStart(); | ||||||
|  |  | ||||||
|         const static int kDefaultHandShakeTimeoutSecs; |         const static int kDefaultHandShakeTimeoutSecs; | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
|   | |||||||
| @@ -169,7 +169,8 @@ namespace ix | |||||||
|  |  | ||||||
|     // Server |     // Server | ||||||
|     WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket, |     WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket, | ||||||
|                                                             int timeoutSecs) |                                                             int timeoutSecs, | ||||||
|  |                                                             bool enablePerMessageDeflate) | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_socketMutex); |         std::lock_guard<std::mutex> lock(_socketMutex); | ||||||
|  |  | ||||||
| @@ -186,7 +187,7 @@ namespace ix | |||||||
|                                               _perMessageDeflateOptions, |                                               _perMessageDeflateOptions, | ||||||
|                                               _enablePerMessageDeflate); |                                               _enablePerMessageDeflate); | ||||||
|  |  | ||||||
|         auto result = webSocketHandshake.serverHandshake(timeoutSecs); |         auto result = webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate); | ||||||
|         if (result.success) |         if (result.success) | ||||||
|         { |         { | ||||||
|             setReadyState(ReadyState::OPEN); |             setReadyState(ReadyState::OPEN); | ||||||
|   | |||||||
| @@ -83,7 +83,9 @@ namespace ix | |||||||
|                                          int timeoutSecs); |                                          int timeoutSecs); | ||||||
|  |  | ||||||
|         // Server |         // Server | ||||||
|         WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs); |         WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, | ||||||
|  |                                             int timeoutSecs, | ||||||
|  |                                             bool enablePerMessageDeflate); | ||||||
|  |  | ||||||
|         PollResult poll(); |         PollResult poll(); | ||||||
|         WebSocketSendInfo sendBinary(const std::string& message, |         WebSocketSendInfo sendBinary(const std::string& message, | ||||||
|   | |||||||
| @@ -6,4 +6,4 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #define IX_WEBSOCKET_VERSION "11.0.8" | #define IX_WEBSOCKET_VERSION "11.3.1" | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								main.cpp
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								main.cpp
									
									
									
									
									
								
							| @@ -9,10 +9,13 @@ | |||||||
|  *  $ mkdir -p build ; cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install |  *  $ mkdir -p build ; cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install | ||||||
|  *  $ clang++ --std=c++14 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation |  *  $ clang++ --std=c++14 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation | ||||||
|  *  $ ./a.out |  *  $ ./a.out | ||||||
|  |  * | ||||||
|  |  *  Or use cmake -DBUILD_DEMO=ON option for other platform | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <ixwebsocket/IXNetSystem.h> | #include <ixwebsocket/IXNetSystem.h> | ||||||
| #include <ixwebsocket/IXWebSocket.h> | #include <ixwebsocket/IXWebSocket.h> | ||||||
|  | #include <ixwebsocket/IXUserAgent.h> | ||||||
| #include <iostream> | #include <iostream> | ||||||
|  |  | ||||||
| int main() | int main() | ||||||
| @@ -23,9 +26,12 @@ int main() | |||||||
|     // Our websocket object |     // Our websocket object | ||||||
|     ix::WebSocket webSocket; |     ix::WebSocket webSocket; | ||||||
|  |  | ||||||
|  |     // Connect to a server with encryption | ||||||
|  |     // See https://machinezone.github.io/IXWebSocket/usage/#tls-support-and-configuration | ||||||
|     std::string url("wss://echo.websocket.org"); |     std::string url("wss://echo.websocket.org"); | ||||||
|     webSocket.setUrl(url); |     webSocket.setUrl(url); | ||||||
|  |  | ||||||
|  |     std::cout << ix::userAgent() << std::endl; | ||||||
|     std::cout << "Connecting to " << url << "..." << std::endl; |     std::cout << "Connecting to " << url << "..." << std::endl; | ||||||
|  |  | ||||||
|     // Setup a callback to be fired (in a background thread, watch out for race conditions !) |     // Setup a callback to be fired (in a background thread, watch out for race conditions !) | ||||||
| @@ -35,10 +41,18 @@ int main() | |||||||
|             if (msg->type == ix::WebSocketMessageType::Message) |             if (msg->type == ix::WebSocketMessageType::Message) | ||||||
|             { |             { | ||||||
|                 std::cout << "received message: " << msg->str << std::endl; |                 std::cout << "received message: " << msg->str << std::endl; | ||||||
|  |                 std::cout << "> " << std::flush; | ||||||
|             } |             } | ||||||
|             else if (msg->type == ix::WebSocketMessageType::Open) |             else if (msg->type == ix::WebSocketMessageType::Open) | ||||||
|             { |             { | ||||||
|                 std::cout << "Connection established" << std::endl; |                 std::cout << "Connection established" << std::endl; | ||||||
|  |                 std::cout << "> " << std::flush; | ||||||
|  |             } | ||||||
|  |             else if (msg->type == ix::WebSocketMessageType::Error) | ||||||
|  |             { | ||||||
|  |                 // Maybe SSL is not configured properly | ||||||
|  |                 std::cout << "Connection error: " << msg->errorInfo.reason << std::endl; | ||||||
|  |                 std::cout << "> " << std::flush; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     ); |     ); | ||||||
| @@ -49,13 +63,16 @@ int main() | |||||||
|     // Send a message to the server (default to TEXT mode) |     // Send a message to the server (default to TEXT mode) | ||||||
|     webSocket.send("hello world"); |     webSocket.send("hello world"); | ||||||
|  |  | ||||||
|     while (true) |     // Display a prompt | ||||||
|     { |  | ||||||
|         std::string text; |  | ||||||
|     std::cout << "> " << std::flush; |     std::cout << "> " << std::flush; | ||||||
|         std::getline(std::cin, text); |  | ||||||
|  |  | ||||||
|  |     std::string text; | ||||||
|  |     // Read text from the console and send messages in text mode. | ||||||
|  |     // Exit with Ctrl-D on Unix or Ctrl-Z on Windows. | ||||||
|  |     while (std::getline(std::cin, text)) | ||||||
|  |     { | ||||||
|         webSocket.send(text); |         webSocket.send(text); | ||||||
|  |         std::cout << "> " << std::flush; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								makefile.dev
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								makefile.dev
									
									
									
									
									
								
							| @@ -22,37 +22,37 @@ install: brew | |||||||
| # Default rule does not use python as that requires first time users to have Python3 installed | # Default rule does not use python as that requires first time users to have Python3 installed | ||||||
| # | # | ||||||
| brew: | brew: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install) | ||||||
|  |  | ||||||
| # Docker default target. We've had problems with OpenSSL and TLS 1.3 (on the | # Docker default target. We've had problems with OpenSSL and TLS 1.3 (on the | ||||||
| # server side ?) and I can't work-around it easily, so we're using mbedtls on | # server side ?) and I can't work-around it easily, so we're using mbedtls on | ||||||
| # Linux for the SSL backend, which works great. | # Linux for the SSL backend, which works great. | ||||||
| ws_mbedtls_install: | ws_mbedtls_install: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_ZLIB=OFF -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; ninja install) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_ZLIB=OFF -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; ninja install) | ||||||
|  |  | ||||||
| ws: | ws: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install) | ||||||
|  |  | ||||||
| ws_unity: | ws_unity: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install) | ||||||
|  |  | ||||||
| ws_install: | ws_install: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install) | ||||||
|  |  | ||||||
| ws_install_release: | ws_install_release: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install) | ||||||
|  |  | ||||||
| ws_openssl_install: | ws_openssl_install: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; ninja install) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; ninja install) | ||||||
|  |  | ||||||
| ws_mbedtls: | ws_mbedtls: | ||||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4) | 	mkdir -p build && (cd build ; cmake -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4) | ||||||
|  |  | ||||||
| ws_no_ssl: | ws_no_ssl: | ||||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4) | 	mkdir -p build && (cd build ; cmake -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4) | ||||||
|  |  | ||||||
| ws_no_python: | ws_no_python: | ||||||
| 	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j4 install) | 	mkdir -p build && (cd build ; cmake -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j4 install) | ||||||
|  |  | ||||||
| uninstall: | uninstall: | ||||||
| 	xargs rm -fv < build/install_manifest.txt | 	xargs rm -fv < build/install_manifest.txt | ||||||
| @@ -111,27 +111,27 @@ test_server: | |||||||
| 	(cd test && npm i ws && node broadcast-server.js) | 	(cd test && npm i ws && node broadcast-server.js) | ||||||
|  |  | ||||||
| test: | test: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 ..) | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 ..) | ||||||
| 	(cd build ; ninja) | 	(cd build ; ninja) | ||||||
| 	(cd build ; ninja test) | 	(cd build ; ninja -v test) | ||||||
|  |  | ||||||
| test_asan: | test_asan: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer") | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer") | ||||||
| 	(cd build ; ninja) | 	(cd build ; ninja) | ||||||
| 	(cd build ; ctest -V .) | 	(cd build ; ctest -V .) | ||||||
|  |  | ||||||
| test_tsan_mbedtls: | test_tsan_mbedtls: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_MBED_TLS=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=thread -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=thread -fno-omit-frame-pointer") | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_MBED_TLS=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=thread -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=thread -fno-omit-frame-pointer") | ||||||
| 	(cd build ; ninja) | 	(cd build ; ninja) | ||||||
| 	(cd build ; ninja test) | 	(cd build ; ninja test) | ||||||
|  |  | ||||||
| test_tsan_openssl: | test_tsan_openssl: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja --DCMAKE_UNITY_BUILD=ON DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPENS_SSL=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=thread -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=thread -fno-omit-frame-pointer") | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_UNITY_BUILD=ON DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPENS_SSL=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=thread -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=thread -fno-omit-frame-pointer") | ||||||
| 	(cd build ; ninja) | 	(cd build ; ninja) | ||||||
| 	(cd build ; ninja test) | 	(cd build ; ninja test) | ||||||
|  |  | ||||||
| test_tsan_sectransport: | test_tsan_sectransport: | ||||||
| 	mkdir -p build && (cd build ; cmake -GNinja --DCMAKE_UNITY_BUILD=ON DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPENS_SSL=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=thread -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=thread -fno-omit-frame-pointer") | 	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPENS_SSL=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=thread -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=thread -fno-omit-frame-pointer") | ||||||
| 	(cd build ; ninja) | 	(cd build ; ninja) | ||||||
| 	(cd build ; ninja test) | 	(cd build ; ninja test) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,14 +17,11 @@ set (TEST_TARGET_NAMES | |||||||
|   IXWebSocketTestConnectionDisconnection |   IXWebSocketTestConnectionDisconnection | ||||||
|   IXUrlParserTest |   IXUrlParserTest | ||||||
|   IXHttpClientTest |   IXHttpClientTest | ||||||
|   IXHttpServerTest |  | ||||||
|   IXUnityBuildsTest |   IXUnityBuildsTest | ||||||
|   IXHttpTest |   IXHttpTest | ||||||
|   IXDNSLookupTest |   IXDNSLookupTest | ||||||
|   IXWebSocketSubProtocolTest |   IXWebSocketSubProtocolTest | ||||||
|   IXWebSocketChatTest |  | ||||||
|   # IXWebSocketBroadcastTest ## FIXME was depending on cobra / take a broadcast server from ws |   # IXWebSocketBroadcastTest ## FIXME was depending on cobra / take a broadcast server from ws | ||||||
|   IXWebSocketPerMessageDeflateCompressorTest |  | ||||||
|   IXStrCaseCompareTest |   IXStrCaseCompareTest | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -33,6 +30,17 @@ set (TEST_TARGET_NAMES | |||||||
| if (UNIX) | if (UNIX) | ||||||
|   list(APPEND TEST_TARGET_NAMES |   list(APPEND TEST_TARGET_NAMES | ||||||
|     IXWebSocketCloseTest |     IXWebSocketCloseTest | ||||||
|  |  | ||||||
|  |     # Fail on Windows in CI probably because the pathing is wrong and | ||||||
|  |     # some resource files cannot be found | ||||||
|  |     IXHttpServerTest | ||||||
|  |     IXWebSocketChatTest | ||||||
|  |   ) | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | if (USE_ZLIB) | ||||||
|  |   list(APPEND TEST_TARGET_NAMES | ||||||
|  |     IXWebSocketPerMessageDeflateCompressorTest | ||||||
|   ) |   ) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -33,11 +33,7 @@ TEST_CASE("dns", "[net]") | |||||||
|         auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80); |         auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80); | ||||||
|  |  | ||||||
|         std::string errMsg; |         std::string errMsg; | ||||||
|         struct addrinfo* res = dnsLookup->resolve(errMsg, |         struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return false; }); | ||||||
|                                                   [] |  | ||||||
|                                                   { |  | ||||||
|                                                       return false; |  | ||||||
|                                                   }); |  | ||||||
|         std::cerr << "Error message: " << errMsg << std::endl; |         std::cerr << "Error message: " << errMsg << std::endl; | ||||||
|         REQUIRE(res == nullptr); |         REQUIRE(res == nullptr); | ||||||
|     } |     } | ||||||
| @@ -48,11 +44,7 @@ TEST_CASE("dns", "[net]") | |||||||
|  |  | ||||||
|         std::string errMsg; |         std::string errMsg; | ||||||
|         // The callback returning true means we are requesting cancellation |         // The callback returning true means we are requesting cancellation | ||||||
|         struct addrinfo* res = dnsLookup->resolve(errMsg, |         struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return true; }); | ||||||
|                                                   [] |  | ||||||
|                                                   { |  | ||||||
|                                                       return true; |  | ||||||
|                                                   }); |  | ||||||
|         std::cerr << "Error message: " << errMsg << std::endl; |         std::cerr << "Error message: " << errMsg << std::endl; | ||||||
|         REQUIRE(res == nullptr); |         REQUIRE(res == nullptr); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -29,6 +29,7 @@ namespace ix | |||||||
|  |  | ||||||
|             // Comparison should be case insensitive |             // Comparison should be case insensitive | ||||||
|             REQUIRE(httpHeaders["Foo"] == "foo"); |             REQUIRE(httpHeaders["Foo"] == "foo"); | ||||||
|  |             REQUIRE(httpHeaders["Foo"] != "bar"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         SECTION("2") |         SECTION("2") | ||||||
| @@ -39,7 +40,7 @@ namespace ix | |||||||
|  |  | ||||||
|             headers["Upgrade"] = "webSocket"; |             headers["Upgrade"] = "webSocket"; | ||||||
|  |  | ||||||
|             REQUIRE(CaseInsensitiveLess::cmp(headers["upgrade"], "WebSocket") == 0); |             REQUIRE(!CaseInsensitiveLess::cmp(headers["upGRADE"], "webSocket")); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -139,8 +139,9 @@ namespace ix | |||||||
|         std::streamoff size = file.tellg(); |         std::streamoff size = file.tellg(); | ||||||
|         file.seekg(0, file.beg); |         file.seekg(0, file.beg); | ||||||
|  |  | ||||||
|         memblock.resize((size_t) size); |         memblock.reserve((size_t) size); | ||||||
|         file.read((char*) &memblock.front(), static_cast<std::streamsize>(size)); |         memblock.insert( | ||||||
|  |             memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>()); | ||||||
|  |  | ||||||
|         return memblock; |         return memblock; | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										5134
									
								
								third_party/cli11/CLI11.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5134
									
								
								third_party/cli11/CLI11.hpp
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										5
									
								
								third_party/cpp-linenoise/linenoise.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								third_party/cpp-linenoise/linenoise.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -1639,7 +1639,10 @@ bool enableRawMode(int fd) { | |||||||
|  |  | ||||||
|         /* Init windows console handles only once */ |         /* Init windows console handles only once */ | ||||||
|         hOut = GetStdHandle(STD_OUTPUT_HANDLE); |         hOut = GetStdHandle(STD_OUTPUT_HANDLE); | ||||||
|         if (hOut==INVALID_HANDLE_VALUE) goto fatal; |         if (hOut==INVALID_HANDLE_VALUE) { | ||||||
|  |             errno = ENOTTY; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     DWORD consolemodeOut; |     DWORD consolemodeOut; | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								ws/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								ws/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -8,7 +8,7 @@ | |||||||
|       "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" |       "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" | ||||||
|     }, |     }, | ||||||
|     "ws": { |     "ws": { | ||||||
|       "version": "6.2.0", |       "version": ">=6.2.2", | ||||||
|       "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.0.tgz", |       "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.0.tgz", | ||||||
|       "integrity": "sha512-deZYUNlt2O4buFCa3t5bKLf8A7FPP/TVjwOeVNpw818Ma5nk4MLXls2eoEGS39o8119QIYxTrTDoPQ5B/gTD6w==", |       "integrity": "sha512-deZYUNlt2O4buFCa3t5bKLf8A7FPP/TVjwOeVNpw818Ma5nk4MLXls2eoEGS39o8119QIYxTrTDoPQ5B/gTD6w==", | ||||||
|       "requires": { |       "requires": { | ||||||
|   | |||||||
							
								
								
									
										201
									
								
								ws/ws.cpp
									
									
									
									
									
								
							
							
						
						
									
										201
									
								
								ws/ws.cpp
									
									
									
									
									
								
							| @@ -55,16 +55,17 @@ namespace | |||||||
|     std::pair<bool, std::vector<uint8_t>> load(const std::string& path) |     std::pair<bool, std::vector<uint8_t>> load(const std::string& path) | ||||||
|     { |     { | ||||||
|         std::vector<uint8_t> memblock; |         std::vector<uint8_t> memblock; | ||||||
|  |  | ||||||
|         std::ifstream file(path); |         std::ifstream file(path); | ||||||
|  |  | ||||||
|         if (!file.is_open()) return std::make_pair(false, memblock); |         if (!file.is_open()) return std::make_pair(false, memblock); | ||||||
|  |  | ||||||
|         file.seekg(0, file.end); |         file.seekg(0, file.end); | ||||||
|         std::streamoff size = file.tellg(); |         std::streamoff size = file.tellg(); | ||||||
|         file.seekg(0, file.beg); |         file.seekg(0, file.beg); | ||||||
|  |  | ||||||
|         memblock.resize((size_t) size); |         memblock.reserve((size_t) size); | ||||||
|         file.read((char*) &memblock.front(), static_cast<std::streamsize>(size)); |         memblock.insert( | ||||||
|  |             memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>()); | ||||||
|  |  | ||||||
|         return std::make_pair(true, memblock); |         return std::make_pair(true, memblock); | ||||||
|     } |     } | ||||||
| @@ -86,9 +87,9 @@ namespace | |||||||
|         std::streamoff size = file.tellg(); |         std::streamoff size = file.tellg(); | ||||||
|         file.seekg(0, file.beg); |         file.seekg(0, file.beg); | ||||||
|  |  | ||||||
|         memblock.resize(size); |         memblock.reserve((size_t) size); | ||||||
|  |         memblock.insert( | ||||||
|         file.read((char*) &memblock.front(), static_cast<std::streamsize>(size)); |             memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>()); | ||||||
|  |  | ||||||
|         std::string bytes(memblock.begin(), memblock.end()); |         std::string bytes(memblock.begin(), memblock.end()); | ||||||
|         return bytes; |         return bytes; | ||||||
| @@ -439,93 +440,6 @@ namespace ix | |||||||
|         return generateReport(url) ? 0 : 1; |         return generateReport(url) ? 0 : 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // |  | ||||||
|     //  broadcast server |  | ||||||
|     // |  | ||||||
|     int ws_broadcast_server_main(int port, |  | ||||||
|                                  const std::string& hostname, |  | ||||||
|                                  const ix::SocketTLSOptions& tlsOptions) |  | ||||||
|     { |  | ||||||
|         spdlog::info("Listening on {}:{}", hostname, port); |  | ||||||
|  |  | ||||||
|         ix::WebSocketServer server(port, hostname); |  | ||||||
|         server.setTLSOptions(tlsOptions); |  | ||||||
|  |  | ||||||
|         server.setOnClientMessageCallback( |  | ||||||
|             [&server](std::shared_ptr<ConnectionState> connectionState, |  | ||||||
|                       WebSocket& webSocket, |  | ||||||
|                       const WebSocketMessagePtr& msg) { |  | ||||||
|                 auto remoteIp = connectionState->getRemoteIp(); |  | ||||||
|                 if (msg->type == ix::WebSocketMessageType::Open) |  | ||||||
|                 { |  | ||||||
|                     spdlog::info("New connection"); |  | ||||||
|                     spdlog::info("remote ip: {}", remoteIp); |  | ||||||
|                     spdlog::info("id: {}", connectionState->getId()); |  | ||||||
|                     spdlog::info("Uri: {}", msg->openInfo.uri); |  | ||||||
|                     spdlog::info("Headers:"); |  | ||||||
|                     for (auto it : msg->openInfo.headers) |  | ||||||
|                     { |  | ||||||
|                         spdlog::info("{}: {}", it.first, it.second); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 else if (msg->type == ix::WebSocketMessageType::Close) |  | ||||||
|                 { |  | ||||||
|                     spdlog::info("Closed connection: code {} reason {}", |  | ||||||
|                                  msg->closeInfo.code, |  | ||||||
|                                  msg->closeInfo.reason); |  | ||||||
|                 } |  | ||||||
|                 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; |  | ||||||
|                     spdlog::info(ss.str()); |  | ||||||
|                 } |  | ||||||
|                 else if (msg->type == ix::WebSocketMessageType::Fragment) |  | ||||||
|                 { |  | ||||||
|                     spdlog::info("Received message fragment"); |  | ||||||
|                 } |  | ||||||
|                 else if (msg->type == ix::WebSocketMessageType::Message) |  | ||||||
|                 { |  | ||||||
|                     spdlog::info("Received {} bytes", msg->wireSize); |  | ||||||
|  |  | ||||||
|                     for (auto&& client : server.getClients()) |  | ||||||
|                     { |  | ||||||
|                         if (client.get() != &webSocket) |  | ||||||
|                         { |  | ||||||
|                             client->send(msg->str, msg->binary, [](int current, int total) -> bool { |  | ||||||
|                                 spdlog::info("Step {} out of {}", current, total); |  | ||||||
|                                 return true; |  | ||||||
|                             }); |  | ||||||
|  |  | ||||||
|                             do |  | ||||||
|                             { |  | ||||||
|                                 size_t bufferedAmount = client->bufferedAmount(); |  | ||||||
|                                 spdlog::info("{} bytes left to be sent", bufferedAmount); |  | ||||||
|  |  | ||||||
|                                 std::chrono::duration<double, std::milli> duration(500); |  | ||||||
|                                 std::this_thread::sleep_for(duration); |  | ||||||
|                             } while (client->bufferedAmount() != 0); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|         auto res = server.listen(); |  | ||||||
|         if (!res.first) |  | ||||||
|         { |  | ||||||
|             spdlog::info(res.second); |  | ||||||
|             return 1; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         server.start(); |  | ||||||
|         server.wait(); |  | ||||||
|  |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* |     /* | ||||||
|      *  ws_chat.cpp |      *  ws_chat.cpp | ||||||
|      *  Author: Benjamin Sergeant |      *  Author: Benjamin Sergeant | ||||||
| @@ -718,7 +632,8 @@ namespace ix | |||||||
|                          uint32_t maxWaitBetweenReconnectionRetries, |                          uint32_t maxWaitBetweenReconnectionRetries, | ||||||
|                          const ix::SocketTLSOptions& tlsOptions, |                          const ix::SocketTLSOptions& tlsOptions, | ||||||
|                          const std::string& subprotocol, |                          const std::string& subprotocol, | ||||||
|                          int pingIntervalSecs); |                          int pingIntervalSecs, | ||||||
|  |                          bool decompressGzipMessages); | ||||||
|  |  | ||||||
|         void subscribe(const std::string& channel); |         void subscribe(const std::string& channel); | ||||||
|         void start(); |         void start(); | ||||||
| @@ -743,6 +658,7 @@ namespace ix | |||||||
|         bool _binaryMode; |         bool _binaryMode; | ||||||
|         std::atomic<int> _receivedBytes; |         std::atomic<int> _receivedBytes; | ||||||
|         std::atomic<int> _sentBytes; |         std::atomic<int> _sentBytes; | ||||||
|  |         bool _decompressGzipMessages; | ||||||
|  |  | ||||||
|         void log(const std::string& msg); |         void log(const std::string& msg); | ||||||
|         WebSocketHttpHeaders parseHeaders(const std::string& data); |         WebSocketHttpHeaders parseHeaders(const std::string& data); | ||||||
| @@ -756,12 +672,14 @@ namespace ix | |||||||
|                                        uint32_t maxWaitBetweenReconnectionRetries, |                                        uint32_t maxWaitBetweenReconnectionRetries, | ||||||
|                                        const ix::SocketTLSOptions& tlsOptions, |                                        const ix::SocketTLSOptions& tlsOptions, | ||||||
|                                        const std::string& subprotocol, |                                        const std::string& subprotocol, | ||||||
|                                        int pingIntervalSecs) |                                        int pingIntervalSecs, | ||||||
|  |                                        bool decompressGzipMessages) | ||||||
|         : _url(url) |         : _url(url) | ||||||
|         , _disablePerMessageDeflate(disablePerMessageDeflate) |         , _disablePerMessageDeflate(disablePerMessageDeflate) | ||||||
|         , _binaryMode(binaryMode) |         , _binaryMode(binaryMode) | ||||||
|         , _receivedBytes(0) |         , _receivedBytes(0) | ||||||
|         , _sentBytes(0) |         , _sentBytes(0) | ||||||
|  |         , _decompressGzipMessages(decompressGzipMessages) | ||||||
|     { |     { | ||||||
|         if (disableAutomaticReconnection) |         if (disableAutomaticReconnection) | ||||||
|         { |         { | ||||||
| @@ -870,7 +788,21 @@ namespace ix | |||||||
|             { |             { | ||||||
|                 spdlog::info("Received {} bytes", msg->wireSize); |                 spdlog::info("Received {} bytes", msg->wireSize); | ||||||
|  |  | ||||||
|                 ss << "ws_connect: received message: " << msg->str; |                 std::string payload = msg->str; | ||||||
|  |                 if (_decompressGzipMessages) | ||||||
|  |                 { | ||||||
|  |                     std::string decompressedBytes; | ||||||
|  |                     if (gzipDecompress(payload, decompressedBytes)) | ||||||
|  |                     { | ||||||
|  |                         payload = decompressedBytes; | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         spdlog::error("Error decompressing: {}", payload); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 ss << "ws_connect: received message: " << payload; | ||||||
|                 log(ss.str()); |                 log(ss.str()); | ||||||
|             } |             } | ||||||
|             else if (msg->type == ix::WebSocketMessageType::Error) |             else if (msg->type == ix::WebSocketMessageType::Error) | ||||||
| @@ -923,7 +855,8 @@ namespace ix | |||||||
|                         uint32_t maxWaitBetweenReconnectionRetries, |                         uint32_t maxWaitBetweenReconnectionRetries, | ||||||
|                         const ix::SocketTLSOptions& tlsOptions, |                         const ix::SocketTLSOptions& tlsOptions, | ||||||
|                         const std::string& subprotocol, |                         const std::string& subprotocol, | ||||||
|                         int pingIntervalSecs) |                         int pingIntervalSecs, | ||||||
|  |                         bool decompressGzipMessages) | ||||||
|     { |     { | ||||||
|         std::cout << "Type Ctrl-D to exit prompt..." << std::endl; |         std::cout << "Type Ctrl-D to exit prompt..." << std::endl; | ||||||
|         WebSocketConnect webSocketChat(url, |         WebSocketConnect webSocketChat(url, | ||||||
| @@ -934,7 +867,8 @@ namespace ix | |||||||
|                                        maxWaitBetweenReconnectionRetries, |                                        maxWaitBetweenReconnectionRetries, | ||||||
|                                        tlsOptions, |                                        tlsOptions, | ||||||
|                                        subprotocol, |                                        subprotocol, | ||||||
|                                        pingIntervalSecs); |                                        pingIntervalSecs, | ||||||
|  |                                        decompressGzipMessages); | ||||||
|         webSocketChat.start(); |         webSocketChat.start(); | ||||||
|  |  | ||||||
|         while (true) |         while (true) | ||||||
| @@ -988,8 +922,11 @@ namespace ix | |||||||
|  |  | ||||||
|         auto addr = res->ai_addr; |         auto addr = res->ai_addr; | ||||||
|  |  | ||||||
|  |         // FIXME: this display weird addresses / we could steal libuv inet.c | ||||||
|  |         // code which display correct results | ||||||
|  |  | ||||||
|         char str[INET_ADDRSTRLEN]; |         char str[INET_ADDRSTRLEN]; | ||||||
|         inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN); |         ix::inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN); | ||||||
|  |  | ||||||
|         spdlog::info("host: {} ip: {}", hostname, str); |         spdlog::info("host: {} ip: {}", hostname, str); | ||||||
|  |  | ||||||
| @@ -1508,10 +1445,18 @@ namespace ix | |||||||
|                     filename = output; |                     filename = output; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  |                 if (filename.empty()) | ||||||
|  |                 { | ||||||
|  |                     spdlog::error("Cannot save content to disk: No output file supplied, and not " | ||||||
|  |                                   "filename could be extracted from the url {}", | ||||||
|  |                                   url); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|                     spdlog::info("Writing to disk: {}", filename); |                     spdlog::info("Writing to disk: {}", filename); | ||||||
|                     std::ofstream out(filename); |                     std::ofstream out(filename); | ||||||
|                 out.write((char*) &response->body.front(), response->body.size()); |                     out << response->body; | ||||||
|                 out.close(); |                 } | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
| @@ -1968,7 +1913,8 @@ namespace ix | |||||||
|  |  | ||||||
|         spdlog::info("ws_receive: Writing to disk: {}", filenameTmp); |         spdlog::info("ws_receive: Writing to disk: {}", filenameTmp); | ||||||
|         std::ofstream out(filenameTmp); |         std::ofstream out(filenameTmp); | ||||||
|         out.write((char*) &content.front(), content.size()); |         std::string contentAsString(content.begin(), content.end()); | ||||||
|  |         out << contentAsString; | ||||||
|         out.close(); |         out.close(); | ||||||
|  |  | ||||||
|         spdlog::info("ws_receive: Renaming {} to {}", filenameTmp, filename); |         spdlog::info("ws_receive: Renaming {} to {}", filenameTmp, filename); | ||||||
| @@ -2156,23 +2102,6 @@ namespace ix | |||||||
|         _condition.wait(lock); |         _condition.wait(lock); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::vector<uint8_t> load(const std::string& path) |  | ||||||
|     { |  | ||||||
|         std::vector<uint8_t> memblock; |  | ||||||
|  |  | ||||||
|         std::ifstream file(path); |  | ||||||
|         if (!file.is_open()) return memblock; |  | ||||||
|  |  | ||||||
|         file.seekg(0, file.end); |  | ||||||
|         std::streamoff size = file.tellg(); |  | ||||||
|         file.seekg(0, file.beg); |  | ||||||
|  |  | ||||||
|         memblock.resize((size_t) size); |  | ||||||
|         file.read((char*) &memblock.front(), static_cast<std::streamsize>(size)); |  | ||||||
|  |  | ||||||
|         return memblock; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void WebSocketSender::start() |     void WebSocketSender::start() | ||||||
|     { |     { | ||||||
|         _webSocket.setUrl(_url); |         _webSocket.setUrl(_url); | ||||||
| @@ -2266,7 +2195,8 @@ namespace ix | |||||||
|         std::vector<uint8_t> content; |         std::vector<uint8_t> content; | ||||||
|         { |         { | ||||||
|             Bench bench("ws_send: load file from disk"); |             Bench bench("ws_send: load file from disk"); | ||||||
|             content = load(filename); |             auto res = load(filename); | ||||||
|  |             content = res.second; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         _id = uuid4(); |         _id = uuid4(); | ||||||
| @@ -2462,9 +2392,9 @@ namespace ix | |||||||
|                             else |                             else | ||||||
|                             { |                             { | ||||||
|                                 std::string readyStateString = |                                 std::string readyStateString = | ||||||
|                                     readyState == ReadyState::Connecting |                                     readyState == ReadyState::Connecting ? "Connecting" | ||||||
|                                         ? "Connecting" |                                     : readyState == ReadyState::Closing  ? "Closing" | ||||||
|                                         : readyState == ReadyState::Closing ? "Closing" : "Closed"; |                                                                          : "Closed"; | ||||||
|                                 size_t bufferedAmount = client->bufferedAmount(); |                                 size_t bufferedAmount = client->bufferedAmount(); | ||||||
|  |  | ||||||
|                                 spdlog::info( |                                 spdlog::info( | ||||||
| @@ -2577,9 +2507,10 @@ int main(int argc, char** argv) | |||||||
|     int delayMs = -1; |     int delayMs = -1; | ||||||
|     int count = 1; |     int count = 1; | ||||||
|     int msgCount = 1000 * 1000; |     int msgCount = 1000 * 1000; | ||||||
|     uint32_t maxWaitBetweenReconnectionRetries; |     uint32_t maxWaitBetweenReconnectionRetries = 10 * 1000; // 10 seconds | ||||||
|     int pingIntervalSecs = 30; |     int pingIntervalSecs = 30; | ||||||
|     int runCount = 1; |     int runCount = 1; | ||||||
|  |     bool decompressGzipMessages = false; | ||||||
|  |  | ||||||
|     auto addGenericOptions = [&pidfile](CLI::App* app) { |     auto addGenericOptions = [&pidfile](CLI::App* app) { | ||||||
|         app->add_option("--pidfile", pidfile, "Pid file"); |         app->add_option("--pidfile", pidfile, "Pid file"); | ||||||
| @@ -2642,6 +2573,7 @@ int main(int argc, char** argv) | |||||||
|                            "Max Wait Time between reconnection retries"); |                            "Max Wait Time between reconnection retries"); | ||||||
|     connectApp->add_option("--ping_interval", pingIntervalSecs, "Interval between sending pings"); |     connectApp->add_option("--ping_interval", pingIntervalSecs, "Interval between sending pings"); | ||||||
|     connectApp->add_option("--subprotocol", subprotocol, "Subprotocol"); |     connectApp->add_option("--subprotocol", subprotocol, "Subprotocol"); | ||||||
|  |     connectApp->add_flag("-g", decompressGzipMessages, "Decompress gziped messages"); | ||||||
|     addGenericOptions(connectApp); |     addGenericOptions(connectApp); | ||||||
|     addTLSOptions(connectApp); |     addTLSOptions(connectApp); | ||||||
|  |  | ||||||
| @@ -2830,7 +2762,8 @@ int main(int argc, char** argv) | |||||||
|                                   maxWaitBetweenReconnectionRetries, |                                   maxWaitBetweenReconnectionRetries, | ||||||
|                                   tlsOptions, |                                   tlsOptions, | ||||||
|                                   subprotocol, |                                   subprotocol, | ||||||
|                                   pingIntervalSecs); |                                   pingIntervalSecs, | ||||||
|  |                                   decompressGzipMessages); | ||||||
|     } |     } | ||||||
|     else if (app.got_subcommand("autoroute")) |     else if (app.got_subcommand("autoroute")) | ||||||
|     { |     { | ||||||
| @@ -2853,9 +2786,19 @@ int main(int argc, char** argv) | |||||||
|         ret = ix::ws_push_server( |         ret = ix::ws_push_server( | ||||||
|             port, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong, sendMsg); |             port, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong, sendMsg); | ||||||
|     } |     } | ||||||
|     else if (app.got_subcommand("transfer")) |     else if (app.got_subcommand("transfer") || app.got_subcommand("broadcast_server")) | ||||||
|     { |     { | ||||||
|         ret = ix::ws_transfer_main(port, hostname, tlsOptions); |         ix::WebSocketServer server(port, hostname); | ||||||
|  |         server.setTLSOptions(tlsOptions); | ||||||
|  |         server.makeBroadcastServer(); | ||||||
|  |         if (!server.listenAndStart()) | ||||||
|  |         { | ||||||
|  |             spdlog::error("Error while starting the server"); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             server.wait(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     else if (app.got_subcommand("send")) |     else if (app.got_subcommand("send")) | ||||||
|     { |     { | ||||||
| @@ -2870,10 +2813,6 @@ int main(int argc, char** argv) | |||||||
|     { |     { | ||||||
|         ret = ix::ws_chat_main(url, user); |         ret = ix::ws_chat_main(url, user); | ||||||
|     } |     } | ||||||
|     else if (app.got_subcommand("broadcast_server")) |  | ||||||
|     { |  | ||||||
|         ret = ix::ws_broadcast_server_main(port, hostname, tlsOptions); |  | ||||||
|     } |  | ||||||
|     else if (app.got_subcommand("ping")) |     else if (app.got_subcommand("ping")) | ||||||
|     { |     { | ||||||
|         ret = ix::ws_ping_pong_main(url, tlsOptions); |         ret = ix::ws_ping_pong_main(url, tlsOptions); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user