Compare commits

..

6 Commits

52 changed files with 1932 additions and 4553 deletions

View File

@ -21,7 +21,5 @@ 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

View File

@ -2,8 +2,7 @@ name: linux
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
pull_request:
jobs: jobs:
linux: linux:

View File

@ -2,8 +2,7 @@ name: linux_asan
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
pull_request:
jobs: jobs:
linux: linux:

View File

@ -2,8 +2,7 @@ name: mac_tsan_mbedtls
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
pull_request:
jobs: jobs:
mac_tsan_mbedtls: mac_tsan_mbedtls:

View File

@ -2,8 +2,7 @@ name: mac_tsan_openssl
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
pull_request:
jobs: jobs:
mac_tsan_openssl: mac_tsan_openssl:

View File

@ -2,8 +2,7 @@ name: mac_tsan_sectransport
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
pull_request:
jobs: jobs:
mac_tsan_sectransport: mac_tsan_sectransport:

View File

@ -2,8 +2,7 @@ name: uwp
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
pull_request:
jobs: jobs:
uwp: uwp:

View File

@ -3,7 +3,6 @@ on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - 'docs/**'
pull_request:
jobs: jobs:
windows: windows:
@ -15,7 +14,7 @@ jobs:
- run: | - run: |
mkdir build mkdir build
cd build cd build
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 .. cmake -GNinja -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_C_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 ..
- run: | - run: |
cd build cd build
ninja ninja

View File

@ -1,28 +0,0 @@
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
View File

@ -7,4 +7,3 @@ ws/.certs/
ws/.srl ws/.srl
ixhttpd ixhttpd
makefile makefile
a.out

View File

@ -1,8 +1,5 @@
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)

View File

@ -12,8 +12,6 @@ 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()
@ -88,7 +86,6 @@ 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
@ -114,7 +111,6 @@ 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)
@ -148,7 +144,7 @@ if (USE_TLS)
endif() endif()
endif() endif()
add_library( ixwebsocket add_library( ixwebsocket STATIC
${IXWEBSOCKET_SOURCES} ${IXWEBSOCKET_SOURCES}
${IXWEBSOCKET_HEADERS} ${IXWEBSOCKET_HEADERS}
) )
@ -180,43 +176,31 @@ 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()
# This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL # Use OPENSSL_ROOT_DIR CMake variable if you need to use your own 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 $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>) target_include_directories(ixwebsocket PUBLIC ${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 find_package(MbedTLS REQUIRED)
if (NOT MBEDTLS_FOUND) target_include_directories(ixwebsocket PUBLIC ${MBEDTLS_INCLUDE_DIRS})
find_package(MbedTLS REQUIRED)
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)
# This ZLIB_FOUND check is to help find a cmake manually configured zlib # Use ZLIB_ROOT CMake variable if you need to use your own 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)
@ -254,29 +238,23 @@ 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:${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket> $<INSTALL_INTERFACE:include/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) install(TARGETS ixwebsocket
EXPORT ixwebsocket
ARCHIVE DESTINATION lib
PUBLIC_HEADER DESTINATION include/ixwebsocket/
)
if (IXWEBSOCKET_INSTALL) install(EXPORT ixwebsocket
install(TARGETS ixwebsocket FILE ixwebsocket-config.cmake
EXPORT ixwebsocket NAMESPACE ixwebsocket::
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} DESTINATION lib/cmake/ixwebsocket)
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket/
)
install(EXPORT ixwebsocket
FILE ixwebsocket-config.cmake
NAMESPACE ixwebsocket::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket)
endif()
if (USE_WS OR USE_TEST) if (USE_WS OR USE_TEST)
include(FetchContent) include(FetchContent)
@ -295,8 +273,3 @@ 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()

View File

@ -15,16 +15,13 @@ 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++11 --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 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()
@ -35,8 +32,6 @@ 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);
@ -49,18 +44,10 @@ 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;
} }
} }
); );
@ -71,16 +58,13 @@ 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");
// Display a prompt while (true)
std::cout << "> " << std::flush;
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); std::string text;
std::cout << "> " << std::flush; std::cout << "> " << std::flush;
std::getline(std::cin, text);
webSocket.send(text);
} }
return 0; return 0;
@ -93,8 +77,6 @@ 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.
@ -105,7 +87,6 @@ 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
@ -115,7 +96,6 @@ 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.
@ -132,11 +112,6 @@ 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
@ -146,5 +121,4 @@ 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

