Compare commits
	
		
			56 Commits
		
	
	
		
			feature/pi
			...
			feature/us
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					5e1a4541bf | ||
| 
						 | 
					2e9c610ac9 | ||
| 
						 | 
					eb063ec60a | ||
| 
						 | 
					37fb14646d | ||
| 
						 | 
					ae543518d3 | ||
| 
						 | 
					c865d64608 | ||
| 
						 | 
					3004422cb6 | ||
| 
						 | 
					0c46a17443 | ||
| 
						 | 
					497373d976 | ||
| 
						 | 
					91198aca0d | ||
| 
						 | 
					b17a5e5f0b | ||
| 
						 | 
					3f0ef59f65 | ||
| 
						 | 
					1e96edc293 | ||
| 
						 | 
					0afb77393b | ||
| 
						 | 
					7614b642bb | ||
| 
						 | 
					bc89580dfe | ||
| 
						 | 
					358ae13a88 | ||
| 
						 | 
					ccf9dcba70 | ||
| 
						 | 
					94604fad61 | ||
| 
						 | 
					5c4cc7c50d | ||
| 
						 | 
					9ed961ec06 | ||
| 
						 | 
					e6bd8cc8c4 | ||
| 
						 | 
					ee25bd0f92 | ||
| 
						 | 
					e77b9176f3 | ||
| 
						 | 
					afe8b966ad | ||
| 
						 | 
					310724c961 | ||
| 
						 | 
					ceba8ae620 | ||
| 
						 | 
					fead661ab7 | ||
| 
						 | 
					9c8c17f577 | ||
| 
						 | 
					a04f83930f | ||
| 
						 | 
					c421d19800 | ||
| 
						 | 
					521f02c90e | ||
| 
						 | 
					c86b6074f2 | ||
| 
						 | 
					d5d1a2c5f4 | ||
| 
						 | 
					2a90e3f478 | ||
| 
						 | 
					1d49ba41ea | ||
| 
						 | 
					e1de1f6682 | ||
| 
						 | 
					47ed5e4d4d | ||
| 
						 | 
					d77f6f5659 | ||
| 
						 | 
					05f0045d5d | ||
| 
						 | 
					c4afb84f6e | ||
| 
						 | 
					b0b2f9b6d2 | ||
| 
						 | 
					ee37feb489 | ||
| 
						 | 
					6b8337596f | ||
| 
						 | 
					250665b92e | ||
| 
						 | 
					86b83c889e | ||
| 
						 | 
					c9c657c07b | ||
| 
						 | 
					4f2babaf54 | ||
| 
						 | 
					1b03bf4555 | ||
| 
						 | 
					977b995af9 | ||
| 
						 | 
					310ab990bd | ||
| 
						 | 
					d6b49b54d4 | ||
| 
						 | 
					f00cf39462 | ||
| 
						 | 
					18550cf1cb | ||
| 
						 | 
					168918f807 | ||
