Compare commits
102 Commits
feature/cp
...
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 | ||
|
b21306376b | ||
|
fbd17685a1 | ||
|
3a673575dd | ||
|
d5e51840ab | ||
|
543c2086b2 | ||
|
95eab59c08 | ||
|
e9e768a288 | ||
|
e2180a1f31 | ||
|
7c1b57c8cd | ||
|
89e7a35a81 | ||
|
de6acfe54e | ||
|
789e620451 | ||
|
4789e190a0 | ||
|
d6366587a0 | ||
|
dddf00e3b1 | ||
|
cc47fb1c83 | ||
|
8e8cea1bcd | ||
|
68c97da518 | ||
|
f8b8799799 | ||
|
615f1778c3 | ||
|
c45b197c85 | ||
|
78713895dd | ||
|
aae2402ed2 | ||
|
b62de6e516 | ||
|
6e747849d7 | ||
|
a3a73ce1ac | ||
|
10c014bf98 | ||
|
9bb3643fc7 | ||
|
56db55caca | ||
|
565a08b229 | ||
|
bf0f11fd65 | ||
|
558daf8911 | ||
|
7ba7ff4b2a | ||
|
c9854be1c4 | ||
|
2fbb1a846f | ||
|
aa142df486 | ||
|
5e200a440f | ||
|
6ed8723d7d | ||
|
ac9710d5d6 | ||
|
35d76c20dc | ||
|
ca7344d9dc | ||
|
7603d1a71b | ||
|
d0cd4aed5a | ||
|
c5aadffa08 | ||
|
ecfca1f905 | ||
|
e49bf24d2d | ||
|
2a1cd6bb3e | ||
|
766e33774c | ||
|
9b90b1d302 | ||
|
ee8a3a52ec | ||
|
531bd624b5 | ||
|
abd6581242 | ||
|
7095367b93 | ||
|
3bb359a774 | ||
|
1c6ff733f9 | ||
|
2ecf5d8a5a | ||
|
0f88969b77 | ||
|
c317100b47 | ||
|
b029f176b6 | ||
|
bcfcfb628e | ||
|
268f528423 | ||
|
31be2e2527 | ||
|
502f021a0e |
19
.github/workflows/unittest_windows.yml
vendored
Normal file
19
.github/workflows/unittest_windows.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: windows
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- 'docs/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- uses: seanmiddleditch/gha-setup-vsdevenv@master
|
||||||
|
- run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
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
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ ixsnake/ixsnake/.certs/
|
|||||||
site/
|
site/
|
||||||
ws/.certs/
|
ws/.certs/
|
||||||
ws/.srl
|
ws/.srl
|
||||||
|
ixhttpd
|
||||||
|
@@ -7,7 +7,7 @@ find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
|
|||||||
set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}")
|
set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}")
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
include(FindPackageHandleStandardArgs)
|
||||||
find_package_handle_standard_args(MBEDTLS DEFAULT_MSG
|
find_package_handle_standard_args(MbedTLS DEFAULT_MSG
|
||||||
MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
|
MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
|
||||||
|
|
||||||
mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
|
mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
|
||||||
|
@@ -30,12 +30,15 @@ set( IXWEBSOCKET_SOURCES
|
|||||||
ixwebsocket/IXConnectionState.cpp
|
ixwebsocket/IXConnectionState.cpp
|
||||||
ixwebsocket/IXDNSLookup.cpp
|
ixwebsocket/IXDNSLookup.cpp
|
||||||
ixwebsocket/IXExponentialBackoff.cpp
|
ixwebsocket/IXExponentialBackoff.cpp
|
||||||
|
ixwebsocket/IXGetFreePort.cpp
|
||||||
ixwebsocket/IXHttp.cpp
|
ixwebsocket/IXHttp.cpp
|
||||||
ixwebsocket/IXHttpClient.cpp
|
ixwebsocket/IXHttpClient.cpp
|
||||||
ixwebsocket/IXHttpServer.cpp
|
ixwebsocket/IXHttpServer.cpp
|
||||||
ixwebsocket/IXNetSystem.cpp
|
ixwebsocket/IXNetSystem.cpp
|
||||||
ixwebsocket/IXSelectInterrupt.cpp
|
ixwebsocket/IXSelectInterrupt.cpp
|
||||||
ixwebsocket/IXSelectInterruptFactory.cpp
|
ixwebsocket/IXSelectInterruptFactory.cpp
|
||||||
|
ixwebsocket/IXSelectInterruptPipe.cpp
|
||||||
|
ixwebsocket/IXSetThreadName.cpp
|
||||||
ixwebsocket/IXSocket.cpp
|
ixwebsocket/IXSocket.cpp
|
||||||
ixwebsocket/IXSocketConnect.cpp
|
ixwebsocket/IXSocketConnect.cpp
|
||||||
ixwebsocket/IXSocketFactory.cpp
|
ixwebsocket/IXSocketFactory.cpp
|
||||||
@@ -51,6 +54,7 @@ set( IXWEBSOCKET_SOURCES
|
|||||||
ixwebsocket/IXWebSocketPerMessageDeflate.cpp
|
ixwebsocket/IXWebSocketPerMessageDeflate.cpp
|
||||||
ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp
|
ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp
|
||||||
ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp
|
ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp
|
||||||
|
ixwebsocket/IXWebSocketProxyServer.cpp
|
||||||
ixwebsocket/IXWebSocketServer.cpp
|
ixwebsocket/IXWebSocketServer.cpp
|
||||||
ixwebsocket/IXWebSocketTransport.cpp
|
ixwebsocket/IXWebSocketTransport.cpp
|
||||||
)
|
)
|
||||||
@@ -58,9 +62,11 @@ set( IXWEBSOCKET_SOURCES
|
|||||||
set( IXWEBSOCKET_HEADERS
|
set( IXWEBSOCKET_HEADERS
|
||||||
ixwebsocket/IXBench.h
|
ixwebsocket/IXBench.h
|
||||||
ixwebsocket/IXCancellationRequest.h
|
ixwebsocket/IXCancellationRequest.h
|
||||||
|
ixwebsocket/IXConnectionInfo.h
|
||||||
ixwebsocket/IXConnectionState.h
|
ixwebsocket/IXConnectionState.h
|
||||||
ixwebsocket/IXDNSLookup.h
|
ixwebsocket/IXDNSLookup.h
|
||||||
ixwebsocket/IXExponentialBackoff.h
|
ixwebsocket/IXExponentialBackoff.h
|
||||||
|
ixwebsocket/IXGetFreePort.h
|
||||||
ixwebsocket/IXHttp.h
|
ixwebsocket/IXHttp.h
|
||||||
ixwebsocket/IXHttpClient.h
|
ixwebsocket/IXHttpClient.h
|
||||||
ixwebsocket/IXHttpServer.h
|
ixwebsocket/IXHttpServer.h
|
||||||
@@ -68,6 +74,7 @@ set( IXWEBSOCKET_HEADERS
|
|||||||
ixwebsocket/IXProgressCallback.h
|
ixwebsocket/IXProgressCallback.h
|
||||||
ixwebsocket/IXSelectInterrupt.h
|
ixwebsocket/IXSelectInterrupt.h
|
||||||
ixwebsocket/IXSelectInterruptFactory.h
|
ixwebsocket/IXSelectInterruptFactory.h
|
||||||
|
ixwebsocket/IXSelectInterruptPipe.h
|
||||||
ixwebsocket/IXSetThreadName.h
|
ixwebsocket/IXSetThreadName.h
|
||||||
ixwebsocket/IXSocket.h
|
ixwebsocket/IXSocket.h
|
||||||
ixwebsocket/IXSocketConnect.h
|
ixwebsocket/IXSocketConnect.h
|
||||||
@@ -92,29 +99,13 @@ set( IXWEBSOCKET_HEADERS
|
|||||||
ixwebsocket/IXWebSocketPerMessageDeflate.h
|
ixwebsocket/IXWebSocketPerMessageDeflate.h
|
||||||
ixwebsocket/IXWebSocketPerMessageDeflateCodec.h
|
ixwebsocket/IXWebSocketPerMessageDeflateCodec.h
|
||||||
ixwebsocket/IXWebSocketPerMessageDeflateOptions.h
|
ixwebsocket/IXWebSocketPerMessageDeflateOptions.h
|
||||||
|
ixwebsocket/IXWebSocketProxyServer.h
|
||||||
ixwebsocket/IXWebSocketSendInfo.h
|
ixwebsocket/IXWebSocketSendInfo.h
|
||||||
ixwebsocket/IXWebSocketServer.h
|
ixwebsocket/IXWebSocketServer.h
|
||||||
ixwebsocket/IXWebSocketTransport.h
|
ixwebsocket/IXWebSocketTransport.h
|
||||||
ixwebsocket/IXWebSocketVersion.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)
|
option(USE_TLS "Enable TLS support" FALSE)
|
||||||
|
|
||||||
if (USE_TLS)
|
if (USE_TLS)
|
||||||
@@ -180,10 +171,8 @@ if (USE_TLS)
|
|||||||
# set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/local/include/openssl-1.0)
|
# set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/local/include/openssl-1.0)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL
|
# Use OPENSSL_ROOT_DIR CMake variable if you need to use your own openssl
|
||||||
if (NOT OPENSSL_FOUND)
|
find_package(OpenSSL REQUIRED)
|
||||||
find_package(OpenSSL REQUIRED)
|
|
||||||
endif()
|
|
||||||
message(STATUS "OpenSSL: " ${OPENSSL_VERSION})
|
message(STATUS "OpenSSL: " ${OPENSSL_VERSION})
|
||||||
|
|
||||||
add_definitions(${OPENSSL_DEFINITIONS})
|
add_definitions(${OPENSSL_DEFINITIONS})
|
||||||
@@ -201,17 +190,17 @@ if (USE_TLS)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# This ZLIB_FOUND check is to help find a cmake manually configured zlib
|
option(USE_ZLIB "Enable zlib support" TRUE)
|
||||||
if (NOT ZLIB_FOUND)
|
|
||||||
|
if (USE_ZLIB)
|
||||||
|
# Use ZLIB_ROOT CMake variable if you need to use your own zlib
|
||||||
find_package(ZLIB)
|
find_package(ZLIB)
|
||||||
endif()
|
if (ZLIB_FOUND)
|
||||||
if (ZLIB_FOUND)
|
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
|
||||||
target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
|
|
||||||
else()
|
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB)
|
||||||
include_directories(third_party/zlib ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib)
|
endif()
|
||||||
add_subdirectory(third_party/zlib)
|
|
||||||
target_link_libraries(ixwebsocket zlibstatic)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
@@ -238,19 +227,29 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
|||||||
target_compile_options(ixwebsocket PRIVATE /MP)
|
target_compile_options(ixwebsocket PRIVATE /MP)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS})
|
target_include_directories(ixwebsocket PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${IXWEBSOCKET_INCLUDE_DIRS}/>
|
||||||
|
$<INSTALL_INTERFACE:include/ixwebsocket>
|
||||||
|
)
|
||||||
|
|
||||||
set_target_properties(ixwebsocket PROPERTIES PUBLIC_HEADER "${IXWEBSOCKET_HEADERS}")
|
set_target_properties(ixwebsocket PROPERTIES PUBLIC_HEADER "${IXWEBSOCKET_HEADERS}")
|
||||||
|
|
||||||
install(TARGETS ixwebsocket
|
install(TARGETS ixwebsocket
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
|
EXPORT ixwebsocket
|
||||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/include/ixwebsocket/
|
ARCHIVE DESTINATION lib
|
||||||
|
PUBLIC_HEADER DESTINATION include/ixwebsocket/
|
||||||
)
|
)
|
||||||
|
|
||||||
|
install(EXPORT ixwebsocket
|
||||||
|
FILE ixwebsocket-config.cmake
|
||||||
|
NAMESPACE ixwebsocket::
|
||||||
|
DESTINATION lib/cmake/ixwebsocket)
|
||||||
|
|
||||||
if (USE_WS OR USE_TEST)
|
if (USE_WS OR USE_TEST)
|
||||||
add_subdirectory(ixcore)
|
add_subdirectory(ixcore)
|
||||||
add_subdirectory(ixcrypto)
|
add_subdirectory(ixcrypto)
|
||||||
add_subdirectory(ixcobra)
|
add_subdirectory(ixcobra)
|
||||||
|
add_subdirectory(ixredis)
|
||||||
add_subdirectory(ixsnake)
|
add_subdirectory(ixsnake)
|
||||||
add_subdirectory(ixsentry)
|
add_subdirectory(ixsentry)
|
||||||
add_subdirectory(ixbots)
|
add_subdirectory(ixbots)
|
||||||
|
@@ -1 +1 @@
|
|||||||
docker/Dockerfile.centos7_httpd
|
docker/Dockerfile.alpine
|
@@ -81,7 +81,7 @@ If your company or project is using this library, feel free to open an issue or
|
|||||||
|
|
||||||
- [Machine Zone](https://www.mz.com)
|
- [Machine Zone](https://www.mz.com)
|
||||||
- [Tokio](https://gitlab.com/HCInk/tokio), a discord library focused on audio playback with node bindings.
|
- [Tokio](https://gitlab.com/HCInk/tokio), a discord library focused on audio playback with node bindings.
|
||||||
- [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), a work in progress discord library
|
- [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), an easy to use Discord-bot framework.
|
||||||
- [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod
|
- [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod
|
||||||
- [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper
|
- [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper
|
||||||
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
FROM alpine:3.11 as build
|
FROM alpine:3.12 as build
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
gcc g++ musl-dev linux-headers \
|
gcc g++ musl-dev linux-headers \
|
||||||
cmake mbedtls-dev make zlib-dev ninja
|
cmake mbedtls-dev make zlib-dev python3-dev ninja
|
||||||
|
|
||||||
RUN addgroup -S app && \
|
RUN addgroup -S app && \
|
||||||
adduser -S -G app app && \
|
adduser -S -G app app && \
|
||||||
@@ -18,9 +18,9 @@ USER app
|
|||||||
RUN make ws_mbedtls_install && \
|
RUN make ws_mbedtls_install && \
|
||||||
sh tools/trim_repo_for_docker.sh
|
sh tools/trim_repo_for_docker.sh
|
||||||
|
|
||||||
FROM alpine:3.11 as runtime
|
FROM alpine:3.12 as runtime
|
||||||
|
|
||||||
RUN apk add --no-cache libstdc++ mbedtls ca-certificates && \
|
RUN apk add --no-cache libstdc++ mbedtls ca-certificates python3 && \
|
||||||
addgroup -S app && \
|
addgroup -S app && \
|
||||||
adduser -S -G app app
|
adduser -S -G app app
|
||||||
|
|
||||||
|
@@ -1,33 +0,0 @@
|
|||||||
FROM centos:7 as build
|
|
||||||
|
|
||||||
RUN yum install -y gcc-c++ make zlib-devel redhat-rpm-config
|
|
||||||
|
|
||||||
RUN groupadd app && useradd -g app app
|
|
||||||
RUN chown -R app:app /opt
|
|
||||||
RUN chown -R app:app /usr/local
|
|
||||||
|
|
||||||
# There is a bug in CMake where we cannot build from the root top folder
|
|
||||||
# So we build from /opt
|
|
||||||
COPY --chown=app:app . /opt
|
|
||||||
WORKDIR /opt
|
|
||||||
|
|
||||||
USER app
|
|
||||||
RUN [ "make", "httpd_linux" ]
|
|
||||||
RUN [ "rm", "-rf", "build" ]
|
|
||||||
|
|
||||||
FROM centos:8 as runtime
|
|
||||||
|
|
||||||
RUN groupadd app && useradd -g app app
|
|
||||||
COPY --chown=app:app --from=build /usr/local/bin/ixhttpd /usr/local/bin/ixhttpd
|
|
||||||
RUN chmod +x /usr/local/bin/ixhttpd
|
|
||||||
RUN ldd /usr/local/bin/ixhttpd
|
|
||||||
|
|
||||||
# Copy source code for gcc
|
|
||||||
COPY --chown=app:app --from=build /opt /opt
|
|
||||||
|
|
||||||
# Now run in usermode
|
|
||||||
USER app
|
|
||||||
WORKDIR /home/app
|
|
||||||
|
|
||||||
ENTRYPOINT ["ixhttpd"]
|
|
||||||
EXPOSE 9999
|
|
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,150 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All changes to this project will be documented in this file.
|
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
|
||||||
|
|
||||||
|
## [9.8.6] - 2020-07-06
|
||||||
|
|
||||||
|
(cmake) change the way zlib and openssl are searched
|
||||||
|
|
||||||
|
## [9.8.5] - 2020-07-06
|
||||||
|
|
||||||
|
(cobra python bots) remove the test which stop the bot when events do not follow cobra metrics system schema with an id and a device entry
|
||||||
|
|
||||||
|
## [9.8.4] - 2020-06-26
|
||||||
|
|
||||||
|
(cobra bots) remove bots which is not required now that we can use Python extensions
|
||||||
|
|
||||||
|
## [9.8.3] - 2020-06-25
|
||||||
|
|
||||||
|
(cmake) new python code is optional and enabled at cmake time with -DUSE_PYTHON=1
|
||||||
|
|
||||||
|
## [9.8.2] - 2020-06-24
|
||||||
|
|
||||||
|
(cobra bots) new cobra metrics bot to send data to statsd using Python for processing the message
|
||||||
|
|
||||||
|
## [9.8.1] - 2020-06-19
|
||||||
|
|
||||||
|
(cobra metrics to statsd bot) fps slow frame info : do not include os name
|
||||||
|
|
||||||
|
## [9.8.0] - 2020-06-19
|
||||||
|
|
||||||
|
(cobra metrics to statsd bot) send info about memory warnings
|
||||||
|
|
||||||
|
## [9.7.9] - 2020-06-18
|
||||||
|
|
||||||
|
(http client) fix deadlock when following redirects
|
||||||
|
|
||||||
|
## [9.7.8] - 2020-06-18
|
||||||
|
|
||||||
|
(cobra metrics to statsd bot) send info about net requests
|
||||||
|
|
||||||
|
## [9.7.7] - 2020-06-17
|
||||||
|
|
||||||
|
(cobra client and bots) add batch_size subscription option for retrieving multiple messages at once
|
||||||
|
|
||||||
|
## [9.7.6] - 2020-06-15
|
||||||
|
|
||||||
|
(websocket) WebSocketServer is not a final class, so that users can extend it (fix #215)
|
||||||
|
|
||||||
|
## [9.7.5] - 2020-06-15
|
||||||
|
|
||||||
|
(cobra bots) minor aesthetic change, in how we display http headers with a : then space as key value separator instead of :: with no space
|
||||||
|
|
||||||
|
## [9.7.4] - 2020-06-11
|
||||||
|
|
||||||
|
(cobra metrics to statsd bot) change from a statsd type of gauge to a timing one
|
||||||
|
|
||||||
|
## [9.7.3] - 2020-06-11
|
||||||
|
|
||||||
|
(redis cobra bots) capture most used devices in a zset
|
||||||
|
|
||||||
|
## [9.7.2] - 2020-06-11
|
||||||
|
|
||||||
|
(ws) add bare bone redis-cli like sub-command, with command line editing powered by libnoise
|
||||||
|
|
||||||
|
## [9.7.1] - 2020-06-11
|
||||||
|
|
||||||
|
(redis cobra bots) ws cobra metrics to redis / hostname invalid parsing
|
||||||
|
|
||||||
|
## [9.7.0] - 2020-06-11
|
||||||
|
|
||||||
|
(redis cobra bots) xadd with maxlen + fix bug in xadd client implementation and ws cobra metrics to redis command argument parsing
|
||||||
|
|
||||||
|
## [9.6.9] - 2020-06-10
|
||||||
|
|
||||||
|
(redis cobra bots) update the cobra to redis bot to use the bot framework, and change it to report fps metrics into redis streams.
|
||||||
|
|
||||||
|
## [9.6.6] - 2020-06-04
|
||||||
|
|
||||||
|
(statsd cobra bots) statsd improvement: prefix does not need a dot as a suffix, message size can be larger than 256 bytes, error handling was invalid, use core logger for logging instead of std::cerr
|
||||||
|
|
||||||
## [9.6.5] - 2020-05-29
|
## [9.6.5] - 2020-05-29
|
||||||
|
|
||||||
(http server) support gzip compression
|
(http server) support gzip compression
|
||||||
|
@@ -22,8 +22,9 @@ Options for building:
|
|||||||
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support
|
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support
|
||||||
* `-DUSE_WS=1` will build the ws interactive command line tool
|
* `-DUSE_WS=1` will build the ws interactive command line tool
|
||||||
* `-DUSE_TEST=1` will build the unittest
|
* `-DUSE_TEST=1` will build the unittest
|
||||||
|
* `-DUSE_PYTHON=1` will use Python3 for cobra bots, require Python3 to be installed.
|
||||||
|
|
||||||
If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file (not maintained much though) or rather the [github actions](https://github.com/machinezone/IXWebSocket/blob/master/.github/workflows/ccpp.yml#L40) which have instructions for building dependencies.
|
If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file (not maintained much though) or rather the [github actions](https://github.com/machinezone/IXWebSocket/blob/master/.github/workflows/unittest_windows.yml) which have instructions for building dependencies.
|
||||||
|
|
||||||
It is also possible to externally include the project, so that everything is fetched over the wire when you build like so:
|
It is also possible to externally include the project, so that everything is fetched over the wire when you build like so:
|
||||||
|
|
||||||
|
146
docs/usage.md
146
docs/usage.md
@@ -246,6 +246,10 @@ uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries();
|
|||||||
|
|
||||||
## WebSocket server API
|
## 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
|
```cpp
|
||||||
#include <ixwebsocket/IXWebSocketServer.h>
|
#include <ixwebsocket/IXWebSocketServer.h>
|
||||||
|
|
||||||
@@ -256,38 +260,49 @@ uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries();
|
|||||||
ix::WebSocketServer server(port);
|
ix::WebSocketServer server(port);
|
||||||
|
|
||||||
server.setOnConnectionCallback(
|
server.setOnConnectionCallback(
|
||||||
[&server](std::shared_ptr<WebSocket> webSocket,
|
[&server](std::weak_ptr<WebSocket> webSocket,
|
||||||
std::shared_ptr<ConnectionState> connectionState)
|
std::shared_ptr<ConnectionState> connectionState,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo)
|
||||||
{
|
{
|
||||||
webSocket->setOnMessageCallback(
|
std::cout << "Remote ip: " << connectionInfo->remoteIp << std::endl;
|
||||||
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr msg)
|
|
||||||
{
|
auto ws = webSocket.lock();
|
||||||
if (msg->type == ix::WebSocketMessageType::Open)
|
if (ws)
|
||||||
|
{
|
||||||
|
ws->setOnMessageCallback(
|
||||||
|
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr msg)
|
||||||
{
|
{
|
||||||
std::cerr << "New connection" << std::endl;
|
if (msg->type == ix::WebSocketMessageType::Open)
|
||||||
|
|
||||||
// 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::cerr << "id: " << connectionState->getId() << std::endl;
|
|
||||||
|
|
||||||
// The uri the client did connect to.
|
|
||||||
std::cerr << "Uri: " << msg->openInfo.uri << std::endl;
|
|
||||||
|
|
||||||
std::cerr << "Headers:" << std::endl;
|
|
||||||
for (auto it : msg->openInfo.headers)
|
|
||||||
{
|
{
|
||||||
std::cerr << 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -309,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
|
## HTTP client API
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -417,11 +500,14 @@ If you want to handle how requests are processed, implement the setOnConnectionC
|
|||||||
```cpp
|
```cpp
|
||||||
setOnConnectionCallback(
|
setOnConnectionCallback(
|
||||||
[this](HttpRequestPtr request,
|
[this](HttpRequestPtr request,
|
||||||
std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr
|
std::shared_ptr<ConnectionState> /*connectionState*/,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr
|
||||||
{
|
{
|
||||||
// Build a string for the response
|
// Build a string for the response
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << request->method
|
ss << connectionInfo->remoteIp
|
||||||
|
<< " "
|
||||||
|
<< request->method
|
||||||
<< " "
|
<< " "
|
||||||
<< request->uri;
|
<< request->uri;
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
* Buid with make httpd
|
* Buid with make httpd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IXHttpServer.h"
|
#include <ixwebsocket/IXHttpServer.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
@@ -8,6 +8,8 @@ set (IXBOTS_SOURCES
|
|||||||
ixbots/IXCobraToSentryBot.cpp
|
ixbots/IXCobraToSentryBot.cpp
|
||||||
ixbots/IXCobraToStatsdBot.cpp
|
ixbots/IXCobraToStatsdBot.cpp
|
||||||
ixbots/IXCobraToStdoutBot.cpp
|
ixbots/IXCobraToStdoutBot.cpp
|
||||||
|
ixbots/IXCobraMetricsToRedisBot.cpp
|
||||||
|
ixbots/IXCobraToPythonBot.cpp
|
||||||
ixbots/IXStatsdClient.cpp
|
ixbots/IXStatsdClient.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,6 +19,8 @@ set (IXBOTS_HEADERS
|
|||||||
ixbots/IXCobraToSentryBot.h
|
ixbots/IXCobraToSentryBot.h
|
||||||
ixbots/IXCobraToStatsdBot.h
|
ixbots/IXCobraToStatsdBot.h
|
||||||
ixbots/IXCobraToStdoutBot.h
|
ixbots/IXCobraToStdoutBot.h
|
||||||
|
ixbots/IXCobraMetricsToRedisBot.h
|
||||||
|
ixbots/IXCobraToPythonBot.h
|
||||||
ixbots/IXStatsdClient.h
|
ixbots/IXStatsdClient.h
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,14 +34,24 @@ if (NOT JSONCPP_FOUND)
|
|||||||
set(JSONCPP_INCLUDE_DIRS ../third_party/jsoncpp)
|
set(JSONCPP_INCLUDE_DIRS ../third_party/jsoncpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (USE_PYTHON)
|
||||||
|
target_compile_definitions(ixbots PUBLIC IXBOTS_USE_PYTHON)
|
||||||
|
find_package(Python COMPONENTS Development)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(IXBOTS_INCLUDE_DIRS
|
set(IXBOTS_INCLUDE_DIRS
|
||||||
.
|
.
|
||||||
..
|
..
|
||||||
../ixcore
|
../ixcore
|
||||||
../ixwebsocket
|
../ixwebsocket
|
||||||
../ixcobra
|
../ixcobra
|
||||||
|
../ixredis
|
||||||
../ixsentry
|
../ixsentry
|
||||||
${JSONCPP_INCLUDE_DIRS}
|
${JSONCPP_INCLUDE_DIRS}
|
||||||
${SPDLOG_INCLUDE_DIRS})
|
${SPDLOG_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
if (USE_PYTHON)
|
||||||
|
set(IXBOTS_INCLUDE_DIRS ${IXBOTS_INCLUDE_DIRS} ${Python_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories( ixbots PUBLIC ${IXBOTS_INCLUDE_DIRS} )
|
target_include_directories( ixbots PUBLIC ${IXBOTS_INCLUDE_DIRS} )
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <ixcobra/IXCobraConnection.h>
|
#include <ixcobra/IXCobraConnection.h>
|
||||||
#include <ixcore/utils/IXCoreLogger.h>
|
#include <ixcore/utils/IXCoreLogger.h>
|
||||||
|
#include <ixwebsocket/IXSetThreadName.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -28,6 +29,7 @@ namespace ix
|
|||||||
auto runtime = botConfig.runtime;
|
auto runtime = botConfig.runtime;
|
||||||
auto maxEventsPerMinute = botConfig.maxEventsPerMinute;
|
auto maxEventsPerMinute = botConfig.maxEventsPerMinute;
|
||||||
auto limitReceivedEvents = botConfig.limitReceivedEvents;
|
auto limitReceivedEvents = botConfig.limitReceivedEvents;
|
||||||
|
auto batchSize = botConfig.batchSize;
|
||||||
|
|
||||||
ix::CobraConnection conn;
|
ix::CobraConnection conn;
|
||||||
conn.configure(config);
|
conn.configure(config);
|
||||||
@@ -43,6 +45,7 @@ namespace ix
|
|||||||
std::atomic<bool> stop(false);
|
std::atomic<bool> stop(false);
|
||||||
std::atomic<bool> throttled(false);
|
std::atomic<bool> throttled(false);
|
||||||
std::atomic<bool> fatalCobraError(false);
|
std::atomic<bool> fatalCobraError(false);
|
||||||
|
std::atomic<bool> stalledConnection(false);
|
||||||
int minuteCounter = 0;
|
int minuteCounter = 0;
|
||||||
|
|
||||||
auto timer = [&sentCount,
|
auto timer = [&sentCount,
|
||||||
@@ -53,7 +56,9 @@ namespace ix
|
|||||||
&receivedCountPerSecs,
|
&receivedCountPerSecs,
|
||||||
&receivedCountPerMinutes,
|
&receivedCountPerMinutes,
|
||||||
&minuteCounter,
|
&minuteCounter,
|
||||||
|
&conn,
|
||||||
&stop] {
|
&stop] {
|
||||||
|
setThreadName("Bot progress");
|
||||||
while (!stop)
|
while (!stop)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
@@ -70,7 +75,11 @@ namespace ix
|
|||||||
<< sentCountPerSecs
|
<< sentCountPerSecs
|
||||||
<< " "
|
<< " "
|
||||||
<< sentCountTotal;
|
<< sentCountTotal;
|
||||||
CoreLogger::info(ss.str());
|
|
||||||
|
if (conn.isAuthenticated())
|
||||||
|
{
|
||||||
|
CoreLogger::info(ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
receivedCountPerSecs = receivedCount - receivedCountTotal;
|
receivedCountPerSecs = receivedCount - receivedCountTotal;
|
||||||
sentCountPerSecs = sentCount - sentCountTotal;
|
sentCountPerSecs = sentCount - sentCountTotal;
|
||||||
@@ -93,7 +102,14 @@ namespace ix
|
|||||||
|
|
||||||
std::thread t1(timer);
|
std::thread t1(timer);
|
||||||
|
|
||||||
auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat, &heartBeatTimeout, &fatalCobraError] {
|
auto heartbeat = [&sentCount,
|
||||||
|
&receivedCount,
|
||||||
|
&stop,
|
||||||
|
&enableHeartbeat,
|
||||||
|
&heartBeatTimeout,
|
||||||
|
&stalledConnection]
|
||||||
|
{
|
||||||
|
setThreadName("Bot heartbeat");
|
||||||
std::string state("na");
|
std::string state("na");
|
||||||
|
|
||||||
if (!enableHeartbeat) return;
|
if (!enableHeartbeat) return;
|
||||||
@@ -108,9 +124,12 @@ namespace ix
|
|||||||
|
|
||||||
if (currentState == state)
|
if (currentState == state)
|
||||||
{
|
{
|
||||||
CoreLogger::error("no messages received or sent for 1 minute, exiting");
|
ss.str("");
|
||||||
fatalCobraError = true;
|
ss << "no messages received or sent for "
|
||||||
break;
|
<< heartBeatTimeout << " seconds, reconnecting";
|
||||||
|
|
||||||
|
CoreLogger::error(ss.str());
|
||||||
|
stalledConnection = true;
|
||||||
}
|
}
|
||||||
state = currentState;
|
state = currentState;
|
||||||
|
|
||||||
@@ -135,6 +154,7 @@ namespace ix
|
|||||||
&receivedCountPerMinutes,
|
&receivedCountPerMinutes,
|
||||||
maxEventsPerMinute,
|
maxEventsPerMinute,
|
||||||
limitReceivedEvents,
|
limitReceivedEvents,
|
||||||
|
batchSize,
|
||||||
&fatalCobraError,
|
&fatalCobraError,
|
||||||
&sentCount](const CobraEventPtr& event) {
|
&sentCount](const CobraEventPtr& event) {
|
||||||
if (event->type == ix::CobraEventType::Open)
|
if (event->type == ix::CobraEventType::Open)
|
||||||
@@ -143,7 +163,7 @@ namespace ix
|
|||||||
|
|
||||||
for (auto&& it : event->headers)
|
for (auto&& it : event->headers)
|
||||||
{
|
{
|
||||||
CoreLogger::info(it.first + "::" + it.second);
|
CoreLogger::info(it.first + ": " + it.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (event->type == ix::CobraEventType::Closed)
|
else if (event->type == ix::CobraEventType::Closed)
|
||||||
@@ -156,7 +176,7 @@ namespace ix
|
|||||||
CoreLogger::info("Subscribing to " + channel);
|
CoreLogger::info("Subscribing to " + channel);
|
||||||
CoreLogger::info("Subscribing at position " + subscriptionPosition);
|
CoreLogger::info("Subscribing at position " + subscriptionPosition);
|
||||||
CoreLogger::info("Subscribing with filter " + filter);
|
CoreLogger::info("Subscribing with filter " + filter);
|
||||||
conn.subscribe(channel, filter, subscriptionPosition,
|
conn.subscribe(channel, filter, subscriptionPosition, batchSize,
|
||||||
[&sentCount, &receivedCountPerMinutes,
|
[&sentCount, &receivedCountPerMinutes,
|
||||||
maxEventsPerMinute, limitReceivedEvents,
|
maxEventsPerMinute, limitReceivedEvents,
|
||||||
&throttled, &receivedCount,
|
&throttled, &receivedCount,
|
||||||
@@ -231,6 +251,13 @@ namespace ix
|
|||||||
std::this_thread::sleep_for(duration);
|
std::this_thread::sleep_for(duration);
|
||||||
|
|
||||||
if (fatalCobraError) break;
|
if (fatalCobraError) break;
|
||||||
|
|
||||||
|
if (stalledConnection)
|
||||||
|
{
|
||||||
|
conn.disconnect();
|
||||||
|
conn.connect();
|
||||||
|
stalledConnection = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Run for a duration, used by unittesting now
|
// Run for a duration, used by unittesting now
|
||||||
@@ -242,6 +269,13 @@ namespace ix
|
|||||||
std::this_thread::sleep_for(duration);
|
std::this_thread::sleep_for(duration);
|
||||||
|
|
||||||
if (fatalCobraError) break;
|
if (fatalCobraError) break;
|
||||||
|
|
||||||
|
if (stalledConnection)
|
||||||
|
{
|
||||||
|
conn.disconnect();
|
||||||
|
conn.connect();
|
||||||
|
stalledConnection = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,4 +299,22 @@ namespace ix
|
|||||||
{
|
{
|
||||||
_onBotMessageCallback = callback;
|
_onBotMessageCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string CobraBot::getDeviceIdentifier(const Json::Value& msg)
|
||||||
|
{
|
||||||
|
std::string deviceId("na");
|
||||||
|
|
||||||
|
auto osName = msg["device"]["os_name"];
|
||||||
|
if (osName == "Android")
|
||||||
|
{
|
||||||
|
deviceId = msg["device"]["model"].asString();
|
||||||
|
}
|
||||||
|
else if (osName == "iOS")
|
||||||
|
{
|
||||||
|
deviceId = msg["device"]["hardware_model"].asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@@ -28,6 +28,8 @@ namespace ix
|
|||||||
int64_t run(const CobraBotConfig& botConfig);
|
int64_t run(const CobraBotConfig& botConfig);
|
||||||
void setOnBotMessageCallback(const OnBotMessageCallback& callback);
|
void setOnBotMessageCallback(const OnBotMessageCallback& callback);
|
||||||
|
|
||||||
|
std::string getDeviceIdentifier(const Json::Value& msg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OnBotMessageCallback _onBotMessageCallback;
|
OnBotMessageCallback _onBotMessageCallback;
|
||||||
};
|
};
|
||||||
|
@@ -27,5 +27,6 @@ namespace ix
|
|||||||
int runtime = -1;
|
int runtime = -1;
|
||||||
int maxEventsPerMinute = std::numeric_limits<int>::max();
|
int maxEventsPerMinute = std::numeric_limits<int>::max();
|
||||||
bool limitReceivedEvents = false;
|
bool limitReceivedEvents = false;
|
||||||
|
int batchSize = 1;
|
||||||
};
|
};
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
149
ixbots/ixbots/IXCobraMetricsToRedisBot.cpp
Normal file
149
ixbots/ixbots/IXCobraMetricsToRedisBot.cpp
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* IXCobraMetricsToRedisBot.cpp
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "IXCobraMetricsToRedisBot.h"
|
||||||
|
|
||||||
|
#include "IXCobraBot.h"
|
||||||
|
#include "IXStatsdClient.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <ixcobra/IXCobraConnection.h>
|
||||||
|
#include <ixcore/utils/IXCoreLogger.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string removeSpaces(const std::string& str)
|
||||||
|
{
|
||||||
|
std::string out(str);
|
||||||
|
out.erase(
|
||||||
|
std::remove_if(out.begin(), out.end(), [](unsigned char x) { return std::isspace(x); }),
|
||||||
|
out.end());
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
bool processPerfMetricsEventSlowFrames(const Json::Value& msg,
|
||||||
|
RedisClient& redisClient,
|
||||||
|
const std::string& deviceId)
|
||||||
|
{
|
||||||
|
auto frameRateHistogramCounts = msg["data"]["FrameRateHistogramCounts"];
|
||||||
|
|
||||||
|
int slowFrames = 0;
|
||||||
|
slowFrames += frameRateHistogramCounts[4].asInt();
|
||||||
|
slowFrames += frameRateHistogramCounts[5].asInt();
|
||||||
|
slowFrames += frameRateHistogramCounts[6].asInt();
|
||||||
|
slowFrames += frameRateHistogramCounts[7].asInt();
|
||||||
|
|
||||||
|
//
|
||||||
|
// XADD without a device id
|
||||||
|
//
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << msg["id"].asString() << "_slow_frames" << "."
|
||||||
|
<< msg["device"]["game"].asString() << "."
|
||||||
|
<< msg["device"]["os_name"].asString() << "."
|
||||||
|
<< removeSpaces(msg["data"]["Tag"].asString());
|
||||||
|
|
||||||
|
int maxLen;
|
||||||
|
maxLen = 100000;
|
||||||
|
std::string id = ss.str();
|
||||||
|
std::string errMsg;
|
||||||
|
if (redisClient.xadd(id, std::to_string(slowFrames), maxLen, errMsg).empty())
|
||||||
|
{
|
||||||
|
CoreLogger::info(std::string("redis XADD error: ") + errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// XADD with a device id
|
||||||
|
//
|
||||||
|
ss.str(""); // reset the stringstream
|
||||||
|
ss << msg["id"].asString() << "_slow_frames_by_device" << "."
|
||||||
|
<< deviceId << "."
|
||||||
|
<< msg["device"]["game"].asString() << "."
|
||||||
|
<< msg["device"]["os_name"].asString() << "."
|
||||||
|
<< removeSpaces(msg["data"]["Tag"].asString());
|
||||||
|
|
||||||
|
id = ss.str();
|
||||||
|
maxLen = 1000;
|
||||||
|
if (redisClient.xadd(id, std::to_string(slowFrames), maxLen, errMsg).empty())
|
||||||
|
{
|
||||||
|
CoreLogger::info(std::string("redis XADD error: ") + errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Add device to the device zset, and increment the score
|
||||||
|
// so that we know which devices are used more than others
|
||||||
|
// ZINCRBY myzset 1 one
|
||||||
|
//
|
||||||
|
ss.str(""); // reset the stringstream
|
||||||
|
ss << msg["id"].asString() << "_slow_frames_devices" << "."
|
||||||
|
<< msg["device"]["game"].asString();
|
||||||
|
|
||||||
|
id = ss.str();
|
||||||
|
std::vector<std::string> args = {
|
||||||
|
"ZINCRBY", id, "1", deviceId
|
||||||
|
};
|
||||||
|
auto response = redisClient.send(args, errMsg);
|
||||||
|
if (response.first == RespType::Error)
|
||||||
|
{
|
||||||
|
CoreLogger::info(std::string("redis ZINCRBY error: ") + errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t cobra_metrics_to_redis_bot(const ix::CobraBotConfig& config,
|
||||||
|
RedisClient& redisClient,
|
||||||
|
bool verbose)
|
||||||
|
{
|
||||||
|
CobraBot bot;
|
||||||
|
|
||||||
|
bot.setOnBotMessageCallback(
|
||||||
|
[&redisClient, &verbose, &bot]
|
||||||
|
(const Json::Value& msg,
|
||||||
|
const std::string& /*position*/,
|
||||||
|
std::atomic<bool>& /*throttled*/,
|
||||||
|
std::atomic<bool>& /*fatalCobraError*/,
|
||||||
|
std::atomic<uint64_t>& sentCount) -> void {
|
||||||
|
if (msg["device"].isNull())
|
||||||
|
{
|
||||||
|
CoreLogger::info("no device entry, skipping event");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg["id"].isNull())
|
||||||
|
{
|
||||||
|
CoreLogger::info("no id entry, skipping event");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Display full message with
|
||||||
|
if (verbose)
|
||||||
|
{
|
||||||
|
CoreLogger::info(msg.toStyledString());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
if (msg["id"].asString() == "engine_performance_metrics_id")
|
||||||
|
{
|
||||||
|
auto deviceId = bot.getDeviceIdentifier(msg);
|
||||||
|
success = processPerfMetricsEventSlowFrames(msg, redisClient, deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) sentCount++;
|
||||||
|
});
|
||||||
|
|
||||||
|
return bot.run(config);
|
||||||
|
}
|
||||||
|
} // namespace ix
|
20
ixbots/ixbots/IXCobraMetricsToRedisBot.h
Normal file
20
ixbots/ixbots/IXCobraMetricsToRedisBot.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* IXCobraMetricsToRedisBot.h
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <ixredis/IXRedisClient.h>
|
||||||
|
#include "IXCobraBotConfig.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
int64_t cobra_metrics_to_redis_bot(const ix::CobraBotConfig& config,
|
||||||
|
RedisClient& redisClient,
|
||||||
|
bool verbose);
|
||||||
|
} // namespace ix
|
||||||
|
|
332
ixbots/ixbots/IXCobraToPythonBot.cpp
Normal file
332
ixbots/ixbots/IXCobraToPythonBot.cpp
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
/*
|
||||||
|
* IXCobraToPythonBot.cpp
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "IXCobraToPythonBot.h"
|
||||||
|
|
||||||
|
#include "IXCobraBot.h"
|
||||||
|
#include "IXStatsdClient.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <ixcobra/IXCobraConnection.h>
|
||||||
|
#include <ixcore/utils/IXCoreLogger.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
//
|
||||||
|
// I cannot get Windows to easily build on CI (github action) so support
|
||||||
|
// is disabled for now. It should be a simple fix
|
||||||
|
// (linking error about missing debug build)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef IXBOTS_USE_PYTHON
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#include <Python.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IXBOTS_USE_PYTHON
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// This function is unused at this point. It produce a correct output,
|
||||||
|
// but triggers memory leaks when called repeateadly, as I cannot figure out how to
|
||||||
|
// make the reference counting Python functions to work properly (Py_DECREF and friends)
|
||||||
|
//
|
||||||
|
PyObject* jsonToPythonObject(const Json::Value& val)
|
||||||
|
{
|
||||||
|
switch(val.type())
|
||||||
|
{
|
||||||
|
case Json::nullValue:
|
||||||
|
{
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Json::intValue:
|
||||||
|
{
|
||||||
|
return PyLong_FromLong(val.asInt64());
|
||||||
|
}
|
||||||
|
|
||||||
|
case Json::uintValue:
|
||||||
|
{
|
||||||
|
return PyLong_FromLong(val.asUInt64());
|
||||||
|
}
|
||||||
|
|
||||||
|
case Json::realValue:
|
||||||
|
{
|
||||||
|
return PyFloat_FromDouble(val.asDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
case Json::stringValue:
|
||||||
|
{
|
||||||
|
return PyUnicode_FromString(val.asCString());
|
||||||
|
}
|
||||||
|
|
||||||
|
case Json::booleanValue:
|
||||||
|
{
|
||||||
|
return val.asBool() ? Py_True : Py_False;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Json::arrayValue:
|
||||||
|
{
|
||||||
|
PyObject* list = PyList_New(val.size());
|
||||||
|
Py_ssize_t i = 0;
|
||||||
|
for (auto&& it = val.begin(); it != val.end(); ++it)
|
||||||
|
{
|
||||||
|
PyList_SetItem(list, i++, jsonToPythonObject(*it));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Json::objectValue:
|
||||||
|
{
|
||||||
|
PyObject* dict = PyDict_New();
|
||||||
|
for (auto&& it = val.begin(); it != val.end(); ++it)
|
||||||
|
{
|
||||||
|
PyObject* key = jsonToPythonObject(it.key());
|
||||||
|
PyObject* value = jsonToPythonObject(*it);
|
||||||
|
|
||||||
|
PyDict_SetItem(dict, key, value);
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
int64_t cobra_to_python_bot(const ix::CobraBotConfig& config,
|
||||||
|
StatsdClient& statsdClient,
|
||||||
|
const std::string& scriptPath)
|
||||||
|
{
|
||||||
|
#ifndef IXBOTS_USE_PYTHON
|
||||||
|
CoreLogger::error("Command is disabled. "
|
||||||
|
"Needs to be configured with USE_PYTHON=1");
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
CobraBot bot;
|
||||||
|
Py_InitializeEx(0); // 0 arg so that we do not install signal handlers
|
||||||
|
// which prevent us from using Ctrl-C
|
||||||
|
|
||||||
|
size_t lastIndex = scriptPath.find_last_of(".");
|
||||||
|
std::string modulePath = scriptPath.substr(0, lastIndex);
|
||||||
|
|
||||||
|
PyObject* pyModuleName = PyUnicode_DecodeFSDefault(modulePath.c_str());
|
||||||
|
|
||||||
|
if (pyModuleName == nullptr)
|
||||||
|
{
|
||||||
|
CoreLogger::error("Python error: Cannot decode file system path");
|
||||||
|
PyErr_Print();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import module
|
||||||
|
PyObject* pyModule = PyImport_Import(pyModuleName);
|
||||||
|
Py_DECREF(pyModuleName);
|
||||||
|
if (pyModule == nullptr)
|
||||||
|
{
|
||||||
|
CoreLogger::error("Python error: Cannot import module.");
|
||||||
|
CoreLogger::error("Module name cannot countain dash characters.");
|
||||||
|
CoreLogger::error("Is PYTHONPATH set correctly ?");
|
||||||
|
PyErr_Print();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// module main funtion name is named 'run'
|
||||||
|
const std::string entryPoint("run");
|
||||||
|
PyObject* pyFunc = PyObject_GetAttrString(pyModule, entryPoint.c_str());
|
||||||
|
|
||||||
|
if (!pyFunc)
|
||||||
|
{
|
||||||
|
CoreLogger::error("run symbol is missing from module.");
|
||||||
|
PyErr_Print();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PyCallable_Check(pyFunc))
|
||||||
|
{
|
||||||
|
CoreLogger::error("run symbol is not a function.");
|
||||||
|
PyErr_Print();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bot.setOnBotMessageCallback(
|
||||||
|
[&statsdClient, pyFunc]
|
||||||
|
(const Json::Value& msg,
|
||||||
|
const std::string& /*position*/,
|
||||||
|
std::atomic<bool>& /*throttled*/,
|
||||||
|
std::atomic<bool>& fatalCobraError,
|
||||||
|
std::atomic<uint64_t>& sentCount) -> void {
|
||||||
|
//
|
||||||
|
// Invoke python script here. First build function parameters, a tuple
|
||||||
|
//
|
||||||
|
const int kVersion = 1; // We can bump this and let the interface evolve
|
||||||
|
|
||||||
|
PyObject *pyArgs = PyTuple_New(2);
|
||||||
|
PyTuple_SetItem(pyArgs, 0, PyLong_FromLong(kVersion)); // First argument
|
||||||
|
|
||||||
|
//
|
||||||
|
// It would be better to create a Python object (a dictionary)
|
||||||
|
// from the json msg, but it is simpler to serialize it to a string
|
||||||
|
// and decode it on the Python side of the fence
|
||||||
|
//
|
||||||
|
PyObject* pySerializedJson = PyUnicode_FromString(msg.toStyledString().c_str());
|
||||||
|
PyTuple_SetItem(pyArgs, 1, pySerializedJson); // Second argument
|
||||||
|
|
||||||
|
// Invoke the python routine
|
||||||
|
PyObject* pyList = PyObject_CallObject(pyFunc, pyArgs);
|
||||||
|
|
||||||
|
// Error calling the function
|
||||||
|
if (pyList == nullptr)
|
||||||
|
{
|
||||||
|
fatalCobraError = true;
|
||||||
|
CoreLogger::error("run() function call failed. Input msg: ");
|
||||||
|
auto serializedMsg = msg.toStyledString();
|
||||||
|
CoreLogger::error(serializedMsg);
|
||||||
|
PyErr_Print();
|
||||||
|
CoreLogger::error("================");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid return type
|
||||||
|
if (!PyList_Check(pyList))
|
||||||
|
{
|
||||||
|
fatalCobraError = true;
|
||||||
|
CoreLogger::error("run() return type should be a list");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The result is a list of dict containing sufficient info
|
||||||
|
// to send messages to statsd
|
||||||
|
auto listSize = PyList_Size(pyList);
|
||||||
|
|
||||||
|
for (Py_ssize_t i = 0 ; i < listSize; ++i)
|
||||||
|
{
|
||||||
|
PyObject* dict = PyList_GetItem(pyList, i);
|
||||||
|
|
||||||
|
// Make sure this is a dict
|
||||||
|
if (!PyDict_Check(dict))
|
||||||
|
{
|
||||||
|
fatalCobraError = true;
|
||||||
|
CoreLogger::error("list element is not a dict");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Retrieve object kind
|
||||||
|
//
|
||||||
|
PyObject* pyKind = PyDict_GetItemString(dict, "kind");
|
||||||
|
if (!PyUnicode_Check(pyKind))
|
||||||
|
{
|
||||||
|
fatalCobraError = true;
|
||||||
|
CoreLogger::error("kind entry is not a string");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string kind(PyUnicode_AsUTF8(pyKind));
|
||||||
|
|
||||||
|
bool counter = false;
|
||||||
|
bool gauge = false;
|
||||||
|
bool timing = false;
|
||||||
|
|
||||||
|
if (kind == "counter")
|
||||||
|
{
|
||||||
|
counter = true;
|
||||||
|
}
|
||||||
|
else if (kind == "gauge")
|
||||||
|
{
|
||||||
|
gauge = true;
|
||||||
|
}
|
||||||
|
else if (kind == "timing")
|
||||||
|
{
|
||||||
|
timing = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fatalCobraError = true;
|
||||||
|
CoreLogger::error(std::string("invalid kind entry: ") + kind +
|
||||||
|
". Supported ones are counter, gauge, timing");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Retrieve object key
|
||||||
|
//
|
||||||
|
PyObject* pyKey = PyDict_GetItemString(dict, "key");
|
||||||
|
if (!PyUnicode_Check(pyKey))
|
||||||
|
{
|
||||||
|
fatalCobraError = true;
|
||||||
|
CoreLogger::error("key entry is not a string");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string key(PyUnicode_AsUTF8(pyKey));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Retrieve object value and send data to statsd
|
||||||
|
//
|
||||||
|
PyObject* pyValue = PyDict_GetItemString(dict, "value");
|
||||||
|
|
||||||
|
// Send data to statsd
|
||||||
|
if (PyFloat_Check(pyValue))
|
||||||
|
{
|
||||||
|
double value = PyFloat_AsDouble(pyValue);
|
||||||
|
|
||||||
|
if (counter)
|
||||||
|
{
|
||||||
|
statsdClient.count(key, value);
|
||||||
|
}
|
||||||
|
else if (gauge)
|
||||||
|
{
|
||||||
|
statsdClient.gauge(key, value);
|
||||||
|
}
|
||||||
|
else if (timing)
|
||||||
|
{
|
||||||
|
statsdClient.timing(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (PyLong_Check(pyValue))
|
||||||
|
{
|
||||||
|
long value = PyLong_AsLong(pyValue);
|
||||||
|
|
||||||
|
if (counter)
|
||||||
|
{
|
||||||
|
statsdClient.count(key, value);
|
||||||
|
}
|
||||||
|
else if (gauge)
|
||||||
|
{
|
||||||
|
statsdClient.gauge(key, value);
|
||||||
|
}
|
||||||
|
else if (timing)
|
||||||
|
{
|
||||||
|
statsdClient.timing(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fatalCobraError = true;
|
||||||
|
CoreLogger::error("value entry is neither an int or a float");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sentCount++; // should we update this for each statsd object sent ?
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(pyArgs);
|
||||||
|
Py_DECREF(pyList);
|
||||||
|
});
|
||||||
|
|
||||||
|
bool status = bot.run(config);
|
||||||
|
|
||||||
|
// Cleanup - we should do something similar in all exit case ...
|
||||||
|
Py_DECREF(pyFunc);
|
||||||
|
Py_DECREF(pyModule);
|
||||||
|
Py_FinalizeEx();
|
||||||
|
|
||||||
|
return status;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
19
ixbots/ixbots/IXCobraToPythonBot.h
Normal file
19
ixbots/ixbots/IXCobraToPythonBot.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* IXCobraMetricsToStatsdBot.h
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <ixbots/IXStatsdClient.h>
|
||||||
|
#include "IXCobraBotConfig.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
int64_t cobra_to_python_bot(const ix::CobraBotConfig& config,
|
||||||
|
StatsdClient& statsdClient,
|
||||||
|
const std::string& scriptPath);
|
||||||
|
} // namespace ix
|
@@ -70,11 +70,17 @@ namespace ix
|
|||||||
std::atomic<bool>& fatalCobraError,
|
std::atomic<bool>& fatalCobraError,
|
||||||
std::atomic<uint64_t>& sentCount) -> void {
|
std::atomic<uint64_t>& sentCount) -> void {
|
||||||
std::string id;
|
std::string id;
|
||||||
|
size_t idx = 0;
|
||||||
for (auto&& attr : tokens)
|
for (auto&& attr : tokens)
|
||||||
{
|
{
|
||||||
id += ".";
|
|
||||||
auto val = extractAttr(attr, msg);
|
auto val = extractAttr(attr, msg);
|
||||||
id += val.asString();
|
id += val.asString();
|
||||||
|
|
||||||
|
// We add a dot separator unless we are processing the last token
|
||||||
|
if (idx++ != tokens.size() - 1)
|
||||||
|
{
|
||||||
|
id += ".";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gauge.empty() && timer.empty())
|
if (gauge.empty() && timer.empty())
|
||||||
|
@@ -39,21 +39,28 @@
|
|||||||
|
|
||||||
#include "IXStatsdClient.h"
|
#include "IXStatsdClient.h"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <ixwebsocket/IXNetSystem.h>
|
#include <ixwebsocket/IXNetSystem.h>
|
||||||
#include <stdio.h>
|
#include <ixwebsocket/IXSetThreadName.h>
|
||||||
|
#include <ixcore/utils/IXCoreLogger.h>
|
||||||
|
#include <sstream>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
StatsdClient::StatsdClient(const std::string& host, int port, const std::string& prefix)
|
StatsdClient::StatsdClient(const std::string& host,
|
||||||
|
int port,
|
||||||
|
const std::string& prefix,
|
||||||
|
bool verbose)
|
||||||
: _host(host)
|
: _host(host)
|
||||||
, _port(port)
|
, _port(port)
|
||||||
, _prefix(prefix)
|
, _prefix(prefix)
|
||||||
, _stop(false)
|
, _stop(false)
|
||||||
|
, _verbose(verbose)
|
||||||
{
|
{
|
||||||
_thread = std::thread([this] {
|
_thread = std::thread([this] {
|
||||||
|
setThreadName("Statsd");
|
||||||
|
|
||||||
while (!_stop)
|
while (!_stop)
|
||||||
{
|
{
|
||||||
flushQueue();
|
flushQueue();
|
||||||
@@ -115,11 +122,15 @@ namespace ix
|
|||||||
{
|
{
|
||||||
cleanup(key);
|
cleanup(key);
|
||||||
|
|
||||||
char buf[256];
|
std::stringstream ss;
|
||||||
snprintf(
|
ss << _prefix << "." << key << ":" << value << "|" << type;
|
||||||
buf, sizeof(buf), "%s%s:%zd|%s\n", _prefix.c_str(), key.c_str(), value, type.c_str());
|
|
||||||
|
|
||||||
enqueue(buf);
|
if (_verbose)
|
||||||
|
{
|
||||||
|
CoreLogger::info(ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
enqueue(ss.str() + "\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,10 +148,13 @@ namespace ix
|
|||||||
{
|
{
|
||||||
auto message = _queue.front();
|
auto message = _queue.front();
|
||||||
auto ret = _socket.sendto(message);
|
auto ret = _socket.sendto(message);
|
||||||
if (ret != 0)
|
if (ret == -1)
|
||||||
{
|
{
|
||||||
std::cerr << "error: " << strerror(UdpSocket::getErrno()) << std::endl;
|
CoreLogger::error(std::string("statsd error: ") + strerror(UdpSocket::getErrno()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we always dequeue regardless of the ability to send the message
|
||||||
|
// so that we keep our queue size under control
|
||||||
_queue.pop_front();
|
_queue.pop_front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,8 @@ namespace ix
|
|||||||
public:
|
public:
|
||||||
StatsdClient(const std::string& host = "127.0.0.1",
|
StatsdClient(const std::string& host = "127.0.0.1",
|
||||||
int port = 8125,
|
int port = 8125,
|
||||||
const std::string& prefix = "");
|
const std::string& prefix = "",
|
||||||
|
bool verbose = false);
|
||||||
~StatsdClient();
|
~StatsdClient();
|
||||||
|
|
||||||
bool init(std::string& errMsg);
|
bool init(std::string& errMsg);
|
||||||
@@ -52,6 +53,7 @@ namespace ix
|
|||||||
std::mutex _mutex; // for the queue
|
std::mutex _mutex; // for the queue
|
||||||
|
|
||||||
std::deque<std::string> _queue;
|
std::deque<std::string> _queue;
|
||||||
|
bool _verbose;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace ix
|
} // end namespace ix
|
||||||
|
@@ -111,6 +111,12 @@ namespace ix
|
|||||||
|
|
||||||
void CobraConnection::disconnect()
|
void CobraConnection::disconnect()
|
||||||
{
|
{
|
||||||
|
auto subscriptionIds = getSubscriptionsIds();
|
||||||
|
for (auto&& subscriptionId : subscriptionIds)
|
||||||
|
{
|
||||||
|
unsubscribe(subscriptionId);
|
||||||
|
}
|
||||||
|
|
||||||
_authenticated = false;
|
_authenticated = false;
|
||||||
_webSocket->stop();
|
_webSocket->stop();
|
||||||
}
|
}
|
||||||
@@ -562,11 +568,13 @@ namespace ix
|
|||||||
void CobraConnection::subscribe(const std::string& channel,
|
void CobraConnection::subscribe(const std::string& channel,
|
||||||
const std::string& filter,
|
const std::string& filter,
|
||||||
const std::string& position,
|
const std::string& position,
|
||||||
|
int batchSize,
|
||||||
SubscriptionCallback cb)
|
SubscriptionCallback cb)
|
||||||
{
|
{
|
||||||
// Create and send a subscribe pdu
|
// Create and send a subscribe pdu
|
||||||
Json::Value body;
|
Json::Value body;
|
||||||
body["channel"] = channel;
|
body["channel"] = channel;
|
||||||
|
body["batch_size"] = batchSize;
|
||||||
|
|
||||||
if (!filter.empty())
|
if (!filter.empty())
|
||||||
{
|
{
|
||||||
@@ -612,6 +620,18 @@ namespace ix
|
|||||||
_webSocket->send(pdu.toStyledString());
|
_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
|
// Enqueue strategy drops old messages when we are at full capacity
|
||||||
//
|
//
|
||||||
|
@@ -88,6 +88,7 @@ namespace ix
|
|||||||
void subscribe(const std::string& channel,
|
void subscribe(const std::string& channel,
|
||||||
const std::string& filter = std::string(),
|
const std::string& filter = std::string(),
|
||||||
const std::string& position = std::string(),
|
const std::string& position = std::string(),
|
||||||
|
int batchSize = 1,
|
||||||
SubscriptionCallback cb = nullptr);
|
SubscriptionCallback cb = nullptr);
|
||||||
|
|
||||||
/// Unsubscribe from a channel
|
/// Unsubscribe from a channel
|
||||||
@@ -162,6 +163,9 @@ namespace ix
|
|||||||
/// Tells whether the internal queue is empty or not
|
/// Tells whether the internal queue is empty or not
|
||||||
bool isQueueEmpty();
|
bool isQueueEmpty();
|
||||||
|
|
||||||
|
/// Retrieve all subscriptions ids
|
||||||
|
std::vector<std::string> getSubscriptionsIds();
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Member variables
|
/// Member variables
|
||||||
///
|
///
|
||||||
|
27
ixredis/CMakeLists.txt
Normal file
27
ixredis/CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#
|
||||||
|
# Author: Benjamin Sergeant
|
||||||
|
# Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
set (IXREDIS_SOURCES
|
||||||
|
ixredis/IXRedisClient.cpp
|
||||||
|
ixredis/IXRedisServer.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set (IXREDIS_HEADERS
|
||||||
|
ixredis/IXRedisClient.h
|
||||||
|
ixredis/IXRedisServer.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(ixredis STATIC
|
||||||
|
${IXREDIS_SOURCES}
|
||||||
|
${IXREDIS_HEADERS}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(IXREDIS_INCLUDE_DIRS
|
||||||
|
.
|
||||||
|
..
|
||||||
|
../ixcore
|
||||||
|
../ixwebsocket)
|
||||||
|
|
||||||
|
target_include_directories( ixredis PUBLIC ${IXREDIS_INCLUDE_DIRS} )
|
@@ -9,6 +9,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <ixwebsocket/IXSocket.h>
|
||||||
#include <ixwebsocket/IXSocketFactory.h>
|
#include <ixwebsocket/IXSocketFactory.h>
|
||||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -250,12 +251,16 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string RedisClient::prepareXaddCommand(const std::string& stream,
|
std::string RedisClient::prepareXaddCommand(const std::string& stream,
|
||||||
const std::string& message)
|
const std::string& message,
|
||||||
|
int maxLen)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "*5\r\n";
|
ss << "*8\r\n";
|
||||||
ss << writeString("XADD");
|
ss << writeString("XADD");
|
||||||
ss << writeString(stream);
|
ss << writeString(stream);
|
||||||
|
ss << writeString("MAXLEN");
|
||||||
|
ss << writeString("~");
|
||||||
|
ss << writeString(std::to_string(maxLen));
|
||||||
ss << writeString("*");
|
ss << writeString("*");
|
||||||
ss << writeString("field");
|
ss << writeString("field");
|
||||||
ss << writeString(message);
|
ss << writeString(message);
|
||||||
@@ -265,6 +270,7 @@ namespace ix
|
|||||||
|
|
||||||
std::string RedisClient::xadd(const std::string& stream,
|
std::string RedisClient::xadd(const std::string& stream,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
|
int maxLen,
|
||||||
std::string& errMsg)
|
std::string& errMsg)
|
||||||
{
|
{
|
||||||
errMsg.clear();
|
errMsg.clear();
|
||||||
@@ -275,7 +281,7 @@ namespace ix
|
|||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string command = prepareXaddCommand(stream, message);
|
std::string command = prepareXaddCommand(stream, message, maxLen);
|
||||||
|
|
||||||
bool sent = _socket->writeBytes(command, nullptr);
|
bool sent = _socket->writeBytes(command, nullptr);
|
||||||
if (!sent)
|
if (!sent)
|
||||||
@@ -348,4 +354,104 @@ namespace ix
|
|||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<RespType, std::string> RedisClient::send(
|
||||||
|
const std::vector<std::string>& args,
|
||||||
|
std::string& errMsg)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "*";
|
||||||
|
ss << std::to_string(args.size());
|
||||||
|
ss << "\r\n";
|
||||||
|
|
||||||
|
for (auto&& arg : args)
|
||||||
|
{
|
||||||
|
ss << writeString(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sent = _socket->writeBytes(ss.str(), nullptr);
|
||||||
|
if (!sent)
|
||||||
|
{
|
||||||
|
errMsg = "Cannot write bytes to socket";
|
||||||
|
return std::make_pair(RespType::Error, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return readResponse(errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<RespType, std::string> RedisClient::readResponse(std::string& errMsg)
|
||||||
|
{
|
||||||
|
// Read result
|
||||||
|
auto pollResult = _socket->isReadyToRead(-1);
|
||||||
|
if (pollResult == PollResultType::Error)
|
||||||
|
{
|
||||||
|
errMsg = "Error while polling for result";
|
||||||
|
return std::make_pair(RespType::Error, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// First line is the string length
|
||||||
|
auto lineResult = _socket->readLine(nullptr);
|
||||||
|
auto lineValid = lineResult.first;
|
||||||
|
auto line = lineResult.second;
|
||||||
|
|
||||||
|
if (!lineValid)
|
||||||
|
{
|
||||||
|
errMsg = "Error while polling for result";
|
||||||
|
return std::make_pair(RespType::Error, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
|
||||||
|
if (line[0] == '+') // Simple string
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
response = line.substr(1, line.size() - 3);
|
||||||
|
return std::make_pair(RespType::String, response);
|
||||||
|
}
|
||||||
|
else if (line[0] == '-') // Errors
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
response = line.substr(1, line.size() - 3);
|
||||||
|
return std::make_pair(RespType::Error, response);
|
||||||
|
}
|
||||||
|
else if (line[0] == ':') // Integers
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
response = line.substr(1, line.size() - 3);
|
||||||
|
return std::make_pair(RespType::Integer, response);
|
||||||
|
}
|
||||||
|
else if (line[0] == '$') // Bulk strings
|
||||||
|
{
|
||||||
|
int stringSize;
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << line.substr(1, line.size() - 1);
|
||||||
|
ss >> stringSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the result, which is the stream id computed by the redis server
|
||||||
|
lineResult = _socket->readLine(nullptr);
|
||||||
|
lineValid = lineResult.first;
|
||||||
|
line = lineResult.second;
|
||||||
|
|
||||||
|
std::string str = line.substr(0, stringSize);
|
||||||
|
return std::make_pair(RespType::String, str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errMsg = "Unhandled response type";
|
||||||
|
return std::make_pair(RespType::Unknown, std::string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RedisClient::getRespTypeDescription(RespType respType)
|
||||||
|
{
|
||||||
|
switch (respType)
|
||||||
|
{
|
||||||
|
case RespType::Integer: return "integer";
|
||||||
|
case RespType::Error: return "error";
|
||||||
|
case RespType::String: return "string";
|
||||||
|
default: return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace ix
|
} // namespace ix
|
@@ -8,12 +8,20 @@
|
|||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <ixwebsocket/IXSocket.h>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <ixwebsocket/IXSocket.h>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
|
enum class RespType : int
|
||||||
|
{
|
||||||
|
String = 0,
|
||||||
|
Error = 1,
|
||||||
|
Integer = 2,
|
||||||
|
Unknown = 3
|
||||||
|
};
|
||||||
|
|
||||||
class RedisClient
|
class RedisClient
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -40,13 +48,22 @@ namespace ix
|
|||||||
// XADD
|
// XADD
|
||||||
std::string xadd(const std::string& channel,
|
std::string xadd(const std::string& channel,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
|
int maxLen,
|
||||||
std::string& errMsg);
|
std::string& errMsg);
|
||||||
|
std::string prepareXaddCommand(const std::string& stream,
|
||||||
std::string prepareXaddCommand(const std::string& stream, const std::string& message);
|
const std::string& message,
|
||||||
|
int maxLen);
|
||||||
std::string readXaddReply(std::string& errMsg);
|
std::string readXaddReply(std::string& errMsg);
|
||||||
|
bool sendCommand(
|
||||||
|
const std::string& commands, int commandsCount, std::string& errMsg);
|
||||||
|
|
||||||
bool sendCommand(const std::string& commands, int commandsCount, std::string& errMsg);
|
// Arbitrary commands
|
||||||
|
std::pair<RespType, std::string> send(
|
||||||
|
const std::vector<std::string>& args,
|
||||||
|
std::string& errMsg);
|
||||||
|
std::pair<RespType, std::string> readResponse(std::string& errMsg);
|
||||||
|
|
||||||
|
std::string getRespTypeDescription(RespType respType);
|
||||||
|
|
||||||
void stop();
|
void stop();
|
||||||
|
|
@@ -45,8 +45,11 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RedisServer::handleConnection(std::unique_ptr<Socket> socket,
|
void RedisServer::handleConnection(std::unique_ptr<Socket> socket,
|
||||||
std::shared_ptr<ConnectionState> connectionState)
|
std::shared_ptr<ConnectionState> connectionState,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo)
|
||||||
{
|
{
|
||||||
|
logInfo("New connection from remote ip " + connectionInfo->remoteIp);
|
||||||
|
|
||||||
_connectedClientsCount++;
|
_connectedClientsCount++;
|
||||||
|
|
||||||
while (!_stopHandlingConnections)
|
while (!_stopHandlingConnections)
|
||||||
@@ -112,7 +115,7 @@ namespace ix
|
|||||||
it.second.erase(socket.get());
|
it.second.erase(socket.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it : _subscribers)
|
for (auto&& it : _subscribers)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Subscription id: " << it.first << " #subscribers: " << it.second.size();
|
ss << "Subscription id: " << it.first << " #subscribers: " << it.second.size();
|
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IXSocket.h"
|
#include <ixwebsocket/IXSocket.h>
|
||||||
#include "IXSocketServer.h"
|
#include <ixwebsocket/IXSocketServer.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -44,7 +44,8 @@ namespace ix
|
|||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
virtual void handleConnection(std::unique_ptr<Socket>,
|
virtual void handleConnection(std::unique_ptr<Socket>,
|
||||||
std::shared_ptr<ConnectionState> connectionState) final;
|
std::shared_ptr<ConnectionState> connectionState,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo) final;
|
||||||
virtual size_t getConnectedClientsCount() final;
|
virtual size_t getConnectedClientsCount() final;
|
||||||
|
|
||||||
bool startsWith(const std::string& str, const std::string& start);
|
bool startsWith(const std::string& str, const std::string& start);
|
@@ -7,16 +7,14 @@ set (IXSNAKE_SOURCES
|
|||||||
ixsnake/IXSnakeServer.cpp
|
ixsnake/IXSnakeServer.cpp
|
||||||
ixsnake/IXSnakeProtocol.cpp
|
ixsnake/IXSnakeProtocol.cpp
|
||||||
ixsnake/IXAppConfig.cpp
|
ixsnake/IXAppConfig.cpp
|
||||||
ixsnake/IXRedisClient.cpp
|
ixsnake/IXStreamSql.cpp
|
||||||
ixsnake/IXRedisServer.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set (IXSNAKE_HEADERS
|
set (IXSNAKE_HEADERS
|
||||||
ixsnake/IXSnakeServer.h
|
ixsnake/IXSnakeServer.h
|
||||||
ixsnake/IXSnakeProtocol.h
|
ixsnake/IXSnakeProtocol.h
|
||||||
ixsnake/IXAppConfig.h
|
ixsnake/IXAppConfig.h
|
||||||
ixsnake/IXRedisClient.h
|
ixsnake/IXStreamSql.h
|
||||||
ixsnake/IXRedisServer.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(ixsnake STATIC
|
add_library(ixsnake STATIC
|
||||||
@@ -30,6 +28,7 @@ set(IXSNAKE_INCLUDE_DIRS
|
|||||||
../ixcore
|
../ixcore
|
||||||
../ixcrypto
|
../ixcrypto
|
||||||
../ixwebsocket
|
../ixwebsocket
|
||||||
|
../ixredis
|
||||||
../third_party)
|
../third_party)
|
||||||
|
|
||||||
target_include_directories( ixsnake PUBLIC ${IXSNAKE_INCLUDE_DIRS} )
|
target_include_directories( ixsnake PUBLIC ${IXSNAKE_INCLUDE_DIRS} )
|
||||||
|
@@ -26,6 +26,12 @@ namespace snake
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto roles = appConfig.apps[appkey]["roles"];
|
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"];
|
auto channel = roles[role]["secret"];
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
@@ -33,6 +33,9 @@ namespace snake
|
|||||||
// Misc
|
// Misc
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool disablePong;
|
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);
|
bool isAppKeyValid(const AppConfig& appConfig, std::string appkey);
|
||||||
|
@@ -6,16 +6,22 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IXRedisClient.h"
|
#include <ixredis/IXRedisClient.h>
|
||||||
#include <future>
|
#include <thread>
|
||||||
#include <ixwebsocket/IXConnectionState.h>
|
#include <ixwebsocket/IXConnectionState.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "IXStreamSql.h"
|
||||||
|
|
||||||
namespace snake
|
namespace snake
|
||||||
{
|
{
|
||||||
class SnakeConnectionState : public ix::ConnectionState
|
class SnakeConnectionState : public ix::ConnectionState
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
virtual ~SnakeConnectionState()
|
||||||
|
{
|
||||||
|
stopSubScriptionThread();
|
||||||
|
}
|
||||||
|
|
||||||
std::string getNonce()
|
std::string getNonce()
|
||||||
{
|
{
|
||||||
return _nonce;
|
return _nonce;
|
||||||
@@ -30,6 +36,7 @@ namespace snake
|
|||||||
{
|
{
|
||||||
return _appkey;
|
return _appkey;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAppkey(const std::string& appkey)
|
void setAppkey(const std::string& appkey)
|
||||||
{
|
{
|
||||||
_appkey = appkey;
|
_appkey = appkey;
|
||||||
@@ -39,6 +46,7 @@ namespace snake
|
|||||||
{
|
{
|
||||||
return _role;
|
return _role;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRole(const std::string& role)
|
void setRole(const std::string& role)
|
||||||
{
|
{
|
||||||
_role = role;
|
_role = role;
|
||||||
@@ -49,7 +57,24 @@ namespace snake
|
|||||||
return _redisClient;
|
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:
|
private:
|
||||||
std::string _nonce;
|
std::string _nonce;
|
||||||
|
@@ -18,21 +18,22 @@
|
|||||||
namespace snake
|
namespace snake
|
||||||
{
|
{
|
||||||
void handleError(const std::string& action,
|
void handleError(const std::string& action,
|
||||||
std::shared_ptr<ix::WebSocket> ws,
|
ix::WebSocket& ws,
|
||||||
nlohmann::json pdu,
|
uint64_t pduId,
|
||||||
const std::string& errMsg)
|
const std::string& errMsg)
|
||||||
{
|
{
|
||||||
std::string actionError(action);
|
std::string actionError(action);
|
||||||
actionError += "/error";
|
actionError += "/error";
|
||||||
|
|
||||||
nlohmann::json response = {
|
nlohmann::json response = {
|
||||||
{"action", actionError}, {"id", pdu.value("id", 1)}, {"body", {{"reason", errMsg}}}};
|
{"action", actionError}, {"id", pduId}, {"body", {{"reason", errMsg}}}};
|
||||||
ws->sendText(response.dump());
|
ws.sendText(response.dump());
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleHandshake(std::shared_ptr<SnakeConnectionState> state,
|
void handleHandshake(std::shared_ptr<SnakeConnectionState> state,
|
||||||
std::shared_ptr<ix::WebSocket> ws,
|
ix::WebSocket& ws,
|
||||||
const nlohmann::json& pdu)
|
const nlohmann::json& pdu,
|
||||||
|
uint64_t pduId)
|
||||||
{
|
{
|
||||||
std::string role = pdu["body"]["data"]["role"];
|
std::string role = pdu["body"]["data"]["role"];
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ namespace snake
|
|||||||
|
|
||||||
nlohmann::json response = {
|
nlohmann::json response = {
|
||||||
{"action", "auth/handshake/ok"},
|
{"action", "auth/handshake/ok"},
|
||||||
{"id", pdu.value("id", 1)},
|
{"id", pduId},
|
||||||
{"body",
|
{"body",
|
||||||
{
|
{
|
||||||
{"data", {{"nonce", state->getNonce()}, {"connection_id", state->getId()}}},
|
{"data", {{"nonce", state->getNonce()}, {"connection_id", state->getId()}}},
|
||||||
@@ -49,13 +50,14 @@ namespace snake
|
|||||||
|
|
||||||
auto serializedResponse = response.dump();
|
auto serializedResponse = response.dump();
|
||||||
|
|
||||||
ws->sendText(serializedResponse);
|
ws.sendText(serializedResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleAuth(std::shared_ptr<SnakeConnectionState> state,
|
void handleAuth(std::shared_ptr<SnakeConnectionState> state,
|
||||||
std::shared_ptr<ix::WebSocket> ws,
|
ix::WebSocket& ws,
|
||||||
const AppConfig& appConfig,
|
const AppConfig& appConfig,
|
||||||
const nlohmann::json& pdu)
|
const nlohmann::json& pdu,
|
||||||
|
uint64_t pduId)
|
||||||
{
|
{
|
||||||
auto secret = getRoleSecret(appConfig, state->appkey(), state->role());
|
auto secret = getRoleSecret(appConfig, state->appkey(), state->role());
|
||||||
|
|
||||||
@@ -63,9 +65,9 @@ namespace snake
|
|||||||
{
|
{
|
||||||
nlohmann::json response = {
|
nlohmann::json response = {
|
||||||
{"action", "auth/authenticate/error"},
|
{"action", "auth/authenticate/error"},
|
||||||
{"id", pdu.value("id", 1)},
|
{"id", pduId},
|
||||||
{"body", {{"error", "authentication_failed"}, {"reason", "invalid secret"}}}};
|
{"body", {{"error", "authentication_failed"}, {"reason", "invalid secret"}}}};
|
||||||
ws->sendText(response.dump());
|
ws.sendText(response.dump());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,19 +81,21 @@ namespace snake
|
|||||||
{"action", "auth/authenticate/error"},
|
{"action", "auth/authenticate/error"},
|
||||||
{"id", pdu.value("id", 1)},
|
{"id", pdu.value("id", 1)},
|
||||||
{"body", {{"error", "authentication_failed"}, {"reason", "invalid hash"}}}};
|
{"body", {{"error", "authentication_failed"}, {"reason", "invalid hash"}}}};
|
||||||
ws->sendText(response.dump());
|
ws.sendText(response.dump());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlohmann::json response = {
|
nlohmann::json response = {
|
||||||
{"action", "auth/authenticate/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}};
|
{"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,
|
void handlePublish(std::shared_ptr<SnakeConnectionState> state,
|
||||||
std::shared_ptr<ix::WebSocket> ws,
|
ix::WebSocket& ws,
|
||||||
const nlohmann::json& pdu)
|
const AppConfig& appConfig,
|
||||||
|
const nlohmann::json& pdu,
|
||||||
|
uint64_t pduId)
|
||||||
{
|
{
|
||||||
std::vector<std::string> channels;
|
std::vector<std::string> channels;
|
||||||
|
|
||||||
@@ -111,10 +115,16 @@ namespace snake
|
|||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Missing channels or channel field in publish data";
|
ss << "Missing channels or channel field in publish data";
|
||||||
handleError("rtm/publish", ws, pdu, ss.str());
|
handleError("rtm/publish", ws, pduId, ss.str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add an extra channel if the config has one specified
|
||||||
|
if (!appConfig.republishChannel.empty())
|
||||||
|
{
|
||||||
|
channels.push_back(appConfig.republishChannel);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto&& channel : channels)
|
for (auto&& channel : channels)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
@@ -125,7 +135,7 @@ namespace snake
|
|||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Cannot publish to redis host " << errMsg;
|
ss << "Cannot publish to redis host " << errMsg;
|
||||||
handleError("rtm/publish", ws, pdu, ss.str());
|
handleError("rtm/publish", ws, pduId, ss.str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,26 +143,27 @@ namespace snake
|
|||||||
nlohmann::json response = {
|
nlohmann::json response = {
|
||||||
{"action", "rtm/publish/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}};
|
{"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
|
// FIXME: this is not cancellable. We should be able to cancel the redis subscription
|
||||||
//
|
//
|
||||||
void handleRedisSubscription(std::shared_ptr<SnakeConnectionState> state,
|
void handleSubscribe(std::shared_ptr<SnakeConnectionState> state,
|
||||||
std::shared_ptr<ix::WebSocket> ws,
|
ix::WebSocket& ws,
|
||||||
const AppConfig& appConfig,
|
const AppConfig& appConfig,
|
||||||
const nlohmann::json& pdu)
|
const nlohmann::json& pdu,
|
||||||
|
uint64_t pduId)
|
||||||
{
|
{
|
||||||
std::string channel = pdu["body"]["channel"];
|
std::string channel = pdu["body"]["channel"];
|
||||||
std::string subscriptionId = channel;
|
state->subscriptionId = channel;
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << state->appkey() << "::" << channel;
|
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;
|
int port = appConfig.redisPort;
|
||||||
|
|
||||||
auto urls = appConfig.redisHosts;
|
auto urls = appConfig.redisHosts;
|
||||||
@@ -163,7 +174,7 @@ namespace snake
|
|||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Cannot connect to redis host " << hostname << ":" << port;
|
ss << "Cannot connect to redis host " << hostname << ":" << port;
|
||||||
handleError("rtm/subscribe", ws, pdu, ss.str());
|
handleError("rtm/subscribe", ws, pduId, ss.str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,80 +186,90 @@ namespace snake
|
|||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Cannot authenticated to redis";
|
ss << "Cannot authenticated to redis";
|
||||||
handleError("rtm/subscribe", ws, pdu, ss.str());
|
handleError("rtm/subscribe", ws, pduId, ss.str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int id = 0;
|
std::string filterStr;
|
||||||
auto callback = [ws, &id, &subscriptionId](const std::string& messageStr) {
|
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);
|
auto msg = nlohmann::json::parse(messageStr);
|
||||||
|
|
||||||
msg = msg["body"]["message"];
|
msg = msg["body"]["message"];
|
||||||
|
|
||||||
|
if (state->streamSql->valid() && !state->streamSql->match(msg))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nlohmann::json response = {
|
nlohmann::json response = {
|
||||||
{"action", "rtm/subscription/data"},
|
{"action", "rtm/subscription/data"},
|
||||||
{"id", id++},
|
{"id", state->id++},
|
||||||
{"body",
|
{"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;
|
std::stringstream ss;
|
||||||
ss << "Redis Response: " << redisResponse << "...";
|
ss << "Redis Response: " << redisResponse << "...";
|
||||||
ix::CoreLogger::log(ss.str().c_str());
|
ix::CoreLogger::log(ss.str().c_str());
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
nlohmann::json response = {{"action", "rtm/subscribe/ok"},
|
nlohmann::json response = {{"action", "rtm/subscribe/ok"},
|
||||||
{"id", pdu.value("id", 1)},
|
{"id", pduId},
|
||||||
{"body", {{"subscription_id", subscriptionId}}}};
|
{"body", {{"subscription_id", state->subscriptionId}}}};
|
||||||
ws->sendText(response.dump());
|
ws.sendText(response.dump());
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Subscribing to " << appChannel << "...";
|
ss << "Subscribing to " << state->appChannel << "...";
|
||||||
ix::CoreLogger::log(ss.str().c_str());
|
ix::CoreLogger::log(ss.str().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!redisClient.subscribe(appChannel, responseCallback, callback))
|
auto subscription = [&redisClient, state, &ws, pduId]
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
if (!redisClient.subscribe(state->appChannel,
|
||||||
ss << "Error subscribing to channel " << appChannel;
|
state->onRedisSubscribeResponseCallback,
|
||||||
handleError("rtm/subscribe", ws, pdu, ss.str());
|
state->onRedisSubscribeCallback))
|
||||||
return;
|
{
|
||||||
}
|
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,
|
state->subscriptionThread = std::thread(subscription);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleUnSubscribe(std::shared_ptr<SnakeConnectionState> state,
|
void handleUnSubscribe(std::shared_ptr<SnakeConnectionState> state,
|
||||||
std::shared_ptr<ix::WebSocket> ws,
|
ix::WebSocket& ws,
|
||||||
const nlohmann::json& pdu)
|
const nlohmann::json& pdu,
|
||||||
|
uint64_t pduId)
|
||||||
{
|
{
|
||||||
// extract subscription_id
|
// extract subscription_id
|
||||||
auto body = pdu["body"];
|
auto body = pdu["body"];
|
||||||
auto subscriptionId = body["subscription_id"];
|
auto subscriptionId = body["subscription_id"];
|
||||||
|
|
||||||
state->redisClient().stop();
|
state->stopSubScriptionThread();
|
||||||
|
|
||||||
nlohmann::json response = {{"action", "rtm/unsubscribe/ok"},
|
nlohmann::json response = {{"action", "rtm/unsubscribe/ok"},
|
||||||
{"id", pdu.value("id", 1)},
|
{"id", pduId},
|
||||||
{"body", {{"subscription_id", subscriptionId}}}};
|
{"body", {{"subscription_id", subscriptionId}}}};
|
||||||
ws->sendText(response.dump());
|
ws.sendText(response.dump());
|
||||||
}
|
}
|
||||||
|
|
||||||
void processCobraMessage(std::shared_ptr<SnakeConnectionState> state,
|
void processCobraMessage(std::shared_ptr<SnakeConnectionState> state,
|
||||||
std::shared_ptr<ix::WebSocket> ws,
|
ix::WebSocket& ws,
|
||||||
const AppConfig& appConfig,
|
const AppConfig& appConfig,
|
||||||
const std::string& str)
|
const std::string& str)
|
||||||
{
|
{
|
||||||
@@ -263,31 +284,32 @@ namespace snake
|
|||||||
ss << "malformed json pdu: " << e.what() << " -> " << str << "";
|
ss << "malformed json pdu: " << e.what() << " -> " << str << "";
|
||||||
|
|
||||||
nlohmann::json response = {{"body", {{"error", "invalid_json"}, {"reason", ss.str()}}}};
|
nlohmann::json response = {{"body", {{"error", "invalid_json"}, {"reason", ss.str()}}}};
|
||||||
ws->sendText(response.dump());
|
ws.sendText(response.dump());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto action = pdu["action"];
|
auto action = pdu["action"];
|
||||||
|
uint64_t pduId = pdu.value("id", 1);
|
||||||
|
|
||||||
if (action == "auth/handshake")
|
if (action == "auth/handshake")
|
||||||
{
|
{
|
||||||
handleHandshake(state, ws, pdu);
|
handleHandshake(state, ws, pdu, pduId);
|
||||||
}
|
}
|
||||||
else if (action == "auth/authenticate")
|
else if (action == "auth/authenticate")
|
||||||
{
|
{
|
||||||
handleAuth(state, ws, appConfig, pdu);
|
handleAuth(state, ws, appConfig, pdu, pduId);
|
||||||
}
|
}
|
||||||
else if (action == "rtm/publish")
|
else if (action == "rtm/publish")
|
||||||
{
|
{
|
||||||
handlePublish(state, ws, pdu);
|
handlePublish(state, ws, appConfig, pdu, pduId);
|
||||||
}
|
}
|
||||||
else if (action == "rtm/subscribe")
|
else if (action == "rtm/subscribe")
|
||||||
{
|
{
|
||||||
handleSubscribe(state, ws, appConfig, pdu);
|
handleSubscribe(state, ws, appConfig, pdu, pduId);
|
||||||
}
|
}
|
||||||
else if (action == "rtm/unsubscribe")
|
else if (action == "rtm/unsubscribe")
|
||||||
{
|
{
|
||||||
handleUnSubscribe(state, ws, pdu);
|
handleUnSubscribe(state, ws, pdu, pduId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -20,7 +20,7 @@ namespace snake
|
|||||||
struct AppConfig;
|
struct AppConfig;
|
||||||
|
|
||||||
void processCobraMessage(std::shared_ptr<SnakeConnectionState> state,
|
void processCobraMessage(std::shared_ptr<SnakeConnectionState> state,
|
||||||
std::shared_ptr<ix::WebSocket> ws,
|
ix::WebSocket& ws,
|
||||||
const AppConfig& appConfig,
|
const AppConfig& appConfig,
|
||||||
const std::string& str);
|
const std::string& str);
|
||||||
} // namespace snake
|
} // namespace snake
|
||||||
|
@@ -59,65 +59,68 @@ namespace snake
|
|||||||
};
|
};
|
||||||
_server.setConnectionStateFactory(factory);
|
_server.setConnectionStateFactory(factory);
|
||||||
|
|
||||||
_server.setOnConnectionCallback(
|
_server.setOnClientMessageCallback(
|
||||||
[this](std::shared_ptr<ix::WebSocket> webSocket,
|
[this](std::shared_ptr<ix::ConnectionState> connectionState,
|
||||||
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 state = std::dynamic_pointer_cast<SnakeConnectionState>(connectionState);
|
||||||
|
auto remoteIp = connectionInfo.remoteIp;
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "[" << state->getId() << "] ";
|
||||||
|
|
||||||
webSocket->setOnMessageCallback(
|
ix::LogLevel logLevel = ix::LogLevel::Debug;
|
||||||
[this, webSocket, state](const ix::WebSocketMessagePtr& msg) {
|
if (msg->type == ix::WebSocketMessageType::Open)
|
||||||
std::stringstream ss;
|
{
|
||||||
ix::LogLevel logLevel = ix::LogLevel::Debug;
|
ss << "New connection" << std::endl;
|
||||||
if (msg->type == ix::WebSocketMessageType::Open)
|
ss << "remote ip: " << remoteIp << std::endl;
|
||||||
{
|
ss << "id: " << state->getId() << std::endl;
|
||||||
ss << "New connection" << std::endl;
|
ss << "Uri: " << msg->openInfo.uri << std::endl;
|
||||||
ss << "id: " << state->getId() << std::endl;
|
ss << "Headers:" << std::endl;
|
||||||
ss << "Uri: " << msg->openInfo.uri << std::endl;
|
for (auto it : msg->openInfo.headers)
|
||||||
ss << "Headers:" << std::endl;
|
{
|
||||||
for (auto it : msg->openInfo.headers)
|
ss << it.first << ": " << it.second << std::endl;
|
||||||
{
|
}
|
||||||
ss << it.first << ": " << it.second << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string appkey = parseAppKey(msg->openInfo.uri);
|
std::string appkey = parseAppKey(msg->openInfo.uri);
|
||||||
state->setAppkey(appkey);
|
state->setAppkey(appkey);
|
||||||
|
|
||||||
// Connect to redis first
|
// Connect to redis first
|
||||||
if (!state->redisClient().connect(_appConfig.redisHosts[0],
|
if (!state->redisClient().connect(_appConfig.redisHosts[0],
|
||||||
_appConfig.redisPort))
|
_appConfig.redisPort))
|
||||||
{
|
{
|
||||||
ss << "Cannot connect to redis host" << std::endl;
|
ss << "Cannot connect to redis host" << std::endl;
|
||||||
logLevel = ix::LogLevel::Error;
|
logLevel = ix::LogLevel::Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
ss << "Closed connection"
|
ss << "Closed connection"
|
||||||
<< " code " << msg->closeInfo.code << " reason "
|
<< " code " << msg->closeInfo.code << " reason "
|
||||||
<< msg->closeInfo.reason << std::endl;
|
<< msg->closeInfo.reason << std::endl;
|
||||||
}
|
}
|
||||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||||
logLevel = ix::LogLevel::Error;
|
logLevel = ix::LogLevel::Error;
|
||||||
}
|
}
|
||||||
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
||||||
{
|
{
|
||||||
ss << "Received message fragment" << std::endl;
|
ss << "Received message fragment" << std::endl;
|
||||||
}
|
}
|
||||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
ss << "Received " << msg->wireSize << " bytes" << std::endl;
|
ss << "Received " << msg->wireSize << " bytes" << " " << msg->str << std::endl;
|
||||||
processCobraMessage(state, webSocket, _appConfig, msg->str);
|
processCobraMessage(state, webSocket, _appConfig, msg->str);
|
||||||
}
|
}
|
||||||
|
|
||||||
ix::CoreLogger::log(ss.str().c_str(), logLevel);
|
ix::CoreLogger::log(ss.str().c_str(), logLevel);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
auto res = _server.listen();
|
auto res = _server.listen();
|
||||||
if (!res.first)
|
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;
|
||||||
|
};
|
||||||
|
}
|
25
ixwebsocket/IXConnectionInfo.h
Normal file
25
ixwebsocket/IXConnectionInfo.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* IXConnectionInfo.h
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
struct ConnectionInfo
|
||||||
|
{
|
||||||
|
std::string remoteIp;
|
||||||
|
int remotePort;
|
||||||
|
|
||||||
|
ConnectionInfo(const std::string& r = std::string(), int p = 0)
|
||||||
|
: remoteIp(r)
|
||||||
|
, remotePort(p)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace ix
|
@@ -16,7 +16,10 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
@@ -127,7 +130,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
// We only have one socket connection, so we cannot
|
// We only have one socket connection, so we cannot
|
||||||
// make multiple requests concurrently.
|
// make multiple requests concurrently.
|
||||||
std::lock_guard<std::mutex> lock(_mutex);
|
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||||
|
|
||||||
uint64_t uploadSize = 0;
|
uint64_t uploadSize = 0;
|
||||||
uint64_t downloadSize = 0;
|
uint64_t downloadSize = 0;
|
||||||
@@ -174,11 +177,13 @@ namespace ix
|
|||||||
ss << verb << " " << path << " HTTP/1.1\r\n";
|
ss << verb << " " << path << " HTTP/1.1\r\n";
|
||||||
ss << "Host: " << host << "\r\n";
|
ss << "Host: " << host << "\r\n";
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
if (args->compress)
|
if (args->compress)
|
||||||
{
|
{
|
||||||
ss << "Accept-Encoding: gzip"
|
ss << "Accept-Encoding: gzip"
|
||||||
<< "\r\n";
|
<< "\r\n";
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Append extra headers
|
// Append extra headers
|
||||||
for (auto&& it : args->extraHeaders)
|
for (auto&& it : args->extraHeaders)
|
||||||
@@ -495,6 +500,7 @@ namespace ix
|
|||||||
|
|
||||||
downloadSize = payload.size();
|
downloadSize = payload.size();
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
// If the content was compressed with gzip, decode it
|
// If the content was compressed with gzip, decode it
|
||||||
if (headers["Content-Encoding"] == "gzip")
|
if (headers["Content-Encoding"] == "gzip")
|
||||||
{
|
{
|
||||||
@@ -513,6 +519,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
payload = decompressedPayload;
|
payload = decompressedPayload;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return std::make_shared<HttpResponse>(code,
|
return std::make_shared<HttpResponse>(code,
|
||||||
description,
|
description,
|
||||||
@@ -672,6 +679,7 @@ namespace ix
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
bool HttpClient::gzipInflate(const std::string& in, std::string& out)
|
bool HttpClient::gzipInflate(const std::string& in, std::string& out)
|
||||||
{
|
{
|
||||||
z_stream inflateState;
|
z_stream inflateState;
|
||||||
@@ -716,6 +724,7 @@ namespace ix
|
|||||||
inflateEnd(&inflateState);
|
inflateEnd(&inflateState);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args)
|
void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args)
|
||||||
{
|
{
|
||||||
|
@@ -90,7 +90,9 @@ namespace ix
|
|||||||
private:
|
private:
|
||||||
void log(const std::string& msg, HttpRequestArgsPtr args);
|
void log(const std::string& msg, HttpRequestArgsPtr args);
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
bool gzipInflate(const std::string& in, std::string& out);
|
bool gzipInflate(const std::string& in, std::string& out);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Async API background thread runner
|
// Async API background thread runner
|
||||||
void run();
|
void run();
|
||||||
@@ -103,7 +105,9 @@ namespace ix
|
|||||||
std::thread _thread;
|
std::thread _thread;
|
||||||
|
|
||||||
std::unique_ptr<Socket> _socket;
|
std::unique_ptr<Socket> _socket;
|
||||||
std::mutex _mutex; // to protect accessing the _socket (only one socket per client)
|
std::recursive_mutex _mutex; // to protect accessing the _socket (only one socket per
|
||||||
|
// client) the mutex needs to be recursive as this function
|
||||||
|
// might be called recursively to follow HTTP redirections
|
||||||
|
|
||||||
SocketTLSOptions _tlsOptions;
|
SocketTLSOptions _tlsOptions;
|
||||||
|
|
||||||
|
@@ -9,11 +9,14 @@
|
|||||||
#include "IXNetSystem.h"
|
#include "IXNetSystem.h"
|
||||||
#include "IXSocketConnect.h"
|
#include "IXSocketConnect.h"
|
||||||
#include "IXUserAgent.h"
|
#include "IXUserAgent.h"
|
||||||
|
#include <cstring>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <cstring>
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -41,6 +44,7 @@ namespace
|
|||||||
return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
|
return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
std::string gzipCompress(const std::string& str)
|
std::string gzipCompress(const std::string& str)
|
||||||
{
|
{
|
||||||
z_stream zs; // z_stream is zlib's control structure
|
z_stream zs; // z_stream is zlib's control structure
|
||||||
@@ -50,8 +54,11 @@ namespace
|
|||||||
const int windowBits = 15;
|
const int windowBits = 15;
|
||||||
const int GZIP_ENCODING = 16;
|
const int GZIP_ENCODING = 16;
|
||||||
|
|
||||||
deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
deflateInit2(&zs,
|
||||||
windowBits | GZIP_ENCODING, 8,
|
Z_DEFAULT_COMPRESSION,
|
||||||
|
Z_DEFLATED,
|
||||||
|
windowBits | GZIP_ENCODING,
|
||||||
|
8,
|
||||||
Z_DEFAULT_STRATEGY);
|
Z_DEFAULT_STRATEGY);
|
||||||
|
|
||||||
zs.next_in = (Bytef*) str.data();
|
zs.next_in = (Bytef*) str.data();
|
||||||
@@ -69,18 +76,18 @@ namespace
|
|||||||
|
|
||||||
ret = deflate(&zs, Z_FINISH);
|
ret = deflate(&zs, Z_FINISH);
|
||||||
|
|
||||||
if(outstring.size() < zs.total_out)
|
if (outstring.size() < zs.total_out)
|
||||||
{
|
{
|
||||||
// append the block to the output string
|
// append the block to the output string
|
||||||
outstring.append(outbuffer,
|
outstring.append(outbuffer, zs.total_out - outstring.size());
|
||||||
zs.total_out - outstring.size());
|
|
||||||
}
|
}
|
||||||
} while(ret == Z_OK);
|
} while (ret == Z_OK);
|
||||||
|
|
||||||
deflateEnd(&zs);
|
deflateEnd(&zs);
|
||||||
|
|
||||||
return outstring;
|
return outstring;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
@@ -113,7 +120,8 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HttpServer::handleConnection(std::unique_ptr<Socket> socket,
|
void HttpServer::handleConnection(std::unique_ptr<Socket> socket,
|
||||||
std::shared_ptr<ConnectionState> connectionState)
|
std::shared_ptr<ConnectionState> connectionState,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo)
|
||||||
{
|
{
|
||||||
_connectedClientsCount++;
|
_connectedClientsCount++;
|
||||||
|
|
||||||
@@ -122,7 +130,8 @@ namespace ix
|
|||||||
|
|
||||||
if (std::get<0>(ret))
|
if (std::get<0>(ret))
|
||||||
{
|
{
|
||||||
auto response = _onConnectionCallback(std::get<2>(ret), connectionState);
|
auto response =
|
||||||
|
_onConnectionCallback(std::get<2>(ret), connectionState, std::move(connectionInfo));
|
||||||
if (!Http::sendResponse(response, socket))
|
if (!Http::sendResponse(response, socket))
|
||||||
{
|
{
|
||||||
logError("Cannot send response");
|
logError("Cannot send response");
|
||||||
@@ -142,7 +151,8 @@ namespace ix
|
|||||||
{
|
{
|
||||||
setOnConnectionCallback(
|
setOnConnectionCallback(
|
||||||
[this](HttpRequestPtr request,
|
[this](HttpRequestPtr request,
|
||||||
std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr {
|
std::shared_ptr<ConnectionState> /*connectionState*/,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr {
|
||||||
std::string uri(request->uri);
|
std::string uri(request->uri);
|
||||||
if (uri.empty() || uri == "/")
|
if (uri.empty() || uri == "/")
|
||||||
{
|
{
|
||||||
@@ -163,15 +173,19 @@ namespace ix
|
|||||||
|
|
||||||
std::string content = res.second;
|
std::string content = res.second;
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
std::string acceptEncoding = request->headers["Accept-encoding"];
|
std::string acceptEncoding = request->headers["Accept-encoding"];
|
||||||
if (acceptEncoding == "gzip" || acceptEncoding == "*")
|
if (acceptEncoding == "*" || acceptEncoding.find("gzip") != std::string::npos)
|
||||||
{
|
{
|
||||||
content = gzipCompress(content);
|
content = gzipCompress(content);
|
||||||
|
headers["Content-Encoding"] = "gzip";
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Log request
|
// Log request
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << request->method << " " << request->headers["User-Agent"] << " "
|
ss << connectionInfo->remoteIp << ":" << connectionInfo->remotePort << " "
|
||||||
|
<< request->method << " " << request->headers["User-Agent"] << " "
|
||||||
<< request->uri << " " << content.size();
|
<< request->uri << " " << content.size();
|
||||||
logInfo(ss.str());
|
logInfo(ss.str());
|
||||||
|
|
||||||
@@ -195,15 +209,16 @@ namespace ix
|
|||||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
|
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
|
||||||
//
|
//
|
||||||
setOnConnectionCallback(
|
setOnConnectionCallback(
|
||||||
[this,
|
[this, redirectUrl](HttpRequestPtr request,
|
||||||
redirectUrl](HttpRequestPtr request,
|
std::shared_ptr<ConnectionState> /*connectionState*/,
|
||||||
std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr {
|
std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr {
|
||||||
WebSocketHttpHeaders headers;
|
WebSocketHttpHeaders headers;
|
||||||
headers["Server"] = userAgent();
|
headers["Server"] = userAgent();
|
||||||
|
|
||||||
// Log request
|
// Log request
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << request->method << " " << request->headers["User-Agent"] << " "
|
ss << connectionInfo->remoteIp << ":" << connectionInfo->remotePort << " "
|
||||||
|
<< request->method << " " << request->headers["User-Agent"] << " "
|
||||||
<< request->uri;
|
<< request->uri;
|
||||||
logInfo(ss.str());
|
logInfo(ss.str());
|
||||||
|
|
||||||
|
@@ -23,7 +23,9 @@ namespace ix
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using OnConnectionCallback =
|
using OnConnectionCallback =
|
||||||
std::function<HttpResponsePtr(HttpRequestPtr, std::shared_ptr<ConnectionState>)>;
|
std::function<HttpResponsePtr(HttpRequestPtr,
|
||||||
|
std::shared_ptr<ConnectionState>,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo)>;
|
||||||
|
|
||||||
HttpServer(int port = SocketServer::kDefaultPort,
|
HttpServer(int port = SocketServer::kDefaultPort,
|
||||||
const std::string& host = SocketServer::kDefaultHost,
|
const std::string& host = SocketServer::kDefaultHost,
|
||||||
@@ -44,7 +46,8 @@ namespace ix
|
|||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
virtual void handleConnection(std::unique_ptr<Socket>,
|
virtual void handleConnection(std::unique_ptr<Socket>,
|
||||||
std::shared_ptr<ConnectionState> connectionState) final;
|
std::shared_ptr<ConnectionState> connectionState,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo) final;
|
||||||
virtual size_t getConnectedClientsCount() final;
|
virtual size_t getConnectedClientsCount() final;
|
||||||
|
|
||||||
void setDefaultConnectionCallback();
|
void setDefaultConnectionCallback();
|
||||||
|
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include "IXSelectInterruptFactory.h"
|
#include "IXSelectInterruptFactory.h"
|
||||||
|
|
||||||
#include "IXUniquePtr.h"
|
|
||||||
#if defined(__linux__) || defined(__APPLE__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
#include "IXSelectInterruptPipe.h"
|
#include "IXSelectInterruptPipe.h"
|
||||||
#else
|
#else
|
||||||
@@ -18,9 +17,9 @@ namespace ix
|
|||||||
SelectInterruptPtr createSelectInterrupt()
|
SelectInterruptPtr createSelectInterrupt()
|
||||||
{
|
{
|
||||||
#if defined(__linux__) || defined(__APPLE__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
return ix::make_unique<SelectInterruptPipe>();
|
return std::make_unique<SelectInterruptPipe>();
|
||||||
#else
|
#else
|
||||||
return ix::make_unique<SelectInterrupt>();
|
return std::make_unique<SelectInterrupt>();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@@ -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"
|
#include "IXSelectInterruptPipe.h"
|
||||||
|
|
||||||
@@ -144,3 +146,5 @@ namespace ix
|
|||||||
return _fildes[kPipeReadIndex];
|
return _fildes[kPipeReadIndex];
|
||||||
}
|
}
|
||||||
} // namespace ix
|
} // 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
|
@@ -10,7 +10,6 @@
|
|||||||
#include "IXNetSystem.h"
|
#include "IXNetSystem.h"
|
||||||
#include "IXSelectInterrupt.h"
|
#include "IXSelectInterrupt.h"
|
||||||
#include "IXSocket.h"
|
#include "IXSocket.h"
|
||||||
#include "IXUniquePtr.h"
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@@ -66,7 +65,7 @@ namespace ix
|
|||||||
|
|
||||||
int timeoutMs = 10;
|
int timeoutMs = 10;
|
||||||
bool readyToRead = false;
|
bool readyToRead = false;
|
||||||
auto selectInterrupt = ix::make_unique<SelectInterrupt>();
|
auto selectInterrupt = std::make_unique<SelectInterrupt>();
|
||||||
PollResultType pollResult = Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt);
|
PollResultType pollResult = Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt);
|
||||||
|
|
||||||
if (pollResult == PollResultType::Timeout)
|
if (pollResult == PollResultType::Timeout)
|
||||||
|
@@ -5,7 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IXSocketFactory.h"
|
#include "IXSocketFactory.h"
|
||||||
#include "IXUniquePtr.h"
|
|
||||||
|
|
||||||
#ifdef IXWEBSOCKET_USE_TLS
|
#ifdef IXWEBSOCKET_USE_TLS
|
||||||
|
|
||||||
@@ -36,17 +35,17 @@ namespace ix
|
|||||||
|
|
||||||
if (!tls)
|
if (!tls)
|
||||||
{
|
{
|
||||||
socket = ix::make_unique<Socket>(fd);
|
socket = std::make_unique<Socket>(fd);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef IXWEBSOCKET_USE_TLS
|
#ifdef IXWEBSOCKET_USE_TLS
|
||||||
#if defined(IXWEBSOCKET_USE_MBED_TLS)
|
#if defined(IXWEBSOCKET_USE_MBED_TLS)
|
||||||
socket = ix::make_unique<SocketMbedTLS>(tlsOptions, fd);
|
socket = std::make_unique<SocketMbedTLS>(tlsOptions, fd);
|
||||||
#elif defined(IXWEBSOCKET_USE_OPEN_SSL)
|
#elif defined(IXWEBSOCKET_USE_OPEN_SSL)
|
||||||
socket = ix::make_unique<SocketOpenSSL>(tlsOptions, fd);
|
socket = std::make_unique<SocketOpenSSL>(tlsOptions, fd);
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
socket = ix::make_unique<SocketAppleSSL>(tlsOptions, fd);
|
socket = std::make_unique<SocketAppleSSL>(tlsOptions, fd);
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
errorMsg = "TLS support is not enabled on this platform.";
|
errorMsg = "TLS support is not enabled on this platform.";
|
||||||
|
@@ -12,7 +12,6 @@
|
|||||||
#include "IXSocket.h"
|
#include "IXSocket.h"
|
||||||
#include "IXSocketConnect.h"
|
#include "IXSocketConnect.h"
|
||||||
#include "IXSocketFactory.h"
|
#include "IXSocketFactory.h"
|
||||||
#include "IXUniquePtr.h"
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -23,7 +22,7 @@ namespace ix
|
|||||||
const int SocketServer::kDefaultPort(8080);
|
const int SocketServer::kDefaultPort(8080);
|
||||||
const std::string SocketServer::kDefaultHost("127.0.0.1");
|
const std::string SocketServer::kDefaultHost("127.0.0.1");
|
||||||
const int SocketServer::kDefaultTcpBacklog(5);
|
const int SocketServer::kDefaultTcpBacklog(5);
|
||||||
const size_t SocketServer::kDefaultMaxConnections(32);
|
const size_t SocketServer::kDefaultMaxConnections(128);
|
||||||
const int SocketServer::kDefaultAddressFamily(AF_INET);
|
const int SocketServer::kDefaultAddressFamily(AF_INET);
|
||||||
|
|
||||||
SocketServer::SocketServer(
|
SocketServer::SocketServer(
|
||||||
@@ -259,7 +258,7 @@ namespace ix
|
|||||||
// Use poll to check whether a new connection is in progress
|
// Use poll to check whether a new connection is in progress
|
||||||
int timeoutMs = 10;
|
int timeoutMs = 10;
|
||||||
bool readyToRead = true;
|
bool readyToRead = true;
|
||||||
auto selectInterrupt = ix::make_unique<SelectInterrupt>();
|
auto selectInterrupt = std::make_unique<SelectInterrupt>();
|
||||||
PollResultType pollResult =
|
PollResultType pollResult =
|
||||||
Socket::poll(readyToRead, timeoutMs, _serverFd, selectInterrupt);
|
Socket::poll(readyToRead, timeoutMs, _serverFd, selectInterrupt);
|
||||||
|
|
||||||
@@ -277,6 +276,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Accept a connection.
|
// Accept a connection.
|
||||||
|
// FIXME: Is this working for ipv6 ?
|
||||||
struct sockaddr_in client; // client address information
|
struct sockaddr_in client; // client address information
|
||||||
int clientFd; // socket connected to client
|
int clientFd; // socket connected to client
|
||||||
socklen_t addressLen = sizeof(client);
|
socklen_t addressLen = sizeof(client);
|
||||||
@@ -308,6 +308,45 @@ namespace ix
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo;
|
||||||
|
|
||||||
|
if (_addressFamily == AF_INET)
|
||||||
|
{
|
||||||
|
char remoteIp[INET_ADDRSTRLEN];
|
||||||
|
if (inet_ntop(AF_INET, &client.sin_addr, remoteIp, INET_ADDRSTRLEN) == nullptr)
|
||||||
|
{
|
||||||
|
int err = Socket::getErrno();
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "SocketServer::run() error calling inet_ntop (ipv4): " << err << ", "
|
||||||
|
<< strerror(err);
|
||||||
|
logError(ss.str());
|
||||||
|
|
||||||
|
Socket::closeSocket(clientFd);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectionInfo = std::make_unique<ConnectionInfo>(remoteIp, client.sin_port);
|
||||||
|
}
|
||||||
|
else // AF_INET6
|
||||||
|
{
|
||||||
|
char remoteIp[INET6_ADDRSTRLEN];
|
||||||
|
if (inet_ntop(AF_INET6, &client.sin_addr, remoteIp, INET6_ADDRSTRLEN) == nullptr)
|
||||||
|
{
|
||||||
|
int err = Socket::getErrno();
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "SocketServer::run() error calling inet_ntop (ipv6): " << err << ", "
|
||||||
|
<< strerror(err);
|
||||||
|
logError(ss.str());
|
||||||
|
|
||||||
|
Socket::closeSocket(clientFd);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectionInfo = std::make_unique<ConnectionInfo>(remoteIp, client.sin_port);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<ConnectionState> connectionState;
|
std::shared_ptr<ConnectionState> connectionState;
|
||||||
if (_connectionStateFactory)
|
if (_connectionStateFactory)
|
||||||
{
|
{
|
||||||
@@ -340,10 +379,13 @@ namespace ix
|
|||||||
|
|
||||||
// Launch the handleConnection work asynchronously in its own thread.
|
// Launch the handleConnection work asynchronously in its own thread.
|
||||||
std::lock_guard<std::mutex> lock(_connectionsThreadsMutex);
|
std::lock_guard<std::mutex> lock(_connectionsThreadsMutex);
|
||||||
_connectionsThreads.push_back(std::make_pair(
|
_connectionsThreads.push_back(
|
||||||
connectionState,
|
std::make_pair(connectionState,
|
||||||
std::thread(
|
std::thread(&SocketServer::handleConnection,
|
||||||
&SocketServer::handleConnection, this, std::move(socket), connectionState)));
|
this,
|
||||||
|
std::move(socket),
|
||||||
|
connectionState,
|
||||||
|
std::move(connectionInfo))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "IXConnectionInfo.h"
|
||||||
#include "IXConnectionState.h"
|
#include "IXConnectionState.h"
|
||||||
#include "IXSocketTLSOptions.h"
|
#include "IXSocketTLSOptions.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@@ -102,7 +103,8 @@ namespace ix
|
|||||||
ConnectionStateFactory _connectionStateFactory;
|
ConnectionStateFactory _connectionStateFactory;
|
||||||
|
|
||||||
virtual void handleConnection(std::unique_ptr<Socket>,
|
virtual void handleConnection(std::unique_ptr<Socket>,
|
||||||
std::shared_ptr<ConnectionState> connectionState) = 0;
|
std::shared_ptr<ConnectionState> connectionState,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo) = 0;
|
||||||
virtual size_t getConnectedClientsCount() = 0;
|
virtual size_t getConnectedClientsCount() = 0;
|
||||||
|
|
||||||
// Returns true if all connection threads are joined
|
// Returns true if all connection threads are joined
|
||||||
|
@@ -1,18 +0,0 @@
|
|||||||
/*
|
|
||||||
* IXUniquePtr.h
|
|
||||||
* Author: Benjamin Sergeant
|
|
||||||
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace ix
|
|
||||||
{
|
|
||||||
template<typename T, typename... Args>
|
|
||||||
std::unique_ptr<T> make_unique(Args&&... args)
|
|
||||||
{
|
|
||||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
|
||||||
}
|
|
||||||
}
|
|
@@ -32,8 +32,8 @@
|
|||||||
#include "IXUrlParser.h"
|
#include "IXUrlParser.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
#include "IXWebSocketVersion.h"
|
#include "IXWebSocketVersion.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// Platform name
|
// Platform name
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
@@ -77,8 +79,10 @@ namespace ix
|
|||||||
ss << " nossl";
|
ss << " nossl";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
// Zlib version
|
// Zlib version
|
||||||
ss << " zlib " << ZLIB_VERSION;
|
ss << " zlib " << ZLIB_VERSION;
|
||||||
|
#endif
|
||||||
|
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
@@ -46,6 +46,7 @@ namespace ix
|
|||||||
WebSocket::~WebSocket()
|
WebSocket::~WebSocket()
|
||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
|
_ws.setOnCloseCallback(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocket::setUrl(const std::string& url)
|
void WebSocket::setUrl(const std::string& url)
|
||||||
|
@@ -28,21 +28,26 @@ namespace ix
|
|||||||
WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor()
|
WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor()
|
||||||
: _compressBufferSize(kBufferSize)
|
: _compressBufferSize(kBufferSize)
|
||||||
{
|
{
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
memset(&_deflateState, 0, sizeof(_deflateState));
|
memset(&_deflateState, 0, sizeof(_deflateState));
|
||||||
|
|
||||||
_deflateState.zalloc = Z_NULL;
|
_deflateState.zalloc = Z_NULL;
|
||||||
_deflateState.zfree = Z_NULL;
|
_deflateState.zfree = Z_NULL;
|
||||||
_deflateState.opaque = Z_NULL;
|
_deflateState.opaque = Z_NULL;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketPerMessageDeflateCompressor::~WebSocketPerMessageDeflateCompressor()
|
WebSocketPerMessageDeflateCompressor::~WebSocketPerMessageDeflateCompressor()
|
||||||
{
|
{
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
deflateEnd(&_deflateState);
|
deflateEnd(&_deflateState);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
|
bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
|
||||||
bool clientNoContextTakeOver)
|
bool clientNoContextTakeOver)
|
||||||
{
|
{
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
int ret = deflateInit2(&_deflateState,
|
int ret = deflateInit2(&_deflateState,
|
||||||
Z_DEFAULT_COMPRESSION,
|
Z_DEFAULT_COMPRESSION,
|
||||||
Z_DEFLATED,
|
Z_DEFLATED,
|
||||||
@@ -57,17 +62,49 @@ namespace ix
|
|||||||
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
|
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketPerMessageDeflateCompressor::endsWith(const std::string& value,
|
template<typename T>
|
||||||
const std::string& ending)
|
bool WebSocketPerMessageDeflateCompressor::endsWithEmptyUnCompressedBlock(const T& value)
|
||||||
{
|
{
|
||||||
if (ending.size() > value.size()) return false;
|
if (kEmptyUncompressedBlock.size() > value.size()) return false;
|
||||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
auto N = value.size();
|
||||||
|
return value[N - 1] == kEmptyUncompressedBlock[3] &&
|
||||||
|
value[N - 2] == kEmptyUncompressedBlock[2] &&
|
||||||
|
value[N - 3] == kEmptyUncompressedBlock[1] &&
|
||||||
|
value[N - 4] == kEmptyUncompressedBlock[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketPerMessageDeflateCompressor::compress(const std::string& in, std::string& out)
|
bool WebSocketPerMessageDeflateCompressor::compress(const std::string& in, std::string& out)
|
||||||
{
|
{
|
||||||
|
return compressData(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketPerMessageDeflateCompressor::compress(const std::string& in,
|
||||||
|
std::vector<uint8_t>& out)
|
||||||
|
{
|
||||||
|
return compressData(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketPerMessageDeflateCompressor::compress(const std::vector<uint8_t>& in,
|
||||||
|
std::string& out)
|
||||||
|
{
|
||||||
|
return compressData(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketPerMessageDeflateCompressor::compress(const std::vector<uint8_t>& in,
|
||||||
|
std::vector<uint8_t>& out)
|
||||||
|
{
|
||||||
|
return compressData(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename S>
|
||||||
|
bool WebSocketPerMessageDeflateCompressor::compressData(const T& in, S& out)
|
||||||
|
{
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
//
|
//
|
||||||
// 7.2.1. Compression
|
// 7.2.1. Compression
|
||||||
//
|
//
|
||||||
@@ -96,7 +133,8 @@ namespace ix
|
|||||||
// The normal buffer size should be 6 but
|
// The normal buffer size should be 6 but
|
||||||
// we remove the 4 octets from the tail (#4)
|
// we remove the 4 octets from the tail (#4)
|
||||||
uint8_t buf[2] = {0x02, 0x00};
|
uint8_t buf[2] = {0x02, 0x00};
|
||||||
out.append((char*) (buf), 2);
|
out.push_back(buf[0]);
|
||||||
|
out.push_back(buf[1]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -114,15 +152,18 @@ namespace ix
|
|||||||
|
|
||||||
output = _compressBufferSize - _deflateState.avail_out;
|
output = _compressBufferSize - _deflateState.avail_out;
|
||||||
|
|
||||||
out.append((char*) (_compressBuffer.get()), output);
|
out.insert(out.end(), _compressBuffer.get(), _compressBuffer.get() + output);
|
||||||
} while (_deflateState.avail_out == 0);
|
} while (_deflateState.avail_out == 0);
|
||||||
|
|
||||||
if (endsWith(out, kEmptyUncompressedBlock))
|
if (endsWithEmptyUnCompressedBlock(out))
|
||||||
{
|
{
|
||||||
out.resize(out.size() - 4);
|
out.resize(out.size() - 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -131,6 +172,7 @@ namespace ix
|
|||||||
WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor()
|
WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor()
|
||||||
: _compressBufferSize(kBufferSize)
|
: _compressBufferSize(kBufferSize)
|
||||||
{
|
{
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
memset(&_inflateState, 0, sizeof(_inflateState));
|
memset(&_inflateState, 0, sizeof(_inflateState));
|
||||||
|
|
||||||
_inflateState.zalloc = Z_NULL;
|
_inflateState.zalloc = Z_NULL;
|
||||||
@@ -138,16 +180,20 @@ namespace ix
|
|||||||
_inflateState.opaque = Z_NULL;
|
_inflateState.opaque = Z_NULL;
|
||||||
_inflateState.avail_in = 0;
|
_inflateState.avail_in = 0;
|
||||||
_inflateState.next_in = Z_NULL;
|
_inflateState.next_in = Z_NULL;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketPerMessageDeflateDecompressor::~WebSocketPerMessageDeflateDecompressor()
|
WebSocketPerMessageDeflateDecompressor::~WebSocketPerMessageDeflateDecompressor()
|
||||||
{
|
{
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
inflateEnd(&_inflateState);
|
inflateEnd(&_inflateState);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
|
bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
|
||||||
bool clientNoContextTakeOver)
|
bool clientNoContextTakeOver)
|
||||||
{
|
{
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
int ret = inflateInit2(&_inflateState, -1 * inflateBits);
|
int ret = inflateInit2(&_inflateState, -1 * inflateBits);
|
||||||
|
|
||||||
if (ret != Z_OK) return false;
|
if (ret != Z_OK) return false;
|
||||||
@@ -157,10 +203,14 @@ namespace ix
|
|||||||
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
|
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in, std::string& out)
|
bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in, std::string& out)
|
||||||
{
|
{
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
//
|
//
|
||||||
// 7.2.2. Decompression
|
// 7.2.2. Decompression
|
||||||
//
|
//
|
||||||
@@ -197,5 +247,8 @@ namespace ix
|
|||||||
} while (_inflateState.avail_out == 0);
|
} while (_inflateState.avail_out == 0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@@ -6,9 +6,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
#include "zlib.h"
|
#include "zlib.h"
|
||||||
|
#endif
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
@@ -20,14 +23,23 @@ namespace ix
|
|||||||
|
|
||||||
bool init(uint8_t deflateBits, bool clientNoContextTakeOver);
|
bool init(uint8_t deflateBits, bool clientNoContextTakeOver);
|
||||||
bool compress(const std::string& in, std::string& out);
|
bool compress(const std::string& in, std::string& out);
|
||||||
|
bool compress(const std::string& in, std::vector<uint8_t>& out);
|
||||||
|
bool compress(const std::vector<uint8_t>& in, std::string& out);
|
||||||
|
bool compress(const std::vector<uint8_t>& in, std::vector<uint8_t>& out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool endsWith(const std::string& value, const std::string& ending);
|
template<typename T, typename S>
|
||||||
|
bool compressData(const T& in, S& out);
|
||||||
|
template<typename T>
|
||||||
|
bool endsWithEmptyUnCompressedBlock(const T& value);
|
||||||
|
|
||||||
int _flush;
|
int _flush;
|
||||||
size_t _compressBufferSize;
|
size_t _compressBufferSize;
|
||||||
std::unique_ptr<unsigned char[]> _compressBuffer;
|
std::unique_ptr<unsigned char[]> _compressBuffer;
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
z_stream _deflateState;
|
z_stream _deflateState;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
class WebSocketPerMessageDeflateDecompressor
|
class WebSocketPerMessageDeflateDecompressor
|
||||||
@@ -43,7 +55,10 @@ namespace ix
|
|||||||
int _flush;
|
int _flush;
|
||||||
size_t _compressBufferSize;
|
size_t _compressBufferSize;
|
||||||
std::unique_ptr<unsigned char[]> _compressBuffer;
|
std::unique_ptr<unsigned char[]> _compressBuffer;
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
z_stream _inflateState;
|
z_stream _inflateState;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@@ -61,6 +61,7 @@ namespace ix
|
|||||||
_clientMaxWindowBits = kDefaultClientMaxWindowBits;
|
_clientMaxWindowBits = kDefaultClientMaxWindowBits;
|
||||||
_serverMaxWindowBits = kDefaultServerMaxWindowBits;
|
_serverMaxWindowBits = kDefaultServerMaxWindowBits;
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
// Split by ;
|
// Split by ;
|
||||||
std::string token;
|
std::string token;
|
||||||
std::stringstream tokenStream(extension);
|
std::stringstream tokenStream(extension);
|
||||||
@@ -112,6 +113,7 @@ namespace ix
|
|||||||
sanitizeClientMaxWindowBits();
|
sanitizeClientMaxWindowBits();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketPerMessageDeflateOptions::sanitizeClientMaxWindowBits()
|
void WebSocketPerMessageDeflateOptions::sanitizeClientMaxWindowBits()
|
||||||
@@ -126,6 +128,7 @@ namespace ix
|
|||||||
|
|
||||||
std::string WebSocketPerMessageDeflateOptions::generateHeader()
|
std::string WebSocketPerMessageDeflateOptions::generateHeader()
|
||||||
{
|
{
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Sec-WebSocket-Extensions: permessage-deflate";
|
ss << "Sec-WebSocket-Extensions: permessage-deflate";
|
||||||
|
|
||||||
@@ -138,11 +141,18 @@ namespace ix
|
|||||||
ss << "\r\n";
|
ss << "\r\n";
|
||||||
|
|
||||||
return ss.str();
|
return ss.str();
|
||||||
|
#else
|
||||||
|
return std::string();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketPerMessageDeflateOptions::enabled() const
|
bool WebSocketPerMessageDeflateOptions::enabled() const
|
||||||
{
|
{
|
||||||
|
#ifdef IXWEBSOCKET_USE_ZLIB
|
||||||
return _enabled;
|
return _enabled;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketPerMessageDeflateOptions::getClientNoContextTakeover() const
|
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,13 +71,38 @@ namespace ix
|
|||||||
_onConnectionCallback = callback;
|
_onConnectionCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebSocketServer::setOnClientMessageCallback(const OnClientMessageCallback& callback)
|
||||||
|
{
|
||||||
|
_onClientMessageCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
void WebSocketServer::handleConnection(std::unique_ptr<Socket> socket,
|
void WebSocketServer::handleConnection(std::unique_ptr<Socket> socket,
|
||||||
std::shared_ptr<ConnectionState> connectionState)
|
std::shared_ptr<ConnectionState> connectionState,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo)
|
||||||
{
|
{
|
||||||
setThreadName("WebSocketServer::" + connectionState->getId());
|
setThreadName("WebSocketServer::" + connectionState->getId());
|
||||||
|
|
||||||
auto webSocket = std::make_shared<WebSocket>();
|
auto webSocket = std::make_shared<WebSocket>();
|
||||||
_onConnectionCallback(webSocket, connectionState);
|
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();
|
webSocket->disableAutomaticReconnection();
|
||||||
|
|
||||||
@@ -111,6 +136,8 @@ namespace ix
|
|||||||
logError(ss.str());
|
logError(ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
webSocket->setOnMessageCallback(nullptr);
|
||||||
|
|
||||||
// Remove this client from our client set
|
// Remove this client from our client set
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_clientsMutex);
|
std::lock_guard<std::mutex> lock(_clientsMutex);
|
||||||
|
@@ -19,11 +19,18 @@
|
|||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class WebSocketServer final : public SocketServer
|
class WebSocketServer : public SocketServer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using OnConnectionCallback =
|
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,
|
WebSocketServer(int port = SocketServer::kDefaultPort,
|
||||||
const std::string& host = SocketServer::kDefaultHost,
|
const std::string& host = SocketServer::kDefaultHost,
|
||||||
@@ -39,6 +46,7 @@ namespace ix
|
|||||||
void disablePerMessageDeflate();
|
void disablePerMessageDeflate();
|
||||||
|
|
||||||
void setOnConnectionCallback(const OnConnectionCallback& callback);
|
void setOnConnectionCallback(const OnConnectionCallback& callback);
|
||||||
|
void setOnClientMessageCallback(const OnClientMessageCallback& callback);
|
||||||
|
|
||||||
// Get all the connected clients
|
// Get all the connected clients
|
||||||
std::set<std::shared_ptr<WebSocket>> getClients();
|
std::set<std::shared_ptr<WebSocket>> getClients();
|
||||||
@@ -52,6 +60,7 @@ namespace ix
|
|||||||
bool _enablePerMessageDeflate;
|
bool _enablePerMessageDeflate;
|
||||||
|
|
||||||
OnConnectionCallback _onConnectionCallback;
|
OnConnectionCallback _onConnectionCallback;
|
||||||
|
OnClientMessageCallback _onClientMessageCallback;
|
||||||
|
|
||||||
std::mutex _clientsMutex;
|
std::mutex _clientsMutex;
|
||||||
std::set<std::shared_ptr<WebSocket>> _clients;
|
std::set<std::shared_ptr<WebSocket>> _clients;
|
||||||
@@ -60,7 +69,8 @@ namespace ix
|
|||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
virtual void handleConnection(std::unique_ptr<Socket> socket,
|
virtual void handleConnection(std::unique_ptr<Socket> socket,
|
||||||
std::shared_ptr<ConnectionState> connectionState) final;
|
std::shared_ptr<ConnectionState> connectionState,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo);
|
||||||
virtual size_t getConnectedClientsCount() final;
|
virtual size_t getConnectedClientsCount() final;
|
||||||
};
|
};
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@@ -65,7 +65,6 @@ namespace ix
|
|||||||
, _receivedMessageCompressed(false)
|
, _receivedMessageCompressed(false)
|
||||||
, _readyState(ReadyState::CLOSED)
|
, _readyState(ReadyState::CLOSED)
|
||||||
, _closeCode(WebSocketCloseConstants::kInternalErrorCode)
|
, _closeCode(WebSocketCloseConstants::kInternalErrorCode)
|
||||||
, _closeReason(WebSocketCloseConstants::kInternalErrorMessage)
|
|
||||||
, _closeWireSize(0)
|
, _closeWireSize(0)
|
||||||
, _closeRemote(false)
|
, _closeRemote(false)
|
||||||
, _enablePerMessageDeflate(false)
|
, _enablePerMessageDeflate(false)
|
||||||
@@ -77,6 +76,7 @@ namespace ix
|
|||||||
, _pingCount(0)
|
, _pingCount(0)
|
||||||
, _lastSendPingTimePoint(std::chrono::steady_clock::now())
|
, _lastSendPingTimePoint(std::chrono::steady_clock::now())
|
||||||
{
|
{
|
||||||
|
setCloseReason(WebSocketCloseConstants::kInternalErrorMessage);
|
||||||
_readbuf.resize(kChunkSize);
|
_readbuf.resize(kChunkSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,10 +179,12 @@ namespace ix
|
|||||||
|
|
||||||
if (readyState == ReadyState::CLOSED)
|
if (readyState == ReadyState::CLOSED)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
if (_onCloseCallback)
|
||||||
_onCloseCallback(_closeCode, _closeReason, _closeWireSize, _closeRemote);
|
{
|
||||||
|
_onCloseCallback(_closeCode, getCloseReason(), _closeWireSize, _closeRemote);
|
||||||
|
}
|
||||||
|
setCloseReason(WebSocketCloseConstants::kInternalErrorMessage);
|
||||||
_closeCode = WebSocketCloseConstants::kInternalErrorCode;
|
_closeCode = WebSocketCloseConstants::kInternalErrorCode;
|
||||||
_closeReason = WebSocketCloseConstants::kInternalErrorMessage;
|
|
||||||
_closeWireSize = 0;
|
_closeWireSize = 0;
|
||||||
_closeRemote = false;
|
_closeRemote = false;
|
||||||
}
|
}
|
||||||
@@ -261,9 +263,10 @@ namespace ix
|
|||||||
{
|
{
|
||||||
// compute lasting delay to wait for next ping / timeout, if at least one set
|
// compute lasting delay to wait for next ping / timeout, if at least one set
|
||||||
auto now = std::chrono::steady_clock::now();
|
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)
|
now - _lastSendPingTimePoint)
|
||||||
.count();
|
.count();
|
||||||
|
lastingTimeoutDelayInMs = (1000 * _pingIntervalSecs) - timeSinceLastPingMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -326,9 +329,10 @@ namespace ix
|
|||||||
return _txbuf.empty();
|
return _txbuf.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class Iterator>
|
||||||
void WebSocketTransport::appendToSendBuffer(const std::vector<uint8_t>& header,
|
void WebSocketTransport::appendToSendBuffer(const std::vector<uint8_t>& header,
|
||||||
std::string::const_iterator begin,
|
Iterator begin,
|
||||||
std::string::const_iterator end,
|
Iterator end,
|
||||||
uint64_t message_size,
|
uint64_t message_size,
|
||||||
uint8_t masking_key[4])
|
uint8_t masking_key[4])
|
||||||
{
|
{
|
||||||
@@ -638,11 +642,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
// we got the CLOSE frame answer from our close, so we can close the connection
|
// we got the CLOSE frame answer from our close, so we can close the connection
|
||||||
// if the code/reason are the same
|
// if the code/reason are the same
|
||||||
bool identicalReason;
|
bool identicalReason = _closeCode == code && getCloseReason() == reason;
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
|
||||||
identicalReason = _closeCode == code && _closeReason == reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (identicalReason)
|
if (identicalReason)
|
||||||
{
|
{
|
||||||
@@ -750,8 +750,9 @@ namespace ix
|
|||||||
return static_cast<unsigned>(seconds);
|
return static_cast<unsigned>(seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
WebSocketSendInfo WebSocketTransport::sendData(wsheader_type::opcode_type type,
|
WebSocketSendInfo WebSocketTransport::sendData(wsheader_type::opcode_type type,
|
||||||
const std::string& message,
|
const T& message,
|
||||||
bool compress,
|
bool compress,
|
||||||
const OnProgressCallback& onProgressCallback)
|
const OnProgressCallback& onProgressCallback)
|
||||||
{
|
{
|
||||||
@@ -764,8 +765,8 @@ namespace ix
|
|||||||
size_t wireSize = message.size();
|
size_t wireSize = message.size();
|
||||||
bool compressionError = false;
|
bool compressionError = false;
|
||||||
|
|
||||||
std::string::const_iterator message_begin = message.begin();
|
auto message_begin = message.cbegin();
|
||||||
std::string::const_iterator message_end = message.end();
|
auto message_end = message.cend();
|
||||||
|
|
||||||
if (compress)
|
if (compress)
|
||||||
{
|
{
|
||||||
@@ -780,8 +781,8 @@ namespace ix
|
|||||||
compressionError = false;
|
compressionError = false;
|
||||||
wireSize = _compressedMessage.size();
|
wireSize = _compressedMessage.size();
|
||||||
|
|
||||||
message_begin = _compressedMessage.begin();
|
message_begin = _compressedMessage.cbegin();
|
||||||
message_end = _compressedMessage.end();
|
message_end = _compressedMessage.cend();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -795,6 +796,11 @@ namespace ix
|
|||||||
if (wireSize < kChunkSize)
|
if (wireSize < kChunkSize)
|
||||||
{
|
{
|
||||||
success = sendFragment(type, true, message_begin, message_end, compress);
|
success = sendFragment(type, true, message_begin, message_end, compress);
|
||||||
|
|
||||||
|
if (onProgressCallback)
|
||||||
|
{
|
||||||
|
onProgressCallback(0, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -859,10 +865,11 @@ namespace ix
|
|||||||
return WebSocketSendInfo(success, compressionError, payloadSize, wireSize);
|
return WebSocketSendInfo(success, compressionError, payloadSize, wireSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class Iterator>
|
||||||
bool WebSocketTransport::sendFragment(wsheader_type::opcode_type type,
|
bool WebSocketTransport::sendFragment(wsheader_type::opcode_type type,
|
||||||
bool fin,
|
bool fin,
|
||||||
std::string::const_iterator message_begin,
|
Iterator message_begin,
|
||||||
std::string::const_iterator message_end,
|
Iterator message_end,
|
||||||
bool compress)
|
bool compress)
|
||||||
{
|
{
|
||||||
uint64_t message_size = static_cast<uint64_t>(message_end - message_begin);
|
uint64_t message_size = static_cast<uint64_t>(message_end - message_begin);
|
||||||
@@ -1055,7 +1062,7 @@ namespace ix
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// no close code/reason set
|
// no close code/reason set
|
||||||
sendData(wsheader_type::CLOSE, "", compress);
|
sendData(wsheader_type::CLOSE, std::string(""), compress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1078,13 +1085,10 @@ namespace ix
|
|||||||
{
|
{
|
||||||
closeSocket();
|
closeSocket();
|
||||||
|
|
||||||
{
|
setCloseReason(reason);
|
||||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
_closeCode = code;
|
||||||
_closeCode = code;
|
_closeWireSize = closeWireSize;
|
||||||
_closeReason = reason;
|
_closeRemote = remote;
|
||||||
_closeWireSize = closeWireSize;
|
|
||||||
_closeRemote = remote;
|
|
||||||
}
|
|
||||||
|
|
||||||
setReadyState(ReadyState::CLOSED);
|
setReadyState(ReadyState::CLOSED);
|
||||||
_requestInitCancellation = false;
|
_requestInitCancellation = false;
|
||||||
@@ -1104,13 +1108,11 @@ namespace ix
|
|||||||
closeWireSize = reason.size();
|
closeWireSize = reason.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
setCloseReason(reason);
|
||||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
_closeCode = code;
|
||||||
_closeCode = code;
|
_closeWireSize = closeWireSize;
|
||||||
_closeReason = reason;
|
_closeRemote = remote;
|
||||||
_closeWireSize = closeWireSize;
|
|
||||||
_closeRemote = remote;
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_closingTimePointMutex);
|
std::lock_guard<std::mutex> lock(_closingTimePointMutex);
|
||||||
_closingTimePoint = std::chrono::steady_clock::now();
|
_closingTimePoint = std::chrono::steady_clock::now();
|
||||||
@@ -1155,4 +1157,15 @@ namespace ix
|
|||||||
return true;
|
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
|
} // namespace ix
|
||||||
|
@@ -178,11 +178,11 @@ namespace ix
|
|||||||
std::atomic<ReadyState> _readyState;
|
std::atomic<ReadyState> _readyState;
|
||||||
|
|
||||||
OnCloseCallback _onCloseCallback;
|
OnCloseCallback _onCloseCallback;
|
||||||
uint16_t _closeCode;
|
|
||||||
std::string _closeReason;
|
std::string _closeReason;
|
||||||
size_t _closeWireSize;
|
mutable std::mutex _closeReasonMutex;
|
||||||
bool _closeRemote;
|
std::atomic<uint16_t> _closeCode;
|
||||||
mutable std::mutex _closeDataMutex;
|
std::atomic<size_t> _closeWireSize;
|
||||||
|
std::atomic<bool> _closeRemote;
|
||||||
|
|
||||||
// Data used for Per Message Deflate compression (with zlib)
|
// Data used for Per Message Deflate compression (with zlib)
|
||||||
WebSocketPerMessageDeflatePtr _perMessageDeflate;
|
WebSocketPerMessageDeflatePtr _perMessageDeflate;
|
||||||
@@ -239,16 +239,15 @@ namespace ix
|
|||||||
bool sendOnSocket();
|
bool sendOnSocket();
|
||||||
bool receiveFromSocket();
|
bool receiveFromSocket();
|
||||||
|
|
||||||
|
template<class T>
|
||||||
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
||||||
const std::string& message,
|
const T& message,
|
||||||
bool compress,
|
bool compress,
|
||||||
const OnProgressCallback& onProgressCallback = nullptr);
|
const OnProgressCallback& onProgressCallback = nullptr);
|
||||||
|
|
||||||
bool sendFragment(wsheader_type::opcode_type type,
|
template<class Iterator>
|
||||||
bool fin,
|
bool sendFragment(
|
||||||
std::string::const_iterator begin,
|
wsheader_type::opcode_type type, bool fin, Iterator begin, Iterator end, bool compress);
|
||||||
std::string::const_iterator end,
|
|
||||||
bool compress);
|
|
||||||
|
|
||||||
void emitMessage(MessageKind messageKind,
|
void emitMessage(MessageKind messageKind,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
@@ -256,9 +255,11 @@ namespace ix
|
|||||||
const OnMessageCallback& onMessageCallback);
|
const OnMessageCallback& onMessageCallback);
|
||||||
|
|
||||||
bool isSendBufferEmpty() const;
|
bool isSendBufferEmpty() const;
|
||||||
|
|
||||||
|
template<class Iterator>
|
||||||
void appendToSendBuffer(const std::vector<uint8_t>& header,
|
void appendToSendBuffer(const std::vector<uint8_t>& header,
|
||||||
std::string::const_iterator begin,
|
Iterator begin,
|
||||||
std::string::const_iterator end,
|
Iterator end,
|
||||||
uint64_t message_size,
|
uint64_t message_size,
|
||||||
uint8_t masking_key[4]);
|
uint8_t masking_key[4]);
|
||||||
|
|
||||||
@@ -266,5 +267,8 @@ namespace ix
|
|||||||
void unmaskReceiveBuffer(const wsheader_type& ws);
|
void unmaskReceiveBuffer(const wsheader_type& ws);
|
||||||
|
|
||||||
std::string getMergedChunks() const;
|
std::string getMergedChunks() const;
|
||||||
|
|
||||||
|
void setCloseReason(const std::string& reason);
|
||||||
|
const std::string& getCloseReason() const;
|
||||||
};
|
};
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@@ -6,4 +6,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define IX_WEBSOCKET_VERSION "9.6.5"
|
#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
|
|
55
makefile
55
makefile
@@ -19,26 +19,28 @@ install: brew
|
|||||||
#
|
#
|
||||||
# Release, Debug, MinSizeRel, RelWithDebInfo are the build types
|
# Release, Debug, MinSizeRel, RelWithDebInfo are the build types
|
||||||
#
|
#
|
||||||
|
# Default rule does not use python as that requires first time users to have Python3 installed
|
||||||
|
#
|
||||||
brew:
|
brew:
|
||||||
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
|
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
|
||||||
|
|
||||||
# Docker default target. We've add problem with OpenSSL and TLS 1.3 (on the
|
# Docker default target. We've had problems with OpenSSL and TLS 1.3 (on the
|
||||||
# server side ?) and I can't work-around it easily, so we're using mbedtls on
|
# server side ?) and I can't work-around it easily, so we're using mbedtls on
|
||||||
# Linux for the SSL backend, which works great.
|
# Linux for the SSL backend, which works great.
|
||||||
ws_mbedtls_install:
|
ws_mbedtls_install:
|
||||||
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; ninja install)
|
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; ninja install)
|
||||||
|
|
||||||
ws:
|
ws:
|
||||||
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. ; ninja install)
|
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
|
||||||
|
|
||||||
ws_install:
|
ws_install:
|
||||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -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:
|
ws_openssl:
|
||||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4)
|
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)
|
||||||
|
|
||||||
ws_mbedtls:
|
ws_mbedtls:
|
||||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4)
|
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4)
|
||||||
|
|
||||||
ws_no_ssl:
|
ws_no_ssl:
|
||||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4)
|
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4)
|
||||||
@@ -102,21 +104,23 @@ test_server:
|
|||||||
# env TEST=Websocket_server make test
|
# env TEST=Websocket_server make test
|
||||||
# env TEST=Websocket_chat make test
|
# env TEST=Websocket_chat make test
|
||||||
# env TEST=heartbeat make test
|
# env TEST=heartbeat make test
|
||||||
test:
|
build_test:
|
||||||
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
|
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. ; ninja install)
|
||||||
|
|
||||||
|
test: build_test
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
test_make:
|
test_make:
|
||||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4)
|
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4)
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
test_tsan:
|
test_tsan:
|
||||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableThreadSanitizer YES)
|
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_TEST=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableThreadSanitizer YES)
|
||||||
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
test_ubsan:
|
test_ubsan:
|
||||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableUndefinedBehaviorSanitizer YES)
|
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_TEST=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableUndefinedBehaviorSanitizer YES)
|
||||||
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
@@ -124,37 +128,37 @@ test_asan: build_test_asan
|
|||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
build_test_asan:
|
build_test_asan:
|
||||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableAddressSanitizer YES)
|
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_TEST=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableAddressSanitizer YES)
|
||||||
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
||||||
|
|
||||||
test_tsan_openssl:
|
test_tsan_openssl:
|
||||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableThreadSanitizer YES)
|
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableThreadSanitizer YES)
|
||||||
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
test_ubsan_openssl:
|
test_ubsan_openssl:
|
||||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableUndefinedBehaviorSanitizer YES)
|
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableUndefinedBehaviorSanitizer YES)
|
||||||
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
test_tsan_openssl_release:
|
test_tsan_openssl_release:
|
||||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Release -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -configuration Release -target ixwebsocket_unittest -enableThreadSanitizer YES)
|
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Release -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_OPEN_SSL=1 .. && xcodebuild -project ixwebsocket.xcodeproj -configuration Release -target ixwebsocket_unittest -enableThreadSanitizer YES)
|
||||||
(cd build/test ; ln -sf Release/ixwebsocket_unittest)
|
(cd build/test ; ln -sf Release/ixwebsocket_unittest)
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
test_tsan_mbedtls:
|
test_tsan_mbedtls:
|
||||||
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_MBED_TLS=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableThreadSanitizer YES)
|
mkdir -p build && (cd build && cmake -GXcode -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_TEST=1 -DUSE_MBED_TLS=1 .. && xcodebuild -project ixwebsocket.xcodeproj -target ixwebsocket_unittest -enableThreadSanitizer YES)
|
||||||
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
build_test_openssl:
|
build_test_openssl:
|
||||||
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; ninja install)
|
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; ninja install)
|
||||||
|
|
||||||
test_openssl: build_test_openssl
|
test_openssl: build_test_openssl
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
|
|
||||||
build_test_mbedtls:
|
build_test_mbedtls:
|
||||||
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_MBED_TLS=1 -DUSE_TEST=1 .. ; make -j 4)
|
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_MBED_TLS=1 -DUSE_TEST=1 .. ; make -j 4)
|
||||||
|
|
||||||
test_mbedtls: build_test_mbedtls
|
test_mbedtls: build_test_mbedtls
|
||||||
(cd test ; python2.7 run.py -r)
|
(cd test ; python2.7 run.py -r)
|
||||||
@@ -170,7 +174,7 @@ autobahn_report:
|
|||||||
cp -rvf ~/sandbox/reports/clients/* ../bsergean.github.io/IXWebSocket/autobahn/
|
cp -rvf ~/sandbox/reports/clients/* ../bsergean.github.io/IXWebSocket/autobahn/
|
||||||
|
|
||||||
httpd:
|
httpd:
|
||||||
clang++ --std=c++11 --stdlib=libc++ -Iixwebsocket httpd.cpp \
|
clang++ --std=c++14 --stdlib=libc++ -o ixhttpd httpd.cpp \
|
||||||
ixwebsocket/IXSelectInterruptFactory.cpp \
|
ixwebsocket/IXSelectInterruptFactory.cpp \
|
||||||
ixwebsocket/IXCancellationRequest.cpp \
|
ixwebsocket/IXCancellationRequest.cpp \
|
||||||
ixwebsocket/IXSocketTLSOptions.cpp \
|
ixwebsocket/IXSocketTLSOptions.cpp \
|
||||||
@@ -189,11 +193,11 @@ httpd:
|
|||||||
ixwebsocket/IXConnectionState.cpp \
|
ixwebsocket/IXConnectionState.cpp \
|
||||||
ixwebsocket/IXUrlParser.cpp \
|
ixwebsocket/IXUrlParser.cpp \
|
||||||
ixwebsocket/IXSelectInterrupt.cpp \
|
ixwebsocket/IXSelectInterrupt.cpp \
|
||||||
ixwebsocket/apple/IXSetThreadName_apple.cpp \
|
ixwebsocket/IXSetThreadName.cpp \
|
||||||
-lz
|
-lz
|
||||||
|
|
||||||
httpd_linux:
|
httpd_linux:
|
||||||
g++ --std=c++11 -o ixhttpd httpd.cpp -Iixwebsocket \
|
g++ --std=c++14 -o ixhttpd httpd.cpp -Iixwebsocket \
|
||||||
ixwebsocket/IXSelectInterruptFactory.cpp \
|
ixwebsocket/IXSelectInterruptFactory.cpp \
|
||||||
ixwebsocket/IXCancellationRequest.cpp \
|
ixwebsocket/IXCancellationRequest.cpp \
|
||||||
ixwebsocket/IXSocketTLSOptions.cpp \
|
ixwebsocket/IXSocketTLSOptions.cpp \
|
||||||
@@ -212,7 +216,7 @@ httpd_linux:
|
|||||||
ixwebsocket/IXConnectionState.cpp \
|
ixwebsocket/IXConnectionState.cpp \
|
||||||
ixwebsocket/IXUrlParser.cpp \
|
ixwebsocket/IXUrlParser.cpp \
|
||||||
ixwebsocket/IXSelectInterrupt.cpp \
|
ixwebsocket/IXSelectInterrupt.cpp \
|
||||||
ixwebsocket/linux/IXSetThreadName_linux.cpp \
|
ixwebsocket/IXSetThreadName.cpp \
|
||||||
-lz -lpthread
|
-lz -lpthread
|
||||||
cp -f ixhttpd /usr/local/bin
|
cp -f ixhttpd /usr/local/bin
|
||||||
|
|
||||||
@@ -223,6 +227,7 @@ rebase_upstream:
|
|||||||
git reset --hard upstream/master
|
git reset --hard upstream/master
|
||||||
git push origin master --force
|
git push origin master --force
|
||||||
|
|
||||||
|
# Legacy target
|
||||||
install_cmake_for_linux:
|
install_cmake_for_linux:
|
||||||
mkdir -p /tmp/cmake
|
mkdir -p /tmp/cmake
|
||||||
(cd /tmp/cmake ; curl -L -O https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz ; tar zxf cmake-3.14.0-Linux-x86_64.tar.gz)
|
(cd /tmp/cmake ; curl -L -O https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz ; tar zxf cmake-3.14.0-Linux-x86_64.tar.gz)
|
||||||
@@ -233,6 +238,12 @@ install_cmake_for_linux:
|
|||||||
doc:
|
doc:
|
||||||
mkdocs gh-deploy
|
mkdocs gh-deploy
|
||||||
|
|
||||||
|
change: format
|
||||||
|
vim ixwebsocket/IXWebSocketVersion.h docs/CHANGELOG.md
|
||||||
|
|
||||||
|
commit:
|
||||||
|
git commit -am "`sh tools/extract_latest_change.sh`"
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
.PHONY: ws
|
.PHONY: ws
|
||||||
|
@@ -37,11 +37,11 @@ set (SOURCES
|
|||||||
|
|
||||||
test_runner.cpp
|
test_runner.cpp
|
||||||
IXTest.cpp
|
IXTest.cpp
|
||||||
IXGetFreePort.cpp
|
|
||||||
../third_party/msgpack11/msgpack11.cpp
|
../third_party/msgpack11/msgpack11.cpp
|
||||||
|
|
||||||
IXSocketTest.cpp
|
IXSocketTest.cpp
|
||||||
IXSocketConnectTest.cpp
|
IXSocketConnectTest.cpp
|
||||||
|
# IXWebSocketLeakTest.cpp # commented until we have a fix for #224
|
||||||
IXWebSocketServerTest.cpp
|
IXWebSocketServerTest.cpp
|
||||||
IXWebSocketTestConnectionDisconnection.cpp
|
IXWebSocketTestConnectionDisconnection.cpp
|
||||||
IXUrlParserTest.cpp
|
IXUrlParserTest.cpp
|
||||||
@@ -55,6 +55,8 @@ set (SOURCES
|
|||||||
IXSentryClientTest.cpp
|
IXSentryClientTest.cpp
|
||||||
IXWebSocketChatTest.cpp
|
IXWebSocketChatTest.cpp
|
||||||
IXWebSocketBroadcastTest.cpp
|
IXWebSocketBroadcastTest.cpp
|
||||||
|
IXWebSocketPerMessageDeflateCompressorTest.cpp
|
||||||
|
IXStreamSqlTest.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Some unittest don't work on windows yet
|
# Some unittest don't work on windows yet
|
||||||
@@ -91,15 +93,30 @@ if (JSONCPP_FOUND)
|
|||||||
target_link_libraries(ixwebsocket_unittest ${JSONCPP_LIBRARIES})
|
target_link_libraries(ixwebsocket_unittest ${JSONCPP_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (USE_PYTHON)
|
||||||
|
find_package(Python COMPONENTS Development)
|
||||||
|
if (NOT Python_FOUND)
|
||||||
|
message(FATAL_ERROR "Python3 not found")
|
||||||
|
endif()
|
||||||
|
message("Python_FOUND:${Python_FOUND}")
|
||||||
|
message("Python_VERSION:${Python_VERSION}")
|
||||||
|
message("Python_Development_FOUND:${Python_Development_FOUND}")
|
||||||
|
message("Python_LIBRARIES:${Python_LIBRARIES}")
|
||||||
|
endif()
|
||||||
|
|
||||||
# library with the most dependencies come first
|
# library with the most dependencies come first
|
||||||
target_link_libraries(ixwebsocket_unittest ixbots)
|
target_link_libraries(ixwebsocket_unittest ixbots)
|
||||||
target_link_libraries(ixwebsocket_unittest ixsnake)
|
target_link_libraries(ixwebsocket_unittest ixsnake)
|
||||||
target_link_libraries(ixwebsocket_unittest ixcobra)
|
target_link_libraries(ixwebsocket_unittest ixcobra)
|
||||||
target_link_libraries(ixwebsocket_unittest ixsentry)
|
target_link_libraries(ixwebsocket_unittest ixsentry)
|
||||||
|
target_link_libraries(ixwebsocket_unittest ixredis)
|
||||||
target_link_libraries(ixwebsocket_unittest ixwebsocket)
|
target_link_libraries(ixwebsocket_unittest ixwebsocket)
|
||||||
target_link_libraries(ixwebsocket_unittest ixcrypto)
|
target_link_libraries(ixwebsocket_unittest ixcrypto)
|
||||||
target_link_libraries(ixwebsocket_unittest ixcore)
|
target_link_libraries(ixwebsocket_unittest ixcore)
|
||||||
|
|
||||||
target_link_libraries(ixwebsocket_unittest spdlog)
|
target_link_libraries(ixwebsocket_unittest spdlog)
|
||||||
|
if (USE_PYTHON)
|
||||||
|
target_link_libraries(ixwebsocket_unittest ${Python_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
install(TARGETS ixwebsocket_unittest DESTINATION bin)
|
install(TARGETS ixwebsocket_unittest DESTINATION bin)
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <ixcobra/IXCobraConnection.h>
|
#include <ixcobra/IXCobraConnection.h>
|
||||||
#include <ixcrypto/IXUuid.h>
|
#include <ixcrypto/IXUuid.h>
|
||||||
#include <ixsnake/IXRedisServer.h>
|
#include <ixredis/IXRedisServer.h>
|
||||||
#include <ixsnake/IXSnakeServer.h>
|
#include <ixsnake/IXSnakeServer.h>
|
||||||
|
|
||||||
using namespace ix;
|
using namespace ix;
|
||||||
@@ -125,10 +125,12 @@ namespace
|
|||||||
{
|
{
|
||||||
std::string filter;
|
std::string filter;
|
||||||
std::string position("$");
|
std::string position("$");
|
||||||
|
int batchSize = 1;
|
||||||
|
|
||||||
_conn.subscribe(channel,
|
_conn.subscribe(channel,
|
||||||
filter,
|
filter,
|
||||||
position,
|
position,
|
||||||
|
batchSize,
|
||||||
[this](const Json::Value& msg, const std::string& /*position*/) {
|
[this](const Json::Value& msg, const std::string& /*position*/) {
|
||||||
spdlog::info("receive {}", msg.toStyledString());
|
spdlog::info("receive {}", msg.toStyledString());
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <ixcobra/IXCobraMetricsPublisher.h>
|
#include <ixcobra/IXCobraMetricsPublisher.h>
|
||||||
#include <ixcrypto/IXUuid.h>
|
#include <ixcrypto/IXUuid.h>
|
||||||
#include <ixsnake/IXRedisServer.h>
|
#include <ixredis/IXRedisServer.h>
|
||||||
#include <ixsnake/IXSnakeServer.h>
|
#include <ixsnake/IXSnakeServer.h>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
@@ -76,10 +76,12 @@ namespace
|
|||||||
log("Subscriber authenticated");
|
log("Subscriber authenticated");
|
||||||
std::string filter;
|
std::string filter;
|
||||||
std::string position("$");
|
std::string position("$");
|
||||||
|
int batchSize = 1;
|
||||||
|
|
||||||
conn.subscribe(channel,
|
conn.subscribe(channel,
|
||||||
filter,
|
filter,
|
||||||
position,
|
position,
|
||||||
|
batchSize,
|
||||||
[](const Json::Value& msg, const std::string& /*position*/) {
|
[](const Json::Value& msg, const std::string& /*position*/) {
|
||||||
log(msg.toStyledString());
|
log(msg.toStyledString());
|
||||||
|
|
||||||
@@ -106,7 +108,7 @@ namespace
|
|||||||
}
|
}
|
||||||
else if (event->type == ix::CobraEventType::UnSubscribed)
|
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)
|
if (event->subscriptionId != channel)
|
||||||
{
|
{
|
||||||
TLogger() << "Subscriber: unexpected channel " << event->subscriptionId;
|
TLogger() << "Subscriber: unexpected channel " << event->subscriptionId;
|
||||||
|
@@ -12,8 +12,8 @@
|
|||||||
#include <ixcobra/IXCobraConnection.h>
|
#include <ixcobra/IXCobraConnection.h>
|
||||||
#include <ixcobra/IXCobraMetricsPublisher.h>
|
#include <ixcobra/IXCobraMetricsPublisher.h>
|
||||||
#include <ixcrypto/IXUuid.h>
|
#include <ixcrypto/IXUuid.h>
|
||||||
|
#include <ixredis/IXRedisServer.h>
|
||||||
#include <ixsentry/IXSentryClient.h>
|
#include <ixsentry/IXSentryClient.h>
|
||||||
#include <ixsnake/IXRedisServer.h>
|
|
||||||
#include <ixsnake/IXSnakeServer.h>
|
#include <ixsnake/IXSnakeServer.h>
|
||||||
#include <ixwebsocket/IXHttpServer.h>
|
#include <ixwebsocket/IXHttpServer.h>
|
||||||
#include <ixwebsocket/IXUserAgent.h>
|
#include <ixwebsocket/IXUserAgent.h>
|
||||||
@@ -95,13 +95,15 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
|
|||||||
|
|
||||||
sentryServer.setOnConnectionCallback(
|
sentryServer.setOnConnectionCallback(
|
||||||
[](HttpRequestPtr request,
|
[](HttpRequestPtr request,
|
||||||
std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr {
|
std::shared_ptr<ConnectionState> /*connectionState*/,
|
||||||
|
std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr {
|
||||||
WebSocketHttpHeaders headers;
|
WebSocketHttpHeaders headers;
|
||||||
headers["Server"] = userAgent();
|
headers["Server"] = userAgent();
|
||||||
|
|
||||||
// Log request
|
// Log request
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << request->method << " " << request->headers["User-Agent"] << " "
|
ss << connectionInfo->remoteIp << ":" << connectionInfo->remotePort << " "
|
||||||
|
<< request->method << " " << request->headers["User-Agent"] << " "
|
||||||
<< request->uri;
|
<< request->uri;
|
||||||
|
|
||||||
if (request->method == "POST")
|
if (request->method == "POST")
|
||||||
|
@@ -12,8 +12,8 @@
|
|||||||
#include <ixcobra/IXCobraConnection.h>
|
#include <ixcobra/IXCobraConnection.h>
|
||||||
#include <ixcobra/IXCobraMetricsPublisher.h>
|
#include <ixcobra/IXCobraMetricsPublisher.h>
|
||||||
#include <ixcrypto/IXUuid.h>
|
#include <ixcrypto/IXUuid.h>
|
||||||
|
#include <ixredis/IXRedisServer.h>
|
||||||
#include <ixsentry/IXSentryClient.h>
|
#include <ixsentry/IXSentryClient.h>
|
||||||
#include <ixsnake/IXRedisServer.h>
|
|
||||||
#include <ixsnake/IXSnakeServer.h>
|
#include <ixsnake/IXSnakeServer.h>
|
||||||
#include <ixwebsocket/IXHttpServer.h>
|
#include <ixwebsocket/IXHttpServer.h>
|
||||||
#include <ixwebsocket/IXUserAgent.h>
|
#include <ixwebsocket/IXUserAgent.h>
|
||||||
|
@@ -12,8 +12,8 @@
|
|||||||
#include <ixcobra/IXCobraConnection.h>
|
#include <ixcobra/IXCobraConnection.h>
|
||||||
#include <ixcobra/IXCobraMetricsPublisher.h>
|
#include <ixcobra/IXCobraMetricsPublisher.h>
|
||||||
#include <ixcrypto/IXUuid.h>
|
#include <ixcrypto/IXUuid.h>
|
||||||
|
#include <ixredis/IXRedisServer.h>
|
||||||
#include <ixsentry/IXSentryClient.h>
|
#include <ixsentry/IXSentryClient.h>
|
||||||
#include <ixsnake/IXRedisServer.h>
|
|
||||||
#include <ixsnake/IXSnakeServer.h>
|
#include <ixsnake/IXSnakeServer.h>
|
||||||
#include <ixwebsocket/IXHttpServer.h>
|
#include <ixwebsocket/IXHttpServer.h>
|
||||||
#include <ixwebsocket/IXUserAgent.h>
|
#include <ixwebsocket/IXUserAgent.h>
|
||||||
@@ -92,6 +92,9 @@ TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]")
|
|||||||
cobraBotConfig.enableHeartbeat = false;
|
cobraBotConfig.enableHeartbeat = false;
|
||||||
bool quiet = 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.
|
// We could try to capture the output ... not sure how.
|
||||||
bool fluentd = true;
|
bool fluentd = true;
|
||||||
|
|
||||||
|
@@ -4,9 +4,9 @@
|
|||||||
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IXGetFreePort.h"
|
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <ixwebsocket/IXGetFreePort.h>
|
||||||
#include <ixwebsocket/IXHttpClient.h>
|
#include <ixwebsocket/IXHttpClient.h>
|
||||||
#include <ixwebsocket/IXHttpServer.h>
|
#include <ixwebsocket/IXHttpServer.h>
|
||||||
|
|
||||||
@@ -67,7 +67,8 @@ TEST_CASE("http server", "[httpd]")
|
|||||||
|
|
||||||
TEST_CASE("http server redirection", "[httpd_redirect]")
|
TEST_CASE("http server redirection", "[httpd_redirect]")
|
||||||
{
|
{
|
||||||
SECTION("Connect to a local HTTP server, with redirection enabled")
|
SECTION(
|
||||||
|
"Connect to a local HTTP server, with redirection enabled, but we do not follow redirects")
|
||||||
{
|
{
|
||||||
int port = getFreePort();
|
int port = getFreePort();
|
||||||
ix::HttpServer server(port, "127.0.0.1");
|
ix::HttpServer server(port, "127.0.0.1");
|
||||||
@@ -117,4 +118,54 @@ TEST_CASE("http server redirection", "[httpd_redirect]")
|
|||||||
|
|
||||||
server.stop();
|
server.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("Connect to a local HTTP server, with redirection enabled, but we do follow redirects")
|
||||||
|
{
|
||||||
|
int port = getFreePort();
|
||||||
|
ix::HttpServer server(port, "127.0.0.1");
|
||||||
|
server.makeRedirectServer("http://www.google.com");
|
||||||
|
|
||||||
|
auto res = server.listen();
|
||||||
|
REQUIRE(res.first);
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
HttpClient httpClient;
|
||||||
|
WebSocketHttpHeaders headers;
|
||||||
|
|
||||||
|
std::string url("http://127.0.0.1:");
|
||||||
|
url += std::to_string(port);
|
||||||
|
url += "/data/foo.txt";
|
||||||
|
auto args = httpClient.createRequest(url);
|
||||||
|
|
||||||
|
args->extraHeaders = headers;
|
||||||
|
args->connectTimeout = 60;
|
||||||
|
args->transferTimeout = 60;
|
||||||
|
args->followRedirects = true;
|
||||||
|
args->maxRedirects = 10;
|
||||||
|
args->verbose = true;
|
||||||
|
args->compress = true;
|
||||||
|
args->logger = [](const std::string& msg) { std::cout << msg; };
|
||||||
|
args->onProgressCallback = [](int current, int total) -> bool {
|
||||||
|
std::cerr << "\r"
|
||||||
|
<< "Downloaded " << current << " bytes out of " << total;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto response = httpClient.get(url, args);
|
||||||
|
|
||||||
|
for (auto it : response->headers)
|
||||||
|
{
|
||||||
|
std::cerr << it.first << ": " << it.second << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Upload size: " << response->uploadSize << std::endl;
|
||||||
|
std::cerr << "Download size: " << response->downloadSize << std::endl;
|
||||||
|
std::cerr << "Status: " << response->statusCode << std::endl;
|
||||||
|
std::cerr << "Error message: " << response->errorMsg << std::endl;
|
||||||
|
|
||||||
|
REQUIRE(response->errorCode == HttpErrorCode::Ok);
|
||||||
|
REQUIRE(response->statusCode == 200);
|
||||||
|
|
||||||
|
server.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
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,36 +84,38 @@ namespace ix
|
|||||||
|
|
||||||
bool startWebSocketEchoServer(ix::WebSocketServer& server)
|
bool startWebSocketEchoServer(ix::WebSocketServer& server)
|
||||||
{
|
{
|
||||||
server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
|
server.setOnClientMessageCallback(
|
||||||
std::shared_ptr<ConnectionState> connectionState) {
|
[&server](std::shared_ptr<ConnectionState> /*connectionState*/,
|
||||||
webSocket->setOnMessageCallback(
|
ConnectionInfo& connectionInfo,
|
||||||
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg) {
|
WebSocket& webSocket,
|
||||||
if (msg->type == ix::WebSocketMessageType::Open)
|
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() << it.first << ": " << it.second;
|
||||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
}
|
||||||
TLogger() << "Headers:";
|
}
|
||||||
for (auto it : msg->openInfo.headers)
|
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();
|
auto res = server.listen();
|
||||||
if (!res.first)
|
if (!res.first)
|
||||||
|
@@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IXGetFreePort.h"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <ixsnake/IXAppConfig.h>
|
#include <ixsnake/IXAppConfig.h>
|
||||||
|
#include <ixwebsocket/IXGetFreePort.h>
|
||||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||||
#include <ixwebsocket/IXWebSocketServer.h>
|
#include <ixwebsocket/IXWebSocketServer.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@@ -189,15 +189,19 @@ namespace
|
|||||||
bool preferTLS = true;
|
bool preferTLS = true;
|
||||||
server.setTLSOptions(makeServerTLSOptions(preferTLS));
|
server.setTLSOptions(makeServerTLSOptions(preferTLS));
|
||||||
|
|
||||||
server.setOnConnectionCallback([&server, &connectionId](
|
server.setOnClientMessageCallback(
|
||||||
std::shared_ptr<ix::WebSocket> webSocket,
|
[&server, &connectionId](std::shared_ptr<ConnectionState> connectionState,
|
||||||
std::shared_ptr<ConnectionState> connectionState) {
|
ConnectionInfo& connectionInfo,
|
||||||
webSocket->setOnMessageCallback([webSocket, connectionState, &connectionId, &server](
|
WebSocket& webSocket,
|
||||||
const ix::WebSocketMessagePtr& msg) {
|
const ix::WebSocketMessagePtr& msg) {
|
||||||
|
auto remoteIp = connectionInfo.remoteIp;
|
||||||
|
|
||||||
|
|
||||||
if (msg->type == ix::WebSocketMessageType::Open)
|
if (msg->type == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
TLogger() << "New connection";
|
TLogger() << "New connection";
|
||||||
connectionState->computeId();
|
connectionState->computeId();
|
||||||
|
TLogger() << "remote ip: " << remoteIp;
|
||||||
TLogger() << "id: " << connectionState->getId();
|
TLogger() << "id: " << connectionState->getId();
|
||||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||||
TLogger() << "Headers:";
|
TLogger() << "Headers:";
|
||||||
@@ -216,14 +220,13 @@ namespace
|
|||||||
{
|
{
|
||||||
for (auto&& client : server.getClients())
|
for (auto&& client : server.getClients())
|
||||||
{
|
{
|
||||||
if (client != webSocket)
|
if (client.get() != &webSocket)
|
||||||
{
|
{
|
||||||
client->send(msg->str, msg->binary);
|
client->send(msg->str, msg->binary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
auto res = server.listen();
|
auto res = server.listen();
|
||||||
if (!res.first)
|
if (!res.first)
|
||||||
|
@@ -193,37 +193,39 @@ namespace
|
|||||||
|
|
||||||
bool startServer(ix::WebSocketServer& server)
|
bool startServer(ix::WebSocketServer& server)
|
||||||
{
|
{
|
||||||
server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
|
server.setOnClientMessageCallback(
|
||||||
std::shared_ptr<ConnectionState> connectionState) {
|
[&server](std::shared_ptr<ConnectionState> connectionState,
|
||||||
webSocket->setOnMessageCallback(
|
ConnectionInfo& connectionInfo,
|
||||||
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg) {
|
WebSocket& webSocket,
|
||||||
if (msg->type == ix::WebSocketMessageType::Open)
|
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() << it.first << ": " << it.second;
|
||||||
TLogger() << "id: " << connectionState->getId();
|
}
|
||||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
}
|
||||||
TLogger() << "Headers:";
|
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||||
for (auto it : msg->openInfo.headers)
|
{
|
||||||
|
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();
|
auto res = server.listen();
|
||||||
if (!res.first)
|
if (!res.first)
|
||||||
|
@@ -168,41 +168,38 @@ namespace
|
|||||||
std::mutex& mutexWrite)
|
std::mutex& mutexWrite)
|
||||||
{
|
{
|
||||||
// A dev/null server
|
// A dev/null server
|
||||||
server.setOnConnectionCallback(
|
server.setOnClientMessageCallback(
|
||||||
[&receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](
|
[&receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](
|
||||||
std::shared_ptr<ix::WebSocket> webSocket,
|
std::shared_ptr<ConnectionState> connectionState,
|
||||||
std::shared_ptr<ConnectionState> connectionState) {
|
ConnectionInfo& connectionInfo,
|
||||||
webSocket->setOnMessageCallback([webSocket,
|
WebSocket& /*webSocket*/,
|
||||||
connectionState,
|
const ix::WebSocketMessagePtr& msg) {
|
||||||
&receivedCloseCode,
|
auto remoteIp = connectionInfo.remoteIp;
|
||||||
&receivedCloseReason,
|
if (msg->type == ix::WebSocketMessageType::Open)
|
||||||
&receivedCloseRemote,
|
{
|
||||||
&mutexWrite](const ix::WebSocketMessagePtr& msg) {
|
TLogger() << "New server connection";
|
||||||
if (msg->type == ix::WebSocketMessageType::Open)
|
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() << it.first << ": " << it.second;
|
||||||
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)
|
}
|
||||||
{
|
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||||
std::stringstream ss;
|
{
|
||||||
ss << "Server closed connection(" << msg->closeInfo.code << ","
|
std::stringstream ss;
|
||||||
<< msg->closeInfo.reason << ")";
|
ss << "Server closed connection(" << msg->closeInfo.code << ","
|
||||||
log(ss.str());
|
<< msg->closeInfo.reason << ")";
|
||||||
|
log(ss.str());
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lck(mutexWrite);
|
std::lock_guard<std::mutex> lck(mutexWrite);
|
||||||
|
|
||||||
receivedCloseCode = msg->closeInfo.code;
|
receivedCloseCode = msg->closeInfo.code;
|
||||||
receivedCloseReason = std::string(msg->closeInfo.reason);
|
receivedCloseReason = std::string(msg->closeInfo.reason);
|
||||||
receivedCloseRemote = msg->closeInfo.remote;
|
receivedCloseRemote = msg->closeInfo.remote;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
auto res = server.listen();
|
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);
|
||||||
|
}
|
||||||
|
}
|
76
test/IXWebSocketPerMessageDeflateCompressorTest.cpp
Normal file
76
test/IXWebSocketPerMessageDeflateCompressorTest.cpp
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* IXWebSocketPerMessageDeflateCodecTest.cpp
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2020 Machine Zone. All rights reserved.
|
||||||
|
*
|
||||||
|
* make build_test && build/test/ixwebsocket_unittest per-message-deflate-codec
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "IXTest.h"
|
||||||
|
#include "catch.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <ixwebsocket/IXWebSocketPerMessageDeflateCodec.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
using namespace ix;
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
std::string compressAndDecompress(const std::string& a)
|
||||||
|
{
|
||||||
|
std::string b, c;
|
||||||
|
|
||||||
|
WebSocketPerMessageDeflateCompressor compressor;
|
||||||
|
compressor.init(11, true);
|
||||||
|
compressor.compress(a, b);
|
||||||
|
|
||||||
|
WebSocketPerMessageDeflateDecompressor decompressor;
|
||||||
|
decompressor.init(11, true);
|
||||||
|
decompressor.decompress(b, c);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string compressAndDecompressVector(const std::string& a)
|
||||||
|
{
|
||||||
|
std::string b, c;
|
||||||
|
|
||||||
|
std::vector<uint8_t> vec(a.begin(), a.end());
|
||||||
|
|
||||||
|
WebSocketPerMessageDeflateCompressor compressor;
|
||||||
|
compressor.init(11, true);
|
||||||
|
compressor.compress(vec, b);
|
||||||
|
|
||||||
|
WebSocketPerMessageDeflateDecompressor decompressor;
|
||||||
|
decompressor.init(11, true);
|
||||||
|
decompressor.decompress(b, c);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("per-message-deflate-codec", "[zlib]")
|
||||||
|
{
|
||||||
|
SECTION("string api")
|
||||||
|
{
|
||||||
|
REQUIRE(compressAndDecompress("") == "");
|
||||||
|
REQUIRE(compressAndDecompress("foo") == "foo");
|
||||||
|
REQUIRE(compressAndDecompress("bar") == "bar");
|
||||||
|
REQUIRE(compressAndDecompress("asdcaseqw`21897dehqwed") == "asdcaseqw`21897dehqwed");
|
||||||
|
REQUIRE(compressAndDecompress("/usr/local/include/ixwebsocket/IXSocketAppleSSL.h") ==
|
||||||
|
"/usr/local/include/ixwebsocket/IXSocketAppleSSL.h");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("vector api")
|
||||||
|
{
|
||||||
|
REQUIRE(compressAndDecompressVector("") == "");
|
||||||
|
REQUIRE(compressAndDecompressVector("foo") == "foo");
|
||||||
|
REQUIRE(compressAndDecompressVector("bar") == "bar");
|
||||||
|
REQUIRE(compressAndDecompressVector("asdcaseqw`21897dehqwed") ==
|
||||||
|
"asdcaseqw`21897dehqwed");
|
||||||
|
REQUIRE(
|
||||||
|
compressAndDecompressVector("/usr/local/include/ixwebsocket/IXSocketAppleSSL.h") ==
|
||||||
|
"/usr/local/include/ixwebsocket/IXSocketAppleSSL.h");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ix
|
@@ -19,7 +19,7 @@ namespace
|
|||||||
class WebSocketClient
|
class WebSocketClient
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WebSocketClient(int port, bool useHeartBeatMethod);
|
WebSocketClient(int port);
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
@@ -29,12 +29,10 @@ namespace
|
|||||||
private:
|
private:
|
||||||
ix::WebSocket _webSocket;
|
ix::WebSocket _webSocket;
|
||||||
int _port;
|
int _port;
|
||||||
bool _useHeartBeatMethod;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocketClient::WebSocketClient(int port, bool useHeartBeatMethod)
|
WebSocketClient::WebSocketClient(int port)
|
||||||
: _port(port)
|
: _port(port)
|
||||||
, _useHeartBeatMethod(useHeartBeatMethod)
|
|
||||||
{
|
{
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
@@ -63,49 +61,37 @@ namespace
|
|||||||
|
|
||||||
// The important bit for this test.
|
// The important bit for this test.
|
||||||
// Set a 1 second heartbeat with the setter method to test
|
// Set a 1 second heartbeat with the setter method to test
|
||||||
if (_useHeartBeatMethod)
|
_webSocket.setPingInterval(1);
|
||||||
{
|
|
||||||
_webSocket.setPingInterval(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_webSocket.setPingInterval(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
log(std::string("Connecting to url: ") + url);
|
log(std::string("Connecting to url: ") + url);
|
||||||
|
|
||||||
_webSocket.setOnMessageCallback([](ix::WebSocketMessageType messageType,
|
_webSocket.setOnMessageCallback([](const ix::WebSocketMessagePtr& msg) {
|
||||||
const std::string& str,
|
|
||||||
size_t wireSize,
|
|
||||||
const ix::WebSocketErrorInfo& error,
|
|
||||||
const ix::WebSocketOpenInfo& openInfo,
|
|
||||||
const ix::WebSocketCloseInfo& closeInfo) {
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if (messageType == ix::WebSocketMessageType::Open)
|
if (msg->type == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
log("client connected");
|
log("client connected");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocketMessageType::Close)
|
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
log("client disconnected");
|
log("client disconnected");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocketMessageType::Error)
|
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||||
{
|
{
|
||||||
ss << "Error ! " << error.reason;
|
ss << "Error ! " << msg->errorInfo.reason;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocketMessageType::Pong)
|
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||||
{
|
{
|
||||||
ss << "Received pong message " << str;
|
ss << "Received pong message " << msg->str;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocketMessageType::Ping)
|
else if (msg->type == ix::WebSocketMessageType::Ping)
|
||||||
{
|
{
|
||||||
ss << "Received ping message " << str;
|
ss << "Received ping message " << msg->str;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocketMessageType::Message)
|
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
// too many messages to log
|
// too many messages to log
|
||||||
}
|
}
|
||||||
@@ -132,33 +118,28 @@ namespace
|
|||||||
std::shared_ptr<ConnectionState> connectionState) {
|
std::shared_ptr<ConnectionState> connectionState) {
|
||||||
webSocket->setOnMessageCallback(
|
webSocket->setOnMessageCallback(
|
||||||
[webSocket, connectionState, &server, &receivedPingMessages](
|
[webSocket, connectionState, &server, &receivedPingMessages](
|
||||||
ix::WebSocketMessageType messageType,
|
const ix::WebSocketMessagePtr& msg) {
|
||||||
const std::string& str,
|
if (msg->type == ix::WebSocketMessageType::Open)
|
||||||
size_t wireSize,
|
|
||||||
const ix::WebSocketErrorInfo& error,
|
|
||||||
const ix::WebSocketOpenInfo& openInfo,
|
|
||||||
const ix::WebSocketCloseInfo& closeInfo) {
|
|
||||||
if (messageType == ix::WebSocketMessageType::Open)
|
|
||||||
{
|
{
|
||||||
TLogger() << "New server connection";
|
TLogger() << "New server connection";
|
||||||
TLogger() << "id: " << connectionState->getId();
|
TLogger() << "id: " << connectionState->getId();
|
||||||
TLogger() << "Uri: " << openInfo.uri;
|
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||||
TLogger() << "Headers:";
|
TLogger() << "Headers:";
|
||||||
for (auto it : openInfo.headers)
|
for (auto it : msg->openInfo.headers)
|
||||||
{
|
{
|
||||||
TLogger() << it.first << ": " << it.second;
|
TLogger() << it.first << ": " << it.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocketMessageType::Close)
|
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
log("Server closed connection");
|
log("Server closed connection");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocketMessageType::Ping)
|
else if (msg->type == ix::WebSocketMessageType::Ping)
|
||||||
{
|
{
|
||||||
log("Server received a ping");
|
log("Server received a ping");
|
||||||
receivedPingMessages++;
|
receivedPingMessages++;
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocketMessageType::Message)
|
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
// to many messages to log
|
// to many messages to log
|
||||||
for (auto client : server.getClients())
|
for (auto client : server.getClients())
|
||||||
@@ -193,8 +174,7 @@ TEST_CASE("Websocket_ping_no_data_sent_setPingInterval", "[setPingInterval]")
|
|||||||
REQUIRE(startServer(server, serverReceivedPingMessages));
|
REQUIRE(startServer(server, serverReceivedPingMessages));
|
||||||
|
|
||||||
std::string session = ix::generateSessionId();
|
std::string session = ix::generateSessionId();
|
||||||
bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval
|
WebSocketClient webSocketClient(port);
|
||||||
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
|
||||||
|
|
||||||
webSocketClient.start();
|
webSocketClient.start();
|
||||||
|
|
||||||
@@ -236,8 +216,7 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval", "[setPingInterval]")
|
|||||||
REQUIRE(startServer(server, serverReceivedPingMessages));
|
REQUIRE(startServer(server, serverReceivedPingMessages));
|
||||||
|
|
||||||
std::string session = ix::generateSessionId();
|
std::string session = ix::generateSessionId();
|
||||||
bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval
|
WebSocketClient webSocketClient(port);
|
||||||
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
|
||||||
|
|
||||||
webSocketClient.start();
|
webSocketClient.start();
|
||||||
|
|
||||||
@@ -261,7 +240,7 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval", "[setPingInterval]")
|
|||||||
// Here we test ping interval
|
// Here we test ping interval
|
||||||
// client has sent data, but ping should have been sent no matter what
|
// client has sent data, but ping should have been sent no matter what
|
||||||
// -> expected ping messages == 3 as 900+900+1300 = 3100 seconds, 1 ping sent every second
|
// -> expected ping messages == 3 as 900+900+1300 = 3100 seconds, 1 ping sent every second
|
||||||
REQUIRE(serverReceivedPingMessages == 3);
|
REQUIRE(serverReceivedPingMessages >= 2);
|
||||||
|
|
||||||
// Give us 1000ms for the server to notice that clients went away
|
// Give us 1000ms for the server to notice that clients went away
|
||||||
ix::msleep(1000);
|
ix::msleep(1000);
|
||||||
@@ -284,8 +263,7 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval_half_full", "[setPingInterva
|
|||||||
REQUIRE(startServer(server, serverReceivedPingMessages));
|
REQUIRE(startServer(server, serverReceivedPingMessages));
|
||||||
|
|
||||||
std::string session = ix::generateSessionId();
|
std::string session = ix::generateSessionId();
|
||||||
bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval
|
WebSocketClient webSocketClient(port);
|
||||||
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
|
||||||
|
|
||||||
webSocketClient.start();
|
webSocketClient.start();
|
||||||
|
|
||||||
@@ -338,8 +316,7 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval_full", "[setPingInterval]")
|
|||||||
REQUIRE(startServer(server, serverReceivedPingMessages));
|
REQUIRE(startServer(server, serverReceivedPingMessages));
|
||||||
|
|
||||||
std::string session = ix::generateSessionId();
|
std::string session = ix::generateSessionId();
|
||||||
bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval
|
WebSocketClient webSocketClient(port);
|
||||||
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
|
||||||
|
|
||||||
webSocketClient.start();
|
webSocketClient.start();
|
||||||
|
|
||||||
@@ -363,8 +340,9 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval_full", "[setPingInterval]")
|
|||||||
|
|
||||||
// Here we test ping interval
|
// Here we test ping interval
|
||||||
// client has sent data, but ping should have been sent no matter what
|
// client has sent data, but ping should have been sent no matter what
|
||||||
// -> expected ping messages == 1, 1 ping sent every second
|
// -> expected ping messages == 2, 1 ping sent every second
|
||||||
REQUIRE(serverReceivedPingMessages == 1);
|
// The first ping is sent right away on connect
|
||||||
|
REQUIRE(serverReceivedPingMessages == 2);
|
||||||
|
|
||||||
ix::msleep(100);
|
ix::msleep(100);
|
||||||
|
|
||||||
@@ -392,8 +370,7 @@ TEST_CASE("Websocket_ping_no_data_sent_setHeartBeatPeriod", "[setPingInterval]")
|
|||||||
REQUIRE(startServer(server, serverReceivedPingMessages));
|
REQUIRE(startServer(server, serverReceivedPingMessages));
|
||||||
|
|
||||||
std::string session = ix::generateSessionId();
|
std::string session = ix::generateSessionId();
|
||||||
bool useSetHeartBeatPeriodMethod = true;
|
WebSocketClient webSocketClient(port);
|
||||||
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
|
||||||
|
|
||||||
webSocketClient.start();
|
webSocketClient.start();
|
||||||
|
|
||||||
@@ -406,14 +383,13 @@ TEST_CASE("Websocket_ping_no_data_sent_setHeartBeatPeriod", "[setPingInterval]")
|
|||||||
|
|
||||||
REQUIRE(server.getClients().size() == 1);
|
REQUIRE(server.getClients().size() == 1);
|
||||||
|
|
||||||
ix::msleep(1900);
|
ix::msleep(2100);
|
||||||
|
|
||||||
webSocketClient.stop();
|
webSocketClient.stop();
|
||||||
|
|
||||||
|
|
||||||
// Here we test ping interval
|
// Here we test ping interval
|
||||||
// -> expected ping messages == 1 as 1900 seconds, 1 ping sent every second
|
// -> expected ping messages == 2 as 2100 seconds, 1 ping sent every second
|
||||||
REQUIRE(serverReceivedPingMessages == 1);
|
REQUIRE(serverReceivedPingMessages == 2);
|
||||||
|
|
||||||
// Give us 1000ms for the server to notice that clients went away
|
// Give us 1000ms for the server to notice that clients went away
|
||||||
ix::msleep(1000);
|
ix::msleep(1000);
|
||||||
@@ -436,8 +412,7 @@ TEST_CASE("Websocket_ping_data_sent_setHeartBeatPeriod", "[setPingInterval]")
|
|||||||
REQUIRE(startServer(server, serverReceivedPingMessages));
|
REQUIRE(startServer(server, serverReceivedPingMessages));
|
||||||
|
|
||||||
std::string session = ix::generateSessionId();
|
std::string session = ix::generateSessionId();
|
||||||
bool useSetHeartBeatPeriodMethod = true;
|
WebSocketClient webSocketClient(port);
|
||||||
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
|
||||||
|
|
||||||
webSocketClient.start();
|
webSocketClient.start();
|
||||||
|
|
||||||
@@ -464,7 +439,7 @@ TEST_CASE("Websocket_ping_data_sent_setHeartBeatPeriod", "[setPingInterval]")
|
|||||||
// Here we test ping interval
|
// Here we test ping interval
|
||||||
// client has sent data, but ping should have been sent no matter what
|
// client has sent data, but ping should have been sent no matter what
|
||||||
// -> expected ping messages == 2 as 900+900+1100 = 2900 seconds, 1 ping sent every second
|
// -> expected ping messages == 2 as 900+900+1100 = 2900 seconds, 1 ping sent every second
|
||||||
REQUIRE(serverReceivedPingMessages == 2);
|
REQUIRE(serverReceivedPingMessages >= 2);
|
||||||
|
|
||||||
// Give us 1000ms for the server to notice that clients went away
|
// Give us 1000ms for the server to notice that clients went away
|
||||||
ix::msleep(1000);
|
ix::msleep(1000);
|
||||||
|
@@ -33,15 +33,19 @@ namespace ix
|
|||||||
};
|
};
|
||||||
server.setConnectionStateFactory(factory);
|
server.setConnectionStateFactory(factory);
|
||||||
|
|
||||||
server.setOnConnectionCallback([&server, &connectionId](
|
server.setOnClientMessageCallback(
|
||||||
std::shared_ptr<ix::WebSocket> webSocket,
|
[&server, &connectionId](std::shared_ptr<ConnectionState> connectionState,
|
||||||
std::shared_ptr<ConnectionState> connectionState) {
|
ConnectionInfo& connectionInfo,
|
||||||
webSocket->setOnMessageCallback([webSocket, connectionState, &connectionId, &server](
|
WebSocket& webSocket,
|
||||||
const ix::WebSocketMessagePtr& msg) {
|
const ix::WebSocketMessagePtr& msg) {
|
||||||
|
auto remoteIp = connectionInfo.remoteIp;
|
||||||
|
|
||||||
|
|
||||||
if (msg->type == ix::WebSocketMessageType::Open)
|
if (msg->type == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
TLogger() << "New connection";
|
TLogger() << "New connection";
|
||||||
connectionState->computeId();
|
connectionState->computeId();
|
||||||
|
TLogger() << "remote ip: " << remoteIp;
|
||||||
TLogger() << "id: " << connectionState->getId();
|
TLogger() << "id: " << connectionState->getId();
|
||||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
TLogger() << "Uri: " << msg->openInfo.uri;
|
||||||
TLogger() << "Headers:";
|
TLogger() << "Headers:";
|
||||||
@@ -60,14 +64,13 @@ namespace ix
|
|||||||
{
|
{
|
||||||
for (auto&& client : server.getClients())
|
for (auto&& client : server.getClients())
|
||||||
{
|
{
|
||||||
if (client != webSocket)
|
if (client.get() != &webSocket)
|
||||||
{
|
{
|
||||||
client->send(msg->str, msg->binary);
|
client->send(msg->str, msg->binary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
auto res = server.listen();
|
auto res = server.listen();
|
||||||
if (!res.first)
|
if (!res.first)
|
||||||
|
@@ -16,39 +16,40 @@ using namespace ix;
|
|||||||
|
|
||||||
bool startServer(ix::WebSocketServer& server, std::string& subProtocols)
|
bool startServer(ix::WebSocketServer& server, std::string& subProtocols)
|
||||||
{
|
{
|
||||||
server.setOnConnectionCallback(
|
server.setOnClientMessageCallback(
|
||||||
[&server, &subProtocols](std::shared_ptr<ix::WebSocket> webSocket,
|
[&server, &subProtocols](std::shared_ptr<ConnectionState> connectionState,
|
||||||
std::shared_ptr<ConnectionState> connectionState) {
|
ConnectionInfo& connectionInfo,
|
||||||
webSocket->setOnMessageCallback([webSocket, connectionState, &server, &subProtocols](
|
WebSocket& webSocket,
|
||||||
const ix::WebSocketMessagePtr& msg) {
|
const ix::WebSocketMessagePtr& msg) {
|
||||||
if (msg->type == ix::WebSocketMessageType::Open)
|
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() << it.first << ": " << it.second;
|
||||||
TLogger() << "id: " << connectionState->getId();
|
}
|
||||||
TLogger() << "Uri: " << msg->openInfo.uri;
|
|
||||||
TLogger() << "Headers:";
|
|
||||||
for (auto it : msg->openInfo.headers)
|
|
||||||
{
|
|
||||||
TLogger() << it.first << ": " << it.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
subProtocols = msg->openInfo.headers["Sec-WebSocket-Protocol"];
|
subProtocols = msg->openInfo.headers["Sec-WebSocket-Protocol"];
|
||||||
}
|
}
|
||||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
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");
|
if (client.get() != &webSocket)
|
||||||
}
|
|
||||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
|
||||||
{
|
|
||||||
for (auto&& client : server.getClients())
|
|
||||||
{
|
{
|
||||||
if (client != webSocket)
|
client->sendBinary(msg->str);
|
||||||
{
|
|
||||||
client->sendBinary(msg->str);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
auto res = server.listen();
|
auto res = server.listen();
|
||||||
|
@@ -10,10 +10,18 @@
|
|||||||
#include <ixwebsocket/IXNetSystem.h>
|
#include <ixwebsocket/IXNetSystem.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <signal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
ix::initNetSystem();
|
ix::initNetSystem();
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
#endif
|
||||||
|
|
||||||
ix::CoreLogger::LogFunc logFunc = [](const char* msg, ix::LogLevel level) {
|
ix::CoreLogger::LogFunc logFunc = [](const char* msg, ix::LogLevel level) {
|
||||||
switch (level)
|
switch (level)
|
||||||
{
|
{
|
||||||
@@ -49,6 +57,7 @@ int main(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
ix::CoreLogger::setLogFunction(logFunc);
|
ix::CoreLogger::setLogFunction(logFunc);
|
||||||
|
spdlog::set_level(spdlog::level::debug);
|
||||||
|
|
||||||
int result = Catch::Session().run(argc, argv);
|
int result = Catch::Session().run(argc, argv);
|
||||||
|
|
||||||
|
2428
third_party/cpp-linenoise/linenoise.cpp
vendored
Normal file
2428
third_party/cpp-linenoise/linenoise.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2298
third_party/cpp-linenoise/linenoise.hpp
vendored
2298
third_party/cpp-linenoise/linenoise.hpp
vendored
File diff suppressed because it is too large
Load Diff
26
third_party/zlib/.gitignore
vendored
26
third_party/zlib/.gitignore
vendored
@@ -1,26 +0,0 @@
|
|||||||
*.diff
|
|
||||||
*.patch
|
|
||||||
*.orig
|
|
||||||
*.rej
|
|
||||||
|
|
||||||
*~
|
|
||||||
*.a
|
|
||||||
*.lo
|
|
||||||
*.o
|
|
||||||
*.dylib
|
|
||||||
|
|
||||||
*.gcda
|
|
||||||
*.gcno
|
|
||||||
*.gcov
|
|
||||||
|
|
||||||
/example
|
|
||||||
/example64
|
|
||||||
/examplesh
|
|
||||||
/libz.so*
|
|
||||||
/minigzip
|
|
||||||
/minigzip64
|
|
||||||
/minigzipsh
|
|
||||||
/zlib.pc
|
|
||||||
/configure.log
|
|
||||||
|
|
||||||
.DS_Store
|
|
227
third_party/zlib/CMakeLists.txt
vendored
227
third_party/zlib/CMakeLists.txt
vendored
@@ -1,227 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 2.4.4)
|
|
||||||
set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
|
|
||||||
|
|
||||||
project(zlib C)
|
|
||||||
|
|
||||||
set(VERSION "1.2.11")
|
|
||||||
|
|
||||||
option(ASM686 "Enable building i686 assembly implementation")
|
|
||||||
option(AMD64 "Enable building amd64 assembly implementation")
|
|
||||||
|
|
||||||
set(INSTALL_BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation directory for executables")
|
|
||||||
set(INSTALL_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation directory for libraries")
|
|
||||||
set(INSTALL_INC_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "Installation directory for headers")
|
|
||||||
set(INSTALL_MAN_DIR "${CMAKE_INSTALL_PREFIX}/share/man" CACHE PATH "Installation directory for manual pages")
|
|
||||||
set(INSTALL_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH "Installation directory for pkgconfig (.pc) files")
|
|
||||||
|
|
||||||
include(CheckTypeSize)
|
|
||||||
include(CheckFunctionExists)
|
|
||||||
include(CheckIncludeFile)
|
|
||||||
include(CheckCSourceCompiles)
|
|
||||||
enable_testing()
|
|
||||||
|
|
||||||
check_include_file(sys/types.h HAVE_SYS_TYPES_H)
|
|
||||||
check_include_file(stdint.h HAVE_STDINT_H)
|
|
||||||
check_include_file(stddef.h HAVE_STDDEF_H)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Check to see if we have large file support
|
|
||||||
#
|
|
||||||
set(CMAKE_REQUIRED_DEFINITIONS -D_LARGEFILE64_SOURCE=1)
|
|
||||||
# We add these other definitions here because CheckTypeSize.cmake
|
|
||||||
# in CMake 2.4.x does not automatically do so and we want
|
|
||||||
# compatibility with CMake 2.4.x.
|
|
||||||
if(HAVE_SYS_TYPES_H)
|
|
||||||
list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_SYS_TYPES_H)
|
|
||||||
endif()
|
|
||||||
if(HAVE_STDINT_H)
|
|
||||||
list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_STDINT_H)
|
|
||||||
endif()
|
|
||||||
if(HAVE_STDDEF_H)
|
|
||||||
list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_STDDEF_H)
|
|
||||||
endif()
|
|
||||||
check_type_size(off64_t OFF64_T)
|
|
||||||
if(HAVE_OFF64_T)
|
|
||||||
add_definitions(-D_LARGEFILE64_SOURCE=1)
|
|
||||||
endif()
|
|
||||||
set(CMAKE_REQUIRED_DEFINITIONS) # clear variable
|
|
||||||
|
|
||||||
#
|
|
||||||
# Check for fseeko
|
|
||||||
#
|
|
||||||
check_function_exists(fseeko HAVE_FSEEKO)
|
|
||||||
if(NOT HAVE_FSEEKO)
|
|
||||||
add_definitions(-DNO_FSEEKO)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
#
|
|
||||||
# Check for unistd.h
|
|
||||||
#
|
|
||||||
check_include_file(unistd.h Z_HAVE_UNISTD_H)
|
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
set(CMAKE_DEBUG_POSTFIX "d")
|
|
||||||
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
|
|
||||||
add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
|
|
||||||
# If we're doing an out of source build and the user has a zconf.h
|
|
||||||
# in their source tree...
|
|
||||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h)
|
|
||||||
message(STATUS "Renaming")
|
|
||||||
message(STATUS " ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h")
|
|
||||||
message(STATUS "to 'zconf.h.included' because this file is included with zlib")
|
|
||||||
message(STATUS "but CMake generates it automatically in the build directory.")
|
|
||||||
file(RENAME ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h.included)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(ZLIB_PC ${CMAKE_CURRENT_BINARY_DIR}/zlib.pc)
|
|
||||||
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/zlib.pc.cmakein
|
|
||||||
${ZLIB_PC} @ONLY)
|
|
||||||
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h.cmakein
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/zconf.h @ONLY)
|
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR})
|
|
||||||
|
|
||||||
|
|
||||||
#============================================================================
|
|
||||||
# zlib
|
|
||||||
#============================================================================
|
|
||||||
|
|
||||||
set(ZLIB_PUBLIC_HDRS
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/zconf.h
|
|
||||||
zlib.h
|
|
||||||
)
|
|
||||||
set(ZLIB_PRIVATE_HDRS
|
|
||||||
crc32.h
|
|
||||||
deflate.h
|
|
||||||
gzguts.h
|
|
||||||
inffast.h
|
|
||||||
inffixed.h
|
|
||||||
inflate.h
|
|
||||||
inftrees.h
|
|
||||||
trees.h
|
|
||||||
zutil.h
|
|
||||||
)
|
|
||||||
set(ZLIB_SRCS
|
|
||||||
adler32.c
|
|
||||||
compress.c
|
|
||||||
crc32.c
|
|
||||||
deflate.c
|
|
||||||
gzclose.c
|
|
||||||
gzlib.c
|
|
||||||
gzread.c
|
|
||||||
gzwrite.c
|
|
||||||
inflate.c
|
|
||||||
infback.c
|
|
||||||
inftrees.c
|
|
||||||
inffast.c
|
|
||||||
trees.c
|
|
||||||
uncompr.c
|
|
||||||
zutil.c
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOT MINGW)
|
|
||||||
set(ZLIB_DLL_SRCS
|
|
||||||
win32/zlib1.rc # If present will override custom build rule below.
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCC)
|
|
||||||
if(ASM686)
|
|
||||||
set(ZLIB_ASMS contrib/asm686/match.S)
|
|
||||||
elseif (AMD64)
|
|
||||||
set(ZLIB_ASMS contrib/amd64/amd64-match.S)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if(ZLIB_ASMS)
|
|
||||||
add_definitions(-DASMV)
|
|
||||||
set_source_files_properties(${ZLIB_ASMS} PROPERTIES LANGUAGE C COMPILE_FLAGS -DNO_UNDERLINE)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
if(ASM686)
|
|
||||||
ENABLE_LANGUAGE(ASM_MASM)
|
|
||||||
set(ZLIB_ASMS
|
|
||||||
contrib/masmx86/inffas32.asm
|
|
||||||
contrib/masmx86/match686.asm
|
|
||||||
)
|
|
||||||
elseif (AMD64)
|
|
||||||
ENABLE_LANGUAGE(ASM_MASM)
|
|
||||||
set(ZLIB_ASMS
|
|
||||||
contrib/masmx64/gvmat64.asm
|
|
||||||
contrib/masmx64/inffasx64.asm
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ZLIB_ASMS)
|
|
||||||
add_definitions(-DASMV -DASMINF)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# parse the full version number from zlib.h and include in ZLIB_FULL_VERSION
|
|
||||||
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/zlib.h _zlib_h_contents)
|
|
||||||
string(REGEX REPLACE ".*#define[ \t]+ZLIB_VERSION[ \t]+\"([-0-9A-Za-z.]+)\".*"
|
|
||||||
"\\1" ZLIB_FULL_VERSION ${_zlib_h_contents})
|
|
||||||
|
|
||||||
if(MINGW)
|
|
||||||
# This gets us DLL resource information when compiling on MinGW.
|
|
||||||
if(NOT CMAKE_RC_COMPILER)
|
|
||||||
set(CMAKE_RC_COMPILER windres.exe)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj
|
|
||||||
COMMAND ${CMAKE_RC_COMPILER}
|
|
||||||
-D GCC_WINDRES
|
|
||||||
-I ${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
-I ${CMAKE_CURRENT_BINARY_DIR}
|
|
||||||
-o ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj
|
|
||||||
-i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zlib1.rc)
|
|
||||||
set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj)
|
|
||||||
endif(MINGW)
|
|
||||||
|
|
||||||
add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
|
|
||||||
add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
|
|
||||||
set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL)
|
|
||||||
set_target_properties(zlib PROPERTIES SOVERSION 1)
|
|
||||||
|
|
||||||
if(NOT CYGWIN)
|
|
||||||
# This property causes shared libraries on Linux to have the full version
|
|
||||||
# encoded into their final filename. We disable this on Cygwin because
|
|
||||||
# it causes cygz-${ZLIB_FULL_VERSION}.dll to be created when cygz.dll
|
|
||||||
# seems to be the default.
|
|
||||||
#
|
|
||||||
# This has no effect with MSVC, on that platform the version info for
|
|
||||||
# the DLL comes from the resource file win32/zlib1.rc
|
|
||||||
set_target_properties(zlib PROPERTIES VERSION ${ZLIB_FULL_VERSION})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(UNIX)
|
|
||||||
# On unix-like platforms the library is almost always called libz
|
|
||||||
set_target_properties(zlib zlibstatic PROPERTIES OUTPUT_NAME z)
|
|
||||||
if(NOT APPLE)
|
|
||||||
set_target_properties(zlib PROPERTIES LINK_FLAGS "-Wl,--version-script,\"${CMAKE_CURRENT_SOURCE_DIR}/zlib.map\"")
|
|
||||||
endif()
|
|
||||||
elseif(BUILD_SHARED_LIBS AND WIN32)
|
|
||||||
# Creates zlib1.dll when building shared library version
|
|
||||||
set_target_properties(zlib PROPERTIES SUFFIX "1.dll")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL )
|
|
||||||
install(TARGETS zlib zlibstatic
|
|
||||||
RUNTIME DESTINATION "${INSTALL_BIN_DIR}"
|
|
||||||
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}"
|
|
||||||
LIBRARY DESTINATION "${INSTALL_LIB_DIR}" )
|
|
||||||
endif()
|
|
||||||
if(NOT SKIP_INSTALL_HEADERS AND NOT SKIP_INSTALL_ALL )
|
|
||||||
install(FILES ${ZLIB_PUBLIC_HDRS} DESTINATION "${INSTALL_INC_DIR}")
|
|
||||||
endif()
|
|
||||||
if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL )
|
|
||||||
install(FILES zlib.3 DESTINATION "${INSTALL_MAN_DIR}/man3")
|
|
||||||
endif()
|
|
||||||
if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL )
|
|
||||||
install(FILES ${ZLIB_PC} DESTINATION "${INSTALL_PKGCONFIG_DIR}")
|
|
||||||
endif()
|
|
1515
third_party/zlib/ChangeLog
vendored
1515
third_party/zlib/ChangeLog
vendored
File diff suppressed because it is too large
Load Diff
368
third_party/zlib/FAQ
vendored
368
third_party/zlib/FAQ
vendored
@@ -1,368 +0,0 @@
|
|||||||
|
|
||||||
Frequently Asked Questions about zlib
|
|
||||||
|
|
||||||
|
|
||||||
If your question is not there, please check the zlib home page
|
|
||||||
http://zlib.net/ which may have more recent information.
|
|
||||||
The lastest zlib FAQ is at http://zlib.net/zlib_faq.html
|
|
||||||
|
|
||||||
|
|
||||||
1. Is zlib Y2K-compliant?
|
|
||||||
|
|
||||||
Yes. zlib doesn't handle dates.
|
|
||||||
|
|
||||||
2. Where can I get a Windows DLL version?
|
|
||||||
|
|
||||||
The zlib sources can be compiled without change to produce a DLL. See the
|
|
||||||
file win32/DLL_FAQ.txt in the zlib distribution. Pointers to the
|
|
||||||
precompiled DLL are found in the zlib web site at http://zlib.net/ .
|
|
||||||
|
|
||||||
3. Where can I get a Visual Basic interface to zlib?
|
|
||||||
|
|
||||||
See
|
|
||||||
* http://marknelson.us/1997/01/01/zlib-engine/
|
|
||||||
* win32/DLL_FAQ.txt in the zlib distribution
|
|
||||||
|
|
||||||
4. compress() returns Z_BUF_ERROR.
|
|
||||||
|
|
||||||
Make sure that before the call of compress(), the length of the compressed
|
|
||||||
buffer is equal to the available size of the compressed buffer and not
|
|
||||||
zero. For Visual Basic, check that this parameter is passed by reference
|
|
||||||
("as any"), not by value ("as long").
|
|
||||||
|
|
||||||
5. deflate() or inflate() returns Z_BUF_ERROR.
|
|
||||||
|
|
||||||
Before making the call, make sure that avail_in and avail_out are not zero.
|
|
||||||
When setting the parameter flush equal to Z_FINISH, also make sure that
|
|
||||||
avail_out is big enough to allow processing all pending input. Note that a
|
|
||||||
Z_BUF_ERROR is not fatal--another call to deflate() or inflate() can be
|
|
||||||
made with more input or output space. A Z_BUF_ERROR may in fact be
|
|
||||||
unavoidable depending on how the functions are used, since it is not
|
|
||||||
possible to tell whether or not there is more output pending when
|
|
||||||
strm.avail_out returns with zero. See http://zlib.net/zlib_how.html for a
|
|
||||||
heavily annotated example.
|
|
||||||
|
|
||||||
6. Where's the zlib documentation (man pages, etc.)?
|
|
||||||
|
|
||||||
It's in zlib.h . Examples of zlib usage are in the files test/example.c
|
|
||||||
and test/minigzip.c, with more in examples/ .
|
|
||||||
|
|
||||||
7. Why don't you use GNU autoconf or libtool or ...?
|
|
||||||
|
|
||||||
Because we would like to keep zlib as a very small and simple package.
|
|
||||||
zlib is rather portable and doesn't need much configuration.
|
|
||||||
|
|
||||||
8. I found a bug in zlib.
|
|
||||||
|
|
||||||
Most of the time, such problems are due to an incorrect usage of zlib.
|
|
||||||
Please try to reproduce the problem with a small program and send the
|
|
||||||
corresponding source to us at zlib@gzip.org . Do not send multi-megabyte
|
|
||||||
data files without prior agreement.
|
|
||||||
|
|
||||||
9. Why do I get "undefined reference to gzputc"?
|
|
||||||
|
|
||||||
If "make test" produces something like
|
|
||||||
|
|
||||||
example.o(.text+0x154): undefined reference to `gzputc'
|
|
||||||
|
|
||||||
check that you don't have old files libz.* in /usr/lib, /usr/local/lib or
|
|
||||||
/usr/X11R6/lib. Remove any old versions, then do "make install".
|
|
||||||
|
|
||||||
10. I need a Delphi interface to zlib.
|
|
||||||
|
|
||||||
See the contrib/delphi directory in the zlib distribution.
|
|
||||||
|
|
||||||
11. Can zlib handle .zip archives?
|
|
||||||
|
|
||||||
Not by itself, no. See the directory contrib/minizip in the zlib
|
|
||||||
distribution.
|
|
||||||
|
|
||||||
12. Can zlib handle .Z files?
|
|
||||||
|
|
||||||
No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt
|
|
||||||
the code of uncompress on your own.
|
|
||||||
|
|
||||||
13. How can I make a Unix shared library?
|
|
||||||
|
|
||||||
By default a shared (and a static) library is built for Unix. So:
|
|
||||||
|
|
||||||
make distclean
|
|
||||||
./configure
|
|
||||||
make
|
|
||||||
|
|
||||||
14. How do I install a shared zlib library on Unix?
|
|
||||||
|
|
||||||
After the above, then:
|
|
||||||
|
|
||||||
make install
|
|
||||||
|
|
||||||
However, many flavors of Unix come with a shared zlib already installed.
|
|
||||||
Before going to the trouble of compiling a shared version of zlib and
|
|
||||||
trying to install it, you may want to check if it's already there! If you
|
|
||||||
can #include <zlib.h>, it's there. The -lz option will probably link to
|
|
||||||
it. You can check the version at the top of zlib.h or with the
|
|
||||||
ZLIB_VERSION symbol defined in zlib.h .
|
|
||||||
|
|
||||||
15. I have a question about OttoPDF.
|
|
||||||
|
|
||||||
We are not the authors of OttoPDF. The real author is on the OttoPDF web
|
|
||||||
site: Joel Hainley, jhainley@myndkryme.com.
|
|
||||||
|
|
||||||
16. Can zlib decode Flate data in an Adobe PDF file?
|
|
||||||
|
|
||||||
Yes. See http://www.pdflib.com/ . To modify PDF forms, see
|
|
||||||
http://sourceforge.net/projects/acroformtool/ .
|
|
||||||
|
|
||||||
17. Why am I getting this "register_frame_info not found" error on Solaris?
|
|
||||||
|
|
||||||
After installing zlib 1.1.4 on Solaris 2.6, running applications using zlib
|
|
||||||
generates an error such as:
|
|
||||||
|
|
||||||
ld.so.1: rpm: fatal: relocation error: file /usr/local/lib/libz.so:
|
|
||||||
symbol __register_frame_info: referenced symbol not found
|
|
||||||
|
|
||||||
The symbol __register_frame_info is not part of zlib, it is generated by
|
|
||||||
the C compiler (cc or gcc). You must recompile applications using zlib
|
|
||||||
which have this problem. This problem is specific to Solaris. See
|
|
||||||
http://www.sunfreeware.com for Solaris versions of zlib and applications
|
|
||||||
using zlib.
|
|
||||||
|
|
||||||
18. Why does gzip give an error on a file I make with compress/deflate?
|
|
||||||
|
|
||||||
The compress and deflate functions produce data in the zlib format, which
|
|
||||||
is different and incompatible with the gzip format. The gz* functions in
|
|
||||||
zlib on the other hand use the gzip format. Both the zlib and gzip formats
|
|
||||||
use the same compressed data format internally, but have different headers
|
|
||||||
and trailers around the compressed data.
|
|
||||||
|
|
||||||
19. Ok, so why are there two different formats?
|
|
||||||
|
|
||||||
The gzip format was designed to retain the directory information about a
|
|
||||||
single file, such as the name and last modification date. The zlib format
|
|
||||||
on the other hand was designed for in-memory and communication channel
|
|
||||||
applications, and has a much more compact header and trailer and uses a
|
|
||||||
faster integrity check than gzip.
|
|
||||||
|
|
||||||
20. Well that's nice, but how do I make a gzip file in memory?
|
|
||||||
|
|
||||||
You can request that deflate write the gzip format instead of the zlib
|
|
||||||
format using deflateInit2(). You can also request that inflate decode the
|
|
||||||
gzip format using inflateInit2(). Read zlib.h for more details.
|
|
||||||
|
|
||||||
21. Is zlib thread-safe?
|
|
||||||
|
|
||||||
Yes. However any library routines that zlib uses and any application-
|
|
||||||
provided memory allocation routines must also be thread-safe. zlib's gz*
|
|
||||||
functions use stdio library routines, and most of zlib's functions use the
|
|
||||||
library memory allocation routines by default. zlib's *Init* functions
|
|
||||||
allow for the application to provide custom memory allocation routines.
|
|
||||||
|
|
||||||
Of course, you should only operate on any given zlib or gzip stream from a
|
|
||||||
single thread at a time.
|
|
||||||
|
|
||||||
22. Can I use zlib in my commercial application?
|
|
||||||
|
|
||||||
Yes. Please read the license in zlib.h.
|
|
||||||
|
|
||||||
23. Is zlib under the GNU license?
|
|
||||||
|
|
||||||
No. Please read the license in zlib.h.
|
|
||||||
|
|
||||||
24. The license says that altered source versions must be "plainly marked". So
|
|
||||||
what exactly do I need to do to meet that requirement?
|
|
||||||
|
|
||||||
You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h. In
|
|
||||||
particular, the final version number needs to be changed to "f", and an
|
|
||||||
identification string should be appended to ZLIB_VERSION. Version numbers
|
|
||||||
x.x.x.f are reserved for modifications to zlib by others than the zlib
|
|
||||||
maintainers. For example, if the version of the base zlib you are altering
|
|
||||||
is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and
|
|
||||||
ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3". You can also
|
|
||||||
update the version strings in deflate.c and inftrees.c.
|
|
||||||
|
|
||||||
For altered source distributions, you should also note the origin and
|
|
||||||
nature of the changes in zlib.h, as well as in ChangeLog and README, along
|
|
||||||
with the dates of the alterations. The origin should include at least your
|
|
||||||
name (or your company's name), and an email address to contact for help or
|
|
||||||
issues with the library.
|
|
||||||
|
|
||||||
Note that distributing a compiled zlib library along with zlib.h and
|
|
||||||
zconf.h is also a source distribution, and so you should change
|
|
||||||
ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes
|
|
||||||
in zlib.h as you would for a full source distribution.
|
|
||||||
|
|
||||||
25. Will zlib work on a big-endian or little-endian architecture, and can I
|
|
||||||
exchange compressed data between them?
|
|
||||||
|
|
||||||
Yes and yes.
|
|
||||||
|
|
||||||
26. Will zlib work on a 64-bit machine?
|
|
||||||
|
|
||||||
Yes. It has been tested on 64-bit machines, and has no dependence on any
|
|
||||||
data types being limited to 32-bits in length. If you have any
|
|
||||||
difficulties, please provide a complete problem report to zlib@gzip.org
|
|
||||||
|
|
||||||
27. Will zlib decompress data from the PKWare Data Compression Library?
|
|
||||||
|
|
||||||
No. The PKWare DCL uses a completely different compressed data format than
|
|
||||||
does PKZIP and zlib. However, you can look in zlib's contrib/blast
|
|
||||||
directory for a possible solution to your problem.
|
|
||||||
|
|
||||||
28. Can I access data randomly in a compressed stream?
|
|
||||||
|
|
||||||
No, not without some preparation. If when compressing you periodically use
|
|
||||||
Z_FULL_FLUSH, carefully write all the pending data at those points, and
|
|
||||||
keep an index of those locations, then you can start decompression at those
|
|
||||||
points. You have to be careful to not use Z_FULL_FLUSH too often, since it
|
|
||||||
can significantly degrade compression. Alternatively, you can scan a
|
|
||||||
deflate stream once to generate an index, and then use that index for
|
|
||||||
random access. See examples/zran.c .
|
|
||||||
|
|
||||||
29. Does zlib work on MVS, OS/390, CICS, etc.?
|
|
||||||
|
|
||||||
It has in the past, but we have not heard of any recent evidence. There
|
|
||||||
were working ports of zlib 1.1.4 to MVS, but those links no longer work.
|
|
||||||
If you know of recent, successful applications of zlib on these operating
|
|
||||||
systems, please let us know. Thanks.
|
|
||||||
|
|
||||||
30. Is there some simpler, easier to read version of inflate I can look at to
|
|
||||||
understand the deflate format?
|
|
||||||
|
|
||||||
First off, you should read RFC 1951. Second, yes. Look in zlib's
|
|
||||||
contrib/puff directory.
|
|
||||||
|
|
||||||
31. Does zlib infringe on any patents?
|
|
||||||
|
|
||||||
As far as we know, no. In fact, that was originally the whole point behind
|
|
||||||
zlib. Look here for some more information:
|
|
||||||
|
|
||||||
http://www.gzip.org/#faq11
|
|
||||||
|
|
||||||
32. Can zlib work with greater than 4 GB of data?
|
|
||||||
|
|
||||||
Yes. inflate() and deflate() will process any amount of data correctly.
|
|
||||||
Each call of inflate() or deflate() is limited to input and output chunks
|
|
||||||
of the maximum value that can be stored in the compiler's "unsigned int"
|
|
||||||
type, but there is no limit to the number of chunks. Note however that the
|
|
||||||
strm.total_in and strm_total_out counters may be limited to 4 GB. These
|
|
||||||
counters are provided as a convenience and are not used internally by
|
|
||||||
inflate() or deflate(). The application can easily set up its own counters
|
|
||||||
updated after each call of inflate() or deflate() to count beyond 4 GB.
|
|
||||||
compress() and uncompress() may be limited to 4 GB, since they operate in a
|
|
||||||
single call. gzseek() and gztell() may be limited to 4 GB depending on how
|
|
||||||
zlib is compiled. See the zlibCompileFlags() function in zlib.h.
|
|
||||||
|
|
||||||
The word "may" appears several times above since there is a 4 GB limit only
|
|
||||||
if the compiler's "long" type is 32 bits. If the compiler's "long" type is
|
|
||||||
64 bits, then the limit is 16 exabytes.
|
|
||||||
|
|
||||||
33. Does zlib have any security vulnerabilities?
|
|
||||||
|
|
||||||
The only one that we are aware of is potentially in gzprintf(). If zlib is
|
|
||||||
compiled to use sprintf() or vsprintf(), then there is no protection
|
|
||||||
against a buffer overflow of an 8K string space (or other value as set by
|
|
||||||
gzbuffer()), other than the caller of gzprintf() assuring that the output
|
|
||||||
will not exceed 8K. On the other hand, if zlib is compiled to use
|
|
||||||
snprintf() or vsnprintf(), which should normally be the case, then there is
|
|
||||||
no vulnerability. The ./configure script will display warnings if an
|
|
||||||
insecure variation of sprintf() will be used by gzprintf(). Also the
|
|
||||||
zlibCompileFlags() function will return information on what variant of
|
|
||||||
sprintf() is used by gzprintf().
|
|
||||||
|
|
||||||
If you don't have snprintf() or vsnprintf() and would like one, you can
|
|
||||||
find a portable implementation here:
|
|
||||||
|
|
||||||
http://www.ijs.si/software/snprintf/
|
|
||||||
|
|
||||||
Note that you should be using the most recent version of zlib. Versions
|
|
||||||
1.1.3 and before were subject to a double-free vulnerability, and versions
|
|
||||||
1.2.1 and 1.2.2 were subject to an access exception when decompressing
|
|
||||||
invalid compressed data.
|
|
||||||
|
|
||||||
34. Is there a Java version of zlib?
|
|
||||||
|
|
||||||
Probably what you want is to use zlib in Java. zlib is already included
|
|
||||||
as part of the Java SDK in the java.util.zip package. If you really want
|
|
||||||
a version of zlib written in the Java language, look on the zlib home
|
|
||||||
page for links: http://zlib.net/ .
|
|
||||||
|
|
||||||
35. I get this or that compiler or source-code scanner warning when I crank it
|
|
||||||
up to maximally-pedantic. Can't you guys write proper code?
|
|
||||||
|
|
||||||
Many years ago, we gave up attempting to avoid warnings on every compiler
|
|
||||||
in the universe. It just got to be a waste of time, and some compilers
|
|
||||||
were downright silly as well as contradicted each other. So now, we simply
|
|
||||||
make sure that the code always works.
|
|
||||||
|
|
||||||
36. Valgrind (or some similar memory access checker) says that deflate is
|
|
||||||
performing a conditional jump that depends on an uninitialized value.
|
|
||||||
Isn't that a bug?
|
|
||||||
|
|
||||||
No. That is intentional for performance reasons, and the output of deflate
|
|
||||||
is not affected. This only started showing up recently since zlib 1.2.x
|
|
||||||
uses malloc() by default for allocations, whereas earlier versions used
|
|
||||||
calloc(), which zeros out the allocated memory. Even though the code was
|
|
||||||
correct, versions 1.2.4 and later was changed to not stimulate these
|
|
||||||
checkers.
|
|
||||||
|
|
||||||
37. Will zlib read the (insert any ancient or arcane format here) compressed
|
|
||||||
data format?
|
|
||||||
|
|
||||||
Probably not. Look in the comp.compression FAQ for pointers to various
|
|
||||||
formats and associated software.
|
|
||||||
|
|
||||||
38. How can I encrypt/decrypt zip files with zlib?
|
|
||||||
|
|
||||||
zlib doesn't support encryption. The original PKZIP encryption is very
|
|
||||||
weak and can be broken with freely available programs. To get strong
|
|
||||||
encryption, use GnuPG, http://www.gnupg.org/ , which already includes zlib
|
|
||||||
compression. For PKZIP compatible "encryption", look at
|
|
||||||
http://www.info-zip.org/
|
|
||||||
|
|
||||||
39. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
|
|
||||||
|
|
||||||
"gzip" is the gzip format, and "deflate" is the zlib format. They should
|
|
||||||
probably have called the second one "zlib" instead to avoid confusion with
|
|
||||||
the raw deflate compressed data format. While the HTTP 1.1 RFC 2616
|
|
||||||
correctly points to the zlib specification in RFC 1950 for the "deflate"
|
|
||||||
transfer encoding, there have been reports of servers and browsers that
|
|
||||||
incorrectly produce or expect raw deflate data per the deflate
|
|
||||||
specification in RFC 1951, most notably Microsoft. So even though the
|
|
||||||
"deflate" transfer encoding using the zlib format would be the more
|
|
||||||
efficient approach (and in fact exactly what the zlib format was designed
|
|
||||||
for), using the "gzip" transfer encoding is probably more reliable due to
|
|
||||||
an unfortunate choice of name on the part of the HTTP 1.1 authors.
|
|
||||||
|
|
||||||
Bottom line: use the gzip format for HTTP 1.1 encoding.
|
|
||||||
|
|
||||||
40. Does zlib support the new "Deflate64" format introduced by PKWare?
|
|
||||||
|
|
||||||
No. PKWare has apparently decided to keep that format proprietary, since
|
|
||||||
they have not documented it as they have previous compression formats. In
|
|
||||||
any case, the compression improvements are so modest compared to other more
|
|
||||||
modern approaches, that it's not worth the effort to implement.
|
|
||||||
|
|
||||||
41. I'm having a problem with the zip functions in zlib, can you help?
|
|
||||||
|
|
||||||
There are no zip functions in zlib. You are probably using minizip by
|
|
||||||
Giles Vollant, which is found in the contrib directory of zlib. It is not
|
|
||||||
part of zlib. In fact none of the stuff in contrib is part of zlib. The
|
|
||||||
files in there are not supported by the zlib authors. You need to contact
|
|
||||||
the authors of the respective contribution for help.
|
|
||||||
|
|
||||||
42. The match.asm code in contrib is under the GNU General Public License.
|
|
||||||
Since it's part of zlib, doesn't that mean that all of zlib falls under the
|
|
||||||
GNU GPL?
|
|
||||||
|
|
||||||
No. The files in contrib are not part of zlib. They were contributed by
|
|
||||||
other authors and are provided as a convenience to the user within the zlib
|
|
||||||
distribution. Each item in contrib has its own license.
|
|
||||||
|
|
||||||
43. Is zlib subject to export controls? What is its ECCN?
|
|
||||||
|
|
||||||
zlib is not subject to export controls, and so is classified as EAR99.
|
|
||||||
|
|
||||||
44. Can you please sign these lengthy legal documents and fax them back to us
|
|
||||||
so that we can use your software in our product?
|
|
||||||
|
|
||||||
No. Go away. Shoo.
|
|
68
third_party/zlib/INDEX
vendored
68
third_party/zlib/INDEX
vendored
@@ -1,68 +0,0 @@
|
|||||||
CMakeLists.txt cmake build file
|
|
||||||
ChangeLog history of changes
|
|
||||||
FAQ Frequently Asked Questions about zlib
|
|
||||||
INDEX this file
|
|
||||||
Makefile dummy Makefile that tells you to ./configure
|
|
||||||
Makefile.in template for Unix Makefile
|
|
||||||
README guess what
|
|
||||||
configure configure script for Unix
|
|
||||||
make_vms.com makefile for VMS
|
|
||||||
test/example.c zlib usages examples for build testing
|
|
||||||
test/minigzip.c minimal gzip-like functionality for build testing
|
|
||||||
test/infcover.c inf*.c code coverage for build coverage testing
|
|
||||||
treebuild.xml XML description of source file dependencies
|
|
||||||
zconf.h.cmakein zconf.h template for cmake
|
|
||||||
zconf.h.in zconf.h template for configure
|
|
||||||
zlib.3 Man page for zlib
|
|
||||||
zlib.3.pdf Man page in PDF format
|
|
||||||
zlib.map Linux symbol information
|
|
||||||
zlib.pc.in Template for pkg-config descriptor
|
|
||||||
zlib.pc.cmakein zlib.pc template for cmake
|
|
||||||
zlib2ansi perl script to convert source files for C++ compilation
|
|
||||||
|
|
||||||
amiga/ makefiles for Amiga SAS C
|
|
||||||
as400/ makefiles for AS/400
|
|
||||||
doc/ documentation for formats and algorithms
|
|
||||||
msdos/ makefiles for MSDOS
|
|
||||||
nintendods/ makefile for Nintendo DS
|
|
||||||
old/ makefiles for various architectures and zlib documentation
|
|
||||||
files that have not yet been updated for zlib 1.2.x
|
|
||||||
qnx/ makefiles for QNX
|
|
||||||
watcom/ makefiles for OpenWatcom
|
|
||||||
win32/ makefiles for Windows
|
|
||||||
|
|
||||||
zlib public header files (required for library use):
|
|
||||||
zconf.h
|
|
||||||
zlib.h
|
|
||||||
|
|
||||||
private source files used to build the zlib library:
|
|
||||||
adler32.c
|
|
||||||
compress.c
|
|
||||||
crc32.c
|
|
||||||
crc32.h
|
|
||||||
deflate.c
|
|
||||||
deflate.h
|
|
||||||
gzclose.c
|
|
||||||
gzguts.h
|
|
||||||
gzlib.c
|
|
||||||
gzread.c
|
|
||||||
gzwrite.c
|
|
||||||
infback.c
|
|
||||||
inffast.c
|
|
||||||
inffast.h
|
|
||||||
inffixed.h
|
|
||||||
inflate.c
|
|
||||||
inflate.h
|
|
||||||
inftrees.c
|
|
||||||
inftrees.h
|
|
||||||
trees.c
|
|
||||||
trees.h
|
|
||||||
uncompr.c
|
|
||||||
zutil.c
|
|
||||||
zutil.h
|
|
||||||
|
|
||||||
source files for sample programs
|
|
||||||
See examples/README.examples
|
|
||||||
|
|
||||||
unsupported contributions by third parties
|
|
||||||
See contrib/README.contrib
|
|
5
third_party/zlib/Makefile
vendored
5
third_party/zlib/Makefile
vendored
@@ -1,5 +0,0 @@
|
|||||||
all:
|
|
||||||
-@echo "Please use ./configure first. Thank you."
|
|
||||||
|
|
||||||
distclean:
|
|
||||||
make -f Makefile.in distclean
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user