View File

@ -2,82 +2,6 @@
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

View File

@ -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). 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_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows)
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support * `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support
* `-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.

View File

@ -256,24 +256,11 @@ 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 The waiting time is capped by default at 10s between 2 attempts, but that value can be changed and queried.
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
@ -347,10 +334,6 @@ 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();
@ -374,10 +357,13 @@ 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<ix::ConnectionState> connectionState, ix::WebSocket & webSocket, const ix::WebSocketMessagePtr & msg) { server.setOnClientMessageCallback(std::shared_ptr<ConnectionState> connectionState,
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::endl; std::cout << "Remote ip: " << connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Open) if (msg->type == ix::WebSocketMessageType::Open)
{ {
@ -395,7 +381,7 @@ server.setOnClientMessageCallback([](std::shared_ptr<ix::ConnectionState> connec
std::cout << "Headers:" << std::endl; std::cout << "Headers:" << std::endl;
for (auto it : msg->openInfo.headers) for (auto it : msg->openInfo.headers)
{ {
std::cout << "\t" << it.first << ": " << it.second << std::endl; std::cout << it.first << ": " << it.second << std::endl;
} }
} }
else if (msg->type == ix::WebSocketMessageType::Message) else if (msg->type == ix::WebSocketMessageType::Message)
@ -404,11 +390,9 @@ server.setOnClientMessageCallback([](std::shared_ptr<ix::ConnectionState> connec
// 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)
@ -417,10 +401,6 @@ 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();

View File

@ -24,12 +24,6 @@
#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

View File

@ -10,22 +10,16 @@
namespace ix namespace ix
{ {
uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount, uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count,
uint32_t maxWaitBetweenReconnectionRetries, uint32_t maxWaitBetweenReconnectionRetries)
uint32_t minWaitBetweenReconnectionRetries)
{ {
uint32_t waitTime = (retryCount < 26) ? (std::pow(2, retryCount) * 100) : 0; uint32_t wait_time = (retry_count < 26) ? (std::pow(2, retry_count) * 100) : 0;
if (waitTime < minWaitBetweenReconnectionRetries) if (wait_time > maxWaitBetweenReconnectionRetries || wait_time == 0)
{ {
waitTime = minWaitBetweenReconnectionRetries; wait_time = maxWaitBetweenReconnectionRetries;
} }
if (waitTime > maxWaitBetweenReconnectionRetries || waitTime == 0) return wait_time;
{
waitTime = maxWaitBetweenReconnectionRetries;
}
return waitTime;
} }
} // namespace ix } // namespace ix

View File

@ -10,7 +10,6 @@
namespace ix namespace ix
{ {
uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount, uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count,
uint32_t maxWaitBetweenReconnectionRetries, uint32_t maxWaitBetweenReconnectionRetries);
uint32_t minWaitBetweenReconnectionRetries);
} // namespace ix } // namespace ix

View File

@ -137,7 +137,7 @@ namespace ix
{ {
contentLength = std::stoi(headers["Content-Length"]); contentLength = std::stoi(headers["Content-Length"]);
} }
catch (const std::exception&) catch (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);

View File

@ -20,11 +20,10 @@
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::kDelete = "DELETE"; const std::string HttpClient::kDel = "DEL";
const std::string HttpClient::kPut = "PUT"; const std::string HttpClient::kPut = "PUT";
const std::string HttpClient::kPatch = "PATCH"; const std::string HttpClient::kPatch = "PATCH";
@ -190,14 +189,14 @@ namespace ix
} }
// Set a default Accept header if none is present // Set a default Accept header if none is present
if (args->extraHeaders.find("Accept") == args->extraHeaders.end()) if (headers.find("Accept") == headers.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 (args->extraHeaders.find("User-Agent") == args->extraHeaders.end()) if (headers.find("User-Agent") == headers.end())
{ {
ss << "User-Agent: " << userAgent() << "\r\n"; ss << "User-Agent: " << userAgent() << "\r\n";
} }
@ -558,9 +557,9 @@ namespace ix
return request(url, kHead, std::string(), args); return request(url, kHead, std::string(), args);
} }
HttpResponsePtr HttpClient::Delete(const std::string& url, HttpRequestArgsPtr args) HttpResponsePtr HttpClient::del(const std::string& url, HttpRequestArgsPtr args)
{ {
return request(url, kDelete, std::string(), args); return request(url, kDel, std::string(), args);
} }
HttpResponsePtr HttpClient::request(const std::string& url, HttpResponsePtr HttpClient::request(const std::string& url,

View File

@ -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 Delete(const std::string& url, HttpRequestArgsPtr args); HttpResponsePtr del(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 kDelete; const static std::string kDel;
const static std::string kPut; const static std::string kPut;
const static std::string kPatch; const static std::string kPatch;

View File

@ -124,171 +124,4 @@ 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
}
// Convert network bytes to host bytes. Copied from the ASIO library
unsigned short network_to_host_short(unsigned short value)
{
#if defined(_WIN32)
unsigned char* value_p = reinterpret_cast<unsigned char*>(&value);
unsigned short result = (static_cast<unsigned short>(value_p[0]) << 8)
| static_cast<unsigned short>(value_p[1]);
return result;
#else // defined(_WIN32)
return ntohs(value);
#endif // defined(_WIN32)
}
} // namespace ix } // namespace ix

View File

@ -7,49 +7,15 @@
#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>
@ -78,9 +44,4 @@ 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);
unsigned short network_to_host_short(unsigned short value);
} // namespace ix } // namespace ix