| 
						 | 
					2750df8aa7 | 
							
								
								
									
										0
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -15,8 +15,11 @@ if (NOT WIN32)
 | 
			
		||||
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
 | 
			
		||||
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshorten-64-to-32")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
set( IXWEBSOCKET_SOURCES
 | 
			
		||||
    ixwebsocket/IXEventFd.cpp
 | 
			
		||||
    ixwebsocket/IXSocket.cpp
 | 
			
		||||
    ixwebsocket/IXSocketServer.cpp
 | 
			
		||||
    ixwebsocket/IXSocketConnect.cpp
 | 
			
		||||
@@ -33,10 +36,13 @@ set( IXWEBSOCKET_SOURCES
 | 
			
		||||
    ixwebsocket/IXWebSocketHttpHeaders.cpp
 | 
			
		||||
    ixwebsocket/IXHttpClient.cpp
 | 
			
		||||
    ixwebsocket/IXUrlParser.cpp
 | 
			
		||||
    ixwebsocket/IXSelectInterrupt.cpp
 | 
			
		||||
    ixwebsocket/IXSelectInterruptPipe.cpp
 | 
			
		||||
    ixwebsocket/IXSelectInterruptFactory.cpp
 | 
			
		||||
    ixwebsocket/IXConnectionState.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set( IXWEBSOCKET_HEADERS
 | 
			
		||||
    ixwebsocket/IXEventFd.h
 | 
			
		||||
    ixwebsocket/IXSocket.h
 | 
			
		||||
    ixwebsocket/IXSocketServer.h
 | 
			
		||||
    ixwebsocket/IXSocketConnect.h
 | 
			
		||||
@@ -58,6 +64,10 @@ set( IXWEBSOCKET_HEADERS
 | 
			
		||||
    ixwebsocket/libwshandshake.hpp
 | 
			
		||||
    ixwebsocket/IXHttpClient.h
 | 
			
		||||
    ixwebsocket/IXUrlParser.h
 | 
			
		||||
    ixwebsocket/IXSelectInterrupt.h
 | 
			
		||||
    ixwebsocket/IXSelectInterruptPipe.h
 | 
			
		||||
    ixwebsocket/IXSelectInterruptFactory.h
 | 
			
		||||
    ixwebsocket/IXConnectionState.h
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Platform specific code
 | 
			
		||||
@@ -67,6 +77,8 @@ elseif (WIN32)
 | 
			
		||||
    list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/windows/IXSetThreadName_windows.cpp)
 | 
			
		||||
else()
 | 
			
		||||
    list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/linux/IXSetThreadName_linux.cpp)
 | 
			
		||||
    list( APPEND IXWEBSOCKET_SOURCES ixwebsocket/IXSelectInterruptEventFd.cpp)
 | 
			
		||||
    list( APPEND IXWEBSOCKET_HEADERS ixwebsocket/IXSelectInterruptEventFd.h)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (USE_TLS)
 | 
			
		||||
@@ -123,3 +135,4 @@ set( IXWEBSOCKET_INCLUDE_DIRS
 | 
			
		||||
target_include_directories( ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS} )
 | 
			
		||||
 | 
			
		||||
add_subdirectory(ws)
 | 
			
		||||
add_subdirectory(third_party/cpp_redis)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								DOCKER_VERSION
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
			
		||||
1.3.2
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
docker/Dockerfile.debian
 | 
			
		||||
Dockerfile.dev
 | 
			
		||||
@@ -12,11 +12,20 @@ RUN apt-get -y install libz-dev
 | 
			
		||||
RUN apt-get -y install vim
 | 
			
		||||
RUN apt-get -y install make
 | 
			
		||||
RUN apt-get -y install cmake
 | 
			
		||||
RUN apt-get -y install curl
 | 
			
		||||
RUN apt-get -y install python
 | 
			
		||||
RUN apt-get -y install netcat
 | 
			
		||||
 | 
			
		||||
# debian strech cmake is too old for building with Docker
 | 
			
		||||
COPY makefile .
 | 
			
		||||
RUN ["make", "install_cmake_for_linux"]
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
WORKDIR ws
 | 
			
		||||
RUN ["sh", "docker_build.sh"]
 | 
			
		||||
ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-rc4-Linux-x86_64/bin
 | 
			
		||||
ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
 | 
			
		||||
 | 
			
		||||
# RUN ["make"]
 | 
			
		||||
 | 
			
		||||
EXPOSE 8765
 | 
			
		||||
CMD ["/ws/ws", "transfer", "--port", "8765", "--hostname", "0.0.0.0"]
 | 
			
		||||
CMD ["/ws/ws", "transfer", "--port", "8765", "--host", "0.0.0.0"]
 | 
			
		||||
							
								
								
									
										30
									
								
								Dockerfile.prod
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,30 @@
 | 
			
		||||
FROM debian:buster
 | 
			
		||||
 | 
			
		||||
ENV DEBIAN_FRONTEND noninteractive
 | 
			
		||||
RUN apt-get update 
 | 
			
		||||
 | 
			
		||||
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 wget 
 | 
			
		||||
RUN mkdir -p /tmp/cmake
 | 
			
		||||
WORKDIR /tmp/cmake
 | 
			
		||||
RUN wget 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 adduser app 
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-Linux-x86_64/bin
 | 
			
		||||
ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
 | 
			
		||||
 | 
			
		||||
RUN ["make"]
 | 
			
		||||
 | 
			
		||||
# Now run in usermode
 | 
			
		||||
USER app
 | 
			
		||||
 | 
			
		||||
EXPOSE 8765
 | 
			
		||||
CMD ["bash"]
 | 
			
		||||
							
								
								
									
										38
									
								
								README.md
									
									
									
									
									
								
							
							
						
						@@ -4,14 +4,14 @@
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
 | 
			
		||||
[*WebSocket*](https://en.wikipedia.org/wiki/WebSocket) is a computer communications protocol, providing full-duplex
 | 
			
		||||
communication channels over a single TCP connection. *IXWebSocket* is a C++ library for client and server Websocket communication, and for client HTTP communication. The code is derived from [easywsclient](https://github.com/dhbaird/easywsclient) and from the [Satori C SDK](https://github.com/satori-com/satori-rtm-sdk-c). It has been tested on the following platforms.
 | 
			
		||||
[*WebSocket*](https://en.wikipedia.org/wiki/WebSocket) is a computer communications protocol, providing full-duplex and bi-directionnal communication channels over a single TCP connection. *IXWebSocket* is a C++ library for client and server Websocket communication, and for client HTTP communication. The code is derived from [easywsclient](https://github.com/dhbaird/easywsclient) and from the [Satori C SDK](https://github.com/satori-com/satori-rtm-sdk-c). It has been tested on the following platforms.
 | 
			
		||||
 | 
			
		||||
* macOS
 | 
			
		||||
* iOS
 | 
			
		||||
* Linux
 | 
			
		||||
* Android 
 | 
			
		||||
* Windows (no TLS support yet)
 | 
			
		||||
* Android
 | 
			
		||||
 | 
			
		||||
The code was made to compile once on Windows but support is currently broken on this platform.
 | 
			
		||||
 | 
			
		||||
## Examples
 | 
			
		||||
 | 
			
		||||
@@ -47,9 +47,12 @@ webSocket.setOnMessageCallback(
 | 
			
		||||
// Now that our callback is setup, we can start our background thread and receive messages
 | 
			
		||||
webSocket.start();
 | 
			
		||||
 | 
			
		||||
// Send a message to the server
 | 
			
		||||
// Send a message to the server (default to BINARY mode)
 | 
			
		||||
webSocket.send("hello world");
 | 
			
		||||
 | 
			
		||||
// The message can be sent in TEXT mode
 | 
			
		||||
webSocket.sendText("hello again");
 | 
			
		||||
 | 
			
		||||
// ... finally ...
 | 
			
		||||
 | 
			
		||||
// Stop the connection
 | 
			
		||||
@@ -64,10 +67,11 @@ Here is what the server API looks like. Note that server support is very recent
 | 
			
		||||
ix::WebSocketServer server(port);
 | 
			
		||||
 | 
			
		||||
server.setOnConnectionCallback(
 | 
			
		||||
    [&server](std::shared_ptr<ix::WebSocket> webSocket)
 | 
			
		||||
    [&server](std::shared_ptr<WebSocket> webSocket,
 | 
			
		||||
              std::shared_ptr<ConnectionState> connectionState)
 | 
			
		||||
    {
 | 
			
		||||
        webSocket->setOnMessageCallback(
 | 
			
		||||
            [webSocket, &server](ix::WebSocketMessageType messageType,
 | 
			
		||||
            [webSocket, connectionState, &server](ix::WebSocketMessageType messageType,
 | 
			
		||||
               const std::string& str,
 | 
			
		||||
               size_t wireSize,
 | 
			
		||||
               const ix::WebSocketErrorInfo& error,
 | 
			
		||||
@@ -78,6 +82,12 @@ server.setOnConnectionCallback(
 | 
			
		||||
                {
 | 
			
		||||
                    std::cerr << "New connection" << std::endl;
 | 
			
		||||
 | 
			
		||||
                    // A connection state object is available, and has a default id
 | 
			
		||||
                    // You can subclass ConnectionState and pass an alternate factory
 | 
			
		||||
                    // to override it. It is useful if you want to store custom
 | 
			
		||||
                    // attributes per connection (authenticated bool flag, attributes, etc...)
 | 
			
		||||
                    std::cerr << "id: " << connectionState->getId() << std::endl;
 | 
			
		||||
 | 
			
		||||
                    // The uri the client did connect to.
 | 
			
		||||
                    std::cerr << "Uri: " << openInfo.uri << std::endl;
 | 
			
		||||
 | 
			
		||||
@@ -125,7 +135,7 @@ HttpRequestArgs args;
 | 
			
		||||
// Custom headers can be set
 | 
			
		||||
WebSocketHttpHeaders headers;
 | 
			
		||||
headers["Foo"] = "bar";
 | 
			
		||||
args.extraHeaders = parseHeaders(headersData);
 | 
			
		||||
args.extraHeaders = headers;
 | 
			
		||||
 | 
			
		||||
// Timeout options
 | 
			
		||||
args.connectTimeout = connectTimeout;
 | 
			
		||||
@@ -184,7 +194,7 @@ There is a Dockerfile for running some code on Linux, and a unittest which can b
 | 
			
		||||
You can build and install the ws command line tool with Homebrew.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
brew create --cmake https://github.com/machinezone/IXWebSocket/archive/v1.1.0.tar.gz
 | 
			
		||||
brew tap bsergean/IXWebSocket
 | 
			
		||||
brew install IXWebSocket
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
@@ -208,11 +218,11 @@ If the remote end (server) breaks the connection, the code will try to perpetual
 | 
			
		||||
 | 
			
		||||
### Large messages
 | 
			
		||||
 | 
			
		||||
Large frames are broken up into smaller chunks or messages to avoid filling up the os tcp buffers, which is permitted thanks to WebSocket [fragmentation](https://tools.ietf.org/html/rfc6455#section-5.4). Messages up to 500M were sent and received succesfully.
 | 
			
		||||
Large frames are broken up into smaller chunks or messages to avoid filling up the os tcp buffers, which is permitted thanks to WebSocket [fragmentation](https://tools.ietf.org/html/rfc6455#section-5.4). Messages up to 1G were sent and received succesfully.
 | 
			
		||||
 | 
			
		||||
## Limitations
 | 
			
		||||
 | 
			
		||||
* There is no text support for sending data, only the binary protocol is supported. Sending json or text over the binary protocol works well.
 | 
			
		||||
* No utf-8 validation is made when sending TEXT message with sendText()
 | 
			
		||||
* Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that.
 | 
			
		||||
* The server code is using select to detect incoming data, and creates one OS thread per connection. This is not as scalable as strategies using epoll or kqueue.
 | 
			
		||||
 | 
			
		||||
@@ -224,13 +234,13 @@ Here is a simplistic diagram which explains how the code is structured in term o
 | 
			
		||||
+-----------------------+ --- Public
 | 
			
		||||
|                       | Start the receiving Background thread. Auto reconnection. Simple websocket Ping.
 | 
			
		||||
|  IXWebSocket          | Interface used by C++ test clients. No IX dependencies.
 | 
			
		||||
|                       | 
 | 
			
		||||
|                       |
 | 
			
		||||
+-----------------------+
 | 
			
		||||
|                       |
 | 
			
		||||
|  IXWebSocketServer    | Run a server and give each connections its own WebSocket object.
 | 
			
		||||
|                       | Each connection is handled in a new OS thread.
 | 
			
		||||
|                       |
 | 
			
		||||
+-----------------------+ --- Private 
 | 
			
		||||
+-----------------------+ --- Private
 | 
			
		||||
|                       |
 | 
			
		||||
|  IXWebSocketTransport | Low level websocket code, framing, managing raw socket. Adapted from easywsclient.
 | 
			
		||||
|                       |
 | 
			
		||||
@@ -380,7 +390,7 @@ websocket.ping("ping data, optional (empty string is ok): limited to 125 bytes l
 | 
			
		||||
### Heartbeat.
 | 
			
		||||
 | 
			
		||||
You can configure an optional heart beat / keep-alive, sent every 45 seconds
 | 
			
		||||
when there is not any traffic to make sure that load balancers do not kill an
 | 
			
		||||
when there is no any traffic to make sure that load balancers do not kill an
 | 
			
		||||
idle connection.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/brocoli.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/grafana_critical_logs.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 94 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/grafana_zlib.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 80 KiB  | 
							
								
								
									
										2
									
								
								doc/redis_conf_2019/makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,2 @@
 | 
			
		||||
all:
 | 
			
		||||
	(cd .. ; make docker && make docker_push)
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/mz_engine.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 74 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/neo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 118 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/neo_map.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 113 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/neo_session.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 168 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/redisconf_10_years.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 673 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/redisconf_first_slide.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.5 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/redisconf_last_slide.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.5 MiB  | 
							
								
								
									
										18
									
								
								doc/redis_conf_2019/remark-latest.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/sentry.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 90 KiB  | 
							
								
								
									
										1164
									
								
								doc/redis_conf_2019/slides.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								doc/redis_conf_2019/tableau.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 36 KiB  | 
							
								
								
									
										21
									
								
								docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,21 @@
 | 
			
		||||
version: "3"
 | 
			
		||||
services:
 | 
			
		||||
  ws:
 | 
			
		||||
    stdin_open: true
 | 
			
		||||
    tty: true
 | 
			
		||||
    image: bsergean/ws:build
 | 
			
		||||
    ports:
 | 
			
		||||
      - "8765:8765"
 | 
			
		||||
    entrypoint: bash
 | 
			
		||||
    networks:
 | 
			
		||||
      - ws-net
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - redis1
 | 
			
		||||
 | 
			
		||||
  redis1:
 | 
			
		||||
    image: redis:alpine
 | 
			
		||||
    networks:
 | 
			
		||||
      - ws-net
 | 
			
		||||
 | 
			
		||||
networks:
 | 
			
		||||
  ws-net:
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
FROM debian:stretch
 | 
			
		||||
 | 
			
		||||
# RUN yum install -y gcc-c++ make cmake openssl-devel gdb
 | 
			
		||||
ENV DEBIAN_FRONTEND noninteractive
 | 
			
		||||
RUN apt-get update 
 | 
			
		||||
RUN apt-get -y install g++ 
 | 
			
		||||
RUN apt-get -y install libssl-dev
 | 
			
		||||
RUN apt-get -y install gdb
 | 
			
		||||
RUN apt-get -y install screen
 | 
			
		||||
RUN apt-get -y install procps
 | 
			
		||||
RUN apt-get -y install lsof
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
WORKDIR examples/ws_connect
 | 
			
		||||
RUN ["sh", "build_linux.sh"]
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
FROM alpine:3.8
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache g++ musl-dev make cmake openssl-dev
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
WORKDIR examples/ws_connect
 | 
			
		||||
RUN ["sh", "build_linux.sh"]
 | 
			
		||||
 | 
			
		||||
EXPOSE 8765
 | 
			
		||||
CMD ["ws_connect"]
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
FROM alpine:3.8
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache g++ musl-dev make cmake openssl-dev
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
WORKDIR examples/ws_connect
 | 
			
		||||
RUN ["sh", "build_linux.sh"]
 | 
			
		||||
 | 
			
		||||
EXPOSE 8765
 | 
			
		||||
CMD ["ws_connect"]
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
FROM gcc:8
 | 
			
		||||
 | 
			
		||||
# RUN yum install -y gcc-c++ make cmake openssl-devel gdb
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
WORKDIR examples/ws_connect
 | 
			
		||||
RUN ["sh", "build_linux.sh"]
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXHash.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2018 Machine Zone. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    uint64_t djb2Hash(const std::string& data)
 | 
			
		||||
    {
 | 
			
		||||
        uint64_t hashAddress = 5381;
 | 
			
		||||
 | 
			
		||||
        for (auto& c : data)
 | 
			
		||||
        {
 | 
			
		||||
            hashAddress = ((hashAddress << 5) + hashAddress) + c;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return hashAddress;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXHash.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2018 Machine Zone. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    uint64_t djb2Hash(const std::string& data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,75 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXUuid.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2018 Machine Zone. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Generate a random uuid similar to the uuid python module
 | 
			
		||||
 *
 | 
			
		||||
 * >>> import uuid
 | 
			
		||||
 * >>> uuid.uuid4().hex
 | 
			
		||||
 * 'bec08155b37d4050a1f3c3fa0276bf12'
 | 
			
		||||
 *
 | 
			
		||||
 * Code adapted from https://github.com/r-lyeh-archived/sole
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXUuid.h"
 | 
			
		||||
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
#include <random>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class Uuid
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
            Uuid();
 | 
			
		||||
            std::string toString() const;
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            uint64_t _ab;
 | 
			
		||||
            uint64_t _cd;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Uuid::Uuid()
 | 
			
		||||
    {
 | 
			
		||||
        static std::random_device rd;
 | 
			
		||||
        static std::uniform_int_distribution<uint64_t> dist(0, (uint64_t)(~0));
 | 
			
		||||
 | 
			
		||||
        _ab = dist(rd);
 | 
			
		||||
        _cd = dist(rd);
 | 
			
		||||
 | 
			
		||||
        _ab = (_ab & 0xFFFFFFFFFFFF0FFFULL) | 0x0000000000004000ULL;
 | 
			
		||||
        _cd = (_cd & 0x3FFFFFFFFFFFFFFFULL) | 0x8000000000000000ULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string Uuid::toString() const
 | 
			
		||||
    {
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << std::hex << std::nouppercase << std::setfill('0');
 | 
			
		||||
 | 
			
		||||
        uint32_t a = (_ab >> 32);
 | 
			
		||||
        uint32_t b = (_ab & 0xFFFFFFFF);
 | 
			
		||||
        uint32_t c = (_cd >> 32);
 | 
			
		||||
        uint32_t d = (_cd & 0xFFFFFFFF);
 | 
			
		||||
 | 
			
		||||
        ss << std::setw(8) << (a);
 | 
			
		||||
        ss << std::setw(4) << (b >> 16);
 | 
			
		||||
        ss << std::setw(4) << (b & 0xFFFF);
 | 
			
		||||
        ss << std::setw(4) << (c >> 16 );
 | 
			
		||||
        ss << std::setw(4) << (c & 0xFFFF);
 | 
			
		||||
        ss << std::setw(8) << d;
 | 
			
		||||
 | 
			
		||||
        return ss.str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string uuid4()
 | 
			
		||||
    {
 | 
			
		||||
        Uuid id;
 | 
			
		||||
        return id.toString();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXUuid.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2017 Machine Zone. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
   /**
 | 
			
		||||
    * Generate a random uuid
 | 
			
		||||
    */
 | 
			
		||||
   std::string uuid4();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								examples/ws_receive/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1 +0,0 @@
 | 
			
		||||
build
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
# Author: Benjamin Sergeant
 | 
			
		||||
# Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
cmake_minimum_required (VERSION 3.4.1)
 | 
			
		||||
project (ws_receive)
 | 
			
		||||
 | 
			
		||||
# There's -Weverything too for clang
 | 
			
		||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshorten-64-to-32")
 | 
			
		||||
 | 
			
		||||
set (CMAKE_CXX_STANDARD 14)
 | 
			
		||||
 | 
			
		||||
option(USE_TLS "Add TLS support" ON)
 | 
			
		||||
 | 
			
		||||
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
 | 
			
		||||
 | 
			
		||||
include_directories(ws_receive .)
 | 
			
		||||
 | 
			
		||||
add_executable(ws_receive 
 | 
			
		||||
  jsoncpp/jsoncpp.cpp
 | 
			
		||||
  ixcrypto/IXBase64.cpp
 | 
			
		||||
  ixcrypto/IXHash.cpp
 | 
			
		||||
  ws_receive.cpp)
 | 
			
		||||
 | 
			
		||||
if (APPLE AND USE_TLS)
 | 
			
		||||
    target_link_libraries(ws_receive "-framework foundation" "-framework security")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
target_link_libraries(ws_receive ixwebsocket)
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
ws_receive is a simple server upload program. It needs to be used in conjonction with ws_send.
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
../cobra_publisher/ixcrypto
 | 
			
		||||
@@ -1,333 +0,0 @@
 | 
			
		||||
/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).
 | 
			
		||||
/// It is intended to be used with #include "json/json-forwards.h"
 | 
			
		||||
/// This header provides forward declaration for all JsonCpp types.
 | 
			
		||||
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Beginning of content of file: LICENSE
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
The JsonCpp library's source code, including accompanying documentation,
 | 
			
		||||
tests and demonstration applications, are licensed under the following
 | 
			
		||||
conditions...
 | 
			
		||||
 | 
			
		||||
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
 | 
			
		||||
jurisdictions which recognize such a disclaimer. In such jurisdictions,
 | 
			
		||||
this software is released into the Public Domain.
 | 
			
		||||
 | 
			
		||||
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
 | 
			
		||||
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
 | 
			
		||||
The JsonCpp Authors, and is released under the terms of the MIT License (see below).
 | 
			
		||||
 | 
			
		||||
In jurisdictions which recognize Public Domain property, the user of this
 | 
			
		||||
software may choose to accept it either as 1) Public Domain, 2) under the
 | 
			
		||||
conditions of the MIT License (see below), or 3) under the terms of dual
 | 
			
		||||
Public Domain/MIT License conditions described here, as they choose.
 | 
			
		||||
 | 
			
		||||
The MIT License is about as close to Public Domain as a license can get, and is
 | 
			
		||||
described in clear, concise terms at:
 | 
			
		||||
 | 
			
		||||
   http://en.wikipedia.org/wiki/MIT_License
 | 
			
		||||
 | 
			
		||||
The full text of the MIT License follows:
 | 
			
		||||
 | 
			
		||||
========================================================================
 | 
			
		||||
Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person
 | 
			
		||||
obtaining a copy of this software and associated documentation
 | 
			
		||||
files (the "Software"), to deal in the Software without
 | 
			
		||||
restriction, including without limitation the rights to use, copy,
 | 
			
		||||
modify, merge, publish, distribute, sublicense, and/or sell copies
 | 
			
		||||
of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be
 | 
			
		||||
included in all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
			
		||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
			
		||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | 
			
		||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 | 
			
		||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 | 
			
		||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
			
		||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
========================================================================
 | 
			
		||||
(END LICENSE TEXT)
 | 
			
		||||
 | 
			
		||||
The MIT license is compatible with both the GPL and commercial
 | 
			
		||||
software, affording one all of the rights of Public Domain with the
 | 
			
		||||
minor nuisance of being required to keep the above copyright notice
 | 
			
		||||
and license text in the source code. Note also that by accepting the
 | 
			
		||||
Public Domain "license" you can re-license your copy using whatever
 | 
			
		||||
license you like.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
// End of content of file: LICENSE
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
 | 
			
		||||
# define JSON_FORWARD_AMALGATED_H_INCLUDED
 | 
			
		||||
/// If defined, indicates that the source file is amalgated
 | 
			
		||||
/// to prevent private header inclusion.
 | 
			
		||||
#define JSON_IS_AMALGAMATION
 | 
			
		||||
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Beginning of content of file: include/json/config.h
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
 | 
			
		||||
// Distributed under MIT license, or public domain if desired and
 | 
			
		||||
// recognized in your jurisdiction.
 | 
			
		||||
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 | 
			
		||||
 | 
			
		||||
#ifndef JSON_CONFIG_H_INCLUDED
 | 
			
		||||
#define JSON_CONFIG_H_INCLUDED
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <string> //typedef String
 | 
			
		||||
#include <stdint.h> //typedef int64_t, uint64_t
 | 
			
		||||
 | 
			
		||||
/// If defined, indicates that json library is embedded in CppTL library.
 | 
			
		||||
//# define JSON_IN_CPPTL 1
 | 
			
		||||
 | 
			
		||||
/// If defined, indicates that json may leverage CppTL library
 | 
			
		||||
//#  define JSON_USE_CPPTL 1
 | 
			
		||||
/// If defined, indicates that cpptl vector based map should be used instead of
 | 
			
		||||
/// std::map
 | 
			
		||||
/// as Value container.
 | 
			
		||||
//#  define JSON_USE_CPPTL_SMALLMAP 1
 | 
			
		||||
 | 
			
		||||
// If non-zero, the library uses exceptions to report bad input instead of C
 | 
			
		||||
// assertion macros. The default is to use exceptions.
 | 
			
		||||
#ifndef JSON_USE_EXCEPTION
 | 
			
		||||
#define JSON_USE_EXCEPTION 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/// If defined, indicates that the source file is amalgated
 | 
			
		||||
/// to prevent private header inclusion.
 | 
			
		||||
/// Remarks: it is automatically defined in the generated amalgated header.
 | 
			
		||||
// #define JSON_IS_AMALGAMATION
 | 
			
		||||
 | 
			
		||||
#ifdef JSON_IN_CPPTL
 | 
			
		||||
#include <cpptl/config.h>
 | 
			
		||||
#ifndef JSON_USE_CPPTL
 | 
			
		||||
#define JSON_USE_CPPTL 1
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JSON_IN_CPPTL
 | 
			
		||||
#define JSON_API CPPTL_API
 | 
			
		||||
#elif defined(JSON_DLL_BUILD)
 | 
			
		||||
#if defined(_MSC_VER) || defined(__MINGW32__)
 | 
			
		||||
#define JSON_API __declspec(dllexport)
 | 
			
		||||
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
 | 
			
		||||
#endif // if defined(_MSC_VER)
 | 
			
		||||
#elif defined(JSON_DLL)
 | 
			
		||||
#if defined(_MSC_VER) || defined(__MINGW32__)
 | 
			
		||||
#define JSON_API __declspec(dllimport)
 | 
			
		||||
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
 | 
			
		||||
#endif // if defined(_MSC_VER)
 | 
			
		||||
#endif // ifdef JSON_IN_CPPTL
 | 
			
		||||
#if !defined(JSON_API)
 | 
			
		||||
#define JSON_API
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
 | 
			
		||||
// integer
 | 
			
		||||
// Storages, and 64 bits integer support is disabled.
 | 
			
		||||
// #define JSON_NO_INT64 1
 | 
			
		||||
 | 
			
		||||
#if defined(_MSC_VER) // MSVC
 | 
			
		||||
#  if _MSC_VER <= 1200 // MSVC 6
 | 
			
		||||
    // Microsoft Visual Studio 6 only support conversion from __int64 to double
 | 
			
		||||
    // (no conversion from unsigned __int64).
 | 
			
		||||
#    define JSON_USE_INT64_DOUBLE_CONVERSION 1
 | 
			
		||||
    // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
 | 
			
		||||
    // characters in the debug information)
 | 
			
		||||
    // All projects I've ever seen with VS6 were using this globally (not bothering
 | 
			
		||||
    // with pragma push/pop).
 | 
			
		||||
#    pragma warning(disable : 4786)
 | 
			
		||||
#  endif // MSVC 6
 | 
			
		||||
 | 
			
		||||
#  if _MSC_VER >= 1500 // MSVC 2008
 | 
			
		||||
    /// Indicates that the following function is deprecated.
 | 
			
		||||
#    define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
 | 
			
		||||
#  endif
 | 
			
		||||
 | 
			
		||||
#endif // defined(_MSC_VER)
 | 
			
		||||
 | 
			
		||||
// In c++11 the override keyword allows you to explicity define that a function
 | 
			
		||||
// is intended to override the base-class version.  This makes the code more
 | 
			
		||||
// managable and fixes a set of common hard-to-find bugs.
 | 
			
		||||
#if __cplusplus >= 201103L
 | 
			
		||||
# define JSONCPP_OVERRIDE override
 | 
			
		||||
# define JSONCPP_NOEXCEPT noexcept
 | 
			
		||||
#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900
 | 
			
		||||
# define JSONCPP_OVERRIDE override
 | 
			
		||||
# define JSONCPP_NOEXCEPT throw()
 | 
			
		||||
#elif defined(_MSC_VER) && _MSC_VER >= 1900
 | 
			
		||||
# define JSONCPP_OVERRIDE override
 | 
			
		||||
# define JSONCPP_NOEXCEPT noexcept
 | 
			
		||||
#else
 | 
			
		||||
# define JSONCPP_OVERRIDE
 | 
			
		||||
# define JSONCPP_NOEXCEPT throw()
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef JSON_HAS_RVALUE_REFERENCES
 | 
			
		||||
 | 
			
		||||
#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
 | 
			
		||||
#define JSON_HAS_RVALUE_REFERENCES 1
 | 
			
		||||
#endif // MSVC >= 2010
 | 
			
		||||
 | 
			
		||||
#ifdef __clang__
 | 
			
		||||
#if __has_feature(cxx_rvalue_references)
 | 
			
		||||
#define JSON_HAS_RVALUE_REFERENCES 1
 | 
			
		||||
#endif  // has_feature
 | 
			
		||||
 | 
			
		||||
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
 | 
			
		||||
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
 | 
			
		||||
#define JSON_HAS_RVALUE_REFERENCES 1
 | 
			
		||||
#endif  // GXX_EXPERIMENTAL
 | 
			
		||||
 | 
			
		||||
#endif // __clang__ || __GNUC__
 | 
			
		||||
 | 
			
		||||
#endif // not defined JSON_HAS_RVALUE_REFERENCES
 | 
			
		||||
 | 
			
		||||
#ifndef JSON_HAS_RVALUE_REFERENCES
 | 
			
		||||
#define JSON_HAS_RVALUE_REFERENCES 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef __clang__
 | 
			
		||||
#  if __has_extension(attribute_deprecated_with_message)
 | 
			
		||||
#    define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))
 | 
			
		||||
#  endif
 | 
			
		||||
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
 | 
			
		||||
#  if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
 | 
			
		||||
#    define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))
 | 
			
		||||
#  elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
 | 
			
		||||
#    define JSONCPP_DEPRECATED(message)  __attribute__((__deprecated__))
 | 
			
		||||
#  endif  // GNUC version
 | 
			
		||||
#endif // __clang__ || __GNUC__
 | 
			
		||||
 | 
			
		||||
#if !defined(JSONCPP_DEPRECATED)
 | 
			
		||||
#define JSONCPP_DEPRECATED(message)
 | 
			
		||||
#endif // if !defined(JSONCPP_DEPRECATED)
 | 
			
		||||
 | 
			
		||||
#if __GNUC__ >= 6
 | 
			
		||||
#  define JSON_USE_INT64_DOUBLE_CONVERSION 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(JSON_IS_AMALGAMATION)
 | 
			
		||||
 | 
			
		||||
# include "version.h"
 | 
			
		||||
 | 
			
		||||
# if JSONCPP_USING_SECURE_MEMORY
 | 
			
		||||
#  include "allocator.h" //typedef Allocator
 | 
			
		||||
# endif
 | 
			
		||||
 | 
			
		||||
#endif // if !defined(JSON_IS_AMALGAMATION)
 | 
			
		||||
 | 
			
		||||
namespace Json {
 | 
			
		||||
typedef int Int;
 | 
			
		||||
typedef unsigned int UInt;
 | 
			
		||||
#if defined(JSON_NO_INT64)
 | 
			
		||||
typedef int LargestInt;
 | 
			
		||||
typedef unsigned int LargestUInt;
 | 
			
		||||
#undef JSON_HAS_INT64
 | 
			
		||||
#else                 // if defined(JSON_NO_INT64)
 | 
			
		||||
// For Microsoft Visual use specific types as long long is not supported
 | 
			
		||||
#if defined(_MSC_VER) // Microsoft Visual Studio
 | 
			
		||||
typedef __int64 Int64;
 | 
			
		||||
typedef unsigned __int64 UInt64;
 | 
			
		||||
#else                 // if defined(_MSC_VER) // Other platforms, use long long
 | 
			
		||||
typedef int64_t Int64;
 | 
			
		||||
typedef uint64_t UInt64;
 | 
			
		||||
#endif // if defined(_MSC_VER)
 | 
			
		||||
typedef Int64 LargestInt;
 | 
			
		||||
typedef UInt64 LargestUInt;
 | 
			
		||||
#define JSON_HAS_INT64
 | 
			
		||||
#endif // if defined(JSON_NO_INT64)
 | 
			
		||||
#if JSONCPP_USING_SECURE_MEMORY
 | 
			
		||||
#define JSONCPP_STRING        std::basic_string<char, std::char_traits<char>, Json::SecureAllocator<char> >
 | 
			
		||||
#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
 | 
			
		||||
#define JSONCPP_OSTREAM       std::basic_ostream<char, std::char_traits<char>>
 | 
			
		||||
#define JSONCPP_ISTRINGSTREAM std::basic_istringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
 | 
			
		||||
#define JSONCPP_ISTREAM       std::istream
 | 
			
		||||
#else
 | 
			
		||||
#define JSONCPP_STRING        std::string
 | 
			
		||||
#define JSONCPP_OSTRINGSTREAM std::ostringstream
 | 
			
		||||
#define JSONCPP_OSTREAM       std::ostream
 | 
			
		||||
#define JSONCPP_ISTRINGSTREAM std::istringstream
 | 
			
		||||
#define JSONCPP_ISTREAM       std::istream
 | 
			
		||||
#endif // if JSONCPP_USING_SECURE_MEMORY
 | 
			
		||||
} // end namespace Json
 | 
			
		||||
 | 
			
		||||
#endif // JSON_CONFIG_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
// End of content of file: include/json/config.h
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Beginning of content of file: include/json/forwards.h
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
 | 
			
		||||
// Distributed under MIT license, or public domain if desired and
 | 
			
		||||
// recognized in your jurisdiction.
 | 
			
		||||
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 | 
			
		||||
 | 
			
		||||
#ifndef JSON_FORWARDS_H_INCLUDED
 | 
			
		||||
#define JSON_FORWARDS_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#if !defined(JSON_IS_AMALGAMATION)
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif // if !defined(JSON_IS_AMALGAMATION)
 | 
			
		||||
 | 
			
		||||
namespace Json {
 | 
			
		||||
 | 
			
		||||
// writer.h
 | 
			
		||||
class FastWriter;
 | 
			
		||||
class StyledWriter;
 | 
			
		||||
 | 
			
		||||
// reader.h
 | 
			
		||||
class Reader;
 | 
			
		||||
 | 
			
		||||
// features.h
 | 
			
		||||
class Features;
 | 
			
		||||
 | 
			
		||||
// value.h
 | 
			
		||||
typedef unsigned int ArrayIndex;
 | 
			
		||||
class StaticString;
 | 
			
		||||
class Path;
 | 
			
		||||
class PathArgument;
 | 
			
		||||
class Value;
 | 
			
		||||
class ValueIteratorBase;
 | 
			
		||||
class ValueIterator;
 | 
			
		||||
class ValueConstIterator;
 | 
			
		||||
 | 
			
		||||
} // namespace Json
 | 
			
		||||
 | 
			
		||||
#endif // JSON_FORWARDS_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
// End of content of file: include/json/forwards.h
 | 
			
		||||
// //////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
 | 
			
		||||
							
								
								
									
										29
									
								
								examples/ws_receive/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						@@ -1,29 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "lockfileVersion": 1,
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "async-limiter": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
 | 
			
		||||
    },
 | 
			
		||||
    "base-64": {
 | 
			
		||||
      "version": "0.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
 | 
			
		||||
      "integrity": "sha1-eAqZyE59YAJgNhURxId2E78k9rs="
 | 
			
		||||
    },
 | 
			
		||||
    "djb2": {
 | 
			
		||||
      "version": "0.0.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/djb2/-/djb2-0.0.2.tgz",
 | 
			
		||||
      "integrity": "sha1-RAs4kao6uBQrVNRpsXe66p6W5O8="
 | 
			
		||||
    },
 | 
			
		||||
    "ws": {
 | 
			
		||||
      "version": "6.1.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
 | 
			
		||||
      "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "async-limiter": "1.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,153 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  ws_receive.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <ixwebsocket/IXWebSocketServer.h>
 | 
			
		||||
#include <jsoncpp/json/json.h>
 | 
			
		||||
#include <ixcrypto/IXBase64.h>
 | 
			
		||||
#include <ixcrypto/IXHash.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
    // We should cleanup the file name and full path further to remove .. as well
 | 
			
		||||
    std::string extractFilename(const std::string& path)
 | 
			
		||||
    {
 | 
			
		||||
        std::string filename("filename.conf");
 | 
			
		||||
        std::string::size_type idx;
 | 
			
		||||
 | 
			
		||||
        idx = path.rfind('/');
 | 
			
		||||
        if (idx != std::string::npos)
 | 
			
		||||
        {
 | 
			
		||||
            std::string filename = path.substr(idx+1);
 | 
			
		||||
            return filename;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return std::string();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    void errorHandler(const std::string& errMsg,
 | 
			
		||||
                      const std::string& id,
 | 
			
		||||
                      std::shared_ptr<ix::WebSocket> webSocket)
 | 
			
		||||
    {
 | 
			
		||||
        Json::Value pdu;
 | 
			
		||||
        pdu["kind"] = "error";
 | 
			
		||||
        pdu["id"] = id;
 | 
			
		||||
        pdu["message"] = errMsg;
 | 
			
		||||
        webSocket->send(pdu.toStyledString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void messageHandler(const std::string& str,
 | 
			
		||||
                        std::shared_ptr<ix::WebSocket> webSocket)
 | 
			
		||||
    {
 | 
			
		||||
        std::cerr << "Received message: " << str.size() << std::endl;
 | 
			
		||||
 | 
			
		||||
        Json::Value data;
 | 
			
		||||
        Json::Reader reader;
 | 
			
		||||
        if (!reader.parse(str, data))
 | 
			
		||||
        {
 | 
			
		||||
            errorHandler("Invalid JSON", std::string(), webSocket);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::cout << "id: " << data["id"].asString() << std::endl;
 | 
			
		||||
 | 
			
		||||
        std::string content = ix::base64_decode(data["content"].asString());
 | 
			
		||||
        std::cout << "Content size: " << content.size() << std::endl;
 | 
			
		||||
 | 
			
		||||
        // Validate checksum
 | 
			
		||||
        uint64_t cksum = ix::djb2Hash(data["content"].asString());
 | 
			
		||||
        uint64_t cksumRef = data["djb2_hash"].asUInt64();
 | 
			
		||||
 | 
			
		||||
        std::cout << "Computed hash: " << cksum << std::endl;
 | 
			
		||||
        std::cout << "Reference hash: " << cksumRef << std::endl;
 | 
			
		||||
 | 
			
		||||
        if (cksum != cksumRef)
 | 
			
		||||
        {
 | 
			
		||||
            errorHandler("Hash mismatch.", std::string(), webSocket);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::string filename = data["filename"].asString();
 | 
			
		||||
        filename = extractFilename(filename);
 | 
			
		||||
 | 
			
		||||
        std::ofstream out(filename);
 | 
			
		||||
        out << content;
 | 
			
		||||
        out.close();
 | 
			
		||||
 | 
			
		||||
        Json::Value pdu;
 | 
			
		||||
        pdu["ack"] = true;
 | 
			
		||||
        pdu["id"] = data["id"];
 | 
			
		||||
        pdu["filename"] = data["filename"];
 | 
			
		||||
        webSocket->send(pdu.toStyledString());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv)
 | 
			
		||||
{
 | 
			
		||||
    int port = 8080;
 | 
			
		||||
    if (argc == 2)
 | 
			
		||||
    {
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << argv[1];
 | 
			
		||||
        ss >> port;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ix::WebSocketServer server(port);
 | 
			
		||||
 | 
			
		||||
    server.setOnConnectionCallback(
 | 
			
		||||
        [&server](std::shared_ptr<ix::WebSocket> webSocket)
 | 
			
		||||
        {
 | 
			
		||||
            webSocket->setOnMessageCallback(
 | 
			
		||||
                [webSocket, &server](ix::WebSocketMessageType messageType,
 | 
			
		||||
                                     const std::string& str,
 | 
			
		||||
                                     size_t wireSize,
 | 
			
		||||
                                     const ix::WebSocketErrorInfo& error,
 | 
			
		||||
                                     const ix::WebSocketOpenInfo& openInfo,
 | 
			
		||||
                                     const ix::WebSocketCloseInfo& closeInfo)
 | 
			
		||||
                {
 | 
			
		||||
                    if (messageType == ix::WebSocket_MessageType_Open)
 | 
			
		||||
                    {
 | 
			
		||||
                        std::cerr << "New connection" << std::endl;
 | 
			
		||||
                        std::cerr << "Uri: " << openInfo.uri << std::endl;
 | 
			
		||||
                        std::cerr << "Headers:" << std::endl;
 | 
			
		||||
                        for (auto it : openInfo.headers)
 | 
			
		||||
                        {
 | 
			
		||||
                            std::cerr << it.first << ": " << it.second << std::endl;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (messageType == ix::WebSocket_MessageType_Close)
 | 
			
		||||
                    {
 | 
			
		||||
                        std::cerr << "Closed connection" << std::endl;
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (messageType == ix::WebSocket_MessageType_Message)
 | 
			
		||||
                    {
 | 
			
		||||
                        messageHandler(str, webSocket);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    auto res = server.listen();
 | 
			
		||||
    if (!res.first)
 | 
			
		||||
    {
 | 
			
		||||
        std::cerr << res.second << std::endl;
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    server.start();
 | 
			
		||||
    server.wait();
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  ws_receive.js
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
const WebSocket = require('ws')
 | 
			
		||||
const djb2 = require('djb2')
 | 
			
		||||
const fs = require('fs')
 | 
			
		||||
 | 
			
		||||
const wss = new WebSocket.Server({ port: 8080,
 | 
			
		||||
                                   perMessageDeflate: false,
 | 
			
		||||
                                   maxPayload: 1024 * 1024 * 1024 * 1024});
 | 
			
		||||
 | 
			
		||||
wss.on('connection', function connection(ws) {
 | 
			
		||||
  ws.on('message', function incoming(data) {
 | 
			
		||||
    console.log('Received message')
 | 
			
		||||
 | 
			
		||||
    let str = data.toString()
 | 
			
		||||
    let obj = JSON.parse(str)
 | 
			
		||||
 | 
			
		||||
    console.log(obj.id)
 | 
			
		||||
    console.log(obj.djb2_hash)
 | 
			
		||||
    console.log(djb2(obj.content))
 | 
			
		||||
 | 
			
		||||
    var content = Buffer.from(obj.content, 'base64')
 | 
			
		||||
    // let bytes = base64.decode(obj.content)
 | 
			
		||||
 | 
			
		||||
    let path = obj.filename
 | 
			
		||||
    fs.writeFile(path, content, function(err) {
 | 
			
		||||
      if (err) {
 | 
			
		||||
        throw err
 | 
			
		||||
      } else {
 | 
			
		||||
        console.log('wrote data to disk')
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let response = {
 | 
			
		||||
      id: obj.id
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ws.send(JSON.stringify(response))
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										1
									
								
								examples/ws_send/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1 +0,0 @@
 | 
			
		||||
build
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
# Author: Benjamin Sergeant
 | 
			
		||||
# Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
cmake_minimum_required (VERSION 3.4.1)
 | 
			
		||||
project (ws_send)
 | 
			
		||||
 | 
			
		||||
# There's -Weverything too for clang
 | 
			
		||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshorten-64-to-32")
 | 
			
		||||
 | 
			
		||||
set (CMAKE_CXX_STANDARD 14)
 | 
			
		||||
 | 
			
		||||
option(USE_TLS "Add TLS support" ON)
 | 
			
		||||
 | 
			
		||||
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
 | 
			
		||||
 | 
			
		||||
include_directories(ws_send .)
 | 
			
		||||
 | 
			
		||||
add_executable(ws_send 
 | 
			
		||||
  jsoncpp/jsoncpp.cpp
 | 
			
		||||
  ixcrypto/IXBase64.cpp
 | 
			
		||||
  ixcrypto/IXUuid.cpp
 | 
			
		||||
  ixcrypto/IXHash.cpp
 | 
			
		||||
  ws_send.cpp)
 | 
			
		||||
 | 
			
		||||
if (APPLE AND USE_TLS)
 | 
			
		||||
    target_link_libraries(ws_send "-framework foundation" "-framework security")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
target_link_libraries(ws_send ixwebsocket)
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
ws_send is a simple upload program. It needs to be used in conjonction with ws_receive.
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
../cobra_publisher/ixcrypto
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
../cobra_publisher/jsoncpp
 | 
			
		||||
@@ -1,306 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  ws_send.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <ixwebsocket/IXWebSocket.h>
 | 
			
		||||
#include <ixwebsocket/IXSocket.h>
 | 
			
		||||
#include <ixcrypto/IXUuid.h>
 | 
			
		||||
#include <ixcrypto/IXBase64.h>
 | 
			
		||||
#include <ixcrypto/IXHash.h>
 | 
			
		||||
#include <jsoncpp/json/json.h>
 | 
			
		||||
 | 
			
		||||
using namespace ix;
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
    void log(const std::string& msg)
 | 
			
		||||
    {
 | 
			
		||||
        std::cout << msg << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class WebSocketSender
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
            WebSocketSender(const std::string& _url,
 | 
			
		||||
                            bool enablePerMessageDeflate);
 | 
			
		||||
 | 
			
		||||
            void subscribe(const std::string& channel);
 | 
			
		||||
            void start();
 | 
			
		||||
            void stop();
 | 
			
		||||
 | 
			
		||||
            void waitForConnection();
 | 
			
		||||
            void waitForAck();
 | 
			
		||||
 | 
			
		||||
            void sendMessage(const std::string& filename, bool throttle);
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            std::string _url;
 | 
			
		||||
            std::string _id;
 | 
			
		||||
            ix::WebSocket _webSocket;
 | 
			
		||||
            bool _enablePerMessageDeflate;
 | 
			
		||||
 | 
			
		||||
            std::mutex _conditionVariableMutex;
 | 
			
		||||
            std::condition_variable _condition;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    WebSocketSender::WebSocketSender(const std::string& url,
 | 
			
		||||
                                     bool enablePerMessageDeflate) :
 | 
			
		||||
        _url(url),
 | 
			
		||||
        _enablePerMessageDeflate(enablePerMessageDeflate)
 | 
			
		||||
    {
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketSender::stop()
 | 
			
		||||
    {
 | 
			
		||||
        _webSocket.stop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketSender::waitForConnection()
 | 
			
		||||
    {
 | 
			
		||||
        std::cout << "Connecting..." << std::endl;
 | 
			
		||||
 | 
			
		||||
        std::unique_lock<std::mutex> lock(_conditionVariableMutex);
 | 
			
		||||
        _condition.wait(lock);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketSender::waitForAck()
 | 
			
		||||
    {
 | 
			
		||||
        std::cout << "Waiting for ack..." << std::endl;
 | 
			
		||||
 | 
			
		||||
        std::unique_lock<std::mutex> lock(_conditionVariableMutex);
 | 
			
		||||
        _condition.wait(lock);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string load(const std::string& path)
 | 
			
		||||
    {
 | 
			
		||||
        // std::vector<uint8_t> memblock;
 | 
			
		||||
        std::string str;
 | 
			
		||||
 | 
			
		||||
        std::ifstream file(path);
 | 
			
		||||
        if (!file.is_open()) return std::string();
 | 
			
		||||
 | 
			
		||||
        file.seekg(0, file.end);
 | 
			
		||||
        std::streamoff size = file.tellg();
 | 
			
		||||
        file.seekg(0, file.beg);
 | 
			
		||||
 | 
			
		||||
        str.resize(size);
 | 
			
		||||
        file.read((char*)&str.front(), static_cast<std::streamsize>(size));
 | 
			
		||||
 | 
			
		||||
        return str;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketSender::start()
 | 
			
		||||
    {
 | 
			
		||||
        _webSocket.setUrl(_url);
 | 
			
		||||
 | 
			
		||||
        ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(
 | 
			
		||||
            _enablePerMessageDeflate, false, false, 15, 15);
 | 
			
		||||
        _webSocket.setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
 | 
			
		||||
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        log(std::string("Connecting to url: ") + _url);
 | 
			
		||||
 | 
			
		||||
        _webSocket.setOnMessageCallback(
 | 
			
		||||
            [this](ix::WebSocketMessageType messageType,
 | 
			
		||||
               const std::string& str,
 | 
			
		||||
               size_t wireSize,
 | 
			
		||||
               const ix::WebSocketErrorInfo& error,
 | 
			
		||||
               const ix::WebSocketOpenInfo& openInfo,
 | 
			
		||||
               const ix::WebSocketCloseInfo& closeInfo)
 | 
			
		||||
            {
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                if (messageType == ix::WebSocket_MessageType_Open)
 | 
			
		||||
                {
 | 
			
		||||
                    _condition.notify_one();
 | 
			
		||||
 | 
			
		||||
                    log("ws_send: connected");
 | 
			
		||||
                    std::cout << "Uri: " << openInfo.uri << std::endl;
 | 
			
		||||
                    std::cout << "Handshake Headers:" << std::endl;
 | 
			
		||||
                    for (auto it : openInfo.headers)
 | 
			
		||||
                    {
 | 
			
		||||
                        std::cout << it.first << ": " << it.second << std::endl;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (messageType == ix::WebSocket_MessageType_Close)
 | 
			
		||||
                {
 | 
			
		||||
                    ss << "ws_send: connection closed:";
 | 
			
		||||
                    ss << " code " << closeInfo.code;
 | 
			
		||||
                    ss << " reason " << closeInfo.reason << std::endl;
 | 
			
		||||
                    log(ss.str());
 | 
			
		||||
                }
 | 
			
		||||
                else if (messageType == ix::WebSocket_MessageType_Message)
 | 
			
		||||
                {
 | 
			
		||||
                    _condition.notify_one();
 | 
			
		||||
 | 
			
		||||
                    ss << "ws_send: received message: "
 | 
			
		||||
                       << str;
 | 
			
		||||
                    log(ss.str());
 | 
			
		||||
 | 
			
		||||
                    Json::Value data;
 | 
			
		||||
                    Json::Reader reader;
 | 
			
		||||
                    if (!reader.parse(str, data))
 | 
			
		||||
                    {
 | 
			
		||||
                        std::cerr << "Invalid JSON response" << std::endl;
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    std::string id = data["id"].asString();
 | 
			
		||||
                    if (_id != id)
 | 
			
		||||
                    {
 | 
			
		||||
                        std::cerr << "Invalid id" << std::endl;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (messageType == ix::WebSocket_MessageType_Error)
 | 
			
		||||
                {
 | 
			
		||||
                    ss << "Connection error: " << error.reason      << std::endl;
 | 
			
		||||
                    ss << "#retries: "         << error.retries     << std::endl;
 | 
			
		||||
                    ss << "Wait time(ms): "    << error.wait_time   << std::endl;
 | 
			
		||||
                    ss << "HTTP Status: "      << error.http_status << std::endl;
 | 
			
		||||
                    log(ss.str());
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    ss << "Invalid ix::WebSocketMessageType";
 | 
			
		||||
                    log(ss.str());
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        _webSocket.start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class Bench
 | 
			
		||||
    {
 | 
			
		||||
        public:
 | 
			
		||||
            Bench(const std::string& description) :
 | 
			
		||||
                _description(description),
 | 
			
		||||
                _start(std::chrono::system_clock::now()),
 | 
			
		||||
                _reported(false)
 | 
			
		||||
            {
 | 
			
		||||
                ;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ~Bench()
 | 
			
		||||
            {
 | 
			
		||||
                if (!_reported)
 | 
			
		||||
                {
 | 
			
		||||
                    report();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void report()
 | 
			
		||||
            {
 | 
			
		||||
                auto now = std::chrono::system_clock::now();
 | 
			
		||||
                auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - _start);
 | 
			
		||||
 | 
			
		||||
                _ms = milliseconds.count();
 | 
			
		||||
                std::cout << _description << " completed in "
 | 
			
		||||
                          << _ms << "ms" << std::endl;
 | 
			
		||||
 | 
			
		||||
                _reported = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            uint64_t getDuration() const
 | 
			
		||||
            {
 | 
			
		||||
                return _ms;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            std::string _description;
 | 
			
		||||
            std::chrono::time_point<std::chrono::system_clock> _start;
 | 
			
		||||
            uint64_t _ms;
 | 
			
		||||
            bool _reported;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void WebSocketSender::sendMessage(const std::string& filename,
 | 
			
		||||
                                      bool throttle)
 | 
			
		||||
    {
 | 
			
		||||
        std::string content;
 | 
			
		||||
        {
 | 
			
		||||
            Bench bench("load file from disk");
 | 
			
		||||
            content = load(filename);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _id = uuid4();
 | 
			
		||||
 | 
			
		||||
        std::string b64Content;
 | 
			
		||||
        {
 | 
			
		||||
            Bench bench("base 64 encode file");
 | 
			
		||||
            b64Content = base64_encode(content, content.size());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Json::Value pdu;
 | 
			
		||||
        pdu["kind"] = "send";
 | 
			
		||||
        pdu["id"] = _id;
 | 
			
		||||
        pdu["content"] = b64Content;
 | 
			
		||||
        pdu["djb2_hash"] = djb2Hash(b64Content);
 | 
			
		||||
        pdu["filename"] = filename;
 | 
			
		||||
 | 
			
		||||
        Bench bench("Sending file through websocket");
 | 
			
		||||
        _webSocket.send(pdu.toStyledString(),
 | 
			
		||||
                        [throttle](int current, int total) -> bool
 | 
			
		||||
        {
 | 
			
		||||
            std::cout << "Step " << current << " out of " << total << std::endl;
 | 
			
		||||
 | 
			
		||||
            if (throttle)
 | 
			
		||||
            {
 | 
			
		||||
                std::chrono::duration<double, std::milli> duration(10);
 | 
			
		||||
                std::this_thread::sleep_for(duration);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        bench.report();
 | 
			
		||||
        auto duration = bench.getDuration();
 | 
			
		||||
        auto transferRate = 1000 * b64Content.size() / duration;
 | 
			
		||||
        transferRate /= (1024 * 1024);
 | 
			
		||||
        std::cout << "Send transfer rate: " << transferRate << "MB/s" << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void wsSend(const std::string& url,
 | 
			
		||||
                const std::string& path,
 | 
			
		||||
                bool enablePerMessageDeflate,
 | 
			
		||||
                bool throttle)
 | 
			
		||||
    {
 | 
			
		||||
        WebSocketSender webSocketSender(url, enablePerMessageDeflate);
 | 
			
		||||
        webSocketSender.start();
 | 
			
		||||
 | 
			
		||||
        webSocketSender.waitForConnection();
 | 
			
		||||
 | 
			
		||||
        std::cout << "Sending..." << std::endl;
 | 
			
		||||
        webSocketSender.sendMessage(path, throttle);
 | 
			
		||||
 | 
			
		||||
        webSocketSender.waitForAck();
 | 
			
		||||
 | 
			
		||||
        std::cout << "Done !" << std::endl;
 | 
			
		||||
        webSocketSender.stop();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv)
 | 
			
		||||
{
 | 
			
		||||
    if (argc != 3)
 | 
			
		||||
    {
 | 
			
		||||
        std::cerr << "Usage: ws_send <url> <path>" << std::endl;
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    std::string url = argv[1];
 | 
			
		||||
    std::string path = argv[2];
 | 
			
		||||
 | 
			
		||||
    bool throttle = false;
 | 
			
		||||
    bool enablePerMessageDeflate = false;
 | 
			
		||||
 | 
			
		||||
    Socket::init();
 | 
			
		||||
    wsSend(url, path, enablePerMessageDeflate, throttle);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								ixwebsocket/IXConnectionState.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,33 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXConnectionState.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXConnectionState.h"
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    std::atomic<uint64_t> ConnectionState::_globalId(0);
 | 
			
		||||
 | 
			
		||||
    ConnectionState::ConnectionState()
 | 
			
		||||
    {
 | 
			
		||||
        computeId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void ConnectionState::computeId()
 | 
			
		||||
    {
 | 
			
		||||
        _id = std::to_string(_globalId++);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const std::string& ConnectionState::getId() const
 | 
			
		||||
    {
 | 
			
		||||
        return _id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<ConnectionState> ConnectionState::createConnectionState()
 | 
			
		||||
    {
 | 
			
		||||
        return std::make_shared<ConnectionState>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										33
									
								
								ixwebsocket/IXConnectionState.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,33 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXConnectionState.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class ConnectionState {
 | 
			
		||||
    public:
 | 
			
		||||
        ConnectionState();
 | 
			
		||||
        virtual ~ConnectionState() = default;
 | 
			
		||||
 | 
			
		||||
        virtual void computeId();
 | 
			
		||||
        virtual const std::string& getId() const;
 | 
			
		||||
 | 
			
		||||
        static std::shared_ptr<ConnectionState> createConnectionState();
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
        std::string _id;
 | 
			
		||||
 | 
			
		||||
        static std::atomic<uint64_t> _globalId;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -73,7 +73,7 @@ namespace ix
 | 
			
		||||
        errMsg = "no error";
 | 
			
		||||
 | 
			
		||||
        // Maybe a cancellation request got in before the background thread terminated ?
 | 
			
		||||
        if (isCancellationRequested())
 | 
			
		||||
        if (isCancellationRequested && isCancellationRequested())
 | 
			
		||||
        {
 | 
			
		||||
            errMsg = "cancellation requested";
 | 
			
		||||
            return nullptr;
 | 
			
		||||
@@ -121,7 +121,7 @@ namespace ix
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Were we cancelled ?
 | 
			
		||||
            if (isCancellationRequested())
 | 
			
		||||
            if (isCancellationRequested && isCancellationRequested())
 | 
			
		||||
            {
 | 
			
		||||
                errMsg = "cancellation requested";
 | 
			
		||||
                return nullptr;
 | 
			
		||||
@@ -129,7 +129,7 @@ namespace ix
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Maybe a cancellation request got in before the bg terminated ?
 | 
			
		||||
        if (isCancellationRequested())
 | 
			
		||||
        if (isCancellationRequested && isCancellationRequested())
 | 
			
		||||
        {
 | 
			
		||||
            errMsg = "cancellation requested";
 | 
			
		||||
            return nullptr;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,82 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXEventFd.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Linux/Android has a special type of virtual files. select(2) will react
 | 
			
		||||
// when reading/writing to those files, unlike closing sockets.
 | 
			
		||||
//
 | 
			
		||||
// https://linux.die.net/man/2/eventfd
 | 
			
		||||
// http://www.sourcexr.com/articles/2013/10/26/lightweight-inter-process-signaling-with-eventfd
 | 
			
		||||
//
 | 
			
		||||
// eventfd was added in Linux kernel 2.x, and our oldest Android (Kitkat 4.4)
 | 
			
		||||
// is on Kernel 3.x
 | 
			
		||||
//
 | 
			
		||||
// cf Android/Kernel table here
 | 
			
		||||
// https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "IXEventFd.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
# include <sys/eventfd.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
#include <unistd.h> // for write
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    EventFd::EventFd() :
 | 
			
		||||
        _eventfd(-1)
 | 
			
		||||
    {
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
        _eventfd = eventfd(0, 0);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EventFd::~EventFd()
 | 
			
		||||
    {
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
        ::close(_eventfd);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool EventFd::notify()
 | 
			
		||||
    {
 | 
			
		||||
#if defined(__linux__)
 | 
			
		||||
        if (_eventfd == -1) return false;
 | 
			
		||||
 | 
			
		||||
        // select will wake up when a non-zero value is written to our eventfd
 | 
			
		||||
        uint64_t value = 1;
 | 
			
		||||
 | 
			
		||||
        // we should write 8 bytes for an uint64_t
 | 
			
		||||
        return write(_eventfd, &value, sizeof(value)) == 8;
 | 
			
		||||
#else
 | 
			
		||||
        return true;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool EventFd::clear()
 | 
			
		||||
    {
 | 
			
		||||
#if defined(__linux__)
 | 
			
		||||
        if (_eventfd == -1) return false;
 | 
			
		||||
 | 
			
		||||
        // 0 is a special value ; select will not wake up
 | 
			
		||||
        uint64_t value = 0;
 | 
			
		||||
 | 
			
		||||
        // we should write 8 bytes for an uint64_t
 | 
			
		||||
        return write(_eventfd, &value, sizeof(value)) == 8;
 | 
			
		||||
#else
 | 
			
		||||
        return true;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int EventFd::getFd()
 | 
			
		||||
    {
 | 
			
		||||
        return _eventfd;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXEventFd.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class EventFd {
 | 
			
		||||
    public:
 | 
			
		||||
        EventFd();
 | 
			
		||||
        virtual ~EventFd();
 | 
			
		||||
 | 
			
		||||
        bool notify();
 | 
			
		||||
        bool clear();
 | 
			
		||||
        int getFd();
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        int _eventfd;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								ixwebsocket/IXSelectInterrupt.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,46 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXSelectInterrupt.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXSelectInterrupt.h"
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    SelectInterrupt::SelectInterrupt()
 | 
			
		||||
    {
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SelectInterrupt::~SelectInterrupt()
 | 
			
		||||
    {
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SelectInterrupt::init(std::string& /*errorMsg*/)
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SelectInterrupt::notify(uint64_t /*value*/)
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint64_t SelectInterrupt::read()
 | 
			
		||||
    {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SelectInterrupt::clear()
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int SelectInterrupt::getFd() const
 | 
			
		||||
    {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										28
									
								
								ixwebsocket/IXSelectInterrupt.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,28 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXSelectInterrupt.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class SelectInterrupt {
 | 
			
		||||
    public:
 | 
			
		||||
        SelectInterrupt();
 | 
			
		||||
        virtual ~SelectInterrupt();
 | 
			
		||||
 | 
			
		||||
        virtual bool init(std::string& errorMsg);
 | 
			
		||||
 | 
			
		||||
        virtual bool notify(uint64_t value);
 | 
			
		||||
        virtual bool clear();
 | 
			
		||||
        virtual uint64_t read();
 | 
			
		||||
        virtual int getFd() const;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										116
									
								
								ixwebsocket/IXSelectInterruptEventFd.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,116 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXSelectInterruptEventFd.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// On Linux we use eventd to wake up select.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Linux/Android has a special type of virtual files. select(2) will react
 | 
			
		||||
// when reading/writing to those files, unlike closing sockets.
 | 
			
		||||
//
 | 
			
		||||
// https://linux.die.net/man/2/eventfd
 | 
			
		||||
// http://www.sourcexr.com/articles/2013/10/26/lightweight-inter-process-signaling-with-eventfd
 | 
			
		||||
//
 | 
			
		||||
// eventfd was added in Linux kernel 2.x, and our oldest Android (Kitkat 4.4)
 | 
			
		||||
// is on Kernel 3.x
 | 
			
		||||
//
 | 
			
		||||
// cf Android/Kernel table here
 | 
			
		||||
// https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
 | 
			
		||||
//
 | 
			
		||||
// On macOS we use UNIX pipes to wake up select.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "IXSelectInterruptEventFd.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/eventfd.h>
 | 
			
		||||
 | 
			
		||||
#include <unistd.h> // for write
 | 
			
		||||
#include <string.h> // for strerror
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    SelectInterruptEventFd::SelectInterruptEventFd()
 | 
			
		||||
    {
 | 
			
		||||
        _eventfd = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SelectInterruptEventFd::~SelectInterruptEventFd()
 | 
			
		||||
    {
 | 
			
		||||
        ::close(_eventfd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SelectInterruptEventFd::init(std::string& errorMsg)
 | 
			
		||||
    {
 | 
			
		||||
        // calling init twice is a programming error
 | 
			
		||||
        assert(_eventfd == -1);
 | 
			
		||||
 | 
			
		||||
        _eventfd = eventfd(0, 0);
 | 
			
		||||
        if (_eventfd < 0)
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "SelectInterruptEventFd::init() failed in eventfd()"
 | 
			
		||||
               << " : " << strerror(errno);
 | 
			
		||||
            errorMsg = ss.str();
 | 
			
		||||
 | 
			
		||||
            _eventfd = -1;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (fcntl(_eventfd, F_SETFL, O_NONBLOCK) == -1)
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "SelectInterruptEventFd::init() failed in fcntl() call"
 | 
			
		||||
               << " : " << strerror(errno);
 | 
			
		||||
            errorMsg = ss.str();
 | 
			
		||||
 | 
			
		||||
            _eventfd = -1;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SelectInterruptEventFd::notify(uint64_t value)
 | 
			
		||||
    {
 | 
			
		||||
        int fd = _eventfd;
 | 
			
		||||
 | 
			
		||||
        if (fd == -1) return false;
 | 
			
		||||
 | 
			
		||||
        // we should write 8 bytes for an uint64_t
 | 
			
		||||
        return write(fd, &value, sizeof(value)) == 8;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO: return max uint64_t for errors ?
 | 
			
		||||
    uint64_t SelectInterruptEventFd::read()
 | 
			
		||||
    {
 | 
			
		||||
        int fd = _eventfd;
 | 
			
		||||
 | 
			
		||||
        uint64_t value = 0;
 | 
			
		||||
        ::read(fd, &value, sizeof(value));
 | 
			
		||||
        return value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SelectInterruptEventFd::clear()
 | 
			
		||||
    {
 | 
			
		||||
        if (_eventfd == -1) return false;
 | 
			
		||||
 | 
			
		||||
        // 0 is a special value ; select will not wake up
 | 
			
		||||
        uint64_t value = 0;
 | 
			
		||||
 | 
			
		||||
        // we should write 8 bytes for an uint64_t
 | 
			
		||||
        return write(_eventfd, &value, sizeof(value)) == 8;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int SelectInterruptEventFd::getFd() const
 | 
			
		||||
    {
 | 
			
		||||
        return _eventfd;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								ixwebsocket/IXSelectInterruptEventFd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,32 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXSelectInterruptEventFd.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "IXSelectInterrupt.h"
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class SelectInterruptEventFd : public SelectInterrupt {
 | 
			
		||||
    public:
 | 
			
		||||
        SelectInterruptEventFd();
 | 
			
		||||
        virtual ~SelectInterruptEventFd();
 | 
			
		||||
 | 
			
		||||
        bool init(std::string& errorMsg) final;
 | 
			
		||||
 | 
			
		||||
        bool notify(uint64_t value) final;
 | 
			
		||||
        bool clear() final;
 | 
			
		||||
        uint64_t read() final;
 | 
			
		||||
        int getFd() const final;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        int _eventfd;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										25
									
								
								ixwebsocket/IXSelectInterruptFactory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,25 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXSelectInterruptFactory.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXSelectInterruptFactory.h"
 | 
			
		||||
 | 
			
		||||
#if defined(__linux__) || defined(__APPLE__)
 | 
			
		||||
# include <ixwebsocket/IXSelectInterruptPipe.h>
 | 
			
		||||
#else
 | 
			
		||||
# include <ixwebsocket/IXSelectInterrupt.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    std::shared_ptr<SelectInterrupt> createSelectInterrupt()
 | 
			
		||||
    {
 | 
			
		||||
#if defined(__linux__) || defined(__APPLE__)
 | 
			
		||||
        return std::make_shared<SelectInterruptPipe>();
 | 
			
		||||
#else
 | 
			
		||||
        return std::make_shared<SelectInterrupt>();
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								ixwebsocket/IXSelectInterruptFactory.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,15 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXSelectInterruptFactory.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class SelectInterrupt;
 | 
			
		||||
    std::shared_ptr<SelectInterrupt> createSelectInterrupt();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										138
									
								
								ixwebsocket/IXSelectInterruptPipe.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,138 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXSelectInterruptPipe.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// On macOS we use UNIX pipes to wake up select.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "IXSelectInterruptPipe.h"
 | 
			
		||||
 | 
			
		||||
#include <unistd.h> // for write
 | 
			
		||||
#include <string.h> // for strerror
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    // File descriptor at index 0 in _fildes is the read end of the pipe
 | 
			
		||||
    // File descriptor at index 1 in _fildes is the write end of the pipe
 | 
			
		||||
    const int SelectInterruptPipe::kPipeReadIndex = 0;
 | 
			
		||||
    const int SelectInterruptPipe::kPipeWriteIndex = 1;
 | 
			
		||||
 | 
			
		||||
    SelectInterruptPipe::SelectInterruptPipe()
 | 
			
		||||
    {
 | 
			
		||||
        _fildes[kPipeReadIndex] = -1;
 | 
			
		||||
        _fildes[kPipeWriteIndex] = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SelectInterruptPipe::~SelectInterruptPipe()
 | 
			
		||||
    {
 | 
			
		||||
        ::close(_fildes[kPipeReadIndex]);
 | 
			
		||||
        ::close(_fildes[kPipeWriteIndex]);
 | 
			
		||||
        _fildes[kPipeReadIndex] = -1;
 | 
			
		||||
        _fildes[kPipeWriteIndex] = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SelectInterruptPipe::init(std::string& errorMsg)
 | 
			
		||||
    {
 | 
			
		||||
        // calling init twice is a programming error
 | 
			
		||||
        assert(_fildes[kPipeReadIndex] == -1);
 | 
			
		||||
        assert(_fildes[kPipeWriteIndex] == -1);
 | 
			
		||||
 | 
			
		||||
        if (pipe(_fildes) < 0)
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "SelectInterruptPipe::init() failed in pipe() call"
 | 
			
		||||
               << " : " << strerror(errno);
 | 
			
		||||
            errorMsg = ss.str();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (fcntl(_fildes[kPipeReadIndex], F_SETFL, O_NONBLOCK) == -1)
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) call"
 | 
			
		||||
               << " : " << strerror(errno);
 | 
			
		||||
            errorMsg = ss.str();
 | 
			
		||||
 | 
			
		||||
            _fildes[kPipeReadIndex] = -1;
 | 
			
		||||
            _fildes[kPipeWriteIndex] = -1;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (fcntl(_fildes[kPipeWriteIndex], F_SETFL, O_NONBLOCK) == -1)
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) call"
 | 
			
		||||
               << " : " << strerror(errno);
 | 
			
		||||
            errorMsg = ss.str();
 | 
			
		||||
 | 
			
		||||
            _fildes[kPipeReadIndex] = -1;
 | 
			
		||||
            _fildes[kPipeWriteIndex] = -1;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#ifdef F_SETNOSIGPIPE
 | 
			
		||||
        if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1)
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "SelectInterruptPipe::init() failed in fcntl(.... F_SETNOSIGPIPE) call"
 | 
			
		||||
               << " : " << strerror(errno);
 | 
			
		||||
            errorMsg = ss.str();
 | 
			
		||||
 | 
			
		||||
            _fildes[kPipeReadIndex] = -1;
 | 
			
		||||
            _fildes[kPipeWriteIndex] = -1;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1)
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "SelectInterruptPipe::init() failed in fcntl(..., F_SETNOSIGPIPE) call"
 | 
			
		||||
               << " : " << strerror(errno);
 | 
			
		||||
            errorMsg = ss.str();
 | 
			
		||||
 | 
			
		||||
            _fildes[kPipeReadIndex] = -1;
 | 
			
		||||
            _fildes[kPipeWriteIndex] = -1;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SelectInterruptPipe::notify(uint64_t value)
 | 
			
		||||
    {
 | 
			
		||||
        int fd = _fildes[kPipeWriteIndex];
 | 
			
		||||
        if (fd == -1) return false;
 | 
			
		||||
 | 
			
		||||
        // we should write 8 bytes for an uint64_t
 | 
			
		||||
        return write(fd, &value, sizeof(value)) == 8;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO: return max uint64_t for errors ?
 | 
			
		||||
    uint64_t SelectInterruptPipe::read()
 | 
			
		||||
    {
 | 
			
		||||
        int fd = _fildes[kPipeReadIndex];
 | 
			
		||||
 | 
			
		||||
        uint64_t value = 0;
 | 
			
		||||
        ::read(fd, &value, sizeof(value));
 | 
			
		||||
 | 
			
		||||
        return value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SelectInterruptPipe::clear()
 | 
			
		||||
    {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int SelectInterruptPipe::getFd() const
 | 
			
		||||
    {
 | 
			
		||||
        return _fildes[kPipeReadIndex];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								ixwebsocket/IXSelectInterruptPipe.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,39 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXSelectInterruptPipe.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "IXSelectInterrupt.h"
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class SelectInterruptPipe : public SelectInterrupt {
 | 
			
		||||
    public:
 | 
			
		||||
        SelectInterruptPipe();
 | 
			
		||||
        virtual ~SelectInterruptPipe();
 | 
			
		||||
 | 
			
		||||
        bool init(std::string& errorMsg) final;
 | 
			
		||||
 | 
			
		||||
        bool notify(uint64_t value) final;
 | 
			
		||||
        bool clear() final;
 | 
			
		||||
        uint64_t read() final;
 | 
			
		||||
        int getFd() const final;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        // Store file descriptors used by the communication pipe. Communication
 | 
			
		||||
        // happens between a control thread and a background thread, which is
 | 
			
		||||
        // blocked on select.
 | 
			
		||||
        int _fildes[2];
 | 
			
		||||
 | 
			
		||||
        // Used to identify the read/write idx
 | 
			
		||||
        static const int kPipeReadIndex;
 | 
			
		||||
        static const int kPipeWriteIndex;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -7,6 +7,8 @@
 | 
			
		||||
#include "IXSocket.h"
 | 
			
		||||
#include "IXSocketConnect.h"
 | 
			
		||||
#include "IXNetSystem.h"
 | 
			
		||||
#include "IXSelectInterrupt.h"
 | 
			
		||||
#include "IXSelectInterruptFactory.h"
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
@@ -23,10 +25,13 @@ namespace ix
 | 
			
		||||
{
 | 
			
		||||
    const int Socket::kDefaultPollNoTimeout = -1; // No poll timeout by default
 | 
			
		||||
    const int Socket::kDefaultPollTimeout = kDefaultPollNoTimeout;
 | 
			
		||||
    const uint64_t Socket::kSendRequest = 1;
 | 
			
		||||
    const uint64_t Socket::kCloseRequest = 2;
 | 
			
		||||
    constexpr size_t Socket::kChunkSize;
 | 
			
		||||
 | 
			
		||||
    Socket::Socket(int fd) :
 | 
			
		||||
        _sockfd(fd)
 | 
			
		||||
        _sockfd(fd),
 | 
			
		||||
        _selectInterrupt(createSelectInterrupt())
 | 
			
		||||
    {
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
@@ -40,50 +45,93 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        if (_sockfd == -1)
 | 
			
		||||
        {
 | 
			
		||||
            if (onPollCallback) onPollCallback(PollResultType_Error);
 | 
			
		||||
            if (onPollCallback) onPollCallback(PollResultType::Error);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int ret = select(timeoutSecs, 0);
 | 
			
		||||
 | 
			
		||||
        PollResultType pollResult = PollResultType_ReadyForRead;
 | 
			
		||||
        if (ret < 0)
 | 
			
		||||
        {
 | 
			
		||||
            pollResult = PollResultType_Error;
 | 
			
		||||
        }
 | 
			
		||||
        else if (ret == 0)
 | 
			
		||||
        {
 | 
			
		||||
            pollResult = PollResultType_Timeout;
 | 
			
		||||
        }
 | 
			
		||||
        PollResultType pollResult = isReadyToRead(1000 * timeoutSecs);
 | 
			
		||||
 | 
			
		||||
        if (onPollCallback) onPollCallback(pollResult);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int Socket::select(int timeoutSecs, int timeoutMs)
 | 
			
		||||
    PollResultType Socket::select(bool readyToRead, int timeoutMs)
 | 
			
		||||
    {
 | 
			
		||||
        fd_set rfds;
 | 
			
		||||
        fd_set wfds;
 | 
			
		||||
        FD_ZERO(&rfds);
 | 
			
		||||
        FD_SET(_sockfd, &rfds);
 | 
			
		||||
        FD_ZERO(&wfds);
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
        FD_SET(_eventfd.getFd(), &rfds);
 | 
			
		||||
#endif
 | 
			
		||||
        fd_set* fds = (readyToRead) ? &rfds : & wfds;
 | 
			
		||||
        FD_SET(_sockfd, fds);
 | 
			
		||||
 | 
			
		||||
        // File descriptor used to interrupt select when needed
 | 
			
		||||
        int interruptFd = _selectInterrupt->getFd();
 | 
			
		||||
        if (interruptFd != -1)
 | 
			
		||||
        {
 | 
			
		||||
            FD_SET(interruptFd, fds);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        struct timeval timeout;
 | 
			
		||||
        timeout.tv_sec = timeoutSecs;
 | 
			
		||||
        timeout.tv_usec = 1000 * timeoutMs;
 | 
			
		||||
        timeout.tv_sec = timeoutMs / 1000;
 | 
			
		||||
        timeout.tv_usec = (timeoutMs < 1000) ? 0 : 1000 * (timeoutMs % 1000);
 | 
			
		||||
 | 
			
		||||
        // Compute the highest fd.
 | 
			
		||||
        int sockfd = _sockfd;
 | 
			
		||||
        int nfds = (std::max)(sockfd, _eventfd.getFd());
 | 
			
		||||
        int ret = ::select(nfds + 1, &rfds, nullptr, nullptr,
 | 
			
		||||
                           (timeoutSecs < 0) ? nullptr : &timeout);
 | 
			
		||||
        return ret;
 | 
			
		||||
        int nfds = (std::max)(sockfd, interruptFd);
 | 
			
		||||
 | 
			
		||||
        int ret = ::select(nfds + 1, &rfds, &wfds, nullptr,
 | 
			
		||||
                           (timeoutMs < 0) ? nullptr : &timeout);
 | 
			
		||||
 | 
			
		||||
        PollResultType pollResult = PollResultType::ReadyForRead;
 | 
			
		||||
        if (ret < 0)
 | 
			
		||||
        {
 | 
			
		||||
            pollResult = PollResultType::Error;
 | 
			
		||||
        }
 | 
			
		||||
        else if (ret == 0)
 | 
			
		||||
        {
 | 
			
		||||
            pollResult = PollResultType::Timeout;
 | 
			
		||||
        }
 | 
			
		||||
        else if (interruptFd != -1 && FD_ISSET(interruptFd, &rfds))
 | 
			
		||||
        {
 | 
			
		||||
            uint64_t value = _selectInterrupt->read();
 | 
			
		||||
 | 
			
		||||
            if (value == kSendRequest)
 | 
			
		||||
            {
 | 
			
		||||
                pollResult = PollResultType::SendRequest;
 | 
			
		||||
            }
 | 
			
		||||
            else if (value == kCloseRequest)
 | 
			
		||||
            {
 | 
			
		||||
                pollResult = PollResultType::CloseRequest;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (sockfd != -1 && readyToRead && FD_ISSET(sockfd, &rfds))
 | 
			
		||||
        {
 | 
			
		||||
            pollResult = PollResultType::ReadyForRead;
 | 
			
		||||
        }
 | 
			
		||||
        else if (sockfd != -1 && !readyToRead && FD_ISSET(sockfd, &wfds))
 | 
			
		||||
        {
 | 
			
		||||
            pollResult = PollResultType::ReadyForWrite;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return pollResult;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Socket::wakeUpFromPoll()
 | 
			
		||||
    PollResultType Socket::isReadyToRead(int timeoutMs)
 | 
			
		||||
    {
 | 
			
		||||
        // this will wake up the thread blocked on select, only needed on Linux
 | 
			
		||||
        _eventfd.notify();
 | 
			
		||||
        bool readyToRead = true;
 | 
			
		||||
        return select(readyToRead, timeoutMs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    PollResultType Socket::isReadyToWrite(int timeoutMs)
 | 
			
		||||
    {
 | 
			
		||||
        bool readyToRead = false;
 | 
			
		||||
        return select(readyToRead, timeoutMs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Wake up from poll/select by writing to the pipe which is watched by select
 | 
			
		||||
    bool Socket::wakeUpFromPoll(uint8_t wakeUpCode)
 | 
			
		||||
    {
 | 
			
		||||
        return _selectInterrupt->notify(wakeUpCode);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool Socket::connect(const std::string& host,
 | 
			
		||||
@@ -93,7 +141,7 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_socketMutex);
 | 
			
		||||
 | 
			
		||||
        if (!_eventfd.clear()) return false;
 | 
			
		||||
        if (!_selectInterrupt->clear()) return false;
 | 
			
		||||
 | 
			
		||||
        _sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
 | 
			
		||||
        return _sockfd != -1;
 | 
			
		||||
@@ -152,24 +200,9 @@ namespace ix
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool Socket::init()
 | 
			
		||||
    bool Socket::init(std::string& errorMsg)
 | 
			
		||||
    {
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
        INT rc;
 | 
			
		||||
        WSADATA wsaData;
 | 
			
		||||
 | 
			
		||||
        rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
 | 
			
		||||
        return rc != 0;
 | 
			
		||||
#else
 | 
			
		||||
        return true;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Socket::cleanup()
 | 
			
		||||
    {
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
        WSACleanup();
 | 
			
		||||
#endif
 | 
			
		||||
        return _selectInterrupt->init(errorMsg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool Socket::writeBytes(const std::string& str,
 | 
			
		||||
@@ -177,7 +210,7 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        while (true)
 | 
			
		||||
        {
 | 
			
		||||
            if (isCancellationRequested()) return false;
 | 
			
		||||
            if (isCancellationRequested && isCancellationRequested()) return false;
 | 
			
		||||
 | 
			
		||||
            char* buffer = const_cast<char*>(str.c_str());
 | 
			
		||||
            int len = (int) str.size();
 | 
			
		||||
@@ -189,7 +222,7 @@ namespace ix
 | 
			
		||||
            {
 | 
			
		||||
                return ret == len;
 | 
			
		||||
            }
 | 
			
		||||
            // There is possibly something to be write, try again
 | 
			
		||||
            // There is possibly something to be writen, try again
 | 
			
		||||
            else if (ret < 0 && (getErrno() == EWOULDBLOCK ||
 | 
			
		||||
                                 getErrno() == EAGAIN))
 | 
			
		||||
            {
 | 
			
		||||
@@ -208,7 +241,7 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        while (true)
 | 
			
		||||
        {
 | 
			
		||||
            if (isCancellationRequested()) return false;
 | 
			
		||||
            if (isCancellationRequested && isCancellationRequested()) return false;
 | 
			
		||||
 | 
			
		||||
            ssize_t ret;
 | 
			
		||||
            ret = recv(buffer, 1);
 | 
			
		||||
@@ -222,10 +255,9 @@ namespace ix
 | 
			
		||||
            else if (ret < 0 && (getErrno() == EWOULDBLOCK ||
 | 
			
		||||
                                 getErrno() == EAGAIN))
 | 
			
		||||
            {
 | 
			
		||||
                // Wait with a timeout until something is ready to read.
 | 
			
		||||
                // Wait with a 1ms timeout until the socket is ready to read.
 | 
			
		||||
                // This way we are not busy looping
 | 
			
		||||
                int res = select(0, 1);
 | 
			
		||||
                if (res < 0 && (errno == EBADF || errno == EINVAL))
 | 
			
		||||
                if (isReadyToRead(1) == PollResultType::Error)
 | 
			
		||||
                {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
@@ -272,9 +304,12 @@ namespace ix
 | 
			
		||||
        std::vector<uint8_t> output;
 | 
			
		||||
        while (output.size() != length)
 | 
			
		||||
        {
 | 
			
		||||
            if (isCancellationRequested()) return std::make_pair(false, std::string());
 | 
			
		||||
            if (isCancellationRequested && isCancellationRequested())
 | 
			
		||||
            {
 | 
			
		||||
                return std::make_pair(false, std::string());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            int size = std::min(kChunkSize, length - output.size());
 | 
			
		||||
            size_t size = std::min(kChunkSize, length - output.size());
 | 
			
		||||
            ssize_t ret = recv((char*)&_readBuffer[0], size);
 | 
			
		||||
 | 
			
		||||
            if (ret <= 0 && (getErrno() != EWOULDBLOCK &&
 | 
			
		||||
@@ -292,9 +327,12 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
            if (onProgressCallback) onProgressCallback((int) output.size(), (int) length);
 | 
			
		||||
 | 
			
		||||
            // Wait with a timeout until something is ready to read.
 | 
			
		||||
            // Wait with a 1ms timeout until the socket is ready to read.
 | 
			
		||||
            // This way we are not busy looping
 | 
			
		||||
            select(0, 1);
 | 
			
		||||
            if (isReadyToRead(1) == PollResultType::Error)
 | 
			
		||||
            {
 | 
			
		||||
                return std::make_pair(false, std::string());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return std::make_pair(true, std::string(output.begin(),
 | 
			
		||||
 
 | 
			
		||||
@@ -11,23 +11,28 @@
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#include <BaseTsd.h>
 | 
			
		||||
typedef SSIZE_T ssize_t;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "IXEventFd.h"
 | 
			
		||||
#include "IXCancellationRequest.h"
 | 
			
		||||
#include "IXProgressCallback.h"
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    enum PollResultType
 | 
			
		||||
    class SelectInterrupt;
 | 
			
		||||
 | 
			
		||||
    enum class PollResultType
 | 
			
		||||
    {
 | 
			
		||||
        PollResultType_ReadyForRead = 0,
 | 
			
		||||
        PollResultType_Timeout = 1,
 | 
			
		||||
        PollResultType_Error = 2
 | 
			
		||||
        ReadyForRead = 0,
 | 
			
		||||
        ReadyForWrite = 1,
 | 
			
		||||
        Timeout = 2,
 | 
			
		||||
        Error = 3,
 | 
			
		||||
        SendRequest = 4,
 | 
			
		||||
        CloseRequest = 5
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class Socket {
 | 
			
		||||
@@ -36,13 +41,17 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        Socket(int fd = -1);
 | 
			
		||||
        virtual ~Socket();
 | 
			
		||||
        bool init(std::string& errorMsg);
 | 
			
		||||
 | 
			
		||||
        void configure();
 | 
			
		||||
 | 
			
		||||
        int select(int timeoutSecs, int timeoutMs);
 | 
			
		||||
        virtual void poll(const OnPollCallback& onPollCallback,
 | 
			
		||||
                          int timeoutSecs = kDefaultPollTimeout);
 | 
			
		||||
        virtual void wakeUpFromPoll();
 | 
			
		||||
        // Functions to check whether there is activity on the socket
 | 
			
		||||
        void poll(const OnPollCallback& onPollCallback,
 | 
			
		||||
                  int timeoutSecs = kDefaultPollTimeout);
 | 
			
		||||
        bool wakeUpFromPoll(uint8_t wakeUpCode);
 | 
			
		||||
 | 
			
		||||
        PollResultType isReadyToWrite(int timeoutMs);
 | 
			
		||||
        PollResultType isReadyToRead(int timeoutMs);
 | 
			
		||||
 | 
			
		||||
        // Virtual methods
 | 
			
		||||
        virtual bool connect(const std::string& url,
 | 
			
		||||
@@ -70,22 +79,27 @@ namespace ix
 | 
			
		||||
            const CancellationRequest& isCancellationRequested);
 | 
			
		||||
 | 
			
		||||
        static int getErrno();
 | 
			
		||||
        static bool init(); // Required on Windows to initialize WinSocket
 | 
			
		||||
        static void cleanup(); // Required on Windows to cleanup WinSocket
 | 
			
		||||
 | 
			
		||||
        // Used as special codes for pipe communication
 | 
			
		||||
        static const uint64_t kSendRequest;
 | 
			
		||||
        static const uint64_t kCloseRequest;
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
        void closeSocket(int fd);
 | 
			
		||||
 | 
			
		||||
        std::atomic<int> _sockfd;
 | 
			
		||||
        std::mutex _socketMutex;
 | 
			
		||||
        EventFd _eventfd;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        PollResultType select(bool readyToRead, int timeoutMs);
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
        std::shared_ptr<SelectInterrupt> _selectInterrupt;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        for (;;)
 | 
			
		||||
        {
 | 
			
		||||
            if (isCancellationRequested()) // Must handle timeout as well
 | 
			
		||||
            if (isCancellationRequested && isCancellationRequested()) // Must handle timeout as well
 | 
			
		||||
            {
 | 
			
		||||
                closeSocket(fd);
 | 
			
		||||
                errMsg = "Cancelled";
 | 
			
		||||
 
 | 
			
		||||
@@ -20,23 +20,45 @@ namespace ix
 | 
			
		||||
                                         std::string& errorMsg)
 | 
			
		||||
    {
 | 
			
		||||
        errorMsg.clear();
 | 
			
		||||
        std::shared_ptr<Socket> socket;
 | 
			
		||||
 | 
			
		||||
        if (!tls)
 | 
			
		||||
        {
 | 
			
		||||
            return std::make_shared<Socket>();
 | 
			
		||||
            socket = std::make_shared<Socket>();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_TLS
 | 
			
		||||
# ifdef __APPLE__
 | 
			
		||||
            return std::make_shared<SocketAppleSSL>();
 | 
			
		||||
            socket = std::make_shared<SocketAppleSSL>();
 | 
			
		||||
# else
 | 
			
		||||
            return std::make_shared<SocketOpenSSL>();
 | 
			
		||||
            socket = std::make_shared<SocketOpenSSL>();
 | 
			
		||||
# endif
 | 
			
		||||
#else
 | 
			
		||||
            errorMsg = "TLS support is not enabled on this platform.";
 | 
			
		||||
            return nullptr;
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!socket->init(errorMsg))
 | 
			
		||||
        {
 | 
			
		||||
            socket.reset();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return socket;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<Socket> createSocket(int fd,
 | 
			
		||||
                                         std::string& errorMsg)
 | 
			
		||||
    {
 | 
			
		||||
        errorMsg.clear();
 | 
			
		||||
 | 
			
		||||
        std::shared_ptr<Socket> socket = std::make_shared<Socket>(fd);
 | 
			
		||||
        if (!socket->init(errorMsg))
 | 
			
		||||
        {
 | 
			
		||||
            socket.reset();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return socket;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,4 +14,7 @@ namespace ix
 | 
			
		||||
    class Socket;
 | 
			
		||||
    std::shared_ptr<Socket> createSocket(bool tls,
 | 
			
		||||
                                         std::string& errorMsg);
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<Socket> createSocket(int fd,
 | 
			
		||||
                                         std::string& errorMsg);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    std::atomic<bool> SocketOpenSSL::_openSSLInitializationSuccessful(false);
 | 
			
		||||
    std::once_flag SocketOpenSSL::_openSSLInitFlag;
 | 
			
		||||
 | 
			
		||||
    SocketOpenSSL::SocketOpenSSL(int fd) : Socket(fd),
 | 
			
		||||
        _ssl_connection(nullptr),
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ namespace ix
 | 
			
		||||
        const SSL_METHOD* _ssl_method;
 | 
			
		||||
        mutable std::mutex _mutex;  // OpenSSL routines are not thread-safe
 | 
			
		||||
 | 
			
		||||
        std::once_flag _openSSLInitFlag;
 | 
			
		||||
        static std::once_flag _openSSLInitFlag;
 | 
			
		||||
        static std::atomic<bool> _openSSLInitializationSuccessful;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,8 @@ namespace ix
 | 
			
		||||
        _host(host),
 | 
			
		||||
        _backlog(backlog),
 | 
			
		||||
        _maxConnections(maxConnections),
 | 
			
		||||
        _stop(false)
 | 
			
		||||
        _stop(false),
 | 
			
		||||
        _connectionStateFactory(&ConnectionState::createConnectionState)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
@@ -145,6 +146,12 @@ namespace ix
 | 
			
		||||
        ::close(_serverFd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SocketServer::setConnectionStateFactory(
 | 
			
		||||
        const ConnectionStateFactory& connectionStateFactory)
 | 
			
		||||
    {
 | 
			
		||||
        _connectionStateFactory = connectionStateFactory;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SocketServer::run()
 | 
			
		||||
    {
 | 
			
		||||
        // Set the socket to non blocking mode, so that accept calls are not blocking
 | 
			
		||||
@@ -214,6 +221,12 @@ namespace ix
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::shared_ptr<ConnectionState> connectionState;
 | 
			
		||||
            if (_connectionStateFactory)
 | 
			
		||||
            {
 | 
			
		||||
                connectionState = _connectionStateFactory();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Launch the handleConnection work asynchronously in its own thread.
 | 
			
		||||
            //
 | 
			
		||||
            // the destructor of a future returned by std::async blocks,
 | 
			
		||||
@@ -221,7 +234,8 @@ namespace ix
 | 
			
		||||
            f = std::async(std::launch::async,
 | 
			
		||||
                           &SocketServer::handleConnection,
 | 
			
		||||
                           this,
 | 
			
		||||
                           clientFd);
 | 
			
		||||
                           clientFd,
 | 
			
		||||
                           connectionState);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "IXConnectionState.h"
 | 
			
		||||
 | 
			
		||||
#include <utility> // pair
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <set>
 | 
			
		||||
@@ -20,6 +22,8 @@ namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class SocketServer {
 | 
			
		||||
    public:
 | 
			
		||||
        using ConnectionStateFactory = std::function<std::shared_ptr<ConnectionState>()>;
 | 
			
		||||
 | 
			
		||||
        SocketServer(int port = SocketServer::kDefaultPort,
 | 
			
		||||
                     const std::string& host = SocketServer::kDefaultHost,
 | 
			
		||||
                     int backlog = SocketServer::kDefaultTcpBacklog,
 | 
			
		||||
@@ -27,6 +31,8 @@ namespace ix
 | 
			
		||||
        virtual ~SocketServer();
 | 
			
		||||
        virtual void stop();
 | 
			
		||||
 | 
			
		||||
        void setConnectionStateFactory(const ConnectionStateFactory& connectionStateFactory);
 | 
			
		||||
 | 
			
		||||
        const static int kDefaultPort;
 | 
			
		||||
        const static std::string kDefaultHost;
 | 
			
		||||
        const static int kDefaultTcpBacklog;
 | 
			
		||||
@@ -60,9 +66,13 @@ namespace ix
 | 
			
		||||
        std::condition_variable _conditionVariable;
 | 
			
		||||
        std::mutex _conditionVariableMutex;
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        ConnectionStateFactory _connectionStateFactory;
 | 
			
		||||
 | 
			
		||||
        // Methods
 | 
			
		||||
        void run();
 | 
			
		||||
        virtual void handleConnection(int fd) = 0;
 | 
			
		||||
        virtual void handleConnection(int fd,
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState) = 0;
 | 
			
		||||
        virtual size_t getConnectedClientsCount() = 0;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -79,10 +79,10 @@ namespace ix
 | 
			
		||||
        return _perMessageDeflateOptions;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocket::setHeartBeatPeriod(int hearBeatPeriod)
 | 
			
		||||
    void WebSocket::setHeartBeatPeriod(int heartBeatPeriod)
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_configMutex);
 | 
			
		||||
        _heartBeatPeriod = hearBeatPeriod;
 | 
			
		||||
        _heartBeatPeriod = heartBeatPeriod;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int WebSocket::getHeartBeatPeriod() const
 | 
			
		||||
@@ -302,7 +302,13 @@ namespace ix
 | 
			
		||||
    WebSocketSendInfo WebSocket::send(const std::string& text,
 | 
			
		||||
                                      const OnProgressCallback& onProgressCallback)
 | 
			
		||||
    {
 | 
			
		||||
        return sendMessage(text, false, onProgressCallback);
 | 
			
		||||
        return sendMessage(text, SendMessageKind::Binary, onProgressCallback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketSendInfo WebSocket::sendText(const std::string& text,
 | 
			
		||||
                                          const OnProgressCallback& onProgressCallback)
 | 
			
		||||
    {
 | 
			
		||||
        return sendMessage(text, SendMessageKind::Text, onProgressCallback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketSendInfo WebSocket::ping(const std::string& text)
 | 
			
		||||
@@ -311,11 +317,11 @@ namespace ix
 | 
			
		||||
        constexpr size_t pingMaxPayloadSize = 125;
 | 
			
		||||
        if (text.size() > pingMaxPayloadSize) return WebSocketSendInfo(false);
 | 
			
		||||
 | 
			
		||||
        return sendMessage(text, true);
 | 
			
		||||
        return sendMessage(text, SendMessageKind::Ping);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketSendInfo WebSocket::sendMessage(const std::string& text,
 | 
			
		||||
                                             bool ping,
 | 
			
		||||
                                             SendMessageKind sendMessageKind,
 | 
			
		||||
                                             const OnProgressCallback& onProgressCallback)
 | 
			
		||||
    {
 | 
			
		||||
        if (!isConnected()) return WebSocketSendInfo(false);
 | 
			
		||||
@@ -332,13 +338,22 @@ namespace ix
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_writeMutex);
 | 
			
		||||
        WebSocketSendInfo webSocketSendInfo;
 | 
			
		||||
 | 
			
		||||
        if (ping)
 | 
			
		||||
        switch (sendMessageKind)
 | 
			
		||||
        {
 | 
			
		||||
            webSocketSendInfo = _ws.sendPing(text);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            webSocketSendInfo = _ws.sendBinary(text, onProgressCallback);
 | 
			
		||||
            case SendMessageKind::Text:
 | 
			
		||||
            {
 | 
			
		||||
                webSocketSendInfo = _ws.sendText(text, onProgressCallback);
 | 
			
		||||
            } break;
 | 
			
		||||
 | 
			
		||||
            case SendMessageKind::Binary:
 | 
			
		||||
            {
 | 
			
		||||
                webSocketSendInfo = _ws.sendBinary(text, onProgressCallback);
 | 
			
		||||
            } break;
 | 
			
		||||
 | 
			
		||||
            case SendMessageKind::Ping:
 | 
			
		||||
            {
 | 
			
		||||
                webSocketSendInfo = _ws.sendPing(text);
 | 
			
		||||
            } break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        WebSocket::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false);
 | 
			
		||||
@@ -379,4 +394,9 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        _automaticReconnection = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t WebSocket::bufferedAmount() const
 | 
			
		||||
    {
 | 
			
		||||
        return _ws.bufferedAmount();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -89,7 +89,7 @@ namespace ix
 | 
			
		||||
        void setUrl(const std::string& url);
 | 
			
		||||
        void setPerMessageDeflateOptions(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions);
 | 
			
		||||
        void setHandshakeTimeout(int handshakeTimeoutSecs);
 | 
			
		||||
        void setHeartBeatPeriod(int hearBeatPeriod);
 | 
			
		||||
        void setHeartBeatPeriod(int heartBeatPeriod);
 | 
			
		||||
 | 
			
		||||
        // Run asynchronously, by calling start and stop.
 | 
			
		||||
        void start();
 | 
			
		||||
@@ -101,6 +101,8 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        WebSocketSendInfo send(const std::string& text,
 | 
			
		||||
                               const OnProgressCallback& onProgressCallback = nullptr);
 | 
			
		||||
        WebSocketSendInfo sendText(const std::string& text,
 | 
			
		||||
                                   const OnProgressCallback& onProgressCallback = nullptr);
 | 
			
		||||
        WebSocketSendInfo ping(const std::string& text);
 | 
			
		||||
        void close();
 | 
			
		||||
 | 
			
		||||
@@ -112,6 +114,7 @@ namespace ix
 | 
			
		||||
        const std::string& getUrl() const;
 | 
			
		||||
        const WebSocketPerMessageDeflateOptions& getPerMessageDeflateOptions() const;
 | 
			
		||||
        int getHeartBeatPeriod() const;
 | 
			
		||||
        size_t bufferedAmount() const;
 | 
			
		||||
 | 
			
		||||
        void enableAutomaticReconnection();
 | 
			
		||||
        void disableAutomaticReconnection();
 | 
			
		||||
@@ -119,7 +122,7 @@ namespace ix
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
        WebSocketSendInfo sendMessage(const std::string& text,
 | 
			
		||||
                                      bool ping,
 | 
			
		||||
                                      SendMessageKind sendMessageKind,
 | 
			
		||||
                                      const OnProgressCallback& callback = nullptr);
 | 
			
		||||
 | 
			
		||||
        bool isConnected() const;
 | 
			
		||||
 
 | 
			
		||||
@@ -114,7 +114,7 @@ namespace ix
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << "HTTP/1.1 ";
 | 
			
		||||
        ss << code;
 | 
			
		||||
        ss << "\r\n";
 | 
			
		||||
        ss << " ";
 | 
			
		||||
        ss << reason;
 | 
			
		||||
        ss << "\r\n";
 | 
			
		||||
 | 
			
		||||
@@ -353,7 +353,7 @@ namespace ix
 | 
			
		||||
        WebSocketHandshakeKeyGen::generate(headers["sec-websocket-key"].c_str(), output);
 | 
			
		||||
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << "HTTP/1.1 101\r\n";
 | 
			
		||||
        ss << "HTTP/1.1 101 Switching Protocols\r\n";
 | 
			
		||||
        ss << "Sec-WebSocket-Accept: " << std::string(output) << "\r\n";
 | 
			
		||||
        ss << "Upgrade: websocket\r\n";
 | 
			
		||||
        ss << "Connection: Upgrade\r\n";
 | 
			
		||||
 
 | 
			
		||||
@@ -49,10 +49,12 @@ namespace ix
 | 
			
		||||
        _onConnectionCallback = callback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketServer::handleConnection(int fd)
 | 
			
		||||
    void WebSocketServer::handleConnection(
 | 
			
		||||
        int fd,
 | 
			
		||||
        std::shared_ptr<ConnectionState> connectionState)
 | 
			
		||||
    {
 | 
			
		||||
        auto webSocket = std::make_shared<WebSocket>();
 | 
			
		||||
        _onConnectionCallback(webSocket);
 | 
			
		||||
        _onConnectionCallback(webSocket, connectionState);
 | 
			
		||||
 | 
			
		||||
        webSocket->disableAutomaticReconnection();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,8 @@
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    using OnConnectionCallback = std::function<void(std::shared_ptr<WebSocket>)>;
 | 
			
		||||
    using OnConnectionCallback = std::function<void(std::shared_ptr<WebSocket>,
 | 
			
		||||
                                                    std::shared_ptr<ConnectionState>)>;
 | 
			
		||||
 | 
			
		||||
    class WebSocketServer : public SocketServer {
 | 
			
		||||
    public:
 | 
			
		||||
@@ -49,7 +50,8 @@ namespace ix
 | 
			
		||||
        const static int kDefaultHandShakeTimeoutSecs;
 | 
			
		||||
 | 
			
		||||
        // Methods
 | 
			
		||||
        virtual void handleConnection(int fd) final;
 | 
			
		||||
        virtual void handleConnection(int fd,
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState) final;
 | 
			
		||||
        virtual size_t getConnectedClientsCount() final;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,31 @@
 | 
			
		||||
/*
 | 
			
		||||
 * The MIT License (MIT)
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2012, 2013 <dhbaird@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in
 | 
			
		||||
 * all copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  IXWebSocketTransport.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 *  Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
@@ -14,14 +38,6 @@
 | 
			
		||||
#include "IXUrlParser.h"
 | 
			
		||||
#include "IXSocketFactory.h"
 | 
			
		||||
 | 
			
		||||
#ifdef IXWEBSOCKET_USE_TLS
 | 
			
		||||
# ifdef __APPLE__
 | 
			
		||||
#  include "IXSocketAppleSSL.h"
 | 
			
		||||
# else
 | 
			
		||||
#  include "IXSocketOpenSSL.h"
 | 
			
		||||
# endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
@@ -37,11 +53,12 @@
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    const std::string WebSocketTransport::kHeartBeatPingMessage("ixwebsocket::hearbeat");
 | 
			
		||||
    const std::string WebSocketTransport::kHeartBeatPingMessage("ixwebsocket::heartbeat");
 | 
			
		||||
    const int WebSocketTransport::kDefaultHeartBeatPeriod(-1);
 | 
			
		||||
    constexpr size_t WebSocketTransport::kChunkSize;
 | 
			
		||||
 | 
			
		||||
    WebSocketTransport::WebSocketTransport() :
 | 
			
		||||
        _useMask(true),
 | 
			
		||||
        _readyState(CLOSED),
 | 
			
		||||
        _closeCode(0),
 | 
			
		||||
        _closeWireSize(0),
 | 
			
		||||
@@ -59,11 +76,11 @@ namespace ix
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketTransport::configure(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions,
 | 
			
		||||
                                       int hearBeatPeriod)
 | 
			
		||||
                                       int heartBeatPeriod)
 | 
			
		||||
    {
 | 
			
		||||
        _perMessageDeflateOptions = perMessageDeflateOptions;
 | 
			
		||||
        _enablePerMessageDeflate = _perMessageDeflateOptions.enabled();
 | 
			
		||||
        _heartBeatPeriod = hearBeatPeriod;
 | 
			
		||||
        _heartBeatPeriod = heartBeatPeriod;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Client
 | 
			
		||||
@@ -80,16 +97,6 @@ namespace ix
 | 
			
		||||
                                       std::string("Could not parse URL ") + url);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (protocol != "ws" && protocol != "wss")
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "Invalid protocol: " << protocol
 | 
			
		||||
               << " for url " << url
 | 
			
		||||
               << " . Supported protocols are ws and wss";
 | 
			
		||||
 | 
			
		||||
            return WebSocketInitResult(false, 0, ss.str());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool tls = protocol == "wss";
 | 
			
		||||
        std::string errorMsg;
 | 
			
		||||
        _socket = createSocket(tls, errorMsg);
 | 
			
		||||
@@ -117,8 +124,16 @@ namespace ix
 | 
			
		||||
    // Server
 | 
			
		||||
    WebSocketInitResult WebSocketTransport::connectToSocket(int fd, int timeoutSecs)
 | 
			
		||||
    {
 | 
			
		||||
        _socket.reset();
 | 
			
		||||
        _socket = std::make_shared<Socket>(fd);
 | 
			
		||||
        // Server should not mask the data it sends to the client
 | 
			
		||||
        _useMask = false;
 | 
			
		||||
 | 
			
		||||
        std::string errorMsg;
 | 
			
		||||
        _socket = createSocket(fd, errorMsg);
 | 
			
		||||
 | 
			
		||||
        if (!_socket)
 | 
			
		||||
        {
 | 
			
		||||
            return WebSocketInitResult(false, 0, errorMsg);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        WebSocketHandshake webSocketHandshake(_requestInitCancellation,
 | 
			
		||||
                                              _socket,
 | 
			
		||||
@@ -178,43 +193,75 @@ namespace ix
 | 
			
		||||
                // If (1) heartbeat is enabled, and (2) no data was received or
 | 
			
		||||
                // send for a duration exceeding our heart-beat period, send a
 | 
			
		||||
                // ping to the server.
 | 
			
		||||
                if (pollResult == PollResultType_Timeout &&
 | 
			
		||||
                if (pollResult == PollResultType::Timeout &&
 | 
			
		||||
                    heartBeatPeriodExceeded())
 | 
			
		||||
                {
 | 
			
		||||
                    std::stringstream ss;
 | 
			
		||||
                    ss << kHeartBeatPingMessage << "::" << _heartBeatPeriod << "s";
 | 
			
		||||
                    sendPing(ss.str());
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                while (true)
 | 
			
		||||
                // Make sure we send all the buffered data
 | 
			
		||||
                // there can be a lot of it for large messages.
 | 
			
		||||
                else if (pollResult == PollResultType::SendRequest)
 | 
			
		||||
                {
 | 
			
		||||
                    ssize_t ret = _socket->recv((char*)&_readbuf[0], _readbuf.size());
 | 
			
		||||
                    while (!isSendBufferEmpty() && !_requestInitCancellation)
 | 
			
		||||
                    {
 | 
			
		||||
                        // Wait with a 10ms timeout until the socket is ready to write.
 | 
			
		||||
                        // This way we are not busy looping
 | 
			
		||||
                        PollResultType result = _socket->isReadyToWrite(10);
 | 
			
		||||
 | 
			
		||||
                    if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
 | 
			
		||||
                                    _socket->getErrno() == EAGAIN))
 | 
			
		||||
                    {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (ret <= 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        _rxbuf.clear();
 | 
			
		||||
                        _socket->close();
 | 
			
		||||
                        setReadyState(CLOSED);
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        _rxbuf.insert(_rxbuf.end(),
 | 
			
		||||
                                      _readbuf.begin(),
 | 
			
		||||
                                      _readbuf.begin() + ret);
 | 
			
		||||
                        if (result == PollResultType::Error)
 | 
			
		||||
                        {
 | 
			
		||||
                            _socket->close();
 | 
			
		||||
                            setReadyState(CLOSED);
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (result == PollResultType::ReadyForWrite)
 | 
			
		||||
                        {
 | 
			
		||||
                            sendOnSocket();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (pollResult == PollResultType::ReadyForRead)
 | 
			
		||||
                {
 | 
			
		||||
                    while (true)
 | 
			
		||||
                    {
 | 
			
		||||
                        ssize_t ret = _socket->recv((char*)&_readbuf[0], _readbuf.size());
 | 
			
		||||
 | 
			
		||||
                if (isSendBufferEmpty() && _readyState == CLOSING)
 | 
			
		||||
                        if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
 | 
			
		||||
                                        _socket->getErrno() == EAGAIN))
 | 
			
		||||
                        {
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (ret <= 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            _rxbuf.clear();
 | 
			
		||||
                            _socket->close();
 | 
			
		||||
                            setReadyState(CLOSED);
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            _rxbuf.insert(_rxbuf.end(),
 | 
			
		||||
                                          _readbuf.begin(),
 | 
			
		||||
                                          _readbuf.begin() + ret);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (pollResult == PollResultType::Error)
 | 
			
		||||
                {
 | 
			
		||||
                    _socket->close();
 | 
			
		||||
                }
 | 
			
		||||
                else if (pollResult == PollResultType::CloseRequest)
 | 
			
		||||
                {
 | 
			
		||||
                    _socket->close();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Avoid a race condition where we get stuck in select
 | 
			
		||||
                // while closing.
 | 
			
		||||
                if (_readyState == CLOSING)
 | 
			
		||||
                {
 | 
			
		||||
                    _socket->close();
 | 
			
		||||
                    setReadyState(CLOSED);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            _heartBeatPeriod);
 | 
			
		||||
@@ -237,19 +284,15 @@ namespace ix
 | 
			
		||||
        _txbuf.insert(_txbuf.end(), header.begin(), header.end());
 | 
			
		||||
        _txbuf.insert(_txbuf.end(), begin, end);
 | 
			
		||||
 | 
			
		||||
        // Masking
 | 
			
		||||
        for (size_t i = 0; i != (size_t) message_size; ++i)
 | 
			
		||||
        if (_useMask)
 | 
			
		||||
        {
 | 
			
		||||
            *(_txbuf.end() - (size_t) message_size + i) ^= masking_key[i&0x3];
 | 
			
		||||
            for (size_t i = 0; i != (size_t) message_size; ++i)
 | 
			
		||||
            {
 | 
			
		||||
                *(_txbuf.end() - (size_t) message_size + i) ^= masking_key[i&0x3];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketTransport::appendToSendBuffer(const std::vector<uint8_t>& buffer)
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_txbufMutex);
 | 
			
		||||
        _txbuf.insert(_txbuf.end(), buffer.begin(), buffer.end());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketTransport::unmaskReceiveBuffer(const wsheader_type& ws)
 | 
			
		||||
    {
 | 
			
		||||
        if (ws.mask)
 | 
			
		||||
@@ -586,11 +629,11 @@ namespace ix
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Make sure we send all the buffered data ; there can be a lot of it
 | 
			
		||||
        // for large messages.
 | 
			
		||||
        // TODO / this will block the sending thread ; we need to eval whether
 | 
			
		||||
        //        this is the right fix
 | 
			
		||||
        while (!isSendBufferEmpty()) sendOnSocket();
 | 
			
		||||
        // Request to flush the send buffer on the background thread if it isn't empty
 | 
			
		||||
        if (!isSendBufferEmpty())
 | 
			
		||||
        {
 | 
			
		||||
            _socket->wakeUpFromPoll(Socket::kSendRequest);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return WebSocketSendInfo(true, compressionError, payloadSize, wireSize);
 | 
			
		||||
    }
 | 
			
		||||
@@ -613,7 +656,8 @@ namespace ix
 | 
			
		||||
        std::vector<uint8_t> header;
 | 
			
		||||
        header.assign(2 +
 | 
			
		||||
                      (message_size >= 126 ? 2 : 0) +
 | 
			
		||||
                      (message_size >= 65536 ? 6 : 0) + 4, 0);
 | 
			
		||||
                      (message_size >= 65536 ? 6 : 0) +
 | 
			
		||||
                      (_useMask ? 4 : 0), 0);
 | 
			
		||||
        header[0] = type;
 | 
			
		||||
 | 
			
		||||
        // The fin bit indicate that this is the last fragment. Fin is French for end.
 | 
			
		||||
@@ -630,27 +674,33 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        if (message_size < 126)
 | 
			
		||||
        {
 | 
			
		||||
            header[1] = (message_size & 0xff) | 0x80;
 | 
			
		||||
            header[1] = (message_size & 0xff) | (_useMask ? 0x80 : 0);
 | 
			
		||||
 | 
			
		||||
            header[2] = masking_key[0];
 | 
			
		||||
            header[3] = masking_key[1];
 | 
			
		||||
            header[4] = masking_key[2];
 | 
			
		||||
            header[5] = masking_key[3];
 | 
			
		||||
            if (_useMask)
 | 
			
		||||
            {
 | 
			
		||||
                header[2] = masking_key[0];
 | 
			
		||||
                header[3] = masking_key[1];
 | 
			
		||||
                header[4] = masking_key[2];
 | 
			
		||||
                header[5] = masking_key[3];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (message_size < 65536)
 | 
			
		||||
        {
 | 
			
		||||
            header[1] = 126 | 0x80;
 | 
			
		||||
            header[1] = 126 | (_useMask ? 0x80 : 0);
 | 
			
		||||
            header[2] = (message_size >> 8) & 0xff;
 | 
			
		||||
            header[3] = (message_size >> 0) & 0xff;
 | 
			
		||||
 | 
			
		||||
            header[4] = masking_key[0];
 | 
			
		||||
            header[5] = masking_key[1];
 | 
			
		||||
            header[6] = masking_key[2];
 | 
			
		||||
            header[7] = masking_key[3];
 | 
			
		||||
            if (_useMask)
 | 
			
		||||
            {
 | 
			
		||||
                header[4] = masking_key[0];
 | 
			
		||||
                header[5] = masking_key[1];
 | 
			
		||||
                header[6] = masking_key[2];
 | 
			
		||||
                header[7] = masking_key[3];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        { // TODO: run coverage testing here
 | 
			
		||||
            header[1] = 127 | 0x80;
 | 
			
		||||
            header[1] = 127 | (_useMask ? 0x80 : 0);
 | 
			
		||||
            header[2] = (message_size >> 56) & 0xff;
 | 
			
		||||
            header[3] = (message_size >> 48) & 0xff;
 | 
			
		||||
            header[4] = (message_size >> 40) & 0xff;
 | 
			
		||||
@@ -660,10 +710,13 @@ namespace ix
 | 
			
		||||
            header[8] = (message_size >>  8) & 0xff;
 | 
			
		||||
            header[9] = (message_size >>  0) & 0xff;
 | 
			
		||||
 | 
			
		||||
            header[10] = masking_key[0];
 | 
			
		||||
            header[11] = masking_key[1];
 | 
			
		||||
            header[12] = masking_key[2];
 | 
			
		||||
            header[13] = masking_key[3];
 | 
			
		||||
            if (_useMask)
 | 
			
		||||
            {
 | 
			
		||||
                header[10] = masking_key[0];
 | 
			
		||||
                header[11] = masking_key[1];
 | 
			
		||||
                header[12] = masking_key[2];
 | 
			
		||||
                header[13] = masking_key[3];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // _txbuf will keep growing until it can be transmitted over the socket:
 | 
			
		||||
@@ -689,6 +742,15 @@ namespace ix
 | 
			
		||||
                        _enablePerMessageDeflate, onProgressCallback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    WebSocketSendInfo WebSocketTransport::sendText(
 | 
			
		||||
        const std::string& message,
 | 
			
		||||
        const OnProgressCallback& onProgressCallback)
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        return sendData(wsheader_type::TEXT_FRAME, message,
 | 
			
		||||
                        _enablePerMessageDeflate, onProgressCallback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketTransport::sendOnSocket()
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_txbufMutex);
 | 
			
		||||
@@ -737,8 +799,18 @@ namespace ix
 | 
			
		||||
        sendData(wsheader_type::CLOSE, normalClosure, compress);
 | 
			
		||||
        setReadyState(CLOSING);
 | 
			
		||||
 | 
			
		||||
        _socket->wakeUpFromPoll();
 | 
			
		||||
        _socket->wakeUpFromPoll(Socket::kCloseRequest);
 | 
			
		||||
        _socket->close();
 | 
			
		||||
 | 
			
		||||
        _closeCode = 1000;
 | 
			
		||||
        _closeReason = "Normal Closure";
 | 
			
		||||
        setReadyState(CLOSED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t WebSocketTransport::bufferedAmount() const
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_txbufMutex);
 | 
			
		||||
        return _txbuf.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,13 @@ namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class Socket;
 | 
			
		||||
 | 
			
		||||
    enum class SendMessageKind
 | 
			
		||||
    {
 | 
			
		||||
        Text,
 | 
			
		||||
        Binary,
 | 
			
		||||
        Ping
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class WebSocketTransport
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
@@ -61,7 +68,7 @@ namespace ix
 | 
			
		||||
        ~WebSocketTransport();
 | 
			
		||||
 | 
			
		||||
        void configure(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions,
 | 
			
		||||
                       int hearBeatPeriod);
 | 
			
		||||
                       int heartBeatPeriod);
 | 
			
		||||
 | 
			
		||||
        WebSocketInitResult connectToUrl(const std::string& url, // Client
 | 
			
		||||
                                         int timeoutSecs);
 | 
			
		||||
@@ -71,12 +78,15 @@ namespace ix
 | 
			
		||||
        void poll();
 | 
			
		||||
        WebSocketSendInfo sendBinary(const std::string& message,
 | 
			
		||||
                                     const OnProgressCallback& onProgressCallback);
 | 
			
		||||
        WebSocketSendInfo sendText(const std::string& message,
 | 
			
		||||
                                   const OnProgressCallback& onProgressCallback);
 | 
			
		||||
        WebSocketSendInfo sendPing(const std::string& message);
 | 
			
		||||
        void close();
 | 
			
		||||
        ReadyStateValues getReadyState() const;
 | 
			
		||||
        void setReadyState(ReadyStateValues readyStateValue);
 | 
			
		||||
        void setOnCloseCallback(const OnCloseCallback& onCloseCallback);
 | 
			
		||||
        void dispatch(const OnMessageCallback& onMessageCallback);
 | 
			
		||||
        size_t bufferedAmount() const;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        std::string _url;
 | 
			
		||||
@@ -99,6 +109,10 @@ namespace ix
 | 
			
		||||
            uint8_t masking_key[4];
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Tells whether we should mask the data we send.
 | 
			
		||||
        // client should mask but server should not
 | 
			
		||||
        bool _useMask;
 | 
			
		||||
 | 
			
		||||
        // Buffer for reading from our socket. That buffer is never resized.
 | 
			
		||||
        std::vector<uint8_t> _readbuf;
 | 
			
		||||
 | 
			
		||||
@@ -147,7 +161,7 @@ namespace ix
 | 
			
		||||
        mutable std::mutex _lastSendTimePointMutex;
 | 
			
		||||
        std::chrono::time_point<std::chrono::steady_clock> _lastSendTimePoint;
 | 
			
		||||
 | 
			
		||||
        // No data was send through the socket for longer that the hearbeat period
 | 
			
		||||
        // No data was send through the socket for longer than the heartbeat period
 | 
			
		||||
        bool heartBeatPeriodExceeded();
 | 
			
		||||
 | 
			
		||||
        void sendOnSocket();
 | 
			
		||||
@@ -173,7 +187,6 @@ namespace ix
 | 
			
		||||
                                std::string::const_iterator end,
 | 
			
		||||
                                uint64_t message_size,
 | 
			
		||||
                                uint8_t masking_key[4]);
 | 
			
		||||
        void appendToSendBuffer(const std::vector<uint8_t>& buffer);
 | 
			
		||||
 | 
			
		||||
        unsigned getRandomUnsigned();
 | 
			
		||||
        void unmaskReceiveBuffer(const wsheader_type& ws);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								makefile
									
									
									
									
									
								
							
							
						
						@@ -3,15 +3,29 @@
 | 
			
		||||
#
 | 
			
		||||
all: brew
 | 
			
		||||
 | 
			
		||||
install: brew
 | 
			
		||||
 | 
			
		||||
brew:
 | 
			
		||||
	mkdir -p build && (cd build ; cmake .. ; make -j install)
 | 
			
		||||
 | 
			
		||||
.PHONY: docker
 | 
			
		||||
 | 
			
		||||
NAME   := bsergean/ws
 | 
			
		||||
TAG    := $(shell cat DOCKER_VERSION)
 | 
			
		||||
IMG    := ${NAME}:${TAG}
 | 
			
		||||
LATEST := ${NAME}:latest
 | 
			
		||||
BUILD  := ${NAME}:build
 | 
			
		||||
 | 
			
		||||
docker:
 | 
			
		||||
	docker build -t broadcast_server:latest .
 | 
			
		||||
	docker build -t ${IMG} .
 | 
			
		||||
	docker tag ${IMG} ${BUILD}
 | 
			
		||||
 | 
			
		||||
docker_push:
 | 
			
		||||
	docker tag ${IMG} ${LATEST}
 | 
			
		||||
	docker push ${LATEST}
 | 
			
		||||
 | 
			
		||||
run:
 | 
			
		||||
	docker run --cap-add sys_ptrace -it broadcast_server:latest bash
 | 
			
		||||
	docker run --cap-add sys_ptrace -it ws:latest
 | 
			
		||||
 | 
			
		||||
# this is helpful to remove trailing whitespaces
 | 
			
		||||
trail:
 | 
			
		||||
@@ -36,6 +50,9 @@ test_server:
 | 
			
		||||
test:
 | 
			
		||||
	python test/run.py
 | 
			
		||||
 | 
			
		||||
ws_test: all
 | 
			
		||||
	(cd ws ; bash test_ws.sh)
 | 
			
		||||
 | 
			
		||||
# For the fork that is configured with appveyor
 | 
			
		||||
rebase_upstream:
 | 
			
		||||
	git fetch upstream
 | 
			
		||||
@@ -43,5 +60,9 @@ rebase_upstream:
 | 
			
		||||
	git reset --hard upstream/master
 | 
			
		||||
	git push origin master --force
 | 
			
		||||
 | 
			
		||||
install_cmake_for_linux:
 | 
			
		||||
	mkdir -p /tmp/cmake
 | 
			
		||||
	(cd /tmp/cmake ; curl -L -O https://github.com/Kitware/CMake/releases/download/v3.14.0-rc4/cmake-3.14.0-rc4-Linux-x86_64.tar.gz ; tar zxf cmake-3.14.0-rc4-Linux-x86_64.tar.gz)
 | 
			
		||||
 | 
			
		||||
.PHONY: test
 | 
			
		||||
.PHONY: build
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ set (SOURCES
 | 
			
		||||
 | 
			
		||||
  IXDNSLookupTest.cpp
 | 
			
		||||
  IXSocketTest.cpp
 | 
			
		||||
  IXSocketConnectTest.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Some unittest don't work on windows yet
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								test/IXSocketConnectTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,43 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXSocketConnectTest.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2018 Machine Zone. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "catch.hpp"
 | 
			
		||||
 | 
			
		||||
#include "IXTest.h"
 | 
			
		||||
#include <ixwebsocket/IXSocketConnect.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
using namespace ix;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TEST_CASE("socket_connect", "[net]")
 | 
			
		||||
{
 | 
			
		||||
    SECTION("Test connecting to a known hostname")
 | 
			
		||||
    {
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        int fd = SocketConnect::connect("www.google.com", 80, errMsg, [] { return false; });
 | 
			
		||||
        std::cerr << "Error message: " << errMsg << std::endl;
 | 
			
		||||
        REQUIRE(fd != -1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("Test connecting to a non-existing hostname")
 | 
			
		||||
    {
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        std::string hostname("12313lhjlkjhopiupoijlkasdckljqwehrlkqjwehraospidcuaposidcasdc");
 | 
			
		||||
        int fd = SocketConnect::connect(hostname, 80, errMsg, [] { return false; });
 | 
			
		||||
        std::cerr << "Error message: " << errMsg << std::endl;
 | 
			
		||||
        REQUIRE(fd == -1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SECTION("Test connecting to a good hostname, with cancellation")
 | 
			
		||||
    {
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        // The callback returning true means we are requesting cancellation
 | 
			
		||||
        int fd = SocketConnect::connect("www.google.com", 80, errMsg, [] { return true; });
 | 
			
		||||
        std::cerr << "Error message: " << errMsg << std::endl;
 | 
			
		||||
        REQUIRE(fd == -1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,17 +5,10 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <ixwebsocket/IXSocketFactory.h>
 | 
			
		||||
#include <ixwebsocket/IXSocket.h>
 | 
			
		||||
#include <ixwebsocket/IXCancellationRequest.h>
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__) or defined(__linux__)
 | 
			
		||||
# ifdef __APPLE__
 | 
			
		||||
#  include <ixwebsocket/IXSocketAppleSSL.h>
 | 
			
		||||
# else
 | 
			
		||||
#  include <ixwebsocket/IXSocketOpenSSL.h>
 | 
			
		||||
# endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "IXTest.h"
 | 
			
		||||
#include "catch.hpp"
 | 
			
		||||
#include <string.h>
 | 
			
		||||
@@ -40,16 +33,15 @@ namespace ix
 | 
			
		||||
        Logger() << "errMsg: " << errMsg;
 | 
			
		||||
        REQUIRE(success);
 | 
			
		||||
 | 
			
		||||
        std::cout << "Sending request: " << request
 | 
			
		||||
                  << "to " << host << ":" << port
 | 
			
		||||
                  << std::endl;
 | 
			
		||||
        Logger() << "Sending request: " << request
 | 
			
		||||
                 << "to " << host << ":" << port;
 | 
			
		||||
        REQUIRE(socket->writeBytes(request, isCancellationRequested));
 | 
			
		||||
 | 
			
		||||
        auto lineResult = socket->readLine(isCancellationRequested);
 | 
			
		||||
        auto lineValid = lineResult.first;
 | 
			
		||||
        auto line = lineResult.second;
 | 
			
		||||
 | 
			
		||||
        std::cout << "read error: " << strerror(Socket::getErrno()) << std::endl;
 | 
			
		||||
        Logger() << "read error: " << strerror(Socket::getErrno());
 | 
			
		||||
 | 
			
		||||
        REQUIRE(lineValid);
 | 
			
		||||
 | 
			
		||||
@@ -63,10 +55,18 @@ TEST_CASE("socket", "[socket]")
 | 
			
		||||
{
 | 
			
		||||
    SECTION("Connect to google HTTP server. Send GET request without header. Should return 200")
 | 
			
		||||
    {
 | 
			
		||||
        std::shared_ptr<Socket> socket(new Socket);
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        bool tls = false;
 | 
			
		||||
        std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
 | 
			
		||||
        std::string host("www.google.com");
 | 
			
		||||
        int port = 80;
 | 
			
		||||
        std::string request("GET / HTTP/1.1\r\n\r\n");
 | 
			
		||||
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << "GET / HTTP/1.1\r\n";
 | 
			
		||||
        ss << "Host: " << host << "\r\n";
 | 
			
		||||
        ss << "\r\n";
 | 
			
		||||
        std::string request(ss.str());
 | 
			
		||||
 | 
			
		||||
        int expectedStatus = 200;
 | 
			
		||||
        int timeoutSecs = 3;
 | 
			
		||||
 | 
			
		||||
@@ -76,11 +76,9 @@ TEST_CASE("socket", "[socket]")
 | 
			
		||||
#if defined(__APPLE__) or defined(__linux__)
 | 
			
		||||
    SECTION("Connect to google HTTPS server. Send GET request without header. Should return 200")
 | 
			
		||||
    {
 | 
			
		||||
# ifdef __APPLE__
 | 
			
		||||
        std::shared_ptr<Socket> socket = std::make_shared<SocketAppleSSL>();
 | 
			
		||||
# else
 | 
			
		||||
        std::shared_ptr<Socket> socket = std::make_shared<SocketOpenSSL>();
 | 
			
		||||
# endif
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        bool tls = true;
 | 
			
		||||
        std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
 | 
			
		||||
        std::string host("www.google.com");
 | 
			
		||||
        int port = 443;
 | 
			
		||||
        std::string request("GET / HTTP/1.1\r\n\r\n");
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stack>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
@@ -148,4 +149,21 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void hexDump(const std::string& prefix,
 | 
			
		||||
                 const std::string& s)
 | 
			
		||||
    {
 | 
			
		||||
        std::ostringstream ss;
 | 
			
		||||
        bool upper_case = false;
 | 
			
		||||
 | 
			
		||||
        for (std::string::size_type i = 0; i < s.length(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            ss << std::hex
 | 
			
		||||
               << std::setfill('0')
 | 
			
		||||
               << std::setw(2)
 | 
			
		||||
               << (upper_case ? std::uppercase : std::nouppercase) << (int)s[i];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::cout << prefix << ": " << s << " => " << ss.str() << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ namespace
 | 
			
		||||
        _webSocket.setUrl(url);
 | 
			
		||||
 | 
			
		||||
        // The important bit for this test.
 | 
			
		||||
        // Set a 1 second hearbeat ; if no traffic is present on the connection for 1 second
 | 
			
		||||
        // Set a 1 second heartbeat ; if no traffic is present on the connection for 1 second
 | 
			
		||||
        // a ping message will be sent by the client.
 | 
			
		||||
        _webSocket.setHeartBeatPeriod(1);
 | 
			
		||||
 | 
			
		||||
@@ -128,10 +128,11 @@ namespace
 | 
			
		||||
    {
 | 
			
		||||
        // A dev/null server
 | 
			
		||||
        server.setOnConnectionCallback(
 | 
			
		||||
            [&server, &receivedPingMessages](std::shared_ptr<ix::WebSocket> webSocket)
 | 
			
		||||
            [&server, &receivedPingMessages](std::shared_ptr<ix::WebSocket> webSocket,
 | 
			
		||||
                                             std::shared_ptr<ConnectionState> connectionState)
 | 
			
		||||
            {
 | 
			
		||||
                webSocket->setOnMessageCallback(
 | 
			
		||||
                    [webSocket, &server, &receivedPingMessages](ix::WebSocketMessageType messageType,
 | 
			
		||||
                    [webSocket, connectionState, &server, &receivedPingMessages](ix::WebSocketMessageType messageType,
 | 
			
		||||
                       const std::string& str,
 | 
			
		||||
                       size_t wireSize,
 | 
			
		||||
                       const ix::WebSocketErrorInfo& error,
 | 
			
		||||
@@ -141,6 +142,7 @@ namespace
 | 
			
		||||
                        if (messageType == ix::WebSocket_MessageType_Open)
 | 
			
		||||
                        {
 | 
			
		||||
                            Logger() << "New server connection";
 | 
			
		||||
                            Logger() << "id: " << connectionState->getId();
 | 
			
		||||
                            Logger() << "Uri: " << openInfo.uri;
 | 
			
		||||
                            Logger() << "Headers:";
 | 
			
		||||
                            for (auto it : openInfo.headers)
 | 
			
		||||
@@ -210,6 +212,10 @@ TEST_CASE("Websocket_heartbeat", "[heartbeat]")
 | 
			
		||||
        webSocketClientA.stop();
 | 
			
		||||
        webSocketClientB.stop();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Here we test heart beat period exceeded for clientA
 | 
			
		||||
        // but it should not be exceeded for clientB which has sent data.
 | 
			
		||||
        // -> expected ping messages == 2, but add a small buffer to make this more reliable.
 | 
			
		||||
        REQUIRE(serverReceivedPingMessages >= 2);
 | 
			
		||||
        REQUIRE(serverReceivedPingMessages <= 4);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
#include <ixwebsocket/IXSocket.h>
 | 
			
		||||
#include <ixwebsocket/IXWebSocket.h>
 | 
			
		||||
#include <ixwebsocket/IXWebSocketServer.h>
 | 
			
		||||
#include <ixwebsocket/IXSocketFactory.h>
 | 
			
		||||
 | 
			
		||||
#include "IXTest.h"
 | 
			
		||||
 | 
			
		||||
@@ -17,13 +18,32 @@ using namespace ix;
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    bool startServer(ix::WebSocketServer& server)
 | 
			
		||||
    // Test that we can override the connectionState impl to provide our own
 | 
			
		||||
    class ConnectionStateCustom : public ConnectionState
 | 
			
		||||
    {
 | 
			
		||||
        void computeId()
 | 
			
		||||
        {
 | 
			
		||||
            // a very boring invariant id that we can test against in the unittest
 | 
			
		||||
            _id = "foobarConnectionId";
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    bool startServer(ix::WebSocketServer& server,
 | 
			
		||||
                     std::string& connectionId)
 | 
			
		||||
    {
 | 
			
		||||
        auto factory = []() -> std::shared_ptr<ConnectionState>
 | 
			
		||||
        {
 | 
			
		||||
            return std::make_shared<ConnectionStateCustom>();
 | 
			
		||||
        };
 | 
			
		||||
        server.setConnectionStateFactory(factory);
 | 
			
		||||
 | 
			
		||||
        server.setOnConnectionCallback(
 | 
			
		||||
            [&server](std::shared_ptr<ix::WebSocket> webSocket)
 | 
			
		||||
            [&server, &connectionId](std::shared_ptr<ix::WebSocket> webSocket,
 | 
			
		||||
                      std::shared_ptr<ConnectionState> connectionState)
 | 
			
		||||
            {
 | 
			
		||||
                webSocket->setOnMessageCallback(
 | 
			
		||||
                    [webSocket, &server](ix::WebSocketMessageType messageType,
 | 
			
		||||
                    [webSocket, connectionState,
 | 
			
		||||
                     &connectionId, &server](ix::WebSocketMessageType messageType,
 | 
			
		||||
                       const std::string& str,
 | 
			
		||||
                       size_t wireSize,
 | 
			
		||||
                       const ix::WebSocketErrorInfo& error,
 | 
			
		||||
@@ -33,12 +53,16 @@ namespace ix
 | 
			
		||||
                        if (messageType == ix::WebSocket_MessageType_Open)
 | 
			
		||||
                        {
 | 
			
		||||
                            Logger() << "New connection";
 | 
			
		||||
                            connectionState->computeId();
 | 
			
		||||
                            Logger() << "id: " << connectionState->getId();
 | 
			
		||||
                            Logger() << "Uri: " << openInfo.uri;
 | 
			
		||||
                            Logger() << "Headers:";
 | 
			
		||||
                            for (auto it : openInfo.headers)
 | 
			
		||||
                            {
 | 
			
		||||
                                Logger() << it.first << ": " << it.second;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            connectionId = connectionState->getId();
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (messageType == ix::WebSocket_MessageType_Close)
 | 
			
		||||
                        {
 | 
			
		||||
@@ -77,19 +101,21 @@ TEST_CASE("Websocket_server", "[websocket_server]")
 | 
			
		||||
    {
 | 
			
		||||
        int port = getFreePort();
 | 
			
		||||
        ix::WebSocketServer server(port);
 | 
			
		||||
        REQUIRE(startServer(server));
 | 
			
		||||
        std::string connectionId;
 | 
			
		||||
        REQUIRE(startServer(server, connectionId));
 | 
			
		||||
 | 
			
		||||
        Socket socket;
 | 
			
		||||
        std::string host("localhost");
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        bool tls = false;
 | 
			
		||||
        std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
 | 
			
		||||
        std::string host("localhost");
 | 
			
		||||
        auto isCancellationRequested = []() -> bool
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
        bool success = socket.connect(host, port, errMsg, isCancellationRequested);
 | 
			
		||||
        bool success = socket->connect(host, port, errMsg, isCancellationRequested);
 | 
			
		||||
        REQUIRE(success);
 | 
			
		||||
 | 
			
		||||
        auto lineResult = socket.readLine(isCancellationRequested);
 | 
			
		||||
        auto lineResult = socket->readLine(isCancellationRequested);
 | 
			
		||||
        auto lineValid = lineResult.first;
 | 
			
		||||
        auto line = lineResult.second;
 | 
			
		||||
 | 
			
		||||
@@ -109,22 +135,24 @@ TEST_CASE("Websocket_server", "[websocket_server]")
 | 
			
		||||
    {
 | 
			
		||||
        int port = getFreePort();
 | 
			
		||||
        ix::WebSocketServer server(port);
 | 
			
		||||
        REQUIRE(startServer(server));
 | 
			
		||||
        std::string connectionId;
 | 
			
		||||
        REQUIRE(startServer(server, connectionId));
 | 
			
		||||
 | 
			
		||||
        Socket socket;
 | 
			
		||||
        std::string host("localhost");
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        bool tls = false;
 | 
			
		||||
        std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
 | 
			
		||||
        std::string host("localhost");
 | 
			
		||||
        auto isCancellationRequested = []() -> bool
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
        bool success = socket.connect(host, port, errMsg, isCancellationRequested);
 | 
			
		||||
        bool success = socket->connect(host, port, errMsg, isCancellationRequested);
 | 
			
		||||
        REQUIRE(success);
 | 
			
		||||
 | 
			
		||||
        Logger() << "writeBytes";
 | 
			
		||||
        socket.writeBytes("GET /\r\n", isCancellationRequested);
 | 
			
		||||
        socket->writeBytes("GET /\r\n", isCancellationRequested);
 | 
			
		||||
 | 
			
		||||
        auto lineResult = socket.readLine(isCancellationRequested);
 | 
			
		||||
        auto lineResult = socket->readLine(isCancellationRequested);
 | 
			
		||||
        auto lineValid = lineResult.first;
 | 
			
		||||
        auto line = lineResult.second;
 | 
			
		||||
 | 
			
		||||
@@ -144,26 +172,28 @@ TEST_CASE("Websocket_server", "[websocket_server]")
 | 
			
		||||
    {
 | 
			
		||||
        int port = getFreePort();
 | 
			
		||||
        ix::WebSocketServer server(port);
 | 
			
		||||
        REQUIRE(startServer(server));
 | 
			
		||||
        std::string connectionId;
 | 
			
		||||
        REQUIRE(startServer(server, connectionId));
 | 
			
		||||
 | 
			
		||||
        Socket socket;
 | 
			
		||||
        std::string host("localhost");
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        bool tls = false;
 | 
			
		||||
        std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
 | 
			
		||||
        std::string host("localhost");
 | 
			
		||||
        auto isCancellationRequested = []() -> bool
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
        bool success = socket.connect(host, port, errMsg, isCancellationRequested);
 | 
			
		||||
        bool success = socket->connect(host, port, errMsg, isCancellationRequested);
 | 
			
		||||
        REQUIRE(success);
 | 
			
		||||
 | 
			
		||||
        socket.writeBytes("GET / HTTP/1.1\r\n"
 | 
			
		||||
                          "Upgrade: websocket\r\n"
 | 
			
		||||
                          "Sec-WebSocket-Version: 13\r\n"
 | 
			
		||||
                          "Sec-WebSocket-Key: foobar\r\n"
 | 
			
		||||
                          "\r\n",
 | 
			
		||||
                          isCancellationRequested);
 | 
			
		||||
        socket->writeBytes("GET / HTTP/1.1\r\n"
 | 
			
		||||
                           "Upgrade: websocket\r\n"
 | 
			
		||||
                           "Sec-WebSocket-Version: 13\r\n"
 | 
			
		||||
                           "Sec-WebSocket-Key: foobar\r\n"
 | 
			
		||||
                           "\r\n",
 | 
			
		||||
                           isCancellationRequested);
 | 
			
		||||
 | 
			
		||||
        auto lineResult = socket.readLine(isCancellationRequested);
 | 
			
		||||
        auto lineResult = socket->readLine(isCancellationRequested);
 | 
			
		||||
        auto lineValid = lineResult.first;
 | 
			
		||||
        auto line = lineResult.second;
 | 
			
		||||
 | 
			
		||||
@@ -174,6 +204,8 @@ TEST_CASE("Websocket_server", "[websocket_server]")
 | 
			
		||||
        // Give us 500ms for the server to notice that clients went away
 | 
			
		||||
        ix::msleep(500);
 | 
			
		||||
 | 
			
		||||
        REQUIRE(connectionId == "foobarConnectionId");
 | 
			
		||||
 | 
			
		||||
        server.stop();
 | 
			
		||||
        REQUIRE(server.getClients().size() == 0);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -164,10 +164,21 @@ namespace
 | 
			
		||||
                    ss << "cmd_websocket_chat: Error ! " << error.reason;
 | 
			
		||||
                    log(ss.str());
 | 
			
		||||
                }
 | 
			
		||||
                else if (messageType == ix::WebSocket_MessageType_Ping)
 | 
			
		||||
                {
 | 
			
		||||
                    log("cmd_websocket_chat: received ping message");
 | 
			
		||||
                }
 | 
			
		||||
                else if (messageType == ix::WebSocket_MessageType_Pong)
 | 
			
		||||
                {
 | 
			
		||||
                    log("cmd_websocket_chat: received pong message");
 | 
			
		||||
                }
 | 
			
		||||
                else if (messageType == ix::WebSocket_MessageType_Fragment)
 | 
			
		||||
                {
 | 
			
		||||
                    log("cmd_websocket_chat: received message fragment");
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    // FIXME: missing ping/pong messages
 | 
			
		||||
                    ss << "Invalid ix::WebSocketMessageType";
 | 
			
		||||
                    ss << "Unexpected ix::WebSocketMessageType";
 | 
			
		||||
                    log(ss.str());
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
@@ -206,10 +217,11 @@ namespace
 | 
			
		||||
    bool startServer(ix::WebSocketServer& server)
 | 
			
		||||
    {
 | 
			
		||||
        server.setOnConnectionCallback(
 | 
			
		||||
            [&server](std::shared_ptr<ix::WebSocket> webSocket)
 | 
			
		||||
            [&server](std::shared_ptr<ix::WebSocket> webSocket,
 | 
			
		||||
                      std::shared_ptr<ConnectionState> connectionState)
 | 
			
		||||
            {
 | 
			
		||||
                webSocket->setOnMessageCallback(
 | 
			
		||||
                    [webSocket, &server](ix::WebSocketMessageType messageType,
 | 
			
		||||
                    [webSocket, connectionState, &server](ix::WebSocketMessageType messageType,
 | 
			
		||||
                       const std::string& str,
 | 
			
		||||
                       size_t wireSize,
 | 
			
		||||
                       const ix::WebSocketErrorInfo& error,
 | 
			
		||||
@@ -219,6 +231,7 @@ namespace
 | 
			
		||||
                        if (messageType == ix::WebSocket_MessageType_Open)
 | 
			
		||||
                        {
 | 
			
		||||
                            Logger() << "New connection";
 | 
			
		||||
                            Logger() << "id: " << connectionState->getId();
 | 
			
		||||
                            Logger() << "Uri: " << openInfo.uri;
 | 
			
		||||
                            Logger() << "Headers:";
 | 
			
		||||
                            for (auto it : openInfo.headers)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								test/run.py
									
									
									
									
									
								
							
							
						
						@@ -2,14 +2,47 @@ import os
 | 
			
		||||
import platform
 | 
			
		||||
import shutil
 | 
			
		||||
 | 
			
		||||
import subprocess
 | 
			
		||||
import threading
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Command(object):
 | 
			
		||||
    """Run system commands with timeout
 | 
			
		||||
    
 | 
			
		||||
    From http://www.bo-yang.net/2016/12/01/python-run-command-with-timeout
 | 
			
		||||
    Python3 might have a builtin way to do that.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, cmd):
 | 
			
		||||
        self.cmd = cmd
 | 
			
		||||
        self.process = None
 | 
			
		||||
 | 
			
		||||
    def run_command(self, capture = False):
 | 
			
		||||
        self.process = subprocess.Popen(self.cmd, shell=True)
 | 
			
		||||
        self.process.communicate()
 | 
			
		||||
 | 
			
		||||
    def run(self, timeout = 5 * 60):
 | 
			
		||||
        '''5 minutes default timeout'''
 | 
			
		||||
        thread = threading.Thread(target=self.run_command, args=())
 | 
			
		||||
        thread.start()
 | 
			
		||||
        thread.join(timeout)
 | 
			
		||||
 | 
			
		||||
        if thread.is_alive():
 | 
			
		||||
            print('Command timeout, kill it: ' + self.cmd)
 | 
			
		||||
            self.process.terminate()
 | 
			
		||||
            thread.join()
 | 
			
		||||
            return False, 255
 | 
			
		||||
        else:
 | 
			
		||||
            return True, self.process.returncode
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
osName = platform.system()
 | 
			
		||||
print('os name = {}'.format(osName))
 | 
			
		||||
 | 
			
		||||
root = os.path.dirname(os.path.realpath(__file__))
 | 
			
		||||
buildDir = os.path.join(root, 'build')
 | 
			
		||||
buildDir = os.path.join(root, 'build', osName)
 | 
			
		||||
 | 
			
		||||
if not os.path.exists(buildDir):
 | 
			
		||||
    os.mkdir(buildDir)
 | 
			
		||||
    os.makedirs(buildDir)
 | 
			
		||||
 | 
			
		||||
os.chdir(buildDir)
 | 
			
		||||
 | 
			
		||||
@@ -38,7 +71,7 @@ sanitizerFlags = sanitizersFlags[sanitizer]
 | 
			
		||||
#     os.environ['CC'] = 'clang-cl'
 | 
			
		||||
#     os.environ['CXX'] = 'clang-cl'
 | 
			
		||||
 | 
			
		||||
cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug {} {} ..'.format(generator, sanitizerFlags)
 | 
			
		||||
cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug {} {} ../..'.format(generator, sanitizerFlags)
 | 
			
		||||
print(cmakeCmd)
 | 
			
		||||
ret = os.system(cmakeCmd)
 | 
			
		||||
assert ret == 0, 'CMake failed, exiting'
 | 
			
		||||
@@ -67,6 +100,7 @@ def findFiles(prefix):
 | 
			
		||||
 | 
			
		||||
# We need to copy the zlib DLL in the current work directory
 | 
			
		||||
shutil.copy(os.path.join(
 | 
			
		||||
    '..',
 | 
			
		||||
    '..',
 | 
			
		||||
    '..',
 | 
			
		||||
    'third_party',
 | 
			
		||||
@@ -77,6 +111,9 @@ shutil.copy(os.path.join(
 | 
			
		||||
    'bin',
 | 
			
		||||
    'zlib.dll'), '.')
 | 
			
		||||
 | 
			
		||||
testCommand = '{} {}'.format(testBinary, os.getenv('TEST', ''))
 | 
			
		||||
ret = os.system(testCommand)
 | 
			
		||||
# lldb = "lldb --batch -o 'run' -k 'thread backtrace all' -k 'quit 1'"
 | 
			
		||||
lldb = ""  # Disabled for now
 | 
			
		||||
testCommand = '{} {} {}'.format(lldb, testBinary, os.getenv('TEST', ''))
 | 
			
		||||
command = Command(testCommand)
 | 
			
		||||
timedout, ret = command.run()
 | 
			
		||||
assert ret == 0, 'Test command failed'
 | 
			
		||||
 
 | 
			
		||||
@@ -11,10 +11,6 @@
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    ix::Socket::init(); // for Windows
 | 
			
		||||
 | 
			
		||||
    int result = Catch::Session().run(argc, argv);
 | 
			
		||||
 | 
			
		||||
    ix::Socket::cleanup(); // for Windows
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								third_party/README.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
			
		||||
Except ZLIB on Windows (whose port is currently broken...) all dependencies here are for the ws command line tools, not for the IXWebSockets library which is standalone.
 | 
			
		||||
							
								
								
									
										20
									
								
								third_party/homebrew_formula.rb
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,20 +0,0 @@
 | 
			
		||||
class Ixwebsocket < Formula
 | 
			
		||||
  desc "WebSocket client and server, and HTTP client command-line tool"
 | 
			
		||||
  homepage "https://github.com/machinezone/IXWebSocket"
 | 
			
		||||
  url "https://github.com/machinezone/IXWebSocket/archive/v1.1.0.tar.gz"
 | 
			
		||||
  sha256 "52592ce3d0a67ad0f90ac9e8a458f61724175d95a01a38d1bad3fcdc5c7b6666"
 | 
			
		||||
  depends_on "cmake" => :build
 | 
			
		||||
 | 
			
		||||
  def install
 | 
			
		||||
    system "cmake", ".", *std_cmake_args
 | 
			
		||||
    system "make", "install"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  test do
 | 
			
		||||
    system "#{bin}/ws", "--help"
 | 
			
		||||
    system "#{bin}/ws", "send", "--help"
 | 
			
		||||
    system "#{bin}/ws", "receive", "--help"
 | 
			
		||||
    system "#{bin}/ws", "transfer", "--help"
 | 
			
		||||
    system "#{bin}/ws", "curl", "--help"
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										1
									
								
								third_party/remote_trailing_whitespaces.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,2 +1,3 @@
 | 
			
		||||
find . -type f -name '*.cpp' -exec sed -i '' 's/[[:space:]]*$//' {} \+
 | 
			
		||||
find . -type f -name '*.h' -exec sed -i '' 's/[[:space:]]*$//' {} \+
 | 
			
		||||
find . -type f -name '*.md' -exec sed -i '' 's/[[:space:]]*$//' {} \+
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								ws/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1 +1,2 @@
 | 
			
		||||
build
 | 
			
		||||
node_modules
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,8 @@ add_executable(ws
 | 
			
		||||
  ixcrypto/IXHash.cpp
 | 
			
		||||
  ixcrypto/IXUuid.cpp
 | 
			
		||||
 | 
			
		||||
  IXRedisClient.cpp
 | 
			
		||||
 | 
			
		||||
  ws_http_client.cpp
 | 
			
		||||
  ws_ping_pong.cpp
 | 
			
		||||
  ws_broadcast_server.cpp
 | 
			
		||||
@@ -32,11 +34,13 @@ add_executable(ws
 | 
			
		||||
  ws_transfer.cpp
 | 
			
		||||
  ws_send.cpp
 | 
			
		||||
  ws_receive.cpp
 | 
			
		||||
  ws_redis_publish.cpp
 | 
			
		||||
  ws_redis_subscribe.cpp
 | 
			
		||||
  ws.cpp)
 | 
			
		||||
 | 
			
		||||
if (APPLE AND USE_TLS)
 | 
			
		||||
    target_link_libraries(ws "-framework foundation" "-framework security")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
target_link_libraries(ws ixwebsocket)
 | 
			
		||||
target_link_libraries(ws ixwebsocket cpp_redis tacopie)
 | 
			
		||||
install(TARGETS ws RUNTIME DESTINATION bin)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										242
									
								
								ws/IXRedisClient.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,242 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXRedisClient.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXRedisClient.h"
 | 
			
		||||
#include <ixwebsocket/IXSocketFactory.h>
 | 
			
		||||
#include <ixwebsocket/IXSocket.h>
 | 
			
		||||
#include <cpp_redis/cpp_redis>
 | 
			
		||||
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    bool RedisClient::connect(const std::string& hostname, int port)
 | 
			
		||||
    {
 | 
			
		||||
         _sub.connect(hostname, port, []
 | 
			
		||||
                 (const std::string& host, std::size_t port, cpp_redis::connect_state status) {
 | 
			
		||||
            if (status == cpp_redis::connect_state::dropped) {
 | 
			
		||||
              std::cout << "client disconnected from " << host << ":" << port << std::endl;
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
         // also subscribe the old way
 | 
			
		||||
        bool tls = false;
 | 
			
		||||
        std::string errorMsg;
 | 
			
		||||
        _socket = createSocket(tls, errorMsg);
 | 
			
		||||
 | 
			
		||||
        if (!_socket)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::string errMsg;
 | 
			
		||||
        return _socket->connect(hostname, port, errMsg, nullptr);
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool RedisClient::auth(const std::string& password,
 | 
			
		||||
                           std::string& response)
 | 
			
		||||
    {
 | 
			
		||||
        // authentication if server-server requires it
 | 
			
		||||
        //  _sub.auth(password, [&response](const cpp_redis::reply& reply) {
 | 
			
		||||
        //    if (reply.is_error()) { std::cerr << "Authentication failed: " << reply.as_string() << std::endl; }
 | 
			
		||||
        //    else {
 | 
			
		||||
        //      std::cout << "successful authentication" << std::endl;
 | 
			
		||||
        //    }
 | 
			
		||||
        //  });
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
#if 0
 | 
			
		||||
        response.clear();
 | 
			
		||||
 | 
			
		||||
        if (!_socket) return false;
 | 
			
		||||
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << "AUTH ";
 | 
			
		||||
        ss << password;
 | 
			
		||||
        ss << "\r\n";
 | 
			
		||||
 | 
			
		||||
        bool sent = _socket->writeBytes(ss.str(), nullptr);
 | 
			
		||||
        if (!sent)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto pollResult = _socket->isReadyToRead(-1);
 | 
			
		||||
        if (pollResult == PollResultType::Error)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto lineResult = _socket->readLine(nullptr);
 | 
			
		||||
        auto lineValid = lineResult.first;
 | 
			
		||||
        auto line = lineResult.second;
 | 
			
		||||
 | 
			
		||||
        response = line;
 | 
			
		||||
        return lineValid;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    bool RedisClient::publish(const std::string& channel,
 | 
			
		||||
                              const std::string& message)
 | 
			
		||||
    {
 | 
			
		||||
        if (!_socket) return false;
 | 
			
		||||
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << "PUBLISH ";
 | 
			
		||||
        ss << channel;
 | 
			
		||||
        ss << " ";
 | 
			
		||||
        ss << message;
 | 
			
		||||
        ss << "\r\n";
 | 
			
		||||
 | 
			
		||||
        bool sent = _socket->writeBytes(ss.str(), nullptr);
 | 
			
		||||
        if (!sent)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto pollResult = _socket->isReadyToRead(-1);
 | 
			
		||||
        if (pollResult == PollResultType::Error)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto lineResult = _socket->readLine(nullptr);
 | 
			
		||||
        auto lineValid = lineResult.first;
 | 
			
		||||
        auto line = lineResult.second;
 | 
			
		||||
 | 
			
		||||
        return lineValid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // FIXME: we assume that redis never return errors...
 | 
			
		||||
    //
 | 
			
		||||
    bool RedisClient::subscribe(const std::string& channel,
 | 
			
		||||
                                const OnRedisSubscribeResponseCallback& responseCallback,
 | 
			
		||||
                                const OnRedisSubscribeCallback& callback)
 | 
			
		||||
    {
 | 
			
		||||
        _sub.subscribe(channel, [&callback](const std::string& chan, const std::string& msg) {
 | 
			
		||||
            callback(msg);
 | 
			
		||||
        });
 | 
			
		||||
        _sub.commit();
 | 
			
		||||
 | 
			
		||||
        while (true)
 | 
			
		||||
        {
 | 
			
		||||
            auto duration = std::chrono::seconds(1);
 | 
			
		||||
            std::this_thread::sleep_for(duration);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
#if 0
 | 
			
		||||
        if (!_socket) return false;
 | 
			
		||||
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << "SUBSCRIBE ";
 | 
			
		||||
        ss << channel;
 | 
			
		||||
        ss << "\r\n";
 | 
			
		||||
 | 
			
		||||
        bool sent = _socket->writeBytes(ss.str(), nullptr);
 | 
			
		||||
        if (!sent)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Wait 1s for the response
 | 
			
		||||
        auto pollResult = _socket->isReadyToRead(-1);
 | 
			
		||||
        if (pollResult == PollResultType::Error)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // build the response as a single string
 | 
			
		||||
        std::stringstream oss;
 | 
			
		||||
 | 
			
		||||
        // Read the first line of the response
 | 
			
		||||
        auto lineResult = _socket->readLine(nullptr);
 | 
			
		||||
        auto lineValid = lineResult.first;
 | 
			
		||||
        auto line = lineResult.second;
 | 
			
		||||
        oss << line;
 | 
			
		||||
 | 
			
		||||
        if (!lineValid) return false;
 | 
			
		||||
 | 
			
		||||
        // There are 5 items for the subscribe repply
 | 
			
		||||
        for (int i = 0; i < 5; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto lineResult = _socket->readLine(nullptr);
 | 
			
		||||
            auto lineValid = lineResult.first;
 | 
			
		||||
            auto line = lineResult.second;
 | 
			
		||||
            oss << line;
 | 
			
		||||
 | 
			
		||||
            if (!lineValid) return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        responseCallback(oss.str());
 | 
			
		||||
 | 
			
		||||
        // Wait indefinitely for new messages
 | 
			
		||||
        while (true)
 | 
			
		||||
        {
 | 
			
		||||
            // Wait until something is ready to read
 | 
			
		||||
            auto pollResult = _socket->isReadyToRead(-1);
 | 
			
		||||
            if (pollResult == PollResultType::Error)
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // The first line of the response describe the return type,
 | 
			
		||||
            // => *3 (an array of 3 elements)
 | 
			
		||||
            auto lineResult = _socket->readLine(nullptr);
 | 
			
		||||
            auto lineValid = lineResult.first;
 | 
			
		||||
            auto line = lineResult.second;
 | 
			
		||||
 | 
			
		||||
            if (!lineValid) return false;
 | 
			
		||||
 | 
			
		||||
            int arraySize;
 | 
			
		||||
            {
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                ss << line.substr(1, line.size()-1);
 | 
			
		||||
                ss >> arraySize;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // There are 6 items for each received message
 | 
			
		||||
            for (int i = 0; i < arraySize; ++i)
 | 
			
		||||
            {
 | 
			
		||||
                auto lineResult = _socket->readLine(nullptr);
 | 
			
		||||
                auto lineValid = lineResult.first;
 | 
			
		||||
                auto line = lineResult.second;
 | 
			
		||||
 | 
			
		||||
                if (!lineValid) return false;
 | 
			
		||||
 | 
			
		||||
                // Messages are string, which start with a string size
 | 
			
		||||
                // => $7 (7 bytes)
 | 
			
		||||
                int stringSize;
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                ss << line.substr(1, line.size()-1);
 | 
			
		||||
                ss >> stringSize;
 | 
			
		||||
 | 
			
		||||
                auto readResult = _socket->readBytes(stringSize, nullptr, nullptr);
 | 
			
		||||
                if (!readResult.first) return false;
 | 
			
		||||
 | 
			
		||||
                if (i == 2)
 | 
			
		||||
                {
 | 
			
		||||
                    // The message is the 3rd element.
 | 
			
		||||
                    callback(readResult.second);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // read last 2 bytes (\r\n)
 | 
			
		||||
                char c;
 | 
			
		||||
                _socket->readByte(&c, nullptr);
 | 
			
		||||
                _socket->readByte(&c, nullptr);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										44
									
								
								ws/IXRedisClient.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,44 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXRedisClient.h
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <cpp_redis/cpp_redis>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class Socket;
 | 
			
		||||
 | 
			
		||||
    class RedisClient {
 | 
			
		||||
    public:
 | 
			
		||||
        using OnRedisSubscribeResponseCallback = std::function<void(const std::string&)>;
 | 
			
		||||
        using OnRedisSubscribeCallback = std::function<void(const std::string&)>;
 | 
			
		||||
 | 
			
		||||
        RedisClient() = default;
 | 
			
		||||
        ~RedisClient() = default;
 | 
			
		||||
 | 
			
		||||
        bool connect(const std::string& hostname,
 | 
			
		||||
                     int port);
 | 
			
		||||
 | 
			
		||||
        bool auth(const std::string& password,
 | 
			
		||||
                  std::string& response);
 | 
			
		||||
 | 
			
		||||
        bool publish(const std::string& channel,
 | 
			
		||||
                     const std::string& message);
 | 
			
		||||
 | 
			
		||||
        bool subscribe(const std::string& channel,
 | 
			
		||||
                       const OnRedisSubscribeResponseCallback& responseCallback,
 | 
			
		||||
                       const OnRedisSubscribeCallback& callback);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        cpp_redis::subscriber _sub;
 | 
			
		||||
 | 
			
		||||
        std::shared_ptr<Socket> _socket;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -20,6 +20,8 @@ Subcommands:
 | 
			
		||||
  broadcast_server            Broadcasting server
 | 
			
		||||
  ping                        Ping pong
 | 
			
		||||
  curl                        HTTP Client
 | 
			
		||||
  redis_publish               Redis publisher
 | 
			
		||||
  redis_subscribe             Redis subscriber
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## file transfer
 | 
			
		||||
@@ -29,7 +31,7 @@ Subcommands:
 | 
			
		||||
ws transfer # running on port 8080.
 | 
			
		||||
 | 
			
		||||
# Start receiver first
 | 
			
		||||
ws receive ws://localhost:8080 
 | 
			
		||||
ws receive ws://localhost:8080
 | 
			
		||||
 | 
			
		||||
# Then send a file. File will be received and written to disk by the receiver process
 | 
			
		||||
ws send ws://localhost:8080 /file/to/path
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								ws/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,19 @@
 | 
			
		||||
{
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "lockfileVersion": 1,
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "async-limiter": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
 | 
			
		||||
    },
 | 
			
		||||
    "ws": {
 | 
			
		||||
      "version": "6.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.0.tgz",
 | 
			
		||||
      "integrity": "sha512-deZYUNlt2O4buFCa3t5bKLf8A7FPP/TVjwOeVNpw818Ma5nk4MLXls2eoEGS39o8119QIYxTrTDoPQ5B/gTD6w==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "async-limiter": "1.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										64
									
								
								ws/test_ws.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,64 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
# Handle Ctrl-C by killing all sub-processing AND exiting
 | 
			
		||||
trap cleanup INT
 | 
			
		||||
 | 
			
		||||
function cleanup {
 | 
			
		||||
    kill `cat /tmp/ws_test/pidfile.transfer`
 | 
			
		||||
    kill `cat /tmp/ws_test/pidfile.receive`
 | 
			
		||||
    kill `cat /tmp/ws_test/pidfile.send`
 | 
			
		||||
    exit 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
rm -rf /tmp/ws_test
 | 
			
		||||
mkdir -p /tmp/ws_test
 | 
			
		||||
 | 
			
		||||
# Start a transport server
 | 
			
		||||
cd /tmp/ws_test
 | 
			
		||||
ws transfer --port 8090 --pidfile /tmp/ws_test/pidfile.transfer &
 | 
			
		||||
 | 
			
		||||
# Wait until the transfer server is up 
 | 
			
		||||
while true
 | 
			
		||||
do
 | 
			
		||||
    nc -zv 127.0.0.1 8090 && {
 | 
			
		||||
        echo "Transfer server up and running"
 | 
			
		||||
        break
 | 
			
		||||
    }
 | 
			
		||||
    echo "sleep ... wait for transfer server"
 | 
			
		||||
    sleep 0.1
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# Start a receiver
 | 
			
		||||
mkdir -p /tmp/ws_test/receive
 | 
			
		||||
cd /tmp/ws_test/receive
 | 
			
		||||
ws receive --delay 10 ws://127.0.0.1:8090 --pidfile /tmp/ws_test/pidfile.receive &
 | 
			
		||||
 | 
			
		||||
mkdir /tmp/ws_test/send
 | 
			
		||||
cd /tmp/ws_test/send
 | 
			
		||||
dd if=/dev/urandom of=20M_file count=20000 bs=1024
 | 
			
		||||
 | 
			
		||||
# Start the sender job
 | 
			
		||||
ws send --pidfile /tmp/ws_test/pidfile.send ws://127.0.0.1:8090 20M_file
 | 
			
		||||
 | 
			
		||||
# Wait until the file has been written to disk
 | 
			
		||||
while true
 | 
			
		||||
do
 | 
			
		||||
    if test -f /tmp/ws_test/receive/20M_file ; then
 | 
			
		||||
        echo "Received file does exists, exiting loop"
 | 
			
		||||
        break
 | 
			
		||||
    fi
 | 
			
		||||
    echo "sleep ... wait for output file"
 | 
			
		||||
    sleep 0.1
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
cksum /tmp/ws_test/send/20M_file
 | 
			
		||||
cksum /tmp/ws_test/receive/20M_file
 | 
			
		||||
 | 
			
		||||
# Give some time to ws receive to terminate
 | 
			
		||||
sleep 2
 | 
			
		||||
 | 
			
		||||
# Cleanup
 | 
			
		||||
kill `cat /tmp/ws_test/pidfile.transfer`
 | 
			
		||||
kill `cat /tmp/ws_test/pidfile.receive`
 | 
			
		||||
kill `cat /tmp/ws_test/pidfile.send`
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										25
									
								
								ws/test_ws_redis.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,25 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
# Handle Ctrl-C by killing all sub-processing AND exiting
 | 
			
		||||
trap cleanup INT
 | 
			
		||||
 | 
			
		||||
function cleanup {
 | 
			
		||||
    kill `cat /tmp/pidfile.subscribe`
 | 
			
		||||
    exit 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
REDIS_HOST=${REDIS_HOST:=localhost}
 | 
			
		||||
 | 
			
		||||
ws redis_subscribe --pidfile /tmp/pidfile.subscribe --host $REDIS_HOST foo &
 | 
			
		||||
 | 
			
		||||
# Wait for the subscriber to be ready
 | 
			
		||||
sleep 0.5
 | 
			
		||||
 | 
			
		||||
# Now publish messages
 | 
			
		||||
ws redis_publish -c 100000 --host ${REDIS_HOST} foo bar
 | 
			
		||||
 | 
			
		||||
# Wait a little for all messages to be received
 | 
			
		||||
sleep 1.5
 | 
			
		||||
 | 
			
		||||
# Cleanup
 | 
			
		||||
cleanup
 | 
			
		||||
							
								
								
									
										65
									
								
								ws/ws.cpp
									
									
									
									
									
								
							
							
						
						@@ -16,6 +16,8 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include <cli11/CLI11.hpp>
 | 
			
		||||
#include <ixwebsocket/IXSocket.h>
 | 
			
		||||
@@ -32,25 +34,39 @@ int main(int argc, char** argv)
 | 
			
		||||
    std::string headers;
 | 
			
		||||
    std::string output;
 | 
			
		||||
    std::string hostname("127.0.0.1");
 | 
			
		||||
    std::string pidfile;
 | 
			
		||||
    std::string channel;
 | 
			
		||||
    std::string message;
 | 
			
		||||
    std::string password;
 | 
			
		||||
    bool headersOnly = false;
 | 
			
		||||
    bool followRedirects = false;
 | 
			
		||||
    bool verbose = false;
 | 
			
		||||
    bool save = false;
 | 
			
		||||
    bool compress = false;
 | 
			
		||||
    int port = 8080;
 | 
			
		||||
    int redisPort = 6379;
 | 
			
		||||
    int connectTimeOut = 60;
 | 
			
		||||
    int transferTimeout = 1800;
 | 
			
		||||
    int maxRedirects = 5;
 | 
			
		||||
    int delayMs = -1;
 | 
			
		||||
    int count = 1;
 | 
			
		||||
 | 
			
		||||
    CLI::App* sendApp = app.add_subcommand("send", "Send a file");
 | 
			
		||||
    sendApp->add_option("url", url, "Connection url")->required();
 | 
			
		||||
    sendApp->add_option("path", path, "Path to the file to send")
 | 
			
		||||
        ->required()->check(CLI::ExistingPath);
 | 
			
		||||
    sendApp->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
 | 
			
		||||
    CLI::App* receiveApp = app.add_subcommand("receive", "Receive a file");
 | 
			
		||||
    receiveApp->add_option("url", url, "Connection url")->required();
 | 
			
		||||
    receiveApp->add_option("--delay", delayMs, "Delay (ms) to wait after receiving a fragment"
 | 
			
		||||
                                               " to artificially slow down the receiver");
 | 
			
		||||
    receiveApp->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
 | 
			
		||||
    CLI::App* transferApp = app.add_subcommand("transfer", "Broadcasting server");
 | 
			
		||||
    transferApp->add_option("--port", port, "Connection url");
 | 
			
		||||
    transferApp->add_option("--host", hostname, "Hostname");
 | 
			
		||||
    transferApp->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
 | 
			
		||||
    CLI::App* connectApp = app.add_subcommand("connect", "Connect to a remote server");
 | 
			
		||||
    connectApp->add_option("url", url, "Connection url")->required();
 | 
			
		||||
@@ -60,11 +76,12 @@ int main(int argc, char** argv)
 | 
			
		||||
    chatApp->add_option("user", user, "User name")->required();
 | 
			
		||||
 | 
			
		||||
    CLI::App* echoServerApp = app.add_subcommand("echo_server", "Echo server");
 | 
			
		||||
    echoServerApp->add_option("--port", port, "Connection url");
 | 
			
		||||
    echoServerApp->add_option("--port", port, "Port");
 | 
			
		||||
    echoServerApp->add_option("--host", hostname, "Hostname");
 | 
			
		||||
 | 
			
		||||
    CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server");
 | 
			
		||||
    broadcastServerApp->add_option("--port", port, "Connection url");
 | 
			
		||||
    broadcastServerApp->add_option("--hostname", hostname, "Hostname");
 | 
			
		||||
    broadcastServerApp->add_option("--port", port, "Port");
 | 
			
		||||
    broadcastServerApp->add_option("--host", hostname, "Hostname");
 | 
			
		||||
 | 
			
		||||
    CLI::App* pingPongApp = app.add_subcommand("ping", "Ping pong");
 | 
			
		||||
    pingPongApp->add_option("url", url, "Connection url")->required();
 | 
			
		||||
@@ -84,13 +101,38 @@ int main(int argc, char** argv)
 | 
			
		||||
    httpClientApp->add_option("--connect-timeout", connectTimeOut, "Connection timeout");
 | 
			
		||||
    httpClientApp->add_option("--transfer-timeout", transferTimeout, "Transfer timeout");
 | 
			
		||||
 | 
			
		||||
    CLI::App* redisPublishApp = app.add_subcommand("redis_publish", "Redis publisher");
 | 
			
		||||
    redisPublishApp->add_option("--port", redisPort, "Port");
 | 
			
		||||
    redisPublishApp->add_option("--host", hostname, "Hostname");
 | 
			
		||||
    redisPublishApp->add_option("--password", password, "Password");
 | 
			
		||||
    redisPublishApp->add_option("channel", channel, "Channel")->required();
 | 
			
		||||
    redisPublishApp->add_option("message", message, "Message")->required();
 | 
			
		||||
    redisPublishApp->add_option("-c", count, "Count");
 | 
			
		||||
 | 
			
		||||
    CLI::App* redisSubscribeApp = app.add_subcommand("redis_subscribe", "Redis subscriber");
 | 
			
		||||
    redisSubscribeApp->add_option("--port", redisPort, "Port");
 | 
			
		||||
    redisSubscribeApp->add_option("--host", hostname, "Hostname");
 | 
			
		||||
    redisSubscribeApp->add_option("--password", password, "Password");
 | 
			
		||||
    redisSubscribeApp->add_option("channel", channel, "Channel")->required();
 | 
			
		||||
    redisSubscribeApp->add_flag("-v", verbose, "Verbose");
 | 
			
		||||
    redisSubscribeApp->add_option("--pidfile", pidfile, "Pid file");
 | 
			
		||||
 | 
			
		||||
    CLI11_PARSE(app, argc, argv);
 | 
			
		||||
 | 
			
		||||
    ix::Socket::init();
 | 
			
		||||
    // pid file handling
 | 
			
		||||
    if (!pidfile.empty())
 | 
			
		||||
    {
 | 
			
		||||
        unlink(pidfile.c_str());
 | 
			
		||||
 | 
			
		||||
        std::ofstream f;
 | 
			
		||||
        f.open(pidfile);
 | 
			
		||||
        f << getpid();
 | 
			
		||||
        f.close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (app.got_subcommand("transfer"))
 | 
			
		||||
    {
 | 
			
		||||
        return ix::ws_transfer_main(port);
 | 
			
		||||
        return ix::ws_transfer_main(port, hostname);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("send"))
 | 
			
		||||
    {
 | 
			
		||||
@@ -99,7 +141,7 @@ int main(int argc, char** argv)
 | 
			
		||||
    else if (app.got_subcommand("receive"))
 | 
			
		||||
    {
 | 
			
		||||
        bool enablePerMessageDeflate = false;
 | 
			
		||||
        return ix::ws_receive_main(url, enablePerMessageDeflate);
 | 
			
		||||
        return ix::ws_receive_main(url, enablePerMessageDeflate, delayMs);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("connect"))
 | 
			
		||||
    {
 | 
			
		||||
@@ -111,7 +153,7 @@ int main(int argc, char** argv)
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("echo_server"))
 | 
			
		||||
    {
 | 
			
		||||
        return ix::ws_echo_server_main(port);
 | 
			
		||||
        return ix::ws_echo_server_main(port, hostname);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("broadcast_server"))
 | 
			
		||||
    {
 | 
			
		||||
@@ -128,6 +170,15 @@ int main(int argc, char** argv)
 | 
			
		||||
                                       followRedirects, maxRedirects, verbose,
 | 
			
		||||
                                       save, output, compress);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("redis_publish"))
 | 
			
		||||
    {
 | 
			
		||||
        return ix::ws_redis_publish_main(hostname, redisPort, password,
 | 
			
		||||
                                         channel, message, count);
 | 
			
		||||
    }
 | 
			
		||||
    else if (app.got_subcommand("redis_subscribe"))
 | 
			
		||||
    {
 | 
			
		||||
        return ix::ws_redis_subscribe_main(hostname, redisPort, password, channel, verbose);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								ws/ws.h
									
									
									
									
									
								
							
							
						
						@@ -24,9 +24,9 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
    int ws_ping_pong_main(const std::string& url);
 | 
			
		||||
 | 
			
		||||
    int ws_echo_server_main(int port);
 | 
			
		||||
 | 
			
		||||
    int ws_echo_server_main(int port, const std::string& hostname);
 | 
			
		||||
    int ws_broadcast_server_main(int port, const std::string& hostname);
 | 
			
		||||
    int ws_transfer_main(int port, const std::string& hostname);
 | 
			
		||||
 | 
			
		||||
    int ws_chat_main(const std::string& url,
 | 
			
		||||
                     const std::string& user);
 | 
			
		||||
@@ -34,10 +34,22 @@ namespace ix
 | 
			
		||||
    int ws_connect_main(const std::string& url);
 | 
			
		||||
 | 
			
		||||
    int ws_receive_main(const std::string& url,
 | 
			
		||||
                        bool enablePerMessageDeflate);
 | 
			
		||||
 | 
			
		||||
    int ws_transfer_main(int port);
 | 
			
		||||
                        bool enablePerMessageDeflate,
 | 
			
		||||
                        int delayMs);
 | 
			
		||||
 | 
			
		||||
    int ws_send_main(const std::string& url,
 | 
			
		||||
                     const std::string& path);
 | 
			
		||||
 | 
			
		||||
    int ws_redis_publish_main(const std::string& hostname,
 | 
			
		||||
                              int port,
 | 
			
		||||
                              const std::string& password,
 | 
			
		||||
                              const std::string& channel,
 | 
			
		||||
                              const std::string& message,
 | 
			
		||||
                              int count);
 | 
			
		||||
 | 
			
		||||
    int ws_redis_subscribe_main(const std::string& hostname,
 | 
			
		||||
                                int port,
 | 
			
		||||
                                const std::string& password,
 | 
			
		||||
                                const std::string& channel,
 | 
			
		||||
                                bool verbose);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,10 +17,11 @@ namespace ix
 | 
			
		||||
        ix::WebSocketServer server(port, hostname);
 | 
			
		||||
 | 
			
		||||
        server.setOnConnectionCallback(
 | 
			
		||||
            [&server](std::shared_ptr<ix::WebSocket> webSocket)
 | 
			
		||||
            [&server](std::shared_ptr<WebSocket> webSocket,
 | 
			
		||||
                      std::shared_ptr<ConnectionState> connectionState)
 | 
			
		||||
            {
 | 
			
		||||
                webSocket->setOnMessageCallback(
 | 
			
		||||
                    [webSocket, &server](ix::WebSocketMessageType messageType,
 | 
			
		||||
                    [webSocket, connectionState, &server](ix::WebSocketMessageType messageType,
 | 
			
		||||
                       const std::string& str,
 | 
			
		||||
                       size_t wireSize,
 | 
			
		||||
                       const ix::WebSocketErrorInfo& error,
 | 
			
		||||
@@ -30,6 +31,7 @@ namespace ix
 | 
			
		||||
                        if (messageType == ix::WebSocket_MessageType_Open)
 | 
			
		||||
                        {
 | 
			
		||||
                            std::cerr << "New connection" << std::endl;
 | 
			
		||||
                            std::cerr << "id: " << connectionState->getId() << std::endl;
 | 
			
		||||
                            std::cerr << "Uri: " << openInfo.uri << std::endl;
 | 
			
		||||
                            std::cerr << "Headers:" << std::endl;
 | 
			
		||||
                            for (auto it : openInfo.headers)
 | 
			
		||||
@@ -71,6 +73,15 @@ namespace ix
 | 
			
		||||
                                                  << " out of " << total << std::endl;
 | 
			
		||||
                                        return true;
 | 
			
		||||
                                    });
 | 
			
		||||
 | 
			
		||||
                                    do
 | 
			
		||||
                                    {
 | 
			
		||||
                                        size_t bufferedAmount = client->bufferedAmount();
 | 
			
		||||
                                        std::cerr << bufferedAmount << " bytes left to be sent" << std::endl;
 | 
			
		||||
 | 
			
		||||
                                        std::chrono::duration<double, std::milli> duration(10);
 | 
			
		||||
                                        std::this_thread::sleep_for(duration);
 | 
			
		||||
                                    } while (client->bufferedAmount() != 0);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 
 | 
			
		||||