Compare commits
	
		
			5 Commits
		
	
	
		
			feature/zl
			...
			feature/cp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7ff12a36b9 | ||
| 
						 | 
					945c692227 | ||
| 
						 | 
					c16b64bcb2 | ||
| 
						 | 
					886b8f54bf | ||
| 
						 | 
					02810f9adf | 
							
								
								
									
										19
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.github/workflows/unittest_windows.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,19 +0,0 @@
 | 
			
		||||
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,4 +5,3 @@ ixsnake/ixsnake/.certs/
 | 
			
		||||
site/
 | 
			
		||||
ws/.certs/
 | 
			
		||||
ws/.srl
 | 
			
		||||
ixhttpd
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
 | 
			
		||||
set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}")
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
 | 
			
		||||
 
 | 
			
		||||
@@ -30,15 +30,12 @@ set( IXWEBSOCKET_SOURCES
 | 
			
		||||
    ixwebsocket/IXConnectionState.cpp
 | 
			
		||||
    ixwebsocket/IXDNSLookup.cpp
 | 
			
		||||
    ixwebsocket/IXExponentialBackoff.cpp
 | 
			
		||||
    ixwebsocket/IXGetFreePort.cpp
 | 
			
		||||
    ixwebsocket/IXHttp.cpp
 | 
			
		||||
    ixwebsocket/IXHttpClient.cpp
 | 
			
		||||
    ixwebsocket/IXHttpServer.cpp
 | 
			
		||||
    ixwebsocket/IXNetSystem.cpp
 | 
			
		||||
    ixwebsocket/IXSelectInterrupt.cpp
 | 
			
		||||
    ixwebsocket/IXSelectInterruptFactory.cpp
 | 
			
		||||
    ixwebsocket/IXSelectInterruptPipe.cpp
 | 
			
		||||
    ixwebsocket/IXSetThreadName.cpp
 | 
			
		||||
    ixwebsocket/IXSocket.cpp
 | 
			
		||||
    ixwebsocket/IXSocketConnect.cpp
 | 
			
		||||
    ixwebsocket/IXSocketFactory.cpp
 | 
			
		||||
@@ -54,7 +51,6 @@ set( IXWEBSOCKET_SOURCES
 | 
			
		||||
    ixwebsocket/IXWebSocketPerMessageDeflate.cpp
 | 
			
		||||
    ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp
 | 
			
		||||
    ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp
 | 
			
		||||
    ixwebsocket/IXWebSocketProxyServer.cpp
 | 
			
		||||
    ixwebsocket/IXWebSocketServer.cpp
 | 
			
		||||
    ixwebsocket/IXWebSocketTransport.cpp
 | 
			
		||||
)
 | 
			
		||||
@@ -62,11 +58,9 @@ set( IXWEBSOCKET_SOURCES
 | 
			
		||||
set( IXWEBSOCKET_HEADERS
 | 
			
		||||
    ixwebsocket/IXBench.h
 | 
			
		||||
    ixwebsocket/IXCancellationRequest.h
 | 
			
		||||
    ixwebsocket/IXConnectionInfo.h
 | 
			
		||||
    ixwebsocket/IXConnectionState.h
 | 
			
		||||
    ixwebsocket/IXDNSLookup.h
 | 
			
		||||
    ixwebsocket/IXExponentialBackoff.h
 | 
			
		||||
    ixwebsocket/IXGetFreePort.h
 | 
			
		||||
    ixwebsocket/IXHttp.h
 | 
			
		||||
    ixwebsocket/IXHttpClient.h
 | 
			
		||||
    ixwebsocket/IXHttpServer.h
 | 
			
		||||
@@ -74,7 +68,6 @@ set( IXWEBSOCKET_HEADERS
 | 
			
		||||
    ixwebsocket/IXProgressCallback.h
 | 
			
		||||
    ixwebsocket/IXSelectInterrupt.h
 | 
			
		||||
    ixwebsocket/IXSelectInterruptFactory.h
 | 
			
		||||
    ixwebsocket/IXSelectInterruptPipe.h
 | 
			
		||||
    ixwebsocket/IXSetThreadName.h
 | 
			
		||||
    ixwebsocket/IXSocket.h
 | 
			
		||||
    ixwebsocket/IXSocketConnect.h
 | 
			
		||||
@@ -99,13 +92,29 @@ set( IXWEBSOCKET_HEADERS
 | 
			
		||||
    ixwebsocket/IXWebSocketPerMessageDeflate.h
 | 
			
		||||
    ixwebsocket/IXWebSocketPerMessageDeflateCodec.h
 | 
			
		||||
    ixwebsocket/IXWebSocketPerMessageDeflateOptions.h
 | 
			
		||||
    ixwebsocket/IXWebSocketProxyServer.h
 | 
			
		||||
    ixwebsocket/IXWebSocketSendInfo.h
 | 
			
		||||
    ixwebsocket/IXWebSocketServer.h
 | 
			
		||||
    ixwebsocket/IXWebSocketTransport.h
 | 
			
		||||
    ixwebsocket/IXWebSocketVersion.h
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
if (UNIX)
 | 
			
		||||
    # Linux, Mac, iOS, Android
 | 
			
		||||
    list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptPipe.cpp )
 | 
			
		||||
    list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptPipe.h )
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Platform specific code
 | 
			
		||||
if (APPLE)
 | 
			
		||||
    list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/apple/IXSetThreadName_apple.cpp)
 | 
			
		||||
elseif (WIN32)
 | 
			
		||||
    list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/windows/IXSetThreadName_windows.cpp)
 | 
			
		||||
elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
 | 
			
		||||
    list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/freebsd/IXSetThreadName_freebsd.cpp)
 | 
			
		||||
else()
 | 
			
		||||
    list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/linux/IXSetThreadName_linux.cpp)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
option(USE_TLS "Enable TLS support" FALSE)
 | 
			
		||||
 | 
			
		||||
if (USE_TLS)
 | 
			
		||||
@@ -171,8 +180,10 @@ if (USE_TLS)
 | 
			
		||||
      # set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/local/include/openssl-1.0)
 | 
			
		||||
    endif()
 | 
			
		||||
 | 
			
		||||
    # Use OPENSSL_ROOT_DIR CMake variable if you need to use your own openssl
 | 
			
		||||
    find_package(OpenSSL REQUIRED) 
 | 
			
		||||
    # This OPENSSL_FOUND check is to help find a cmake manually configured OpenSSL
 | 
			
		||||
    if (NOT OPENSSL_FOUND)
 | 
			
		||||
      find_package(OpenSSL REQUIRED)
 | 
			
		||||
    endif()
 | 
			
		||||
    message(STATUS "OpenSSL: " ${OPENSSL_VERSION})
 | 
			
		||||
 | 
			
		||||
    add_definitions(${OPENSSL_DEFINITIONS})
 | 
			
		||||
@@ -190,17 +201,17 @@ if (USE_TLS)
 | 
			
		||||
  endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
option(USE_ZLIB "Enable zlib support" TRUE)
 | 
			
		||||
 | 
			
		||||
if (USE_ZLIB)
 | 
			
		||||
  # Use ZLIB_ROOT CMake variable if you need to use your own zlib
 | 
			
		||||
# This ZLIB_FOUND check is to help find a cmake manually configured zlib
 | 
			
		||||
if (NOT ZLIB_FOUND)
 | 
			
		||||
  find_package(ZLIB)
 | 
			
		||||
  if (ZLIB_FOUND)
 | 
			
		||||
    include_directories(${ZLIB_INCLUDE_DIRS})
 | 
			
		||||
    target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
    target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB)
 | 
			
		||||
  endif()
 | 
			
		||||
endif()
 | 
			
		||||
if (ZLIB_FOUND)
 | 
			
		||||
  include_directories(${ZLIB_INCLUDE_DIRS})
 | 
			
		||||
  target_link_libraries(ixwebsocket ${ZLIB_LIBRARIES})
 | 
			
		||||
else()
 | 
			
		||||
  include_directories(third_party/zlib ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib)
 | 
			
		||||
  add_subdirectory(third_party/zlib)
 | 
			
		||||
  target_link_libraries(ixwebsocket zlibstatic)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (WIN32)
 | 
			
		||||
@@ -227,29 +238,19 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
 | 
			
		||||
    target_compile_options(ixwebsocket PRIVATE /MP)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
target_include_directories(ixwebsocket PUBLIC
 | 
			
		||||
  $<BUILD_INTERFACE:${IXWEBSOCKET_INCLUDE_DIRS}/>
 | 
			
		||||
  $<INSTALL_INTERFACE:include/ixwebsocket>
 | 
			
		||||
)
 | 
			
		||||
target_include_directories(ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS})
 | 
			
		||||
 | 
			
		||||
set_target_properties(ixwebsocket PROPERTIES PUBLIC_HEADER "${IXWEBSOCKET_HEADERS}")
 | 
			
		||||
 | 
			
		||||
install(TARGETS ixwebsocket
 | 
			
		||||
        EXPORT ixwebsocket
 | 
			
		||||
        ARCHIVE DESTINATION lib
 | 
			
		||||
        PUBLIC_HEADER DESTINATION include/ixwebsocket/
 | 
			
		||||
        ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
 | 
			
		||||
        PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/include/ixwebsocket/
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
install(EXPORT ixwebsocket
 | 
			
		||||
        FILE ixwebsocket-config.cmake
 | 
			
		||||
        NAMESPACE ixwebsocket::
 | 
			
		||||
        DESTINATION lib/cmake/ixwebsocket)
 | 
			
		||||
 | 
			
		||||
if (USE_WS OR USE_TEST)
 | 
			
		||||
  add_subdirectory(ixcore)
 | 
			
		||||
  add_subdirectory(ixcrypto)
 | 
			
		||||
  add_subdirectory(ixcobra)
 | 
			
		||||
  add_subdirectory(ixredis)
 | 
			
		||||
  add_subdirectory(ixsnake)
 | 
			
		||||
  add_subdirectory(ixsentry)
 | 
			
		||||
  add_subdirectory(ixbots)
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
docker/Dockerfile.alpine
 | 
			
		||||
docker/Dockerfile.centos7_httpd
 | 
			
		||||
@@ -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)
 | 
			
		||||