View File

@ -37,7 +37,6 @@ 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;
@ -52,7 +51,6 @@ namespace ix
__except (EXCEPTION_EXECUTE_HANDLER) __except (EXCEPTION_EXECUTE_HANDLER)
{ {
} }
#endif
} }
#endif #endif

View File

@ -15,6 +15,20 @@
#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"

View File

@ -16,11 +16,6 @@
#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)
@ -132,11 +127,7 @@ 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;

View File

@ -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_sockets.h> #include <mbedtls/net.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>

View File

@ -24,11 +24,6 @@
#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
{ {

View File

@ -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 (ix::inet_pton(_addressFamily, _host.c_str(), &server.sin_addr.s_addr) <= 0) if (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 (ix::inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= 0) if (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 (ix::inet_ntop(AF_INET, &client.sin_addr, remoteIp4, INET_ADDRSTRLEN) == nullptr) if (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;
@ -351,14 +351,13 @@ namespace ix
continue; continue;
} }
remotePort = ix::network_to_host_short(client.sin_port); remotePort = client.sin_port;
remoteIp = remoteIp4; remoteIp = remoteIp4;
} }
else // AF_INET6 else // AF_INET6
{ {
char remoteIp6[INET6_ADDRSTRLEN]; char remoteIp6[INET6_ADDRSTRLEN];
if (ix::inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) == if (inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) == nullptr)
nullptr)
{ {
int err = Socket::getErrno(); int err = Socket::getErrno();
std::stringstream ss; std::stringstream ss;
@ -371,7 +370,7 @@ namespace ix
continue; continue;
} }
remotePort = ix::network_to_host_short(client.sin_port); remotePort = client.sin_port;
remoteIp = remoteIp6; remoteIp = remoteIp6;
} }

View File

