Compare commits
39 Commits
v9.9.0
...
feature/zl
Author | SHA1 | Date | |
---|---|---|---|
5036e338c7 | |||
80fb8cfb59 | |||
43c0ae0812 | |||
dcc447ec4a | |||
5127094f0e | |||
2e3d625c1e | |||
029289413c | |||
4d51098c86 | |||
c2b05af022 | |||
e85f975ab0 | |||
dc77d62a5d | |||
4f41f209a2 | |||
5940e53d77 | |||
22dffd5b7e | |||
af2f31045d | |||
5daa59f9f3 | |||
2ea9d06a93 | |||
847fc142d1 | |||
0388459bd0 | |||
9a47ec1217 | |||
45a40c8640 | |||
e34f1c30d6 | |||
c14a4c0e3e | |||
b146e93a3a | |||
9957ec9724 | |||
78a42f61bd | |||
e78019dad6 | |||
0f026c5da2 | |||
c26a2d5d39 | |||
2798886c0b | |||
ffde283a4b | |||
f7031d0d3e | |||
595e6c57df | |||
87709c201e | |||
e70d83ace1 | |||
ca829a3a98 | |||
26a1e63626 | |||
c98959b895 | |||
baf18648e9 |
13
.github/workflows/unittest_linux.yml
vendored
13
.github/workflows/unittest_linux.yml
vendored
@ -1,13 +0,0 @@
|
||||
name: linux
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make test_make
|
||||
run: make test_make
|
15
.github/workflows/unittest_mac_tsan_mbedtls.yml
vendored
15
.github/workflows/unittest_mac_tsan_mbedtls.yml
vendored
@ -1,15 +0,0 @@
|
||||
name: mac_tsan_mbedtls
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
mac_tsan_mbedtls:
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: install mbedtls
|
||||
run: brew install mbedtls
|
||||
- name: make test
|
||||
run: make test_tsan_mbedtls
|
15
.github/workflows/unittest_mac_tsan_openssl.yml
vendored
15
.github/workflows/unittest_mac_tsan_openssl.yml
vendored
@ -1,15 +0,0 @@
|
||||
name: mac_tsan_openssl
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
mac_tsan_openssl:
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: install openssl
|
||||
run: brew install openssl@1.1
|
||||
- name: make test
|
||||
run: make test_tsan_openssl
|
@ -1,13 +0,0 @@
|
||||
name: mac_tsan_sectransport
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
mac_tsan_sectransport:
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make test_tsan
|
||||
run: make test_tsan
|
40
.github/workflows/unittest_uwp.yml
vendored
40
.github/workflows/unittest_uwp.yml
vendored
@ -1,40 +0,0 @@
|
||||
name: uwp
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
uwp:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: seanmiddleditch/gha-setup-vsdevenv@master
|
||||
- run: |
|
||||
vcpkg install zlib:x64-uwp
|
||||
- run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DUSE_TEST=1 ..
|
||||
- run: cmake --build build
|
||||
|
||||
#
|
||||
# Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg
|
||||
#
|
||||
# windows_openssl:
|
||||
# runs-on: windows-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v1
|
||||
# - uses: seanmiddleditch/gha-setup-vsdevenv@master
|
||||
# - run: |
|
||||
# vcpkg install zlib:x64-windows
|
||||
# vcpkg install openssl:x64-windows
|
||||
# - run: |
|
||||
# mkdir build
|
||||
# cd build
|
||||
# cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_OPEN_SSL=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 ..
|
||||
# - run: cmake --build build
|
||||
#
|
||||
# # Running the unittest does not work, the binary cannot be found
|
||||
# #- run: ../build/test/ixwebsocket_unittest.exe
|
||||
# # working-directory: test
|
6
.github/workflows/unittest_windows.yml
vendored
6
.github/workflows/unittest_windows.yml
vendored
@ -10,10 +10,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: seanmiddleditch/gha-setup-vsdevenv@master
|
||||
- run: |
|
||||
vcpkg install zlib:x64-windows
|
||||
- run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 ..
|
||||
cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 ..
|
||||
- run: cmake --build build
|
||||
- run: ../build/test/ixwebsocket_unittest.exe
|
||||
working-directory: test
|
||||
|
@ -30,12 +30,15 @@ set( IXWEBSOCKET_SOURCES
|
||||
ixwebsocket/IXConnectionState.cpp
|
||||
ixwebsocket/IXDNSLookup.cpp
|
||||
ixwebsocket/IXExponentialBackoff.cpp
|
||||
ixwebsocket/IXGetFreePort.cpp
|
||||
ixwebsocket/IXHttp.cpp
|
||||
ixwebsocket/IXHttpClient.cpp
|
||||
ixwebsocket/IXHttpServer.cpp
|
||||
ixwebsocket/IXNetSystem.cpp
|
||||
ixwebsocket/IXSelectInterrupt.cpp
|
||||
ixwebsocket/IXSelectInterruptFactory.cpp
|
||||
ixwebsocket/IXSelectInterruptPipe.cpp
|
||||
ixwebsocket/IXSetThreadName.cpp
|
||||
ixwebsocket/IXSocket.cpp
|
||||
ixwebsocket/IXSocketConnect.cpp
|
||||
ixwebsocket/IXSocketFactory.cpp
|
||||
@ -51,6 +54,7 @@ set( IXWEBSOCKET_SOURCES
|
||||
ixwebsocket/IXWebSocketPerMessageDeflate.cpp
|
||||
ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp
|
||||
ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp
|
||||
ixwebsocket/IXWebSocketProxyServer.cpp
|
||||
ixwebsocket/IXWebSocketServer.cpp
|
||||
ixwebsocket/IXWebSocketTransport.cpp
|
||||
)
|
||||
@ -62,6 +66,7 @@ set( IXWEBSOCKET_HEADERS
|
||||
ixwebsocket/IXConnectionState.h
|
||||
ixwebsocket/IXDNSLookup.h
|
||||
ixwebsocket/IXExponentialBackoff.h
|
||||
ixwebsocket/IXGetFreePort.h
|
||||
ixwebsocket/IXHttp.h
|
||||
ixwebsocket/IXHttpClient.h
|
||||
ixwebsocket/IXHttpServer.h
|
||||
@ -69,6 +74,7 @@ set( IXWEBSOCKET_HEADERS
|
||||
ixwebsocket/IXProgressCallback.h
|
||||
ixwebsocket/IXSelectInterrupt.h
|
||||
ixwebsocket/IXSelectInterruptFactory.h
|
||||
ixwebsocket/IXSelectInterruptPipe.h
|
||||
ixwebsocket/IXSetThreadName.h
|
||||
ixwebsocket/IXSocket.h
|
||||
ixwebsocket/IXSocketConnect.h
|
||||
@ -93,29 +99,13 @@ set( IXWEBSOCKET_HEADERS
|
||||
ixwebsocket/IXWebSocketPerMessageDeflate.h
|
||||
ixwebsocket/IXWebSocketPerMessageDeflateCodec.h
|
||||
ixwebsocket/IXWebSocketPerMessageDeflateOptions.h
|
||||
ixwebsocket/IXWebSocketProxyServer.h
|
||||
ixwebsocket/IXWebSocketSendInfo.h
|
||||
ixwebsocket/IXWebSocketServer.h
|
||||
ixwebsocket/IXWebSocketTransport.h
|
||||
ixwebsocket/IXWebSocketVersion.h
|
||||
)
|
||||
|
||||
if (UNIX)
|
||||
# Linux, Mac, iOS, Android
|
||||
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptPipe.cpp )
|
||||
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptPipe.h )
|
||||
endif()
|
||||
|
||||
# Platform specific code
|
||||
if (APPLE)
|
||||
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/apple/IXSetThreadName_apple.cpp)
|
||||
elseif (WIN32)
|
||||
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/windows/IXSetThreadName_windows.cpp)
|
||||
elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/freebsd/IXSetThreadName_freebsd.cpp)
|
||||
else()
|
||||
list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/linux/IXSetThreadName_linux.cpp)
|
||||
endif()
|
||||
|
||||
option(USE_TLS "Enable TLS support" FALSE)
|
||||
|
||||
if (USE_TLS)
|
||||
@ -200,10 +190,18 @@ if (USE_TLS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Use ZLIB_ROOT CMake variable if you need to use your own zlib
|
||||
find_package(ZLIB REQUIRED)
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
|
||||
option(USE_ZLIB "Enable zlib support" TRUE)
|
||||
|
||||
if (USE_ZLIB)
|
||||
# Use ZLIB_ROOT CMake variable if you need to use your own zlib
|
||||
find_package(ZLIB)
|
||||
if (ZLIB_FOUND)
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
|
||||
|
||||
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi)
|
||||
|
23
docker/Dockerfile.ubuntu_groovy
Normal file
23
docker/Dockerfile.ubuntu_groovy
Normal file
@ -0,0 +1,23 @@
|
||||
# Build time
|
||||
FROM ubuntu:groovy as build
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get -y install g++ libssl-dev libz-dev make python ninja-build
|
||||
RUN apt-get -y install cmake
|
||||
RUN apt-get -y install gdb
|
||||
|
||||
COPY . /opt
|
||||
WORKDIR /opt
|
||||
|
||||
#
|
||||
# To use the container interactively for debugging/building
|
||||
# 1. Build with
|
||||
# CMD ["ls"]
|
||||
# 2. Run with
|
||||
# docker run --entrypoint sh -it docker-game-eng-dev.addsrv.com/ws:9.10.6
|
||||
#
|
||||
|
||||
RUN ["make", "test"]
|
||||
# CMD ["ls"]
|
@ -1,6 +1,70 @@
|
||||
# Changelog
|
||||
All changes to this project will be documented in this file.
|
||||
|
||||
## [10.1.1] - 2020-07-29
|
||||
|
||||
(websocket client) onProgressCallback not called for short messages on a websocket (fix #233)
|
||||
|
||||
## [10.1.0] - 2020-07-29
|
||||
|
||||
(websocket client) heartbeat is not sent at the requested frequency (fix #232)
|
||||
|
||||
## [10.0.3] - 2020-07-28
|
||||
|
||||
compiler warning fixes
|
||||
|
||||
## [10.0.2] - 2020-07-28
|
||||
|
||||
(ixcobra) CobraConnection: unsubscribe from all subscriptions when disconnecting
|
||||
|
||||
## [10.0.1] - 2020-07-27
|
||||
|
||||
(socket utility) move ix::getFreePort to ixwebsocket library
|
||||
|
||||
## [10.0.0] - 2020-07-25
|
||||
|
||||
(ixwebsocket server) change legacy api with 2 nested callbacks, so that the first api takes a weak_ptr<WebSocket> as its first argument
|
||||
|
||||
## [9.10.7] - 2020-07-25
|
||||
|
||||
(ixwebsocket) add WebSocketProxyServer, from ws. Still need to make the interface better.
|
||||
|
||||
## [9.10.6] - 2020-07-24
|
||||
|
||||
(ws) port broadcast_server sub-command to the new server API
|
||||
|
||||
## [9.10.5] - 2020-07-24
|
||||
|
||||
(unittest) port most unittests to the new server API
|
||||
|
||||
## [9.10.3] - 2020-07-24
|
||||
|
||||
(ws) port ws transfer to the new server API
|
||||
|
||||
## [9.10.2] - 2020-07-24
|
||||
|
||||
(websocket client) reset WebSocketTransport onClose callback in the WebSocket destructor
|
||||
|
||||
## [9.10.1] - 2020-07-24
|
||||
|
||||
(websocket server) reset client websocket callback when the connection is closed
|
||||
|
||||
## [9.10.0] - 2020-07-23
|
||||
|
||||
(websocket server) add a new simpler API to handle client connections / that API does not trigger a memory leak while the previous one did
|
||||
|
||||
## [9.9.3] - 2020-07-17
|
||||
|
||||
(build) merge platform specific files which were used to have different implementations for setting a thread name into a single file, to make it easier to include every source files and build the ixwebsocket library (fix #226)
|
||||
|
||||
## [9.9.2] - 2020-07-10
|
||||
|
||||
(socket server) bump default max connection count from 32 to 128
|
||||
|
||||
## [9.9.1] - 2020-07-10
|
||||
|
||||
(snake) implement super simple stream sql expression support in snake server
|
||||
|
||||
## [9.9.0] - 2020-07-08
|
||||
|
||||
(socket+websocket+http+redis+snake servers) expose the remote ip and remote port when a new connection is made
|
||||
|
134
docs/usage.md
134
docs/usage.md
@ -246,6 +246,10 @@ uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries();
|
||||
|
||||
## WebSocket server API
|
||||
|
||||
### Legacy api
|
||||
|
||||
This api was actually changed to take a weak_ptr<WebSocket> as the first argument to setOnConnectionCallback ; previously it would take a shared_ptr<WebSocket> which was creating cycles and then memory leaks problems.
|
||||
|
||||
```cpp
|
||||
#include <ixwebsocket/IXWebSocketServer.h>
|
||||
|
||||
@ -256,41 +260,49 @@ uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries();
|
||||
ix::WebSocketServer server(port);
|
||||
|
||||
server.setOnConnectionCallback(
|
||||
[&server](std::shared_ptr<WebSocket> webSocket,
|
||||
[&server](std::weak_ptr<WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo)
|
||||
{
|
||||
std::cout << "Remote ip: " << connectionInfo->remoteIp << std::endl;
|
||||
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr msg)
|
||||
{
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
auto ws = webSocket.lock();
|
||||
if (ws)
|
||||
{
|
||||
ws->setOnMessageCallback(
|
||||
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr msg)
|
||||
{
|
||||
std::cout << "New connection" << std::endl;
|
||||
|
||||
// A connection state object is available, and has a default id
|
||||
// You can subclass ConnectionState and pass an alternate factory
|
||||
// to override it. It is useful if you want to store custom
|
||||
// attributes per connection (authenticated bool flag, attributes, etc...)
|
||||
std::cout << "id: " << connectionState->getId() << std::endl;
|
||||
|
||||
// The uri the client did connect to.
|
||||
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
|
||||
std::cout << "Headers:" << std::endl;
|
||||
for (auto it : msg->openInfo.headers)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
std::cout << it.first << ": " << it.second << std::endl;
|
||||
std::cout << "New connection" << std::endl;
|
||||
|
||||
// A connection state object is available, and has a default id
|
||||
// You can subclass ConnectionState and pass an alternate factory
|
||||
// to override it. It is useful if you want to store custom
|
||||
// attributes per connection (authenticated bool flag, attributes, etc...)
|
||||
std::cout << "id: " << connectionState->getId() << std::endl;
|
||||
|
||||
// The uri the client did connect to.
|
||||
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
|
||||
std::cout << "Headers:" << std::endl;
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cout << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
// For an echo server, we just send back to the client whatever was received by the server
|
||||
// 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.
|
||||
// Here we send it in the same mode as it was received.
|
||||
auto ws = webSocket.lock();
|
||||
if (ws)
|
||||
{
|
||||
ws->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
// For an echo server, we just send back to the client whatever was received by the server
|
||||
// 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.
|
||||
// Here we send it in the same mode as it was received.
|
||||
webSocket->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -312,6 +324,74 @@ server.wait();
|
||||
|
||||
```
|
||||
|
||||
### New api
|
||||
|
||||
The new API does not require to use 2 nested callbacks, which is a bit annoying. The real fix is that there was a memory leak due to a shared_ptr cycle, due to passing down a shared_ptr<WebSocket> down to the callbacks.
|
||||
|
||||
The webSocket reference is guaranteed to be always valid ; by design the callback will never be invoked with a null webSocket object.
|
||||
|
||||
```cpp
|
||||
#include <ixwebsocket/IXWebSocketServer.h>
|
||||
|
||||
...
|
||||
|
||||
// Run a server on localhost at a given port.
|
||||
// Bound host name, max connections and listen backlog can also be passed in as parameters.
|
||||
ix::WebSocketServer server(port);
|
||||
|
||||
server.setOnClientMessageCallback(std::shared_ptr<ConnectionState> connectionState,
|
||||
ConnectionInfo& connectionInfo,
|
||||
WebSocket& webSocket,
|
||||
const WebSocketMessagePtr& msg)
|
||||
{
|
||||
// The ConnectionInfo object contains information about the connection,
|
||||
// at this point only the client ip address and the port.
|
||||
std::cout << "Remote ip: " << connectionInfo.remoteIp << std::endl;
|
||||
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
std::cout << "New connection" << std::endl;
|
||||
|
||||
// A connection state object is available, and has a default id
|
||||
// You can subclass ConnectionState and pass an alternate factory
|
||||
// to override it. It is useful if you want to store custom
|
||||
// attributes per connection (authenticated bool flag, attributes, etc...)
|
||||
std::cout << "id: " << connectionState->getId() << std::endl;
|
||||
|
||||
// The uri the client did connect to.
|
||||
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
|
||||
std::cout << "Headers:" << std::endl;
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cout << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
// For an echo server, we just send back to the client whatever was received by the server
|
||||
// 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.
|
||||
// Here we send it in the same mode as it was received.
|
||||
webSocket.send(msg->str, msg->binary);
|
||||
}
|
||||
);
|
||||
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
{
|
||||
// Error handling
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Run the server in the background. Server can be stoped by calling server.stop()
|
||||
server.start();
|
||||
|
||||
// Block until server.stop() is called.
|
||||
server.wait();
|
||||
|
||||
```
|
||||
|
||||
## HTTP client API
|
||||
|
||||
```cpp
|
||||
|
@ -111,6 +111,12 @@ namespace ix
|
||||
|
||||
void CobraConnection::disconnect()
|
||||
{
|
||||
auto subscriptionIds = getSubscriptionsIds();
|
||||
for (auto&& subscriptionId : subscriptionIds)
|
||||
{
|
||||
unsubscribe(subscriptionId);
|
||||
}
|
||||
|
||||
_authenticated = false;
|
||||
_webSocket->stop();
|
||||
}
|
||||
@ -614,6 +620,18 @@ namespace ix
|
||||
_webSocket->send(pdu.toStyledString());
|
||||
}
|
||||
|
||||
std::vector<std::string> CobraConnection::getSubscriptionsIds()
|
||||
{
|
||||
std::vector<std::string> subscriptionIds;
|
||||
std::lock_guard<std::mutex> lock(_cbsMutex);
|
||||
|
||||
for (auto&& it : _cbs)
|
||||
{
|
||||
subscriptionIds.push_back(it.first);
|
||||
}
|
||||
return subscriptionIds;
|
||||
}
|
||||
|
||||
//
|
||||
// Enqueue strategy drops old messages when we are at full capacity
|
||||
//
|
||||
|
@ -163,6 +163,9 @@ namespace ix
|
||||
/// Tells whether the internal queue is empty or not
|
||||
bool isQueueEmpty();
|
||||
|
||||
/// Retrieve all subscriptions ids
|
||||
std::vector<std::string> getSubscriptionsIds();
|
||||
|
||||
///
|
||||
/// Member variables
|
||||
///
|
||||
|
@ -7,12 +7,14 @@ set (IXSNAKE_SOURCES
|
||||
ixsnake/IXSnakeServer.cpp
|
||||
ixsnake/IXSnakeProtocol.cpp
|
||||
ixsnake/IXAppConfig.cpp
|
||||
ixsnake/IXStreamSql.cpp
|
||||
)
|
||||
|
||||
set (IXSNAKE_HEADERS
|
||||
ixsnake/IXSnakeServer.h
|
||||
ixsnake/IXSnakeProtocol.h
|
||||
ixsnake/IXAppConfig.h
|
||||
ixsnake/IXStreamSql.h
|
||||
)
|
||||
|
||||
add_library(ixsnake STATIC
|
||||
|
@ -26,6 +26,12 @@ namespace snake
|
||||
}
|
||||
|
||||
auto roles = appConfig.apps[appkey]["roles"];
|
||||
if (roles.count(role) == 0)
|
||||
{
|
||||
std::cerr << "Missing role " << role << std::endl;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
auto channel = roles[role]["secret"];
|
||||
return channel;
|
||||
}
|
||||
|
@ -33,6 +33,9 @@ namespace snake
|
||||
// Misc
|
||||
bool verbose;
|
||||
bool disablePong;
|
||||
|
||||
// If non empty, every published message gets republished to a given channel
|
||||
std::string republishChannel;
|
||||
};
|
||||
|
||||
bool isAppKeyValid(const AppConfig& appConfig, std::string appkey);
|
||||
|
@ -7,15 +7,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <ixredis/IXRedisClient.h>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <ixwebsocket/IXConnectionState.h>
|
||||
#include <string>
|
||||
#include "IXStreamSql.h"
|
||||
|
||||
namespace snake
|
||||
{
|
||||
class SnakeConnectionState : public ix::ConnectionState
|
||||
{
|
||||
public:
|
||||
virtual ~SnakeConnectionState()
|
||||
{
|
||||
stopSubScriptionThread();
|
||||
}
|
||||
|
||||
std::string getNonce()
|
||||
{
|
||||
return _nonce;
|
||||
@ -30,6 +36,7 @@ namespace snake
|
||||
{
|
||||
return _appkey;
|
||||
}
|
||||
|
||||
void setAppkey(const std::string& appkey)
|
||||
{
|
||||
_appkey = appkey;
|
||||
@ -39,6 +46,7 @@ namespace snake
|
||||
{
|
||||
return _role;
|
||||
}
|
||||
|
||||
void setRole(const std::string& role)
|
||||
{
|
||||
_role = role;
|
||||
@ -49,7 +57,24 @@ namespace snake
|
||||
return _redisClient;
|
||||
}
|
||||
|
||||
std::future<void> fut;
|
||||
void stopSubScriptionThread()
|
||||
{
|
||||
if (subscriptionThread.joinable())
|
||||
{
|
||||
subscriptionRedisClient.stop();
|
||||
subscriptionThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
// We could make those accessible through methods
|
||||
std::thread subscriptionThread;
|
||||
std::string appChannel;
|
||||
std::string subscriptionId;
|
||||
uint64_t id;
|
||||
std::unique_ptr<StreamSql> streamSql;
|
||||
ix::RedisClient subscriptionRedisClient;
|
||||
ix::RedisClient::OnRedisSubscribeResponseCallback onRedisSubscribeResponseCallback;
|
||||
ix::RedisClient::OnRedisSubscribeCallback onRedisSubscribeCallback;
|
||||
|
||||
private:
|
||||
std::string _nonce;
|
||||
|
@ -18,21 +18,22 @@
|
||||
namespace snake
|
||||
{
|
||||
void handleError(const std::string& action,
|
||||
std::shared_ptr<ix::WebSocket> ws,
|
||||
nlohmann::json pdu,
|
||||
ix::WebSocket& ws,
|
||||
uint64_t pduId,
|
||||
const std::string& errMsg)
|
||||
{
|
||||
std::string actionError(action);
|
||||
actionError += "/error";
|
||||
|
||||
nlohmann::json response = {
|
||||
{"action", actionError}, {"id", pdu.value("id", 1)}, {"body", {{"reason", errMsg}}}};
|
||||
ws->sendText(response.dump());
|
||||
{"action", actionError}, {"id", pduId}, {"body", {{"reason", errMsg}}}};
|
||||
ws.sendText(response.dump());
|
||||
}
|
||||
|
||||
void handleHandshake(std::shared_ptr<SnakeConnectionState> state,
|
||||
std::shared_ptr<ix::WebSocket> ws,
|
||||
const nlohmann::json& pdu)
|
||||
ix::WebSocket& ws,
|
||||
const nlohmann::json& pdu,
|
||||
uint64_t pduId)
|
||||
{
|
||||
std::string role = pdu["body"]["data"]["role"];
|
||||
|
||||
@ -41,7 +42,7 @@ namespace snake
|
||||
|
||||
nlohmann::json response = {
|
||||
{"action", "auth/handshake/ok"},
|
||||
{"id", pdu.value("id", 1)},
|
||||
{"id", pduId},
|
||||
{"body",
|
||||
{
|
||||
{"data", {{"nonce", state->getNonce()}, {"connection_id", state->getId()}}},
|
||||
@ -49,13 +50,14 @@ namespace snake
|
||||
|
||||
auto serializedResponse = response.dump();
|
||||
|
||||
ws->sendText(serializedResponse);
|
||||
ws.sendText(serializedResponse);
|
||||
}
|
||||
|
||||
void handleAuth(std::shared_ptr<SnakeConnectionState> state,
|
||||
std::shared_ptr<ix::WebSocket> ws,
|
||||
ix::WebSocket& ws,
|
||||
const AppConfig& appConfig,
|
||||
const nlohmann::json& pdu)
|
||||
const nlohmann::json& pdu,
|
||||
uint64_t pduId)
|
||||
{
|
||||
auto secret = getRoleSecret(appConfig, state->appkey(), state->role());
|
||||
|
||||
@ -63,9 +65,9 @@ namespace snake
|
||||
{
|
||||
nlohmann::json response = {
|
||||
{"action", "auth/authenticate/error"},
|
||||
{"id", pdu.value("id", 1)},
|
||||
{"id", pduId},
|
||||
{"body", {{"error", "authentication_failed"}, {"reason", "invalid secret"}}}};
|
||||
ws->sendText(response.dump());
|
||||
ws.sendText(response.dump());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -79,19 +81,21 @@ namespace snake
|
||||
{"action", "auth/authenticate/error"},
|
||||
{"id", pdu.value("id", 1)},
|
||||
{"body", {{"error", "authentication_failed"}, {"reason", "invalid hash"}}}};
|
||||
ws->sendText(response.dump());
|
||||
ws.sendText(response.dump());
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json response = {
|
||||
{"action", "auth/authenticate/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}};
|
||||
|
||||
ws->sendText(response.dump());
|
||||
ws.sendText(response.dump());
|
||||
}
|
||||
|
||||
void handlePublish(std::shared_ptr<SnakeConnectionState> state,
|
||||
std::shared_ptr<ix::WebSocket> ws,
|
||||
const nlohmann::json& pdu)
|
||||
ix::WebSocket& ws,
|
||||
const AppConfig& appConfig,
|
||||
const nlohmann::json& pdu,
|
||||
uint64_t pduId)
|
||||
{
|
||||
std::vector<std::string> channels;
|
||||
|
||||
@ -111,10 +115,16 @@ namespace snake
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Missing channels or channel field in publish data";
|
||||
handleError("rtm/publish", ws, pdu, ss.str());
|
||||
handleError("rtm/publish", ws, pduId, ss.str());
|
||||
return;
|
||||
}
|
||||
|
||||
// add an extra channel if the config has one specified
|
||||
if (!appConfig.republishChannel.empty())
|
||||
{
|
||||
channels.push_back(appConfig.republishChannel);
|
||||
}
|
||||
|
||||
for (auto&& channel : channels)
|
||||
{
|
||||
std::stringstream ss;
|
||||
@ -125,7 +135,7 @@ namespace snake
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Cannot publish to redis host " << errMsg;
|
||||
handleError("rtm/publish", ws, pdu, ss.str());
|
||||
handleError("rtm/publish", ws, pduId, ss.str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -133,26 +143,27 @@ namespace snake
|
||||
nlohmann::json response = {
|
||||
{"action", "rtm/publish/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}};
|
||||
|
||||
ws->sendText(response.dump());
|
||||
ws.sendText(response.dump());
|
||||
}
|
||||
|
||||
//
|
||||
// FIXME: this is not cancellable. We should be able to cancel the redis subscription
|
||||
//
|
||||
void handleRedisSubscription(std::shared_ptr<SnakeConnectionState> state,
|
||||
std::shared_ptr<ix::WebSocket> ws,
|
||||
const AppConfig& appConfig,
|
||||
const nlohmann::json& pdu)
|
||||
void handleSubscribe(std::shared_ptr<SnakeConnectionState> state,
|
||||
ix::WebSocket& ws,
|
||||
const AppConfig& appConfig,
|
||||
const nlohmann::json& pdu,
|
||||
uint64_t pduId)
|
||||
{
|
||||
std::string channel = pdu["body"]["channel"];
|
||||
std::string subscriptionId = channel;
|
||||
state->subscriptionId = channel;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << state->appkey() << "::" << channel;
|
||||
|
||||
std::string appChannel(ss.str());
|
||||
state->appChannel = ss.str();
|
||||
|
||||
ix::RedisClient redisClient;
|
||||
ix::RedisClient& redisClient = state->subscriptionRedisClient;
|
||||
int port = appConfig.redisPort;
|
||||
|
||||
auto urls = appConfig.redisHosts;
|
||||
@ -163,7 +174,7 @@ namespace snake
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Cannot connect to redis host " << hostname << ":" << port;
|
||||
handleError("rtm/subscribe", ws, pdu, ss.str());
|
||||
handleError("rtm/subscribe", ws, pduId, ss.str());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -175,80 +186,90 @@ namespace snake
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Cannot authenticated to redis";
|
||||
handleError("rtm/subscribe", ws, pdu, ss.str());
|
||||
handleError("rtm/subscribe", ws, pduId, ss.str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int id = 0;
|
||||
auto callback = [ws, &id, &subscriptionId](const std::string& messageStr) {
|
||||
std::string filterStr;
|
||||
if (pdu["body"].find("filter") != pdu["body"].end())
|
||||
{
|
||||
std::string filterStr = pdu["body"]["filter"];
|
||||
}
|
||||
state->streamSql = std::make_unique<StreamSql>(filterStr);
|
||||
state->id = 0;
|
||||
state->onRedisSubscribeCallback = [&ws, state](const std::string& messageStr) {
|
||||
auto msg = nlohmann::json::parse(messageStr);
|
||||
|
||||
msg = msg["body"]["message"];
|
||||
|
||||
if (state->streamSql->valid() && !state->streamSql->match(msg))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json response = {
|
||||
{"action", "rtm/subscription/data"},
|
||||
{"id", id++},
|
||||
{"id", state->id++},
|
||||
{"body",
|
||||
{{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}};
|
||||
{{"subscription_id", state->subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}};
|
||||
|
||||
ws->sendText(response.dump());
|
||||
ws.sendText(response.dump());
|
||||
};
|
||||
|
||||
auto responseCallback = [ws, pdu, &subscriptionId](const std::string& redisResponse) {
|
||||
state->onRedisSubscribeResponseCallback = [&ws, state, pduId](const std::string& redisResponse) {
|
||||
std::stringstream ss;
|
||||
ss << "Redis Response: " << redisResponse << "...";
|
||||
ix::CoreLogger::log(ss.str().c_str());
|
||||
|
||||
// Success
|
||||
nlohmann::json response = {{"action", "rtm/subscribe/ok"},
|
||||
{"id", pdu.value("id", 1)},
|
||||
{"body", {{"subscription_id", subscriptionId}}}};
|
||||
ws->sendText(response.dump());
|
||||
{"id", pduId},
|
||||
{"body", {{"subscription_id", state->subscriptionId}}}};
|
||||
ws.sendText(response.dump());
|
||||
};
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Subscribing to " << appChannel << "...";
|
||||
ss << "Subscribing to " << state->appChannel << "...";
|
||||
ix::CoreLogger::log(ss.str().c_str());
|
||||
}
|
||||
|
||||
if (!redisClient.subscribe(appChannel, responseCallback, callback))
|
||||
auto subscription = [&redisClient, state, &ws, pduId]
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Error subscribing to channel " << appChannel;
|
||||
handleError("rtm/subscribe", ws, pdu, ss.str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!redisClient.subscribe(state->appChannel,
|
||||
state->onRedisSubscribeResponseCallback,
|
||||
state->onRedisSubscribeCallback))
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Error subscribing to channel " << state->appChannel;
|
||||
handleError("rtm/subscribe", ws, pduId, ss.str());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
void handleSubscribe(std::shared_ptr<SnakeConnectionState> state,
|
||||
std::shared_ptr<ix::WebSocket> ws,
|
||||
const AppConfig& appConfig,
|
||||
const nlohmann::json& pdu)
|
||||
{
|
||||
state->fut =
|
||||
std::async(std::launch::async, handleRedisSubscription, state, ws, appConfig, pdu);
|
||||
state->subscriptionThread = std::thread(subscription);
|
||||
}
|
||||
|
||||
void handleUnSubscribe(std::shared_ptr<SnakeConnectionState> state,
|
||||
std::shared_ptr<ix::WebSocket> ws,
|
||||
const nlohmann::json& pdu)
|
||||
ix::WebSocket& ws,
|
||||
const nlohmann::json& pdu,
|
||||
uint64_t pduId)
|
||||
{
|
||||
// extract subscription_id
|
||||
auto body = pdu["body"];
|
||||
auto subscriptionId = body["subscription_id"];
|
||||
|
||||
state->redisClient().stop();
|
||||
state->stopSubScriptionThread();
|
||||
|
||||
nlohmann::json response = {{"action", "rtm/unsubscribe/ok"},
|
||||
{"id", pdu.value("id", 1)},
|
||||
{"id", pduId},
|
||||
{"body", {{"subscription_id", subscriptionId}}}};
|
||||
ws->sendText(response.dump());
|
||||
ws.sendText(response.dump());
|
||||
}
|
||||
|
||||
void processCobraMessage(std::shared_ptr<SnakeConnectionState> state,
|
||||
std::shared_ptr<ix::WebSocket> ws,
|
||||
ix::WebSocket& ws,
|
||||
const AppConfig& appConfig,
|
||||
const std::string& str)
|
||||
{
|
||||
@ -263,31 +284,32 @@ namespace snake
|
||||
ss << "malformed json pdu: " << e.what() << " -> " << str << "";
|
||||
|
||||
nlohmann::json response = {{"body", {{"error", "invalid_json"}, {"reason", ss.str()}}}};
|
||||
ws->sendText(response.dump());
|
||||
ws.sendText(response.dump());
|
||||
return;
|
||||
}
|
||||
|
||||
auto action = pdu["action"];
|
||||
uint64_t pduId = pdu.value("id", 1);
|
||||
|
||||
if (action == "auth/handshake")
|
||||
{
|
||||
handleHandshake(state, ws, pdu);
|
||||
handleHandshake(state, ws, pdu, pduId);
|
||||
}
|
||||
else if (action == "auth/authenticate")
|
||||
{
|
||||
handleAuth(state, ws, appConfig, pdu);
|
||||
handleAuth(state, ws, appConfig, pdu, pduId);
|
||||
}
|
||||
else if (action == "rtm/publish")
|
||||
{
|
||||
handlePublish(state, ws, pdu);
|
||||
handlePublish(state, ws, appConfig, pdu, pduId);
|
||||
}
|
||||
else if (action == "rtm/subscribe")
|
||||
{
|
||||
handleSubscribe(state, ws, appConfig, pdu);
|
||||
handleSubscribe(state, ws, appConfig, pdu, pduId);
|
||||
}
|
||||
else if (action == "rtm/unsubscribe")
|
||||
{
|
||||
handleUnSubscribe(state, ws, pdu);
|
||||
handleUnSubscribe(state, ws, pdu, pduId);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -20,7 +20,7 @@ namespace snake
|
||||
struct AppConfig;
|
||||
|
||||
void processCobraMessage(std::shared_ptr<SnakeConnectionState> state,
|
||||
std::shared_ptr<ix::WebSocket> ws,
|
||||
ix::WebSocket& ws,
|
||||
const AppConfig& appConfig,
|
||||
const std::string& str);
|
||||
} // namespace snake
|
||||
|
@ -59,68 +59,68 @@ namespace snake
|
||||
};
|
||||
_server.setConnectionStateFactory(factory);
|
||||
|
||||
_server.setOnConnectionCallback(
|
||||
[this](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ix::ConnectionState> connectionState,
|
||||
std::unique_ptr<ix::ConnectionInfo> connectionInfo) {
|
||||
_server.setOnClientMessageCallback(
|
||||
[this](std::shared_ptr<ix::ConnectionState> connectionState,
|
||||
ix::ConnectionInfo& connectionInfo,
|
||||
ix::WebSocket& webSocket,
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
auto state = std::dynamic_pointer_cast<SnakeConnectionState>(connectionState);
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
auto remoteIp = connectionInfo.remoteIp;
|
||||
|
||||
webSocket->setOnMessageCallback(
|
||||
[this, webSocket, state, remoteIp](const ix::WebSocketMessagePtr& msg) {
|
||||
std::stringstream ss;
|
||||
ix::LogLevel logLevel = ix::LogLevel::Debug;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
ss << "New connection" << std::endl;
|
||||
ss << "remote ip: " << remoteIp << std::endl;
|
||||
ss << "id: " << state->getId() << std::endl;
|
||||
ss << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
ss << "Headers:" << std::endl;
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
ss << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
std::stringstream ss;
|
||||
ss << "[" << state->getId() << "] ";
|
||||
|
||||
std::string appkey = parseAppKey(msg->openInfo.uri);
|
||||
state->setAppkey(appkey);
|
||||
ix::LogLevel logLevel = ix::LogLevel::Debug;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
ss << "New connection" << std::endl;
|
||||
ss << "remote ip: " << remoteIp << std::endl;
|
||||
ss << "id: " << state->getId() << std::endl;
|
||||
ss << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
ss << "Headers:" << std::endl;
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
ss << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
|
||||
// Connect to redis first
|
||||
if (!state->redisClient().connect(_appConfig.redisHosts[0],
|
||||
_appConfig.redisPort))
|
||||
{
|
||||
ss << "Cannot connect to redis host" << std::endl;
|
||||
logLevel = ix::LogLevel::Error;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
ss << "Closed connection"
|
||||
<< " code " << msg->closeInfo.code << " reason "
|
||||
<< msg->closeInfo.reason << std::endl;
|
||||
}
|
||||
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;
|
||||
logLevel = ix::LogLevel::Error;
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
||||
{
|
||||
ss << "Received message fragment" << std::endl;
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
ss << "Received " << msg->wireSize << " bytes" << std::endl;
|
||||
processCobraMessage(state, webSocket, _appConfig, msg->str);
|
||||
}
|
||||
std::string appkey = parseAppKey(msg->openInfo.uri);
|
||||
state->setAppkey(appkey);
|
||||
|
||||
ix::CoreLogger::log(ss.str().c_str(), logLevel);
|
||||
});
|
||||
});
|
||||
// Connect to redis first
|
||||
if (!state->redisClient().connect(_appConfig.redisHosts[0],
|
||||
_appConfig.redisPort))
|
||||
{
|
||||
ss << "Cannot connect to redis host" << std::endl;
|
||||
logLevel = ix::LogLevel::Error;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
ss << "Closed connection"
|
||||
<< " code " << msg->closeInfo.code << " reason "
|
||||
<< msg->closeInfo.reason << std::endl;
|
||||
}
|
||||
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;
|
||||
logLevel = ix::LogLevel::Error;
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
||||
{
|
||||
ss << "Received message fragment" << std::endl;
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
ss << "Received " << msg->wireSize << " bytes" << " " << msg->str << std::endl;
|
||||
processCobraMessage(state, webSocket, _appConfig, msg->str);
|
||||
}
|
||||
|
||||
ix::CoreLogger::log(ss.str().c_str(), logLevel);
|
||||
});
|
||||
|
||||
auto res = _server.listen();
|
||||
if (!res.first)
|
||||
|
63
ixsnake/ixsnake/IXStreamSql.cpp
Normal file
63
ixsnake/ixsnake/IXStreamSql.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* IXStreamSql.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*
|
||||
* Super simple hacked up version of a stream sql expression,
|
||||
* that only supports non nested field evaluation
|
||||
*/
|
||||
|
||||
#include "IXStreamSql.h"
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace snake
|
||||
{
|
||||
StreamSql::StreamSql(const std::string& sqlFilter)
|
||||
: _valid(false)
|
||||
{
|
||||
std::string token;
|
||||
std::stringstream tokenStream(sqlFilter);
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
// Split by ' '
|
||||
while (std::getline(tokenStream, token, ' '))
|
||||
{
|
||||
tokens.push_back(token);
|
||||
}
|
||||
|
||||
_valid = tokens.size() == 8;
|
||||
if (!_valid) return;
|
||||
|
||||
_field = tokens[5];
|
||||
_operator = tokens[6];
|
||||
_value = tokens[7];
|
||||
|
||||
// remove single quotes
|
||||
_value = _value.substr(1, _value.size() - 2);
|
||||
|
||||
if (_operator == "LIKE")
|
||||
{
|
||||
_value = _value.substr(1, _value.size() - 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool StreamSql::valid() const
|
||||
{
|
||||
return _valid;
|
||||
}
|
||||
|
||||
bool StreamSql::match(const nlohmann::json& msg)
|
||||
{
|
||||
if (!_valid) return false;
|
||||
|
||||
if (msg.find(_field) == msg.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string value = msg[_field];
|
||||
return value == _value;
|
||||
}
|
||||
|
||||
} // namespace snake
|
29
ixsnake/ixsnake/IXStreamSql.h
Normal file
29
ixsnake/ixsnake/IXStreamSql.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* IXStreamSql.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
namespace snake
|
||||
{
|
||||
class StreamSql
|
||||
{
|
||||
public:
|
||||
StreamSql(const std::string& sqlFilter = std::string());
|
||||
~StreamSql() = default;
|
||||
|
||||
bool match(const nlohmann::json& msg);
|
||||
bool valid() const;
|
||||
|
||||
private:
|
||||
std::string _field;
|
||||
std::string _operator;
|
||||
std::string _value;
|
||||
bool _valid;
|
||||
};
|
||||
}
|
@ -16,7 +16,10 @@
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@ -174,11 +177,13 @@ namespace ix
|
||||
ss << verb << " " << path << " HTTP/1.1\r\n";
|
||||
ss << "Host: " << host << "\r\n";
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
if (args->compress)
|
||||
{
|
||||
ss << "Accept-Encoding: gzip"
|
||||
<< "\r\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
// Append extra headers
|
||||
for (auto&& it : args->extraHeaders)
|
||||
@ -495,6 +500,7 @@ namespace ix
|
||||
|
||||
downloadSize = payload.size();
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
// If the content was compressed with gzip, decode it
|
||||
if (headers["Content-Encoding"] == "gzip")
|
||||
{
|
||||
@ -513,6 +519,7 @@ namespace ix
|
||||
}
|
||||
payload = decompressedPayload;
|
||||
}
|
||||
#endif
|
||||
|
||||
return std::make_shared<HttpResponse>(code,
|
||||
description,
|
||||
@ -672,6 +679,7 @@ namespace ix
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
bool HttpClient::gzipInflate(const std::string& in, std::string& out)
|
||||
{
|
||||
z_stream inflateState;
|
||||
@ -716,6 +724,7 @@ namespace ix
|
||||
inflateEnd(&inflateState);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args)
|
||||
{
|
||||
|
@ -90,7 +90,9 @@ namespace ix
|
||||
private:
|
||||
void log(const std::string& msg, HttpRequestArgsPtr args);
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
bool gzipInflate(const std::string& in, std::string& out);
|
||||
#endif
|
||||
|
||||
// Async API background thread runner
|
||||
void run();
|
||||
|
@ -13,7 +13,10 @@
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -41,6 +44,7 @@ namespace
|
||||
return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
|
||||
}
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
std::string gzipCompress(const std::string& str)
|
||||
{
|
||||
z_stream zs; // z_stream is zlib's control structure
|
||||
@ -83,6 +87,7 @@ namespace
|
||||
|
||||
return outstring;
|
||||
}
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
namespace ix
|
||||
@ -125,9 +130,8 @@ namespace ix
|
||||
|
||||
if (std::get<0>(ret))
|
||||
{
|
||||
auto response = _onConnectionCallback(std::get<2>(ret),
|
||||
connectionState,
|
||||
std::move(connectionInfo));
|
||||
auto response =
|
||||
_onConnectionCallback(std::get<2>(ret), connectionState, std::move(connectionInfo));
|
||||
if (!Http::sendResponse(response, socket))
|
||||
{
|
||||
logError("Cannot send response");
|
||||
@ -169,12 +173,14 @@ namespace ix
|
||||
|
||||
std::string content = res.second;
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
std::string acceptEncoding = request->headers["Accept-encoding"];
|
||||
if (acceptEncoding == "*" || acceptEncoding.find("gzip") != std::string::npos)
|
||||
{
|
||||
content = gzipCompress(content);
|
||||
headers["Content-Encoding"] = "gzip";
|
||||
}
|
||||
#endif
|
||||
|
||||
// Log request
|
||||
std::stringstream ss;
|
||||
@ -203,10 +209,9 @@ namespace ix
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
|
||||
//
|
||||
setOnConnectionCallback(
|
||||
[this,
|
||||
redirectUrl](HttpRequestPtr request,
|
||||
std::shared_ptr<ConnectionState> /*connectionState*/,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr {
|
||||
[this, redirectUrl](HttpRequestPtr request,
|
||||
std::shared_ptr<ConnectionState> /*connectionState*/,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr {
|
||||
WebSocketHttpHeaders headers;
|
||||
headers["Server"] = userAgent();
|
||||
|
||||
|
@ -5,8 +5,10 @@
|
||||
*/
|
||||
|
||||
//
|
||||
// On macOS we use UNIX pipes to wake up select.
|
||||
// On UNIX we use pipes to wake up select. There is no way to do that
|
||||
// on Windows so this file is compiled out on Windows.
|
||||
//
|
||||
#ifndef _WIN32
|
||||
|
||||
#include "IXSelectInterruptPipe.h"
|
||||
|
||||
@ -144,3 +146,5 @@ namespace ix
|
||||
return _fildes[kPipeReadIndex];
|
||||
}
|
||||
} // namespace ix
|
||||
|
||||
#endif // !_WIN32
|
||||
|
81
ixwebsocket/IXSetThreadName.cpp
Normal file
81
ixwebsocket/IXSetThreadName.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* IXSetThreadName.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2018 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
#include "IXSetThreadName.h"
|
||||
|
||||
// unix systems
|
||||
#if defined(__APPLE__) || defined(__linux__) || defined(BSD)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
// freebsd needs this header as well
|
||||
#if defined(BSD)
|
||||
#include <pthread_np.h>
|
||||
#endif
|
||||
|
||||
// Windows
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace ix
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
|
||||
#pragma pack(push, 8)
|
||||
typedef struct tagTHREADNAME_INFO
|
||||
{
|
||||
DWORD dwType; // Must be 0x1000.
|
||||
LPCSTR szName; // Pointer to name (in user addr space).
|
||||
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
||||
DWORD dwFlags; // Reserved for future use, must be zero.
|
||||
} THREADNAME_INFO;
|
||||
#pragma pack(pop)
|
||||
|
||||
void SetThreadName(DWORD dwThreadID, const char* threadName)
|
||||
{
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = threadName;
|
||||
info.dwThreadID = dwThreadID;
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try
|
||||
{
|
||||
RaiseException(
|
||||
MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*) &info);
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void setThreadName(const std::string& name)
|
||||
{
|
||||
#if defined(__APPLE__)
|
||||
//
|
||||
// Apple reserves 16 bytes for its thread names
|
||||
// Notice that the Apple version of pthread_setname_np
|
||||
// does not take a pthread_t argument
|
||||
//
|
||||
pthread_setname_np(name.substr(0, 63).c_str());
|
||||
#elif defined(__linux__)
|
||||
//
|
||||
// Linux only reserves 16 bytes for its thread names
|
||||
// See prctl and PR_SET_NAME property in
|
||||
// http://man7.org/linux/man-pages/man2/prctl.2.html
|
||||
//
|
||||
pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
|
||||
#elif defined(_WIN32)
|
||||
SetThreadName(-1, name.c_str());
|
||||
#elif defined(BSD)
|
||||
pthread_set_name_np(pthread_self(), name.substr(0, 15).c_str());
|
||||
#else
|
||||
// ... assert here ?
|
||||
#endif
|
||||
}
|
||||
} // namespace ix
|
@ -22,7 +22,7 @@ namespace ix
|
||||
const int SocketServer::kDefaultPort(8080);
|
||||
const std::string SocketServer::kDefaultHost("127.0.0.1");
|
||||
const int SocketServer::kDefaultTcpBacklog(5);
|
||||
const size_t SocketServer::kDefaultMaxConnections(32);
|
||||
const size_t SocketServer::kDefaultMaxConnections(128);
|
||||
const int SocketServer::kDefaultAddressFamily(AF_INET);
|
||||
|
||||
SocketServer::SocketServer(
|
||||
@ -379,10 +379,13 @@ namespace ix
|
||||
|
||||
// Launch the handleConnection work asynchronously in its own thread.
|
||||
std::lock_guard<std::mutex> lock(_connectionsThreadsMutex);
|
||||
_connectionsThreads.push_back(std::make_pair(
|
||||
connectionState,
|
||||
std::thread(
|
||||
&SocketServer::handleConnection, this, std::move(socket), connectionState, std::move(connectionInfo))));
|
||||
_connectionsThreads.push_back(
|
||||
std::make_pair(connectionState,
|
||||
std::thread(&SocketServer::handleConnection,
|
||||
this,
|
||||
std::move(socket),
|
||||
connectionState,
|
||||
std::move(connectionInfo))));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,9 @@
|
||||
|
||||
#include "IXWebSocketVersion.h"
|
||||
#include <sstream>
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
// Platform name
|
||||
#if defined(_WIN32)
|
||||
@ -77,8 +79,10 @@ namespace ix
|
||||
ss << " nossl";
|
||||
#endif
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
// Zlib version
|
||||
ss << " zlib " << ZLIB_VERSION;
|
||||
#endif
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ namespace ix
|
||||
WebSocket::~WebSocket()
|
||||
{
|
||||
stop();
|
||||
_ws.setOnCloseCallback(nullptr);
|
||||
}
|
||||
|
||||
void WebSocket::setUrl(const std::string& url)
|
||||
|
@ -28,21 +28,26 @@ namespace ix
|
||||
WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor()
|
||||
: _compressBufferSize(kBufferSize)
|
||||
{
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
memset(&_deflateState, 0, sizeof(_deflateState));
|
||||
|
||||
_deflateState.zalloc = Z_NULL;
|
||||
_deflateState.zfree = Z_NULL;
|
||||
_deflateState.opaque = Z_NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
WebSocketPerMessageDeflateCompressor::~WebSocketPerMessageDeflateCompressor()
|
||||
{
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
deflateEnd(&_deflateState);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
|
||||
bool clientNoContextTakeOver)
|
||||
{
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
int ret = deflateInit2(&_deflateState,
|
||||
Z_DEFAULT_COMPRESSION,
|
||||
Z_DEFLATED,
|
||||
@ -57,6 +62,9 @@ namespace ix
|
||||
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -96,6 +104,7 @@ namespace ix
|
||||
template<typename T, typename S>
|
||||
bool WebSocketPerMessageDeflateCompressor::compressData(const T& in, S& out)
|
||||
{
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
//
|
||||
// 7.2.1. Compression
|
||||
//
|
||||
@ -152,6 +161,9 @@ namespace ix
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
@ -160,6 +172,7 @@ namespace ix
|
||||
WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor()
|
||||
: _compressBufferSize(kBufferSize)
|
||||
{
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
memset(&_inflateState, 0, sizeof(_inflateState));
|
||||
|
||||
_inflateState.zalloc = Z_NULL;
|
||||
@ -167,16 +180,20 @@ namespace ix
|
||||
_inflateState.opaque = Z_NULL;
|
||||
_inflateState.avail_in = 0;
|
||||
_inflateState.next_in = Z_NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
WebSocketPerMessageDeflateDecompressor::~WebSocketPerMessageDeflateDecompressor()
|
||||
{
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
inflateEnd(&_inflateState);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
|
||||
bool clientNoContextTakeOver)
|
||||
{
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
int ret = inflateInit2(&_inflateState, -1 * inflateBits);
|
||||
|
||||
if (ret != Z_OK) return false;
|
||||
@ -186,10 +203,14 @@ namespace ix
|
||||
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in, std::string& out)
|
||||
{
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
//
|
||||
// 7.2.2. Decompression
|
||||
//
|
||||
@ -226,5 +247,8 @@ namespace ix
|
||||
} while (_inflateState.avail_out == 0);
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
} // namespace ix
|
||||
|
@ -6,7 +6,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
#include "zlib.h"
|
||||
#endif
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -34,7 +36,10 @@ namespace ix
|
||||
int _flush;
|
||||
size_t _compressBufferSize;
|
||||
std::unique_ptr<unsigned char[]> _compressBuffer;
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
z_stream _deflateState;
|
||||
#endif
|
||||
};
|
||||
|
||||
class WebSocketPerMessageDeflateDecompressor
|
||||
@ -50,7 +55,10 @@ namespace ix
|
||||
int _flush;
|
||||
size_t _compressBufferSize;
|
||||
std::unique_ptr<unsigned char[]> _compressBuffer;
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
z_stream _inflateState;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ix
|
||||
|
@ -61,6 +61,7 @@ namespace ix
|
||||
_clientMaxWindowBits = kDefaultClientMaxWindowBits;
|
||||
_serverMaxWindowBits = kDefaultServerMaxWindowBits;
|
||||
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
// Split by ;
|
||||
std::string token;
|
||||
std::stringstream tokenStream(extension);
|
||||
@ -112,6 +113,7 @@ namespace ix
|
||||
sanitizeClientMaxWindowBits();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebSocketPerMessageDeflateOptions::sanitizeClientMaxWindowBits()
|
||||
@ -126,6 +128,7 @@ namespace ix
|
||||
|
||||
std::string WebSocketPerMessageDeflateOptions::generateHeader()
|
||||
{
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
std::stringstream ss;
|
||||
ss << "Sec-WebSocket-Extensions: permessage-deflate";
|
||||
|
||||
@ -138,11 +141,18 @@ namespace ix
|
||||
ss << "\r\n";
|
||||
|
||||
return ss.str();
|
||||
#else
|
||||
return std::string();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool WebSocketPerMessageDeflateOptions::enabled() const
|
||||
{
|
||||
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||
return _enabled;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool WebSocketPerMessageDeflateOptions::getClientNoContextTakeover() const
|
||||
|
123
ixwebsocket/IXWebSocketProxyServer.cpp
Normal file
123
ixwebsocket/IXWebSocketProxyServer.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* IXWebSocketProxyServer.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXWebSocketProxyServer.h"
|
||||
|
||||
#include "IXWebSocketServer.h"
|
||||
#include <sstream>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class ProxyConnectionState : public ix::ConnectionState
|
||||
{
|
||||
public:
|
||||
ProxyConnectionState()
|
||||
: _connected(false)
|
||||
{
|
||||
}
|
||||
|
||||
ix::WebSocket& webSocket()
|
||||
{
|
||||
return _serverWebSocket;
|
||||
}
|
||||
|
||||
bool isConnected()
|
||||
{
|
||||
return _connected;
|
||||
}
|
||||
|
||||
void setConnected()
|
||||
{
|
||||
_connected = true;
|
||||
}
|
||||
|
||||
private:
|
||||
ix::WebSocket _serverWebSocket;
|
||||
bool _connected;
|
||||
};
|
||||
|
||||
int websocket_proxy_server_main(int port,
|
||||
const std::string& hostname,
|
||||
const ix::SocketTLSOptions& tlsOptions,
|
||||
const std::string& remoteUrl,
|
||||
bool /*verbose*/)
|
||||
{
|
||||
ix::WebSocketServer server(port, hostname);
|
||||
server.setTLSOptions(tlsOptions);
|
||||
|
||||
auto factory = []() -> std::shared_ptr<ix::ConnectionState> {
|
||||
return std::make_shared<ProxyConnectionState>();
|
||||
};
|
||||
server.setConnectionStateFactory(factory);
|
||||
|
||||
server.setOnConnectionCallback([remoteUrl](std::weak_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState);
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
|
||||
// Server connection
|
||||
state->webSocket().setOnMessageCallback(
|
||||
[webSocket, state, remoteIp](const WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
state->setTerminated();
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
auto ws = webSocket.lock();
|
||||
if (ws)
|
||||
{
|
||||
ws->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Client connection
|
||||
auto ws = webSocket.lock();
|
||||
if (ws)
|
||||
{
|
||||
ws->setOnMessageCallback([state, remoteUrl](const WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
// Connect to the 'real' server
|
||||
std::string url(remoteUrl);
|
||||
url += msg->openInfo.uri;
|
||||
state->webSocket().setUrl(url);
|
||||
state->webSocket().disableAutomaticReconnection();
|
||||
state->webSocket().start();
|
||||
|
||||
// we should sleep here for a bit until we've established the
|
||||
// connection with the remote server
|
||||
while (state->webSocket().getReadyState() != ReadyState::Open)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
state->webSocket().close(msg->closeInfo.code, msg->closeInfo.reason);
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
state->webSocket().send(msg->str, msg->binary);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
server.start();
|
||||
server.wait();
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace ix
|
20
ixwebsocket/IXWebSocketProxyServer.h
Normal file
20
ixwebsocket/IXWebSocketProxyServer.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* IXWebSocketProxyServer.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "IXSocketTLSOptions.h"
|
||||
#include <cstdint>
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
int websocket_proxy_server_main(int port,
|
||||
const std::string& hostname,
|
||||
const ix::SocketTLSOptions& tlsOptions,
|
||||
const std::string& remoteUrl,
|
||||
bool verbose);
|
||||
} // namespace ix
|
@ -71,6 +71,11 @@ namespace ix
|
||||
_onConnectionCallback = callback;
|
||||
}
|
||||
|
||||
void WebSocketServer::setOnClientMessageCallback(const OnClientMessageCallback& callback)
|
||||
{
|
||||
_onClientMessageCallback = callback;
|
||||
}
|
||||
|
||||
void WebSocketServer::handleConnection(std::unique_ptr<Socket> socket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo)
|
||||
@ -78,7 +83,26 @@ namespace ix
|
||||
setThreadName("WebSocketServer::" + connectionState->getId());
|
||||
|
||||
auto webSocket = std::make_shared<WebSocket>();
|
||||
_onConnectionCallback(webSocket, connectionState, std::move(connectionInfo));
|
||||
if (_onConnectionCallback)
|
||||
{
|
||||
_onConnectionCallback(webSocket, connectionState, std::move(connectionInfo));
|
||||
}
|
||||
else if (_onClientMessageCallback)
|
||||
{
|
||||
webSocket->setOnMessageCallback(
|
||||
[this, &ws = *webSocket.get(), connectionState, &ci = *connectionInfo.get()](
|
||||
const WebSocketMessagePtr& msg) {
|
||||
_onClientMessageCallback(connectionState, ci, ws, msg);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
logError(
|
||||
"WebSocketServer Application developer error: No server callback is registerered.");
|
||||
logError("Missing call to setOnConnectionCallback or setOnClientMessageCallback.");
|
||||
connectionState->setTerminated();
|
||||
return;
|
||||
}
|
||||
|
||||
webSocket->disableAutomaticReconnection();
|
||||
|
||||
@ -112,6 +136,8 @@ namespace ix
|
||||
logError(ss.str());
|
||||
}
|
||||
|
||||
webSocket->setOnMessageCallback(nullptr);
|
||||
|
||||
// Remove this client from our client set
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_clientsMutex);
|
||||
|
@ -23,9 +23,15 @@ namespace ix
|
||||
{
|
||||
public:
|
||||
using OnConnectionCallback =
|
||||
std::function<void(std::shared_ptr<WebSocket>, std::shared_ptr<ConnectionState>,
|
||||
std::function<void(std::weak_ptr<WebSocket>,
|
||||
std::shared_ptr<ConnectionState>,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo)>;
|
||||
|
||||
using OnClientMessageCallback = std::function<void(std::shared_ptr<ConnectionState>,
|
||||
ConnectionInfo&,
|
||||
WebSocket&,
|
||||
const WebSocketMessagePtr&)>;
|
||||
|
||||
WebSocketServer(int port = SocketServer::kDefaultPort,
|
||||
const std::string& host = SocketServer::kDefaultHost,
|
||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||
@ -40,6 +46,7 @@ namespace ix
|
||||
void disablePerMessageDeflate();
|
||||
|
||||
void setOnConnectionCallback(const OnConnectionCallback& callback);
|
||||
void setOnClientMessageCallback(const OnClientMessageCallback& callback);
|
||||
|
||||
// Get all the connected clients
|
||||
std::set<std::shared_ptr<WebSocket>> getClients();
|
||||
@ -53,6 +60,7 @@ namespace ix
|
||||
bool _enablePerMessageDeflate;
|
||||
|
||||
OnConnectionCallback _onConnectionCallback;
|
||||
OnClientMessageCallback _onClientMessageCallback;
|
||||
|
||||
std::mutex _clientsMutex;
|
||||
std::set<std::shared_ptr<WebSocket>> _clients;
|
||||
|
@ -65,7 +65,6 @@ namespace ix
|
||||
, _receivedMessageCompressed(false)
|
||||
, _readyState(ReadyState::CLOSED)
|
||||
, _closeCode(WebSocketCloseConstants::kInternalErrorCode)
|
||||
, _closeReason(WebSocketCloseConstants::kInternalErrorMessage)
|
||||
, _closeWireSize(0)
|
||||
, _closeRemote(false)
|
||||
, _enablePerMessageDeflate(false)
|
||||
@ -77,6 +76,7 @@ namespace ix
|
||||
, _pingCount(0)
|
||||
, _lastSendPingTimePoint(std::chrono::steady_clock::now())
|
||||
{
|
||||
setCloseReason(WebSocketCloseConstants::kInternalErrorMessage);
|
||||
_readbuf.resize(kChunkSize);
|
||||
}
|
||||
|
||||
@ -179,10 +179,12 @@ namespace ix
|
||||
|
||||
if (readyState == ReadyState::CLOSED)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
||||
_onCloseCallback(_closeCode, _closeReason, _closeWireSize, _closeRemote);
|
||||
if (_onCloseCallback)
|
||||
{
|
||||
_onCloseCallback(_closeCode, getCloseReason(), _closeWireSize, _closeRemote);
|
||||
}
|
||||
setCloseReason(WebSocketCloseConstants::kInternalErrorMessage);
|
||||
_closeCode = WebSocketCloseConstants::kInternalErrorCode;
|
||||
_closeReason = WebSocketCloseConstants::kInternalErrorMessage;
|
||||
_closeWireSize = 0;
|
||||
_closeRemote = false;
|
||||
}
|
||||
@ -261,9 +263,10 @@ namespace ix
|
||||
{
|
||||
// compute lasting delay to wait for next ping / timeout, if at least one set
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
lastingTimeoutDelayInMs = (int) std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
int timeSinceLastPingMs = (int) std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - _lastSendPingTimePoint)
|
||||
.count();
|
||||
lastingTimeoutDelayInMs = (1000 * _pingIntervalSecs) - timeSinceLastPingMs;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -639,11 +642,7 @@ namespace ix
|
||||
{
|
||||
// we got the CLOSE frame answer from our close, so we can close the connection
|
||||
// if the code/reason are the same
|
||||
bool identicalReason;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
||||
identicalReason = _closeCode == code && _closeReason == reason;
|
||||
}
|
||||
bool identicalReason = _closeCode == code && getCloseReason() == reason;
|
||||
|
||||
if (identicalReason)
|
||||
{
|
||||
@ -797,6 +796,11 @@ namespace ix
|
||||
if (wireSize < kChunkSize)
|
||||
{
|
||||
success = sendFragment(type, true, message_begin, message_end, compress);
|
||||
|
||||
if (onProgressCallback)
|
||||
{
|
||||
onProgressCallback(0, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1081,13 +1085,10 @@ namespace ix
|
||||
{
|
||||
closeSocket();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
||||
_closeCode = code;
|
||||
_closeReason = reason;
|
||||
_closeWireSize = closeWireSize;
|
||||
_closeRemote = remote;
|
||||
}
|
||||
setCloseReason(reason);
|
||||
_closeCode = code;
|
||||
_closeWireSize = closeWireSize;
|
||||
_closeRemote = remote;
|
||||
|
||||
setReadyState(ReadyState::CLOSED);
|
||||
_requestInitCancellation = false;
|
||||
@ -1107,13 +1108,11 @@ namespace ix
|
||||
closeWireSize = reason.size();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
||||
_closeCode = code;
|
||||
_closeReason = reason;
|
||||
_closeWireSize = closeWireSize;
|
||||
_closeRemote = remote;
|
||||
}
|
||||
setCloseReason(reason);
|
||||
_closeCode = code;
|
||||
_closeWireSize = closeWireSize;
|
||||
_closeRemote = remote;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_closingTimePointMutex);
|
||||
_closingTimePoint = std::chrono::steady_clock::now();
|
||||
@ -1158,4 +1157,15 @@ namespace ix
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebSocketTransport::setCloseReason(const std::string& reason)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_closeReasonMutex);
|
||||
_closeReason = reason;
|
||||
}
|
||||
|
||||
const std::string& WebSocketTransport::getCloseReason() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_closeReasonMutex);
|
||||
return _closeReason;
|
||||
}
|
||||
} // namespace ix
|
||||
|
@ -178,11 +178,11 @@ namespace ix
|
||||
std::atomic<ReadyState> _readyState;
|
||||
|
||||
OnCloseCallback _onCloseCallback;
|
||||
uint16_t _closeCode;
|
||||
std::string _closeReason;
|
||||
size_t _closeWireSize;
|
||||
bool _closeRemote;
|
||||
mutable std::mutex _closeDataMutex;
|
||||
mutable std::mutex _closeReasonMutex;
|
||||
std::atomic<uint16_t> _closeCode;
|
||||
std::atomic<size_t> _closeWireSize;
|
||||
std::atomic<bool> _closeRemote;
|
||||
|
||||
// Data used for Per Message Deflate compression (with zlib)
|
||||
WebSocketPerMessageDeflatePtr _perMessageDeflate;
|
||||
@ -267,5 +267,8 @@ namespace ix
|
||||
void unmaskReceiveBuffer(const wsheader_type& ws);
|
||||
|
||||
std::string getMergedChunks() const;
|
||||
|
||||
void setCloseReason(const std::string& reason);
|
||||
const std::string& getCloseReason() const;
|
||||
};
|
||||
} // namespace ix
|
||||
|
@ -6,4 +6,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define IX_WEBSOCKET_VERSION "9.9.0"
|
||||
#define IX_WEBSOCKET_VERSION "10.1.1"
|
||||
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* IXSetThreadName_apple.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
#include "../IXSetThreadName.h"
|
||||
#include <pthread.h>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
void setThreadName(const std::string& name)
|
||||
{
|
||||
//
|
||||
// Apple reserves 16 bytes for its thread names
|
||||
// Notice that the Apple version of pthread_setname_np
|
||||
// does not take a pthread_t argument
|
||||
//
|
||||
pthread_setname_np(name.substr(0, 63).c_str());
|
||||
}
|
||||
} // namespace ix
|
@ -1,16 +0,0 @@
|
||||
/*
|
||||
* IXSetThreadName_freebsd.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
#include "../IXSetThreadName.h"
|
||||
#include <pthread.h>
|
||||
#include <pthread_np.h>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
void setThreadName(const std::string& name)
|
||||
{
|
||||
pthread_set_name_np(pthread_self(), name.substr(0, 15).c_str());
|
||||
}
|
||||
} // namespace ix
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* IXSetThreadName_linux.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
#include "../IXSetThreadName.h"
|
||||
#include <pthread.h>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
void setThreadName(const std::string& name)
|
||||
{
|
||||
//
|
||||
// Linux only reserves 16 bytes for its thread names
|
||||
// See prctl and PR_SET_NAME property in
|
||||
// http://man7.org/linux/man-pages/man2/prctl.2.html
|
||||
//
|
||||
pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
|
||||
}
|
||||
} // namespace ix
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* IXSetThreadName_windows.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
#include "../IXSetThreadName.h"
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
|
||||
#pragma pack(push, 8)
|
||||
typedef struct tagTHREADNAME_INFO
|
||||
{
|
||||
DWORD dwType; // Must be 0x1000.
|
||||
LPCSTR szName; // Pointer to name (in user addr space).
|
||||
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
||||
DWORD dwFlags; // Reserved for future use, must be zero.
|
||||
} THREADNAME_INFO;
|
||||
#pragma pack(pop)
|
||||
|
||||
void SetThreadName(DWORD dwThreadID, const char* threadName)
|
||||
{
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = threadName;
|
||||
info.dwThreadID = dwThreadID;
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try
|
||||
{
|
||||
RaiseException(
|
||||
MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*) &info);
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void setThreadName(const std::string& name)
|
||||
{
|
||||
SetThreadName(-1, name.c_str());
|
||||
}
|
||||
} // namespace ix
|
15
makefile
15
makefile
@ -34,7 +34,7 @@ ws:
|
||||
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
|
||||
|
||||
ws_install:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && make -j 4 install)
|
||||
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_ZLIB=0 -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
|
||||
|
||||
ws_openssl:
|
||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4)
|
||||
@ -174,7 +174,7 @@ autobahn_report:
|
||||
cp -rvf ~/sandbox/reports/clients/* ../bsergean.github.io/IXWebSocket/autobahn/
|
||||
|
||||
httpd:
|
||||
clang++ --std=c++14 --stdlib=libc++ httpd.cpp \
|
||||
clang++ --std=c++14 --stdlib=libc++ -o ixhttpd httpd.cpp \
|
||||
ixwebsocket/IXSelectInterruptFactory.cpp \
|
||||
ixwebsocket/IXCancellationRequest.cpp \
|
||||
ixwebsocket/IXSocketTLSOptions.cpp \
|
||||
@ -193,11 +193,11 @@ httpd:
|
||||
ixwebsocket/IXConnectionState.cpp \
|
||||
ixwebsocket/IXUrlParser.cpp \
|
||||
ixwebsocket/IXSelectInterrupt.cpp \
|
||||
ixwebsocket/apple/IXSetThreadName_apple.cpp \
|
||||
ixwebsocket/IXSetThreadName.cpp \
|
||||
-lz
|
||||
|
||||
httpd_linux:
|
||||
g++ --std=c++11 -o ixhttpd httpd.cpp -Iixwebsocket \
|
||||
g++ --std=c++14 -o ixhttpd httpd.cpp -Iixwebsocket \
|
||||
ixwebsocket/IXSelectInterruptFactory.cpp \
|
||||
ixwebsocket/IXCancellationRequest.cpp \
|
||||
ixwebsocket/IXSocketTLSOptions.cpp \
|
||||
@ -216,7 +216,7 @@ httpd_linux:
|
||||
ixwebsocket/IXConnectionState.cpp \
|
||||
ixwebsocket/IXUrlParser.cpp \
|
||||
ixwebsocket/IXSelectInterrupt.cpp \
|
||||
ixwebsocket/linux/IXSetThreadName_linux.cpp \
|
||||
ixwebsocket/IXSetThreadName.cpp \
|
||||
-lz -lpthread
|
||||
cp -f ixhttpd /usr/local/bin
|
||||
|
||||
@ -238,9 +238,12 @@ install_cmake_for_linux:
|
||||
doc:
|
||||
mkdocs gh-deploy
|
||||
|
||||
change:
|
||||
change: format
|
||||
vim ixwebsocket/IXWebSocketVersion.h docs/CHANGELOG.md
|
||||
|
||||
commit:
|
||||
git commit -am "`sh tools/extract_latest_change.sh`"
|
||||
|
||||
.PHONY: test
|
||||
.PHONY: build
|
||||
.PHONY: ws
|
||||
|
@ -37,11 +37,11 @@ set (SOURCES
|
||||
|
||||
test_runner.cpp
|
||||
IXTest.cpp
|
||||
IXGetFreePort.cpp
|
||||
../third_party/msgpack11/msgpack11.cpp
|
||||
|
||||
IXSocketTest.cpp
|
||||
IXSocketConnectTest.cpp
|
||||
# IXWebSocketLeakTest.cpp # commented until we have a fix for #224
|
||||
IXWebSocketServerTest.cpp
|
||||
IXWebSocketTestConnectionDisconnection.cpp
|
||||
IXUrlParserTest.cpp
|
||||
@ -56,6 +56,7 @@ set (SOURCES
|
||||
IXWebSocketChatTest.cpp
|
||||
IXWebSocketBroadcastTest.cpp
|
||||
IXWebSocketPerMessageDeflateCompressorTest.cpp
|
||||
IXStreamSqlTest.cpp
|
||||
)
|
||||
|
||||
# Some unittest don't work on windows yet
|
||||
|
@ -108,7 +108,7 @@ namespace
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::UnSubscribed)
|
||||
{
|
||||
TLogger() << "Subscriber: ununexpected from channel " << event->subscriptionId;
|
||||
TLogger() << "Subscriber: unsubscribed from channel " << event->subscriptionId;
|
||||
if (event->subscriptionId != channel)
|
||||
{
|
||||
TLogger() << "Subscriber: unexpected channel " << event->subscriptionId;
|
||||
|
@ -92,6 +92,9 @@ TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]")
|
||||
cobraBotConfig.enableHeartbeat = false;
|
||||
bool quiet = false;
|
||||
|
||||
cobraBotConfig.filter =
|
||||
std::string("select * from `") + channel + "` where id = 'sms_metric_A_id'";
|
||||
|
||||
// We could try to capture the output ... not sure how.
|
||||
bool fluentd = true;
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXGetFreePort.h"
|
||||
#include "catch.hpp"
|
||||
#include <iostream>
|
||||
#include <ixwebsocket/IXGetFreePort.h>
|
||||
#include <ixwebsocket/IXHttpClient.h>
|
||||
#include <ixwebsocket/IXHttpServer.h>
|
||||
|
||||
|
42
test/IXStreamSqlTest.cpp
Normal file
42
test/IXStreamSqlTest.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* IXStreamSqlTest.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2020 Machine Zone. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXTest.h"
|
||||
#include "catch.hpp"
|
||||
#include <iostream>
|
||||
#include <ixsnake/IXStreamSql.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace ix;
|
||||
|
||||
namespace ix
|
||||
{
|
||||
TEST_CASE("stream_sql", "[streamsql]")
|
||||
{
|
||||
SECTION("expression A")
|
||||
{
|
||||
snake::StreamSql streamSql(
|
||||
"select * from subscriber_republished_v1_neo where session LIKE '%123456%'");
|
||||
|
||||
nlohmann::json msg = {{"session", "123456"}, {"id", "foo_id"}, {"timestamp", 12}};
|
||||
|
||||
CHECK(streamSql.match(msg));
|
||||
}
|
||||
|
||||
SECTION("expression A")
|
||||
{
|
||||
snake::StreamSql streamSql("select * from `subscriber_republished_v1_neo` where "
|
||||
"session = '30091320ed8d4e50b758f8409b83bed7'");
|
||||
|
||||
nlohmann::json msg = {{"session", "30091320ed8d4e50b758f8409b83bed7"},
|
||||
{"id", "foo_id"},
|
||||
{"timestamp", 12}};
|
||||
|
||||
CHECK(streamSql.match(msg));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ix
|
@ -84,39 +84,38 @@ namespace ix
|
||||
|
||||
bool startWebSocketEchoServer(ix::WebSocketServer& server)
|
||||
{
|
||||
server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState, remoteIp, &server](const ix::WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
server.setOnClientMessageCallback(
|
||||
[&server](std::shared_ptr<ConnectionState> /*connectionState*/,
|
||||
ConnectionInfo& connectionInfo,
|
||||
WebSocket& webSocket,
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
auto remoteIp = connectionInfo.remoteIp;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
TLogger() << "New connection";
|
||||
TLogger() << "Remote ip: " << remoteIp;
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
TLogger() << "New connection";
|
||||
TLogger() << "Remote ip: " << remoteIp;
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
TLogger() << "Closed connection";
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client.get() != &webSocket)
|
||||
{
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
client->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
TLogger() << "Closed connection";
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
{
|
||||
client->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
|
@ -6,9 +6,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IXGetFreePort.h"
|
||||
#include <iostream>
|
||||
#include <ixsnake/IXAppConfig.h>
|
||||
#include <ixwebsocket/IXGetFreePort.h>
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
#include <ixwebsocket/IXWebSocketServer.h>
|
||||
#include <mutex>
|
||||
|
@ -189,13 +189,14 @@ namespace
|
||||
bool preferTLS = true;
|
||||
server.setTLSOptions(makeServerTLSOptions(preferTLS));
|
||||
|
||||
server.setOnConnectionCallback([&server, &connectionId](
|
||||
std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &connectionId, &server](
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
server.setOnClientMessageCallback(
|
||||
[&server, &connectionId](std::shared_ptr<ConnectionState> connectionState,
|
||||
ConnectionInfo& connectionInfo,
|
||||
WebSocket& webSocket,
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
auto remoteIp = connectionInfo.remoteIp;
|
||||
|
||||
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
TLogger() << "New connection";
|
||||
@ -219,14 +220,13 @@ namespace
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
if (client.get() != &webSocket)
|
||||
{
|
||||
client->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
|
@ -193,41 +193,39 @@ namespace
|
||||
|
||||
bool startServer(ix::WebSocketServer& server)
|
||||
{
|
||||
server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState, remoteIp, &server](const ix::WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
server.setOnClientMessageCallback(
|
||||
[&server](std::shared_ptr<ConnectionState> connectionState,
|
||||
ConnectionInfo& connectionInfo,
|
||||
WebSocket& webSocket,
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
auto remoteIp = connectionInfo.remoteIp;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
TLogger() << "New connection";
|
||||
TLogger() << "remote ip: " << remoteIp;
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
TLogger() << "New connection";
|
||||
TLogger() << "remote ip: " << remoteIp;
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
log("Closed connection");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client.get() != &webSocket)
|
||||
{
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
client->sendBinary(msg->str);
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
log("Closed connection");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
{
|
||||
client->sendBinary(msg->str);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
|
@ -168,45 +168,38 @@ namespace
|
||||
std::mutex& mutexWrite)
|
||||
{
|
||||
// A dev/null server
|
||||
server.setOnConnectionCallback(
|
||||
server.setOnClientMessageCallback(
|
||||
[&receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](
|
||||
std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
webSocket->setOnMessageCallback([webSocket,
|
||||
connectionState,
|
||||
remoteIp,
|
||||
&receivedCloseCode,
|
||||
&receivedCloseReason,
|
||||
&receivedCloseRemote,
|
||||
&mutexWrite](const ix::WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
ConnectionInfo& connectionInfo,
|
||||
WebSocket& /*webSocket*/,
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
auto remoteIp = connectionInfo.remoteIp;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
TLogger() << "New server connection";
|
||||
TLogger() << "remote ip: " << remoteIp;
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
TLogger() << "New server connection";
|
||||
TLogger() << "remote ip: " << remoteIp;
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Server closed connection(" << msg->closeInfo.code << ","
|
||||
<< msg->closeInfo.reason << ")";
|
||||
log(ss.str());
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Server closed connection(" << msg->closeInfo.code << ","
|
||||
<< msg->closeInfo.reason << ")";
|
||||
log(ss.str());
|
||||
|
||||
std::lock_guard<std::mutex> lck(mutexWrite);
|
||||
std::lock_guard<std::mutex> lck(mutexWrite);
|
||||
|
||||
receivedCloseCode = msg->closeInfo.code;
|
||||
receivedCloseReason = std::string(msg->closeInfo.reason);
|
||||
receivedCloseRemote = msg->closeInfo.remote;
|
||||
}
|
||||
});
|
||||
receivedCloseCode = msg->closeInfo.code;
|
||||
receivedCloseReason = std::string(msg->closeInfo.reason);
|
||||
receivedCloseRemote = msg->closeInfo.remote;
|
||||
}
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
|
183
test/IXWebSocketLeakTest.cpp
Normal file
183
test/IXWebSocketLeakTest.cpp
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* IXWebSocketServer.cpp
|
||||
* Author: Benjamin Sergeant, @marcelkauf
|
||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "IXTest.h"
|
||||
#include "catch.hpp"
|
||||
#include <ixwebsocket/IXWebSocket.h>
|
||||
#include <ixwebsocket/IXWebSocketServer.h>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
using namespace ix;
|
||||
|
||||
namespace
|
||||
{
|
||||
class WebSocketClient
|
||||
{
|
||||
public:
|
||||
WebSocketClient(int port);
|
||||
void start();
|
||||
void stop();
|
||||
bool isReady() const;
|
||||
bool hasConnectionError() const;
|
||||
|
||||
private:
|
||||
ix::WebSocket _webSocket;
|
||||
int _port;
|
||||
std::atomic<bool> _connectionError;
|
||||
};
|
||||
|
||||
WebSocketClient::WebSocketClient(int port)
|
||||
: _port(port)
|
||||
, _connectionError(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool WebSocketClient::hasConnectionError() const
|
||||
{
|
||||
return _connectionError;
|
||||
}
|
||||
|
||||
bool WebSocketClient::isReady() const
|
||||
{
|
||||
return _webSocket.getReadyState() == ix::ReadyState::Open;
|
||||
}
|
||||
|
||||
void WebSocketClient::stop()
|
||||
{
|
||||
_webSocket.stop();
|
||||
}
|
||||
|
||||
void WebSocketClient::start()
|
||||
{
|
||||
std::string url;
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "ws://localhost:" << _port << "/";
|
||||
|
||||
url = ss.str();
|
||||
}
|
||||
|
||||
_webSocket.setUrl(url);
|
||||
_webSocket.disableAutomaticReconnection();
|
||||
|
||||
std::stringstream ss;
|
||||
log(std::string("Connecting to url: ") + url);
|
||||
|
||||
_webSocket.setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
|
||||
std::stringstream ss;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
log("client connected");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
log("client disconnected");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
_connectionError = true;
|
||||
log("error");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||
{
|
||||
log("pong");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Ping)
|
||||
{
|
||||
log("ping");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
log("message");
|
||||
}
|
||||
else
|
||||
{
|
||||
log("invalid type");
|
||||
}
|
||||
});
|
||||
|
||||
_webSocket.start();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Websocket leak test")
|
||||
{
|
||||
SECTION("Websocket destructor is called when closing the connection.")
|
||||
{
|
||||
// stores the server websocket in order to check the use_count
|
||||
std::shared_ptr<WebSocket> webSocketPtr;
|
||||
|
||||
{
|
||||
int port = getFreePort();
|
||||
WebSocketServer server(port);
|
||||
|
||||
server.setOnConnectionCallback(
|
||||
[&webSocketPtr](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
// original ptr in WebSocketServer::handleConnection and the callback argument
|
||||
REQUIRE(webSocket.use_count() == 2);
|
||||
webSocket->setOnMessageCallback([&webSocketPtr, webSocket, connectionState](
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
log(std::string("New connection id: ") + connectionState->getId());
|
||||
// original ptr in WebSocketServer::handleConnection, captured ptr of
|
||||
// this callback, and ptr in WebSocketServer::_clients
|
||||
REQUIRE(webSocket.use_count() == 3);
|
||||
webSocketPtr = std::shared_ptr<WebSocket>(webSocket);
|
||||
REQUIRE(webSocket.use_count() == 4);
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
log(std::string("Client closed connection id: ") +
|
||||
connectionState->getId());
|
||||
}
|
||||
else
|
||||
{
|
||||
log(std::string(msg->str));
|
||||
}
|
||||
});
|
||||
// original ptr in WebSocketServer::handleConnection, argument of this callback,
|
||||
// and captured ptr in websocket callback
|
||||
REQUIRE(webSocket.use_count() == 3);
|
||||
});
|
||||
|
||||
server.listen();
|
||||
server.start();
|
||||
|
||||
WebSocketClient webSocketClient(port);
|
||||
webSocketClient.start();
|
||||
|
||||
while (true)
|
||||
{
|
||||
REQUIRE(!webSocketClient.hasConnectionError());
|
||||
if (webSocketClient.isReady()) break;
|
||||
ix::msleep(10);
|
||||
}
|
||||
|
||||
REQUIRE(server.getClients().size() == 1);
|
||||
// same value as in Open-handler above
|
||||
REQUIRE(webSocketPtr.use_count() == 4);
|
||||
|
||||
ix::msleep(500);
|
||||
webSocketClient.stop();
|
||||
ix::msleep(500);
|
||||
REQUIRE(server.getClients().size() == 0);
|
||||
|
||||
// websocket should only be referenced by webSocketPtr but is still used by the
|
||||
// websocket callback
|
||||
REQUIRE(webSocketPtr.use_count() == 1);
|
||||
webSocketPtr->setOnMessageCallback(nullptr);
|
||||
// websocket should only be referenced by webSocketPtr
|
||||
REQUIRE(webSocketPtr.use_count() == 1);
|
||||
server.stop();
|
||||
}
|
||||
// websocket should only be referenced by webSocketPtr
|
||||
REQUIRE(webSocketPtr.use_count() == 1);
|
||||
}
|
||||
}
|
@ -33,13 +33,14 @@ namespace ix
|
||||
};
|
||||
server.setConnectionStateFactory(factory);
|
||||
|
||||
server.setOnConnectionCallback([&server, &connectionId](
|
||||
std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &connectionId, &server](
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
server.setOnClientMessageCallback(
|
||||
[&server, &connectionId](std::shared_ptr<ConnectionState> connectionState,
|
||||
ConnectionInfo& connectionInfo,
|
||||
WebSocket& webSocket,
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
auto remoteIp = connectionInfo.remoteIp;
|
||||
|
||||
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
TLogger() << "New connection";
|
||||
@ -63,14 +64,13 @@ namespace ix
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
if (client.get() != &webSocket)
|
||||
{
|
||||
client->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
|
@ -16,42 +16,40 @@ using namespace ix;
|
||||
|
||||
bool startServer(ix::WebSocketServer& server, std::string& subProtocols)
|
||||
{
|
||||
server.setOnConnectionCallback(
|
||||
[&server, &subProtocols](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &server, &subProtocols](
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
server.setOnClientMessageCallback(
|
||||
[&server, &subProtocols](std::shared_ptr<ConnectionState> connectionState,
|
||||
ConnectionInfo& connectionInfo,
|
||||
WebSocket& webSocket,
|
||||
const ix::WebSocketMessagePtr& msg) {
|
||||
auto remoteIp = connectionInfo.remoteIp;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
TLogger() << "New connection";
|
||||
TLogger() << "remote ip: " << remoteIp;
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
TLogger() << "New connection";
|
||||
TLogger() << "remote ip: " << remoteIp;
|
||||
TLogger() << "id: " << connectionState->getId();
|
||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||
TLogger() << "Headers:";
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
TLogger() << it.first << ": " << it.second;
|
||||
}
|
||||
|
||||
subProtocols = msg->openInfo.headers["Sec-WebSocket-Protocol"];
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
subProtocols = msg->openInfo.headers["Sec-WebSocket-Protocol"];
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
log("Closed connection");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
log("Closed connection");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
if (client.get() != &webSocket)
|
||||
{
|
||||
if (client != webSocket)
|
||||
{
|
||||
client->sendBinary(msg->str);
|
||||
}
|
||||
client->sendBinary(msg->str);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
|
@ -10,10 +10,18 @@
|
||||
#include <ixwebsocket/IXNetSystem.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
ix::initNetSystem();
|
||||
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
ix::CoreLogger::LogFunc logFunc = [](const char* msg, ix::LogLevel level) {
|
||||
switch (level)
|
||||
{
|
||||
@ -49,6 +57,7 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
};
|
||||
ix::CoreLogger::setLogFunction(logFunc);
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
|
||||
int result = Catch::Session().run(argc, argv);
|
||||
|
||||
|
3
tools/extract_latest_change.sh
Normal file
3
tools/extract_latest_change.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
grep -A 3 '^##' docs/CHANGELOG.md | head -n 3 | tail -n 1
|
@ -20,7 +20,6 @@ option(USE_TLS "Add TLS support" ON)
|
||||
|
||||
include_directories(ws .)
|
||||
include_directories(ws ..)
|
||||
include_directories(ws ../third_party)
|
||||
include_directories(ws ../third_party/spdlog/include)
|
||||
include_directories(ws ../third_party/cpp-linenoise)
|
||||
|
||||
@ -66,7 +65,6 @@ add_executable(ws
|
||||
ws_cobra_publish.cpp
|
||||
ws_httpd.cpp
|
||||
ws_autobahn.cpp
|
||||
ws_proxy_server.cpp
|
||||
ws_sentry_minidump_upload.cpp
|
||||
ws_dns_lookup.cpp
|
||||
ws.cpp)
|
||||
|
@ -14,6 +14,7 @@ function cleanup_and_exit {
|
||||
}
|
||||
|
||||
WITH_TLS=${WITH_TLS:-0}
|
||||
BLOCKS=${BLOCKS:-20000}
|
||||
|
||||
rm -rf /tmp/ws_test
|
||||
mkdir -p /tmp/ws_test
|
||||
@ -57,7 +58,7 @@ ws receive "${protocol}127.0.0.1:8090" ${delay} --pidfile /tmp/ws_test/pidfile.r
|
||||
|
||||
mkdir -p /tmp/ws_test/send
|
||||
cd /tmp/ws_test/send
|
||||
dd if=/dev/urandom of=/tmp/ws_test/send/20M_file count=20000 bs=1024
|
||||
dd if=/dev/urandom of=/tmp/ws_test/send/20M_file count=$BLOCKS bs=1024
|
||||
|
||||
# Start the sender job
|
||||
ws send ${client_tls} --pidfile /tmp/ws_test/pidfile.send "${protocol}127.0.0.1:8090" /tmp/ws_test/send/20M_file
|
||||
|
45
ws/ws.cpp
45
ws/ws.cpp
@ -22,6 +22,7 @@
|
||||
#include <ixwebsocket/IXNetSystem.h>
|
||||
#include <ixwebsocket/IXSocket.h>
|
||||
#include <ixwebsocket/IXUserAgent.h>
|
||||
#include <ixwebsocket/IXWebSocketProxyServer.h>
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
@ -74,6 +75,7 @@ int main(int argc, char** argv)
|
||||
}
|
||||
};
|
||||
ix::CoreLogger::setLogFunction(logFunc);
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
@ -122,6 +124,7 @@ int main(int argc, char** argv)
|
||||
std::string key;
|
||||
std::string logfile;
|
||||
std::string scriptPath;
|
||||
std::string republishChannel;
|
||||
ix::SocketTLSOptions tlsOptions;
|
||||
ix::CobraConfig cobraConfig;
|
||||
ix::CobraBotConfig cobraBotConfig;
|
||||
@ -391,6 +394,7 @@ int main(int argc, char** argv)
|
||||
snakeApp->add_option("--redis_password", redisPassword, "Redis password");
|
||||
snakeApp->add_option("--apps_config_path", appsConfigPath, "Path to auth data")
|
||||
->check(CLI::ExistingPath);
|
||||
snakeApp->add_option("--republish_channel", republishChannel, "Republish channel");
|
||||
snakeApp->add_flag("-v", verbose, "Verbose");
|
||||
snakeApp->add_flag("-d", disablePong, "Disable Pongs");
|
||||
addTLSOptions(snakeApp);
|
||||
@ -482,7 +486,24 @@ int main(int argc, char** argv)
|
||||
cobraBotConfig.cobraConfig.socketTLSOptions = tlsOptions;
|
||||
|
||||
int ret = 1;
|
||||
if (app.got_subcommand("transfer"))
|
||||
if (app.got_subcommand("connect"))
|
||||
{
|
||||
ret = ix::ws_connect_main(url,
|
||||
headers,
|
||||
disableAutomaticReconnection,
|
||||
disablePerMessageDeflate,
|
||||
binaryMode,
|
||||
maxWaitBetweenReconnectionRetries,
|
||||
tlsOptions,
|
||||
subprotocol,
|
||||
pingIntervalSecs);
|
||||
}
|
||||
else if (app.got_subcommand("echo_server"))
|
||||
{
|
||||
ret = ix::ws_echo_server_main(
|
||||
port, greetings, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong);
|
||||
}
|
||||
else if (app.got_subcommand("transfer"))
|
||||
{
|
||||
ret = ix::ws_transfer_main(port, hostname, tlsOptions);
|
||||
}
|
||||
@ -495,27 +516,10 @@ int main(int argc, char** argv)
|
||||
bool enablePerMessageDeflate = false;
|
||||
ret = ix::ws_receive_main(url, enablePerMessageDeflate, delayMs, tlsOptions);
|
||||
}
|
||||
else if (app.got_subcommand("connect"))
|
||||
{
|
||||
ret = ix::ws_connect_main(url,
|
||||
headers,
|
||||
disableAutomaticReconnection,
|
||||
disablePerMessageDeflate,
|
||||
binaryMode,
|
||||
maxWaitBetweenReconnectionRetries,
|
||||
tlsOptions,
|
||||
subprotocol,
|
||||
pingIntervalSecs);
|
||||
}
|
||||
else if (app.got_subcommand("chat"))
|
||||
{
|
||||
ret = ix::ws_chat_main(url, user);
|
||||
}
|
||||
else if (app.got_subcommand("echo_server"))
|
||||
{
|
||||
ret = ix::ws_echo_server_main(
|
||||
port, greetings, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong);
|
||||
}
|
||||
else if (app.got_subcommand("broadcast_server"))
|
||||
{
|
||||
ret = ix::ws_broadcast_server_main(port, hostname, tlsOptions);
|
||||
@ -637,7 +641,8 @@ int main(int argc, char** argv)
|
||||
verbose,
|
||||
appsConfigPath,
|
||||
tlsOptions,
|
||||
disablePong);
|
||||
disablePong,
|
||||
republishChannel);
|
||||
}
|
||||
else if (app.got_subcommand("httpd"))
|
||||
{
|
||||
@ -653,7 +658,7 @@ int main(int argc, char** argv)
|
||||
}
|
||||
else if (app.got_subcommand("proxy_server"))
|
||||
{
|
||||
ret = ix::ws_proxy_server_main(port, hostname, tlsOptions, remoteHost, verbose);
|
||||
ret = ix::websocket_proxy_server_main(port, hostname, tlsOptions, remoteHost, verbose);
|
||||
}
|
||||
else if (app.got_subcommand("upload_minidump"))
|
||||
{
|
||||
|
9
ws/ws.h
9
ws/ws.h
@ -103,7 +103,8 @@ namespace ix
|
||||
bool verbose,
|
||||
const std::string& appsConfigPath,
|
||||
const ix::SocketTLSOptions& tlsOptions,
|
||||
bool disablePong);
|
||||
bool disablePong,
|
||||
const std::string& republishChannel);
|
||||
|
||||
int ws_httpd_main(int port,
|
||||
const std::string& hostname,
|
||||
@ -115,12 +116,6 @@ namespace ix
|
||||
|
||||
int ws_redis_server_main(int port, const std::string& hostname);
|
||||
|
||||
int ws_proxy_server_main(int port,
|
||||
const std::string& hostname,
|
||||
const ix::SocketTLSOptions& tlsOptions,
|
||||
const std::string& remoteHost,
|
||||
bool verbose);
|
||||
|
||||
int ws_sentry_minidump_upload(const std::string& metadataPath,
|
||||
const std::string& minidump,
|
||||
const std::string& project,
|
||||
|
@ -20,12 +20,12 @@ namespace ix
|
||||
ix::WebSocketServer server(port, hostname);
|
||||
server.setTLSOptions(tlsOptions);
|
||||
|
||||
server.setOnConnectionCallback([&server](std::shared_ptr<WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &server](
|
||||
const WebSocketMessagePtr& msg) {
|
||||
server.setOnClientMessageCallback(
|
||||
[&server](std::shared_ptr<ConnectionState> connectionState,
|
||||
ConnectionInfo& connectionInfo,
|
||||
WebSocket& webSocket,
|
||||
const WebSocketMessagePtr& msg) {
|
||||
auto remoteIp = connectionInfo.remoteIp;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
spdlog::info("New connection");
|
||||
@ -63,7 +63,7 @@ namespace ix
|
||||
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
if (client.get() != &webSocket)
|
||||
{
|
||||
client->send(msg->str, msg->binary, [](int current, int total) -> bool {
|
||||
spdlog::info("Step {} out of {}", current, total);
|
||||
@ -82,7 +82,6 @@ namespace ix
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <ixcobra/IXCobraMetricsPublisher.h>
|
||||
#include <jsoncpp/json/json.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <ixcobra/IXCobraMetricsPublisher.h>
|
||||
#include <jsoncpp/json/json.h>
|
||||
#include <mutex>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
|
@ -200,7 +200,7 @@ namespace ix
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||
{
|
||||
spdlog::info("Received pong");
|
||||
spdlog::info("Received pong {}", msg->str);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -42,50 +42,48 @@ namespace ix
|
||||
server.disablePong();
|
||||
}
|
||||
|
||||
server.setOnConnectionCallback(
|
||||
[greetings](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState, remoteIp, greetings](const WebSocketMessagePtr& msg) {
|
||||
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);
|
||||
}
|
||||
server.setOnClientMessageCallback(
|
||||
[greetings](std::shared_ptr<ConnectionState> connectionState,
|
||||
ConnectionInfo& connectionInfo,
|
||||
WebSocket& webSocket,
|
||||
const WebSocketMessagePtr& msg) {
|
||||
auto remoteIp = connectionInfo.remoteIp;
|
||||
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);
|
||||
}
|
||||
|
||||
if (greetings)
|
||||
{
|
||||
webSocket->sendText("Welcome !");
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
spdlog::info("Closed connection: client id {} code {} reason {}",
|
||||
connectionState->getId(),
|
||||
msg->closeInfo.code,
|
||||
msg->closeInfo.reason);
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
spdlog::error("Connection error: {}", msg->errorInfo.reason);
|
||||
spdlog::error("#retries: {}", msg->errorInfo.retries);
|
||||
spdlog::error("Wait time(ms): {}", msg->errorInfo.wait_time);
|
||||
spdlog::error("HTTP Status: {}", msg->errorInfo.http_status);
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
spdlog::info("Received {} bytes", msg->wireSize);
|
||||
webSocket->send(msg->str, msg->binary);
|
||||
}
|
||||
});
|
||||
if (greetings)
|
||||
{
|
||||
webSocket.sendText("Welcome !");
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
spdlog::info("Closed connection: client id {} code {} reason {}",
|
||||
connectionState->getId(),
|
||||
msg->closeInfo.code,
|
||||
msg->closeInfo.reason);
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
spdlog::error("Connection error: {}", msg->errorInfo.reason);
|
||||
spdlog::error("#retries: {}", msg->errorInfo.retries);
|
||||
spdlog::error("Wait time(ms): {}", msg->errorInfo.wait_time);
|
||||
spdlog::error("HTTP Status: {}", msg->errorInfo.http_status);
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
spdlog::info("Received {} bytes", msg->wireSize);
|
||||
webSocket.send(msg->str, msg->binary);
|
||||
}
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
|
@ -1,176 +0,0 @@
|
||||
/*
|
||||
* ws_proxy_server.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <ixwebsocket/IXWebSocketServer.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class ProxyConnectionState : public ix::ConnectionState
|
||||
{
|
||||
public:
|
||||
ProxyConnectionState()
|
||||
: _connected(false)
|
||||
{
|
||||
}
|
||||
|
||||
ix::WebSocket& webSocket()
|
||||
{
|
||||
return _serverWebSocket;
|
||||
}
|
||||
|
||||
bool isConnected()
|
||||
{
|
||||
return _connected;
|
||||
}
|
||||
|
||||
void setConnected()
|
||||
{
|
||||
_connected = true;
|
||||
}
|
||||
|
||||
private:
|
||||
ix::WebSocket _serverWebSocket;
|
||||
bool _connected;
|
||||
};
|
||||
|
||||
int ws_proxy_server_main(int port,
|
||||
const std::string& hostname,
|
||||
const ix::SocketTLSOptions& tlsOptions,
|
||||
const std::string& remoteUrl,
|
||||
bool verbose)
|
||||
{
|
||||
spdlog::info("Listening on {}:{}", hostname, port);
|
||||
|
||||
ix::WebSocketServer server(port, hostname);
|
||||
server.setTLSOptions(tlsOptions);
|
||||
|
||||
auto factory = []() -> std::shared_ptr<ix::ConnectionState> {
|
||||
return std::make_shared<ProxyConnectionState>();
|
||||
};
|
||||
server.setConnectionStateFactory(factory);
|
||||
|
||||
server.setOnConnectionCallback([remoteUrl,
|
||||
verbose](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState);
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
|
||||
// Server connection
|
||||
state->webSocket().setOnMessageCallback([webSocket, state, remoteIp, verbose](
|
||||
const WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
spdlog::info("New connection to remote server");
|
||||
spdlog::info("remote ip: {}", remoteIp);
|
||||
spdlog::info("id: {}", state->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 remote server connection: client id {} code {} reason {}",
|
||||
state->getId(),
|
||||
msg->closeInfo.code,
|
||||
msg->closeInfo.reason);
|
||||
state->setTerminated();
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
spdlog::error("Connection error: {}", msg->errorInfo.reason);
|
||||
spdlog::error("#retries: {}", msg->errorInfo.retries);
|
||||
spdlog::error("Wait time(ms): {}", msg->errorInfo.wait_time);
|
||||
spdlog::error("HTTP Status: {}", msg->errorInfo.http_status);
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
spdlog::info("Received {} bytes from server", msg->wireSize);
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info("payload {}", msg->str);
|
||||
}
|
||||
|
||||
webSocket->send(msg->str, msg->binary);
|
||||
}
|
||||
});
|
||||
|
||||
// Client connection
|
||||
webSocket->setOnMessageCallback(
|
||||
[state, remoteUrl, verbose](const WebSocketMessagePtr& msg) {
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
spdlog::info("New connection from client");
|
||||
spdlog::info("id: {}", state->getId());
|
||||
spdlog::info("Uri: {}", msg->openInfo.uri);
|
||||
spdlog::info("Headers:");
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
spdlog::info("{}: {}", it.first, it.second);
|
||||
}
|
||||
|
||||
// Connect to the 'real' server
|
||||
std::string url(remoteUrl);
|
||||
url += msg->openInfo.uri;
|
||||
state->webSocket().setUrl(url);
|
||||
state->webSocket().disableAutomaticReconnection();
|
||||
state->webSocket().start();
|
||||
|
||||
// we should sleep here for a bit until we've established the
|
||||
// connection with the remote server
|
||||
while (state->webSocket().getReadyState() != ReadyState::Open)
|
||||
{
|
||||
spdlog::info("waiting for server connection establishment");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
spdlog::info("server connection established");
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
spdlog::info("Closed client connection: client id {} code {} reason {}",
|
||||
state->getId(),
|
||||
msg->closeInfo.code,
|
||||
msg->closeInfo.reason);
|
||||
state->webSocket().close(msg->closeInfo.code, msg->closeInfo.reason);
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
spdlog::error("Connection error: {}", msg->errorInfo.reason);
|
||||
spdlog::error("#retries: {}", msg->errorInfo.retries);
|
||||
spdlog::error("Wait time(ms): {}", msg->errorInfo.wait_time);
|
||||
spdlog::error("HTTP Status: {}", msg->errorInfo.http_status);
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
spdlog::info("Received {} bytes from client", msg->wireSize);
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info("payload {}", msg->str);
|
||||
}
|
||||
|
||||
state->webSocket().send(msg->str, msg->binary);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
{
|
||||
spdlog::info(res.second);
|
||||
return 1;
|
||||
}
|
||||
|
||||
server.start();
|
||||
server.wait();
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace ix
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <ixsentry/IXSentryClient.h>
|
||||
#include <jsoncpp/json/json.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
|
||||
|
@ -45,7 +45,8 @@ namespace ix
|
||||
bool verbose,
|
||||
const std::string& appsConfigPath,
|
||||
const SocketTLSOptions& socketTLSOptions,
|
||||
bool disablePong)
|
||||
bool disablePong,
|
||||
const std::string& republishChannel)
|
||||
{
|
||||
snake::AppConfig appConfig;
|
||||
appConfig.port = port;
|
||||
@ -55,6 +56,7 @@ namespace ix
|
||||
appConfig.redisPassword = redisPassword;
|
||||
appConfig.socketTLSOptions = socketTLSOptions;
|
||||
appConfig.disablePong = disablePong;
|
||||
appConfig.republishChannel = republishChannel;
|
||||
|
||||
// Parse config file
|
||||
auto str = readAsString(appsConfigPath);
|
||||
|
@ -19,12 +19,12 @@ namespace ix
|
||||
ix::WebSocketServer server(port, hostname);
|
||||
server.setTLSOptions(tlsOptions);
|
||||
|
||||
server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState,
|
||||
std::unique_ptr<ConnectionInfo> connectionInfo) {
|
||||
auto remoteIp = connectionInfo->remoteIp;
|
||||
webSocket->setOnMessageCallback([webSocket, connectionState, remoteIp, &server](
|
||||
const WebSocketMessagePtr& msg) {
|
||||
server.setOnClientMessageCallback(
|
||||
[&server](std::shared_ptr<ConnectionState> connectionState,
|
||||
ConnectionInfo& connectionInfo,
|
||||
WebSocket& webSocket,
|
||||
const WebSocketMessagePtr& msg) {
|
||||
auto remoteIp = connectionInfo.remoteIp;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
spdlog::info("ws_transfer: New connection");
|
||||
@ -43,7 +43,7 @@ namespace ix
|
||||
connectionState->getId(),
|
||||
msg->closeInfo.code,
|
||||
msg->closeInfo.reason);
|
||||
auto remaining = server.getClients().erase(webSocket);
|
||||
auto remaining = server.getClients().size() - 1;
|
||||
spdlog::info("ws_transfer: {} remaining clients", remaining);
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
@ -65,7 +65,7 @@ namespace ix
|
||||
size_t receivers = 0;
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
if (client.get() != &webSocket)
|
||||
{
|
||||
auto readyState = client->getReadyState();
|
||||
auto id = connectionState->getId();
|
||||
@ -119,7 +119,6 @@ namespace ix
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
auto res = server.listen();
|
||||
if (!res.first)
|
||||
|
Reference in New Issue
Block a user