- [Tokio](https://gitlab.com/HCInk/tokio), a discord library focused on audio playback with node bindings.
 | 
			
		||||
- [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), an easy to use Discord-bot framework.
 | 
			
		||||
- [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), a work in progress discord library
 | 
			
		||||
- [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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
FROM alpine:3.12 as build
 | 
			
		||||
FROM alpine:3.11 as build
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache \
 | 
			
		||||
    gcc g++ musl-dev linux-headers \
 | 
			
		||||
    cmake mbedtls-dev make zlib-dev python3-dev ninja
 | 
			
		||||
    cmake mbedtls-dev make zlib-dev ninja
 | 
			
		||||
 | 
			
		||||
RUN addgroup -S app && \
 | 
			
		||||
    adduser -S -G app app && \
 | 
			
		||||
@@ -18,9 +18,9 @@ USER app
 | 
			
		||||
RUN make ws_mbedtls_install && \
 | 
			
		||||
    sh tools/trim_repo_for_docker.sh
 | 
			
		||||
 | 
			
		||||
FROM alpine:3.12 as runtime
 | 
			
		||||
FROM alpine:3.11 as runtime
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache libstdc++ mbedtls ca-certificates python3 && \
 | 
			
		||||
RUN apk add --no-cache libstdc++ mbedtls ca-certificates && \
 | 
			
		||||
    addgroup -S app && \
 | 
			
		||||
    adduser -S -G app app
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								docker/Dockerfile.centos7_httpd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								docker/Dockerfile.centos7_httpd
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
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
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
# 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,150 +1,6 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
All changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## [10.1.1] - 2020-07-29
 | 
			
		||||
 | 
			
		||||
(websocket client) onProgressCallback not called for short messages on a websocket (fix #233)
 | 
			
		||||
 | 
			
		||||
## [10.1.0] - 2020-07-29
 | 
			
		||||
 | 
			
		||||
(websocket client) heartbeat is not sent at the requested frequency (fix #232)
 | 
			
		||||
 | 
			
		||||
## [10.0.3] - 2020-07-28
 | 
			
		||||
 | 
			
		||||
compiler warning fixes
 | 
			
		||||
 | 
			
		||||
## [10.0.2] - 2020-07-28
 | 
			
		||||
 | 
			
		||||
(ixcobra) CobraConnection: unsubscribe from all subscriptions when disconnecting
 | 
			
		||||
 | 
			
		||||
## [10.0.1] - 2020-07-27
 | 
			
		||||
 | 
			
		||||
(socket utility) move ix::getFreePort to ixwebsocket library
 | 
			
		||||
 | 
			
		||||
## [10.0.0] - 2020-07-25
 | 
			
		||||
 | 
			
		||||
(ixwebsocket server) change legacy api with 2 nested callbacks, so that the first api takes a weak_ptr<WebSocket> as its first argument
 | 
			
		||||
 | 
			
		||||
## [9.10.7] - 2020-07-25
 | 
			
		||||
 | 
			
		||||
(ixwebsocket) add WebSocketProxyServer, from ws. Still need to make the interface better.
 | 
			
		||||
 | 
			
		||||
## [9.10.6] - 2020-07-24
 | 
			
		||||
 | 
			
		||||
(ws) port broadcast_server sub-command to the new server API
 | 
			
		||||
 | 
			
		||||
## [9.10.5] - 2020-07-24
 | 
			
		||||
 | 
			
		||||
(unittest) port most unittests to the new server API
 | 
			
		||||
 | 
			
		||||
## [9.10.3] - 2020-07-24
 | 
			
		||||
 | 
			
		||||
(ws) port ws transfer to the new server API
 | 
			
		||||
 | 
			
		||||
## [9.10.2] - 2020-07-24
 | 
			
		||||
 | 
			
		||||
(websocket client) reset WebSocketTransport onClose callback in the WebSocket destructor
 | 
			
		||||
 | 
			
		||||
## [9.10.1] - 2020-07-24
 | 
			
		||||
 | 
			
		||||
(websocket server) reset client websocket callback when the connection is closed
 | 
			
		||||
 | 
			
		||||
## [9.10.0] - 2020-07-23
 | 
			
		||||
 | 
			
		||||
(websocket server) add a new simpler API to handle client connections / that API does not trigger a memory leak while the previous one did
 | 
			
		||||
 | 
			
		||||
## [9.9.3] - 2020-07-17
 | 
			
		||||
 | 
			
		||||
(build) merge platform specific files which were used to have different implementations for setting a thread name into a single file, to make it easier to include every source files and build the ixwebsocket library (fix #226)
 | 
			
		||||
 | 
			
		||||
## [9.9.2] - 2020-07-10
 | 
			
		||||
 | 
			
		||||
(socket server) bump default max connection count from 32 to 128
 | 
			
		||||
 | 
			
		||||
## [9.9.1] - 2020-07-10
 | 
			
		||||
 | 
			
		||||
(snake) implement super simple stream sql expression support in snake server
 | 
			
		||||
 | 
			
		||||
## [9.9.0] - 2020-07-08
 | 
			
		||||
 | 
			
		||||
(socket+websocket+http+redis+snake servers) expose the remote ip and remote port when a new connection is made
 | 
			
		||||
 | 
			
		||||
## [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
 | 
			
		||||
 | 
			
		||||
(http server) support gzip compression
 | 
			
		||||
 
 | 
			
		||||
@@ -22,9 +22,8 @@ Options for building:
 | 
			
		||||
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support
 | 
			
		||||
* `-DUSE_WS=1` will build the ws interactive command line tool
 | 
			
		||||
* `-DUSE_TEST=1` will build the unittest
 | 
			
		||||
* `-DUSE_PYTHON=1` will use Python3 for cobra bots, require Python3 to be installed.
 | 
			
		||||
 | 
			
		||||
If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file (not maintained much though) or rather the [github actions](https://github.com/machinezone/IXWebSocket/blob/master/.github/workflows/unittest_windows.yml) which have instructions for building dependencies.
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
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,10 +246,6 @@ uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries();
 | 
			
		||||
 | 
			
		||||
## WebSocket server API
 | 
			
		||||
 | 
			
		||||
### Legacy api
 | 
			
		||||
 | 
			
		||||
This api was actually changed to take a weak_ptr<WebSocket> as the first argument to setOnConnectionCallback ; previously it would take a shared_ptr<WebSocket> which was creating cycles and then memory leaks problems.
 | 
			
		||||
 | 
			
		||||
```cpp
 | 
			
		||||
#include <ixwebsocket/IXWebSocketServer.h>
 | 
			
		||||
 | 
			
		||||
@@ -260,50 +256,39 @@ This api was actually changed to take a weak_ptr<WebSocket> as the first argumen
 | 
			
		||||
ix::WebSocketServer server(port);
 | 
			
		||||
 | 
			
		||||
server.setOnConnectionCallback(
 | 
			
		||||
    [&server](std::weak_ptr<WebSocket> webSocket,
 | 
			
		||||
              std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
              std::unique_ptr<ConnectionInfo> connectionInfo)
 | 
			
		||||
    [&server](std::shared_ptr<WebSocket> webSocket,
 | 
			
		||||
              std::shared_ptr<ConnectionState> connectionState)
 | 
			
		||||
    {
 | 
			
		||||
        std::cout << "Remote ip: " << connectionInfo->remoteIp << std::endl;
 | 
			
		||||
 | 
			
		||||
        auto ws = webSocket.lock();
 | 
			
		||||
        if (ws)
 | 
			
		||||
        {
 | 
			
		||||
            ws->setOnMessageCallback(
 | 
			
		||||
                [webSocket, connectionState, &server](const ix::WebSocketMessagePtr msg)
 | 
			
		||||
        webSocket->setOnMessageCallback(
 | 
			
		||||
            [webSocket, connectionState, &server](const ix::WebSocketMessagePtr msg)
 | 
			
		||||
            {
 | 
			
		||||
                if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                {
 | 
			
		||||
                    if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                    std::cerr << "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::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::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);
 | 
			
		||||
                        }
 | 
			
		||||
                        std::cerr << 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);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@@ -324,74 +309,6 @@ server.wait();
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### New api
 | 
			
		||||
 | 
			
		||||
The new API does not require to use 2 nested callbacks, which is a bit annoying. The real fix is that there was a memory leak due to a shared_ptr cycle, due to passing down a shared_ptr<WebSocket> down to the callbacks.
 | 
			
		||||
 | 
			
		||||
The webSocket reference is guaranteed to be always valid ; by design the callback will never be invoked with a null webSocket object.
 | 
			
		||||
 | 
			
		||||
```cpp
 | 
			
		||||
#include <ixwebsocket/IXWebSocketServer.h>
 | 
			
		||||
 | 
			
		||||
...
 | 
			
		||||
 | 
			
		||||
// Run a server on localhost at a given port.
 | 
			
		||||
// Bound host name, max connections and listen backlog can also be passed in as parameters.
 | 
			
		||||
ix::WebSocketServer server(port);
 | 
			
		||||
 | 
			
		||||
server.setOnClientMessageCallback(std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                                  ConnectionInfo& connectionInfo,
 | 
			
		||||
                                  WebSocket& webSocket,
 | 
			
		||||
                                  const WebSocketMessagePtr& msg)
 | 
			
		||||
{
 | 
			
		||||
    // The ConnectionInfo object contains information about the connection,
 | 
			
		||||
    // at this point only the client ip address and the port.
 | 
			
		||||
    std::cout << "Remote ip: " << connectionInfo.remoteIp << std::endl;
 | 
			
		||||
 | 
			
		||||
    if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
    {
 | 
			
		||||
        std::cout << "New connection" << std::endl;
 | 
			
		||||
 | 
			
		||||
        // A connection state object is available, and has a default id
 | 
			
		||||
        // You can subclass ConnectionState and pass an alternate factory
 | 
			
		||||
        // to override it. It is useful if you want to store custom
 | 
			
		||||
        // attributes per connection (authenticated bool flag, attributes, etc...)
 | 
			
		||||
        std::cout << "id: " << connectionState->getId() << std::endl;
 | 
			
		||||
 | 
			
		||||
        // The uri the client did connect to.
 | 
			
		||||
        std::cout << "Uri: " << msg->openInfo.uri << std::endl;
 | 
			
		||||
 | 
			
		||||
        std::cout << "Headers:" << std::endl;
 | 
			
		||||
        for (auto it : msg->openInfo.headers)
 | 
			
		||||
        {
 | 
			
		||||
            std::cout << it.first << ": " << it.second << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
    {
 | 
			
		||||
        // For an echo server, we just send back to the client whatever was received by the server
 | 
			
		||||
        // All connected clients are available in an std::set. See the broadcast cpp example.
 | 
			
		||||
        // Second parameter tells whether we are sending the message in binary or text mode.
 | 
			
		||||
        // Here we send it in the same mode as it was received.
 | 
			
		||||
        webSocket.send(msg->str, msg->binary);
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
auto res = server.listen();
 | 
			
		||||
if (!res.first)
 | 
			
		||||
{
 | 
			
		||||
    // Error handling
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Run the server in the background. Server can be stoped by calling server.stop()
 | 
			
		||||
server.start();
 | 
			
		||||
 | 
			
		||||
// Block until server.stop() is called.
 | 
			
		||||
server.wait();
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## HTTP client API
 | 
			
		||||
 | 
			
		||||
```cpp
 | 
			
		||||
@@ -500,14 +417,11 @@ If you want to handle how requests are processed, implement the setOnConnectionC
 | 
			
		||||
```cpp
 | 
			
		||||
setOnConnectionCallback(
 | 
			
		||||
    [this](HttpRequestPtr request,
 | 
			
		||||
           std::shared_ptr<ConnectionState> /*connectionState*/,
 | 
			
		||||
           std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr
 | 
			
		||||
           std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr
 | 
			
		||||
    {
 | 
			
		||||
        // Build a string for the response
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << connectionInfo->remoteIp
 | 
			
		||||
           << " "
 | 
			
		||||
           << request->method
 | 
			
		||||
        ss << request->method
 | 
			
		||||
           << " "
 | 
			
		||||
           << request->uri;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 *  Buid with make httpd
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <ixwebsocket/IXHttpServer.h>
 | 
			
		||||
#include "IXHttpServer.h"
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,6 @@ set (IXBOTS_SOURCES
 | 
			
		||||
    ixbots/IXCobraToSentryBot.cpp
 | 
			
		||||
    ixbots/IXCobraToStatsdBot.cpp
 | 
			
		||||
    ixbots/IXCobraToStdoutBot.cpp
 | 
			
		||||
    ixbots/IXCobraMetricsToRedisBot.cpp
 | 
			
		||||
    ixbots/IXCobraToPythonBot.cpp
 | 
			
		||||
    ixbots/IXStatsdClient.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -19,8 +17,6 @@ set (IXBOTS_HEADERS
 | 
			
		||||
    ixbots/IXCobraToSentryBot.h
 | 
			
		||||
    ixbots/IXCobraToStatsdBot.h
 | 
			
		||||
    ixbots/IXCobraToStdoutBot.h
 | 
			
		||||
    ixbots/IXCobraMetricsToRedisBot.h
 | 
			
		||||
    ixbots/IXCobraToPythonBot.h
 | 
			
		||||
    ixbots/IXStatsdClient.h
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -34,24 +30,14 @@ if (NOT JSONCPP_FOUND)
 | 
			
		||||
  set(JSONCPP_INCLUDE_DIRS ../third_party/jsoncpp)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (USE_PYTHON)
 | 
			
		||||
  target_compile_definitions(ixbots PUBLIC IXBOTS_USE_PYTHON)
 | 
			
		||||
  find_package(Python COMPONENTS Development)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
set(IXBOTS_INCLUDE_DIRS
 | 
			
		||||
    .
 | 
			
		||||
    ..
 | 
			
		||||
    ../ixcore
 | 
			
		||||
    ../ixwebsocket
 | 
			
		||||
    ../ixcobra
 | 
			
		||||
    ../ixredis
 | 
			
		||||
    ../ixsentry
 | 
			
		||||
    ${JSONCPP_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} )
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@
 | 
			
		||||
 | 
			
		||||
#include <ixcobra/IXCobraConnection.h>
 | 
			
		||||
#include <ixcore/utils/IXCoreLogger.h>
 | 
			
		||||
#include <ixwebsocket/IXSetThreadName.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
@@ -29,7 +28,6 @@ namespace ix
 | 
			
		||||
        auto runtime = botConfig.runtime;
 | 
			
		||||
        auto maxEventsPerMinute = botConfig.maxEventsPerMinute;
 | 
			
		||||
        auto limitReceivedEvents = botConfig.limitReceivedEvents;
 | 
			
		||||
        auto batchSize = botConfig.batchSize;
 | 
			
		||||
 | 
			
		||||
        ix::CobraConnection conn;
 | 
			
		||||
        conn.configure(config);
 | 
			
		||||
@@ -45,7 +43,6 @@ namespace ix
 | 
			
		||||
        std::atomic<bool> stop(false);
 | 
			
		||||
        std::atomic<bool> throttled(false);
 | 
			
		||||
        std::atomic<bool> fatalCobraError(false);
 | 
			
		||||
        std::atomic<bool> stalledConnection(false);
 | 
			
		||||
        int minuteCounter = 0;
 | 
			
		||||
 | 
			
		||||
        auto timer = [&sentCount,
 | 
			
		||||
@@ -56,9 +53,7 @@ namespace ix
 | 
			
		||||
                      &receivedCountPerSecs,
 | 
			
		||||
                      &receivedCountPerMinutes,
 | 
			
		||||
                      &minuteCounter,
 | 
			
		||||
                      &conn,
 | 
			
		||||
                      &stop] {
 | 
			
		||||
            setThreadName("Bot progress");
 | 
			
		||||
            while (!stop)
 | 
			
		||||
            {
 | 
			
		||||
                //
 | 
			
		||||
@@ -75,11 +70,7 @@ namespace ix
 | 
			
		||||
                   << sentCountPerSecs
 | 
			
		||||
                   << " "
 | 
			
		||||
                   << sentCountTotal;
 | 
			
		||||
 | 
			
		||||
                if (conn.isAuthenticated())
 | 
			
		||||
                {
 | 
			
		||||
                    CoreLogger::info(ss.str());
 | 
			
		||||
                }
 | 
			
		||||
                CoreLogger::info(ss.str());
 | 
			
		||||
 | 
			
		||||
                receivedCountPerSecs = receivedCount - receivedCountTotal;
 | 
			
		||||
                sentCountPerSecs = sentCount - sentCountTotal;
 | 
			
		||||
@@ -102,14 +93,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        std::thread t1(timer);
 | 
			
		||||
 | 
			
		||||
        auto heartbeat = [&sentCount,
 | 
			
		||||
                          &receivedCount,
 | 
			
		||||
                          &stop,
 | 
			
		||||
                          &enableHeartbeat,
 | 
			
		||||
                          &heartBeatTimeout,
 | 
			
		||||
                          &stalledConnection]
 | 
			
		||||
        {
 | 
			
		||||
            setThreadName("Bot heartbeat");
 | 
			
		||||
        auto heartbeat = [&sentCount, &receivedCount, &stop, &enableHeartbeat, &heartBeatTimeout, &fatalCobraError] {
 | 
			
		||||
            std::string state("na");
 | 
			
		||||
 | 
			
		||||
            if (!enableHeartbeat) return;
 | 
			
		||||
@@ -124,12 +108,9 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
                if (currentState == state)
 | 
			
		||||
                {
 | 
			
		||||
                    ss.str("");
 | 
			
		||||
                    ss << "no messages received or sent for "
 | 
			
		||||
                       << heartBeatTimeout << " seconds, reconnecting";
 | 
			
		||||
 | 
			
		||||
                    CoreLogger::error(ss.str());
 | 
			
		||||
                    stalledConnection = true;
 | 
			
		||||
                    CoreLogger::error("no messages received or sent for 1 minute, exiting");
 | 
			
		||||
                    fatalCobraError = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                state = currentState;
 | 
			
		||||
 | 
			
		||||
@@ -154,7 +135,6 @@ namespace ix
 | 
			
		||||
                               &receivedCountPerMinutes,
 | 
			
		||||
                               maxEventsPerMinute,
 | 
			
		||||
                               limitReceivedEvents,
 | 
			
		||||
                               batchSize,
 | 
			
		||||
                               &fatalCobraError,
 | 
			
		||||
                               &sentCount](const CobraEventPtr& event) {
 | 
			
		||||
            if (event->type == ix::CobraEventType::Open)
 | 
			
		||||
@@ -163,7 +143,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
                for (auto&& it : event->headers)
 | 
			
		||||
                {
 | 
			
		||||
                    CoreLogger::info(it.first + ": " + it.second);
 | 
			
		||||
                    CoreLogger::info(it.first + "::" + it.second);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (event->type == ix::CobraEventType::Closed)
 | 
			
		||||
@@ -176,7 +156,7 @@ namespace ix
 | 
			
		||||
                CoreLogger::info("Subscribing to " + channel);
 | 
			
		||||
                CoreLogger::info("Subscribing at position " + subscriptionPosition);
 | 
			
		||||
                CoreLogger::info("Subscribing with filter " + filter);
 | 
			
		||||
                conn.subscribe(channel, filter, subscriptionPosition, batchSize,
 | 
			
		||||
                conn.subscribe(channel, filter, subscriptionPosition,
 | 
			
		||||
                    [&sentCount, &receivedCountPerMinutes,
 | 
			
		||||
                     maxEventsPerMinute, limitReceivedEvents,
 | 
			
		||||
                     &throttled, &receivedCount,
 | 
			
		||||
@@ -251,13 +231,6 @@ namespace ix
 | 
			
		||||
                std::this_thread::sleep_for(duration);
 | 
			
		||||
 | 
			
		||||
                if (fatalCobraError) break;
 | 
			
		||||
 | 
			
		||||
                if (stalledConnection)
 | 
			
		||||
                {
 | 
			
		||||
                    conn.disconnect();
 | 
			
		||||
                    conn.connect();
 | 
			
		||||
                    stalledConnection = false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Run for a duration, used by unittesting now
 | 
			
		||||
@@ -269,13 +242,6 @@ namespace ix
 | 
			
		||||
                std::this_thread::sleep_for(duration);
 | 
			
		||||
 | 
			
		||||
                if (fatalCobraError) break;
 | 
			
		||||
 | 
			
		||||
                if (stalledConnection)
 | 
			
		||||
                {
 | 
			
		||||
                    conn.disconnect();
 | 
			
		||||
                    conn.connect();
 | 
			
		||||
                    stalledConnection = false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -299,22 +265,4 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        _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
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,6 @@ namespace ix
 | 
			
		||||
        int64_t run(const CobraBotConfig& botConfig);
 | 
			
		||||
        void setOnBotMessageCallback(const OnBotMessageCallback& callback);
 | 
			
		||||
 | 
			
		||||
        std::string getDeviceIdentifier(const Json::Value& msg);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        OnBotMessageCallback _onBotMessageCallback;
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,5 @@ namespace ix
 | 
			
		||||
        int runtime = -1;
 | 
			
		||||
        int maxEventsPerMinute = std::numeric_limits<int>::max();
 | 
			
		||||
        bool limitReceivedEvents = false;
 | 
			
		||||
        int batchSize = 1;
 | 
			
		||||
    };
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -1,149 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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
 | 
			
		||||
 | 
			
		||||
@@ -1,332 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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,17 +70,11 @@ namespace ix
 | 
			
		||||
                                                     std::atomic<bool>& fatalCobraError,
 | 
			
		||||
                                                     std::atomic<uint64_t>& sentCount) -> void {
 | 
			
		||||
                std::string id;
 | 
			
		||||
                size_t idx = 0;
 | 
			
		||||
                for (auto&& attr : tokens)
 | 
			
		||||
                {
 | 
			
		||||
                    id += ".";
 | 
			
		||||
                    auto val = extractAttr(attr, msg);
 | 
			
		||||
                    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())
 | 
			
		||||
 
 | 
			
		||||
@@ -39,28 +39,21 @@
 | 
			
		||||
 | 
			
		||||
#include "IXStatsdClient.h"
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <ixwebsocket/IXNetSystem.h>
 | 
			
		||||
#include <ixwebsocket/IXSetThreadName.h>
 | 
			
		||||
#include <ixcore/utils/IXCoreLogger.h>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    StatsdClient::StatsdClient(const std::string& host,
 | 
			
		||||
                               int port,
 | 
			
		||||
                               const std::string& prefix,
 | 
			
		||||
                               bool verbose)
 | 
			
		||||
    StatsdClient::StatsdClient(const std::string& host, int port, const std::string& prefix)
 | 
			
		||||
        : _host(host)
 | 
			
		||||
        , _port(port)
 | 
			
		||||
        , _prefix(prefix)
 | 
			
		||||
        , _stop(false)
 | 
			
		||||
        , _verbose(verbose)
 | 
			
		||||
    {
 | 
			
		||||
        _thread = std::thread([this] {
 | 
			
		||||
            setThreadName("Statsd");
 | 
			
		||||
 | 
			
		||||
            while (!_stop)
 | 
			
		||||
            {
 | 
			
		||||
                flushQueue();
 | 
			
		||||
@@ -122,15 +115,11 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        cleanup(key);
 | 
			
		||||
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << _prefix << "." << key << ":" << value << "|" << type;
 | 
			
		||||
        char buf[256];
 | 
			
		||||
        snprintf(
 | 
			
		||||
            buf, sizeof(buf), "%s%s:%zd|%s\n", _prefix.c_str(), key.c_str(), value, type.c_str());
 | 
			
		||||
 | 
			
		||||
        if (_verbose)
 | 
			
		||||
        {
 | 
			
		||||
            CoreLogger::info(ss.str());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        enqueue(ss.str() + "\n");
 | 
			
		||||
        enqueue(buf);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -148,13 +137,10 @@ namespace ix
 | 
			
		||||
        {
 | 
			
		||||
            auto message = _queue.front();
 | 
			
		||||
            auto ret = _socket.sendto(message);
 | 
			
		||||
            if (ret == -1)
 | 
			
		||||
            if (ret != 0)
 | 
			
		||||
            {
 | 
			
		||||
                CoreLogger::error(std::string("statsd error: ") + strerror(UdpSocket::getErrno()));
 | 
			
		||||
                std::cerr << "error: " << strerror(UdpSocket::getErrno()) << std::endl;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // we always dequeue regardless of the ability to send the message
 | 
			
		||||
            // so that we keep our queue size under control
 | 
			
		||||
            _queue.pop_front();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,7 @@ namespace ix
 | 
			
		||||
    public:
 | 
			
		||||
        StatsdClient(const std::string& host = "127.0.0.1",
 | 
			
		||||
                     int port = 8125,
 | 
			
		||||
                     const std::string& prefix = "",
 | 
			
		||||
                     bool verbose = false);
 | 
			
		||||
                     const std::string& prefix = "");
 | 
			
		||||
        ~StatsdClient();
 | 
			
		||||
 | 
			
		||||
        bool init(std::string& errMsg);
 | 
			
		||||
@@ -53,7 +52,6 @@ namespace ix
 | 
			
		||||
        std::mutex _mutex; // for the queue
 | 
			
		||||
 | 
			
		||||
        std::deque<std::string> _queue;
 | 
			
		||||
        bool _verbose;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
} // end namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -111,12 +111,6 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
    void CobraConnection::disconnect()
 | 
			
		||||
    {
 | 
			
		||||
        auto subscriptionIds = getSubscriptionsIds();
 | 
			
		||||
        for (auto&& subscriptionId : subscriptionIds)
 | 
			
		||||
        {
 | 
			
		||||
            unsubscribe(subscriptionId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _authenticated = false;
 | 
			
		||||
        _webSocket->stop();
 | 
			
		||||
    }
 | 
			
		||||
@@ -568,13 +562,11 @@ namespace ix
 | 
			
		||||
    void CobraConnection::subscribe(const std::string& channel,
 | 
			
		||||
                                    const std::string& filter,
 | 
			
		||||
                                    const std::string& position,
 | 
			
		||||
                                    int batchSize,
 | 
			
		||||
                                    SubscriptionCallback cb)
 | 
			
		||||
    {
 | 
			
		||||
        // Create and send a subscribe pdu
 | 
			
		||||
        Json::Value body;
 | 
			
		||||
        body["channel"] = channel;
 | 
			
		||||
        body["batch_size"] = batchSize;
 | 
			
		||||
 | 
			
		||||
        if (!filter.empty())
 | 
			
		||||
        {
 | 
			
		||||
@@ -620,18 +612,6 @@ namespace ix
 | 
			
		||||
        _webSocket->send(pdu.toStyledString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> CobraConnection::getSubscriptionsIds()
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<std::string> subscriptionIds;
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_cbsMutex);
 | 
			
		||||
 | 
			
		||||
        for (auto&& it : _cbs)
 | 
			
		||||
        {
 | 
			
		||||
            subscriptionIds.push_back(it.first);
 | 
			
		||||
        }
 | 
			
		||||
        return subscriptionIds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // Enqueue strategy drops old messages when we are at full capacity
 | 
			
		||||
    //
 | 
			
		||||
 
 | 
			
		||||
@@ -88,7 +88,6 @@ namespace ix
 | 
			
		||||
        void subscribe(const std::string& channel,
 | 
			
		||||
                       const std::string& filter = std::string(),
 | 
			
		||||
                       const std::string& position = std::string(),
 | 
			
		||||
                       int batchSize = 1,
 | 
			
		||||
                       SubscriptionCallback cb = nullptr);
 | 
			
		||||
 | 
			
		||||
        /// Unsubscribe from a channel
 | 
			
		||||
@@ -163,9 +162,6 @@ namespace ix
 | 
			
		||||
        /// Tells whether the internal queue is empty or not
 | 
			
		||||
        bool isQueueEmpty();
 | 
			
		||||
 | 
			
		||||
        /// Retrieve all subscriptions ids
 | 
			
		||||
        std::vector<std::string> getSubscriptionsIds();
 | 
			
		||||
 | 
			
		||||
        ///
 | 
			
		||||
        /// Member variables
 | 
			
		||||
        ///
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
# 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} )
 | 
			
		||||
@@ -7,14 +7,16 @@ set (IXSNAKE_SOURCES
 | 
			
		||||
    ixsnake/IXSnakeServer.cpp
 | 
			
		||||
    ixsnake/IXSnakeProtocol.cpp
 | 
			
		||||
    ixsnake/IXAppConfig.cpp
 | 
			
		||||
    ixsnake/IXStreamSql.cpp
 | 
			
		||||
    ixsnake/IXRedisClient.cpp
 | 
			
		||||
    ixsnake/IXRedisServer.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set (IXSNAKE_HEADERS
 | 
			
		||||
    ixsnake/IXSnakeServer.h
 | 
			
		||||
    ixsnake/IXSnakeProtocol.h
 | 
			
		||||
    ixsnake/IXAppConfig.h
 | 
			
		||||
    ixsnake/IXStreamSql.h
 | 
			
		||||
    ixsnake/IXRedisClient.h
 | 
			
		||||
    ixsnake/IXRedisServer.h
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_library(ixsnake STATIC
 | 
			
		||||
@@ -28,7 +30,6 @@ set(IXSNAKE_INCLUDE_DIRS
 | 
			
		||||
    ../ixcore
 | 
			
		||||
    ../ixcrypto
 | 
			
		||||
    ../ixwebsocket
 | 
			
		||||
    ../ixredis
 | 
			
		||||
    ../third_party)
 | 
			
		||||
 | 
			
		||||
target_include_directories( ixsnake PUBLIC ${IXSNAKE_INCLUDE_DIRS} )
 | 
			
		||||
 
 | 
			
		||||
@@ -26,12 +26,6 @@ namespace snake
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto roles = appConfig.apps[appkey]["roles"];
 | 
			
		||||
        if (roles.count(role) == 0)
 | 
			
		||||
        {
 | 
			
		||||
            std::cerr << "Missing role " << role << std::endl;
 | 
			
		||||
            return std::string();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto channel = roles[role]["secret"];
 | 
			
		||||
        return channel;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -33,9 +33,6 @@ namespace snake
 | 
			
		||||
        // Misc
 | 
			
		||||
        bool verbose;
 | 
			
		||||
        bool disablePong;
 | 
			
		||||
 | 
			
		||||
        // If non empty, every published message gets republished to a given channel
 | 
			
		||||
        std::string republishChannel;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    bool isAppKeyValid(const AppConfig& appConfig, std::string appkey);
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <ixwebsocket/IXSocket.h>
 | 
			
		||||
#include <ixwebsocket/IXSocketFactory.h>
 | 
			
		||||
#include <ixwebsocket/IXSocketTLSOptions.h>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
@@ -251,16 +250,12 @@ namespace ix
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string RedisClient::prepareXaddCommand(const std::string& stream,
 | 
			
		||||
                                                const std::string& message,
 | 
			
		||||
                                                int maxLen)
 | 
			
		||||
                                                const std::string& message)
 | 
			
		||||
    {
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << "*8\r\n";
 | 
			
		||||
        ss << "*5\r\n";
 | 
			
		||||
        ss << writeString("XADD");
 | 
			
		||||
        ss << writeString(stream);
 | 
			
		||||
        ss << writeString("MAXLEN");
 | 
			
		||||
        ss << writeString("~");
 | 
			
		||||
        ss << writeString(std::to_string(maxLen));
 | 
			
		||||
        ss << writeString("*");
 | 
			
		||||
        ss << writeString("field");
 | 
			
		||||
        ss << writeString(message);
 | 
			
		||||
@@ -270,7 +265,6 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
    std::string RedisClient::xadd(const std::string& stream,
 | 
			
		||||
                                  const std::string& message,
 | 
			
		||||
                                  int maxLen,
 | 
			
		||||
                                  std::string& errMsg)
 | 
			
		||||
    {
 | 
			
		||||
        errMsg.clear();
 | 
			
		||||
@@ -281,7 +275,7 @@ namespace ix
 | 
			
		||||
            return std::string();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::string command = prepareXaddCommand(stream, message, maxLen);
 | 
			
		||||
        std::string command = prepareXaddCommand(stream, message);
 | 
			
		||||
 | 
			
		||||
        bool sent = _socket->writeBytes(command, nullptr);
 | 
			
		||||
        if (!sent)
 | 
			
		||||
@@ -354,104 +348,4 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
@@ -8,20 +8,12 @@
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <ixwebsocket/IXSocket.h>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <ixwebsocket/IXSocket.h>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    enum class RespType : int
 | 
			
		||||
    {
 | 
			
		||||
        String = 0,
 | 
			
		||||
        Error = 1,
 | 
			
		||||
        Integer = 2,
 | 
			
		||||
        Unknown = 3
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class RedisClient
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
@@ -48,22 +40,13 @@ namespace ix
 | 
			
		||||
        // XADD
 | 
			
		||||
        std::string xadd(const std::string& channel,
 | 
			
		||||
                         const std::string& message,
 | 
			
		||||
                         int maxLen,
 | 
			
		||||
                         std::string& errMsg);
 | 
			
		||||
        std::string prepareXaddCommand(const std::string& stream,
 | 
			
		||||
                                       const std::string& message,
 | 
			
		||||
                                       int maxLen);
 | 
			
		||||
 | 
			
		||||
        std::string prepareXaddCommand(const std::string& stream, const std::string& message);
 | 
			
		||||
 | 
			
		||||
        std::string readXaddReply(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);
 | 
			
		||||
        bool sendCommand(const std::string& commands, int commandsCount, std::string& errMsg);
 | 
			
		||||
 | 
			
		||||
        void stop();
 | 
			
		||||
 | 
			
		||||
@@ -45,11 +45,8 @@ namespace ix
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RedisServer::handleConnection(std::unique_ptr<Socket> socket,
 | 
			
		||||
                                       std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                                       std::unique_ptr<ConnectionInfo> connectionInfo)
 | 
			
		||||
                                       std::shared_ptr<ConnectionState> connectionState)
 | 
			
		||||
    {
 | 
			
		||||
        logInfo("New connection from remote ip " + connectionInfo->remoteIp);
 | 
			
		||||
 | 
			
		||||
        _connectedClientsCount++;
 | 
			
		||||
 | 
			
		||||
        while (!_stopHandlingConnections)
 | 
			
		||||
@@ -115,7 +112,7 @@ namespace ix
 | 
			
		||||
            it.second.erase(socket.get());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (auto&& it : _subscribers)
 | 
			
		||||
        for (auto it : _subscribers)
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "Subscription id: " << it.first << " #subscribers: " << it.second.size();
 | 
			
		||||
@@ -6,8 +6,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <ixwebsocket/IXSocket.h>
 | 
			
		||||
#include <ixwebsocket/IXSocketServer.h>
 | 
			
		||||
#include "IXSocket.h"
 | 
			
		||||
#include "IXSocketServer.h"
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <memory>
 | 
			
		||||
@@ -44,8 +44,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        // Methods
 | 
			
		||||
        virtual void handleConnection(std::unique_ptr<Socket>,
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                                      std::unique_ptr<ConnectionInfo> connectionInfo) final;
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState) final;
 | 
			
		||||
        virtual size_t getConnectedClientsCount() final;
 | 
			
		||||
 | 
			
		||||
        bool startsWith(const std::string& str, const std::string& start);
 | 
			
		||||
@@ -6,22 +6,16 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <ixredis/IXRedisClient.h>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include "IXRedisClient.h"
 | 
			
		||||
#include <future>
 | 
			
		||||
#include <ixwebsocket/IXConnectionState.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include "IXStreamSql.h"
 | 
			
		||||
 | 
			
		||||
namespace snake
 | 
			
		||||
{
 | 
			
		||||
    class SnakeConnectionState : public ix::ConnectionState
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~SnakeConnectionState()
 | 
			
		||||
        {
 | 
			
		||||
            stopSubScriptionThread();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::string getNonce()
 | 
			
		||||
        {
 | 
			
		||||
            return _nonce;
 | 
			
		||||
@@ -36,7 +30,6 @@ namespace snake
 | 
			
		||||
        {
 | 
			
		||||
            return _appkey;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setAppkey(const std::string& appkey)
 | 
			
		||||
        {
 | 
			
		||||
            _appkey = appkey;
 | 
			
		||||
@@ -46,7 +39,6 @@ namespace snake
 | 
			
		||||
        {
 | 
			
		||||
            return _role;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setRole(const std::string& role)
 | 
			
		||||
        {
 | 
			
		||||
            _role = role;
 | 
			
		||||
@@ -57,24 +49,7 @@ namespace snake
 | 
			
		||||
            return _redisClient;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        std::future<void> fut;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        std::string _nonce;
 | 
			
		||||
 
 | 
			
		||||
@@ -18,22 +18,21 @@
 | 
			
		||||
namespace snake
 | 
			
		||||
{
 | 
			
		||||
    void handleError(const std::string& action,
 | 
			
		||||
                     ix::WebSocket& ws,
 | 
			
		||||
                     uint64_t pduId,
 | 
			
		||||
                     std::shared_ptr<ix::WebSocket> ws,
 | 
			
		||||
                     nlohmann::json pdu,
 | 
			
		||||
                     const std::string& errMsg)
 | 
			
		||||
    {
 | 
			
		||||
        std::string actionError(action);
 | 
			
		||||
        actionError += "/error";
 | 
			
		||||
 | 
			
		||||
        nlohmann::json response = {
 | 
			
		||||
            {"action", actionError}, {"id", pduId}, {"body", {{"reason", errMsg}}}};
 | 
			
		||||
        ws.sendText(response.dump());
 | 
			
		||||
            {"action", actionError}, {"id", pdu.value("id", 1)}, {"body", {{"reason", errMsg}}}};
 | 
			
		||||
        ws->sendText(response.dump());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void handleHandshake(std::shared_ptr<SnakeConnectionState> state,
 | 
			
		||||
                         ix::WebSocket& ws,
 | 
			
		||||
                         const nlohmann::json& pdu,
 | 
			
		||||
                         uint64_t pduId)
 | 
			
		||||
                         std::shared_ptr<ix::WebSocket> ws,
 | 
			
		||||
                         const nlohmann::json& pdu)
 | 
			
		||||
    {
 | 
			
		||||
        std::string role = pdu["body"]["data"]["role"];
 | 
			
		||||
 | 
			
		||||
@@ -42,7 +41,7 @@ namespace snake
 | 
			
		||||
 | 
			
		||||
        nlohmann::json response = {
 | 
			
		||||
            {"action", "auth/handshake/ok"},
 | 
			
		||||
            {"id", pduId},
 | 
			
		||||
            {"id", pdu.value("id", 1)},
 | 
			
		||||
            {"body",
 | 
			
		||||
             {
 | 
			
		||||
                 {"data", {{"nonce", state->getNonce()}, {"connection_id", state->getId()}}},
 | 
			
		||||
@@ -50,14 +49,13 @@ namespace snake
 | 
			
		||||
 | 
			
		||||
        auto serializedResponse = response.dump();
 | 
			
		||||
 | 
			
		||||
        ws.sendText(serializedResponse);
 | 
			
		||||
        ws->sendText(serializedResponse);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void handleAuth(std::shared_ptr<SnakeConnectionState> state,
 | 
			
		||||
                    ix::WebSocket& ws,
 | 
			
		||||
                    std::shared_ptr<ix::WebSocket> ws,
 | 
			
		||||
                    const AppConfig& appConfig,
 | 
			
		||||
                    const nlohmann::json& pdu,
 | 
			
		||||
                    uint64_t pduId)
 | 
			
		||||
                    const nlohmann::json& pdu)
 | 
			
		||||
    {
 | 
			
		||||
        auto secret = getRoleSecret(appConfig, state->appkey(), state->role());
 | 
			
		||||
 | 
			
		||||
@@ -65,9 +63,9 @@ namespace snake
 | 
			
		||||
        {
 | 
			
		||||
            nlohmann::json response = {
 | 
			
		||||
                {"action", "auth/authenticate/error"},
 | 
			
		||||
                {"id", pduId},
 | 
			
		||||
                {"id", pdu.value("id", 1)},
 | 
			
		||||
                {"body", {{"error", "authentication_failed"}, {"reason", "invalid secret"}}}};
 | 
			
		||||
            ws.sendText(response.dump());
 | 
			
		||||
            ws->sendText(response.dump());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -81,21 +79,19 @@ namespace snake
 | 
			
		||||
                {"action", "auth/authenticate/error"},
 | 
			
		||||
                {"id", pdu.value("id", 1)},
 | 
			
		||||
                {"body", {{"error", "authentication_failed"}, {"reason", "invalid hash"}}}};
 | 
			
		||||
            ws.sendText(response.dump());
 | 
			
		||||
            ws->sendText(response.dump());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        nlohmann::json response = {
 | 
			
		||||
            {"action", "auth/authenticate/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}};
 | 
			
		||||
 | 
			
		||||
        ws.sendText(response.dump());
 | 
			
		||||
        ws->sendText(response.dump());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void handlePublish(std::shared_ptr<SnakeConnectionState> state,
 | 
			
		||||
                       ix::WebSocket& ws,
 | 
			
		||||
                       const AppConfig& appConfig,
 | 
			
		||||
                       const nlohmann::json& pdu,
 | 
			
		||||
                       uint64_t pduId)
 | 
			
		||||
                       std::shared_ptr<ix::WebSocket> ws,
 | 
			
		||||
                       const nlohmann::json& pdu)
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<std::string> channels;
 | 
			
		||||
 | 
			
		||||
@@ -115,16 +111,10 @@ namespace snake
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "Missing channels or channel field in publish data";
 | 
			
		||||
            handleError("rtm/publish", ws, pduId, ss.str());
 | 
			
		||||
            handleError("rtm/publish", ws, pdu, ss.str());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // add an extra channel if the config has one specified
 | 
			
		||||
        if (!appConfig.republishChannel.empty())
 | 
			
		||||
        {
 | 
			
		||||
            channels.push_back(appConfig.republishChannel);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (auto&& channel : channels)
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
@@ -135,7 +125,7 @@ namespace snake
 | 
			
		||||
            {
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                ss << "Cannot publish to redis host " << errMsg;
 | 
			
		||||
                handleError("rtm/publish", ws, pduId, ss.str());
 | 
			
		||||
                handleError("rtm/publish", ws, pdu, ss.str());
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -143,27 +133,26 @@ namespace snake
 | 
			
		||||
        nlohmann::json response = {
 | 
			
		||||
            {"action", "rtm/publish/ok"}, {"id", pdu.value("id", 1)}, {"body", {}}};
 | 
			
		||||
 | 
			
		||||
        ws.sendText(response.dump());
 | 
			
		||||
        ws->sendText(response.dump());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // FIXME: this is not cancellable. We should be able to cancel the redis subscription
 | 
			
		||||
    //
 | 
			
		||||
    void handleSubscribe(std::shared_ptr<SnakeConnectionState> state,
 | 
			
		||||
                         ix::WebSocket& ws,
 | 
			
		||||
                         const AppConfig& appConfig,
 | 
			
		||||
                         const nlohmann::json& pdu,
 | 
			
		||||
                         uint64_t pduId)
 | 
			
		||||
    void handleRedisSubscription(std::shared_ptr<SnakeConnectionState> state,
 | 
			
		||||
                                 std::shared_ptr<ix::WebSocket> ws,
 | 
			
		||||
                                 const AppConfig& appConfig,
 | 
			
		||||
                                 const nlohmann::json& pdu)
 | 
			
		||||
    {
 | 
			
		||||
        std::string channel = pdu["body"]["channel"];
 | 
			
		||||
        state->subscriptionId = channel;
 | 
			
		||||
        std::string subscriptionId = channel;
 | 
			
		||||
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << state->appkey() << "::" << channel;
 | 
			
		||||
 | 
			
		||||
        state->appChannel = ss.str();
 | 
			
		||||
        std::string appChannel(ss.str());
 | 
			
		||||
 | 
			
		||||
        ix::RedisClient& redisClient = state->subscriptionRedisClient;
 | 
			
		||||
        ix::RedisClient redisClient;
 | 
			
		||||
        int port = appConfig.redisPort;
 | 
			
		||||
 | 
			
		||||
        auto urls = appConfig.redisHosts;
 | 
			
		||||
@@ -174,7 +163,7 @@ namespace snake
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "Cannot connect to redis host " << hostname << ":" << port;
 | 
			
		||||
            handleError("rtm/subscribe", ws, pduId, ss.str());
 | 
			
		||||
            handleError("rtm/subscribe", ws, pdu, ss.str());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -186,90 +175,80 @@ namespace snake
 | 
			
		||||
            {
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                ss << "Cannot authenticated to redis";
 | 
			
		||||
                handleError("rtm/subscribe", ws, pduId, ss.str());
 | 
			
		||||
                handleError("rtm/subscribe", ws, pdu, ss.str());
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::string filterStr;
 | 
			
		||||
        if (pdu["body"].find("filter") != pdu["body"].end())
 | 
			
		||||
        {
 | 
			
		||||
            std::string filterStr = pdu["body"]["filter"];
 | 
			
		||||
        }
 | 
			
		||||
        state->streamSql = std::make_unique<StreamSql>(filterStr);
 | 
			
		||||
        state->id = 0;
 | 
			
		||||
        state->onRedisSubscribeCallback = [&ws, state](const std::string& messageStr) {
 | 
			
		||||
        int id = 0;
 | 
			
		||||
        auto callback = [ws, &id, &subscriptionId](const std::string& messageStr) {
 | 
			
		||||
            auto msg = nlohmann::json::parse(messageStr);
 | 
			
		||||
 | 
			
		||||
            msg = msg["body"]["message"];
 | 
			
		||||
 | 
			
		||||
            if (state->streamSql->valid() && !state->streamSql->match(msg))
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            nlohmann::json response = {
 | 
			
		||||
                {"action", "rtm/subscription/data"},
 | 
			
		||||
                {"id", state->id++},
 | 
			
		||||
                {"id", id++},
 | 
			
		||||
                {"body",
 | 
			
		||||
                 {{"subscription_id", state->subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}};
 | 
			
		||||
                 {{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}};
 | 
			
		||||
 | 
			
		||||
            ws.sendText(response.dump());
 | 
			
		||||
            ws->sendText(response.dump());
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        state->onRedisSubscribeResponseCallback = [&ws, state, pduId](const std::string& redisResponse) {
 | 
			
		||||
        auto responseCallback = [ws, pdu, &subscriptionId](const std::string& redisResponse) {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "Redis Response: " << redisResponse << "...";
 | 
			
		||||
            ix::CoreLogger::log(ss.str().c_str());
 | 
			
		||||
 | 
			
		||||
            // Success
 | 
			
		||||
            nlohmann::json response = {{"action", "rtm/subscribe/ok"},
 | 
			
		||||
                                       {"id", pduId},
 | 
			
		||||
                                       {"body", {{"subscription_id", state->subscriptionId}}}};
 | 
			
		||||
            ws.sendText(response.dump());
 | 
			
		||||
                                       {"id", pdu.value("id", 1)},
 | 
			
		||||
                                       {"body", {{"subscription_id", subscriptionId}}}};
 | 
			
		||||
            ws->sendText(response.dump());
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "Subscribing to " << state->appChannel << "...";
 | 
			
		||||
            ss << "Subscribing to " << appChannel << "...";
 | 
			
		||||
            ix::CoreLogger::log(ss.str().c_str());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto subscription = [&redisClient, state, &ws, pduId]
 | 
			
		||||
        if (!redisClient.subscribe(appChannel, responseCallback, callback))
 | 
			
		||||
        {
 | 
			
		||||
            if (!redisClient.subscribe(state->appChannel, 
 | 
			
		||||
                                       state->onRedisSubscribeResponseCallback,
 | 
			
		||||
                                       state->onRedisSubscribeCallback))
 | 
			
		||||
            {
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                ss << "Error subscribing to channel " << state->appChannel;
 | 
			
		||||
                handleError("rtm/subscribe", ws, pduId, ss.str());
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "Error subscribing to channel " << appChannel;
 | 
			
		||||
            handleError("rtm/subscribe", ws, pdu, ss.str());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        state->subscriptionThread = std::thread(subscription);
 | 
			
		||||
    void handleSubscribe(std::shared_ptr<SnakeConnectionState> state,
 | 
			
		||||
                         std::shared_ptr<ix::WebSocket> ws,
 | 
			
		||||
                         const AppConfig& appConfig,
 | 
			
		||||
                         const nlohmann::json& pdu)
 | 
			
		||||
    {
 | 
			
		||||
        state->fut =
 | 
			
		||||
            std::async(std::launch::async, handleRedisSubscription, state, ws, appConfig, pdu);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void handleUnSubscribe(std::shared_ptr<SnakeConnectionState> state,
 | 
			
		||||
                           ix::WebSocket& ws,
 | 
			
		||||
                           const nlohmann::json& pdu,
 | 
			
		||||
                           uint64_t pduId)
 | 
			
		||||
                           std::shared_ptr<ix::WebSocket> ws,
 | 
			
		||||
                           const nlohmann::json& pdu)
 | 
			
		||||
    {
 | 
			
		||||
        // extract subscription_id
 | 
			
		||||
        auto body = pdu["body"];
 | 
			
		||||
        auto subscriptionId = body["subscription_id"];
 | 
			
		||||
 | 
			
		||||
        state->stopSubScriptionThread();
 | 
			
		||||
        state->redisClient().stop();
 | 
			
		||||
 | 
			
		||||
        nlohmann::json response = {{"action", "rtm/unsubscribe/ok"},
 | 
			
		||||
                                   {"id", pduId},
 | 
			
		||||
                                   {"id", pdu.value("id", 1)},
 | 
			
		||||
                                   {"body", {{"subscription_id", subscriptionId}}}};
 | 
			
		||||
        ws.sendText(response.dump());
 | 
			
		||||
        ws->sendText(response.dump());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void processCobraMessage(std::shared_ptr<SnakeConnectionState> state,
 | 
			
		||||
                             ix::WebSocket& ws,
 | 
			
		||||
                             std::shared_ptr<ix::WebSocket> ws,
 | 
			
		||||
                             const AppConfig& appConfig,
 | 
			
		||||
                             const std::string& str)
 | 
			
		||||
    {
 | 
			
		||||
@@ -284,32 +263,31 @@ namespace snake
 | 
			
		||||
            ss << "malformed json pdu: " << e.what() << " -> " << str << "";
 | 
			
		||||
 | 
			
		||||
            nlohmann::json response = {{"body", {{"error", "invalid_json"}, {"reason", ss.str()}}}};
 | 
			
		||||
            ws.sendText(response.dump());
 | 
			
		||||
            ws->sendText(response.dump());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto action = pdu["action"];
 | 
			
		||||
        uint64_t pduId = pdu.value("id", 1);
 | 
			
		||||
 | 
			
		||||
        if (action == "auth/handshake")
 | 
			
		||||
        {
 | 
			
		||||
            handleHandshake(state, ws, pdu, pduId);
 | 
			
		||||
            handleHandshake(state, ws, pdu);
 | 
			
		||||
        }
 | 
			
		||||
        else if (action == "auth/authenticate")
 | 
			
		||||
        {
 | 
			
		||||
            handleAuth(state, ws, appConfig, pdu, pduId);
 | 
			
		||||
            handleAuth(state, ws, appConfig, pdu);
 | 
			
		||||
        }
 | 
			
		||||
        else if (action == "rtm/publish")
 | 
			
		||||
        {
 | 
			
		||||
            handlePublish(state, ws, appConfig, pdu, pduId);
 | 
			
		||||
            handlePublish(state, ws, pdu);
 | 
			
		||||
        }
 | 
			
		||||
        else if (action == "rtm/subscribe")
 | 
			
		||||
        {
 | 
			
		||||
            handleSubscribe(state, ws, appConfig, pdu, pduId);
 | 
			
		||||
            handleSubscribe(state, ws, appConfig, pdu);
 | 
			
		||||
        }
 | 
			
		||||
        else if (action == "rtm/unsubscribe")
 | 
			
		||||
        {
 | 
			
		||||
            handleUnSubscribe(state, ws, pdu, pduId);
 | 
			
		||||
            handleUnSubscribe(state, ws, pdu);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ namespace snake
 | 
			
		||||
    struct AppConfig;
 | 
			
		||||
 | 
			
		||||
    void processCobraMessage(std::shared_ptr<SnakeConnectionState> state,
 | 
			
		||||
                             ix::WebSocket& ws,
 | 
			
		||||
                             std::shared_ptr<ix::WebSocket> ws,
 | 
			
		||||
                             const AppConfig& appConfig,
 | 
			
		||||
                             const std::string& str);
 | 
			
		||||
} // namespace snake
 | 
			
		||||
 
 | 
			
		||||
@@ -59,68 +59,65 @@ namespace snake
 | 
			
		||||
        };
 | 
			
		||||
        _server.setConnectionStateFactory(factory);
 | 
			
		||||
 | 
			
		||||
        _server.setOnClientMessageCallback(
 | 
			
		||||
            [this](std::shared_ptr<ix::ConnectionState> connectionState,
 | 
			
		||||
                   ix::ConnectionInfo& connectionInfo,
 | 
			
		||||
                   ix::WebSocket& webSocket,
 | 
			
		||||
                   const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
        _server.setOnConnectionCallback(
 | 
			
		||||
            [this](std::shared_ptr<ix::WebSocket> webSocket,
 | 
			
		||||
                   std::shared_ptr<ix::ConnectionState> connectionState) {
 | 
			
		||||
                auto state = std::dynamic_pointer_cast<SnakeConnectionState>(connectionState);
 | 
			
		||||
                auto remoteIp = connectionInfo.remoteIp;
 | 
			
		||||
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                ss << "[" << state->getId() << "] ";
 | 
			
		||||
                webSocket->setOnMessageCallback(
 | 
			
		||||
                    [this, webSocket, state](const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                        std::stringstream ss;
 | 
			
		||||
                        ix::LogLevel logLevel = ix::LogLevel::Debug;
 | 
			
		||||
                        if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                        {
 | 
			
		||||
                            ss << "New connection" << std::endl;
 | 
			
		||||
                            ss << "id: " << state->getId() << std::endl;
 | 
			
		||||
                            ss << "Uri: " << msg->openInfo.uri << std::endl;
 | 
			
		||||
                            ss << "Headers:" << std::endl;
 | 
			
		||||
                            for (auto it : msg->openInfo.headers)
 | 
			
		||||
                            {
 | 
			
		||||
                                ss << it.first << ": " << it.second << std::endl;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                ix::LogLevel logLevel = ix::LogLevel::Debug;
 | 
			
		||||
                if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                {
 | 
			
		||||
                    ss << "New connection" << std::endl;
 | 
			
		||||
                    ss << "remote ip: " << remoteIp << std::endl;
 | 
			
		||||
                    ss << "id: " << state->getId() << std::endl;
 | 
			
		||||
                    ss << "Uri: " << msg->openInfo.uri << std::endl;
 | 
			
		||||
                    ss << "Headers:" << std::endl;
 | 
			
		||||
                    for (auto it : msg->openInfo.headers)
 | 
			
		||||
                    {
 | 
			
		||||
                        ss << it.first << ": " << it.second << std::endl;
 | 
			
		||||
                    }
 | 
			
		||||
                            std::string appkey = parseAppKey(msg->openInfo.uri);
 | 
			
		||||
                            state->setAppkey(appkey);
 | 
			
		||||
 | 
			
		||||
                    std::string appkey = parseAppKey(msg->openInfo.uri);
 | 
			
		||||
                    state->setAppkey(appkey);
 | 
			
		||||
                            // Connect to redis first
 | 
			
		||||
                            if (!state->redisClient().connect(_appConfig.redisHosts[0],
 | 
			
		||||
                                                              _appConfig.redisPort))
 | 
			
		||||
                            {
 | 
			
		||||
                                ss << "Cannot connect to redis host" << std::endl;
 | 
			
		||||
                                logLevel = ix::LogLevel::Error;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (msg->type == ix::WebSocketMessageType::Close)
 | 
			
		||||
                        {
 | 
			
		||||
                            ss << "Closed connection"
 | 
			
		||||
                               << " code " << msg->closeInfo.code << " reason "
 | 
			
		||||
                               << msg->closeInfo.reason << std::endl;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (msg->type == ix::WebSocketMessageType::Error)
 | 
			
		||||
                        {
 | 
			
		||||
                            std::stringstream ss;
 | 
			
		||||
                            ss << "Connection error: " << msg->errorInfo.reason << std::endl;
 | 
			
		||||
                            ss << "#retries: " << msg->errorInfo.retries << std::endl;
 | 
			
		||||
                            ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
 | 
			
		||||
                            ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
 | 
			
		||||
                            logLevel = ix::LogLevel::Error;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (msg->type == ix::WebSocketMessageType::Fragment)
 | 
			
		||||
                        {
 | 
			
		||||
                            ss << "Received message fragment" << std::endl;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
                        {
 | 
			
		||||
                            ss << "Received " << msg->wireSize << " bytes" << std::endl;
 | 
			
		||||
                            processCobraMessage(state, webSocket, _appConfig, msg->str);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    // Connect to redis first
 | 
			
		||||
                    if (!state->redisClient().connect(_appConfig.redisHosts[0],
 | 
			
		||||
                                                      _appConfig.redisPort))
 | 
			
		||||
                    {
 | 
			
		||||
                        ss << "Cannot connect to redis host" << std::endl;
 | 
			
		||||
                        logLevel = ix::LogLevel::Error;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (msg->type == ix::WebSocketMessageType::Close)
 | 
			
		||||
                {
 | 
			
		||||
                    ss << "Closed connection"
 | 
			
		||||
                       << " code " << msg->closeInfo.code << " reason "
 | 
			
		||||
                       << msg->closeInfo.reason << std::endl;
 | 
			
		||||
                }
 | 
			
		||||
                else if (msg->type == ix::WebSocketMessageType::Error)
 | 
			
		||||
                {
 | 
			
		||||
                    std::stringstream ss;
 | 
			
		||||
                    ss << "Connection error: " << msg->errorInfo.reason << std::endl;
 | 
			
		||||
                    ss << "#retries: " << msg->errorInfo.retries << std::endl;
 | 
			
		||||
                    ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
 | 
			
		||||
                    ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
 | 
			
		||||
                    logLevel = ix::LogLevel::Error;
 | 
			
		||||
                }
 | 
			
		||||
                else if (msg->type == ix::WebSocketMessageType::Fragment)
 | 
			
		||||
                {
 | 
			
		||||
                    ss << "Received message fragment" << std::endl;
 | 
			
		||||
                }
 | 
			
		||||
                else if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
                {
 | 
			
		||||
                    ss << "Received " << msg->wireSize << " bytes" << " " << msg->str << std::endl;
 | 
			
		||||
                    processCobraMessage(state, webSocket, _appConfig, msg->str);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                ix::CoreLogger::log(ss.str().c_str(), logLevel);
 | 
			
		||||
        });
 | 
			
		||||
                        ix::CoreLogger::log(ss.str().c_str(), logLevel);
 | 
			
		||||
                    });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        auto res = _server.listen();
 | 
			
		||||
        if (!res.first)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,63 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -1,25 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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,10 +16,7 @@
 | 
			
		||||
#include <random>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
#include <zlib.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
@@ -130,7 +127,7 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        // We only have one socket connection, so we cannot
 | 
			
		||||
        // make multiple requests concurrently.
 | 
			
		||||
        std::lock_guard<std::recursive_mutex> lock(_mutex);
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_mutex);
 | 
			
		||||
 | 
			
		||||
        uint64_t uploadSize = 0;
 | 
			
		||||
        uint64_t downloadSize = 0;
 | 
			
		||||
@@ -177,13 +174,11 @@ namespace ix
 | 
			
		||||
        ss << verb << " " << path << " HTTP/1.1\r\n";
 | 
			
		||||
        ss << "Host: " << host << "\r\n";
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        if (args->compress)
 | 
			
		||||
        {
 | 
			
		||||
            ss << "Accept-Encoding: gzip"
 | 
			
		||||
               << "\r\n";
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        // Append extra headers
 | 
			
		||||
        for (auto&& it : args->extraHeaders)
 | 
			
		||||
@@ -500,7 +495,6 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        downloadSize = payload.size();
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        // If the content was compressed with gzip, decode it
 | 
			
		||||
        if (headers["Content-Encoding"] == "gzip")
 | 
			
		||||
        {
 | 
			
		||||
@@ -519,7 +513,6 @@ namespace ix
 | 
			
		||||
            }
 | 
			
		||||
            payload = decompressedPayload;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        return std::make_shared<HttpResponse>(code,
 | 
			
		||||
                                              description,
 | 
			
		||||
@@ -679,7 +672,6 @@ namespace ix
 | 
			
		||||
        return ss.str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
    bool HttpClient::gzipInflate(const std::string& in, std::string& out)
 | 
			
		||||
    {
 | 
			
		||||
        z_stream inflateState;
 | 
			
		||||
@@ -724,7 +716,6 @@ namespace ix
 | 
			
		||||
        inflateEnd(&inflateState);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -90,9 +90,7 @@ namespace ix
 | 
			
		||||
    private:
 | 
			
		||||
        void log(const std::string& msg, HttpRequestArgsPtr args);
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        bool gzipInflate(const std::string& in, std::string& out);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        // Async API background thread runner
 | 
			
		||||
        void run();
 | 
			
		||||
@@ -105,9 +103,7 @@ namespace ix
 | 
			
		||||
        std::thread _thread;
 | 
			
		||||
 | 
			
		||||
        std::unique_ptr<Socket> _socket;
 | 
			
		||||
        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
 | 
			
		||||
        std::mutex _mutex; // to protect accessing the _socket (only one socket per client)
 | 
			
		||||
 | 
			
		||||
        SocketTLSOptions _tlsOptions;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,14 +9,11 @@
 | 
			
		||||
#include "IXNetSystem.h"
 | 
			
		||||
#include "IXSocketConnect.h"
 | 
			
		||||
#include "IXUserAgent.h"
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
#include <zlib.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
@@ -44,7 +41,6 @@ namespace
 | 
			
		||||
        return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
    std::string gzipCompress(const std::string& str)
 | 
			
		||||
    {
 | 
			
		||||
        z_stream zs; // z_stream is zlib's control structure
 | 
			
		||||
@@ -54,11 +50,8 @@ namespace
 | 
			
		||||
        const int windowBits = 15;
 | 
			
		||||
        const int GZIP_ENCODING = 16;
 | 
			
		||||
 | 
			
		||||
        deflateInit2(&zs,
 | 
			
		||||
                     Z_DEFAULT_COMPRESSION,
 | 
			
		||||
                     Z_DEFLATED,
 | 
			
		||||
                     windowBits | GZIP_ENCODING,
 | 
			
		||||
                     8,
 | 
			
		||||
        deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
 | 
			
		||||
                     windowBits | GZIP_ENCODING, 8,
 | 
			
		||||
                     Z_DEFAULT_STRATEGY);
 | 
			
		||||
 | 
			
		||||
        zs.next_in = (Bytef*) str.data();
 | 
			
		||||
@@ -76,18 +69,18 @@ namespace
 | 
			
		||||
 | 
			
		||||
            ret = deflate(&zs, Z_FINISH);
 | 
			
		||||
 | 
			
		||||
            if (outstring.size() < zs.total_out)
 | 
			
		||||
            if(outstring.size() < zs.total_out)
 | 
			
		||||
            {
 | 
			
		||||
                // append the block to the output string
 | 
			
		||||
                outstring.append(outbuffer, zs.total_out - outstring.size());
 | 
			
		||||
                outstring.append(outbuffer,
 | 
			
		||||
                                 zs.total_out - outstring.size());
 | 
			
		||||
            }
 | 
			
		||||
        } while (ret == Z_OK);
 | 
			
		||||
        } while(ret == Z_OK);
 | 
			
		||||
 | 
			
		||||
        deflateEnd(&zs);
 | 
			
		||||
 | 
			
		||||
        return outstring;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
@@ -120,8 +113,7 @@ namespace ix
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void HttpServer::handleConnection(std::unique_ptr<Socket> socket,
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                                      std::unique_ptr<ConnectionInfo> connectionInfo)
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState)
 | 
			
		||||
    {
 | 
			
		||||
        _connectedClientsCount++;
 | 
			
		||||
 | 
			
		||||
@@ -130,8 +122,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        if (std::get<0>(ret))
 | 
			
		||||
        {
 | 
			
		||||
            auto response =
 | 
			
		||||
                _onConnectionCallback(std::get<2>(ret), connectionState, std::move(connectionInfo));
 | 
			
		||||
            auto response = _onConnectionCallback(std::get<2>(ret), connectionState);
 | 
			
		||||
            if (!Http::sendResponse(response, socket))
 | 
			
		||||
            {
 | 
			
		||||
                logError("Cannot send response");
 | 
			
		||||
@@ -151,8 +142,7 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        setOnConnectionCallback(
 | 
			
		||||
            [this](HttpRequestPtr request,
 | 
			
		||||
                   std::shared_ptr<ConnectionState> /*connectionState*/,
 | 
			
		||||
                   std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr {
 | 
			
		||||
                   std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr {
 | 
			
		||||
                std::string uri(request->uri);
 | 
			
		||||
                if (uri.empty() || uri == "/")
 | 
			
		||||
                {
 | 
			
		||||
@@ -173,19 +163,15 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
                std::string content = res.second;
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
                std::string acceptEncoding = request->headers["Accept-encoding"];
 | 
			
		||||
                if (acceptEncoding == "*" || acceptEncoding.find("gzip") != std::string::npos)
 | 
			
		||||
                if (acceptEncoding == "gzip" || acceptEncoding == "*")
 | 
			
		||||
                {
 | 
			
		||||
                    content = gzipCompress(content);
 | 
			
		||||
                    headers["Content-Encoding"] = "gzip";
 | 
			
		||||
                }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
                // Log request
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                ss << connectionInfo->remoteIp << ":" << connectionInfo->remotePort << " "
 | 
			
		||||
                   << request->method << " " << request->headers["User-Agent"] << " "
 | 
			
		||||
                ss << request->method << " " << request->headers["User-Agent"] << " "
 | 
			
		||||
                   << request->uri << " " << content.size();
 | 
			
		||||
                logInfo(ss.str());
 | 
			
		||||
 | 
			
		||||
@@ -209,16 +195,15 @@ namespace ix
 | 
			
		||||
        // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
 | 
			
		||||
        //
 | 
			
		||||
        setOnConnectionCallback(
 | 
			
		||||
            [this, redirectUrl](HttpRequestPtr request,
 | 
			
		||||
                                std::shared_ptr<ConnectionState> /*connectionState*/,
 | 
			
		||||
                                std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr {
 | 
			
		||||
            [this,
 | 
			
		||||
             redirectUrl](HttpRequestPtr request,
 | 
			
		||||
                          std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr {
 | 
			
		||||
                WebSocketHttpHeaders headers;
 | 
			
		||||
                headers["Server"] = userAgent();
 | 
			
		||||
 | 
			
		||||
                // Log request
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                ss << connectionInfo->remoteIp << ":" << connectionInfo->remotePort << " "
 | 
			
		||||
                   << request->method << " " << request->headers["User-Agent"] << " "
 | 
			
		||||
                ss << request->method << " " << request->headers["User-Agent"] << " "
 | 
			
		||||
                   << request->uri;
 | 
			
		||||
                logInfo(ss.str());
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,7 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        using OnConnectionCallback =
 | 
			
		||||
            std::function<HttpResponsePtr(HttpRequestPtr,
 | 
			
		||||
                                          std::shared_ptr<ConnectionState>,
 | 
			
		||||
                                          std::unique_ptr<ConnectionInfo> connectionInfo)>;
 | 
			
		||||
            std::function<HttpResponsePtr(HttpRequestPtr, std::shared_ptr<ConnectionState>)>;
 | 
			
		||||
 | 
			
		||||
        HttpServer(int port = SocketServer::kDefaultPort,
 | 
			
		||||
                   const std::string& host = SocketServer::kDefaultHost,
 | 
			
		||||
@@ -46,8 +44,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        // Methods
 | 
			
		||||
        virtual void handleConnection(std::unique_ptr<Socket>,
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                                      std::unique_ptr<ConnectionInfo> connectionInfo) final;
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState) final;
 | 
			
		||||
        virtual size_t getConnectedClientsCount() final;
 | 
			
		||||
 | 
			
		||||
        void setDefaultConnectionCallback();
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
 | 
			
		||||
#include "IXSelectInterruptFactory.h"
 | 
			
		||||
 | 
			
		||||
#include "IXUniquePtr.h"
 | 
			
		||||
#if defined(__linux__) || defined(__APPLE__)
 | 
			
		||||
#include "IXSelectInterruptPipe.h"
 | 
			
		||||
#else
 | 
			
		||||
@@ -17,9 +18,9 @@ namespace ix
 | 
			
		||||
    SelectInterruptPtr createSelectInterrupt()
 | 
			
		||||
    {
 | 
			
		||||
#if defined(__linux__) || defined(__APPLE__)
 | 
			
		||||
        return std::make_unique<SelectInterruptPipe>();
 | 
			
		||||
        return ix::make_unique<SelectInterruptPipe>();
 | 
			
		||||
#else
 | 
			
		||||
        return std::make_unique<SelectInterrupt>();
 | 
			
		||||
        return ix::make_unique<SelectInterrupt>();
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,8 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// 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.
 | 
			
		||||
// On macOS we use UNIX pipes to wake up select.
 | 
			
		||||
//
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
 | 
			
		||||
#include "IXSelectInterruptPipe.h"
 | 
			
		||||
 | 
			
		||||
@@ -146,5 +144,3 @@ namespace ix
 | 
			
		||||
        return _fildes[kPipeReadIndex];
 | 
			
		||||
    }
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 | 
			
		||||
#endif // !_WIN32
 | 
			
		||||
 
 | 
			
		||||
@@ -1,81 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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,6 +10,7 @@
 | 
			
		||||
#include "IXNetSystem.h"
 | 
			
		||||
#include "IXSelectInterrupt.h"
 | 
			
		||||
#include "IXSocket.h"
 | 
			
		||||
#include "IXUniquePtr.h"
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
@@ -65,7 +66,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
            int timeoutMs = 10;
 | 
			
		||||
            bool readyToRead = false;
 | 
			
		||||
            auto selectInterrupt = std::make_unique<SelectInterrupt>();
 | 
			
		||||
            auto selectInterrupt = ix::make_unique<SelectInterrupt>();
 | 
			
		||||
            PollResultType pollResult = Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt);
 | 
			
		||||
 | 
			
		||||
            if (pollResult == PollResultType::Timeout)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXSocketFactory.h"
 | 
			
		||||
#include "IXUniquePtr.h"
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_TLS
 | 
			
		||||
 | 
			
		||||
@@ -35,17 +36,17 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        if (!tls)
 | 
			
		||||
        {
 | 
			
		||||
            socket = std::make_unique<Socket>(fd);
 | 
			
		||||
            socket = ix::make_unique<Socket>(fd);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_TLS
 | 
			
		||||
#if defined(IXWEBSOCKET_USE_MBED_TLS)
 | 
			
		||||
            socket = std::make_unique<SocketMbedTLS>(tlsOptions, fd);
 | 
			
		||||
            socket = ix::make_unique<SocketMbedTLS>(tlsOptions, fd);
 | 
			
		||||
#elif defined(IXWEBSOCKET_USE_OPEN_SSL)
 | 
			
		||||
            socket = std::make_unique<SocketOpenSSL>(tlsOptions, fd);
 | 
			
		||||
            socket = ix::make_unique<SocketOpenSSL>(tlsOptions, fd);
 | 
			
		||||
#elif defined(__APPLE__)
 | 
			
		||||
            socket = std::make_unique<SocketAppleSSL>(tlsOptions, fd);
 | 
			
		||||
            socket = ix::make_unique<SocketAppleSSL>(tlsOptions, fd);
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
            errorMsg = "TLS support is not enabled on this platform.";
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@
 | 
			
		||||
#include "IXSocket.h"
 | 
			
		||||
#include "IXSocketConnect.h"
 | 
			
		||||
#include "IXSocketFactory.h"
 | 
			
		||||
#include "IXUniquePtr.h"
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
@@ -22,7 +23,7 @@ namespace ix
 | 
			
		||||
    const int SocketServer::kDefaultPort(8080);
 | 
			
		||||
    const std::string SocketServer::kDefaultHost("127.0.0.1");
 | 
			
		||||
    const int SocketServer::kDefaultTcpBacklog(5);
 | 
			
		||||
    const size_t SocketServer::kDefaultMaxConnections(128);
 | 
			
		||||
    const size_t SocketServer::kDefaultMaxConnections(32);
 | 
			
		||||
    const int SocketServer::kDefaultAddressFamily(AF_INET);
 | 
			
		||||
 | 
			
		||||
    SocketServer::SocketServer(
 | 
			
		||||
@@ -258,7 +259,7 @@ namespace ix
 | 
			
		||||
            // Use poll to check whether a new connection is in progress
 | 
			
		||||
            int timeoutMs = 10;
 | 
			
		||||
            bool readyToRead = true;
 | 
			
		||||
            auto selectInterrupt = std::make_unique<SelectInterrupt>();
 | 
			
		||||
            auto selectInterrupt = ix::make_unique<SelectInterrupt>();
 | 
			
		||||
            PollResultType pollResult =
 | 
			
		||||
                Socket::poll(readyToRead, timeoutMs, _serverFd, selectInterrupt);
 | 
			
		||||
 | 
			
		||||
@@ -276,7 +277,6 @@ namespace ix
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Accept a connection.
 | 
			
		||||
            // FIXME: Is this working for ipv6 ?
 | 
			
		||||
            struct sockaddr_in client; // client address information
 | 
			
		||||
            int clientFd;              // socket connected to client
 | 
			
		||||
            socklen_t addressLen = sizeof(client);
 | 
			
		||||
@@ -308,45 +308,6 @@ namespace ix
 | 
			
		||||
                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;
 | 
			
		||||
            if (_connectionStateFactory)
 | 
			
		||||
            {
 | 
			
		||||
@@ -379,13 +340,10 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
            // Launch the handleConnection work asynchronously in its own thread.
 | 
			
		||||
            std::lock_guard<std::mutex> lock(_connectionsThreadsMutex);
 | 
			
		||||
            _connectionsThreads.push_back(
 | 
			
		||||
                std::make_pair(connectionState,
 | 
			
		||||
                               std::thread(&SocketServer::handleConnection,
 | 
			
		||||
                                           this,
 | 
			
		||||
                                           std::move(socket),
 | 
			
		||||
                                           connectionState,
 | 
			
		||||
                                           std::move(connectionInfo))));
 | 
			
		||||
            _connectionsThreads.push_back(std::make_pair(
 | 
			
		||||
                connectionState,
 | 
			
		||||
                std::thread(
 | 
			
		||||
                    &SocketServer::handleConnection, this, std::move(socket), connectionState)));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "IXConnectionInfo.h"
 | 
			
		||||
#include "IXConnectionState.h"
 | 
			
		||||
#include "IXSocketTLSOptions.h"
 | 
			
		||||
#include <atomic>
 | 
			
		||||
@@ -103,8 +102,7 @@ namespace ix
 | 
			
		||||
        ConnectionStateFactory _connectionStateFactory;
 | 
			
		||||
 | 
			
		||||
        virtual void handleConnection(std::unique_ptr<Socket>,
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                                      std::unique_ptr<ConnectionInfo> connectionInfo) = 0;
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState) = 0;
 | 
			
		||||
        virtual size_t getConnectedClientsCount() = 0;
 | 
			
		||||
 | 
			
		||||
        // Returns true if all connection threads are joined
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								ixwebsocket/IXUniquePtr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								ixwebsocket/IXUniquePtr.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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 <algorithm>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,7 @@
 | 
			
		||||
 | 
			
		||||
#include "IXWebSocketVersion.h"
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
#include <zlib.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Platform name
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
@@ -79,10 +77,8 @@ namespace ix
 | 
			
		||||
        ss << " nossl";
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        // Zlib version
 | 
			
		||||
        ss << " zlib " << ZLIB_VERSION;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        return ss.str();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,6 @@ namespace ix
 | 
			
		||||
    WebSocket::~WebSocket()
 | 
			
		||||
    {
 | 
			
		||||
        stop();
 | 
			
		||||
        _ws.setOnCloseCallback(nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocket::setUrl(const std::string& url)
 | 
			
		||||
 
 | 
			
		||||
@@ -28,26 +28,21 @@ namespace ix
 | 
			
		||||
    WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor()
 | 
			
		||||
        : _compressBufferSize(kBufferSize)
 | 
			
		||||
    {
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        memset(&_deflateState, 0, sizeof(_deflateState));
 | 
			
		||||
 | 
			
		||||
        _deflateState.zalloc = Z_NULL;
 | 
			
		||||
        _deflateState.zfree = Z_NULL;
 | 
			
		||||
        _deflateState.opaque = Z_NULL;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketPerMessageDeflateCompressor::~WebSocketPerMessageDeflateCompressor()
 | 
			
		||||
    {
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        deflateEnd(&_deflateState);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
 | 
			
		||||
                                                    bool clientNoContextTakeOver)
 | 
			
		||||
    {
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        int ret = deflateInit2(&_deflateState,
 | 
			
		||||
                               Z_DEFAULT_COMPRESSION,
 | 
			
		||||
                               Z_DEFLATED,
 | 
			
		||||
@@ -62,49 +57,17 @@ namespace ix
 | 
			
		||||
        _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
#else
 | 
			
		||||
        return false;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<typename T>
 | 
			
		||||
    bool WebSocketPerMessageDeflateCompressor::endsWithEmptyUnCompressedBlock(const T& value)
 | 
			
		||||
    bool WebSocketPerMessageDeflateCompressor::endsWith(const std::string& value,
 | 
			
		||||
                                                        const std::string& ending)
 | 
			
		||||
    {
 | 
			
		||||
        if (kEmptyUncompressedBlock.size() > value.size()) return false;
 | 
			
		||||
        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];
 | 
			
		||||
        if (ending.size() > value.size()) return false;
 | 
			
		||||
        return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        //
 | 
			
		||||
@@ -133,8 +96,7 @@ namespace ix
 | 
			
		||||
            // The normal buffer size should be 6 but
 | 
			
		||||
            // we remove the 4 octets from the tail (#4)
 | 
			
		||||
            uint8_t buf[2] = {0x02, 0x00};
 | 
			
		||||
            out.push_back(buf[0]);
 | 
			
		||||
            out.push_back(buf[1]);
 | 
			
		||||
            out.append((char*) (buf), 2);
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
@@ -152,18 +114,15 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
            output = _compressBufferSize - _deflateState.avail_out;
 | 
			
		||||
 | 
			
		||||
            out.insert(out.end(), _compressBuffer.get(), _compressBuffer.get() + output);
 | 
			
		||||
            out.append((char*) (_compressBuffer.get()), output);
 | 
			
		||||
        } while (_deflateState.avail_out == 0);
 | 
			
		||||
 | 
			
		||||
        if (endsWithEmptyUnCompressedBlock(out))
 | 
			
		||||
        if (endsWith(out, kEmptyUncompressedBlock))
 | 
			
		||||
        {
 | 
			
		||||
            out.resize(out.size() - 4);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
#else
 | 
			
		||||
        return false;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
@@ -172,7 +131,6 @@ namespace ix
 | 
			
		||||
    WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor()
 | 
			
		||||
        : _compressBufferSize(kBufferSize)
 | 
			
		||||
    {
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        memset(&_inflateState, 0, sizeof(_inflateState));
 | 
			
		||||
 | 
			
		||||
        _inflateState.zalloc = Z_NULL;
 | 
			
		||||
@@ -180,20 +138,16 @@ namespace ix
 | 
			
		||||
        _inflateState.opaque = Z_NULL;
 | 
			
		||||
        _inflateState.avail_in = 0;
 | 
			
		||||
        _inflateState.next_in = Z_NULL;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketPerMessageDeflateDecompressor::~WebSocketPerMessageDeflateDecompressor()
 | 
			
		||||
    {
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        inflateEnd(&_inflateState);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
 | 
			
		||||
                                                      bool clientNoContextTakeOver)
 | 
			
		||||
    {
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        int ret = inflateInit2(&_inflateState, -1 * inflateBits);
 | 
			
		||||
 | 
			
		||||
        if (ret != Z_OK) return false;
 | 
			
		||||
@@ -203,14 +157,10 @@ namespace ix
 | 
			
		||||
        _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
#else
 | 
			
		||||
        return false;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in, std::string& out)
 | 
			
		||||
    {
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        //
 | 
			
		||||
        // 7.2.2.  Decompression
 | 
			
		||||
        //
 | 
			
		||||
@@ -247,8 +197,5 @@ namespace ix
 | 
			
		||||
        } while (_inflateState.avail_out == 0);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
#else
 | 
			
		||||
        return false;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,9 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
#include "zlib.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
@@ -23,23 +20,14 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        bool init(uint8_t deflateBits, bool clientNoContextTakeOver);
 | 
			
		||||
        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:
 | 
			
		||||
        template<typename T, typename S>
 | 
			
		||||
        bool compressData(const T& in, S& out);
 | 
			
		||||
        template<typename T>
 | 
			
		||||
        bool endsWithEmptyUnCompressedBlock(const T& value);
 | 
			
		||||
        static bool endsWith(const std::string& value, const std::string& ending);
 | 
			
		||||
 | 
			
		||||
        int _flush;
 | 
			
		||||
        size_t _compressBufferSize;
 | 
			
		||||
        std::unique_ptr<unsigned char[]> _compressBuffer;
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        z_stream _deflateState;
 | 
			
		||||
#endif
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class WebSocketPerMessageDeflateDecompressor
 | 
			
		||||
@@ -55,10 +43,7 @@ namespace ix
 | 
			
		||||
        int _flush;
 | 
			
		||||
        size_t _compressBufferSize;
 | 
			
		||||
        std::unique_ptr<unsigned char[]> _compressBuffer;
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        z_stream _inflateState;
 | 
			
		||||
#endif
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -61,7 +61,6 @@ namespace ix
 | 
			
		||||
        _clientMaxWindowBits = kDefaultClientMaxWindowBits;
 | 
			
		||||
        _serverMaxWindowBits = kDefaultServerMaxWindowBits;
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        // Split by ;
 | 
			
		||||
        std::string token;
 | 
			
		||||
        std::stringstream tokenStream(extension);
 | 
			
		||||
@@ -113,7 +112,6 @@ namespace ix
 | 
			
		||||
                sanitizeClientMaxWindowBits();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketPerMessageDeflateOptions::sanitizeClientMaxWindowBits()
 | 
			
		||||
@@ -128,7 +126,6 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
    std::string WebSocketPerMessageDeflateOptions::generateHeader()
 | 
			
		||||
    {
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << "Sec-WebSocket-Extensions: permessage-deflate";
 | 
			
		||||
 | 
			
		||||
@@ -141,18 +138,11 @@ namespace ix
 | 
			
		||||
        ss << "\r\n";
 | 
			
		||||
 | 
			
		||||
        return ss.str();
 | 
			
		||||
#else
 | 
			
		||||
        return std::string();
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool WebSocketPerMessageDeflateOptions::enabled() const
 | 
			
		||||
    {
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_ZLIB
 | 
			
		||||
        return _enabled;
 | 
			
		||||
#else
 | 
			
		||||
        return false;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool WebSocketPerMessageDeflateOptions::getClientNoContextTakeover() const
 | 
			
		||||
 
 | 
			
		||||
@@ -1,123 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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,38 +71,13 @@ namespace ix
 | 
			
		||||
        _onConnectionCallback = callback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketServer::setOnClientMessageCallback(const OnClientMessageCallback& callback)
 | 
			
		||||
    {
 | 
			
		||||
        _onClientMessageCallback = callback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketServer::handleConnection(std::unique_ptr<Socket> socket,
 | 
			
		||||
                                           std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                                           std::unique_ptr<ConnectionInfo> connectionInfo)
 | 
			
		||||
                                           std::shared_ptr<ConnectionState> connectionState)
 | 
			
		||||
    {
 | 
			
		||||
        setThreadName("WebSocketServer::" + connectionState->getId());
 | 
			
		||||
 | 
			
		||||
        auto webSocket = std::make_shared<WebSocket>();
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
        _onConnectionCallback(webSocket, connectionState);
 | 
			
		||||
 | 
			
		||||
        webSocket->disableAutomaticReconnection();
 | 
			
		||||
 | 
			
		||||
@@ -136,8 +111,6 @@ namespace ix
 | 
			
		||||
            logError(ss.str());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        webSocket->setOnMessageCallback(nullptr);
 | 
			
		||||
 | 
			
		||||
        // Remove this client from our client set
 | 
			
		||||
        {
 | 
			
		||||
            std::lock_guard<std::mutex> lock(_clientsMutex);
 | 
			
		||||
 
 | 
			
		||||
@@ -19,18 +19,11 @@
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class WebSocketServer : public SocketServer
 | 
			
		||||
    class WebSocketServer final : public SocketServer
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        using OnConnectionCallback =
 | 
			
		||||
            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&)>;
 | 
			
		||||
            std::function<void(std::shared_ptr<WebSocket>, std::shared_ptr<ConnectionState>)>;
 | 
			
		||||
 | 
			
		||||
        WebSocketServer(int port = SocketServer::kDefaultPort,
 | 
			
		||||
                        const std::string& host = SocketServer::kDefaultHost,
 | 
			
		||||
@@ -46,7 +39,6 @@ namespace ix
 | 
			
		||||
        void disablePerMessageDeflate();
 | 
			
		||||
 | 
			
		||||
        void setOnConnectionCallback(const OnConnectionCallback& callback);
 | 
			
		||||
        void setOnClientMessageCallback(const OnClientMessageCallback& callback);
 | 
			
		||||
 | 
			
		||||
        // Get all the connected clients
 | 
			
		||||
        std::set<std::shared_ptr<WebSocket>> getClients();
 | 
			
		||||
@@ -60,7 +52,6 @@ namespace ix
 | 
			
		||||
        bool _enablePerMessageDeflate;
 | 
			
		||||
 | 
			
		||||
        OnConnectionCallback _onConnectionCallback;
 | 
			
		||||
        OnClientMessageCallback _onClientMessageCallback;
 | 
			
		||||
 | 
			
		||||
        std::mutex _clientsMutex;
 | 
			
		||||
        std::set<std::shared_ptr<WebSocket>> _clients;
 | 
			
		||||
@@ -69,8 +60,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        // Methods
 | 
			
		||||
        virtual void handleConnection(std::unique_ptr<Socket> socket,
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                                      std::unique_ptr<ConnectionInfo> connectionInfo);
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState) final;
 | 
			
		||||
        virtual size_t getConnectedClientsCount() final;
 | 
			
		||||
    };
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -65,6 +65,7 @@ namespace ix
 | 
			
		||||
        , _receivedMessageCompressed(false)
 | 
			
		||||
        , _readyState(ReadyState::CLOSED)
 | 
			
		||||
        , _closeCode(WebSocketCloseConstants::kInternalErrorCode)
 | 
			
		||||
        , _closeReason(WebSocketCloseConstants::kInternalErrorMessage)
 | 
			
		||||
        , _closeWireSize(0)
 | 
			
		||||
        , _closeRemote(false)
 | 
			
		||||
        , _enablePerMessageDeflate(false)
 | 
			
		||||
@@ -76,7 +77,6 @@ namespace ix
 | 
			
		||||
        , _pingCount(0)
 | 
			
		||||
        , _lastSendPingTimePoint(std::chrono::steady_clock::now())
 | 
			
		||||
    {
 | 
			
		||||
        setCloseReason(WebSocketCloseConstants::kInternalErrorMessage);
 | 
			
		||||
        _readbuf.resize(kChunkSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -179,12 +179,10 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        if (readyState == ReadyState::CLOSED)
 | 
			
		||||
        {
 | 
			
		||||
            if (_onCloseCallback)
 | 
			
		||||
            {
 | 
			
		||||
                _onCloseCallback(_closeCode, getCloseReason(), _closeWireSize, _closeRemote);
 | 
			
		||||
            }
 | 
			
		||||
            setCloseReason(WebSocketCloseConstants::kInternalErrorMessage);
 | 
			
		||||
            std::lock_guard<std::mutex> lock(_closeDataMutex);
 | 
			
		||||
            _onCloseCallback(_closeCode, _closeReason, _closeWireSize, _closeRemote);
 | 
			
		||||
            _closeCode = WebSocketCloseConstants::kInternalErrorCode;
 | 
			
		||||
            _closeReason = WebSocketCloseConstants::kInternalErrorMessage;
 | 
			
		||||
            _closeWireSize = 0;
 | 
			
		||||
            _closeRemote = false;
 | 
			
		||||
        }
 | 
			
		||||
@@ -263,10 +261,9 @@ namespace ix
 | 
			
		||||
        {
 | 
			
		||||
            // compute lasting delay to wait for next ping / timeout, if at least one set
 | 
			
		||||
            auto now = std::chrono::steady_clock::now();
 | 
			
		||||
            int timeSinceLastPingMs = (int) std::chrono::duration_cast<std::chrono::milliseconds>(
 | 
			
		||||
            lastingTimeoutDelayInMs = (int) std::chrono::duration_cast<std::chrono::milliseconds>(
 | 
			
		||||
                                          now - _lastSendPingTimePoint)
 | 
			
		||||
                                          .count();
 | 
			
		||||
            lastingTimeoutDelayInMs = (1000 * _pingIntervalSecs) - timeSinceLastPingMs;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
@@ -329,10 +326,9 @@ namespace ix
 | 
			
		||||
        return _txbuf.empty();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class Iterator>
 | 
			
		||||
    void WebSocketTransport::appendToSendBuffer(const std::vector<uint8_t>& header,
 | 
			
		||||
                                                Iterator begin,
 | 
			
		||||
                                                Iterator end,
 | 
			
		||||
                                                std::string::const_iterator begin,
 | 
			
		||||
                                                std::string::const_iterator end,
 | 
			
		||||
                                                uint64_t message_size,
 | 
			
		||||
                                                uint8_t masking_key[4])
 | 
			
		||||
    {
 | 
			
		||||
@@ -642,7 +638,11 @@ namespace ix
 | 
			
		||||
                {
 | 
			
		||||
                    // we got the CLOSE frame answer from our close, so we can close the connection
 | 
			
		||||
                    // if the code/reason are the same
 | 
			
		||||
                    bool identicalReason = _closeCode == code && getCloseReason() == reason;
 | 
			
		||||
                    bool identicalReason;
 | 
			
		||||
                    {
 | 
			
		||||
                        std::lock_guard<std::mutex> lock(_closeDataMutex);
 | 
			
		||||
                        identicalReason = _closeCode == code && _closeReason == reason;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (identicalReason)
 | 
			
		||||
                    {
 | 
			
		||||
@@ -750,9 +750,8 @@ namespace ix
 | 
			
		||||
        return static_cast<unsigned>(seconds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class T>
 | 
			
		||||
    WebSocketSendInfo WebSocketTransport::sendData(wsheader_type::opcode_type type,
 | 
			
		||||
                                                   const T& message,
 | 
			
		||||
                                                   const std::string& message,
 | 
			
		||||
                                                   bool compress,
 | 
			
		||||
                                                   const OnProgressCallback& onProgressCallback)
 | 
			
		||||
    {
 | 
			
		||||
@@ -765,8 +764,8 @@ namespace ix
 | 
			
		||||
        size_t wireSize = message.size();
 | 
			
		||||
        bool compressionError = false;
 | 
			
		||||
 | 
			
		||||
        auto message_begin = message.cbegin();
 | 
			
		||||
        auto message_end = message.cend();
 | 
			
		||||
        std::string::const_iterator message_begin = message.begin();
 | 
			
		||||
        std::string::const_iterator message_end = message.end();
 | 
			
		||||
 | 
			
		||||
        if (compress)
 | 
			
		||||
        {
 | 
			
		||||
@@ -781,8 +780,8 @@ namespace ix
 | 
			
		||||
            compressionError = false;
 | 
			
		||||
            wireSize = _compressedMessage.size();
 | 
			
		||||
 | 
			
		||||
            message_begin = _compressedMessage.cbegin();
 | 
			
		||||
            message_end = _compressedMessage.cend();
 | 
			
		||||
            message_begin = _compressedMessage.begin();
 | 
			
		||||
            message_end = _compressedMessage.end();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
@@ -796,11 +795,6 @@ namespace ix
 | 
			
		||||
        if (wireSize < kChunkSize)
 | 
			
		||||
        {
 | 
			
		||||
            success = sendFragment(type, true, message_begin, message_end, compress);
 | 
			
		||||
 | 
			
		||||
            if (onProgressCallback)
 | 
			
		||||
            {
 | 
			
		||||
                onProgressCallback(0, 1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
@@ -865,11 +859,10 @@ namespace ix
 | 
			
		||||
        return WebSocketSendInfo(success, compressionError, payloadSize, wireSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<class Iterator>
 | 
			
		||||
    bool WebSocketTransport::sendFragment(wsheader_type::opcode_type type,
 | 
			
		||||
                                          bool fin,
 | 
			
		||||
                                          Iterator message_begin,
 | 
			
		||||
                                          Iterator message_end,
 | 
			
		||||
                                          std::string::const_iterator message_begin,
 | 
			
		||||
                                          std::string::const_iterator message_end,
 | 
			
		||||
                                          bool compress)
 | 
			
		||||
    {
 | 
			
		||||
        uint64_t message_size = static_cast<uint64_t>(message_end - message_begin);
 | 
			
		||||
@@ -1062,7 +1055,7 @@ namespace ix
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // no close code/reason set
 | 
			
		||||
            sendData(wsheader_type::CLOSE, std::string(""), compress);
 | 
			
		||||
            sendData(wsheader_type::CLOSE, "", compress);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1085,10 +1078,13 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        closeSocket();
 | 
			
		||||
 | 
			
		||||
        setCloseReason(reason);
 | 
			
		||||
        _closeCode = code;
 | 
			
		||||
        _closeWireSize = closeWireSize;
 | 
			
		||||
        _closeRemote = remote;
 | 
			
		||||
        {
 | 
			
		||||
            std::lock_guard<std::mutex> lock(_closeDataMutex);
 | 
			
		||||
            _closeCode = code;
 | 
			
		||||
            _closeReason = reason;
 | 
			
		||||
            _closeWireSize = closeWireSize;
 | 
			
		||||
            _closeRemote = remote;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setReadyState(ReadyState::CLOSED);
 | 
			
		||||
        _requestInitCancellation = false;
 | 
			
		||||
@@ -1108,11 +1104,13 @@ namespace ix
 | 
			
		||||
            closeWireSize = reason.size();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setCloseReason(reason);
 | 
			
		||||
        _closeCode = code;
 | 
			
		||||
        _closeWireSize = closeWireSize;
 | 
			
		||||
        _closeRemote = remote;
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            std::lock_guard<std::mutex> lock(_closeDataMutex);
 | 
			
		||||
            _closeCode = code;
 | 
			
		||||
            _closeReason = reason;
 | 
			
		||||
            _closeWireSize = closeWireSize;
 | 
			
		||||
            _closeRemote = remote;
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            std::lock_guard<std::mutex> lock(_closingTimePointMutex);
 | 
			
		||||
            _closingTimePoint = std::chrono::steady_clock::now();
 | 
			
		||||
@@ -1157,15 +1155,4 @@ namespace ix
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketTransport::setCloseReason(const std::string& reason)
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_closeReasonMutex);
 | 
			
		||||
        _closeReason = reason;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const std::string& WebSocketTransport::getCloseReason() const
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_closeReasonMutex);
 | 
			
		||||
        return _closeReason;
 | 
			
		||||
    }
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -178,11 +178,11 @@ namespace ix
 | 
			
		||||
        std::atomic<ReadyState> _readyState;
 | 
			
		||||
 | 
			
		||||
        OnCloseCallback _onCloseCallback;
 | 
			
		||||
        uint16_t _closeCode;
 | 
			
		||||
        std::string _closeReason;
 | 
			
		||||
        mutable std::mutex _closeReasonMutex;
 | 
			
		||||
        std::atomic<uint16_t> _closeCode;
 | 
			
		||||
        std::atomic<size_t> _closeWireSize;
 | 
			
		||||
        std::atomic<bool> _closeRemote;
 | 
			
		||||
        size_t _closeWireSize;
 | 
			
		||||
        bool _closeRemote;
 | 
			
		||||
        mutable std::mutex _closeDataMutex;
 | 
			
		||||
 | 
			
		||||
        // Data used for Per Message Deflate compression (with zlib)
 | 
			
		||||
        WebSocketPerMessageDeflatePtr _perMessageDeflate;
 | 
			
		||||
@@ -239,15 +239,16 @@ namespace ix
 | 
			
		||||
        bool sendOnSocket();
 | 
			
		||||
        bool receiveFromSocket();
 | 
			
		||||
 | 
			
		||||
        template<class T>
 | 
			
		||||
        WebSocketSendInfo sendData(wsheader_type::opcode_type type,
 | 
			
		||||
                                   const T& message,
 | 
			
		||||
                                   const std::string& message,
 | 
			
		||||
                                   bool compress,
 | 
			
		||||
                                   const OnProgressCallback& onProgressCallback = nullptr);
 | 
			
		||||
 | 
			
		||||
        template<class Iterator>
 | 
			
		||||
        bool sendFragment(
 | 
			
		||||
            wsheader_type::opcode_type type, bool fin, Iterator begin, Iterator end, bool compress);
 | 
			
		||||
        bool sendFragment(wsheader_type::opcode_type type,
 | 
			
		||||
                          bool fin,
 | 
			
		||||
                          std::string::const_iterator begin,
 | 
			
		||||
                          std::string::const_iterator end,
 | 
			
		||||
                          bool compress);
 | 
			
		||||
 | 
			
		||||
        void emitMessage(MessageKind messageKind,
 | 
			
		||||
                         const std::string& message,
 | 
			
		||||
@@ -255,11 +256,9 @@ namespace ix
 | 
			
		||||
                         const OnMessageCallback& onMessageCallback);
 | 
			
		||||
 | 
			
		||||
        bool isSendBufferEmpty() const;
 | 
			
		||||
 | 
			
		||||
        template<class Iterator>
 | 
			
		||||
        void appendToSendBuffer(const std::vector<uint8_t>& header,
 | 
			
		||||
                                Iterator begin,
 | 
			
		||||
                                Iterator end,
 | 
			
		||||
                                std::string::const_iterator begin,
 | 
			
		||||
                                std::string::const_iterator end,
 | 
			
		||||
                                uint64_t message_size,
 | 
			
		||||
                                uint8_t masking_key[4]);
 | 
			
		||||
 | 
			
		||||
@@ -267,8 +266,5 @@ namespace ix
 | 
			
		||||
        void unmaskReceiveBuffer(const wsheader_type& ws);
 | 
			
		||||
 | 
			
		||||
        std::string getMergedChunks() const;
 | 
			
		||||
 | 
			
		||||
        void setCloseReason(const std::string& reason);
 | 
			
		||||
        const std::string& getCloseReason() const;
 | 
			
		||||
    };
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -6,4 +6,4 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#define IX_WEBSOCKET_VERSION "10.1.1"
 | 
			
		||||
#define IX_WEBSOCKET_VERSION "9.6.5"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								ixwebsocket/apple/IXSetThreadName_apple.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								ixwebsocket/apple/IXSetThreadName_apple.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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
 | 
			
		||||
							
								
								
									
										16
									
								
								ixwebsocket/freebsd/IXSetThreadName_freebsd.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								ixwebsocket/freebsd/IXSetThreadName_freebsd.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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
 | 
			
		||||
							
								
								
									
										20
									
								
								ixwebsocket/linux/IXSetThreadName_linux.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								ixwebsocket/linux/IXSetThreadName_linux.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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
 | 
			
		||||
							
								
								
									
										46
									
								
								ixwebsocket/windows/IXSetThreadName_windows.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								ixwebsocket/windows/IXSetThreadName_windows.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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,28 +19,26 @@ install: brew
 | 
			
		||||
#
 | 
			
		||||
# 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:
 | 
			
		||||
	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 had problems with OpenSSL and TLS 1.3 (on the
 | 
			
		||||
# Docker default target. We've add problem with OpenSSL and TLS 1.3 (on the
 | 
			
		||||
# server side ?) and I can't work-around it easily, so we're using mbedtls on
 | 
			
		||||
# Linux for the SSL backend, which works great.
 | 
			
		||||
ws_mbedtls_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)
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; ninja install)
 | 
			
		||||
 | 
			
		||||
ws:
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. ; ninja install)
 | 
			
		||||
 | 
			
		||||
ws_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)
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j 4 install)
 | 
			
		||||
 | 
			
		||||
ws_openssl:
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4)
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4)
 | 
			
		||||
 | 
			
		||||
ws_mbedtls:
 | 
			
		||||
	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)
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4)
 | 
			
		||||
 | 
			
		||||
ws_no_ssl:
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4)
 | 
			
		||||
@@ -104,23 +102,21 @@ test_server:
 | 
			
		||||
# env TEST=Websocket_server make test
 | 
			
		||||
# env TEST=Websocket_chat make test
 | 
			
		||||
# env TEST=heartbeat make test
 | 
			
		||||
build_test:
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. ; ninja install)
 | 
			
		||||
 | 
			
		||||
test: build_test
 | 
			
		||||
test:
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
 | 
			
		||||
test_make:
 | 
			
		||||
	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)
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; make -j 4)
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
 | 
			
		||||
test_tsan:
 | 
			
		||||
	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)
 | 
			
		||||
	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)
 | 
			
		||||
	(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
 | 
			
		||||
test_ubsan:
 | 
			
		||||
	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)
 | 
			
		||||
	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)
 | 
			
		||||
	(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
 | 
			
		||||
@@ -128,37 +124,37 @@ test_asan: build_test_asan
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
 | 
			
		||||
build_test_asan:
 | 
			
		||||
	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)
 | 
			
		||||
	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)
 | 
			
		||||
	(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
 | 
			
		||||
 | 
			
		||||
test_tsan_openssl:
 | 
			
		||||
	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)
 | 
			
		||||
	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)
 | 
			
		||||
	(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
 | 
			
		||||
test_ubsan_openssl:
 | 
			
		||||
	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)
 | 
			
		||||
	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)
 | 
			
		||||
	(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
 | 
			
		||||
test_tsan_openssl_release:
 | 
			
		||||
	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)
 | 
			
		||||
	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)
 | 
			
		||||
	(cd build/test ; ln -sf Release/ixwebsocket_unittest)
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
 | 
			
		||||
test_tsan_mbedtls:
 | 
			
		||||
	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)
 | 
			
		||||
	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)
 | 
			
		||||
	(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
 | 
			
		||||
build_test_openssl:
 | 
			
		||||
	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)
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_OPEN_SSL=1 -DUSE_TEST=1 .. ; ninja install)
 | 
			
		||||
 | 
			
		||||
test_openssl: build_test_openssl
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
 | 
			
		||||
build_test_mbedtls:
 | 
			
		||||
	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)
 | 
			
		||||
	mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_MBED_TLS=1 -DUSE_TEST=1 .. ; make -j 4)
 | 
			
		||||
 | 
			
		||||
test_mbedtls: build_test_mbedtls
 | 
			
		||||
	(cd test ; python2.7 run.py -r)
 | 
			
		||||
@@ -174,7 +170,7 @@ autobahn_report:
 | 
			
		||||
	cp -rvf ~/sandbox/reports/clients/* ../bsergean.github.io/IXWebSocket/autobahn/
 | 
			
		||||
 | 
			
		||||
httpd:
 | 
			
		||||
	clang++ --std=c++14 --stdlib=libc++ -o ixhttpd httpd.cpp \
 | 
			
		||||
	clang++ --std=c++11 --stdlib=libc++ -Iixwebsocket httpd.cpp \
 | 
			
		||||
		ixwebsocket/IXSelectInterruptFactory.cpp \
 | 
			
		||||
		ixwebsocket/IXCancellationRequest.cpp \
 | 
			
		||||
		ixwebsocket/IXSocketTLSOptions.cpp \
 | 
			
		||||
@@ -193,11 +189,11 @@ httpd:
 | 
			
		||||
		ixwebsocket/IXConnectionState.cpp \
 | 
			
		||||
		ixwebsocket/IXUrlParser.cpp \
 | 
			
		||||
		ixwebsocket/IXSelectInterrupt.cpp \
 | 
			
		||||
		ixwebsocket/IXSetThreadName.cpp \
 | 
			
		||||
		ixwebsocket/apple/IXSetThreadName_apple.cpp \
 | 
			
		||||
		-lz
 | 
			
		||||
 | 
			
		||||
httpd_linux:
 | 
			
		||||
	g++ --std=c++14 -o ixhttpd httpd.cpp -Iixwebsocket \
 | 
			
		||||
	g++ --std=c++11 -o ixhttpd httpd.cpp -Iixwebsocket \
 | 
			
		||||
		ixwebsocket/IXSelectInterruptFactory.cpp \
 | 
			
		||||
		ixwebsocket/IXCancellationRequest.cpp \
 | 
			
		||||
		ixwebsocket/IXSocketTLSOptions.cpp \
 | 
			
		||||
@@ -216,7 +212,7 @@ httpd_linux:
 | 
			
		||||
		ixwebsocket/IXConnectionState.cpp \
 | 
			
		||||
		ixwebsocket/IXUrlParser.cpp \
 | 
			
		||||
		ixwebsocket/IXSelectInterrupt.cpp \
 | 
			
		||||
		ixwebsocket/IXSetThreadName.cpp \
 | 
			
		||||
		ixwebsocket/linux/IXSetThreadName_linux.cpp \
 | 
			
		||||
		-lz -lpthread
 | 
			
		||||
	cp -f ixhttpd /usr/local/bin
 | 
			
		||||
 | 
			
		||||
@@ -227,7 +223,6 @@ rebase_upstream:
 | 
			
		||||
	git reset --hard upstream/master
 | 
			
		||||
	git push origin master --force
 | 
			
		||||
 | 
			
		||||
# Legacy target
 | 
			
		||||
install_cmake_for_linux:
 | 
			
		||||
	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)
 | 
			
		||||
@@ -238,12 +233,6 @@ install_cmake_for_linux:
 | 
			
		||||
doc:
 | 
			
		||||
	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: build
 | 
			
		||||
.PHONY: ws
 | 
			
		||||
 
 | 
			
		||||
@@ -37,11 +37,11 @@ set (SOURCES
 | 
			
		||||
 | 
			
		||||
  test_runner.cpp
 | 
			
		||||
  IXTest.cpp
 | 
			
		||||
  IXGetFreePort.cpp
 | 
			
		||||
  ../third_party/msgpack11/msgpack11.cpp
 | 
			
		||||
 | 
			
		||||
  IXSocketTest.cpp
 | 
			
		||||
  IXSocketConnectTest.cpp
 | 
			
		||||
  # IXWebSocketLeakTest.cpp # commented until we have a fix for #224
 | 
			
		||||
  IXWebSocketServerTest.cpp
 | 
			
		||||
  IXWebSocketTestConnectionDisconnection.cpp
 | 
			
		||||
  IXUrlParserTest.cpp
 | 
			
		||||
@@ -55,8 +55,6 @@ set (SOURCES
 | 
			
		||||
  IXSentryClientTest.cpp
 | 
			
		||||
  IXWebSocketChatTest.cpp
 | 
			
		||||
  IXWebSocketBroadcastTest.cpp
 | 
			
		||||
  IXWebSocketPerMessageDeflateCompressorTest.cpp
 | 
			
		||||
  IXStreamSqlTest.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Some unittest don't work on windows yet
 | 
			
		||||
@@ -93,30 +91,15 @@ if (JSONCPP_FOUND)
 | 
			
		||||
  target_link_libraries(ixwebsocket_unittest ${JSONCPP_LIBRARIES})
 | 
			
		||||
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
 | 
			
		||||
target_link_libraries(ixwebsocket_unittest ixbots)
 | 
			
		||||
target_link_libraries(ixwebsocket_unittest ixsnake)
 | 
			
		||||
target_link_libraries(ixwebsocket_unittest ixcobra)
 | 
			
		||||
target_link_libraries(ixwebsocket_unittest ixsentry)
 | 
			
		||||
target_link_libraries(ixwebsocket_unittest ixredis)
 | 
			
		||||
target_link_libraries(ixwebsocket_unittest ixwebsocket)
 | 
			
		||||
target_link_libraries(ixwebsocket_unittest ixcrypto)
 | 
			
		||||
target_link_libraries(ixwebsocket_unittest ixcore)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(ixwebsocket_unittest spdlog)
 | 
			
		||||
if (USE_PYTHON)
 | 
			
		||||
  target_link_libraries(ixwebsocket_unittest ${Python_LIBRARIES})
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
install(TARGETS ixwebsocket_unittest DESTINATION bin)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <ixcobra/IXCobraConnection.h>
 | 
			
		||||
#include <ixcrypto/IXUuid.h>
 | 
			
		||||
#include <ixredis/IXRedisServer.h>
 | 
			
		||||
#include <ixsnake/IXRedisServer.h>
 | 
			
		||||
#include <ixsnake/IXSnakeServer.h>
 | 
			
		||||
 | 
			
		||||
using namespace ix;
 | 
			
		||||
@@ -125,12 +125,10 @@ namespace
 | 
			
		||||
    {
 | 
			
		||||
        std::string filter;
 | 
			
		||||
        std::string position("$");
 | 
			
		||||
        int batchSize = 1;
 | 
			
		||||
 | 
			
		||||
        _conn.subscribe(channel,
 | 
			
		||||
                        filter,
 | 
			
		||||
                        position,
 | 
			
		||||
                        batchSize,
 | 
			
		||||
                        [this](const Json::Value& msg, const std::string& /*position*/) {
 | 
			
		||||
                            spdlog::info("receive {}", msg.toStyledString());
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <ixcobra/IXCobraMetricsPublisher.h>
 | 
			
		||||
#include <ixcrypto/IXUuid.h>
 | 
			
		||||
#include <ixredis/IXRedisServer.h>
 | 
			
		||||
#include <ixsnake/IXRedisServer.h>
 | 
			
		||||
#include <ixsnake/IXSnakeServer.h>
 | 
			
		||||
#include <set>
 | 
			
		||||
 | 
			
		||||
@@ -76,12 +76,10 @@ namespace
 | 
			
		||||
                log("Subscriber authenticated");
 | 
			
		||||
                std::string filter;
 | 
			
		||||
                std::string position("$");
 | 
			
		||||
                int batchSize = 1;
 | 
			
		||||
 | 
			
		||||
                conn.subscribe(channel,
 | 
			
		||||
                               filter,
 | 
			
		||||
                               position,
 | 
			
		||||
                               batchSize,
 | 
			
		||||
                               [](const Json::Value& msg, const std::string& /*position*/) {
 | 
			
		||||
                                   log(msg.toStyledString());
 | 
			
		||||
 | 
			
		||||
@@ -108,7 +106,7 @@ namespace
 | 
			
		||||
            }
 | 
			
		||||
            else if (event->type == ix::CobraEventType::UnSubscribed)
 | 
			
		||||
            {
 | 
			
		||||
                TLogger() << "Subscriber: unsubscribed from channel " << event->subscriptionId;
 | 
			
		||||
                TLogger() << "Subscriber: ununexpected from channel " << event->subscriptionId;
 | 
			
		||||
                if (event->subscriptionId != channel)
 | 
			
		||||
                {
 | 
			
		||||
                    TLogger() << "Subscriber: unexpected channel " << event->subscriptionId;
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,8 @@
 | 
			
		||||
#include <ixcobra/IXCobraConnection.h>
 | 
			
		||||
#include <ixcobra/IXCobraMetricsPublisher.h>
 | 
			
		||||
#include <ixcrypto/IXUuid.h>
 | 
			
		||||
#include <ixredis/IXRedisServer.h>
 | 
			
		||||
#include <ixsentry/IXSentryClient.h>
 | 
			
		||||
#include <ixsnake/IXRedisServer.h>
 | 
			
		||||
#include <ixsnake/IXSnakeServer.h>
 | 
			
		||||
#include <ixwebsocket/IXHttpServer.h>
 | 
			
		||||
#include <ixwebsocket/IXUserAgent.h>
 | 
			
		||||
@@ -95,15 +95,13 @@ TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
 | 
			
		||||
 | 
			
		||||
        sentryServer.setOnConnectionCallback(
 | 
			
		||||
            [](HttpRequestPtr request,
 | 
			
		||||
               std::shared_ptr<ConnectionState> /*connectionState*/,
 | 
			
		||||
               std::unique_ptr<ConnectionInfo> connectionInfo) -> HttpResponsePtr {
 | 
			
		||||
               std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr {
 | 
			
		||||
                WebSocketHttpHeaders headers;
 | 
			
		||||
                headers["Server"] = userAgent();
 | 
			
		||||
 | 
			
		||||
                // Log request
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                ss << connectionInfo->remoteIp << ":" << connectionInfo->remotePort << " "
 | 
			
		||||
                   << request->method << " " << request->headers["User-Agent"] << " "
 | 
			
		||||
                ss << request->method << " " << request->headers["User-Agent"] << " "
 | 
			
		||||
                   << request->uri;
 | 
			
		||||
 | 
			
		||||
                if (request->method == "POST")
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,8 @@
 | 
			
		||||
#include <ixcobra/IXCobraConnection.h>
 | 
			
		||||
#include <ixcobra/IXCobraMetricsPublisher.h>
 | 
			
		||||
#include <ixcrypto/IXUuid.h>
 | 
			
		||||
#include <ixredis/IXRedisServer.h>
 | 
			
		||||
#include <ixsentry/IXSentryClient.h>
 | 
			
		||||
#include <ixsnake/IXRedisServer.h>
 | 
			
		||||
#include <ixsnake/IXSnakeServer.h>
 | 
			
		||||
#include <ixwebsocket/IXHttpServer.h>
 | 
			
		||||
#include <ixwebsocket/IXUserAgent.h>
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,8 @@
 | 
			
		||||
#include <ixcobra/IXCobraConnection.h>
 | 
			
		||||
#include <ixcobra/IXCobraMetricsPublisher.h>
 | 
			
		||||
#include <ixcrypto/IXUuid.h>
 | 
			
		||||
#include <ixredis/IXRedisServer.h>
 | 
			
		||||
#include <ixsentry/IXSentryClient.h>
 | 
			
		||||
#include <ixsnake/IXRedisServer.h>
 | 
			
		||||
#include <ixsnake/IXSnakeServer.h>
 | 
			
		||||
#include <ixwebsocket/IXHttpServer.h>
 | 
			
		||||
#include <ixwebsocket/IXUserAgent.h>
 | 
			
		||||
@@ -92,9 +92,6 @@ TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]")
 | 
			
		||||
        cobraBotConfig.enableHeartbeat = false;
 | 
			
		||||
        bool quiet = false;
 | 
			
		||||
 | 
			
		||||
        cobraBotConfig.filter =
 | 
			
		||||
            std::string("select * from `") + channel + "` where id = 'sms_metric_A_id'";
 | 
			
		||||
 | 
			
		||||
        // We could try to capture the output ... not sure how.
 | 
			
		||||
        bool fluentd = true;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,9 @@
 | 
			
		||||
 *  Copyright (c) 2019 Machine Zone. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXGetFreePort.h"
 | 
			
		||||
#include "catch.hpp"
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <ixwebsocket/IXGetFreePort.h>
 | 
			
		||||
#include <ixwebsocket/IXHttpClient.h>
 | 
			
		||||
#include <ixwebsocket/IXHttpServer.h>
 | 
			
		||||
 | 
			
		||||
@@ -67,8 +67,7 @@ TEST_CASE("http server", "[httpd]")
 | 
			
		||||
 | 
			
		||||
TEST_CASE("http server redirection", "[httpd_redirect]")
 | 
			
		||||
{
 | 
			
		||||
    SECTION(
 | 
			
		||||
        "Connect to a local HTTP server, with redirection enabled, but we do not follow redirects")
 | 
			
		||||
    SECTION("Connect to a local HTTP server, with redirection enabled")
 | 
			
		||||
    {
 | 
			
		||||
        int port = getFreePort();
 | 
			
		||||
        ix::HttpServer server(port, "127.0.0.1");
 | 
			
		||||
@@ -118,54 +117,4 @@ TEST_CASE("http server redirection", "[httpd_redirect]")
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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,38 +84,36 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
    bool startWebSocketEchoServer(ix::WebSocketServer& server)
 | 
			
		||||
    {
 | 
			
		||||
        server.setOnClientMessageCallback(
 | 
			
		||||
            [&server](std::shared_ptr<ConnectionState> /*connectionState*/,
 | 
			
		||||
                      ConnectionInfo& connectionInfo,
 | 
			
		||||
                      WebSocket& webSocket,
 | 
			
		||||
                      const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                auto remoteIp = connectionInfo.remoteIp;
 | 
			
		||||
                if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                {
 | 
			
		||||
                    TLogger() << "New connection";
 | 
			
		||||
                    TLogger() << "Remote ip: " << remoteIp;
 | 
			
		||||
                    TLogger() << "Uri: " << msg->openInfo.uri;
 | 
			
		||||
                    TLogger() << "Headers:";
 | 
			
		||||
                    for (auto it : msg->openInfo.headers)
 | 
			
		||||
        server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
 | 
			
		||||
                                                 std::shared_ptr<ConnectionState> connectionState) {
 | 
			
		||||
            webSocket->setOnMessageCallback(
 | 
			
		||||
                [webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                    if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                    {
 | 
			
		||||
                        TLogger() << it.first << ": " << it.second;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (msg->type == ix::WebSocketMessageType::Close)
 | 
			
		||||
                {
 | 
			
		||||
                    TLogger() << "Closed connection";
 | 
			
		||||
                }
 | 
			
		||||
                else if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
                {
 | 
			
		||||
                    for (auto&& client : server.getClients())
 | 
			
		||||
                    {
 | 
			
		||||
                        if (client.get() != &webSocket)
 | 
			
		||||
                        TLogger() << "New connection";
 | 
			
		||||
                        TLogger() << "Uri: " << msg->openInfo.uri;
 | 
			
		||||
                        TLogger() << "Headers:";
 | 
			
		||||
                        for (auto it : msg->openInfo.headers)
 | 
			
		||||
                        {
 | 
			
		||||
                            client->send(msg->str, msg->binary);
 | 
			
		||||
                            TLogger() << it.first << ": " << it.second;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
                    else if (msg->type == ix::WebSocketMessageType::Close)
 | 
			
		||||
                    {
 | 
			
		||||
                        TLogger() << "Closed connection";
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
                    {
 | 
			
		||||
                        for (auto&& client : server.getClients())
 | 
			
		||||
                        {
 | 
			
		||||
                            if (client != webSocket)
 | 
			
		||||
                            {
 | 
			
		||||
                                client->send(msg->str, msg->binary);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        auto res = server.listen();
 | 
			
		||||
        if (!res.first)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,9 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "IXGetFreePort.h"
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <ixsnake/IXAppConfig.h>
 | 
			
		||||
#include <ixwebsocket/IXGetFreePort.h>
 | 
			
		||||
#include <ixwebsocket/IXSocketTLSOptions.h>
 | 
			
		||||
#include <ixwebsocket/IXWebSocketServer.h>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 
 | 
			
		||||
@@ -189,19 +189,15 @@ namespace
 | 
			
		||||
        bool preferTLS = true;
 | 
			
		||||
        server.setTLSOptions(makeServerTLSOptions(preferTLS));
 | 
			
		||||
 | 
			
		||||
        server.setOnClientMessageCallback(
 | 
			
		||||
            [&server, &connectionId](std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                                     ConnectionInfo& connectionInfo,
 | 
			
		||||
                                     WebSocket& webSocket,
 | 
			
		||||
                                     const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                auto remoteIp = connectionInfo.remoteIp;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        server.setOnConnectionCallback([&server, &connectionId](
 | 
			
		||||
                                           std::shared_ptr<ix::WebSocket> webSocket,
 | 
			
		||||
                                           std::shared_ptr<ConnectionState> connectionState) {
 | 
			
		||||
            webSocket->setOnMessageCallback([webSocket, connectionState, &connectionId, &server](
 | 
			
		||||
                                                const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                {
 | 
			
		||||
                    TLogger() << "New connection";
 | 
			
		||||
                    connectionState->computeId();
 | 
			
		||||
                    TLogger() << "remote ip: " << remoteIp;
 | 
			
		||||
                    TLogger() << "id: " << connectionState->getId();
 | 
			
		||||
                    TLogger() << "Uri: " << msg->openInfo.uri;
 | 
			
		||||
                    TLogger() << "Headers:";
 | 
			
		||||
@@ -220,13 +216,14 @@ namespace
 | 
			
		||||
                {
 | 
			
		||||
                    for (auto&& client : server.getClients())
 | 
			
		||||
                    {
 | 
			
		||||
                        if (client.get() != &webSocket)
 | 
			
		||||
                        if (client != webSocket)
 | 
			
		||||
                        {
 | 
			
		||||
                            client->send(msg->str, msg->binary);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        auto res = server.listen();
 | 
			
		||||
        if (!res.first)
 | 
			
		||||
 
 | 
			
		||||
@@ -193,39 +193,37 @@ namespace
 | 
			
		||||
 | 
			
		||||
    bool startServer(ix::WebSocketServer& server)
 | 
			
		||||
    {
 | 
			
		||||
        server.setOnClientMessageCallback(
 | 
			
		||||
            [&server](std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                      ConnectionInfo& connectionInfo,
 | 
			
		||||
                      WebSocket& webSocket,
 | 
			
		||||
                      const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                auto remoteIp = connectionInfo.remoteIp;
 | 
			
		||||
                if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                {
 | 
			
		||||
                    TLogger() << "New connection";
 | 
			
		||||
                    TLogger() << "remote ip: " << remoteIp;
 | 
			
		||||
                    TLogger() << "id: " << connectionState->getId();
 | 
			
		||||
                    TLogger() << "Uri: " << msg->openInfo.uri;
 | 
			
		||||
                    TLogger() << "Headers:";
 | 
			
		||||
                    for (auto it : msg->openInfo.headers)
 | 
			
		||||
        server.setOnConnectionCallback([&server](std::shared_ptr<ix::WebSocket> webSocket,
 | 
			
		||||
                                                 std::shared_ptr<ConnectionState> connectionState) {
 | 
			
		||||
            webSocket->setOnMessageCallback(
 | 
			
		||||
                [webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                    if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                    {
 | 
			
		||||
                        TLogger() << it.first << ": " << it.second;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (msg->type == ix::WebSocketMessageType::Close)
 | 
			
		||||
                {
 | 
			
		||||
                    log("Closed connection");
 | 
			
		||||
                }
 | 
			
		||||
                else if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
                {
 | 
			
		||||
                    for (auto&& client : server.getClients())
 | 
			
		||||
                    {
 | 
			
		||||
                        if (client.get() != &webSocket)
 | 
			
		||||
                        TLogger() << "New connection";
 | 
			
		||||
                        TLogger() << "id: " << connectionState->getId();
 | 
			
		||||
                        TLogger() << "Uri: " << msg->openInfo.uri;
 | 
			
		||||
                        TLogger() << "Headers:";
 | 
			
		||||
                        for (auto it : msg->openInfo.headers)
 | 
			
		||||
                        {
 | 
			
		||||
                            client->sendBinary(msg->str);
 | 
			
		||||
                            TLogger() << it.first << ": " << it.second;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
                    else if (msg->type == ix::WebSocketMessageType::Close)
 | 
			
		||||
                    {
 | 
			
		||||
                        log("Closed connection");
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
                    {
 | 
			
		||||
                        for (auto&& client : server.getClients())
 | 
			
		||||
                        {
 | 
			
		||||
                            if (client != webSocket)
 | 
			
		||||
                            {
 | 
			
		||||
                                client->sendBinary(msg->str);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        auto res = server.listen();
 | 
			
		||||
        if (!res.first)
 | 
			
		||||
 
 | 
			
		||||
@@ -168,38 +168,41 @@ namespace
 | 
			
		||||
                     std::mutex& mutexWrite)
 | 
			
		||||
    {
 | 
			
		||||
        // A dev/null server
 | 
			
		||||
        server.setOnClientMessageCallback(
 | 
			
		||||
        server.setOnConnectionCallback(
 | 
			
		||||
            [&receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](
 | 
			
		||||
                std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                ConnectionInfo& connectionInfo,
 | 
			
		||||
                WebSocket& /*webSocket*/,
 | 
			
		||||
                const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                auto remoteIp = connectionInfo.remoteIp;
 | 
			
		||||
                if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                {
 | 
			
		||||
                    TLogger() << "New server connection";
 | 
			
		||||
                    TLogger() << "remote ip: " << remoteIp;
 | 
			
		||||
                    TLogger() << "id: " << connectionState->getId();
 | 
			
		||||
                    TLogger() << "Uri: " << msg->openInfo.uri;
 | 
			
		||||
                    TLogger() << "Headers:";
 | 
			
		||||
                    for (auto it : msg->openInfo.headers)
 | 
			
		||||
                std::shared_ptr<ix::WebSocket> webSocket,
 | 
			
		||||
                std::shared_ptr<ConnectionState> connectionState) {
 | 
			
		||||
                webSocket->setOnMessageCallback([webSocket,
 | 
			
		||||
                                                 connectionState,
 | 
			
		||||
                                                 &receivedCloseCode,
 | 
			
		||||
                                                 &receivedCloseReason,
 | 
			
		||||
                                                 &receivedCloseRemote,
 | 
			
		||||
                                                 &mutexWrite](const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                    if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                    {
 | 
			
		||||
                        TLogger() << it.first << ": " << it.second;
 | 
			
		||||
                        TLogger() << "New server connection";
 | 
			
		||||
                        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)
 | 
			
		||||
                {
 | 
			
		||||
                    std::stringstream ss;
 | 
			
		||||
                    ss << "Server closed connection(" << msg->closeInfo.code << ","
 | 
			
		||||
                       << msg->closeInfo.reason << ")";
 | 
			
		||||
                    log(ss.str());
 | 
			
		||||
                    else if (msg->type == ix::WebSocketMessageType::Close)
 | 
			
		||||
                    {
 | 
			
		||||
                        std::stringstream ss;
 | 
			
		||||
                        ss << "Server closed connection(" << msg->closeInfo.code << ","
 | 
			
		||||
                           << msg->closeInfo.reason << ")";
 | 
			
		||||
                        log(ss.str());
 | 
			
		||||
 | 
			
		||||
                    std::lock_guard<std::mutex> lck(mutexWrite);
 | 
			
		||||
                        std::lock_guard<std::mutex> lck(mutexWrite);
 | 
			
		||||
 | 
			
		||||
                    receivedCloseCode = msg->closeInfo.code;
 | 
			
		||||
                    receivedCloseReason = std::string(msg->closeInfo.reason);
 | 
			
		||||
                    receivedCloseRemote = msg->closeInfo.remote;
 | 
			
		||||
                }
 | 
			
		||||
                        receivedCloseCode = msg->closeInfo.code;
 | 
			
		||||
                        receivedCloseReason = std::string(msg->closeInfo.reason);
 | 
			
		||||
                        receivedCloseRemote = msg->closeInfo.remote;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        auto res = server.listen();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,183 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,76 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  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
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        WebSocketClient(int port);
 | 
			
		||||
        WebSocketClient(int port, bool useHeartBeatMethod);
 | 
			
		||||
 | 
			
		||||
        void start();
 | 
			
		||||
        void stop();
 | 
			
		||||
@@ -29,10 +29,12 @@ namespace
 | 
			
		||||
    private:
 | 
			
		||||
        ix::WebSocket _webSocket;
 | 
			
		||||
        int _port;
 | 
			
		||||
        bool _useHeartBeatMethod;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    WebSocketClient::WebSocketClient(int port)
 | 
			
		||||
    WebSocketClient::WebSocketClient(int port, bool useHeartBeatMethod)
 | 
			
		||||
        : _port(port)
 | 
			
		||||
        , _useHeartBeatMethod(useHeartBeatMethod)
 | 
			
		||||
    {
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
@@ -61,37 +63,49 @@ namespace
 | 
			
		||||
 | 
			
		||||
        // The important bit for this test.
 | 
			
		||||
        // Set a 1 second heartbeat with the setter method to test
 | 
			
		||||
        _webSocket.setPingInterval(1);
 | 
			
		||||
        if (_useHeartBeatMethod)
 | 
			
		||||
        {
 | 
			
		||||
            _webSocket.setPingInterval(1);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            _webSocket.setPingInterval(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        log(std::string("Connecting to url: ") + url);
 | 
			
		||||
 | 
			
		||||
        _webSocket.setOnMessageCallback([](const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
        _webSocket.setOnMessageCallback([](ix::WebSocketMessageType messageType,
 | 
			
		||||
                                           const std::string& str,
 | 
			
		||||
                                           size_t wireSize,
 | 
			
		||||
                                           const ix::WebSocketErrorInfo& error,
 | 
			
		||||
                                           const ix::WebSocketOpenInfo& openInfo,
 | 
			
		||||
                                           const ix::WebSocketCloseInfo& closeInfo) {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
            if (messageType == ix::WebSocketMessageType::Open)
 | 
			
		||||
            {
 | 
			
		||||
                log("client connected");
 | 
			
		||||
            }
 | 
			
		||||
            else if (msg->type == ix::WebSocketMessageType::Close)
 | 
			
		||||
            else if (messageType == ix::WebSocketMessageType::Close)
 | 
			
		||||
            {
 | 
			
		||||
                log("client disconnected");
 | 
			
		||||
            }
 | 
			
		||||
            else if (msg->type == ix::WebSocketMessageType::Error)
 | 
			
		||||
            else if (messageType == ix::WebSocketMessageType::Error)
 | 
			
		||||
            {
 | 
			
		||||
                ss << "Error ! " << msg->errorInfo.reason;
 | 
			
		||||
                ss << "Error ! " << error.reason;
 | 
			
		||||
                log(ss.str());
 | 
			
		||||
            }
 | 
			
		||||
            else if (msg->type == ix::WebSocketMessageType::Pong)
 | 
			
		||||
            else if (messageType == ix::WebSocketMessageType::Pong)
 | 
			
		||||
            {
 | 
			
		||||
                ss << "Received pong message " << msg->str;
 | 
			
		||||
                ss << "Received pong message " << str;
 | 
			
		||||
                log(ss.str());
 | 
			
		||||
            }
 | 
			
		||||
            else if (msg->type == ix::WebSocketMessageType::Ping)
 | 
			
		||||
            else if (messageType == ix::WebSocketMessageType::Ping)
 | 
			
		||||
            {
 | 
			
		||||
                ss << "Received ping message " << msg->str;
 | 
			
		||||
                ss << "Received ping message " << str;
 | 
			
		||||
                log(ss.str());
 | 
			
		||||
            }
 | 
			
		||||
            else if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
            else if (messageType == ix::WebSocketMessageType::Message)
 | 
			
		||||
            {
 | 
			
		||||
                // too many messages to log
 | 
			
		||||
            }
 | 
			
		||||
@@ -118,28 +132,33 @@ namespace
 | 
			
		||||
                                             std::shared_ptr<ConnectionState> connectionState) {
 | 
			
		||||
                webSocket->setOnMessageCallback(
 | 
			
		||||
                    [webSocket, connectionState, &server, &receivedPingMessages](
 | 
			
		||||
                        const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                        if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                        ix::WebSocketMessageType messageType,
 | 
			
		||||
                        const std::string& str,
 | 
			
		||||
                        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() << "id: " << connectionState->getId();
 | 
			
		||||
                            TLogger() << "Uri: " << msg->openInfo.uri;
 | 
			
		||||
                            TLogger() << "Uri: " << openInfo.uri;
 | 
			
		||||
                            TLogger() << "Headers:";
 | 
			
		||||
                            for (auto it : msg->openInfo.headers)
 | 
			
		||||
                            for (auto it : openInfo.headers)
 | 
			
		||||
                            {
 | 
			
		||||
                                TLogger() << it.first << ": " << it.second;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (msg->type == ix::WebSocketMessageType::Close)
 | 
			
		||||
                        else if (messageType == ix::WebSocketMessageType::Close)
 | 
			
		||||
                        {
 | 
			
		||||
                            log("Server closed connection");
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (msg->type == ix::WebSocketMessageType::Ping)
 | 
			
		||||
                        else if (messageType == ix::WebSocketMessageType::Ping)
 | 
			
		||||
                        {
 | 
			
		||||
                            log("Server received a ping");
 | 
			
		||||
                            receivedPingMessages++;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
                        else if (messageType == ix::WebSocketMessageType::Message)
 | 
			
		||||
                        {
 | 
			
		||||
                            // to many messages to log
 | 
			
		||||
                            for (auto client : server.getClients())
 | 
			
		||||
@@ -174,7 +193,8 @@ TEST_CASE("Websocket_ping_no_data_sent_setPingInterval", "[setPingInterval]")
 | 
			
		||||
        REQUIRE(startServer(server, serverReceivedPingMessages));
 | 
			
		||||
 | 
			
		||||
        std::string session = ix::generateSessionId();
 | 
			
		||||
        WebSocketClient webSocketClient(port);
 | 
			
		||||
        bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval
 | 
			
		||||
        WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
 | 
			
		||||
 | 
			
		||||
        webSocketClient.start();
 | 
			
		||||
 | 
			
		||||
@@ -216,7 +236,8 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval", "[setPingInterval]")
 | 
			
		||||
        REQUIRE(startServer(server, serverReceivedPingMessages));
 | 
			
		||||
 | 
			
		||||
        std::string session = ix::generateSessionId();
 | 
			
		||||
        WebSocketClient webSocketClient(port);
 | 
			
		||||
        bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval
 | 
			
		||||
        WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
 | 
			
		||||
 | 
			
		||||
        webSocketClient.start();
 | 
			
		||||
 | 
			
		||||
@@ -240,7 +261,7 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval", "[setPingInterval]")
 | 
			
		||||
        // Here we test ping interval
 | 
			
		||||
        // 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
 | 
			
		||||
        REQUIRE(serverReceivedPingMessages >= 2);
 | 
			
		||||
        REQUIRE(serverReceivedPingMessages == 3);
 | 
			
		||||
 | 
			
		||||
        // Give us 1000ms for the server to notice that clients went away
 | 
			
		||||
        ix::msleep(1000);
 | 
			
		||||
@@ -263,7 +284,8 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval_half_full", "[setPingInterva
 | 
			
		||||
        REQUIRE(startServer(server, serverReceivedPingMessages));
 | 
			
		||||
 | 
			
		||||
        std::string session = ix::generateSessionId();
 | 
			
		||||
        WebSocketClient webSocketClient(port);
 | 
			
		||||
        bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval
 | 
			
		||||
        WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
 | 
			
		||||
 | 
			
		||||
        webSocketClient.start();
 | 
			
		||||
 | 
			
		||||
@@ -316,7 +338,8 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval_full", "[setPingInterval]")
 | 
			
		||||
        REQUIRE(startServer(server, serverReceivedPingMessages));
 | 
			
		||||
 | 
			
		||||
        std::string session = ix::generateSessionId();
 | 
			
		||||
        WebSocketClient webSocketClient(port);
 | 
			
		||||
        bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval
 | 
			
		||||
        WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
 | 
			
		||||
 | 
			
		||||
        webSocketClient.start();
 | 
			
		||||
 | 
			
		||||
@@ -340,9 +363,8 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval_full", "[setPingInterval]")
 | 
			
		||||
 | 
			
		||||
        // Here we test ping interval
 | 
			
		||||
        // client has sent data, but ping should have been sent no matter what
 | 
			
		||||
        // -> expected ping messages == 2, 1 ping sent every second
 | 
			
		||||
        // The first ping is sent right away on connect
 | 
			
		||||
        REQUIRE(serverReceivedPingMessages == 2);
 | 
			
		||||
        // -> expected ping messages == 1, 1 ping sent every second
 | 
			
		||||
        REQUIRE(serverReceivedPingMessages == 1);
 | 
			
		||||
 | 
			
		||||
        ix::msleep(100);
 | 
			
		||||
 | 
			
		||||
@@ -370,7 +392,8 @@ TEST_CASE("Websocket_ping_no_data_sent_setHeartBeatPeriod", "[setPingInterval]")
 | 
			
		||||
        REQUIRE(startServer(server, serverReceivedPingMessages));
 | 
			
		||||
 | 
			
		||||
        std::string session = ix::generateSessionId();
 | 
			
		||||
        WebSocketClient webSocketClient(port);
 | 
			
		||||
        bool useSetHeartBeatPeriodMethod = true;
 | 
			
		||||
        WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
 | 
			
		||||
 | 
			
		||||
        webSocketClient.start();
 | 
			
		||||
 | 
			
		||||
@@ -383,13 +406,14 @@ TEST_CASE("Websocket_ping_no_data_sent_setHeartBeatPeriod", "[setPingInterval]")
 | 
			
		||||
 | 
			
		||||
        REQUIRE(server.getClients().size() == 1);
 | 
			
		||||
 | 
			
		||||
        ix::msleep(2100);
 | 
			
		||||
        ix::msleep(1900);
 | 
			
		||||
 | 
			
		||||
        webSocketClient.stop();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Here we test ping interval
 | 
			
		||||
        // -> expected ping messages == 2 as 2100 seconds, 1 ping sent every second
 | 
			
		||||
        REQUIRE(serverReceivedPingMessages == 2);
 | 
			
		||||
        // -> expected ping messages == 1 as 1900 seconds, 1 ping sent every second
 | 
			
		||||
        REQUIRE(serverReceivedPingMessages == 1);
 | 
			
		||||
 | 
			
		||||
        // Give us 1000ms for the server to notice that clients went away
 | 
			
		||||
        ix::msleep(1000);
 | 
			
		||||
@@ -412,7 +436,8 @@ TEST_CASE("Websocket_ping_data_sent_setHeartBeatPeriod", "[setPingInterval]")
 | 
			
		||||
        REQUIRE(startServer(server, serverReceivedPingMessages));
 | 
			
		||||
 | 
			
		||||
        std::string session = ix::generateSessionId();
 | 
			
		||||
        WebSocketClient webSocketClient(port);
 | 
			
		||||
        bool useSetHeartBeatPeriodMethod = true;
 | 
			
		||||
        WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
 | 
			
		||||
 | 
			
		||||
        webSocketClient.start();
 | 
			
		||||
 | 
			
		||||
@@ -439,7 +464,7 @@ TEST_CASE("Websocket_ping_data_sent_setHeartBeatPeriod", "[setPingInterval]")
 | 
			
		||||
        // Here we test ping interval
 | 
			
		||||
        // 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
 | 
			
		||||
        REQUIRE(serverReceivedPingMessages >= 2);
 | 
			
		||||
        REQUIRE(serverReceivedPingMessages == 2);
 | 
			
		||||
 | 
			
		||||
        // Give us 1000ms for the server to notice that clients went away
 | 
			
		||||
        ix::msleep(1000);
 | 
			
		||||
 
 | 
			
		||||
@@ -33,19 +33,15 @@ namespace ix
 | 
			
		||||
        };
 | 
			
		||||
        server.setConnectionStateFactory(factory);
 | 
			
		||||
 | 
			
		||||
        server.setOnClientMessageCallback(
 | 
			
		||||
            [&server, &connectionId](std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                                     ConnectionInfo& connectionInfo,
 | 
			
		||||
                                     WebSocket& webSocket,
 | 
			
		||||
                                     const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                auto remoteIp = connectionInfo.remoteIp;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        server.setOnConnectionCallback([&server, &connectionId](
 | 
			
		||||
                                           std::shared_ptr<ix::WebSocket> webSocket,
 | 
			
		||||
                                           std::shared_ptr<ConnectionState> connectionState) {
 | 
			
		||||
            webSocket->setOnMessageCallback([webSocket, connectionState, &connectionId, &server](
 | 
			
		||||
                                                const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                {
 | 
			
		||||
                    TLogger() << "New connection";
 | 
			
		||||
                    connectionState->computeId();
 | 
			
		||||
                    TLogger() << "remote ip: " << remoteIp;
 | 
			
		||||
                    TLogger() << "id: " << connectionState->getId();
 | 
			
		||||
                    TLogger() << "Uri: " << msg->openInfo.uri;
 | 
			
		||||
                    TLogger() << "Headers:";
 | 
			
		||||
@@ -64,13 +60,14 @@ namespace ix
 | 
			
		||||
                {
 | 
			
		||||
                    for (auto&& client : server.getClients())
 | 
			
		||||
                    {
 | 
			
		||||
                        if (client.get() != &webSocket)
 | 
			
		||||
                        if (client != webSocket)
 | 
			
		||||
                        {
 | 
			
		||||
                            client->send(msg->str, msg->binary);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        auto res = server.listen();
 | 
			
		||||
        if (!res.first)
 | 
			
		||||
 
 | 
			
		||||
@@ -16,40 +16,39 @@ using namespace ix;
 | 
			
		||||
 | 
			
		||||
bool startServer(ix::WebSocketServer& server, std::string& subProtocols)
 | 
			
		||||
{
 | 
			
		||||
    server.setOnClientMessageCallback(
 | 
			
		||||
        [&server, &subProtocols](std::shared_ptr<ConnectionState> connectionState,
 | 
			
		||||
                                 ConnectionInfo& connectionInfo,
 | 
			
		||||
                                 WebSocket& webSocket,
 | 
			
		||||
                                 const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
            auto remoteIp = connectionInfo.remoteIp;
 | 
			
		||||
            if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
            {
 | 
			
		||||
                TLogger() << "New connection";
 | 
			
		||||
                TLogger() << "remote ip: " << remoteIp;
 | 
			
		||||
                TLogger() << "id: " << connectionState->getId();
 | 
			
		||||
                TLogger() << "Uri: " << msg->openInfo.uri;
 | 
			
		||||
                TLogger() << "Headers:";
 | 
			
		||||
                for (auto it : msg->openInfo.headers)
 | 
			
		||||
    server.setOnConnectionCallback(
 | 
			
		||||
        [&server, &subProtocols](std::shared_ptr<ix::WebSocket> webSocket,
 | 
			
		||||
                                 std::shared_ptr<ConnectionState> connectionState) {
 | 
			
		||||
            webSocket->setOnMessageCallback([webSocket, connectionState, &server, &subProtocols](
 | 
			
		||||
                                                const ix::WebSocketMessagePtr& msg) {
 | 
			
		||||
                if (msg->type == ix::WebSocketMessageType::Open)
 | 
			
		||||
                {
 | 
			
		||||
                    TLogger() << it.first << ": " << it.second;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                subProtocols = msg->openInfo.headers["Sec-WebSocket-Protocol"];
 | 
			
		||||
            }
 | 
			
		||||
            else if (msg->type == ix::WebSocketMessageType::Close)
 | 
			
		||||
            {
 | 
			
		||||
                log("Closed connection");
 | 
			
		||||
            }
 | 
			
		||||
            else if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
            {
 | 
			
		||||
                for (auto&& client : server.getClients())
 | 
			
		||||
                {
 | 
			
		||||
                    if (client.get() != &webSocket)
 | 
			
		||||
                    TLogger() << "New connection";
 | 
			
		||||
                    TLogger() << "id: " << connectionState->getId();
 | 
			
		||||
                    TLogger() << "Uri: " << msg->openInfo.uri;
 | 
			
		||||
                    TLogger() << "Headers:";
 | 
			
		||||
                    for (auto it : msg->openInfo.headers)
 | 
			
		||||
                    {
 | 
			
		||||
                        client->sendBinary(msg->str);
 | 
			
		||||
                        TLogger() << it.first << ": " << it.second;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    subProtocols = msg->openInfo.headers["Sec-WebSocket-Protocol"];
 | 
			
		||||
                }
 | 
			
		||||
                else if (msg->type == ix::WebSocketMessageType::Close)
 | 
			
		||||
                {
 | 
			
		||||
                    log("Closed connection");
 | 
			
		||||
                }
 | 
			
		||||
                else if (msg->type == ix::WebSocketMessageType::Message)
 | 
			
		||||
                {
 | 
			
		||||
                    for (auto&& client : server.getClients())
 | 
			
		||||
                    {
 | 
			
		||||
                        if (client != webSocket)
 | 
			
		||||
                        {
 | 
			
		||||
                            client->sendBinary(msg->str);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    auto res = server.listen();
 | 
			
		||||
 
 | 
			
		||||
@@ -10,18 +10,10 @@
 | 
			
		||||
#include <ixwebsocket/IXNetSystem.h>
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    ix::initNetSystem();
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
    signal(SIGPIPE, SIG_IGN);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    ix::CoreLogger::LogFunc logFunc = [](const char* msg, ix::LogLevel level) {
 | 
			
		||||
        switch (level)
 | 
			
		||||
        {
 | 
			
		||||
@@ -57,7 +49,6 @@ int main(int argc, char* argv[])
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ix::CoreLogger::setLogFunction(logFunc);
 | 
			
		||||
    spdlog::set_level(spdlog::level::debug);
 | 
			
		||||
 | 
			
		||||
    int result = Catch::Session().run(argc, argv);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2428
									
								
								third_party/cpp-linenoise/linenoise.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2428
									
								
								third_party/cpp-linenoise/linenoise.cpp
									
									
									
									
										vendored
									
									
								
							
										
											
												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
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								third_party/zlib/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
*.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
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								third_party/zlib/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,227 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										1515
									
								
								third_party/zlib/ChangeLog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										368
									
								
								third_party/zlib/FAQ
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										368
									
								
								third_party/zlib/FAQ
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,368 @@
 | 
			
		||||
 | 
			
		||||
                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
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								third_party/zlib/INDEX
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								third_party/zlib/Makefile
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
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