@ -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
{ {
#if defined(_WIN32) && !defined(__GNUC__) #ifdef _WIN32
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);

View File

@ -15,12 +15,6 @@
#include <cmath> #include <cmath>
namespace
{
const std::string emptyMsg;
} // namespace
namespace ix namespace ix
{ {
OnTrafficTrackerCallback WebSocket::_onTrafficTrackerCallback = nullptr; OnTrafficTrackerCallback WebSocket::_onTrafficTrackerCallback = nullptr;
@ -28,14 +22,12 @@ 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)
@ -44,7 +36,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(),
@ -64,18 +56,13 @@ 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;
@ -94,7 +81,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;
@ -144,24 +131,12 @@ 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
@ -223,7 +198,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),
@ -238,9 +213,7 @@ namespace ix
return status; return status;
} }
WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs)
int timeoutSecs,
bool enablePerMessageDeflate)
{ {
{ {
std::lock_guard<std::mutex> lock(_configMutex); std::lock_guard<std::mutex> lock(_configMutex);
@ -248,8 +221,7 @@ namespace ix
_perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs); _perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs);
} }
WebSocketInitResult status = WebSocketInitResult status = _ws.connectToSocket(std::move(socket), timeoutSecs);
_ws.connectToSocket(std::move(socket), timeoutSecs, enablePerMessageDeflate);
if (!status.success) if (!status.success)
{ {
return status; return status;
@ -257,7 +229,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),
@ -331,10 +303,8 @@ namespace ix
if (_automaticReconnection) if (_automaticReconnection)
{ {
duration = duration = millis(calculateRetryWaitMilliseconds(
millis(calculateRetryWaitMilliseconds(retries++, retries++, _maxWaitBetweenReconnectionRetries));
_maxWaitBetweenReconnectionRetries,
_minWaitBetweenReconnectionRetries));
connectErr.wait_time = duration.count(); connectErr.wait_time = duration.count();
connectErr.retries = retries; connectErr.retries = retries;
@ -344,7 +314,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(),

View File

@ -58,7 +58,6 @@ 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();
@ -92,8 +91,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;
@ -101,9 +100,7 @@ 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:
@ -117,9 +114,7 @@ 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>, WebSocketInitResult connectToSocket(std::unique_ptr<Socket>, int timeoutSecs);
int timeoutSecs,
bool enablePerMessageDeflate);
WebSocketTransport _ws; WebSocketTransport _ws;
@ -142,9 +137,7 @@ 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;

View File

@ -204,9 +204,6 @@ 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;
@ -244,8 +241,7 @@ 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;
@ -299,8 +295,7 @@ 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, "
@ -343,7 +338,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() && enablePerMessageDeflate) if (webSocketPerMessageDeflateOptions.enabled())
{ {
_enablePerMessageDeflate = true; _enablePerMessageDeflate = true;

View File

@ -35,7 +35,7 @@ namespace ix
int port, int port,
int timeoutSecs); int timeoutSecs);
WebSocketInitResult serverHandshake(int timeoutSecs, bool enablePerMessageDeflate); WebSocketInitResult serverHandshake(int timeoutSecs);
private: private:
std::string genRandomString(const int len); std::string genRandomString(const int len);

View File

@ -42,18 +42,6 @@ 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>;

View File

@ -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 uint8_t minServerMaxWindowBits = 8; static const int minServerMaxWindowBits = 8;
static const uint8_t maxServerMaxWindowBits = 15; static const int maxServerMaxWindowBits = 15;
const uint8_t WebSocketPerMessageDeflateOptions::kDefaultClientMaxWindowBits = 15; const uint8_t WebSocketPerMessageDeflateOptions::kDefaultClientMaxWindowBits = 15;
static const uint8_t minClientMaxWindowBits = 8; static const int minClientMaxWindowBits = 8;
static const uint8_t maxClientMaxWindowBits = 15; static const int maxClientMaxWindowBits = 15;
WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions( WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions(
bool enabled, bool enabled,
@ -85,7 +85,11 @@ namespace ix
if (startsWith(token, "server_max_window_bits=")) if (startsWith(token, "server_max_window_bits="))
{ {
uint8_t x = strtol(token.substr(token.find_last_of("=") + 1).c_str(), nullptr, 10); std::string val = token.substr(token.find_last_of("=") + 1);
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
@ -95,7 +99,11 @@ namespace ix
if (startsWith(token, "client_max_window_bits=")) if (startsWith(token, "client_max_window_bits="))
{ {
uint8_t x = strtol(token.substr(token.find_last_of("=") + 1).c_str(), nullptr, 10); std::string val = token.substr(token.find_last_of("=") + 1);
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

View File

@ -39,8 +39,8 @@ namespace ix
bool _enabled; bool _enabled;
bool _clientNoContextTakeover; bool _clientNoContextTakeover;
bool _serverNoContextTakeover; bool _serverNoContextTakeover;
uint8_t _clientMaxWindowBits; int _clientMaxWindowBits;
uint8_t _serverMaxWindowBits; int _serverMaxWindowBits;
void sanitizeClientMaxWindowBits(); void sanitizeClientMaxWindowBits();
}; };

View File

@ -97,10 +97,9 @@ namespace ix
} }
else if (_onClientMessageCallback) else if (_onClientMessageCallback)
{ {
WebSocket* webSocketRawPtr = webSocket.get();
webSocket->setOnMessageCallback( webSocket->setOnMessageCallback(
[this, webSocketRawPtr, connectionState](const WebSocketMessagePtr& msg) { [this, &ws = *webSocket.get(), connectionState](const WebSocketMessagePtr& msg) {
_onClientMessageCallback(connectionState, *webSocketRawPtr, msg); _onClientMessageCallback(connectionState, ws, msg);
}); });
} }
else else
@ -129,8 +128,7 @@ namespace ix
_clients.insert(webSocket); _clients.insert(webSocket);
} }
auto status = webSocket->connectToSocket( auto status = webSocket->connectToSocket(std::move(socket), _handshakeTimeoutSecs);
std::move(socket), _handshakeTimeoutSecs, _enablePerMessageDeflate);
if (status.success) if (status.success)
{ {
// Process incoming messages and execute callbacks // Process incoming messages and execute callbacks
@ -170,45 +168,4 @@ 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

View File

@ -47,9 +47,6 @@ 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:

View File

@ -169,8 +169,7 @@ 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);
@ -187,7 +186,7 @@ namespace ix
_perMessageDeflateOptions, _perMessageDeflateOptions,
_enablePerMessageDeflate); _enablePerMessageDeflate);
auto result = webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate); auto result = webSocketHandshake.serverHandshake(timeoutSecs);
if (result.success) if (result.success)
{ {
setReadyState(ReadyState::OPEN); setReadyState(ReadyState::OPEN);

View File

@ -83,9 +83,7 @@ namespace ix
int timeoutSecs); int timeoutSecs);
// Server // Server
WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs);
int timeoutSecs,
bool enablePerMessageDeflate);
PollResult poll(); PollResult poll();
WebSocketSendInfo sendBinary(const std::string& message, WebSocketSendInfo sendBinary(const std::string& message,

View File

@ -6,4 +6,4 @@
#pragma once #pragma once
#define IX_WEBSOCKET_VERSION "11.3.1" #define IX_WEBSOCKET_VERSION "11.0.8"

View File

@ -9,13 +9,10 @@
* $ 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()
@ -26,12 +23,9 @@ 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 !)
@ -41,18 +35,10 @@ 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;
} }
} }
); );
@ -63,16 +49,13 @@ 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");
// Display a prompt while (true)
std::cout << "> " << std::flush;
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); std::string text;
std::cout << "> " << std::flush; std::cout << "> " << std::flush;
std::getline(std::cin, text);
webSocket.send(text);
} }
return 0; return 0;

