Compare commits
	
		
			1 Commits
		
	
	
		
			v11.2.1
			...
			bug/docker
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					e31b913280 | 
							
								
								
									
										2
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							@@ -14,7 +14,7 @@ jobs:
 | 
			
		||||
    - run: |
 | 
			
		||||
        mkdir 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: |
 | 
			
		||||
        cd build
 | 
			
		||||
        ninja
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								.github/workflows/unittest_windows_gcc.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/unittest_windows_gcc.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,27 +0,0 @@
 | 
			
		||||
name: windows_gcc
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    paths-ignore:
 | 
			
		||||
    - 'docs/**'
 | 
			
		||||
 | 
			
		||||
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,4 +7,3 @@ ws/.certs/
 | 
			
		||||
ws/.srl
 | 
			
		||||
ixhttpd
 | 
			
		||||
makefile
 | 
			
		||||
a.out
 | 
			
		||||
 
 | 
			
		||||
@@ -12,14 +12,12 @@ set (CMAKE_CXX_STANDARD 11)
 | 
			
		||||
set (CXX_STANDARD_REQUIRED ON)
 | 
			
		||||
set (CMAKE_CXX_EXTENSIONS OFF)
 | 
			
		||||
 | 
			
		||||
option (BUILD_DEMO OFF)
 | 
			
		||||
 | 
			
		||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
 | 
			
		||||
  set(CMAKE_POSITION_INDEPENDENT_CODE ON)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (UNIX)
 | 
			
		||||
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
 | 
			
		||||
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -gz")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
 | 
			
		||||
@@ -113,7 +111,6 @@ set( IXWEBSOCKET_HEADERS
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
if (USE_TLS)
 | 
			
		||||
@@ -147,7 +144,7 @@ if (USE_TLS)
 | 
			
		||||
    endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
add_library( ixwebsocket
 | 
			
		||||
add_library( ixwebsocket STATIC
 | 
			
		||||
    ${IXWEBSOCKET_SOURCES}
 | 
			
		||||
    ${IXWEBSOCKET_HEADERS}
 | 
			
		||||
)
 | 
			
		||||
@@ -194,7 +191,7 @@ if (USE_TLS)
 | 
			
		||||
    target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES})
 | 
			
		||||
  elseif (USE_SECURE_TRANSPORT)
 | 
			
		||||
    message(STATUS "TLS configured to use secure transport")
 | 
			
		||||
    target_link_libraries(ixwebsocket "-framework Foundation" "-framework Security")
 | 
			
		||||
    target_link_libraries(ixwebsocket "-framework foundation" "-framework security")
 | 
			
		||||
  endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
@@ -276,8 +273,3 @@ if (USE_WS OR USE_TEST)
 | 
			
		||||
    add_subdirectory(test)
 | 
			
		||||
  endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (BUILD_DEMO) 
 | 
			
		||||
  add_executable(demo main.cpp)
 | 
			
		||||
  target_link_libraries(demo ixwebsocket) 
 | 
			
		||||
endif()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								README.md
									
									
									
									
									
								
							@@ -15,11 +15,9 @@ 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.
 | 
			
		||||
 *
 | 
			
		||||
 *  On macOS
 | 
			
		||||
 *  $ 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
 | 
			
		||||
 *  $ 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
 | 
			
		||||
 *  $ ./a.out
 | 
			
		||||
 *
 | 
			
		||||
 *  Or use cmake -DBUILD_DEMO=ON option for other platforms
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <ixwebsocket/IXNetSystem.h>
 | 
			
		||||
