Compare commits

..

63 Commits

Author SHA1 Message Date
80432edbd0 add 2 new ubuntu docker files 2020-11-15 21:26:59 -08:00
23606b45c7 C++11 compatible 2020-11-15 21:09:58 -08:00
2aac0afca3 compile attempt 2 with old OpenSSL versions 2020-11-15 11:32:50 -08:00
508d8c7253 compile attempt with old OpenSSL versions 2020-11-15 11:23:44 -08:00
8f5134528b (ixwebsocket) use a C++11 compatible make_unique shim 2020-11-15 09:56:54 -08:00
738c6040f7 fix memory leak in dns unittest 2020-11-12 13:07:31 -08:00
1350e9b307 missing vector header 2020-11-11 21:47:07 -08:00
4e2a40e031 (socket) replace a std::vector with an std::array used as a tmp buffer in Socket::readBytes 2020-11-11 21:39:31 -08:00
594d2e194a linux asan / run test in verbose mode 2020-11-11 11:32:47 -08:00
977a1ed7e1 link ordering fix for Linux 2020-11-11 19:23:51 +00:00
8b3789af56 linux build fix attempt 2020-11-11 11:16:19 -08:00
f60485d9c2 use ctest for testing 2020-11-11 11:11:34 -08:00
b05b124cb3 update readme 2020-11-11 09:20:42 -08:00
723c208f22 fix version 2020-11-11 09:18:03 -08:00
21758f1183 (openssl security fix) in the client to server connection, peer verification is not done in all cases. See https://github.com/machinezone/IXWebSocket/pull/250 2020-11-11 09:16:14 -08:00
422febf15d (openssl) Always set verify peer when it is not disabled (#250) 2020-11-11 09:12:39 -08:00
51ec32405d (docker) build docker container with zlib disabled 2020-11-07 11:22:52 -08:00
6a90dc7259 (cmake) DEFLATE -> Deflate in CMake to stop warnings about casing 2020-11-07 09:40:54 -08:00
262f32857f (ws autoroute) Display result in compliant way (AUTOROUTE IXWebSocket :: N ms) so that result can be parsed easily 2020-11-07 09:34:54 -08:00
91fb3992ac (ws gunzip + IXGZipCodec) Can decompress gziped data with libdeflate. ws gunzip computed output filename was incorrect (was the extension aka gz) instead of the file without the extension. Also check whether the output file is writeable. 2020-11-07 09:34:54 -08:00
e8b12feaeb Update README.md (#249) 2020-11-01 18:17:12 -08:00
730fbc5b31 unity build fixes 2020-10-26 19:18:55 -07:00
d0562664ad (http code) With zlib disabled, some code should not be reached 2020-10-19 13:37:42 -07:00
d9b4beff8b Fix an issue with disabling zlib and getting linker errors from the http client. (#247)
* (http client) #ifdefs so we dont try to compress http requests with zlib

* (http client) Remove some #ifdefs for including zlib and removing fields
2020-10-19 13:36:04 -07:00
b2f21840c6 (ws curl) Add support for --data-binary option, to set the request body. When present the request will be sent with the POST verb 2020-10-12 14:03:01 -07:00
67cb48537a (http client + server + ws) Add support for compressing http client requests with gzip. --compress_request argument is used in ws to enable this. The Content-Encoding is set to gzip, and decoded on the server side if present. 2020-10-09 17:51:56 -07:00
fa0408e70b (http client + server + ws) Add support for uploading files with ws -F foo=@filename, new -D http server option to debug incoming client requests, internal api changed for http POST, PUT and PATCH to supply an HttpFormDataParameters 2020-10-08 12:43:18 -07:00
032ed9af9c IXExponentialBackoff.cpp: fix typo in source code file name in the header block 2020-10-05 10:39:11 -07:00
dc84080401 Add support for gzip compression through libdeflate 2020-09-30 14:34:03 -07:00
82e759732b (cmake) Stop using FetchContent cmake module to retrieve jsoncpp third party dependency 2020-09-30 14:24:04 -07:00
61dbcc2b84 fix docker and linux build 2020-09-28 11:56:49 -07:00
e61680ff0f linux build fix about memset not being found 2020-09-28 11:01:59 -07:00
6f188a5131 (ws) add gzip and gunzip ws sub commands 2020-09-28 10:19:27 -07:00
6077f86af8 (cmake) use FetchContent cmake module to retrieve jsoncpp third party dependency 2020-09-26 14:11:40 -07:00
93167e3917 cmake / move FetchContent spdlog to a single place 2020-09-26 13:55:03 -07:00
2526a94454 (cmake) use FetchContent cmake module to retrieve spdlog third party dependency 2020-09-26 13:51:19 -07:00
97cc543e53 (cobra connection) retrieve cobra server connection id from the cobra handshake message and display it in ws clients, metrics publisher and bots 2020-09-22 09:30:19 -07:00
62d220f49a (cobra 2 cobra) specify as an HTTP header which channel we will republish to 2020-09-22 08:55:21 -07:00
49995e32f0 (cobra bots) change an error log to a warning log when reconnecting because no messages were received for a minute 2020-09-18 15:25:10 -07:00
d525c28907 (cobra connection and bots) set an HTTP header when connecting to help with debugging bots 2020-09-18 15:11:20 -07:00
39c84c7d51 Rename HttpResponse's payload to body (#245)
* rename payload to body

* Fixed ws cmd line tool to use the renamed body

Co-authored-by: Jay <jasoncarr@Jasons-MacBook-Pro.local>
2020-09-12 19:01:37 -07:00
128bc0afa9 (http server) read body request when the Content-Length is specified + set timeout to read the request to 30 seconds max by default, and make it configurable as a constructor parameter 2020-09-12 14:17:06 -07:00
b04e5c5529 http server: use socket->readBytes which reads in bulk instead of N calls to socket->readByte 2020-09-12 14:09:25 -07:00
1e8c421d66 formatting 2020-09-12 13:55:27 -07:00
72d6651ded Read body in parseRequest for HttpServer (#244)
Co-authored-by: Jay <jasoncarr@Jasons-MacBook-Pro.local>
2020-09-12 13:53:56 -07:00
a4e5d1b47a (ws) autoroute command exit on its own once all messages have been received 2020-09-09 18:01:38 -07:00
9f51a54a83 (docker) ws docker file installs strace 2020-09-04 13:47:12 -07:00
b74f7319c6 add a note to the readme about the fact that the MinGW compiler is not supported. close #242 2020-09-03 13:50:46 -07:00
0ad66a27f2 Fix ws/ws.cpp:2875:10: warning: unused variable noSend [-Wunused-variable] 2020-09-03 09:17:52 -07:00
a40003e85a (ws) echo_client command renamed to autoroute. Command exit once the server close the connection. push_server commands exit once N messages have been sent. 2020-09-03 09:13:23 -07:00
5534a7fdf9 add a github action to publish a docker container for ws 2020-09-02 11:52:59 -07:00
efb245278d unittest / switch from using the REQUIRE macro, which halts (and usually crash) the test to the CHECK macro in IXWebSocketChatTest.cpp 2020-08-31 13:56:45 -07:00
5896d3740f (ws + cobra bots) add a cobra_to_cobra ws subcommand to subscribe to a channel and republish received events to a different channel 2020-08-31 13:45:00 -07:00
73b9c0b89b (socket servers) merge the ConnectionInfo class with the ConnectionState one, which simplify all the server apis 2020-08-28 14:55:40 -07:00
629c155044 (ws) fix silly compile error (missing ix:: namespace) 2020-08-26 14:30:58 -07:00
08640d877f (ws) set the main thread name, to help with debugging in XCode, gdb, lldb etc... 2020-08-26 13:38:45 -07:00
ed5c63144e (ws) cobra to python bot / take a module python name as argument foo.bar.baz instead of a path foo/bar/baz.py 2020-08-19 10:00:00 -07:00
ee69aed2b0 (ws) on Linux with mbedtls, when the system ca certs are specified (the default) pick up sensible OS supplied paths (tested with CentOS and Alpine) 2020-08-19 09:31:57 -07:00
fcb92f862d (ws push_server) on the server side, stop sending and close the connection when the remote end has disconnected 2020-08-18 14:09:27 -07:00
e8e98e667d add ruby websocket bencharking code using
faye-websocket-ruby to receive messages as fast as possible
2020-08-18 13:45:53 -07:00
e1502017ce (ixwebsocket) replace std::unique_ptr<unsigned char[]> with std::array for some fixed arrays (which are in C++11) 2020-08-17 16:48:26 -07:00
72472f2899 IXWebSocketPerMessageDeflateCodec: use std::array instead of std::unique_ptr for a fixed size array 2020-08-17 16:36:24 -07:00
42f71364ca IXHttpClient.cpp: use std::array instead of std::unique_ptr for a fixed size array 2020-08-17 16:25:55 -07:00
251 changed files with 1897 additions and 43575 deletions

66
.github/workflows/docker.yml vendored Normal file
View File

@ -0,0 +1,66 @@
name: docker
# When its time to do a release do a build for amd64
# and push all of them to Docker Hub.
# Only trigger on semver shaped tags.
on:
push:
tags:
- "v*.*.*"
jobs:
login:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Prepare
id: prep
run: |
DOCKER_IMAGE=machinezone/ws
VERSION=edge
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
fi
if [ "${{ github.event_name }}" = "schedule" ]; then
VERSION=nightly
fi
TAGS="${DOCKER_IMAGE}:${VERSION}"
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
TAGS="$TAGS,${DOCKER_IMAGE}:latest"
fi
echo ::set-output name=tags::${TAGS}
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
- name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to GitHub Package Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2-build-push
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./Dockerfile
target: prod
platforms: linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.prep.outputs.tags }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache

View File

@ -9,5 +9,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: make test_make
run: make test_make
- uses: seanmiddleditch/gha-setup-ninja@master
- name: make test
run: make test

View File

@ -0,0 +1,14 @@
name: linux_asan
on:
push:
paths-ignore:
- 'docs/**'
jobs:
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master
- name: make test_asan
run: make test_asan

View File

@ -9,6 +9,7 @@ jobs:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master
- name: install mbedtls
run: brew install mbedtls
- name: make test

View File

@ -9,6 +9,7 @@ jobs:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master
- name: install openssl
run: brew install openssl@1.1
- name: make test

View File

@ -9,5 +9,6 @@ jobs:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: make test_tsan
run: make test_tsan
- uses: seanmiddleditch/gha-setup-ninja@master
- name: make test_tsan_sectransport
run: make test_tsan_sectransport

19
CMake/FindDeflate.cmake Normal file
View File

@ -0,0 +1,19 @@
# Find package structure taken from libcurl
include(FindPackageHandleStandardArgs)
find_path(DEFLATE_INCLUDE_DIRS libdeflate.h)
find_library(DEFLATE_LIBRARY deflate)
find_package_handle_standard_args(Deflate
FOUND_VAR
DEFLATE_FOUND
REQUIRED_VARS
DEFLATE_LIBRARY
DEFLATE_INCLUDE_DIRS
FAIL_MESSAGE
"Could NOT find deflate"
)
set(DEFLATE_INCLUDE_DIRS ${DEFLATE_INCLUDE_DIRS})
set(DEFLATE_LIBRARIES ${DEFLATE_LIBRARY})

View File

@ -8,7 +8,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
project(ixwebsocket C CXX)
set (CMAKE_CXX_STANDARD 14)
set (CMAKE_CXX_STANDARD 11)
set (CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF)
@ -31,6 +31,7 @@ set( IXWEBSOCKET_SOURCES
ixwebsocket/IXDNSLookup.cpp
ixwebsocket/IXExponentialBackoff.cpp
ixwebsocket/IXGetFreePort.cpp
ixwebsocket/IXGzipCodec.cpp
ixwebsocket/IXHttp.cpp
ixwebsocket/IXHttpClient.cpp
ixwebsocket/IXHttpServer.cpp
@ -44,6 +45,7 @@ set( IXWEBSOCKET_SOURCES
ixwebsocket/IXSocketFactory.cpp
ixwebsocket/IXSocketServer.cpp
ixwebsocket/IXSocketTLSOptions.cpp
ixwebsocket/IXStrCaseCompare.cpp
ixwebsocket/IXUdpSocket.cpp
ixwebsocket/IXUrlParser.cpp
ixwebsocket/IXUserAgent.cpp
@ -62,11 +64,11 @@ 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/IXGzipCodec.h
ixwebsocket/IXHttp.h
ixwebsocket/IXHttpClient.h
ixwebsocket/IXHttpServer.h
@ -81,6 +83,7 @@ set( IXWEBSOCKET_HEADERS
ixwebsocket/IXSocketFactory.h
ixwebsocket/IXSocketServer.h
ixwebsocket/IXSocketTLSOptions.h
ixwebsocket/IXStrCaseCompare.h
ixwebsocket/IXUdpSocket.h
ixwebsocket/IXUrlParser.h
ixwebsocket/IXUtf8Validator.h
@ -201,6 +204,14 @@ if (USE_ZLIB)
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_ZLIB)
endif()
# brew install libdeflate
find_package(Deflate)
if (DEFLATE_FOUND)
include_directories(${DEFLATE_INCLUDE_DIRS})
target_link_libraries(ixwebsocket ${DEFLATE_LIBRARIES})
target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_DEFLATE)
endif()
if (WIN32)
target_link_libraries(ixwebsocket wsock32 ws2_32 shlwapi)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
@ -252,12 +263,19 @@ if (USE_WS OR USE_TEST)
add_subdirectory(ixsentry)
add_subdirectory(ixbots)
add_subdirectory(third_party/spdlog spdlog)
include(FetchContent)
FetchContent_Declare(spdlog
GIT_REPOSITORY "https://github.com/gabime/spdlog"
GIT_TAG "v1.8.0"
GIT_SHALLOW 1)
FetchContent_MakeAvailable(spdlog)
if (USE_WS)
add_subdirectory(ws)
add_subdirectory(ws)
endif()
if (USE_TEST)
add_subdirectory(test)
enable_testing()
add_subdirectory(test)
endif()
endif()

View File

@ -2,7 +2,9 @@
IXWebSocket is a C++ library for WebSocket client and server development. It has minimal dependencies (no boost), is very simple to use and support everything you'll likely need for websocket dev (SSL, deflate compression, compiles on most platforms, etc...). HTTP client and server code is also available, but it hasn't received as much testing.
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android). It was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Two important design goals are simplicity and correctness.
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android). It was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Note that the MinGW compiler is not supported at this point. Two important design goals are simplicity and correctness.
A bad security bug affecting users compiling with SSL enabled and OpenSSL as the backend was just fixed in newly released version 11.0.0. Please upgrade ! (more details in the [https://github.com/machinezone/IXWebSocket/pull/250](PR).
```cpp
/*
@ -84,23 +86,26 @@ If your company or project is using this library, feel free to open an issue or
- [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), an easy to use Discord-bot framework.
- [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod
- [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper
- [discord.cpp](https://github.com/luccanunes/discord.cpp), a discord library for making bots
## Continuous Integration
| OS | TLS | Sanitizer | Status |
|-------------------|-------------------|-------------------|-------------------|
| Linux | OpenSSL | None | [![Build2][1]][7] |
| macOS | Secure Transport | Thread Sanitizer | [![Build2][2]][7] |
| macOS | OpenSSL | Thread Sanitizer | [![Build2][3]][7] |
| macOS | MbedTLS | Thread Sanitizer | [![Build2][4]][7] |
| Windows | Disabled | None | [![Build2][5]][7] |
| UWP | Disabled | None | [![Build2][6]][7] |
| Linux | OpenSSL | None | [![Build2][1]][0] |
| macOS | Secure Transport | Thread Sanitizer | [![Build2][2]][0] |
| macOS | OpenSSL | Thread Sanitizer | [![Build2][3]][0] |
| macOS | MbedTLS | Thread Sanitizer | [![Build2][4]][0] |
| Windows | Disabled | None | [![Build2][5]][0] |
| UWP | Disabled | None | [![Build2][6]][0] |
| Linux | OpenSSL | Address Sanitizer | [![Build2][7]][0] |
[0]: https://github.com/machinezone/IXWebSocket
[1]: https://github.com/machinezone/IXWebSocket/workflows/linux/badge.svg
[2]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_sectransport/badge.svg
[3]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_openssl/badge.svg
[4]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_mbedtls/badge.svg
[5]: https://github.com/machinezone/IXWebSocket/workflows/windows/badge.svg
[6]: https://github.com/machinezone/IXWebSocket/workflows/uwp/badge.svg
[7]: https://github.com/machinezone/IXWebSocket
[7]: https://github.com/machinezone/IXWebSocket/workflows/linux_asan/badge.svg

View File

@ -1,67 +1,11 @@
version: "3"
version: "3.3"
services:
# snake:
# image: bsergean/ws:build
# entrypoint: ws snake --port 8767 --host 0.0.0.0 --redis_hosts redis1
# ports:
# - "8767:8767"
# networks:
# - ws-net
# depends_on:
# - redis1
push:
entrypoint: ws push_server --host 0.0.0.0
image: ${DOCKER_REPO}/ws:build
# proxy:
# image: bsergean/ws:build
# entrypoint: strace ws proxy_server --remote_host 'wss://cobra.addsrv.com' --host 0.0.0.0 --port 8765 -v
# ports:
# - "8765:8765"
# networks:
# - ws-net
#pyproxy:
# image: bsergean/ws_proxy:build
# entrypoint: /usr/bin/ws_proxy.py --remote_url 'wss://cobra.addsrv.com' --host 0.0.0.0 --port 8765
# ports:
# - "8765:8765"
# networks:
# - ws-net
# # ws:
# # security_opt:
# # - seccomp:unconfined
# # cap_add:
# # - SYS_PTRACE
# # stdin_open: true
# # tty: true
# # image: bsergean/ws:build
# # entrypoint: sh
# # networks:
# # - ws-net
# # depends_on:
# # - redis1
# #
# # redis1:
# # image: redis:alpine
# # networks:
# # - ws-net
# #
# # statsd:
# # image: jaconel/statsd
# # ports:
# # - "8125:8125"
# # environment:
# # - STATSD_DUMP_MSG=true
# # - GRAPHITE_HOST=127.0.0.1
# # networks:
# # - ws-net
compile:
image: alpine
entrypoint: sh
stdin_open: true
tty: true
volumes:
- /Users/bsergeant/src/foss:/home/bsergean/src/foss
networks:
ws-net:
autoroute:
entrypoint: ws autoroute ws://push:8008
image: ${DOCKER_REPO}/ws:build
depends_on:
- push

View File

@ -2,7 +2,7 @@ FROM alpine:3.12 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 python3-dev ninja git
RUN addgroup -S app && \
adduser -S -G app app && \
@ -20,7 +20,7 @@ RUN make ws_mbedtls_install && \
FROM alpine:3.12 as runtime
RUN apk add --no-cache libstdc++ mbedtls ca-certificates python3 && \
RUN apk add --no-cache libstdc++ mbedtls ca-certificates python3 strace && \
addgroup -S app && \
adduser -S -G app app

26
docker/Dockerfile.centos7 Normal file
View File

@ -0,0 +1,26 @@
FROM centos:7 as build
RUN yum install -y gcc-c++ make zlib-devel openssl-devel redhat-rpm-config
RUN groupadd app && useradd -g app app
RUN chown -R app:app /opt
RUN chown -R app:app /usr/local
WORKDIR /tmp
RUN curl -O https://cmake.org/files/v3.14/cmake-3.14.0-Linux-x86_64.tar.gz
RUN tar zxvf cmake-3.14.0-Linux-x86_64.tar.gz
RUN cp -rf cmake-3.14.0-Linux-x86_64/* /usr/
RUN yum install -y git
# 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", "ws_no_python" ]
RUN [ "rm", "-rf", "build" ]
ENTRYPOINT ["ws"]
CMD ["--help"]

View File

@ -0,0 +1,27 @@
# Build time
FROM ubuntu:precise as build
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get -y install wget
RUN mkdir -p /tmp/cmake
WORKDIR /tmp/cmake
RUN wget --no-check-certificate https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN tar zxf cmake-3.14.0-Linux-x86_64.tar.gz
RUN apt-get -y install g++
RUN apt-get -y install libssl-dev
RUN apt-get -y install libz-dev
RUN apt-get -y install make
RUN apt-get -y install python
RUN apt-get -y install git
COPY . .
ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-Linux-x86_64/bin
ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
RUN ["make", "ws_no_python"]
ENTRYPOINT ["ws"]
CMD ["--help"]

View File

@ -0,0 +1,22 @@
# Build time
FROM ubuntu:trusty as build
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get -y install wget
RUN mkdir -p /tmp/cmake
WORKDIR /tmp/cmake
RUN wget --no-check-certificate https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
RUN tar zxf cmake-3.14.0-Linux-x86_64.tar.gz
RUN apt-get -y install g++ libssl-dev libz-dev make python git
COPY . .
ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-Linux-x86_64/bin
ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
RUN ["make", "ws_no_python"]
ENTRYPOINT ["ws"]
CMD ["--help"]

View File

@ -2,6 +2,130 @@
All changes to this project will be documented in this file.
## [11.0.2] - 2020-11-15
(ixwebsocket) use a C++11 compatible make_unique shim
## [11.0.1] - 2020-11-11
(socket) replace a std::vector with an std::array used as a tmp buffer in Socket::readBytes
## [11.0.0] - 2020-11-11
(openssl security fix) in the client to server connection, peer verification is not done in all cases. See https://github.com/machinezone/IXWebSocket/pull/250
## [10.5.7] - 2020-11-07
(docker) build docker container with zlib disabled
## [10.5.6] - 2020-11-07
(cmake) DEFLATE -> Deflate in CMake to stop warnings about casing
## [10.5.5] - 2020-11-07
(ws autoroute) Display result in compliant way (AUTOROUTE IXWebSocket :: N ms) so that result can be parsed easily
## [10.5.4] - 2020-10-30
(ws gunzip + IXGZipCodec) Can decompress gziped data with libdeflate. ws gunzip computed output filename was incorrect (was the extension aka gz) instead of the file without the extension. Also check whether the output file is writeable.
## [10.5.3] - 2020-10-19
(http code) With zlib disabled, some code should not be reached
## [10.5.2] - 2020-10-12
(ws curl) Add support for --data-binary option, to set the request body. When present the request will be sent with the POST verb
## [10.5.1] - 2020-10-09
(http client + server + ws) Add support for compressing http client requests with gzip. --compress_request argument is used in ws to enable this. The Content-Encoding is set to gzip, and decoded on the server side if present.
## [10.5.0] - 2020-09-30
(http client + server + ws) Add support for uploading files with ws -F foo=@filename, new -D http server option to debug incoming client requests, internal api changed for http POST, PUT and PATCH to supply an HttpFormDataParameters
## [10.4.9] - 2020-09-30
(http server + utility code) Add support for doing gzip compression with libdeflate library, if available
## [10.4.8] - 2020-09-30
(cmake) Stop using FetchContent cmake module to retrieve jsoncpp third party dependency
## [10.4.7] - 2020-09-28
(ws) add gzip and gunzip ws sub commands
## [10.4.6] - 2020-09-26
(cmake) use FetchContent cmake module to retrieve jsoncpp third party dependency
## [10.4.5] - 2020-09-26
(cmake) use FetchContent cmake module to retrieve spdlog third party dependency
## [10.4.4] - 2020-09-22
(cobra connection) retrieve cobra server connection id from the cobra handshake message and display it in ws clients, metrics publisher and bots
## [10.4.3] - 2020-09-22
(cobra 2 cobra) specify as an HTTP header which channel we will republish to
## [10.4.2] - 2020-09-18
(cobra bots) change an error log to a warning log when reconnecting because no messages were received for a minute
## [10.4.1] - 2020-09-18
(cobra connection and bots) set an HTTP header when connecting to help with debugging bots
## [10.4.0] - 2020-09-12
(http server) read body request when the Content-Length is specified + set timeout to read the request to 30 seconds max by default, and make it configurable as a constructor parameter
## [10.3.5] - 2020-09-09
(ws) autoroute command exit on its own once all messages have been received
## [10.3.4] - 2020-09-04
(docker) ws docker file installs strace
## [10.3.3] - 2020-09-02
(ws) echo_client command renamed to autoroute. Command exit once the server close the connection. push_server commands exit once N messages have been sent.
## [10.3.2] - 2020-08-31
(ws + cobra bots) add a cobra_to_cobra ws subcommand to subscribe to a channel and republish received events to a different channel
## [10.3.1] - 2020-08-28
(socket servers) merge the ConnectionInfo class with the ConnectionState one, which simplify all the server apis
## [10.3.0] - 2020-08-26
(ws) set the main thread name, to help with debugging in XCode, gdb, lldb etc...
## [10.2.9] - 2020-08-19
(ws) cobra to python bot / take a module python name as argument foo.bar.baz instead of a path foo/bar/baz.py
## [10.2.8] - 2020-08-19
(ws) on Linux with mbedtls, when the system ca certs are specified (the default) pick up sensible OS supplied paths (tested with CentOS and Alpine)
## [10.2.7] - 2020-08-18
(ws push_server) on the server side, stop sending and close the connection when the remote end has disconnected
## [10.2.6] - 2020-08-17
(ixwebsocket) replace std::unique_ptr<unsigned char[]> with std::array for some fixed arrays (which are in C++11)
## [10.2.5] - 2020-08-15
(ws) merge all ws_*.cpp files into a single one to speedup compilation

View File

@ -280,10 +280,9 @@ ix::WebSocketServer server(port);
server.setOnConnectionCallback(
[&server](std::weak_ptr<WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState,
std::unique_ptr<ConnectionInfo> connectionInfo)
std::shared_ptr<ConnectionState> connectionState)
{
std::cout << "Remote ip: " << connectionInfo->remoteIp << std::endl;
std::cout << "Remote ip: " << connectionState->remoteIp << std::endl;
auto ws = webSocket.lock();
if (ws)
@ -359,13 +358,12 @@ The webSocket reference is guaranteed to be always valid ; by design the callbac
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,
// The ConnectionState 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;
std::cout << "Remote ip: " << connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Open)
{
@ -460,18 +458,25 @@ out = httpClient.get(url, args);
// POST request with parameters
HttpParameters httpParameters;
httpParameters["foo"] = "bar";
out = httpClient.post(url, httpParameters, args);
// HTTP form data can be passed in as well, for multi-part upload of files
HttpFormDataParameters httpFormDataParameters;
httpParameters["baz"] = "booz";
out = httpClient.post(url, httpParameters, httpFormDataParameters, args);
// POST request with a body
out = httpClient.post(url, std::string("foo=bar"), args);
// PUT and PATCH are available too.
//
// Result
//
auto statusCode = response->statusCode; // Can be HttpErrorCode::Ok, HttpErrorCode::UrlMalformed, etc...
auto errorCode = response->errorCode; // 200, 404, etc...
auto responseHeaders = response->headers; // All the headers in a special case-insensitive unordered_map of (string, string)
auto payload = response->payload; // All the bytes from the response as an std::string
auto body = response->body; // All the bytes from the response as an std::string
auto errorMsg = response->errorMsg; // Descriptive error message in case of failure
auto uploadSize = response->uploadSize; // Byte count of uploaded data
auto downloadSize = response->downloadSize; // Byte count of downloaded data
@ -519,12 +524,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
ss << connectionState->getRemoteIp();
<< " "
<< request->method
<< " "

View File

@ -5,6 +5,7 @@
set (IXBOTS_SOURCES
ixbots/IXCobraBot.cpp
ixbots/IXCobraToCobraBot.cpp
ixbots/IXCobraToSentryBot.cpp
ixbots/IXCobraToStatsdBot.cpp
ixbots/IXCobraToStdoutBot.cpp
@ -16,6 +17,7 @@ set (IXBOTS_SOURCES
set (IXBOTS_HEADERS
ixbots/IXCobraBot.h
ixbots/IXCobraBotConfig.h
ixbots/IXCobraToCobraBot.h
ixbots/IXCobraToSentryBot.h
ixbots/IXCobraToStatsdBot.h
ixbots/IXCobraToStdoutBot.h

View File

@ -31,6 +31,8 @@ namespace ix
auto limitReceivedEvents = botConfig.limitReceivedEvents;
auto batchSize = botConfig.batchSize;
config.headers["X-Cobra-Channel"] = channel;
ix::CobraConnection conn;
conn.configure(config);
conn.connect();
@ -128,7 +130,7 @@ namespace ix
ss << "no messages received or sent for "
<< heartBeatTimeout << " seconds, reconnecting";
CoreLogger::error(ss.str());
CoreLogger::warn(ss.str());
stalledConnection = true;
}
state = currentState;
@ -168,7 +170,11 @@ namespace ix
}
else if (event->type == ix::CobraEventType::Closed)
{
CoreLogger::info("Subscriber closed: {}" + event->errMsg);
CoreLogger::info("Subscriber closed: " + event->errMsg);
}
else if (event->type == ix::CobraEventType::Handshake)
{
CoreLogger::info("Subscriber: Cobra handshake connection id: " + event->connectionId);
}
else if (event->type == ix::CobraEventType::Authenticated)
{

View File

@ -0,0 +1,45 @@
/*
* IXCobraToCobraBot.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#include "IXCobraToCobraBot.h"
#include "IXCobraBot.h"
#include <ixcobra/IXCobraMetricsPublisher.h>
#include <sstream>
namespace ix
{
int64_t cobra_to_cobra_bot(const ix::CobraBotConfig& cobraBotConfig,
const std::string& republishChannel,
const std::string& publisherRolename,
const std::string& publisherRolesecret)
{
CobraBot bot;
CobraMetricsPublisher cobraMetricsPublisher;
CobraConfig cobraPublisherConfig = cobraBotConfig.cobraConfig;
cobraPublisherConfig.rolename = publisherRolename;
cobraPublisherConfig.rolesecret = publisherRolesecret;
cobraPublisherConfig.headers["X-Cobra-Republish-Channel"] = republishChannel;
cobraMetricsPublisher.configure(cobraPublisherConfig, republishChannel);
bot.setOnBotMessageCallback(
[&republishChannel, &cobraMetricsPublisher](const Json::Value& msg,
const std::string& /*position*/,
std::atomic<bool>& /*throttled*/,
std::atomic<bool>& /*fatalCobraError*/,
std::atomic<uint64_t>& sentCount) -> void {
Json::Value msgWithNoId(msg);
msgWithNoId.removeMember("id");
cobraMetricsPublisher.push(republishChannel, msg);
sentCount++;
});
return bot.run(cobraBotConfig);
}
} // namespace ix

View File

@ -0,0 +1,20 @@
/*
* IXCobraToCobraBot.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_cobra_bot(const ix::CobraBotConfig& config,
const std::string& republishChannel,
const std::string& publisherRolename,
const std::string& publisherRolesecret);
} // namespace ix

View File

@ -102,7 +102,7 @@ namespace ix
{
int64_t cobra_to_python_bot(const ix::CobraBotConfig& config,
StatsdClient& statsdClient,
const std::string& scriptPath)
const std::string& moduleName)
{
#ifndef IXBOTS_USE_PYTHON
CoreLogger::error("Command is disabled. "
@ -113,10 +113,7 @@ namespace ix
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());
PyObject* pyModuleName = PyUnicode_DecodeFSDefault(moduleName.c_str());
if (pyModuleName == nullptr)
{

View File

@ -15,5 +15,5 @@ namespace ix
{
int64_t cobra_to_python_bot(const ix::CobraBotConfig& config,
StatsdClient& statsdClient,
const std::string& scriptPath);
const std::string& moduleName);
} // namespace ix

View File

@ -41,7 +41,7 @@ namespace ix
else
{
CoreLogger::error("Error sending data to sentry: " + std::to_string(response->statusCode));
CoreLogger::error("Response: " + response->payload);
CoreLogger::error("Response: " + response->body);
// Error 429 Too Many Requests
if (response->statusCode == 429)

View File

@ -8,6 +8,7 @@
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
namespace ix
{
@ -19,6 +20,7 @@ namespace ix
std::string rolesecret;
WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions;
SocketTLSOptions socketTLSOptions;
WebSocketHttpHeaders headers;
CobraConfig(const std::string& a = std::string(),
const std::string& e = std::string(),

View File

@ -14,6 +14,7 @@
#include <ixcrypto/IXHMac.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXUniquePtr.h>
#include <sstream>
#include <stdexcept>
@ -91,13 +92,14 @@ namespace ix
const std::string& errorMsg,
const WebSocketHttpHeaders& headers,
const std::string& subscriptionId,
CobraConnection::MsgId msgId)
CobraConnection::MsgId msgId,
const std::string& connectionId)
{
std::lock_guard<std::mutex> lock(_eventCallbackMutex);
if (_eventCallback)
{
_eventCallback(
std::make_unique<CobraEvent>(eventType, errorMsg, headers, subscriptionId, msgId));
ix::make_unique<CobraEvent>(eventType, errorMsg, headers, subscriptionId, msgId, connectionId));
}
}
@ -255,7 +257,8 @@ namespace ix
const std::string& rolename,
const std::string& rolesecret,
const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
const SocketTLSOptions& socketTLSOptions)
const SocketTLSOptions& socketTLSOptions,
const WebSocketHttpHeaders& headers)
{
_roleName = rolename;
_roleSecret = rolesecret;
@ -269,6 +272,7 @@ namespace ix
_webSocket->setUrl(url);
_webSocket->setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
_webSocket->setTLSOptions(socketTLSOptions);
_webSocket->setExtraHeaders(headers);
// Send a websocket ping every N seconds (N = 30) now
// This should keep the connection open and prevent some load balancers such as
@ -283,7 +287,8 @@ namespace ix
config.rolename,
config.rolesecret,
config.webSocketPerMessageDeflateOptions,
config.socketTLSOptions);
config.socketTLSOptions,
config.headers);
}
//
@ -349,6 +354,18 @@ namespace ix
if (!nonce.isString()) return false;
if (!data.isMember("connection_id")) return false;
Json::Value connectionId = data["connection_id"];
if (!connectionId.isString()) return false;
invokeEventCallback(ix::CobraEventType::Handshake,
std::string(),
WebSocketHttpHeaders(),
std::string(),
0,
connectionId.asString());
return sendAuthMessage(nonce.asString());
}

View File

@ -56,7 +56,8 @@ namespace ix
const std::string& rolename,
const std::string& rolesecret,
const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
const SocketTLSOptions& socketTLSOptions);
const SocketTLSOptions& socketTLSOptions,
const WebSocketHttpHeaders& headers);
void configure(const ix::CobraConfig& config);
@ -157,7 +158,9 @@ namespace ix
const std::string& errorMsg = std::string(),
const WebSocketHttpHeaders& headers = WebSocketHttpHeaders(),
const std::string& subscriptionId = std::string(),
uint64_t msgId = std::numeric_limits<uint64_t>::max());
uint64_t msgId = std::numeric_limits<uint64_t>::max(),
const std::string& connectionId = std::string());
void invokeErrorCallback(const std::string& errorMsg, const std::string& serializedPdu);
/// Tells whether the internal queue is empty or not

View File

@ -21,17 +21,20 @@ namespace ix
const ix::WebSocketHttpHeaders& headers;
const std::string& subscriptionId;
uint64_t msgId; // CobraConnection::MsgId
const std::string& connectionId;
CobraEvent(ix::CobraEventType t,
const std::string& e,
const ix::WebSocketHttpHeaders& h,
const std::string& s,
uint64_t m)
uint64_t m,
const std::string& c)
: type(t)
, errMsg(e)
, headers(h)
, subscriptionId(s)
, msgId(m)
, connectionId(c)
{
;
}

View File

@ -20,6 +20,7 @@ namespace ix
Pong = 7,
HandshakeError = 8,
AuthenticationError = 9,
SubscriptionError = 10
SubscriptionError = 10,
Handshake = 11
};
}

View File

@ -24,6 +24,7 @@ namespace ix
{
_cobra_connection.setEventCallback([](const CobraEventPtr& event) {
std::stringstream ss;
ix::LogLevel logLevel = LogLevel::Info;
if (event->type == ix::CobraEventType::Open)
{
@ -34,6 +35,10 @@ namespace ix
ss << it.first << ": " << it.second << std::endl;
}
}
else if (event->type == ix::CobraEventType::Handshake)
{
ss << "Cobra handshake connection id: " << event->connectionId;
}
else if (event->type == ix::CobraEventType::Authenticated)
{
ss << "Authenticated";
@ -41,6 +46,7 @@ namespace ix
else if (event->type == ix::CobraEventType::Error)
{
ss << "Error: " << event->errMsg;
logLevel = ix::LogLevel::Error;
}
else if (event->type == ix::CobraEventType::Closed)
{
@ -57,6 +63,7 @@ namespace ix
else if (event->type == ix::CobraEventType::Published)
{
ss << "Published message " << event->msgId << " acked";
logLevel = ix::LogLevel::Debug;
}
else if (event->type == ix::CobraEventType::Pong)
{
@ -65,17 +72,20 @@ namespace ix
else if (event->type == ix::CobraEventType::HandshakeError)
{
ss << "Handshake error: " << event->errMsg;
logLevel = ix::LogLevel::Error;
}
else if (event->type == ix::CobraEventType::AuthenticationError)
{
ss << "Authentication error: " << event->errMsg;
logLevel = ix::LogLevel::Error;
}
else if (event->type == ix::CobraEventType::SubscriptionError)
{
ss << "Subscription error: " << event->errMsg;
logLevel = ix::LogLevel::Error;
}
CoreLogger::log(ss.str().c_str());
CoreLogger::log(ss.str().c_str(), logLevel);
});
}

View File

@ -19,4 +19,16 @@ namespace ix
return hashAddress;
}
uint64_t djb2HashStr(const std::string& data)
{
uint64_t hashAddress = 5381;
for (size_t i = 0; i < data.size(); ++i)
{
hashAddress = ((hashAddress << 5) + hashAddress) + data[i];
}
return hashAddress;
}
} // namespace ix

View File

@ -8,8 +8,10 @@
#include <cstdint>
#include <vector>
#include <string>
namespace ix
{
uint64_t djb2Hash(const std::vector<uint8_t>& data);
uint64_t djb2HashStr(const std::string& data);
}

View File

@ -10,6 +10,7 @@
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <ixwebsocket/IXSocket.h>
namespace ix

View File

@ -45,10 +45,9 @@ 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);
logInfo("New connection from remote ip " + connectionState->getRemoteIp());
_connectedClientsCount++;

View File

@ -16,6 +16,7 @@
#include <string>
#include <thread>
#include <utility> // pair
#include <vector> // pair
namespace ix
{
@ -44,8 +45,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);

View File

@ -2,6 +2,17 @@
# Author: Benjamin Sergeant
# Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
#
include(CheckCSourceCompiles)
check_c_source_compiles("#include <regex>
int main()
{
const std::regex dsnRegex;
std::smatch group;
std::regex_match(std::string(), group, dsnRegex);
return 0;
}"
HAVE_STD_REGEX)
set (IXSENTRY_SOURCES
ixsentry/IXSentryClient.cpp
@ -28,3 +39,7 @@ set(IXSENTRY_INCLUDE_DIRS
${JSONCPP_INCLUDE_DIRS})
target_include_directories( ixsentry PUBLIC ${IXSENTRY_INCLUDE_DIRS} )
if (HAVE_STD_REGEX)
target_compile_definitions( ixsentry PUBLIC HAVE_STD_REGEX=1 )
endif()

View File

@ -20,9 +20,12 @@ namespace ix
SentryClient::SentryClient(const std::string& dsn)
: _dsn(dsn)
, _validDsn(false)
#ifdef HAVE_STD_REGEX
, _luaFrameRegex("\t([^/]+):([0-9]+): in function ['<]([^/]+)['>]")
#endif
, _httpClient(std::make_shared<HttpClient>(true))
{
#ifdef HAVE_STD_REGEX
const std::regex dsnRegex("(http[s]?)://([^:]+):([^@]+)@([^/]+)/([0-9]+)");
std::smatch group;
@ -38,6 +41,7 @@ namespace ix
_publicKey = group.str(2);
_secretKey = group.str(3);
}
#endif
}
void SentryClient::setTLSOptions(const SocketTLSOptions& tlsOptions)
@ -77,6 +81,7 @@ namespace ix
{
Json::Value frames;
#ifdef HAVE_STD_REGEX
// Split by lines
std::string line;
std::stringstream tokenStream(stack);
@ -107,6 +112,7 @@ namespace ix
}
std::reverse(frames.begin(), frames.end());
#endif
return frames;
}

View File

@ -11,7 +11,9 @@
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <json/json.h>
#include <memory>
#ifdef HAVE_STD_REGEX
#include <regex>
#endif
namespace ix
{
@ -62,7 +64,9 @@ namespace ix
Json::FastWriter _jsonWriter;
#ifdef HAVE_STD_REGEX
std::regex _luaFrameRegex;
#endif
std::shared_ptr<HttpClient> _httpClient;
};

View File

@ -13,6 +13,7 @@
#include <ixcore/utils/IXCoreLogger.h>
#include <ixcrypto/IXHMac.h>
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXUniquePtr.h>
#include <sstream>
namespace snake
@ -196,7 +197,7 @@ namespace snake
{
std::string filterStr = pdu["body"]["filter"];
}
state->streamSql = std::make_unique<StreamSql>(filterStr);
state->streamSql = ix::make_unique<StreamSql>(filterStr);
state->id = 0;
state->onRedisSubscribeCallback = [&ws, state](const std::string& messageStr) {
auto msg = nlohmann::json::parse(messageStr);

View File

@ -61,11 +61,10 @@ namespace snake
_server.setOnClientMessageCallback(
[this](std::shared_ptr<ix::ConnectionState> connectionState,
ix::ConnectionInfo& connectionInfo,
ix::WebSocket& webSocket,
const ix::WebSocketMessagePtr& msg) {
auto state = std::dynamic_pointer_cast<SnakeConnectionState>(connectionState);
auto remoteIp = connectionInfo.remoteIp;
auto remoteIp = connectionState->getRemoteIp();
std::stringstream ss;
ss << "[" << state->getId() << "] ";

View File

@ -12,10 +12,8 @@ namespace ix
{
Bench::Bench(const std::string& description)
: _description(description)
, _start(std::chrono::high_resolution_clock::now())
, _reported(false)
{
;
reset();
}
Bench::~Bench()
@ -26,19 +24,38 @@ namespace ix
}
}
void Bench::reset()
{
_start = std::chrono::high_resolution_clock::now();
_reported = false;
}
void Bench::report()
{
auto now = std::chrono::high_resolution_clock::now();
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - _start);
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - _start);
_ms = milliseconds.count();
std::cerr << _description << " completed in " << _ms << "ms" << std::endl;
_duration = microseconds.count();
std::cerr << _description << " completed in " << _duration << " us" << std::endl;
setReported();
}
void Bench::record()
{
auto now = std::chrono::high_resolution_clock::now();
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - _start);
_duration = microseconds.count();
}
void Bench::setReported()
{
_reported = true;
}
uint64_t Bench::getDuration() const
{
return _ms;
return _duration;
}
} // namespace ix

View File

@ -17,13 +17,16 @@ namespace ix
Bench(const std::string& description);
~Bench();
void reset();
void record();
void report();
void setReported();
uint64_t getDuration() const;
private:
std::string _description;
std::chrono::time_point<std::chrono::high_resolution_clock> _start;
uint64_t _ms;
uint64_t _duration;
bool _reported;
};
} // namespace ix

View File

@ -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

View File

@ -50,4 +50,24 @@ namespace ix
_onSetTerminatedCallback();
}
}
const std::string& ConnectionState::getRemoteIp()
{
return _remoteIp;
}
int ConnectionState::getRemotePort()
{
return _remotePort;
}
void ConnectionState::setRemoteIp(const std::string& remoteIp)
{
_remoteIp = remoteIp;
}
void ConnectionState::setRemotePort(int remotePort)
{
_remotePort = remotePort;
}
} // namespace ix

View File

@ -28,11 +28,17 @@ namespace ix
void setTerminated();
bool isTerminated() const;
const std::string& getRemoteIp();
int getRemotePort();
static std::shared_ptr<ConnectionState> createConnectionState();
private:
void setOnSetTerminatedCallback(const OnSetTerminatedCallback& callback);
void setRemoteIp(const std::string& remoteIp);
void setRemotePort(int remotePort);
protected:
std::atomic<bool> _terminated;
std::string _id;
@ -40,6 +46,9 @@ namespace ix
static std::atomic<uint64_t> _globalId;
std::string _remoteIp;
int _remotePort;
friend class SocketServer;
};
} // namespace ix

View File

@ -68,6 +68,11 @@ namespace ix
: resolveUnCancellable(errMsg, isCancellationRequested);
}
void DNSLookup::release(struct addrinfo* addr)
{
freeaddrinfo(addr);
}
struct addrinfo* DNSLookup::resolveUnCancellable(
std::string& errMsg, const CancellationRequest& isCancellationRequested)
{

View File

@ -31,6 +31,8 @@ namespace ix
const CancellationRequest& isCancellationRequested,
bool cancellable = true);
void release(struct addrinfo* addr);
private:
struct addrinfo* resolveCancellable(std::string& errMsg,
const CancellationRequest& isCancellationRequested);

View File

@ -1,5 +1,5 @@
/*
* IXExponentialBackoff.h
* IXExponentialBackoff.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/

183
ixwebsocket/IXGzipCodec.cpp Normal file
View File

@ -0,0 +1,183 @@
/*
* IXGzipCodec.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#include "IXGzipCodec.h"
#include "IXBench.h"
#include <array>
#include <string.h>
#ifdef IXWEBSOCKET_USE_ZLIB
#include <zlib.h>
#endif
#ifdef IXWEBSOCKET_USE_DEFLATE
#include <libdeflate.h>
#endif
namespace ix
{
std::string gzipCompress(const std::string& str)
{
#ifndef IXWEBSOCKET_USE_ZLIB
return std::string();
#else
#ifdef IXWEBSOCKET_USE_DEFLATE
int compressionLevel = 6;
struct libdeflate_compressor* compressor;
compressor = libdeflate_alloc_compressor(compressionLevel);
const void* uncompressed_data = str.data();
size_t uncompressed_size = str.size();
void* compressed_data;
size_t actual_compressed_size;
size_t max_compressed_size;
max_compressed_size = libdeflate_gzip_compress_bound(compressor, uncompressed_size);
compressed_data = malloc(max_compressed_size);
if (compressed_data == NULL)
{
return std::string();
}
actual_compressed_size = libdeflate_gzip_compress(
compressor, uncompressed_data, uncompressed_size, compressed_data, max_compressed_size);
libdeflate_free_compressor(compressor);
if (actual_compressed_size == 0)
{
free(compressed_data);
return std::string();
}
std::string out;
out.assign(reinterpret_cast<char*>(compressed_data), actual_compressed_size);
free(compressed_data);
return out;
#else
z_stream zs; // z_stream is zlib's control structure
memset(&zs, 0, sizeof(zs));
// deflateInit2 configure the file format: request gzip instead of deflate
const int windowBits = 15;
const int GZIP_ENCODING = 16;
deflateInit2(&zs,
Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
windowBits | GZIP_ENCODING,
8,
Z_DEFAULT_STRATEGY);
zs.next_in = (Bytef*) str.data();
zs.avail_in = (uInt) str.size(); // set the z_stream's input
int ret;
char outbuffer[32768];
std::string outstring;
// retrieve the compressed bytes blockwise
do
{
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = deflate(&zs, Z_FINISH);
if (outstring.size() < zs.total_out)
{
// append the block to the output string
outstring.append(outbuffer, zs.total_out - outstring.size());
}
} while (ret == Z_OK);
deflateEnd(&zs);
return outstring;
#endif // IXWEBSOCKET_USE_DEFLATE
#endif // IXWEBSOCKET_USE_ZLIB
}
#ifdef IXWEBSOCKET_USE_DEFLATE
static uint32_t loadDecompressedGzipSize(const uint8_t* p)
{
return ((uint32_t) p[0] << 0) | ((uint32_t) p[1] << 8) | ((uint32_t) p[2] << 16) |
((uint32_t) p[3] << 24);
}
#endif
bool gzipDecompress(const std::string& in, std::string& out)
{
#ifndef IXWEBSOCKET_USE_ZLIB
return false;
#else
#ifdef IXWEBSOCKET_USE_DEFLATE
struct libdeflate_decompressor* decompressor;
decompressor = libdeflate_alloc_decompressor();
const void* compressed_data = in.data();
size_t compressed_size = in.size();
// Retrieve uncompressed size from the trailer of the gziped data
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&in.front());
auto uncompressed_size = loadDecompressedGzipSize(&ptr[compressed_size - 4]);
// Use it to redimension our output buffer
out.resize(uncompressed_size);
libdeflate_result result = libdeflate_gzip_decompress(
decompressor, compressed_data, compressed_size, &out.front(), uncompressed_size, NULL);
libdeflate_free_decompressor(decompressor);
return result == LIBDEFLATE_SUCCESS;
#else
z_stream inflateState;
memset(&inflateState, 0, sizeof(inflateState));
inflateState.zalloc = Z_NULL;
inflateState.zfree = Z_NULL;
inflateState.opaque = Z_NULL;
inflateState.avail_in = 0;
inflateState.next_in = Z_NULL;
if (inflateInit2(&inflateState, 16 + MAX_WBITS) != Z_OK)
{
return false;
}
inflateState.avail_in = (uInt) in.size();
inflateState.next_in = (unsigned char*) (const_cast<char*>(in.data()));
const int kBufferSize = 1 << 14;
std::array<unsigned char, kBufferSize> compressBuffer;
do
{
inflateState.avail_out = (uInt) kBufferSize;
inflateState.next_out = &compressBuffer.front();
int ret = inflate(&inflateState, Z_SYNC_FLUSH);
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
{
inflateEnd(&inflateState);
return false;
}
out.append(reinterpret_cast<char*>(&compressBuffer.front()),
kBufferSize - inflateState.avail_out);
} while (inflateState.avail_out == 0);
inflateEnd(&inflateState);
return true;
#endif // IXWEBSOCKET_USE_DEFLATE
#endif // IXWEBSOCKET_USE_ZLIB
}
} // namespace ix

15
ixwebsocket/IXGzipCodec.h Normal file
View File

@ -0,0 +1,15 @@
/*
* IXGzipCodec.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
std::string gzipCompress(const std::string& str);
bool gzipDecompress(const std::string& in, std::string& out);
} // namespace ix

View File

@ -7,6 +7,7 @@
#include "IXHttp.h"
#include "IXCancellationRequest.h"
#include "IXGzipCodec.h"
#include "IXSocket.h"
#include <sstream>
#include <vector>
@ -93,14 +94,12 @@ namespace ix
}
std::tuple<bool, std::string, HttpRequestPtr> Http::parseRequest(
std::unique_ptr<Socket>& socket)
std::unique_ptr<Socket>& socket, int timeoutSecs)
{
HttpRequestPtr httpRequest;
std::atomic<bool> requestInitCancellation(false);
int timeoutSecs = 5; // FIXME
auto isCancellationRequested =
makeCancellationRequestWithTimeout(timeoutSecs, requestInitCancellation);
@ -130,7 +129,53 @@ namespace ix
return std::make_tuple(false, "Error parsing HTTP headers", httpRequest);
}
httpRequest = std::make_shared<HttpRequest>(uri, method, httpVersion, headers);
std::string body;
if (headers.find("Content-Length") != headers.end())
{
int contentLength = 0;
try
{
contentLength = std::stoi(headers["Content-Length"]);
}
catch (std::exception)
{
return std::make_tuple(
false, "Error parsing HTTP Header 'Content-Length'", httpRequest);
}
if (contentLength < 0)
{
return std::make_tuple(
false, "Error: 'Content-Length' should be a positive integer", httpRequest);
}
auto res = socket->readBytes(contentLength, nullptr, isCancellationRequested);
if (!res.first)
{
return std::make_tuple(
false, std::string("Error reading request: ") + res.second, httpRequest);
}
body = res.second;
}
// If the content was compressed with gzip, decode it
if (headers["Content-Encoding"] == "gzip")
{
#ifdef IXWEBSOCKET_USE_ZLIB
std::string decompressedPayload;
if (!gzipDecompress(body, decompressedPayload))
{
return std::make_tuple(
false, std::string("Error during gzip decompression of the body"), httpRequest);
}
body = decompressedPayload;
#else
std::string errorMsg("ixwebsocket was not compiled with gzip support on");
return std::make_tuple(false, errorMsg, httpRequest);
#endif
}
httpRequest = std::make_shared<HttpRequest>(uri, method, httpVersion, body, headers);
return std::make_tuple(true, "", httpRequest);
}
@ -151,7 +196,7 @@ namespace ix
// Write headers
ss.str("");
ss << "Content-Length: " << response->payload.size() << "\r\n";
ss << "Content-Length: " << response->body.size() << "\r\n";
for (auto&& it : response->headers)
{
ss << it.first << ": " << it.second << "\r\n";
@ -163,6 +208,6 @@ namespace ix
return false;
}
return response->payload.empty() ? true : socket->writeBytes(response->payload, nullptr);
return response->body.empty() ? true : socket->writeBytes(response->body, nullptr);
}
} // namespace ix

View File

@ -39,7 +39,7 @@ namespace ix
std::string description;
HttpErrorCode errorCode;
WebSocketHttpHeaders headers;
std::string payload;
std::string body;
std::string errorMsg;
uint64_t uploadSize;
uint64_t downloadSize;
@ -48,7 +48,7 @@ namespace ix
const std::string& des = std::string(),
const HttpErrorCode& c = HttpErrorCode::Ok,
const WebSocketHttpHeaders& h = WebSocketHttpHeaders(),
const std::string& p = std::string(),
const std::string& b = std::string(),
const std::string& e = std::string(),
uint64_t u = 0,
uint64_t d = 0)
@ -56,7 +56,7 @@ namespace ix
, description(des)
, errorCode(c)
, headers(h)
, payload(p)
, body(b)
, errorMsg(e)
, uploadSize(u)
, downloadSize(d)
@ -84,6 +84,7 @@ namespace ix
int maxRedirects = 5;
bool verbose = false;
bool compress = true;
bool compressRequest = false;
Logger logger;
OnProgressCallback onProgressCallback;
};
@ -95,15 +96,18 @@ namespace ix
std::string uri;
std::string method;
std::string version;
std::string body;
WebSocketHttpHeaders headers;
HttpRequest(const std::string& u,
const std::string& m,
const std::string& v,
const std::string& b,
const WebSocketHttpHeaders& h = WebSocketHttpHeaders())
: uri(u)
, method(m)
, version(v)
, body(b)
, headers(h)
{
}
@ -115,7 +119,7 @@ namespace ix
{
public:
static std::tuple<bool, std::string, HttpRequestPtr> parseRequest(
std::unique_ptr<Socket>& socket);
std::unique_ptr<Socket>& socket, int timeoutSecs);
static bool sendResponse(HttpResponsePtr response, std::unique_ptr<Socket>& socket);
static std::pair<std::string, int> parseStatusLine(const std::string& line);

View File

@ -6,6 +6,7 @@
#include "IXHttpClient.h"
#include "IXGzipCodec.h"
#include "IXSocketFactory.h"
#include "IXUrlParser.h"
#include "IXUserAgent.h"
@ -17,10 +18,6 @@
#include <sstream>
#include <vector>
#ifdef IXWEBSOCKET_USE_ZLIB
#include <zlib.h>
#endif
namespace ix
{
const std::string HttpClient::kPost = "POST";
@ -206,6 +203,15 @@ namespace ix
if (verb == kPost || verb == kPut || verb == kPatch || _forceBody)
{
// Set request compression header
#ifdef IXWEBSOCKET_USE_ZLIB
if (args->compressRequest)
{
ss << "Content-Encoding: gzip"
<< "\r\n";
}
#endif
ss << "Content-Length: " << body.size() << "\r\n";
// Set default Content-Type if unspecified
@ -500,12 +506,12 @@ namespace ix
downloadSize = payload.size();
#ifdef IXWEBSOCKET_USE_ZLIB
// If the content was compressed with gzip, decode it
if (headers["Content-Encoding"] == "gzip")
{
#ifdef IXWEBSOCKET_USE_ZLIB
std::string decompressedPayload;
if (!gzipInflate(payload, decompressedPayload))
if (!gzipDecompress(payload, decompressedPayload))
{
std::string errorMsg("Error decompressing payload");
return std::make_shared<HttpResponse>(code,
@ -518,8 +524,18 @@ namespace ix
downloadSize);
}
payload = decompressedPayload;
}
#else
std::string errorMsg("ixwebsocket was not compiled with gzip support on");
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::Gzip,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
#endif
}
return std::make_shared<HttpResponse>(code,
description,
@ -546,11 +562,42 @@ namespace ix
return request(url, kDel, std::string(), args);
}
HttpResponsePtr HttpClient::request(const std::string& url,
const std::string& verb,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args)
{
std::string body;
if (httpFormDataParameters.empty())
{
body = serializeHttpParameters(httpParameters);
}
else
{
std::string multipartBoundary = generateMultipartBoundary();
args->multipartBoundary = multipartBoundary;
body = serializeHttpFormDataParameters(
multipartBoundary, httpFormDataParameters, httpParameters);
}
#ifdef IXWEBSOCKET_USE_ZLIB
if (args->compressRequest)
{
body = gzipCompress(body);
}
#endif
return request(url, verb, body, args);
}
HttpResponsePtr HttpClient::post(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args)
{
return request(url, kPost, serializeHttpParameters(httpParameters), args);
return request(url, kPost, httpParameters, httpFormDataParameters, args);
}
HttpResponsePtr HttpClient::post(const std::string& url,
@ -562,9 +609,10 @@ namespace ix
HttpResponsePtr HttpClient::put(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args)
{
return request(url, kPut, serializeHttpParameters(httpParameters), args);
return request(url, kPut, httpParameters, httpFormDataParameters, args);
}
HttpResponsePtr HttpClient::put(const std::string& url,
@ -576,9 +624,10 @@ namespace ix
HttpResponsePtr HttpClient::patch(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args)
{
return request(url, kPatch, serializeHttpParameters(httpParameters), args);
return request(url, kPatch, httpParameters, httpFormDataParameters, args);
}
HttpResponsePtr HttpClient::patch(const std::string& url,
@ -679,53 +728,6 @@ namespace ix
return ss.str();
}
#ifdef IXWEBSOCKET_USE_ZLIB
bool HttpClient::gzipInflate(const std::string& in, std::string& out)
{
z_stream inflateState;
std::memset(&inflateState, 0, sizeof(inflateState));
inflateState.zalloc = Z_NULL;
inflateState.zfree = Z_NULL;
inflateState.opaque = Z_NULL;
inflateState.avail_in = 0;
inflateState.next_in = Z_NULL;
if (inflateInit2(&inflateState, 16 + MAX_WBITS) != Z_OK)
{
return false;
}
inflateState.avail_in = (uInt) in.size();
inflateState.next_in = (unsigned char*) (const_cast<char*>(in.data()));
const int kBufferSize = 1 << 14;
std::unique_ptr<unsigned char[]> compressBuffer =
std::make_unique<unsigned char[]>(kBufferSize);
do
{
inflateState.avail_out = (uInt) kBufferSize;
inflateState.next_out = compressBuffer.get();
int ret = inflate(&inflateState, Z_SYNC_FLUSH);
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
{
inflateEnd(&inflateState);
return false;
}
out.append(reinterpret_cast<char*>(compressBuffer.get()),
kBufferSize - inflateState.avail_out);
} while (inflateState.avail_out == 0);
inflateEnd(&inflateState);
return true;
}
#endif
void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args)
{
if (args->logger)

View File

@ -34,6 +34,7 @@ namespace ix
HttpResponsePtr post(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args);
HttpResponsePtr post(const std::string& url,
const std::string& body,
@ -41,6 +42,7 @@ namespace ix
HttpResponsePtr put(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args);
HttpResponsePtr put(const std::string& url,
const std::string& body,
@ -48,6 +50,7 @@ namespace ix
HttpResponsePtr patch(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args);
HttpResponsePtr patch(const std::string& url,
const std::string& body,
@ -58,7 +61,15 @@ namespace ix
const std::string& body,
HttpRequestArgsPtr args,
int redirects = 0);
HttpResponsePtr request(const std::string& url,
const std::string& verb,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args);
void setForceBody(bool value);
// Async API
HttpRequestArgsPtr createRequest(const std::string& url = std::string(),
const std::string& verb = HttpClient::kGet);
@ -90,10 +101,6 @@ 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();
// Async API

View File

@ -6,6 +6,7 @@
#include "IXHttpServer.h"
#include "IXGzipCodec.h"
#include "IXNetSystem.h"
#include "IXSocketConnect.h"
#include "IXUserAgent.h"
@ -14,10 +15,6 @@
#include <sstream>
#include <vector>
#ifdef IXWEBSOCKET_USE_ZLIB
#include <zlib.h>
#endif
namespace
{
std::pair<bool, std::vector<uint8_t>> load(const std::string& path)
@ -43,59 +40,21 @@ namespace
auto vec = res.second;
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
memset(&zs, 0, sizeof(zs));
// deflateInit2 configure the file format: request gzip instead of deflate
const int windowBits = 15;
const int GZIP_ENCODING = 16;
deflateInit2(&zs,
Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
windowBits | GZIP_ENCODING,
8,
Z_DEFAULT_STRATEGY);
zs.next_in = (Bytef*) str.data();
zs.avail_in = (uInt) str.size(); // set the z_stream's input
int ret;
char outbuffer[32768];
std::string outstring;
// retrieve the compressed bytes blockwise
do
{
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = deflate(&zs, Z_FINISH);
if (outstring.size() < zs.total_out)
{
// append the block to the output string
outstring.append(outbuffer, zs.total_out - outstring.size());
}
} while (ret == Z_OK);
deflateEnd(&zs);
return outstring;
}
#endif
} // namespace
namespace ix
{
HttpServer::HttpServer(
int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
const int HttpServer::kDefaultTimeoutSecs(30);
HttpServer::HttpServer(int port,
const std::string& host,
int backlog,
size_t maxConnections,
int addressFamily,
int timeoutSecs)
: SocketServer(port, host, backlog, maxConnections, addressFamily)
, _connectedClientsCount(0)
, _timeoutSecs(timeoutSecs)
{
setDefaultConnectionCallback();
}
@ -120,18 +79,16 @@ 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++;
auto ret = Http::parseRequest(socket);
auto ret = Http::parseRequest(socket, _timeoutSecs);
// FIXME: handle errors in parseRequest
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 +108,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 == "/")
{
@ -184,8 +140,8 @@ namespace ix
// Log request
std::stringstream ss;
ss << connectionInfo->remoteIp << ":" << connectionInfo->remotePort << " "
<< request->method << " " << request->headers["User-Agent"] << " "
ss << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
<< request->uri << " " << content.size();
logInfo(ss.str());
@ -209,16 +165,16 @@ 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 << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
<< request->uri;
logInfo(ss.str());
@ -234,4 +190,40 @@ namespace ix
301, "OK", HttpErrorCode::Ok, headers, std::string());
});
}
//
// Display the client parameter and body on the console
//
void HttpServer::makeDebugServer()
{
setOnConnectionCallback(
[this](HttpRequestPtr request,
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr {
WebSocketHttpHeaders headers;
headers["Server"] = userAgent();
// Log request
std::stringstream ss;
ss << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
<< request->uri;
logInfo(ss.str());
logInfo("== Headers == ");
for (auto&& it : request->headers)
{
std::ostringstream oss;
oss << it.first << ": " << it.second;
logInfo(oss.str());
}
logInfo("");
logInfo("== Body == ");
logInfo(request->body);
logInfo("");
return std::make_shared<HttpResponse>(
200, "OK", HttpErrorCode::Ok, headers, std::string("OK"));
});
}
} // namespace ix

View File

@ -23,15 +23,14 @@ 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,
int backlog = SocketServer::kDefaultTcpBacklog,
size_t maxConnections = SocketServer::kDefaultMaxConnections,
int addressFamily = SocketServer::kDefaultAddressFamily);
int addressFamily = SocketServer::kDefaultAddressFamily,
int timeoutSecs = HttpServer::kDefaultTimeoutSecs);
virtual ~HttpServer();
virtual void stop() final;
@ -39,15 +38,19 @@ namespace ix
void makeRedirectServer(const std::string& redirectUrl);
void makeDebugServer();
private:
// Member variables
OnConnectionCallback _onConnectionCallback;
std::atomic<int> _connectedClientsCount;
const static int kDefaultTimeoutSecs;
int _timeoutSecs;
// 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();

View File

@ -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

View File

@ -11,6 +11,7 @@
#include "IXSelectInterruptFactory.h"
#include "IXSocketConnect.h"
#include <algorithm>
#include <array>
#include <assert.h>
#include <fcntl.h>
#include <stdint.h>
@ -18,6 +19,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <vector>
#ifdef min
#undef min
@ -27,7 +29,6 @@ namespace ix
{
const int Socket::kDefaultPollNoTimeout = -1; // No poll timeout by default
const int Socket::kDefaultPollTimeout = kDefaultPollNoTimeout;
constexpr size_t Socket::kChunkSize;
Socket::Socket(int fd)
: _sockfd(fd)
@ -364,10 +365,7 @@ namespace ix
const OnProgressCallback& onProgressCallback,
const CancellationRequest& isCancellationRequested)
{
if (_readBuffer.empty())
{
_readBuffer.resize(kChunkSize);
}
std::array<uint8_t, 1 << 14> readBuffer;
std::vector<uint8_t> output;
while (output.size() != length)
@ -378,12 +376,12 @@ namespace ix
return std::make_pair(false, errorMsg);
}
size_t size = std::min(kChunkSize, length - output.size());
ssize_t ret = recv((char*) &_readBuffer[0], size);
size_t size = std::min(readBuffer.size(), length - output.size());
ssize_t ret = recv((char*) &readBuffer[0], size);
if (ret > 0)
{
output.insert(output.end(), _readBuffer.begin(), _readBuffer.begin() + ret);
output.insert(output.end(), readBuffer.begin(), readBuffer.begin() + ret);
}
else if (ret <= 0 && !Socket::isWaitNeeded())
{

View File

@ -11,7 +11,6 @@
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#ifdef _WIN32
#include <BaseTsd.h>
@ -102,10 +101,6 @@ namespace ix
static const int kDefaultPollTimeout;
static const int kDefaultPollNoTimeout;
// Buffer for reading from our socket. That buffer is never resized.
std::vector<uint8_t> _readBuffer;
static constexpr size_t kChunkSize = 1 << 15;
SelectInterruptPtr _selectInterrupt;
};
} // namespace ix

View File

@ -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)

View File

@ -6,6 +6,7 @@
#include "IXSocketFactory.h"
#include "IXUniquePtr.h"
#ifdef IXWEBSOCKET_USE_TLS
#ifdef IXWEBSOCKET_USE_MBED_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.";

View File

@ -10,8 +10,10 @@
#include "IXSocketOpenSSL.h"
#include "IXSocketConnect.h"
#include "IXUniquePtr.h"
#include <cassert>
#include <errno.h>
#include <vector>
#ifdef _WIN32
#include <Shlwapi.h>
#else
@ -85,8 +87,7 @@ namespace ix
std::atomic<bool> SocketOpenSSL::_openSSLInitializationSuccessful(false);
std::once_flag SocketOpenSSL::_openSSLInitFlag;
std::unique_ptr<std::mutex[]> SocketOpenSSL::_openSSLMutexes =
std::make_unique<std::mutex[]>(CRYPTO_num_locks());
std::vector<std::unique_ptr<std::mutex>> openSSLMutexes;
SocketOpenSSL::SocketOpenSSL(const SocketTLSOptions& tlsOptions, int fd)
: Socket(fd)
@ -111,6 +112,11 @@ namespace ix
if (CRYPTO_get_locking_callback() == nullptr)
{
openSSLMutexes.clear();
for (int i = 0; i < CRYPTO_num_locks(); ++i)
{
openSSLMutexes.push_back(ix::make_unique<std::mutex>());
}
CRYPTO_set_locking_callback(SocketOpenSSL::openSSLLockingCallback);
}
#endif
@ -128,11 +134,11 @@ namespace ix
{
if (mode & CRYPTO_LOCK)
{
_openSSLMutexes[type].lock();
openSSLMutexes[type]->lock();
}
else
{
_openSSLMutexes[type].unlock();
openSSLMutexes[type]->unlock();
}
}
@ -503,14 +509,13 @@ namespace ix
errMsg += ERR_error_string(sslErr, nullptr);
return false;
}
SSL_CTX_set_verify(
_ssl_context, SSL_VERIFY_PEER, [](int preverify, X509_STORE_CTX*) -> int {
return preverify;
});
SSL_CTX_set_verify_depth(_ssl_context, 4);
}
}
SSL_CTX_set_verify(_ssl_context,
SSL_VERIFY_PEER,
[](int preverify, X509_STORE_CTX*) -> int { return preverify; });
SSL_CTX_set_verify_depth(_ssl_context, 4);
}
else
{

View File

@ -61,7 +61,6 @@ namespace ix
static std::once_flag _openSSLInitFlag;
static std::atomic<bool> _openSSLInitializationSuccessful;
static std::unique_ptr<std::mutex[]> _openSSLMutexes;
};
} // namespace ix

View File

@ -332,12 +332,13 @@ namespace ix
}
// Retrieve connection info, the ip address of the remote peer/client)
std::unique_ptr<ConnectionInfo> connectionInfo;
std::string remoteIp;
int remotePort;
if (_addressFamily == AF_INET)
{
char remoteIp[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &client.sin_addr, remoteIp, INET_ADDRSTRLEN) == nullptr)
char remoteIp4[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &client.sin_addr, remoteIp4, INET_ADDRSTRLEN) == nullptr)
{
int err = Socket::getErrno();
std::stringstream ss;
@ -350,12 +351,13 @@ namespace ix
continue;
}
connectionInfo = std::make_unique<ConnectionInfo>(remoteIp, client.sin_port);
remotePort = client.sin_port;
remoteIp = remoteIp4;
}
else // AF_INET6
{
char remoteIp[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, &client.sin_addr, remoteIp, INET6_ADDRSTRLEN) == nullptr)
char remoteIp6[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) == nullptr)
{
int err = Socket::getErrno();
std::stringstream ss;
@ -368,7 +370,8 @@ namespace ix
continue;
}
connectionInfo = std::make_unique<ConnectionInfo>(remoteIp, client.sin_port);
remotePort = client.sin_port;
remoteIp = remoteIp6;
}
std::shared_ptr<ConnectionState> connectionState;
@ -377,6 +380,8 @@ namespace ix
connectionState = _connectionStateFactory();
}
connectionState->setOnSetTerminatedCallback([this] { onSetTerminatedCallback(); });
connectionState->setRemoteIp(remoteIp);
connectionState->setRemotePort(remotePort);
if (_stop) return;
@ -404,13 +409,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)));
}
}

View File

@ -6,7 +6,6 @@
#pragma once
#include "IXConnectionInfo.h"
#include "IXConnectionState.h"
#include "IXSelectInterrupt.h"
#include "IXSocketTLSOptions.h"
@ -105,8 +104,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

View File

@ -0,0 +1,37 @@
/*
* IXStrCaseCompare.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone. All rights reserved.
*/
#include "IXStrCaseCompare.h"
#include <algorithm>
#include <locale>
namespace ix
{
bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char& c1,
const unsigned char& c2) const
{
#ifdef _WIN32
return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale());
#else
return std::tolower(c1) < std::tolower(c2);
#endif
}
bool CaseInsensitiveLess::cmp(const std::string& s1, const std::string& s2)
{
return std::lexicographical_compare(s1.begin(),
s1.end(), // source range
s2.begin(),
s2.end(), // dest range
NocaseCompare()); // comparison
}
bool CaseInsensitiveLess::operator()(const std::string& s1, const std::string& s2) const
{
return CaseInsensitiveLess::cmp(s1, s2);
}
} // namespace ix

View File

@ -0,0 +1,25 @@
/*
* IXStrCaseCompare.h
* Author: Benjamin Sergeant
* Copyright (c) 2020 Machine Zone. All rights reserved.
*/
#pragma once
#include <string>
namespace ix
{
struct CaseInsensitiveLess
{
// Case Insensitive compare_less binary function
struct NocaseCompare
{
bool operator()(const unsigned char& c1, const unsigned char& c2) const;
};
static bool cmp(const std::string& s1, const std::string& s2);
bool operator()(const std::string& s1, const std::string& s2) const;
};
} // namespace ix

18
ixwebsocket/IXUniquePtr.h Normal file
View 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)...));
}
} // namespace ix

View File

@ -8,6 +8,7 @@
#include "IXExponentialBackoff.h"
#include "IXSetThreadName.h"
#include "IXUniquePtr.h"
#include "IXUtf8Validator.h"
#include "IXWebSocketHandshake.h"
#include <cassert>
@ -34,12 +35,12 @@ namespace ix
_ws.setOnCloseCallback(
[this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) {
_onMessageCallback(
std::make_unique<WebSocketMessage>(WebSocketMessageType::Close,
"",
wireSize,
WebSocketErrorInfo(),
WebSocketOpenInfo(),
WebSocketCloseInfo(code, reason, remote)));
ix::make_unique<WebSocketMessage>(WebSocketMessageType::Close,
"",
wireSize,
WebSocketErrorInfo(),
WebSocketOpenInfo(),
WebSocketCloseInfo(code, reason, remote)));
});
}
@ -54,6 +55,7 @@ namespace ix
std::lock_guard<std::mutex> lock(_configMutex);
_url = url;
}
void WebSocket::setExtraHeaders(const WebSocketHttpHeaders& headers)
{
std::lock_guard<std::mutex> lock(_configMutex);
@ -194,7 +196,7 @@ namespace ix
return status;
}
_onMessageCallback(std::make_unique<WebSocketMessage>(
_onMessageCallback(ix::make_unique<WebSocketMessage>(
WebSocketMessageType::Open,
"",
0,
@ -226,12 +228,12 @@ namespace ix
}
_onMessageCallback(
std::make_unique<WebSocketMessage>(WebSocketMessageType::Open,
"",
0,
WebSocketErrorInfo(),
WebSocketOpenInfo(status.uri, status.headers),
WebSocketCloseInfo()));
ix::make_unique<WebSocketMessage>(WebSocketMessageType::Open,
"",
0,
WebSocketErrorInfo(),
WebSocketOpenInfo(status.uri, status.headers),
WebSocketCloseInfo()));
if (_pingIntervalSecs > 0)
{
@ -311,12 +313,12 @@ namespace ix
connectErr.reason = status.errorStr;
connectErr.http_status = status.http_status;
_onMessageCallback(std::make_unique<WebSocketMessage>(WebSocketMessageType::Error,
"",
0,
connectErr,
WebSocketOpenInfo(),
WebSocketCloseInfo()));
_onMessageCallback(ix::make_unique<WebSocketMessage>(WebSocketMessageType::Error,
"",
0,
connectErr,
WebSocketOpenInfo(),
WebSocketCloseInfo()));
}
}
}
@ -387,13 +389,13 @@ namespace ix
bool binary = messageKind == WebSocketTransport::MessageKind::MSG_BINARY;
_onMessageCallback(std::make_unique<WebSocketMessage>(webSocketMessageType,
msg,
wireSize,
webSocketErrorInfo,
WebSocketOpenInfo(),
WebSocketCloseInfo(),
binary));
_onMessageCallback(ix::make_unique<WebSocketMessage>(webSocketMessageType,
msg,
wireSize,
webSocketErrorInfo,
WebSocketOpenInfo(),
WebSocketCloseInfo(),
binary));
WebSocket::invokeTrafficTrackerCallback(wireSize, true);
});

View File

@ -11,6 +11,7 @@
#include "IXUrlParser.h"
#include "IXUserAgent.h"
#include "IXWebSocketHandshakeKeyGen.h"
#include "IXStrCaseCompare.h"
#include <algorithm>
#include <iostream>
#include <random>
@ -35,9 +36,7 @@ namespace ix
bool WebSocketHandshake::insensitiveStringCompare(const std::string& a, const std::string& b)
{
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) {
return tolower(a) == tolower(b);
});
return CaseInsensitiveLess::cmp(a, b);
}
std::string WebSocketHandshake::genRandomString(const int len)

View File

@ -12,25 +12,6 @@
namespace ix
{
bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char& c1,
const unsigned char& c2) const
{
#ifdef _WIN32
return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale());
#else
return std::tolower(c1) < std::tolower(c2);
#endif
}
bool CaseInsensitiveLess::operator()(const std::string& s1, const std::string& s2) const
{
return std::lexicographical_compare(s1.begin(),
s1.end(), // source range
s2.begin(),
s2.end(), // dest range
NocaseCompare()); // comparison
}
std::pair<bool, WebSocketHttpHeaders> parseHttpHeaders(
std::unique_ptr<Socket>& socket, const CancellationRequest& isCancellationRequested)
{

View File

@ -7,6 +7,7 @@
#pragma once
#include "IXCancellationRequest.h"
#include "IXStrCaseCompare.h"
#include <map>
#include <memory>
#include <string>
@ -15,17 +16,6 @@ namespace ix
{
class Socket;
struct CaseInsensitiveLess
{
// Case Insensitive compare_less binary function
struct NocaseCompare
{
bool operator()(const unsigned char& c1, const unsigned char& c2) const;
};
bool operator()(const std::string& s1, const std::string& s2) const;
};
using WebSocketHttpHeaders = std::map<std::string, std::string, CaseInsensitiveLess>;
std::pair<bool, WebSocketHttpHeaders> parseHttpHeaders(

View File

@ -48,14 +48,15 @@
#include "IXWebSocketPerMessageDeflate.h"
#include "IXUniquePtr.h"
#include "IXWebSocketPerMessageDeflateCodec.h"
#include "IXWebSocketPerMessageDeflateOptions.h"
namespace ix
{
WebSocketPerMessageDeflate::WebSocketPerMessageDeflate()
: _compressor(std::make_unique<WebSocketPerMessageDeflateCompressor>())
, _decompressor(std::make_unique<WebSocketPerMessageDeflateDecompressor>())
: _compressor(ix::make_unique<WebSocketPerMessageDeflateCompressor>())
, _decompressor(ix::make_unique<WebSocketPerMessageDeflateDecompressor>())
{
;
}

View File

@ -16,8 +16,6 @@ namespace
// is treated as a char* and the null termination (\x00) makes it
// look like an empty string.
const std::string kEmptyUncompressedBlock = std::string("\x00\x00\xff\xff", 4);
const int kBufferSize = 1 << 14;
} // namespace
namespace ix
@ -26,7 +24,6 @@ namespace ix
// Compressor
//
WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor()
: _compressBufferSize(kBufferSize)
{
#ifdef IXWEBSOCKET_USE_ZLIB
memset(&_deflateState, 0, sizeof(_deflateState));
@ -57,8 +54,6 @@ namespace ix
if (ret != Z_OK) return false;
_compressBuffer = std::make_unique<unsigned char[]>(_compressBufferSize);
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
return true;
@ -145,14 +140,14 @@ namespace ix
do
{
// Output to local buffer
_deflateState.avail_out = (uInt) _compressBufferSize;
_deflateState.next_out = _compressBuffer.get();
_deflateState.avail_out = (uInt) _compressBuffer.size();
_deflateState.next_out = &_compressBuffer.front();
deflate(&_deflateState, _flush);
output = _compressBufferSize - _deflateState.avail_out;
output = _compressBuffer.size() - _deflateState.avail_out;
out.insert(out.end(), _compressBuffer.get(), _compressBuffer.get() + output);
out.insert(out.end(), _compressBuffer.begin(), _compressBuffer.begin() + output);
} while (_deflateState.avail_out == 0);
if (endsWithEmptyUnCompressedBlock(out))
@ -170,7 +165,6 @@ namespace ix
// Decompressor
//
WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor()
: _compressBufferSize(kBufferSize)
{
#ifdef IXWEBSOCKET_USE_ZLIB
memset(&_inflateState, 0, sizeof(_inflateState));
@ -198,8 +192,6 @@ namespace ix
if (ret != Z_OK) return false;
_compressBuffer = std::make_unique<unsigned char[]>(_compressBufferSize);
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
return true;
@ -232,8 +224,8 @@ namespace ix
do
{
_inflateState.avail_out = (uInt) _compressBufferSize;
_inflateState.next_out = _compressBuffer.get();
_inflateState.avail_out = (uInt) _compressBuffer.size();
_inflateState.next_out = &_compressBuffer.front();
int ret = inflate(&_inflateState, Z_SYNC_FLUSH);
@ -242,8 +234,8 @@ namespace ix
return false; // zlib error
}
out.append(reinterpret_cast<char*>(_compressBuffer.get()),
_compressBufferSize - _inflateState.avail_out);
out.append(reinterpret_cast<char*>(&_compressBuffer.front()),
_compressBuffer.size() - _inflateState.avail_out);
} while (_inflateState.avail_out == 0);
return true;

View File

@ -9,7 +9,7 @@
#ifdef IXWEBSOCKET_USE_ZLIB
#include "zlib.h"
#endif
#include <memory>
#include <array>
#include <string>
#include <vector>
@ -34,8 +34,7 @@ namespace ix
bool endsWithEmptyUnCompressedBlock(const T& value);
int _flush;
size_t _compressBufferSize;
std::unique_ptr<unsigned char[]> _compressBuffer;
std::array<unsigned char, 1 << 14> _compressBuffer;
#ifdef IXWEBSOCKET_USE_ZLIB
z_stream _deflateState;
@ -53,8 +52,7 @@ namespace ix
private:
int _flush;
size_t _compressBufferSize;
std::unique_ptr<unsigned char[]> _compressBuffer;
std::array<unsigned char, 1 << 14> _compressBuffer;
#ifdef IXWEBSOCKET_USE_ZLIB
z_stream _inflateState;

View File

@ -56,10 +56,9 @@ namespace ix
server.setOnConnectionCallback(
[remoteUrl, remoteUrlsMapping](std::weak_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState,
std::unique_ptr<ConnectionInfo> connectionInfo) {
std::shared_ptr<ConnectionState> connectionState) {
auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState);
auto remoteIp = connectionInfo->remoteIp;
auto remoteIp = connectionState->getRemoteIp();
// Server connection
state->webSocket().setOnMessageCallback(

View File

@ -77,15 +77,14 @@ namespace ix
}
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));
_onConnectionCallback(webSocket, connectionState);
if (!webSocket->isOnMessageCallbackRegistered())
{
@ -99,9 +98,8 @@ namespace ix
else if (_onClientMessageCallback)
{
webSocket->setOnMessageCallback(
[this, &ws = *webSocket.get(), connectionState, &ci = *connectionInfo.get()](
const WebSocketMessagePtr& msg) {
_onClientMessageCallback(connectionState, ci, ws, msg);
[this, &ws = *webSocket.get(), connectionState](const WebSocketMessagePtr& msg) {
_onClientMessageCallback(connectionState, ws, msg);
});
}
else

View File

@ -23,14 +23,10 @@ namespace ix
{
public:
using OnConnectionCallback =
std::function<void(std::weak_ptr<WebSocket>,
std::shared_ptr<ConnectionState>,
std::unique_ptr<ConnectionInfo> connectionInfo)>;
std::function<void(std::weak_ptr<WebSocket>, std::shared_ptr<ConnectionState>)>;
using OnClientMessageCallback = std::function<void(std::shared_ptr<ConnectionState>,
ConnectionInfo&,
WebSocket&,
const WebSocketMessagePtr&)>;
using OnClientMessageCallback = std::function<void(
std::shared_ptr<ConnectionState>, WebSocket&, const WebSocketMessagePtr&)>;
WebSocketServer(int port = SocketServer::kDefaultPort,
const std::string& host = SocketServer::kDefaultHost,
@ -69,8 +65,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);
virtual size_t getConnectedClientsCount() final;
};
} // namespace ix

View File

@ -36,6 +36,7 @@
#include "IXSocketFactory.h"
#include "IXSocketTLSOptions.h"
#include "IXUniquePtr.h"
#include "IXUrlParser.h"
#include "IXUtf8Validator.h"
#include "IXWebSocketHandshake.h"
@ -124,7 +125,7 @@ namespace ix
std::string errorMsg;
bool tls = protocol == "wss";
_socket = createSocket(tls, -1, errorMsg, _socketTLSOptions);
_perMessageDeflate = std::make_unique<WebSocketPerMessageDeflate>();
_perMessageDeflate = ix::make_unique<WebSocketPerMessageDeflate>();
if (!_socket)
{
@ -177,7 +178,7 @@ namespace ix
_blockingSend = true;
_socket = std::move(socket);
_perMessageDeflate = std::make_unique<WebSocketPerMessageDeflate>();
_perMessageDeflate = ix::make_unique<WebSocketPerMessageDeflate>();
WebSocketHandshake webSocketHandshake(_requestInitCancellation,
_socket,

View File

@ -6,4 +6,4 @@
#pragma once
#define IX_WEBSOCKET_VERSION "10.2.5"
#define IX_WEBSOCKET_VERSION "11.0.2"

View File

@ -28,11 +28,14 @@ brew:
# 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_ZLIB=OFF -DUSE_PYTHON=1 -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)
ws_unity:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
ws_install:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install)
@ -49,7 +52,7 @@ ws_no_ssl:
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4)
ws_no_python:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -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 -j4 install)
uninstall:
xargs rm -fv < build/install_manifest.txt
@ -107,71 +110,30 @@ format:
test_server:
(cd test && npm i ws && node broadcast-server.js)
# 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:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_TEST=1 ..)
(cd build ; ninja)
(cd build ; ninja test)
test: build_test
(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)
(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)
(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)
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
(cd test ; python2.7 run.py -r)
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)
(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)
(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)
(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)
(cd build/test ; ln -sf Release/ixwebsocket_unittest)
(cd test ; python2.7 run.py -r)
test_asan:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer")
(cd build ; ninja)
(cd build ; ctest -V .)
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)
(cd build/test ; ln -sf Debug/ixwebsocket_unittest)
(cd test ; python2.7 run.py -r)
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_MBED_TLS=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=thread -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=thread -fno-omit-frame-pointer")
(cd build ; ninja)
(cd build ; ninja test)
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)
test_tsan_openssl:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_OPENS_SSL=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=thread -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=thread -fno-omit-frame-pointer")
(cd build ; ninja)
(cd build ; ninja test)
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)
test_mbedtls: build_test_mbedtls
(cd test ; python2.7 run.py -r)
test_no_ssl:
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TEST=1 .. ; make -j 4)
(cd test ; python2.7 run.py -r)
test_tsan_sectransport:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_OPENS_SSL=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=thread -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=thread -fno-omit-frame-pointer")
(cd build ; ninja)
(cd build ; ninja test)
ws_test: ws
(cd ws ; env DEBUG=1 PATH=../ws/build:$$PATH bash test_ws.sh)
@ -247,6 +209,9 @@ doc:
change: format
vim ixwebsocket/IXWebSocketVersion.h docs/CHANGELOG.md
change_no_format:
vim ixwebsocket/IXWebSocketVersion.h docs/CHANGELOG.md
commit:
git commit -am "`sh tools/extract_latest_change.sh`"

View File

@ -2,73 +2,44 @@
# Author: Benjamin Sergeant
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
#
cmake_minimum_required (VERSION 3.4.1)
cmake_minimum_required (VERSION 3.14)
project (ixwebsocket_unittest)
set (CMAKE_CXX_STANDARD 14)
set (CMAKE_CXX_STANDARD 11)
if (MAC)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../third_party/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH})
find_package(Sanitizers)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
set(CMAKE_LD_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
option(USE_TLS "Add TLS support" ON)
endif()
include_directories(
${PROJECT_SOURCE_DIR}/Catch2/single_include
../third_party
../third_party/msgpack11
../third_party/spdlog/include
../ws
)
add_definitions(-DSPDLOG_COMPILED_LIB=1)
find_package(JsonCpp)
if (NOT JSONCPP_FOUND)
include_directories(../third_party/jsoncpp)
set(JSONCPP_SOURCES ../third_party/jsoncpp/jsoncpp.cpp)
endif()
option(USE_TLS "Add TLS support" ON)
# Shared sources
set (SOURCES
${JSONCPP_SOURCES}
test_runner.cpp
IXTest.cpp
../third_party/msgpack11/msgpack11.cpp
IXSocketTest.cpp
IXSocketConnectTest.cpp
set (TEST_TARGET_NAMES
IXSocketTest
IXSocketConnectTest
# IXWebSocketLeakTest.cpp # commented until we have a fix for #224
IXWebSocketServerTest.cpp
IXWebSocketTestConnectionDisconnection.cpp
IXUrlParserTest.cpp
IXWebSocketServerTest.cpp
IXHttpClientTest.cpp
IXHttpServerTest.cpp
IXUnityBuildsTest.cpp
IXHttpTest.cpp
IXDNSLookupTest.cpp
IXWebSocketSubProtocolTest.cpp
IXSentryClientTest.cpp
IXWebSocketChatTest.cpp
IXWebSocketBroadcastTest.cpp
IXWebSocketPerMessageDeflateCompressorTest.cpp
IXStreamSqlTest.cpp
IXWebSocketServerTest
IXWebSocketTestConnectionDisconnection
IXUrlParserTest
IXHttpClientTest
IXHttpServerTest
IXUnityBuildsTest
IXHttpTest
IXDNSLookupTest
IXWebSocketSubProtocolTest
IXSentryClientTest
IXWebSocketChatTest
IXWebSocketBroadcastTest
IXWebSocketPerMessageDeflateCompressorTest
IXStreamSqlTest
)
# Some unittest don't work on windows yet
# Windows without TLS does not have hmac yet
if (UNIX)
list(APPEND SOURCES
IXWebSocketCloseTest.cpp
IXCobraChatTest.cpp
IXCobraMetricsPublisherTest.cpp
IXCobraToSentryBotTest.cpp
IXCobraToStatsdBotTest.cpp
IXCobraToStdoutBotTest.cpp
list(APPEND TEST_TARGET_NAMES
IXWebSocketCloseTest
IXCobraChatTest
IXCobraMetricsPublisherTest
IXCobraToSentryBotTest
IXCobraToStatsdBotTest
IXCobraToStdoutBotTest
)
endif()
@ -78,19 +49,9 @@ endif()
# Disable tests for now that are failing or not reliable
add_executable(ixwebsocket_unittest ${SOURCES})
if (MAC)
add_sanitizers(ixwebsocket_unittest)
endif()
if (APPLE AND USE_TLS)
target_link_libraries(ixwebsocket_unittest "-framework foundation" "-framework security")
endif()
if (JSONCPP_FOUND)
target_include_directories(ixwebsocket_unittest PUBLIC ${JSONCPP_INCLUDE_DIRS})
target_link_libraries(ixwebsocket_unittest ${JSONCPP_LIBRARIES})
find_package(JsonCpp)
if (NOT JSONCPP_FOUND)
set(JSONCPP_SOURCES ../third_party/jsoncpp/jsoncpp.cpp)
endif()
if (USE_PYTHON)
@ -104,19 +65,69 @@ if (USE_PYTHON)
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)
add_library(ixwebsocket_test)
target_sources(ixwebsocket_test PRIVATE
${JSONCPP_SOURCES}
test_runner.cpp
IXTest.cpp
../third_party/msgpack11/msgpack11.cpp
)
target_compile_definitions(ixwebsocket_test PRIVATE ${TEST_PROGRAMS_DEFINITIONS})
target_include_directories(ixwebsocket_test PRIVATE
${PROJECT_SOURCE_DIR}/Catch2/single_include
../third_party
)
target_link_libraries(ixwebsocket_test ixsnake)
target_link_libraries(ixwebsocket_test ixcobra)
target_link_libraries(ixwebsocket_test ixwebsocket)
target_link_libraries(ixwebsocket_test ixcrypto)
target_link_libraries(ixwebsocket_test spdlog)
target_link_libraries(ixwebsocket_unittest spdlog)
if (USE_PYTHON)
target_link_libraries(ixwebsocket_unittest ${Python_LIBRARIES})
endif()
foreach(TEST_TARGET_NAME ${TEST_TARGET_NAMES})
add_executable(${TEST_TARGET_NAME}
${TEST_TARGET_NAME}.cpp
)
install(TARGETS ixwebsocket_unittest DESTINATION bin)
target_include_directories(${TEST_TARGET_NAME} PRIVATE
${PROJECT_SOURCE_DIR}/Catch2/single_include
../third_party
../third_party/msgpack11
)
target_compile_definitions(${TEST_TARGET_NAME} PRIVATE SPDLOG_COMPILED_LIB=1)
if (NOT JSONCPP_FOUND)
target_include_directories(${TEST_TARGET_NAME} PRIVATE ../third_party/jsoncpp)
endif()
if (APPLE AND USE_TLS)
target_link_libraries(${TEST_TARGET_NAME} "-framework foundation" "-framework security")
endif()
# library with the most dependencies come first
target_link_libraries(${TEST_TARGET_NAME} ixwebsocket_test)
target_link_libraries(${TEST_TARGET_NAME} ixbots)
target_link_libraries(${TEST_TARGET_NAME} ixsnake)
target_link_libraries(${TEST_TARGET_NAME} ixcobra)
target_link_libraries(${TEST_TARGET_NAME} ixsentry)
if (JSONCPP_FOUND)
target_include_directories(${TEST_TARGET_NAME} PRIVATE ${JSONCPP_INCLUDE_DIRS})
target_link_libraries(${TEST_TARGET_NAME} ${JSONCPP_LIBRARIES})
endif()
target_link_libraries(${TEST_TARGET_NAME} ixredis)
target_link_libraries(${TEST_TARGET_NAME} ixwebsocket)
target_link_libraries(${TEST_TARGET_NAME} ixcrypto)
target_link_libraries(${TEST_TARGET_NAME} ixcore)
target_link_libraries(${TEST_TARGET_NAME} spdlog)
if (USE_PYTHON)
target_link_libraries(${TEST_TARGET_NAME} ${Python_LIBRARIES})
endif()
add_test(NAME ${TEST_TARGET_NAME}
COMMAND ${TEST_TARGET_NAME}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endforeach()

View File

@ -38,35 +38,6 @@ namespace
}
});
}
void runPublisher(const ix::CobraConfig& config, const std::string& channel)
{
ix::CobraMetricsPublisher cobraMetricsPublisher;
cobraMetricsPublisher.configure(config, channel);
cobraMetricsPublisher.setSession(uuid4());
cobraMetricsPublisher.enable(true);
Json::Value msg;
msg["fps"] = 60;
cobraMetricsPublisher.setGenericAttributes("game", "ody");
// Wait a bit
ix::msleep(500);
// publish some messages
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #1)
cobraMetricsPublisher.push("sms_metric_B_id", msg); // (msg #2)
ix::msleep(500);
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #3)
cobraMetricsPublisher.push("sms_metric_D_id", msg); // (msg #4)
ix::msleep(500);
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #4)
cobraMetricsPublisher.push("sms_metric_F_id", msg); // (msg #5)
ix::msleep(500);
}
} // namespace
TEST_CASE("Cobra_to_sentry_bot", "[cobra_bots]")
@ -95,15 +66,14 @@ 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 << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
<< request->uri;
if (request->method == "POST")

View File

@ -20,38 +20,6 @@
using namespace ix;
namespace
{
void runPublisher(const ix::CobraConfig& config, const std::string& channel)
{
ix::CobraMetricsPublisher cobraMetricsPublisher;
cobraMetricsPublisher.configure(config, channel);
cobraMetricsPublisher.setSession(uuid4());
cobraMetricsPublisher.enable(true);
Json::Value msg;
msg["fps"] = 60;
cobraMetricsPublisher.setGenericAttributes("game", "ody");
// Wait a bit
ix::msleep(500);
// publish some messages
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #1)
cobraMetricsPublisher.push("sms_metric_B_id", msg); // (msg #2)
ix::msleep(500);
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #3)
cobraMetricsPublisher.push("sms_metric_D_id", msg); // (msg #4)
ix::msleep(500);
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #4)
cobraMetricsPublisher.push("sms_metric_F_id", msg); // (msg #5)
ix::msleep(500);
}
} // namespace
TEST_CASE("Cobra_to_statsd_bot", "[cobra_bots]")
{
SECTION("Exchange and count sent/received messages.")

View File

@ -10,7 +10,6 @@
#include <iostream>
#include <ixbots/IXCobraToStdoutBot.h>
#include <ixcobra/IXCobraConnection.h>
#include <ixcobra/IXCobraMetricsPublisher.h>
#include <ixcrypto/IXUuid.h>
#include <ixredis/IXRedisServer.h>
#include <ixsentry/IXSentryClient.h>
@ -20,38 +19,6 @@
using namespace ix;
namespace
{
void runPublisher(const ix::CobraConfig& config, const std::string& channel)
{
ix::CobraMetricsPublisher cobraMetricsPublisher;
cobraMetricsPublisher.configure(config, channel);
cobraMetricsPublisher.setSession(uuid4());
cobraMetricsPublisher.enable(true);
Json::Value msg;
msg["fps"] = 60;
cobraMetricsPublisher.setGenericAttributes("game", "ody");
// Wait a bit
ix::msleep(500);
// publish some messages
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #1)
cobraMetricsPublisher.push("sms_metric_B_id", msg); // (msg #2)
ix::msleep(500);
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #3)
cobraMetricsPublisher.push("sms_metric_D_id", msg); // (msg #4)
ix::msleep(500);
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #4)
cobraMetricsPublisher.push("sms_metric_F_id", msg); // (msg #5)
ix::msleep(500);
}
} // namespace
TEST_CASE("Cobra_to_stdout_bot", "[cobra_bots]")
{
SECTION("Exchange and count sent/received messages.")

View File

@ -24,6 +24,8 @@ TEST_CASE("dns", "[net]")
res = dnsLookup->resolve(errMsg, [] { return false; });
std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res != nullptr);
dnsLookup->release(res);
}
SECTION("Test resolving a non-existing hostname")

View File

@ -63,6 +63,54 @@ TEST_CASE("http server", "[httpd]")
server.stop();
}
SECTION("Posting plain text data to a local HTTP server")
{
int port = getFreePort();
ix::HttpServer server(port, "127.0.0.1");
server.setOnConnectionCallback(
[](HttpRequestPtr request, std::shared_ptr<ConnectionState>) -> HttpResponsePtr {
if (request->method == "POST")
{
return std::make_shared<HttpResponse>(
200, "OK", HttpErrorCode::Ok, WebSocketHttpHeaders(), request->body);
}
return std::make_shared<HttpResponse>(400, "BAD REQUEST");
});
auto res = server.listen();
REQUIRE(res.first);
server.start();
HttpClient httpClient;
WebSocketHttpHeaders headers;
headers["Content-Type"] = "text/plain";
std::string url("http://127.0.0.1:");
url += std::to_string(port);
auto args = httpClient.createRequest(url);
args->extraHeaders = headers;
args->connectTimeout = 60;
args->transferTimeout = 60;
args->verbose = true;
args->logger = [](const std::string& msg) { std::cout << msg; };
args->body = "Hello World!";
auto response = httpClient.post(url, args->body, args);
std::cerr << "Status: " << response->statusCode << std::endl;
std::cerr << "Error message: " << response->errorMsg << std::endl;
std::cerr << "Body: " << response->body << std::endl;
REQUIRE(response->errorCode == HttpErrorCode::Ok);
REQUIRE(response->statusCode == 200);
REQUIRE(response->body == args->body);
server.stop();
}
}
TEST_CASE("http server redirection", "[httpd_redirect]")

View File

@ -10,6 +10,8 @@
#include <fstream>
#include <iomanip>
#include <iostream>
#include <ixcobra/IXCobraMetricsPublisher.h>
#include <ixcrypto/IXUuid.h>
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXWebSocket.h>
#include <mutex>
@ -85,11 +87,10 @@ namespace ix
bool startWebSocketEchoServer(ix::WebSocketServer& server)
{
server.setOnClientMessageCallback(
[&server](std::shared_ptr<ConnectionState> /*connectionState*/,
ConnectionInfo& connectionInfo,
[&server](std::shared_ptr<ConnectionState> connectionState,
WebSocket& webSocket,
const ix::WebSocketMessagePtr& msg) {
auto remoteIp = connectionInfo.remoteIp;
auto remoteIp = connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Open)
{
TLogger() << "New connection";
@ -244,4 +245,33 @@ namespace ix
return endpoint;
}
void runPublisher(const ix::CobraConfig& config, const std::string& channel)
{
ix::CobraMetricsPublisher cobraMetricsPublisher;
cobraMetricsPublisher.configure(config, channel);
cobraMetricsPublisher.setSession(uuid4());
cobraMetricsPublisher.enable(true);
Json::Value msg;
msg["fps"] = 60;
cobraMetricsPublisher.setGenericAttributes("game", "ody");
// Wait a bit
ix::msleep(500);
// publish some messages
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #1)
cobraMetricsPublisher.push("sms_metric_B_id", msg); // (msg #2)
ix::msleep(500);
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #3)
cobraMetricsPublisher.push("sms_metric_D_id", msg); // (msg #4)
ix::msleep(500);
cobraMetricsPublisher.push("sms_metric_A_id", msg); // (msg #4)
cobraMetricsPublisher.push("sms_metric_F_id", msg); // (msg #5)
ix::msleep(500);
}
} // namespace ix

View File

@ -7,6 +7,7 @@
#pragma once
#include <iostream>
#include <ixcobra/IXCobraConfig.h>
#include <ixsnake/IXAppConfig.h>
#include <ixwebsocket/IXGetFreePort.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
@ -59,4 +60,6 @@ namespace ix
std::string getWsScheme(bool preferTLS);
std::string makeCobraEndpoint(int port, bool preferTLS);
void runPublisher(const ix::CobraConfig& config, const std::string& channel);
} // namespace ix

View File

@ -18,10 +18,10 @@ using namespace ix;
namespace
{
class WebSocketChat
class WebSocketBroadcastChat
{
public:
WebSocketChat(const std::string& user, const std::string& session, int port);
WebSocketBroadcastChat(const std::string& user, const std::string& session, int port);
void subscribe(const std::string& channel);
void start();
@ -47,7 +47,9 @@ namespace
mutable std::mutex _mutex;
};
WebSocketChat::WebSocketChat(const std::string& user, const std::string& session, int port)
WebSocketBroadcastChat::WebSocketBroadcastChat(const std::string& user,
const std::string& session,
int port)
: _user(user)
, _session(session)
, _port(port)
@ -55,35 +57,35 @@ namespace
_webSocket.setTLSOptions(makeClientTLSOptions());
}
size_t WebSocketChat::getReceivedMessagesCount() const
size_t WebSocketBroadcastChat::getReceivedMessagesCount() const
{
std::lock_guard<std::mutex> lock(_mutex);
return _receivedMessages.size();
}
const std::vector<std::string>& WebSocketChat::getReceivedMessages() const
const std::vector<std::string>& WebSocketBroadcastChat::getReceivedMessages() const
{
std::lock_guard<std::mutex> lock(_mutex);
return _receivedMessages;
}
void WebSocketChat::appendMessage(const std::string& message)
void WebSocketBroadcastChat::appendMessage(const std::string& message)
{
std::lock_guard<std::mutex> lock(_mutex);
_receivedMessages.push_back(message);
}
bool WebSocketChat::isReady() const
bool WebSocketBroadcastChat::isReady() const
{
return _webSocket.getReadyState() == ix::ReadyState::Open;
}
void WebSocketChat::stop()
void WebSocketBroadcastChat::stop()
{
_webSocket.stop();
}
void WebSocketChat::start()
void WebSocketBroadcastChat::start()
{
std::string url;
{
@ -156,7 +158,8 @@ namespace
_webSocket.start();
}
std::pair<std::string, std::string> WebSocketChat::decodeMessage(const std::string& str)
std::pair<std::string, std::string> WebSocketBroadcastChat::decodeMessage(
const std::string& str)
{
std::string errMsg;
MsgPack msg = MsgPack::parse(str, errMsg);
@ -167,7 +170,7 @@ namespace
return std::pair<std::string, std::string>(msg_user, msg_text);
}
std::string WebSocketChat::encodeMessage(const std::string& text)
std::string WebSocketBroadcastChat::encodeMessage(const std::string& text)
{
std::map<MsgPack, MsgPack> obj;
obj["user"] = _user;
@ -179,7 +182,7 @@ namespace
return output;
}
void WebSocketChat::sendMessage(const std::string& text)
void WebSocketBroadcastChat::sendMessage(const std::string& text)
{
_webSocket.sendBinary(encodeMessage(text));
}
@ -191,11 +194,9 @@ namespace
server.setOnClientMessageCallback(
[&server, &connectionId](std::shared_ptr<ConnectionState> connectionState,
ConnectionInfo& connectionInfo,
WebSocket& webSocket,
const ix::WebSocketMessagePtr& msg) {
auto remoteIp = connectionInfo.remoteIp;
auto remoteIp = connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Open)
{
@ -250,11 +251,11 @@ TEST_CASE("Websocket_broadcast_server", "[websocket_server]")
REQUIRE(startServer(server, connectionId));
std::string session = ix::generateSessionId();
std::vector<std::shared_ptr<WebSocketChat>> chatClients;
std::vector<std::shared_ptr<WebSocketBroadcastChat>> chatClients;
for (int i = 0; i < 10; ++i)
{
std::string user("user_" + std::to_string(i));
chatClients.push_back(std::make_shared<WebSocketChat>(user, session, port));
chatClients.push_back(std::make_shared<WebSocketBroadcastChat>(user, session, port));
chatClients[i]->start();
ix::msleep(50);
}

View File

@ -195,10 +195,9 @@ namespace
{
server.setOnClientMessageCallback(
[&server](std::shared_ptr<ConnectionState> connectionState,
ConnectionInfo& connectionInfo,
WebSocket& webSocket,
const ix::WebSocketMessagePtr& msg) {
auto remoteIp = connectionInfo.remoteIp;
auto remoteIp = connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Open)
{
TLogger() << "New connection";
@ -286,27 +285,27 @@ TEST_CASE("Websocket_chat", "[websocket_chat]")
int attempts = 0;
while (chatA.getReceivedMessagesCount() != 3 || chatB.getReceivedMessagesCount() != 3)
{
REQUIRE(attempts++ < 10);
CHECK(attempts++ < 10);
ix::msleep(1000);
}
chatA.stop();
chatB.stop();
REQUIRE(chatA.getReceivedMessagesCount() == 3);
REQUIRE(chatB.getReceivedMessagesCount() == 3);
CHECK(chatA.getReceivedMessagesCount() == 3);
CHECK(chatB.getReceivedMessagesCount() == 3);
REQUIRE(chatB.getReceivedMessages()[0] == "from A1");
REQUIRE(chatB.getReceivedMessages()[1] == "from A2");
REQUIRE(chatB.getReceivedMessages()[2] == "from A3");
CHECK(chatB.getReceivedMessages()[0] == "from A1");
CHECK(chatB.getReceivedMessages()[1] == "from A2");
CHECK(chatB.getReceivedMessages()[2] == "from A3");
REQUIRE(chatA.getReceivedMessages()[0] == "from B1");
REQUIRE(chatA.getReceivedMessages()[1] == "from B2");
REQUIRE(chatA.getReceivedMessages()[2].size() == bigMessage.size());
CHECK(chatA.getReceivedMessages()[0] == "from B1");
CHECK(chatA.getReceivedMessages()[1] == "from B2");
CHECK(chatA.getReceivedMessages()[2].size() == bigMessage.size());
// Give us 1000ms for the server to notice that clients went away
ix::msleep(1000);
REQUIRE(server.getClients().size() == 0);
CHECK(server.getClients().size() == 0);
ix::reportWebSocketTraffic();
}

View File

@ -171,10 +171,9 @@ namespace
server.setOnClientMessageCallback(
[&receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](
std::shared_ptr<ConnectionState> connectionState,
ConnectionInfo& connectionInfo,
WebSocket& /*webSocket*/,
const ix::WebSocketMessagePtr& msg) {
auto remoteIp = connectionInfo.remoteIp;
auto remoteIp = connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Open)
{
TLogger() << "New server connection";

View File

@ -35,11 +35,9 @@ namespace ix
server.setOnClientMessageCallback(
[&server, &connectionId](std::shared_ptr<ConnectionState> connectionState,
ConnectionInfo& connectionInfo,
WebSocket& webSocket,
const ix::WebSocketMessagePtr& msg) {
auto remoteIp = connectionInfo.remoteIp;
auto remoteIp = connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Open)
{

View File

@ -18,10 +18,9 @@ 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;
auto remoteIp = connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Open)
{
TLogger() << "New connection";

View File

@ -0,0 +1,6 @@
```
export GEM_HOME=$HOME/local/gems
bundle install faye-websocket
```
https://stackoverflow.com/questions/486995/ruby-equivalent-of-virtualenv

View File

@ -0,0 +1,59 @@
#
# $ ruby --version
# ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin19]
#
# Install a gem locally by setting GEM_HOME
# https://stackoverflow.com/questions/486995/ruby-equivalent-of-virtualenv
# export GEM_HOME=$HOME/local/gems
# bundle install faye-websocket
#
# In a different terminal, start a push server:
# $ ws push_server -q
#
# $ ruby devnull_client.rb
# [:open]
# Connected to server
# messages received per second: 115926
# messages received per second: 119156
# messages received per second: 119156
# messages received per second: 119157
# messages received per second: 119156
# messages received per second: 119156
# messages received per second: 119157
# messages received per second: 119156
# messages received per second: 119156
# messages received per second: 119157
# messages received per second: 119156
# messages received per second: 119157
# messages received per second: 119156
# ^C[:close, 1006, ""]
#
require 'faye/websocket'
require 'eventmachine'
EM.run {
ws = Faye::WebSocket::Client.new('ws://127.0.0.1:8008')
counter = 0
EM.add_periodic_timer(1) do
print "messages received per second: #{counter}\n"
counter = 0 # reset counter
end
ws.on :open do |event|
p [:open]
print "Connected to server\n"
end
ws.on :message do |event|
# Uncomment the next line to validate that we receive something correct
# p [:message, event.data]
counter += 1
end
ws.on :close do |event|
p [:close, event.code, event.reason]
ws = nil
end
}

View File

@ -1,108 +0,0 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 140
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
...

View File

@ -1,54 +0,0 @@
Checks: 'cppcoreguidelines-*,
performance-*,
modernize-*,
google-*,
misc-*,
cert-*,
readability-*,
clang-analyzer-*,
-performance-unnecessary-value-param,
-modernize-use-trailing-return-type,
-google-runtime-references,
-misc-non-private-member-variables-in-classes,
-readability-braces-around-statements,
-google-readability-braces-around-statements,
-cppcoreguidelines-avoid-magic-numbers,
-readability-magic-numbers,
-readability-magic-numbers,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-avoid-c-arrays,
-modernize-avoid-c-arrays,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-readability-named-parameter,
-cert-env33-c
'
WarningsAsErrors: ''
HeaderFilterRegex: '*spdlog/[^f].*'
AnalyzeTemporaryDtors: false
FormatStyle: none
CheckOptions:
- key: google-readability-braces-around-statements.ShortStatementLines
value: '1'
- key: google-readability-function-size.StatementThreshold
value: '800'
- key: google-readability-namespace-comments.ShortNamespaceLines
value: '10'
- key: google-readability-namespace-comments.SpacesBeforeComments
value: '2'
- key: modernize-loop-convert.MaxCopySize
value: '16'
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'

View File

@ -1 +0,0 @@
* text=false

View File

@ -1,83 +0,0 @@
# Auto generated files
build/*
*.slo
*.lo
*.o
*.obj
*.suo
*.tlog
*.ilk
*.log
*.pdb
*.idb
*.iobj
*.ipdb
*.opensdf
*.sdf
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# Codelite
.codelite
# KDevelop
*.kdev4
# .orig files
*.orig
# example files
example/*
!example/example.cpp
!example/bench.cpp
!example/utils.h
!example/Makefile*
!example/example.sln
!example/example.vcxproj
!example/CMakeLists.txt
!example/meson.build
!example/multisink.cpp
!example/jni
# generated files
generated
# Cmake
CMakeCache.txt
CMakeFiles
CMakeScripts
Makefile
cmake_install.cmake
install_manifest.txt
/tests/tests.VC.VC.opendb
/tests/tests.VC.db
/tests/tests
/tests/logs/*
# idea
.idea/
cmake-build-*/
*.db
*.ipch
*.filters
*.db-wal
*.opendb
*.db-shm
*.vcxproj
*.tcl
*.user
*.sln

View File

@ -1,112 +0,0 @@
# Adapted from various sources, including:
# - Louis Dionne's Hana: https://github.com/ldionne/hana
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
sudo: required
language: cpp
# gcc 4.8
addons: &gcc48
apt:
packages:
- g++-4.8
sources:
- ubuntu-toolchain-r-test
# gcc 7.0
addons: &gcc7
apt:
packages:
- g++-7
sources:
- ubuntu-toolchain-r-test
# Clang 3.5
addons: &clang35
apt:
packages:
- clang-3.5
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.5
# Clang 7.0
addons: &clang70
apt:
packages:
- clang-7
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-7
matrix:
include:
# Test gcc-4.8: C++11, Build=Debug/Release
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11
os: linux
addons: *gcc48
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11
os: linux
addons: *gcc48
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11
os: linux
addons: *gcc7
# Test clang-3.5: C++11, Build=Debug/Release
- env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11
os: linux
addons: *clang35
- env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11
os: linux
addons: *clang35
# Test clang-7.0: C++11, Build=Debug, ASAN=On
- env: CLANG_VERSION=7 BUILD_TYPE=Debug CPP=11 ASAN=On TSAN=Off
dist: bionic
- env: CLANG_VERSION=7 BUILD_TYPE=Release CPP=11 ASAN=On TSAN=Off
dist: bionic
# osx
- env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off
os: osx
before_script:
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export CXX="clang++" CC="clang"; fi
- which $CXX
- which $CC
- $CXX --version
- cmake --version
script:
- cd ${TRAVIS_BUILD_DIR}
- mkdir -p build && cd build
- |
cmake .. \
--warn-uninitialized \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_CXX_STANDARD=$CPP \
-DSPDLOG_BUILD_EXAMPLE=ON \
-DSPDLOG_BUILD_EXAMPLE_HO=ON \
-DSPDLOG_ENABLE_WARNINGS=ON \
-DSPDLOG_BUILD_BENCH=OFF \
-DSPDLOG_BUILD_TESTS=ON \
-DSPDLOG_BUILD_TESTS_HO=OFf \
-DSPDLOG_SANITIZE_ADDRESS=$ASAN
- make VERBOSE=1 -j2
- ctest -j2 --output-on-failure
notifications:
email: false

Some files were not shown because too many files have changed in this diff Show More