View File

@ -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=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) 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)
# 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_INSTALL_MESSAGE=LAZY -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_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_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install) mkdir -p build && (cd build ; cmake -GNinja -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_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install) mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
ws_install: ws_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) mkdir -p build && (cd build ; cmake -GNinja -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_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install) mkdir -p build && (cd build ; cmake -GNinja -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_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; ninja install) mkdir -p build && (cd build ; cmake -GNinja -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_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4) mkdir -p build && (cd build ; cmake -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_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4) mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4)
ws_no_python: ws_no_python:
mkdir -p build && (cd build ; cmake -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j4 install) mkdir -p build && (cd build ; cmake -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_INSTALL_MESSAGE=LAZY -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 ..) mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 ..)
(cd build ; ninja) (cd build ; ninja)
(cd build ; ninja -v test) (cd build ; ninja test)
test_asan: test_asan:
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") 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")
(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_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") 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")
(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_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") 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")
(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_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") 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")
(cd build ; ninja) (cd build ; ninja)
(cd build ; ninja test) (cd build ; ninja test)

View File

@ -33,7 +33,11 @@ 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, [] { return false; }); struct addrinfo* res = dnsLookup->resolve(errMsg,
[]
{
return false;
});
std::cerr << "Error message: " << errMsg << std::endl; std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res == nullptr); REQUIRE(res == nullptr);
} }
@ -44,7 +48,11 @@ 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, [] { return true; }); struct addrinfo* res = dnsLookup->resolve(errMsg,
[]
{
return true;
});
std::cerr << "Error message: " << errMsg << std::endl; std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res == nullptr); REQUIRE(res == nullptr);
} }

View File

@ -29,7 +29,6 @@ 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")
@ -40,7 +39,7 @@ namespace ix
headers["Upgrade"] = "webSocket"; headers["Upgrade"] = "webSocket";
REQUIRE(!CaseInsensitiveLess::cmp(headers["upGRADE"], "webSocket")); REQUIRE(CaseInsensitiveLess::cmp(headers["upgrade"], "WebSocket") == 0);
} }
} }

View File