@@ -46,12 +44,10 @@ int main()
 | 
			
		||||
            if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
            {
 | 
			
		||||
                std::cout << "received message: " << msg->str << std::endl;
 | 
			
		||||
                std::cout << "> " << std::flush;
 | 
			
		||||
            }
 | 
			
		||||
            else if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
            {
 | 
			
		||||
                std::cout << "Connection established" << std::endl;
 | 
			
		||||
                std::cout << "> " << std::flush;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
@@ -62,16 +58,13 @@ int main()
 | 
			
		||||
    // Send a message to the server (default to TEXT mode)
 | 
			
		||||
    webSocket.send("hello world");
 | 
			
		||||
 | 
			
		||||
    // Display a prompt
 | 
			
		||||
    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))
 | 
			
		||||
    while (true)
 | 
			
		||||
    {
 | 
			
		||||
        webSocket.send(text);
 | 
			
		||||
        std::string text;
 | 
			
		||||
        std::cout << "> " << std::flush;
 | 
			
		||||
        std::getline(std::cin, text);
 | 
			
		||||
 | 
			
		||||
        webSocket.send(text);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
@@ -96,7 +89,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
 | 
			
		||||
- [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
 | 
			
		||||
- [Teleport](http://teleportconnect.com/), Teleport is your own personal remote robot avatar
 | 
			
		||||
 | 
			
		||||
## Alternative libraries
 | 
			
		||||
 | 
			
		||||
@@ -106,7 +98,6 @@ There are plenty of great websocket libraries out there, which might work for yo
 | 
			
		||||
* [beast](https://github.com/boostorg/beast) - C++
 | 
			
		||||
* [libwebsockets](https://libwebsockets.org/) - 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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								build.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								build.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
rm -rf build
 | 
			
		||||
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=OFF .. 
 | 
			
		||||
ninja
 | 
			
		||||
@@ -1,39 +1,25 @@
 | 
			
		||||
FROM alpine:3.12 as build
 | 
			
		||||
# Build time
 | 
			
		||||
#
 | 
			
		||||
# Build with
 | 
			
		||||
# docker build --ssh default -t ws .
 | 
			
		||||
#
 | 
			
		||||
# focal == ubuntu 2020.04
 | 
			
		||||
# groovy == ubuntu 2020.10
 | 
			
		||||
FROM ubuntu:groovy
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache \
 | 
			
		||||
    gcc g++ musl-dev linux-headers \
 | 
			
		||||
    cmake mbedtls-dev make zlib-dev python3-dev ninja git
 | 
			
		||||
RUN apt update
 | 
			
		||||
 | 
			
		||||
RUN addgroup -S app && \
 | 
			
		||||
    adduser -S -G app app && \
 | 
			
		||||
    chown -R app:app /opt && \
 | 
			
		||||
    chown -R app:app /usr/local
 | 
			
		||||
RUN apt-get -y install g++ cmake make automake ccache libtool flex bison pkg-config git python3 jq
 | 
			
		||||
RUN apt-get -y install libjemalloc-dev libssl-dev libmcrypt-dev mcrypt zlib1g lua5.1-dev uuid-dev libz-dev binutils-dev
 | 
			
		||||
RUN apt-get -y install libboost-dev libboost-test-dev libboost-program-options-dev libboost-all-dev libboost-regex-dev 
 | 
			
		||||
RUN apt-get -y install ninja-build
 | 
			
		||||
 | 
			
		||||
# There is a bug in CMake where we cannot build from the root top folder
 | 
			
		||||
# So we build from /opt
 | 
			
		||||
COPY --chown=app:app . /opt
 | 
			
		||||
COPY . /opt
 | 
			
		||||
WORKDIR /opt
 | 
			
		||||
 | 
			
		||||
USER app
 | 
			
		||||
RUN make -f makefile.dev ws_mbedtls_install && \
 | 
			
		||||
    sh tools/trim_repo_for_docker.sh
 | 
			
		||||
RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
 | 
			
		||||
RUN rm -rf build
 | 
			
		||||
RUN --mount=type=ssh ./build.sh
 | 
			
		||||
 | 
			
		||||
FROM alpine:3.12 as runtime
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache libstdc++ mbedtls ca-certificates python3 strace && \
 | 
			
		||||
    addgroup -S app && \
 | 
			
		||||
    adduser -S -G app app
 | 
			
		||||
 | 
			
		||||
COPY --chown=app:app --from=build /usr/local/bin/ws /usr/local/bin/ws
 | 
			
		||||
 | 
			
		||||
# COPY --chown=app:app --from=build /opt /opt
 | 
			
		||||
 | 
			
		||||
RUN chmod +x /usr/local/bin/ws && \
 | 
			
		||||
    ldd /usr/local/bin/ws
 | 
			
		||||
 | 
			
		||||
# Now run in usermode
 | 
			
		||||
USER app
 | 
			
		||||
WORKDIR /home/app
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["ws"]
 | 
			
		||||
EXPOSE 8008
 | 
			
		||||
COPY /opt/build/ws/ws /usr/local/bin/ws
 | 
			
		||||
CMD ["/usr/local/bin/ws"]
 | 
			
		||||
 
 | 
			
		||||
@@ -2,34 +2,6 @@
 | 
			
		||||
 | 
			
		||||
All changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## [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
 | 
			
		||||
 | 
			
		||||
(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:
 | 
			
		||||
 | 
			
		||||
* `-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_TLS=1` will enable TLS support
 | 
			
		||||
* `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows)
 | 
			
		||||
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support
 | 
			
		||||
* `-DUSE_WS=1` will build the ws interactive command line tool
 | 
			
		||||
* `-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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -256,24 +256,11 @@ Wait time(ms): 6400
 | 
			
		||||
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 minimum waiting time can also be set.
 | 
			
		||||
The waiting time is capped by default at 10s between 2 attempts, but that value can be changed and queried.
 | 
			
		||||
 | 
			
		||||
```cpp
 | 
			
		||||
webSocket.setMaxWaitBetweenReconnectionRetries(5 * 1000); // 5000ms = 5s
 | 
			
		||||
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
 | 
			
		||||
@@ -347,10 +334,6 @@ if (!res.first)
 | 
			
		||||
    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()
 | 
			
		||||
server.start();
 | 
			
		||||
 | 
			
		||||
@@ -418,10 +401,6 @@ if (!res.first)
 | 
			
		||||
    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()
 | 
			
		||||
server.start();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,12 +24,6 @@
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
// mingw build quirks
 | 
			
		||||
#if defined(_WIN32) && defined(__GNUC__)
 | 
			
		||||
#define AI_NUMERICSERV NI_NUMERICSERV
 | 
			
		||||
#define AI_ADDRCONFIG LUP_ADDRCONFIG
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    const int64_t DNSLookup::kDefaultWait = 1; // ms
 | 
			
		||||
 
 | 
			
		||||
@@ -10,22 +10,16 @@
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount,
 | 
			
		||||
                                            uint32_t maxWaitBetweenReconnectionRetries,
 | 
			
		||||
                                            uint32_t minWaitBetweenReconnectionRetries)
 | 
			
		||||
    uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count,
 | 
			
		||||
                                            uint32_t maxWaitBetweenReconnectionRetries)
 | 
			
		||||
    {
 | 
			
		||||
        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)
 | 
			
		||||
        {
 | 
			
		||||
            waitTime = maxWaitBetweenReconnectionRetries;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return waitTime;
 | 
			
		||||
        return wait_time;
 | 
			
		||||
    }
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount,
 | 
			
		||||
                                            uint32_t maxWaitBetweenReconnectionRetries,
 | 
			
		||||
                                            uint32_t minWaitBetweenReconnectionRetries);
 | 
			
		||||
    uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count,
 | 
			
		||||
                                            uint32_t maxWaitBetweenReconnectionRetries);
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -124,156 +124,4 @@ namespace ix
 | 
			
		||||
#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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static int hexval(unsigned c)
 | 
			
		||||
    {
 | 
			
		||||
        if (c - '0' < 10) return c - '0';
 | 
			
		||||
        c |= 32;
 | 
			
		||||
        if (c - 'a' < 6) return c - 'a' + 10;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // 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
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
 | 
			
		||||
#ifndef WIN32_LEAN_AND_MEAN
 | 
			
		||||
#define WIN32_LEAN_AND_MEAN
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <WS2tcpip.h>
 | 
			
		||||
#include <WinSock2.h>
 | 
			
		||||
#include <basetsd.h>
 | 
			
		||||
@@ -21,22 +16,6 @@
 | 
			
		||||
// Define our own poll on Windows, as a wrapper on top of select
 | 
			
		||||
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
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
@@ -65,7 +44,4 @@ namespace ix
 | 
			
		||||
    bool uninitNetSystem();
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,6 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
    void SetThreadName(DWORD dwThreadID, const char* threadName)
 | 
			
		||||
    {
 | 
			
		||||
#ifndef __GNUC__
 | 
			
		||||
        THREADNAME_INFO info;
 | 
			
		||||
        info.dwType = 0x1000;
 | 
			
		||||
        info.szName = threadName;
 | 
			
		||||
@@ -52,7 +51,6 @@ namespace ix
 | 
			
		||||
        __except (EXCEPTION_EXECUTE_HANDLER)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -104,7 +104,7 @@ namespace ix
 | 
			
		||||
            server.sin_family = _addressFamily;
 | 
			
		||||
            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;
 | 
			
		||||
                ss << "SocketServer::listen() error calling inet_pton "
 | 
			
		||||
@@ -133,7 +133,7 @@ namespace ix
 | 
			
		||||
            server.sin6_family = _addressFamily;
 | 
			
		||||
            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;
 | 
			
		||||
                ss << "SocketServer::listen() error calling inet_pton "
 | 
			
		||||
@@ -338,7 +338,7 @@ namespace ix
 | 
			
		||||
            if (_addressFamily == AF_INET)
 | 
			
		||||
            {
 | 
			
		||||
                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();
 | 
			
		||||
                    std::stringstream ss;
 | 
			
		||||
@@ -357,8 +357,7 @@ namespace ix
 | 
			
		||||
            else // AF_INET6
 | 
			
		||||
            {
 | 
			
		||||
                char remoteIp6[INET6_ADDRSTRLEN];
 | 
			
		||||
                if (ix::inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) ==
 | 
			
		||||
                    nullptr)
 | 
			
		||||
                if (inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) == nullptr)
 | 
			
		||||
                {
 | 
			
		||||
                    int err = Socket::getErrno();
 | 
			
		||||
                    std::stringstream ss;
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ namespace ix
 | 
			
		||||
    bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char& c1,
 | 
			
		||||
                                                        const unsigned char& c2) const
 | 
			
		||||
    {
 | 
			
		||||
#if defined(_WIN32) && !defined(__GNUC__)
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
        return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale());
 | 
			
		||||
#else
 | 
			
		||||
        return std::tolower(c1) < std::tolower(c2);
 | 
			
		||||
 
 | 
			
		||||
@@ -22,14 +22,12 @@ namespace ix
 | 
			
		||||
    const int WebSocket::kDefaultPingIntervalSecs(-1);
 | 
			
		||||
    const bool WebSocket::kDefaultEnablePong(true);
 | 
			
		||||
    const uint32_t WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s
 | 
			
		||||
    const uint32_t WebSocket::kDefaultMinWaitBetweenReconnectionRetries(1);         // 1 ms
 | 
			
		||||
 | 
			
		||||
    WebSocket::WebSocket()
 | 
			
		||||
        : _onMessageCallback(OnMessageCallback())
 | 
			
		||||
        , _stop(false)
 | 
			
		||||
        , _automaticReconnection(true)
 | 
			
		||||
        , _maxWaitBetweenReconnectionRetries(kDefaultMaxWaitBetweenReconnectionRetries)
 | 
			
		||||
        , _minWaitBetweenReconnectionRetries(kDefaultMinWaitBetweenReconnectionRetries)
 | 
			
		||||
        , _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs)
 | 
			
		||||
        , _enablePong(kDefaultEnablePong)
 | 
			
		||||
        , _pingIntervalSecs(kDefaultPingIntervalSecs)
 | 
			
		||||
@@ -58,11 +56,6 @@ namespace ix
 | 
			
		||||
        _url = url;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocket::setHandshakeTimeout(int handshakeTimeoutSecs)
 | 
			
		||||
    {
 | 
			
		||||
        _handshakeTimeoutSecs = handshakeTimeoutSecs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocket::setExtraHeaders(const WebSocketHttpHeaders& headers)
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_configMutex);
 | 
			
		||||
@@ -138,24 +131,12 @@ namespace ix
 | 
			
		||||
        _maxWaitBetweenReconnectionRetries = maxWaitBetweenReconnectionRetries;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocket::setMinWaitBetweenReconnectionRetries(uint32_t minWaitBetweenReconnectionRetries)
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_configMutex);
 | 
			
		||||
        _minWaitBetweenReconnectionRetries = minWaitBetweenReconnectionRetries;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint32_t WebSocket::getMaxWaitBetweenReconnectionRetries() const
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_configMutex);
 | 
			
		||||
        return _maxWaitBetweenReconnectionRetries;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint32_t WebSocket::getMinWaitBetweenReconnectionRetries() const
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_configMutex);
 | 
			
		||||
        return _minWaitBetweenReconnectionRetries;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocket::start()
 | 
			
		||||
    {
 | 
			
		||||
        if (_thread.joinable()) return; // we've already been started
 | 
			
		||||
@@ -232,9 +213,7 @@ namespace ix
 | 
			
		||||
        return status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket,
 | 
			
		||||
                                                   int timeoutSecs,
 | 
			
		||||
                                                   bool enablePerMessageDeflate)
 | 
			
		||||
    WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs)
 | 
			
		||||
    {
 | 
			
		||||
        {
 | 
			
		||||
            std::lock_guard<std::mutex> lock(_configMutex);
 | 
			
		||||
@@ -242,8 +221,7 @@ namespace ix
 | 
			
		||||
                _perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        WebSocketInitResult status =
 | 
			
		||||
            _ws.connectToSocket(std::move(socket), timeoutSecs, enablePerMessageDeflate);
 | 
			
		||||
        WebSocketInitResult status = _ws.connectToSocket(std::move(socket), timeoutSecs);
 | 
			
		||||
        if (!status.success)
 | 
			
		||||
        {
 | 
			
		||||
            return status;
 | 
			
		||||
@@ -325,10 +303,8 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
                if (_automaticReconnection)
 | 
			
		||||
                {
 | 
			
		||||
                    duration =
 | 
			
		||||
                        millis(calculateRetryWaitMilliseconds(retries++,
 | 
			
		||||
                                                              _maxWaitBetweenReconnectionRetries,
 | 
			
		||||
                                                              _minWaitBetweenReconnectionRetries));
 | 
			
		||||
                    duration = millis(calculateRetryWaitMilliseconds(
 | 
			
		||||
                        retries++, _maxWaitBetweenReconnectionRetries));
 | 
			
		||||
 | 
			
		||||
                    connectErr.wait_time = duration.count();
 | 
			
		||||
                    connectErr.retries = retries;
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,6 @@ namespace ix
 | 
			
		||||
        void enablePerMessageDeflate();
 | 
			
		||||
        void disablePerMessageDeflate();
 | 
			
		||||
        void addSubProtocol(const std::string& subProtocol);
 | 
			
		||||
        void setHandshakeTimeout(int handshakeTimeoutSecs);
 | 
			
		||||
 | 
			
		||||
        // Run asynchronously, by calling start and stop.
 | 
			
		||||
        void start();
 | 
			
		||||
@@ -101,9 +100,7 @@ namespace ix
 | 
			
		||||
        void disableAutomaticReconnection();
 | 
			
		||||
        bool isAutomaticReconnectionEnabled() const;
 | 
			
		||||
        void setMaxWaitBetweenReconnectionRetries(uint32_t maxWaitBetweenReconnectionRetries);
 | 
			
		||||
        void setMinWaitBetweenReconnectionRetries(uint32_t minWaitBetweenReconnectionRetries);
 | 
			
		||||
        uint32_t getMaxWaitBetweenReconnectionRetries() const;
 | 
			
		||||
        uint32_t getMinWaitBetweenReconnectionRetries() const;
 | 
			
		||||
        const std::vector<std::string>& getSubProtocols();
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
@@ -117,9 +114,7 @@ namespace ix
 | 
			
		||||
        static void invokeTrafficTrackerCallback(size_t size, bool incoming);
 | 
			
		||||
 | 
			
		||||
        // Server
 | 
			
		||||
        WebSocketInitResult connectToSocket(std::unique_ptr<Socket>,
 | 
			
		||||
                                            int timeoutSecs,
 | 
			
		||||
                                            bool enablePerMessageDeflate);
 | 
			
		||||
        WebSocketInitResult connectToSocket(std::unique_ptr<Socket>, int timeoutSecs);
 | 
			
		||||
 | 
			
		||||
        WebSocketTransport _ws;
 | 
			
		||||
 | 
			
		||||
@@ -142,9 +137,7 @@ namespace ix
 | 
			
		||||
        // Automatic reconnection
 | 
			
		||||
        std::atomic<bool> _automaticReconnection;
 | 
			
		||||
        static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries;
 | 
			
		||||
        static const uint32_t kDefaultMinWaitBetweenReconnectionRetries;
 | 
			
		||||
        uint32_t _maxWaitBetweenReconnectionRetries;
 | 
			
		||||
        uint32_t _minWaitBetweenReconnectionRetries;
 | 
			
		||||
 | 
			
		||||
        // Make the sleeping in the automatic reconnection cancellable
 | 
			
		||||
        std::mutex _sleepMutex;
 | 
			
		||||
 
 | 
			
		||||
@@ -241,8 +241,7 @@ namespace ix
 | 
			
		||||
        return WebSocketInitResult(true, status, "", headers, path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs,
 | 
			
		||||
                                                            bool enablePerMessageDeflate)
 | 
			
		||||
    WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs)
 | 
			
		||||
    {
 | 
			
		||||
        _requestInitCancellation = false;
 | 
			
		||||
 | 
			
		||||
@@ -339,7 +338,7 @@ namespace ix
 | 
			
		||||
        WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(header);
 | 
			
		||||
 | 
			
		||||
        // If the client has requested that extension,
 | 
			
		||||
        if (webSocketPerMessageDeflateOptions.enabled() && enablePerMessageDeflate)
 | 
			
		||||
        if (webSocketPerMessageDeflateOptions.enabled())
 | 
			
		||||
        {
 | 
			
		||||
            _enablePerMessageDeflate = true;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ namespace ix
 | 
			
		||||
                                            int port,
 | 
			
		||||
                                            int timeoutSecs);
 | 
			
		||||
 | 
			
		||||
        WebSocketInitResult serverHandshake(int timeoutSecs, bool enablePerMessageDeflate);
 | 
			
		||||
        WebSocketInitResult serverHandshake(int timeoutSecs);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        std::string genRandomString(const int len);
 | 
			
		||||
 
 | 
			
		||||
@@ -129,8 +129,7 @@ namespace ix
 | 
			
		||||
            _clients.insert(webSocket);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto status = webSocket->connectToSocket(
 | 
			
		||||
            std::move(socket), _handshakeTimeoutSecs, _enablePerMessageDeflate);
 | 
			
		||||
        auto status = webSocket->connectToSocket(std::move(socket), _handshakeTimeoutSecs);
 | 
			
		||||
        if (status.success)
 | 
			
		||||
        {
 | 
			
		||||
            // Process incoming messages and execute callbacks
 | 
			
		||||
@@ -176,40 +175,40 @@ namespace ix
 | 
			
		||||
    //
 | 
			
		||||
    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())
 | 
			
		||||
        setOnClientMessageCallback(
 | 
			
		||||
            [this](std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                   WebSocket& webSocket,
 | 
			
		||||
                   const WebSocketMessagePtr& msg) {
 | 
			
		||||
                auto remoteIp = connectionState->getRemoteIp();
 | 
			
		||||
                if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
                {
 | 
			
		||||
                    if (client.get() != &webSocket)
 | 
			
		||||
                    for (auto&& client : getClients())
 | 
			
		||||
                    {
 | 
			
		||||
                        client->send(msg->str, msg->binary);
 | 
			
		||||
 | 
			
		||||
                        // Make sure the OS send buffer is flushed before moving on
 | 
			
		||||
                        do
 | 
			
		||||
                        if (client.get() != &webSocket)
 | 
			
		||||
                        {
 | 
			
		||||
                            size_t bufferedAmount = client->bufferedAmount();
 | 
			
		||||
                            std::chrono::duration<double, std::milli> duration(500);
 | 
			
		||||
                            std::this_thread::sleep_for(duration);
 | 
			
		||||
                        } while (client->bufferedAmount() != 0);
 | 
			
		||||
                            client->send(msg->str, msg->binary);
 | 
			
		||||
 | 
			
		||||
                            // Make sure the OS send buffer is flushed before moving on
 | 
			
		||||
                            do
 | 
			
		||||
                            {
 | 
			
		||||
                                size_t bufferedAmount = client->bufferedAmount();
 | 
			
		||||
                                std::chrono::duration<double, std::milli> duration(500);
 | 
			
		||||
                                std::this_thread::sleep_for(duration);
 | 
			
		||||
                            } while (client->bufferedAmount() != 0);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool WebSocketServer::listenAndStart()
 | 
			
		||||
    int WebSocketServer::listenAndStart()
 | 
			
		||||
    {
 | 
			
		||||
        auto res = listen();
 | 
			
		||||
        if (!res.first)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        start();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ namespace ix
 | 
			
		||||
        std::set<std::shared_ptr<WebSocket>> getClients();
 | 
			
		||||
 | 
			
		||||
        void makeBroadcastServer();
 | 
			
		||||
        bool listenAndStart();
 | 
			
		||||
        int listenAndStart();
 | 
			
		||||
 | 
			
		||||
        const static int kDefaultHandShakeTimeoutSecs;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -169,8 +169,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
    // Server
 | 
			
		||||
    WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket,
 | 
			
		||||
                                                            int timeoutSecs,
 | 
			
		||||
                                                            bool enablePerMessageDeflate)
 | 
			
		||||
                                                            int timeoutSecs)
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_socketMutex);
 | 
			
		||||
 | 
			
		||||
@@ -187,7 +186,7 @@ namespace ix
 | 
			
		||||
                                              _perMessageDeflateOptions,
 | 
			
		||||
                                              _enablePerMessageDeflate);
 | 
			
		||||
 | 
			
		||||
        auto result = webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate);
 | 
			
		||||
        auto result = webSocketHandshake.serverHandshake(timeoutSecs);
 | 
			
		||||
        if (result.success)
 | 
			
		||||
        {
 | 
			
		||||
            setReadyState(ReadyState::OPEN);
 | 
			
		||||
 
 | 
			
		||||
@@ -83,9 +83,7 @@ namespace ix
 | 
			
		||||
                                         int timeoutSecs);
 | 
			
		||||
 | 
			
		||||
        // Server
 | 
			
		||||
        WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket,
 | 
			
		||||
                                            int timeoutSecs,
 | 
			
		||||
                                            bool enablePerMessageDeflate);
 | 
			
		||||
        WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs);
 | 
			
		||||
 | 
			
		||||
        PollResult poll();
 | 
			
		||||
        WebSocketSendInfo sendBinary(const std::string& message,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,4 +6,4 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#define IX_WEBSOCKET_VERSION "11.2.0"
 | 
			
		||||
#define IX_WEBSOCKET_VERSION "11.0.8"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								main.cpp
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								main.cpp
									
									
									
									
									
								
							@@ -9,8 +9,6 @@
 | 
			
		||||
 *  $ 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
 | 
			
		||||
 *  $ ./a.out
 | 
			
		||||
 *
 | 
			
		||||
 *  Or use cmake -DBUILD_DEMO=ON option for other platform
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <ixwebsocket/IXNetSystem.h>
 | 
			
		||||
@@ -37,12 +35,10 @@ int main()
 | 
			
		||||
            if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
            {
 | 
			
		||||
                std::cout << "received message: " << msg->str << std::endl;
 | 
			
		||||
                std::cout << "> " << std::flush;
 | 
			
		||||
            }
 | 
			
		||||
            else if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
            {
 | 
			
		||||
                std::cout << "Connection established" << std::endl;
 | 
			
		||||
                std::cout << "> " << std::flush;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
@@ -53,16 +49,13 @@ int main()
 | 
			
		||||
    // Send a message to the server (default to TEXT mode)
 | 
			
		||||
    webSocket.send("hello world");
 | 
			
		||||
 | 
			
		||||
    // Display a prompt
 | 
			
		||||
    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))
 | 
			
		||||
    while (true)
 | 
			
		||||
    {
 | 
			
		||||
        webSocket.send(text);
 | 
			
		||||
        std::string text;
 | 
			
		||||
        std::cout << "> " << std::flush;
 | 
			
		||||
        std::getline(std::cin, text);
 | 
			
		||||
 | 
			
		||||
        webSocket.send(text);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -113,7 +113,7 @@ test_server:
 | 
			
		||||
test:
 | 
			
		||||
	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 -v test)
 | 
			
		||||
	(cd build ; ninja test)
 | 
			
		||||
 | 
			
		||||
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")
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,11 @@ TEST_CASE("dns", "[net]")
 | 
			
		||||
        auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        REQUIRE(res == nullptr);
 | 
			
		||||
    }
 | 
			
		||||
@@ -44,7 +48,11 @@ TEST_CASE("dns", "[net]")
 | 
			
		||||
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        // 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;
 | 
			
		||||
        REQUIRE(res == nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -139,9 +139,8 @@ namespace ix
 | 
			
		||||
        std::streamoff size = file.tellg();
 | 
			
		||||
        file.seekg(0, file.beg);
 | 
			
		||||
 | 
			
		||||
        memblock.reserve((size_t) size);
 | 
			
		||||
        memblock.insert(
 | 
			
		||||
            memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>());
 | 
			
		||||
        memblock.resize((size_t) size);
 | 
			
		||||
        file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
 | 
			
		||||
 | 
			
		||||
        return memblock;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								third_party/cpp-linenoise/linenoise.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								third_party/cpp-linenoise/linenoise.cpp
									
									
									
									
										vendored
									
									
								
							@@ -1639,10 +1639,7 @@ bool enableRawMode(int fd) {
 | 
			
		||||
 | 
			
		||||
        /* Init windows console handles only once */
 | 
			
		||||
        hOut = GetStdHandle(STD_OUTPUT_HANDLE);
 | 
			
		||||
        if (hOut==INVALID_HANDLE_VALUE) {
 | 
			
		||||
            errno = ENOTTY;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (hOut==INVALID_HANDLE_VALUE) goto fatal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DWORD consolemodeOut;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										72
									
								
								ws/ws.cpp
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								ws/ws.cpp
									
									
									
									
									
								
							@@ -55,17 +55,16 @@ namespace
 | 
			
		||||
    std::pair<bool, std::vector<uint8_t>> load(const std::string& path)
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<uint8_t> memblock;
 | 
			
		||||
        std::ifstream file(path);
 | 
			
		||||
 | 
			
		||||
        std::ifstream file(path);
 | 
			
		||||
        if (!file.is_open()) return std::make_pair(false, memblock);
 | 
			
		||||
 | 
			
		||||
        file.seekg(0, file.end);
 | 
			
		||||
        std::streamoff size = file.tellg();
 | 
			
		||||
        file.seekg(0, file.beg);
 | 
			
		||||
 | 
			
		||||
        memblock.reserve((size_t) size);
 | 
			
		||||
        memblock.insert(
 | 
			
		||||
            memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>());
 | 
			
		||||
        memblock.resize((size_t) size);
 | 
			
		||||
        file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
 | 
			
		||||
 | 
			
		||||
        return std::make_pair(true, memblock);
 | 
			
		||||
    }
 | 
			
		||||
@@ -87,9 +86,9 @@ namespace
 | 
			
		||||
        std::streamoff size = file.tellg();
 | 
			
		||||
        file.seekg(0, file.beg);
 | 
			
		||||
 | 
			
		||||
        memblock.reserve((size_t) size);
 | 
			
		||||
        memblock.insert(
 | 
			
		||||
            memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>());
 | 
			
		||||
        memblock.resize(size);
 | 
			
		||||
 | 
			
		||||
        file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
 | 
			
		||||
 | 
			
		||||
        std::string bytes(memblock.begin(), memblock.end());
 | 
			
		||||
        return bytes;
 | 
			
		||||
@@ -906,7 +905,7 @@ namespace ix
 | 
			
		||||
        // code which display correct results
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
@@ -1425,18 +1424,10 @@ namespace ix
 | 
			
		||||
                    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);
 | 
			
		||||
                    std::ofstream out(filename);
 | 
			
		||||
                    out << response->body;
 | 
			
		||||
                }
 | 
			
		||||
                spdlog::info("Writing to disk: {}", filename);
 | 
			
		||||
                std::ofstream out(filename);
 | 
			
		||||
                out.write((char*) &response->body.front(), response->body.size());
 | 
			
		||||
                out.close();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
@@ -1893,8 +1884,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        spdlog::info("ws_receive: Writing to disk: {}", filenameTmp);
 | 
			
		||||
        std::ofstream out(filenameTmp);
 | 
			
		||||
        std::string contentAsString(content.begin(), content.end());
 | 
			
		||||
        out << contentAsString;
 | 
			
		||||
        out.write((char*) &content.front(), content.size());
 | 
			
		||||
        out.close();
 | 
			
		||||
 | 
			
		||||
        spdlog::info("ws_receive: Renaming {} to {}", filenameTmp, filename);
 | 
			
		||||
@@ -2082,6 +2072,23 @@ namespace ix
 | 
			
		||||
        _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()
 | 
			
		||||
    {
 | 
			
		||||
        _webSocket.setUrl(_url);
 | 
			
		||||
@@ -2175,8 +2182,7 @@ namespace ix
 | 
			
		||||
        std::vector<uint8_t> content;
 | 
			
		||||
        {
 | 
			
		||||
            Bench bench("ws_send: load file from disk");
 | 
			
		||||
            auto res = load(filename);
 | 
			
		||||
            content = res.second;
 | 
			
		||||
            content = load(filename);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _id = uuid4();
 | 
			
		||||
@@ -2372,9 +2378,9 @@ namespace ix
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                std::string readyStateString =
 | 
			
		||||
                                    readyState == ReadyState::Connecting ? "Connecting"
 | 
			
		||||
                                    : readyState == ReadyState::Closing  ? "Closing"
 | 
			
		||||
                                                                         : "Closed";
 | 
			
		||||
                                    readyState == ReadyState::Connecting
 | 
			
		||||
                                        ? "Connecting"
 | 
			
		||||
                                        : readyState == ReadyState::Closing ? "Closing" : "Closed";
 | 
			
		||||
                                size_t bufferedAmount = client->bufferedAmount();
 | 
			
		||||
 | 
			
		||||
                                spdlog::info(
 | 
			
		||||
@@ -2487,7 +2493,7 @@ int main(int argc, char** argv)
 | 
			
		||||
    int delayMs = -1;
 | 
			
		||||
    int count = 1;
 | 
			
		||||
    int msgCount = 1000 * 1000;
 | 
			
		||||
    uint32_t maxWaitBetweenReconnectionRetries = 10 * 1000; // 10 seconds
 | 
			
		||||
    uint32_t maxWaitBetweenReconnectionRetries;
 | 
			
		||||
    int pingIntervalSecs = 30;
 | 
			
		||||
    int runCount = 1;
 | 
			
		||||
 | 
			
		||||
@@ -2768,14 +2774,8 @@ int main(int argc, char** argv)
 | 
			
		||||
        ix::WebSocketServer server(port, hostname);
 | 
			
		||||
        server.setTLSOptions(tlsOptions);
 | 
			
		||||
        server.makeBroadcastServer();
 | 
			
		||||
        if (!server.listenAndStart())
 | 
			
		||||
        {
 | 
			
		||||
            spdlog::error("Error while starting the server");
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            server.wait();
 | 
			
		||||
        }
 | 
			
		||||
        server.listenAndStart();
 | 
			
		||||
        server.wait();
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("send"))
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user