@ -139,9 +139,8 @@ namespace ix
std::streamoff size = file.tellg(); std::streamoff size = file.tellg();
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.reserve((size_t) size); memblock.resize((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>());
return memblock; return memblock;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1639,10 +1639,7 @@ 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) { if (hOut==INVALID_HANDLE_VALUE) goto fatal;
errno = ENOTTY;
return false;
}
} }
DWORD consolemodeOut; DWORD consolemodeOut;

2
ws/package-lock.json generated
View File

@ -8,7 +8,7 @@
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
}, },
"ws": { "ws": {
"version": ">=6.2.2", "version": "6.2.0",
"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": {

205
ws/ws.cpp
View File

@ -55,17 +55,16 @@ 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.reserve((size_t) size); memblock.resize((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>());
return std::make_pair(true, memblock); return std::make_pair(true, memblock);
} }
@ -87,9 +86,9 @@ namespace
std::streamoff size = file.tellg(); std::streamoff size = file.tellg();
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.reserve((size_t) size); memblock.resize(size);
memblock.insert(
memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>()); file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
std::string bytes(memblock.begin(), memblock.end()); std::string bytes(memblock.begin(), memblock.end());
return bytes; return bytes;
@ -440,6 +439,93 @@ 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
@ -632,8 +718,7 @@ 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();
@ -658,7 +743,6 @@ 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);
@ -672,14 +756,12 @@ 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)
{ {
@ -788,21 +870,7 @@ namespace ix
{ {
spdlog::info("Received {} bytes", msg->wireSize); spdlog::info("Received {} bytes", msg->wireSize);
std::string payload = msg->str; ss << "ws_connect: received message: " << 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)
@ -855,8 +923,7 @@ 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,
@ -867,8 +934,7 @@ namespace ix
maxWaitBetweenReconnectionRetries, maxWaitBetweenReconnectionRetries,
tlsOptions, tlsOptions,
subprotocol, subprotocol,
pingIntervalSecs, pingIntervalSecs);
decompressGzipMessages);
webSocketChat.start(); webSocketChat.start();
while (true) while (true)
@ -922,11 +988,8 @@ 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];
ix::inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN); inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN);
spdlog::info("host: {} ip: {}", hostname, str); spdlog::info("host: {} ip: {}", hostname, str);
@ -1445,18 +1508,10 @@ namespace ix
filename = output; filename = output;
} }
if (filename.empty()) spdlog::info("Writing to disk: {}", filename);
{ std::ofstream out(filename);
spdlog::error("Cannot save content to disk: No output file supplied, and not " out.write((char*) &response->body.front(), response->body.size());
"filename could be extracted from the url {}", out.close();
url);
}
else
{
spdlog::info("Writing to disk: {}", filename);
std::ofstream out(filename);
out << response->body;
}
} }
else else
{ {
@ -1913,8 +1968,7 @@ 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);
std::string contentAsString(content.begin(), content.end()); out.write((char*) &content.front(), content.size());
out << contentAsString;
out.close(); out.close();
spdlog::info("ws_receive: Renaming {} to {}", filenameTmp, filename); spdlog::info("ws_receive: Renaming {} to {}", filenameTmp, filename);
@ -2102,6 +2156,23 @@ 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);
@ -2195,8 +2266,7 @@ 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");
auto res = load(filename); content = load(filename);
content = res.second;
} }
_id = uuid4(); _id = uuid4();
@ -2392,9 +2462,9 @@ namespace ix
else else
{ {
std::string readyStateString = std::string readyStateString =
readyState == ReadyState::Connecting ? "Connecting" readyState == ReadyState::Connecting
: readyState == ReadyState::Closing ? "Closing" ? "Connecting"
: "Closed"; : readyState == ReadyState::Closing ? "Closing" : "Closed";
size_t bufferedAmount = client->bufferedAmount(); size_t bufferedAmount = client->bufferedAmount();
spdlog::info( spdlog::info(
@ -2507,10 +2577,9 @@ 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 = 10 * 1000; // 10 seconds uint32_t maxWaitBetweenReconnectionRetries;
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");
@ -2573,7 +2642,6 @@ 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);
@ -2762,8 +2830,7 @@ 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"))
{ {
@ -2786,19 +2853,9 @@ 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") || app.got_subcommand("broadcast_server")) else if (app.got_subcommand("transfer"))
{ {
ix::WebSocketServer server(port, hostname); ret = ix::ws_transfer_main(port, hostname, tlsOptions);
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"))
{ {
@ -2813,6 +2870,10 @@ 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);