Compare commits
6 Commits
v1.0.8
...
user/bserg
Author | SHA1 | Date | |
---|---|---|---|
|
4373a92c61 | ||
|
91e67f6e53 | ||
|
1ca1f612be | ||
|
1b9e55d3f8 | ||
|
0d80971328 | ||
|
80c1ed0611 |
@@ -1 +0,0 @@
|
|||||||
build
|
|
14
.travis.yml
14
.travis.yml
@@ -2,16 +2,10 @@ language: cpp
|
|||||||
dist: xenial
|
dist: xenial
|
||||||
|
|
||||||
compiler:
|
compiler:
|
||||||
- gcc
|
|
||||||
- clang
|
- clang
|
||||||
os:
|
# - gcc
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
exclude:
|
|
||||||
# GCC fails on recent Travis OSX images.
|
|
||||||
- compiler: gcc
|
|
||||||
os: osx
|
|
||||||
|
|
||||||
|
os: osx
|
||||||
|
# os: windows
|
||||||
|
# script: make test
|
||||||
script: python test/run.py
|
script: python test/run.py
|
||||||
|
@@ -10,7 +10,6 @@ set (CMAKE_CXX_STANDARD 14)
|
|||||||
set (CXX_STANDARD_REQUIRED ON)
|
set (CXX_STANDARD_REQUIRED ON)
|
||||||
set (CMAKE_CXX_EXTENSIONS OFF)
|
set (CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
# -Wshorten-64-to-32 does not work with clang
|
|
||||||
if (NOT WIN32)
|
if (NOT WIN32)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
|
||||||
endif()
|
endif()
|
||||||
@@ -39,7 +38,6 @@ set( IXWEBSOCKET_HEADERS
|
|||||||
ixwebsocket/IXSetThreadName.h
|
ixwebsocket/IXSetThreadName.h
|
||||||
ixwebsocket/IXDNSLookup.h
|
ixwebsocket/IXDNSLookup.h
|
||||||
ixwebsocket/IXCancellationRequest.h
|
ixwebsocket/IXCancellationRequest.h
|
||||||
ixwebsocket/IXProgressCallback.h
|
|
||||||
ixwebsocket/IXWebSocket.h
|
ixwebsocket/IXWebSocket.h
|
||||||
ixwebsocket/IXWebSocketServer.h
|
ixwebsocket/IXWebSocketServer.h
|
||||||
ixwebsocket/IXWebSocketTransport.h
|
ixwebsocket/IXWebSocketTransport.h
|
||||||
@@ -114,5 +112,3 @@ set( IXWEBSOCKET_INCLUDE_DIRS
|
|||||||
.
|
.
|
||||||
../../shared/OpenSSL/include)
|
../../shared/OpenSSL/include)
|
||||||
target_include_directories( ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS} )
|
target_include_directories( ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS} )
|
||||||
|
|
||||||
add_subdirectory(ws)
|
|
||||||
|
17
README.md
17
README.md
@@ -15,7 +15,7 @@ communication channels over a single TCP connection. *IXWebSocket* is a C++ libr
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
The ws folder countains many interactive programs for chat and file transfers demonstrating client and server usage.
|
The examples folder countains a simple chat program, using a node.js broadcast server.
|
||||||
|
|
||||||
Here is what the client API looks like.
|
Here is what the client API looks like.
|
||||||
|
|
||||||
@@ -134,16 +134,22 @@ No manual polling to fetch data is required. Data is sent and received instantly
|
|||||||
|
|
||||||
If the remote end (server) breaks the connection, the code will try to perpetually reconnect, by using an exponential backoff strategy, capped at one retry every 10 seconds.
|
If the remote end (server) breaks the connection, the code will try to perpetually reconnect, by using an exponential backoff strategy, capped at one retry every 10 seconds.
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
## Limitations
|
## 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.
|
* There is no text support for sending data, only the binary protocol is supported. Sending json or text over the binary protocol works well.
|
||||||
* 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.
|
* 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 isn't as scalable as strategies using epoll or kqueue.
|
* The server code is using select to detect incoming data, and creates one OS thread per connection. This isn't as scalable as strategies using epoll or kqueue.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
1. Bring up a terminal and jump to the examples folder.
|
||||||
|
2. Compile the example C++ code. `sh build.sh`
|
||||||
|
3. Install node.js from [here](https://nodejs.org/en/download/).
|
||||||
|
4. Type `npm install` to install the node.js dependencies. Then `node broadcast-server.js` to run the server.
|
||||||
|
5. Bring up a second terminal. `./cmd_websocket_chat bob`
|
||||||
|
6. Bring up a third terminal. `./cmd_websocket_chat bill`
|
||||||
|
7. Start typing things in any of those terminals. Hopefully you should see your message being received on the other end.
|
||||||
|
|
||||||
## C++ code organization
|
## C++ code organization
|
||||||
|
|
||||||
Here's a simplistic diagram which explains how the code is structured in term of class/modules.
|
Here's a simplistic diagram which explains how the code is structured in term of class/modules.
|
||||||
@@ -303,7 +309,6 @@ A ping message can be sent to the server, with an optional data string.
|
|||||||
|
|
||||||
```
|
```
|
||||||
websocket.ping("ping data, optional (empty string is ok): limited to 125 bytes long");
|
websocket.ping("ping data, optional (empty string is ok): limited to 125 bytes long");
|
||||||
```
|
|
||||||
|
|
||||||
### Heartbeat.
|
### Heartbeat.
|
||||||
|
|
||||||
|
@@ -15,8 +15,5 @@ RUN apt-get -y install cmake
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
WORKDIR ws
|
WORKDIR test
|
||||||
RUN ["sh", "docker_build.sh"]
|
RUN ["sh", "build_linux.sh"]
|
||||||
|
|
||||||
EXPOSE 8765
|
|
||||||
CMD ["/ws/ws", "transfer", "8765"]
|
|
||||||
|
9
examples/broadcast_server/.gitignore
vendored
Normal file
9
examples/broadcast_server/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
CMakeCache.txt
|
||||||
|
package-lock.json
|
||||||
|
CMakeFiles
|
||||||
|
ixwebsocket_unittest
|
||||||
|
cmake_install.cmake
|
||||||
|
node_modules
|
||||||
|
ixwebsocket
|
||||||
|
Makefile
|
||||||
|
build
|
30
examples/broadcast_server/CMakeLists.txt
Normal file
30
examples/broadcast_server/CMakeLists.txt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#
|
||||||
|
# Author: Benjamin Sergeant
|
||||||
|
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
cmake_minimum_required (VERSION 3.4.1)
|
||||||
|
project (broadcast_server)
|
||||||
|
|
||||||
|
# There's -Weverything too for clang
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshorten-64-to-32")
|
||||||
|
|
||||||
|
set (OPENSSL_PREFIX /usr/local/opt/openssl) # Homebrew openssl
|
||||||
|
|
||||||
|
set (CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
|
option(USE_TLS "Add TLS support" ON)
|
||||||
|
|
||||||
|
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
|
||||||
|
|
||||||
|
include_directories(broadcast_server .)
|
||||||
|
|
||||||
|
add_executable(broadcast_server
|
||||||
|
broadcast_server.cpp)
|
||||||
|
|
||||||
|
if (APPLE AND USE_TLS)
|
||||||
|
target_link_libraries(broadcast_server "-framework foundation" "-framework security")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(broadcast_server ixwebsocket)
|
||||||
|
install(TARGETS broadcast_server DESTINATION bin)
|
74
examples/broadcast_server/broadcast_server.cpp
Normal file
74
examples/broadcast_server/broadcast_server.cpp
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* broadcast_server.cpp
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <ixwebsocket/IXWebSocketServer.h>
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
for (auto&& client : server.getClients())
|
||||||
|
{
|
||||||
|
if (client != webSocket)
|
||||||
|
{
|
||||||
|
client->send(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
auto res = server.listen();
|
||||||
|
if (!res.first)
|
||||||
|
{
|
||||||
|
std::cerr << res.second << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
server.wait();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
3
examples/chat/.gitignore
vendored
Normal file
3
examples/chat/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
build
|
||||||
|
venv
|
||||||
|
node_modules
|
23
examples/chat/CMakeLists.txt
Normal file
23
examples/chat/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#
|
||||||
|
# cmd_websocket_chat.cpp
|
||||||
|
# Author: Benjamin Sergeant
|
||||||
|
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
cmake_minimum_required (VERSION 3.4.1)
|
||||||
|
project (cmd_websocket_chat)
|
||||||
|
|
||||||
|
set (CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
|
option(USE_TLS "Add TLS support" ON)
|
||||||
|
|
||||||
|
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
|
||||||
|
|
||||||
|
add_executable(cmd_websocket_chat cmd_websocket_chat.cpp)
|
||||||
|
|
||||||
|
if (APPLE AND USE_TLS)
|
||||||
|
target_link_libraries(cmd_websocket_chat "-framework foundation" "-framework security")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(cmd_websocket_chat ixwebsocket)
|
||||||
|
install(TARGETS cmd_websocket_chat DESTINATION bin)
|
39
examples/chat/README.md
Normal file
39
examples/chat/README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Building
|
||||||
|
|
||||||
|
1. cmake -G .
|
||||||
|
2. make
|
||||||
|
|
||||||
|
## Disable TLS
|
||||||
|
|
||||||
|
chat$ cmake -DUSE_TLS=OFF .
|
||||||
|
-- Configuring done
|
||||||
|
-- Generating done
|
||||||
|
-- Build files have been written to: /Users/bsergeant/src/foss/ixwebsocket/examples/chat
|
||||||
|
chat$ make
|
||||||
|
Scanning dependencies of target ixwebsocket
|
||||||
|
[ 16%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocket.cpp.o
|
||||||
|
[ 33%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocket.cpp.o
|
||||||
|
[ 50%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketTransport.cpp.o
|
||||||
|
[ 66%] Linking CXX static library libixwebsocket.a
|
||||||
|
[ 66%] Built target ixwebsocket
|
||||||
|
[ 83%] Linking CXX executable cmd_websocket_chat
|
||||||
|
[100%] Built target cmd_websocket_chat
|
||||||
|
|
||||||
|
## Enable TLS (default)
|
||||||
|
|
||||||
|
```
|
||||||
|
chat$ cmake -DUSE_TLS=ON .
|
||||||
|
-- Configuring done
|
||||||
|
-- Generating done
|
||||||
|
-- Build files have been written to: /Users/bsergeant/src/foss/ixwebsocket/examples/chat
|
||||||
|
(venv) chat$ make
|
||||||
|
Scanning dependencies of target ixwebsocket
|
||||||
|
[ 14%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocket.cpp.o
|
||||||
|
[ 28%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocket.cpp.o
|
||||||
|
[ 42%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketTransport.cpp.o
|
||||||
|
[ 57%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketAppleSSL.cpp.o
|
||||||
|
[ 71%] Linking CXX static library libixwebsocket.a
|
||||||
|
[ 71%] Built target ixwebsocket
|
||||||
|
[ 85%] Linking CXX executable cmd_websocket_chat
|
||||||
|
[100%] Built target cmd_websocket_chat
|
||||||
|
```
|
15
examples/chat/build_linux.sh
Normal file
15
examples/chat/build_linux.sh
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Author: Benjamin Sergeant
|
||||||
|
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
# 'manual' way of building. You can also use cmake.
|
||||||
|
|
||||||
|
g++ --std=c++11 \
|
||||||
|
../../ixwebsocket/IXSocket.cpp \
|
||||||
|
../../ixwebsocket/IXWebSocketTransport.cpp \
|
||||||
|
../../ixwebsocket/IXWebSocket.cpp \
|
||||||
|
-I ../.. \
|
||||||
|
cmd_websocket_chat.cpp \
|
||||||
|
-o cmd_websocket_chat
|
17
examples/chat/build_macos.sh
Normal file
17
examples/chat/build_macos.sh
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Author: Benjamin Sergeant
|
||||||
|
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
# 'manual' way of building. You can also use cmake.
|
||||||
|
|
||||||
|
clang++ --std=c++11 --stdlib=libc++ \
|
||||||
|
../../ixwebsocket/IXSocket.cpp \
|
||||||
|
../../ixwebsocket/IXWebSocketTransport.cpp \
|
||||||
|
../../ixwebsocket/IXSocketAppleSSL.cpp \
|
||||||
|
../../ixwebsocket/IXWebSocket.cpp \
|
||||||
|
cmd_websocket_chat.cpp \
|
||||||
|
-o cmd_websocket_chat \
|
||||||
|
-framework Security \
|
||||||
|
-framework Foundation
|
@@ -1,13 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
* ws_chat.cpp
|
* cmd_websocket_chat.cpp
|
||||||
* Author: Benjamin Sergeant
|
* Author: Benjamin Sergeant
|
||||||
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
|
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// Simple chat program that talks to the node.js server at
|
// Simple chat program that talks to the node.js server at
|
||||||
// websocket_chat_server/broacast-server.js
|
// websocket_chat_server/broacast-server.js
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
@@ -19,13 +20,19 @@
|
|||||||
// for convenience
|
// for convenience
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
namespace ix
|
using namespace ix;
|
||||||
|
|
||||||
|
namespace
|
||||||
{
|
{
|
||||||
|
void log(const std::string& msg)
|
||||||
|
{
|
||||||
|
std::cout << msg << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
class WebSocketChat
|
class WebSocketChat
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WebSocketChat(const std::string& url,
|
WebSocketChat(const std::string& user);
|
||||||
const std::string& user);
|
|
||||||
|
|
||||||
void subscribe(const std::string& channel);
|
void subscribe(const std::string& channel);
|
||||||
void start();
|
void start();
|
||||||
@@ -39,27 +46,19 @@ namespace ix
|
|||||||
std::pair<std::string, std::string> decodeMessage(const std::string& str);
|
std::pair<std::string, std::string> decodeMessage(const std::string& str);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _url;
|
|
||||||
std::string _user;
|
std::string _user;
|
||||||
ix::WebSocket _webSocket;
|
|
||||||
std::queue<std::string> _receivedQueue;
|
|
||||||
|
|
||||||
void log(const std::string& msg);
|
ix::WebSocket _webSocket;
|
||||||
|
|
||||||
|
std::queue<std::string> _receivedQueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocketChat::WebSocketChat(const std::string& url,
|
WebSocketChat::WebSocketChat(const std::string& user) :
|
||||||
const std::string& user) :
|
|
||||||
_url(url),
|
|
||||||
_user(user)
|
_user(user)
|
||||||
{
|
{
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketChat::log(const std::string& msg)
|
|
||||||
{
|
|
||||||
std::cout << msg << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t WebSocketChat::getReceivedMessagesCount() const
|
size_t WebSocketChat::getReceivedMessagesCount() const
|
||||||
{
|
{
|
||||||
return _receivedQueue.size();
|
return _receivedQueue.size();
|
||||||
@@ -77,10 +76,11 @@ namespace ix
|
|||||||
|
|
||||||
void WebSocketChat::start()
|
void WebSocketChat::start()
|
||||||
{
|
{
|
||||||
_webSocket.setUrl(_url);
|
std::string url("ws://localhost:8080/");
|
||||||
|
_webSocket.setUrl(url);
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
log(std::string("Connecting to url: ") + _url);
|
log(std::string("Connecting to url: ") + url);
|
||||||
|
|
||||||
_webSocket.setOnMessageCallback(
|
_webSocket.setOnMessageCallback(
|
||||||
[this](ix::WebSocketMessageType messageType,
|
[this](ix::WebSocketMessageType messageType,
|
||||||
@@ -115,7 +115,7 @@ namespace ix
|
|||||||
// store text
|
// store text
|
||||||
_receivedQueue.push(result.second);
|
_receivedQueue.push(result.second);
|
||||||
|
|
||||||
ss << std::endl
|
ss << std::endl
|
||||||
<< result.first << " > " << result.second
|
<< result.first << " > " << result.second
|
||||||
<< std::endl
|
<< std::endl
|
||||||
<< _user << " > ";
|
<< _user << " > ";
|
||||||
@@ -164,11 +164,10 @@ namespace ix
|
|||||||
_webSocket.send(encodeMessage(text));
|
_webSocket.send(encodeMessage(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
int ws_chat_main(const std::string& url,
|
void interactiveMain(const std::string& user)
|
||||||
const std::string& user)
|
|
||||||
{
|
{
|
||||||
std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
|
std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
|
||||||
WebSocketChat webSocketChat(url, user);
|
WebSocketChat webSocketChat(user);
|
||||||
webSocketChat.start();
|
webSocketChat.start();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
@@ -189,3 +188,16 @@ namespace ix
|
|||||||
webSocketChat.stop();
|
webSocketChat.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
std::string user("user");
|
||||||
|
if (argc == 2)
|
||||||
|
{
|
||||||
|
user = argv[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket::init();
|
||||||
|
interactiveMain(user);
|
||||||
|
return 0;
|
||||||
|
}
|
31
examples/chat/package-lock.json
generated
Normal file
31
examples/chat/package-lock.json
generated
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"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=="
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
},
|
||||||
|
"ultron": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "3.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
|
||||||
|
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
|
||||||
|
"requires": {
|
||||||
|
"async-limiter": "1.0.0",
|
||||||
|
"safe-buffer": "5.1.2",
|
||||||
|
"ultron": "1.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
examples/chat/package.json
Normal file
6
examples/chat/package.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"msgpack-js": "^0.3.0",
|
||||||
|
"ws": "^3.3.3"
|
||||||
|
}
|
||||||
|
}
|
@@ -15,6 +15,8 @@ set (CMAKE_CXX_STANDARD 14)
|
|||||||
|
|
||||||
option(USE_TLS "Add TLS support" ON)
|
option(USE_TLS "Add TLS support" ON)
|
||||||
|
|
||||||
|
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
|
||||||
|
|
||||||
include_directories(cobra_publisher ${OPENSSL_PREFIX}/include)
|
include_directories(cobra_publisher ${OPENSSL_PREFIX}/include)
|
||||||
include_directories(cobra_publisher .)
|
include_directories(cobra_publisher .)
|
||||||
|
|
@@ -59,8 +59,8 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CobraConnection::invokeEventCallback(ix::CobraConnectionEventType eventType,
|
void CobraConnection::invokeEventCallback(ix::CobraConnectionEventType eventType,
|
||||||
const std::string& errorMsg,
|
const std::string& errorMsg,
|
||||||
const WebSocketHttpHeaders& headers)
|
const WebSocketHttpHeaders& headers)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_eventCallbackMutex);
|
std::lock_guard<std::mutex> lock(_eventCallbackMutex);
|
||||||
if (_eventCallback)
|
if (_eventCallback)
|
||||||
@@ -176,10 +176,10 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CobraConnection::configure(const std::string& appkey,
|
void CobraConnection::configure(const std::string& appkey,
|
||||||
const std::string& endpoint,
|
const std::string& endpoint,
|
||||||
const std::string& rolename,
|
const std::string& rolename,
|
||||||
const std::string& rolesecret,
|
const std::string& rolesecret,
|
||||||
WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions)
|
WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions)
|
||||||
{
|
{
|
||||||
_appkey = appkey;
|
_appkey = appkey;
|
||||||
_endpoint = endpoint;
|
_endpoint = endpoint;
|
||||||
@@ -229,7 +229,7 @@ namespace ix
|
|||||||
return _webSocket.send(serializedJson).success;
|
return _webSocket.send(serializedJson).success;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Extract the nonce from the handshake response
|
// Extract the nonce from the handshake response
|
||||||
// use it to compute a hash during authentication
|
// use it to compute a hash during authentication
|
||||||
//
|
//
|
||||||
@@ -297,7 +297,7 @@ namespace ix
|
|||||||
if (!pdu.isMember("body")) return false;
|
if (!pdu.isMember("body")) return false;
|
||||||
Json::Value body = pdu["body"];
|
Json::Value body = pdu["body"];
|
||||||
|
|
||||||
// Identify subscription_id, so that we can find
|
// Identify subscription_id, so that we can find
|
||||||
// which callback to execute
|
// which callback to execute
|
||||||
if (!body.isMember("subscription_id")) return false;
|
if (!body.isMember("subscription_id")) return false;
|
||||||
Json::Value subscriptionId = body["subscription_id"];
|
Json::Value subscriptionId = body["subscription_id"];
|
||||||
@@ -339,7 +339,7 @@ namespace ix
|
|||||||
// publish is not thread safe as we are trying to reuse some Json objects.
|
// publish is not thread safe as we are trying to reuse some Json objects.
|
||||||
//
|
//
|
||||||
bool CobraConnection::publish(const Json::Value& channels,
|
bool CobraConnection::publish(const Json::Value& channels,
|
||||||
const Json::Value& msg)
|
const Json::Value& msg)
|
||||||
{
|
{
|
||||||
_body["channels"] = channels;
|
_body["channels"] = channels;
|
||||||
_body["message"] = msg;
|
_body["message"] = msg;
|
||||||
@@ -371,7 +371,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CobraConnection::subscribe(const std::string& channel,
|
void CobraConnection::subscribe(const std::string& channel,
|
||||||
SubscriptionCallback cb)
|
SubscriptionCallback cb)
|
||||||
{
|
{
|
||||||
// Create and send a subscribe pdu
|
// Create and send a subscribe pdu
|
||||||
Json::Value body;
|
Json::Value body;
|
||||||
@@ -471,5 +471,5 @@ namespace ix
|
|||||||
{
|
{
|
||||||
connect();
|
connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ix
|
} // namespace ix
|
@@ -84,7 +84,7 @@ namespace ix
|
|||||||
|
|
||||||
/// Returns true only if we're connected
|
/// Returns true only if we're connected
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
|
|
||||||
/// Flush the publish queue
|
/// Flush the publish queue
|
||||||
bool flushQueue();
|
bool flushQueue();
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ namespace ix
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// Member variables
|
/// Member variables
|
||||||
///
|
///
|
||||||
WebSocket _webSocket;
|
WebSocket _webSocket;
|
||||||
|
|
||||||
/// Configuration data
|
/// Configuration data
|
||||||
@@ -148,10 +148,10 @@ namespace ix
|
|||||||
std::unordered_map<std::string, SubscriptionCallback> _cbs;
|
std::unordered_map<std::string, SubscriptionCallback> _cbs;
|
||||||
mutable std::mutex _cbsMutex;
|
mutable std::mutex _cbsMutex;
|
||||||
|
|
||||||
// Message Queue can be touched on control+background thread,
|
// Message Queue can be touched on control+background thread,
|
||||||
// protecting with a mutex.
|
// protecting with a mutex.
|
||||||
//
|
//
|
||||||
// Message queue is used when there are problems sending messages so
|
// Message queue is used when there are problems sending messages so
|
||||||
// that sending can be retried later.
|
// that sending can be retried later.
|
||||||
std::deque<std::string> _messageQueue;
|
std::deque<std::string> _messageQueue;
|
||||||
mutable std::mutex _queueMutex;
|
mutable std::mutex _queueMutex;
|
||||||
@@ -159,5 +159,5 @@ namespace ix
|
|||||||
// Cap the queue size (100 elems so far -> ~100k)
|
// Cap the queue size (100 elems so far -> ~100k)
|
||||||
static constexpr size_t kQueueMaxSize = 256;
|
static constexpr size_t kQueueMaxSize = 256;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ix
|
} // namespace ix
|
@@ -1,39 +1,39 @@
|
|||||||
/*
|
/*
|
||||||
base64.cpp and base64.h
|
base64.cpp and base64.h
|
||||||
|
|
||||||
Copyright (C) 2004-2008 René Nyffenegger
|
Copyright (C) 2004-2008 René Nyffenegger
|
||||||
|
|
||||||
This source code is provided 'as-is', without any express or implied
|
This source code is provided 'as-is', without any express or implied
|
||||||
warranty. In no event will the author be held liable for any damages
|
warranty. In no event will the author be held liable for any damages
|
||||||
arising from the use of this software.
|
arising from the use of this software.
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any purpose,
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
including commercial applications, and to alter it and redistribute it
|
including commercial applications, and to alter it and redistribute it
|
||||||
freely, subject to the following restrictions:
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
1. The origin of this source code must not be misrepresented; you must not
|
1. The origin of this source code must not be misrepresented; you must not
|
||||||
claim that you wrote the original source code. If you use this source code
|
claim that you wrote the original source code. If you use this source code
|
||||||
in a product, an acknowledgment in the product documentation would be
|
in a product, an acknowledgment in the product documentation would be
|
||||||
appreciated but is not required.
|
appreciated but is not required.
|
||||||
|
|
||||||
2. Altered source versions must be plainly marked as such, and must not be
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
misrepresented as being the original source code.
|
misrepresented as being the original source code.
|
||||||
|
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IXBase64.h"
|
#include "IXBase64.h"
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
static const std::string base64_chars =
|
static const std::string base64_chars =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
"0123456789+/";
|
"0123456789+/";
|
||||||
|
|
||||||
std::string base64_encode(const std::string& data, size_t len)
|
std::string base64_encode(const std::string& data, size_t len)
|
||||||
{
|
{
|
||||||
std::string ret;
|
std::string ret;
|
||||||
@@ -41,9 +41,9 @@ namespace ix
|
|||||||
int j = 0;
|
int j = 0;
|
||||||
unsigned char char_array_3[3];
|
unsigned char char_array_3[3];
|
||||||
unsigned char char_array_4[4];
|
unsigned char char_array_4[4];
|
||||||
|
|
||||||
const char* bytes_to_encode = data.c_str();
|
const char* bytes_to_encode = data.c_str();
|
||||||
|
|
||||||
while(len--)
|
while(len--)
|
||||||
{
|
{
|
||||||
char_array_3[i++] = *(bytes_to_encode++);
|
char_array_3[i++] = *(bytes_to_encode++);
|
||||||
@@ -53,83 +53,32 @@ namespace ix
|
|||||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||||
|
|
||||||
for(i = 0; (i <4) ; i++)
|
for(i = 0; (i <4) ; i++)
|
||||||
ret += base64_chars[char_array_4[i]];
|
ret += base64_chars[char_array_4[i]];
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(i)
|
if(i)
|
||||||
{
|
{
|
||||||
for(j = i; j < 3; j++)
|
for(j = i; j < 3; j++)
|
||||||
char_array_3[j] = '\0';
|
char_array_3[j] = '\0';
|
||||||
|
|
||||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||||
|
|
||||||
for(j = 0; (j < i + 1); j++)
|
for(j = 0; (j < i + 1); j++)
|
||||||
ret += base64_chars[char_array_4[j]];
|
ret += base64_chars[char_array_4[j]];
|
||||||
|
|
||||||
while((i++ < 3))
|
while((i++ < 3))
|
||||||
ret += '=';
|
ret += '=';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool is_base64(unsigned char c)
|
|
||||||
{
|
|
||||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string base64_decode(const std::string& encoded_string)
|
|
||||||
{
|
|
||||||
int in_len = (int)encoded_string.size();
|
|
||||||
int i = 0;
|
|
||||||
int j = 0;
|
|
||||||
int in_ = 0;
|
|
||||||
unsigned char char_array_4[4], char_array_3[3];
|
|
||||||
std::string ret;
|
|
||||||
|
|
||||||
while(in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_]))
|
|
||||||
{
|
|
||||||
char_array_4[i++] = encoded_string[in_]; in_++;
|
|
||||||
if(i ==4)
|
|
||||||
{
|
|
||||||
for(i = 0; i <4; i++)
|
|
||||||
char_array_4[i] = base64_chars.find(char_array_4[i]);
|
|
||||||
|
|
||||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
||||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
||||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
||||||
|
|
||||||
for(i = 0; (i < 3); i++)
|
|
||||||
ret += char_array_3[i];
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i)
|
|
||||||
{
|
|
||||||
for(j = i; j <4; j++)
|
|
||||||
char_array_4[j] = 0;
|
|
||||||
|
|
||||||
for(j = 0; j <4; j++)
|
|
||||||
char_array_4[j] = base64_chars.find(char_array_4[j]);
|
|
||||||
|
|
||||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
||||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
||||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
||||||
|
|
||||||
for(j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -11,5 +11,4 @@
|
|||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
std::string base64_encode(const std::string& data, size_t len);
|
std::string base64_encode(const std::string& data, size_t len);
|
||||||
std::string base64_decode(const std::string& encoded_string);
|
|
||||||
}
|
}
|
@@ -7,28 +7,28 @@
|
|||||||
// //////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The JsonCpp library's source code, including accompanying documentation,
|
The JsonCpp library's source code, including accompanying documentation,
|
||||||
tests and demonstration applications, are licensed under the following
|
tests and demonstration applications, are licensed under the following
|
||||||
conditions...
|
conditions...
|
||||||
|
|
||||||
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
||||||
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
||||||
this software is released into the Public Domain.
|
this software is released into the Public Domain.
|
||||||
|
|
||||||
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
|
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
|
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).
|
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
|
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
|
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
|
conditions of the MIT License (see below), or 3) under the terms of dual
|
||||||
Public Domain/MIT License conditions described here, as they choose.
|
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
|
The MIT License is about as close to Public Domain as a license can get, and is
|
||||||
described in clear, concise terms at:
|
described in clear, concise terms at:
|
||||||
|
|
||||||
http://en.wikipedia.org/wiki/MIT_License
|
http://en.wikipedia.org/wiki/MIT_License
|
||||||
|
|
||||||
The full text of the MIT License follows:
|
The full text of the MIT License follows:
|
||||||
|
|
||||||
========================================================================
|
========================================================================
|
@@ -6,28 +6,28 @@
|
|||||||
// //////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The JsonCpp library's source code, including accompanying documentation,
|
The JsonCpp library's source code, including accompanying documentation,
|
||||||
tests and demonstration applications, are licensed under the following
|
tests and demonstration applications, are licensed under the following
|
||||||
conditions...
|
conditions...
|
||||||
|
|
||||||
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
||||||
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
||||||
this software is released into the Public Domain.
|
this software is released into the Public Domain.
|
||||||
|
|
||||||
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
|
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
|
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).
|
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
|
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
|
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
|
conditions of the MIT License (see below), or 3) under the terms of dual
|
||||||
Public Domain/MIT License conditions described here, as they choose.
|
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
|
The MIT License is about as close to Public Domain as a license can get, and is
|
||||||
described in clear, concise terms at:
|
described in clear, concise terms at:
|
||||||
|
|
||||||
http://en.wikipedia.org/wiki/MIT_License
|
http://en.wikipedia.org/wiki/MIT_License
|
||||||
|
|
||||||
The full text of the MIT License follows:
|
The full text of the MIT License follows:
|
||||||
|
|
||||||
========================================================================
|
========================================================================
|
||||||
@@ -1673,7 +1673,7 @@ public:
|
|||||||
- `"rejectDupKeys": false or true`
|
- `"rejectDupKeys": false or true`
|
||||||
- If true, `parse()` returns false when a key is duplicated within an object.
|
- If true, `parse()` returns false when a key is duplicated within an object.
|
||||||
- `"allowSpecialFloats": false or true`
|
- `"allowSpecialFloats": false or true`
|
||||||
- If true, special float values (NaNs and infinities) are allowed
|
- If true, special float values (NaNs and infinities) are allowed
|
||||||
and their values are lossfree restorable.
|
and their values are lossfree restorable.
|
||||||
|
|
||||||
You can examine 'settings_` yourself
|
You can examine 'settings_` yourself
|
@@ -6,28 +6,28 @@
|
|||||||
// //////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The JsonCpp library's source code, including accompanying documentation,
|
The JsonCpp library's source code, including accompanying documentation,
|
||||||
tests and demonstration applications, are licensed under the following
|
tests and demonstration applications, are licensed under the following
|
||||||
conditions...
|
conditions...
|
||||||
|
|
||||||
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
||||||
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
||||||
this software is released into the Public Domain.
|
this software is released into the Public Domain.
|
||||||
|
|
||||||
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
|
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
|
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).
|
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
|
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
|
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
|
conditions of the MIT License (see below), or 3) under the terms of dual
|
||||||
Public Domain/MIT License conditions described here, as they choose.
|
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
|
The MIT License is about as close to Public Domain as a license can get, and is
|
||||||
described in clear, concise terms at:
|
described in clear, concise terms at:
|
||||||
|
|
||||||
http://en.wikipedia.org/wiki/MIT_License
|
http://en.wikipedia.org/wiki/MIT_License
|
||||||
|
|
||||||
The full text of the MIT License follows:
|
The full text of the MIT License follows:
|
||||||
|
|
||||||
========================================================================
|
========================================================================
|
||||||
@@ -238,7 +238,7 @@ static inline void fixNumericLocaleInput(char* begin, char* end) {
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above
|
#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above
|
||||||
#define snprintf sprintf_s
|
#define snprintf sprintf_s
|
||||||
#elif _MSC_VER >= 1900 // VC++ 14.0 and above
|
#elif _MSC_VER >= 1900 // VC++ 14.0 and above
|
||||||
#define snprintf std::snprintf
|
#define snprintf std::snprintf
|
||||||
@@ -383,7 +383,7 @@ bool Reader::parse(const char* beginDoc,
|
|||||||
|
|
||||||
bool Reader::readValue() {
|
bool Reader::readValue() {
|
||||||
// readValue() may call itself only if it calls readObject() or ReadArray().
|
// readValue() may call itself only if it calls readObject() or ReadArray().
|
||||||
// These methods execute nodes_.push() just before and nodes_.pop)() just after calling readValue().
|
// These methods execute nodes_.push() just before and nodes_.pop)() just after calling readValue().
|
||||||
// parse() executes one nodes_.push(), so > instead of >=.
|
// parse() executes one nodes_.push(), so > instead of >=.
|
||||||
if (nodes_.size() > stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue().");
|
if (nodes_.size() > stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue().");
|
||||||
|
|
||||||
@@ -4215,7 +4215,7 @@ Value& Path::make(Value& root) const {
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__BORLANDC__)
|
#if defined(__BORLANDC__)
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#define isfinite _finite
|
#define isfinite _finite
|
||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
@@ -5290,7 +5290,7 @@ StreamWriter* StreamWriterBuilder::newStreamWriter() const
|
|||||||
JSONCPP_STRING cs_str = settings_["commentStyle"].asString();
|
JSONCPP_STRING cs_str = settings_["commentStyle"].asString();
|
||||||
bool eyc = settings_["enableYAMLCompatibility"].asBool();
|
bool eyc = settings_["enableYAMLCompatibility"].asBool();
|
||||||
bool dnp = settings_["dropNullPlaceholders"].asBool();
|
bool dnp = settings_["dropNullPlaceholders"].asBool();
|
||||||
bool usf = settings_["useSpecialFloats"].asBool();
|
bool usf = settings_["useSpecialFloats"].asBool();
|
||||||
unsigned int pre = settings_["precision"].asUInt();
|
unsigned int pre = settings_["precision"].asUInt();
|
||||||
CommentStyle::Enum cs = CommentStyle::All;
|
CommentStyle::Enum cs = CommentStyle::All;
|
||||||
if (cs_str == "All") {
|
if (cs_str == "All") {
|
30
examples/echo_server/CMakeLists.txt
Normal file
30
examples/echo_server/CMakeLists.txt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#
|
||||||
|
# Author: Benjamin Sergeant
|
||||||
|
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
cmake_minimum_required (VERSION 3.4.1)
|
||||||
|
project (echo_server)
|
||||||
|
|
||||||
|
# There's -Weverything too for clang
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshorten-64-to-32")
|
||||||
|
|
||||||
|
set (OPENSSL_PREFIX /usr/local/opt/openssl) # Homebrew openssl
|
||||||
|
|
||||||
|
set (CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
|
option(USE_TLS "Add TLS support" ON)
|
||||||
|
|
||||||
|
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
|
||||||
|
|
||||||
|
include_directories(echo_server .)
|
||||||
|
|
||||||
|
add_executable(echo_server
|
||||||
|
echo_server.cpp)
|
||||||
|
|
||||||
|
if (APPLE AND USE_TLS)
|
||||||
|
target_link_libraries(echo_server "-framework foundation" "-framework security")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(echo_server ixwebsocket)
|
||||||
|
install(TARGETS echo_server DESTINATION bin)
|
68
examples/echo_server/echo_server.cpp
Normal file
68
examples/echo_server/echo_server.cpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* echo_server.cpp
|
||||||
|
* Author: Benjamin Sergeant
|
||||||
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <ixwebsocket/IXWebSocketServer.h>
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
webSocket->send(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
auto res = server.listen();
|
||||||
|
if (!res.first)
|
||||||
|
{
|
||||||
|
std::cerr << res.second << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
server.wait();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
1
.gitignore → examples/ping_pong/.gitignore
vendored
1
.gitignore → examples/ping_pong/.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
|
venv
|
||||||
build
|
build
|
27
examples/ping_pong/CMakeLists.txt
Normal file
27
examples/ping_pong/CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#
|
||||||
|
# Author: Benjamin Sergeant
|
||||||
|
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
cmake_minimum_required (VERSION 3.4.1)
|
||||||
|
project (ping_pong)
|
||||||
|
|
||||||
|
set (CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
|
option(USE_TLS "Add TLS support" ON)
|
||||||
|
|
||||||
|
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
|
||||||
|
|
||||||
|
add_executable(ping_pong ping_pong.cpp)
|
||||||
|
|
||||||
|
if (APPLE AND USE_TLS)
|
||||||
|
target_link_libraries(ping_pong "-framework foundation" "-framework security")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
target_link_libraries(ping_pong wsock32 ws2_32)
|
||||||
|
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(ping_pong ixwebsocket)
|
||||||
|
install(TARGETS ping_pong DESTINATION bin)
|
15
examples/ping_pong/build_linux.sh
Normal file
15
examples/ping_pong/build_linux.sh
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Author: Benjamin Sergeant
|
||||||
|
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
# 'manual' way of building. You can also use cmake.
|
||||||
|
|
||||||
|
g++ --std=c++11 \
|
||||||
|
../../ixwebsocket/IXSocket.cpp \
|
||||||
|
../../ixwebsocket/IXWebSocketTransport.cpp \
|
||||||
|
../../ixwebsocket/IXWebSocket.cpp \
|
||||||
|
-I ../.. \
|
||||||
|
cmd_websocket_chat.cpp \
|
||||||
|
-o cmd_websocket_chat
|
17
examples/ping_pong/client.py
Normal file
17
examples/ping_pong/client.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import websockets
|
||||||
|
|
||||||
|
async def hello(uri):
|
||||||
|
async with websockets.connect(uri) as websocket:
|
||||||
|
await websocket.send("Hello world!")
|
||||||
|
response = await websocket.recv()
|
||||||
|
print(response)
|
||||||
|
|
||||||
|
pong_waiter = await websocket.ping('coucou')
|
||||||
|
ret = await pong_waiter # only if you want to wait for the pong
|
||||||
|
print(ret)
|
||||||
|
|
||||||
|
asyncio.get_event_loop().run_until_complete(
|
||||||
|
hello('ws://localhost:5678'))
|
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ws_ping_pong.cpp
|
* ping_pong.cpp
|
||||||
* Author: Benjamin Sergeant
|
* Author: Benjamin Sergeant
|
||||||
* Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
|
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -9,8 +9,15 @@
|
|||||||
#include <ixwebsocket/IXWebSocket.h>
|
#include <ixwebsocket/IXWebSocket.h>
|
||||||
#include <ixwebsocket/IXSocket.h>
|
#include <ixwebsocket/IXSocket.h>
|
||||||
|
|
||||||
namespace ix
|
using namespace ix;
|
||||||
|
|
||||||
|
namespace
|
||||||
{
|
{
|
||||||
|
void log(const std::string& msg)
|
||||||
|
{
|
||||||
|
std::cout << msg << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
class WebSocketPingPong
|
class WebSocketPingPong
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -26,8 +33,6 @@ namespace ix
|
|||||||
private:
|
private:
|
||||||
std::string _url;
|
std::string _url;
|
||||||
ix::WebSocket _webSocket;
|
ix::WebSocket _webSocket;
|
||||||
|
|
||||||
void log(const std::string& msg);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocketPingPong::WebSocketPingPong(const std::string& url) :
|
WebSocketPingPong::WebSocketPingPong(const std::string& url) :
|
||||||
@@ -36,11 +41,6 @@ namespace ix
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketPingPong::log(const std::string& msg)
|
|
||||||
{
|
|
||||||
std::cout << msg << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketPingPong::stop()
|
void WebSocketPingPong::stop()
|
||||||
{
|
{
|
||||||
_webSocket.stop();
|
_webSocket.stop();
|
||||||
@@ -124,7 +124,7 @@ namespace ix
|
|||||||
_webSocket.send(text);
|
_webSocket.send(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ws_ping_pong_main(const std::string& url)
|
void interactiveMain(const std::string& url)
|
||||||
{
|
{
|
||||||
std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
|
std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
|
||||||
WebSocketPingPong webSocketPingPong(url);
|
WebSocketPingPong webSocketPingPong(url);
|
||||||
@@ -155,3 +155,17 @@ namespace ix
|
|||||||
webSocketPingPong.stop();
|
webSocketPingPong.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
std::cerr << "Usage: ping_pong <url>" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string url = argv[1];
|
||||||
|
|
||||||
|
Socket::init();
|
||||||
|
interactiveMain(url);
|
||||||
|
return 0;
|
||||||
|
}
|
21
examples/ping_pong/server.py
Normal file
21
examples/ping_pong/server.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os
|
||||||
|
import asyncio
|
||||||
|
import websockets
|
||||||
|
|
||||||
|
async def echo(websocket, path):
|
||||||
|
async for message in websocket:
|
||||||
|
print(message)
|
||||||
|
await websocket.send(message)
|
||||||
|
|
||||||
|
if os.getenv('TEST_CLOSE'):
|
||||||
|
print('Closing')
|
||||||
|
# breakpoint()
|
||||||
|
await websocket.close(1001, 'close message')
|
||||||
|
# await websocket.close()
|
||||||
|
break
|
||||||
|
|
||||||
|
asyncio.get_event_loop().run_until_complete(
|
||||||
|
websockets.serve(echo, 'localhost', 5678))
|
||||||
|
asyncio.get_event_loop().run_forever()
|
9
examples/ping_pong/test.sh
Normal file
9
examples/ping_pong/test.sh
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test -d build || {
|
||||||
|
mkdir -p build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
}
|
||||||
|
(cd build ; make)
|
||||||
|
./build/ping_pong ws://localhost:5678
|
3
examples/ws_connect/.gitignore
vendored
Normal file
3
examples/ws_connect/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
build
|
||||||
|
venv
|
||||||
|
node_modules
|
22
examples/ws_connect/CMakeLists.txt
Normal file
22
examples/ws_connect/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#
|
||||||
|
# Author: Benjamin Sergeant
|
||||||
|
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
cmake_minimum_required (VERSION 3.4.1)
|
||||||
|
project (ws_connect)
|
||||||
|
|
||||||
|
set (CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
|
option(USE_TLS "Add TLS support" ON)
|
||||||
|
|
||||||
|
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
|
||||||
|
|
||||||
|
add_executable(ws_connect ws_connect.cpp)
|
||||||
|
|
||||||
|
if (APPLE AND USE_TLS)
|
||||||
|
target_link_libraries(ws_connect "-framework foundation" "-framework security")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(ws_connect ixwebsocket)
|
||||||
|
install(TARGETS ws_connect DESTINATION bin)
|
11
examples/ws_connect/README.md
Normal file
11
examples/ws_connect/README.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Building
|
||||||
|
|
||||||
|
1. mkdir build
|
||||||
|
2. cd build
|
||||||
|
3. cmake ..
|
||||||
|
4. make
|
||||||
|
|
||||||
|
## Disable TLS
|
||||||
|
|
||||||
|
* Enable: `cmake -DUSE_TLS=OFF ..`
|
||||||
|
* Disable: `cmake -DUSE_TLS=ON ..`
|
25
examples/ws_connect/build_linux.sh
Normal file
25
examples/ws_connect/build_linux.sh
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Author: Benjamin Sergeant
|
||||||
|
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
# 'manual' way of building. You can also use cmake.
|
||||||
|
|
||||||
|
g++ --std=c++11 \
|
||||||
|
-DIXWEBSOCKET_USE_TLS \
|
||||||
|
-g \
|
||||||
|
../../ixwebsocket/IXEventFd.cpp \
|
||||||
|
../../ixwebsocket/IXSocket.cpp \
|
||||||
|
../../ixwebsocket/IXSetThreadName.cpp \
|
||||||
|
../../ixwebsocket/IXWebSocketTransport.cpp \
|
||||||
|
../../ixwebsocket/IXWebSocket.cpp \
|
||||||
|
../../ixwebsocket/IXDNSLookup.cpp \
|
||||||
|
../../ixwebsocket/IXSocketConnect.cpp \
|
||||||
|
../../ixwebsocket/IXSocketOpenSSL.cpp \
|
||||||
|
../../ixwebsocket/IXWebSocketPerMessageDeflate.cpp \
|
||||||
|
../../ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp \
|
||||||
|
-I ../.. \
|
||||||
|
ws_connect.cpp \
|
||||||
|
-o ws_connect \
|
||||||
|
-lcrypto -lssl -lz -lpthread
|
@@ -9,8 +9,15 @@
|
|||||||
#include <ixwebsocket/IXWebSocket.h>
|
#include <ixwebsocket/IXWebSocket.h>
|
||||||
#include <ixwebsocket/IXSocket.h>
|
#include <ixwebsocket/IXSocket.h>
|
||||||
|
|
||||||
namespace ix
|
using namespace ix;
|
||||||
|
|
||||||
|
namespace
|
||||||
{
|
{
|
||||||
|
void log(const std::string& msg)
|
||||||
|
{
|
||||||
|
std::cout << msg << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
class WebSocketConnect
|
class WebSocketConnect
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -25,8 +32,6 @@ namespace ix
|
|||||||
private:
|
private:
|
||||||
std::string _url;
|
std::string _url;
|
||||||
ix::WebSocket _webSocket;
|
ix::WebSocket _webSocket;
|
||||||
|
|
||||||
void log(const std::string& msg);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocketConnect::WebSocketConnect(const std::string& url) :
|
WebSocketConnect::WebSocketConnect(const std::string& url) :
|
||||||
@@ -35,11 +40,6 @@ namespace ix
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketConnect::log(const std::string& msg)
|
|
||||||
{
|
|
||||||
std::cout << msg << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketConnect::stop()
|
void WebSocketConnect::stop()
|
||||||
{
|
{
|
||||||
_webSocket.stop();
|
_webSocket.stop();
|
||||||
@@ -148,12 +148,18 @@ namespace ix
|
|||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
webSocketChat.stop();
|
webSocketChat.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ws_connect_main(const std::string& url)
|
|
||||||
{
|
|
||||||
Socket::init();
|
|
||||||
interactiveMain(url);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
std::cerr << "Usage: ws_connect <url>" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string url = argv[1];
|
||||||
|
|
||||||
|
Socket::init();
|
||||||
|
interactiveMain(url);
|
||||||
|
return 0;
|
||||||
|
}
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
CancellationRequest makeCancellationRequestWithTimeout(int secs,
|
CancellationRequest makeCancellationRequestWithTimeout(int secs,
|
||||||
std::atomic<bool>& requestInitCancellation)
|
std::atomic<bool>& requestInitCancellation)
|
||||||
@@ -20,7 +20,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
// Was an explicit cancellation requested ?
|
// Was an explicit cancellation requested ?
|
||||||
if (requestInitCancellation) return true;
|
if (requestInitCancellation) return true;
|
||||||
|
|
||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
if ((now - start) > timeout) return true;
|
if ((now - start) > timeout) return true;
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
using CancellationRequest = std::function<bool()>;
|
using CancellationRequest = std::function<bool()>;
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
const int64_t DNSLookup::kDefaultWait = 10; // ms
|
const int64_t DNSLookup::kDefaultWait = 10; // ms
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ namespace ix
|
|||||||
_done(false),
|
_done(false),
|
||||||
_id(_nextId++)
|
_id(_nextId++)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DNSLookup::~DNSLookup()
|
DNSLookup::~DNSLookup()
|
||||||
@@ -36,7 +36,7 @@ namespace ix
|
|||||||
_activeJobs.erase(_id);
|
_activeJobs.erase(_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
|
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg)
|
std::string& errMsg)
|
||||||
{
|
{
|
||||||
@@ -49,7 +49,7 @@ namespace ix
|
|||||||
std::string sport = std::to_string(port);
|
std::string sport = std::to_string(port);
|
||||||
|
|
||||||
struct addrinfo* res;
|
struct addrinfo* res;
|
||||||
int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(),
|
int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(),
|
||||||
&hints, &res);
|
&hints, &res);
|
||||||
if (getaddrinfo_result)
|
if (getaddrinfo_result)
|
||||||
{
|
{
|
||||||
@@ -101,7 +101,7 @@ namespace ix
|
|||||||
_activeJobs.insert(_id);
|
_activeJobs.insert(_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Good resource on thread forced termination
|
// Good resource on thread forced termination
|
||||||
// https://www.bo-yang.net/2017/11/19/cpp-kill-detached-thread
|
// https://www.bo-yang.net/2017/11/19/cpp-kill-detached-thread
|
||||||
//
|
//
|
||||||
@@ -141,7 +141,7 @@ namespace ix
|
|||||||
void DNSLookup::run(uint64_t id, const std::string& hostname, int port) // thread runner
|
void DNSLookup::run(uint64_t id, const std::string& hostname, int port) // thread runner
|
||||||
{
|
{
|
||||||
// We don't want to read or write into members variables of an object that could be
|
// We don't want to read or write into members variables of an object that could be
|
||||||
// gone, so we use temporary variables (res) or we pass in by copy everything that
|
// gone, so we use temporary variables (res) or we pass in by copy everything that
|
||||||
// getAddrInfo needs to work.
|
// getAddrInfo needs to work.
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
struct addrinfo* res = getAddrInfo(hostname, port, errMsg);
|
struct addrinfo* res = getAddrInfo(hostname, port, errMsg);
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
* Author: Benjamin Sergeant
|
* Author: Benjamin Sergeant
|
||||||
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
* Resolve a hostname+port to a struct addrinfo obtained with getaddrinfo
|
* Resolve a hostname+port to a struct addrinfo obtained with getaddrinfo
|
||||||
* Does this in a background thread so that it can be cancelled, since
|
* Does this in a background thread so that it can be cancelled, since
|
||||||
* getaddrinfo is a blocking call, and we don't want to block the main thread on Mobile.
|
* getaddrinfo is a blocking call, and we don't want to block the main thread on Mobile.
|
||||||
*/
|
*/
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
struct addrinfo;
|
struct addrinfo;
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class DNSLookup {
|
class DNSLookup {
|
||||||
public:
|
public:
|
||||||
@@ -39,7 +39,7 @@ namespace ix
|
|||||||
struct addrinfo* resolveBlocking(std::string& errMsg,
|
struct addrinfo* resolveBlocking(std::string& errMsg,
|
||||||
const CancellationRequest& isCancellationRequested);
|
const CancellationRequest& isCancellationRequested);
|
||||||
|
|
||||||
static struct addrinfo* getAddrInfo(const std::string& hostname,
|
static struct addrinfo* getAddrInfo(const std::string& hostname,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg);
|
std::string& errMsg);
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
// eventfd was added in Linux kernel 2.x, and our oldest Android (Kitkat 4.4)
|
// eventfd was added in Linux kernel 2.x, and our oldest Android (Kitkat 4.4)
|
||||||
// is on Kernel 3.x
|
// is on Kernel 3.x
|
||||||
//
|
//
|
||||||
// cf Android/Kernel table here
|
// cf Android/Kernel table here
|
||||||
// https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
|
// https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -28,9 +28,9 @@
|
|||||||
#include <unistd.h> // for write
|
#include <unistd.h> // for write
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
EventFd::EventFd() :
|
EventFd::EventFd() :
|
||||||
_eventfd(-1)
|
_eventfd(-1)
|
||||||
{
|
{
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
@@ -65,7 +65,7 @@ namespace ix
|
|||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
if (_eventfd == -1) return false;
|
if (_eventfd == -1) return false;
|
||||||
|
|
||||||
// 0 is a special value ; select will not wake up
|
// 0 is a special value ; select will not wake up
|
||||||
uint64_t value = 0;
|
uint64_t value = 0;
|
||||||
|
|
||||||
// we should write 8 bytes for an uint64_t
|
// we should write 8 bytes for an uint64_t
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class EventFd {
|
class EventFd {
|
||||||
public:
|
public:
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
/*
|
|
||||||
* IXProgressCallback.h
|
|
||||||
* Author: Benjamin Sergeant
|
|
||||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace ix
|
|
||||||
{
|
|
||||||
using OnProgressCallback = std::function<bool(int current, int total)>;
|
|
||||||
}
|
|
@@ -15,16 +15,17 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
const int Socket::kDefaultPollNoTimeout = -1; // No poll timeout by default
|
const int Socket::kDefaultPollNoTimeout = -1; // No poll timeout by default
|
||||||
const int Socket::kDefaultPollTimeout = kDefaultPollNoTimeout;
|
const int Socket::kDefaultPollTimeout = kDefaultPollNoTimeout;
|
||||||
|
|
||||||
Socket::Socket(int fd) :
|
Socket::Socket(int fd) :
|
||||||
_sockfd(fd)
|
_sockfd(fd)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -43,22 +44,21 @@ namespace ix
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fd_set rfds;
|
|
||||||
FD_ZERO(&rfds);
|
|
||||||
FD_SET(_sockfd, &rfds);
|
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
FD_SET(_eventfd.getFd(), &rfds);
|
constexpr int nfds = 2;
|
||||||
|
#else
|
||||||
|
constexpr int nfds = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct timeval timeout;
|
struct pollfd fds[nfds];
|
||||||
timeout.tv_sec = timeoutSecs;
|
fds[0].fd = _sockfd;
|
||||||
timeout.tv_usec = 0;
|
fds[0].events = POLLIN;
|
||||||
|
|
||||||
int sockfd = _sockfd;
|
#ifdef __linux__
|
||||||
int nfds = (std::max)(sockfd, _eventfd.getFd());
|
fds[1].fd = _eventfd.getFd();
|
||||||
int ret = select(nfds + 1, &rfds, nullptr, nullptr,
|
fds[1].events = POLLIN;
|
||||||
(timeoutSecs < 0) ? nullptr : &timeout);
|
#endif
|
||||||
|
int ret = ::poll(fds, nfds, timeoutSecs * 1000);
|
||||||
|
|
||||||
PollResultType pollResult = PollResultType_ReadyForRead;
|
PollResultType pollResult = PollResultType_ReadyForRead;
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@@ -71,6 +71,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
onPollCallback(pollResult);
|
onPollCallback(pollResult);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::wakeUpFromPoll()
|
void Socket::wakeUpFromPoll()
|
||||||
@@ -150,7 +151,7 @@ namespace ix
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
INT rc;
|
INT rc;
|
||||||
WSADATA wsaData;
|
WSADATA wsaData;
|
||||||
|
|
||||||
rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||||
return rc != 0;
|
return rc != 0;
|
||||||
#else
|
#else
|
||||||
|
@@ -19,7 +19,7 @@ typedef SSIZE_T ssize_t;
|
|||||||
#include "IXEventFd.h"
|
#include "IXEventFd.h"
|
||||||
#include "IXCancellationRequest.h"
|
#include "IXCancellationRequest.h"
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
enum PollResultType
|
enum PollResultType
|
||||||
{
|
{
|
||||||
@@ -42,7 +42,7 @@ namespace ix
|
|||||||
virtual void wakeUpFromPoll();
|
virtual void wakeUpFromPoll();
|
||||||
|
|
||||||
// Virtual methods
|
// Virtual methods
|
||||||
virtual bool connect(const std::string& url,
|
virtual bool connect(const std::string& url,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
const CancellationRequest& isCancellationRequested);
|
const CancellationRequest& isCancellationRequested);
|
||||||
|
@@ -50,7 +50,7 @@ OSStatus read_from_socket(SSLConnectionRef connection, void *data, size_t *len)
|
|||||||
else
|
else
|
||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
else if (0 == status)
|
else if (0 == status)
|
||||||
{
|
{
|
||||||
*len = 0;
|
*len = 0;
|
||||||
return errSSLClosedGraceful;
|
return errSSLClosedGraceful;
|
||||||
@@ -102,7 +102,7 @@ OSStatus write_to_socket(SSLConnectionRef connection, const void *data, size_t *
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
*len = 0;
|
*len = 0;
|
||||||
if (EAGAIN == errno)
|
if (EAGAIN == errno)
|
||||||
{
|
{
|
||||||
return errSSLWouldBlock;
|
return errSSLWouldBlock;
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ std::string getSSLErrorDescription(OSStatus status)
|
|||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
SocketAppleSSL::SocketAppleSSL(int fd) : Socket(fd),
|
SocketAppleSSL::SocketAppleSSL(int fd) : Socket(fd),
|
||||||
_sslContext(nullptr)
|
_sslContext(nullptr)
|
||||||
@@ -176,11 +176,11 @@ namespace ix
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
status = SSLHandshake(_sslContext);
|
status = SSLHandshake(_sslContext);
|
||||||
} while (errSSLWouldBlock == status ||
|
} while (errSSLWouldBlock == status ||
|
||||||
errSSLServerAuthCompleted == status);
|
errSSLServerAuthCompleted == status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noErr != status)
|
if (noErr != status)
|
||||||
{
|
{
|
||||||
errMsg = getSSLErrorDescription(status);
|
errMsg = getSSLErrorDescription(status);
|
||||||
close();
|
close();
|
||||||
@@ -230,7 +230,7 @@ namespace ix
|
|||||||
ssize_t SocketAppleSSL::recv(void* buf, size_t nbyte)
|
ssize_t SocketAppleSSL::recv(void* buf, size_t nbyte)
|
||||||
{
|
{
|
||||||
OSStatus status = errSSLWouldBlock;
|
OSStatus status = errSSLWouldBlock;
|
||||||
while (errSSLWouldBlock == status)
|
while (errSSLWouldBlock == status)
|
||||||
{
|
{
|
||||||
size_t processed = 0;
|
size_t processed = 0;
|
||||||
std::lock_guard<std::mutex> lock(_mutex);
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
@@ -239,7 +239,7 @@ namespace ix
|
|||||||
if (processed > 0)
|
if (processed > 0)
|
||||||
return (ssize_t) processed;
|
return (ssize_t) processed;
|
||||||
|
|
||||||
// The connection was reset, inform the caller that this
|
// The connection was reset, inform the caller that this
|
||||||
// Socket should close
|
// Socket should close
|
||||||
if (status == errSSLClosedGraceful ||
|
if (status == errSSLClosedGraceful ||
|
||||||
status == errSSLClosedNoNotify ||
|
status == errSSLClosedNoNotify ||
|
||||||
|
@@ -14,15 +14,15 @@
|
|||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class SocketAppleSSL : public Socket
|
class SocketAppleSSL : public Socket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SocketAppleSSL(int fd = -1);
|
SocketAppleSSL(int fd = -1);
|
||||||
~SocketAppleSSL();
|
~SocketAppleSSL();
|
||||||
|
|
||||||
virtual bool connect(const std::string& host,
|
virtual bool connect(const std::string& host,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
const CancellationRequest& isCancellationRequested) final;
|
const CancellationRequest& isCancellationRequested) final;
|
||||||
|
@@ -30,7 +30,7 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// This function can be cancelled every 50 ms
|
// This function can be cancelled every 50 ms
|
||||||
@@ -42,7 +42,7 @@ namespace ix
|
|||||||
const CancellationRequest& isCancellationRequested)
|
const CancellationRequest& isCancellationRequested)
|
||||||
{
|
{
|
||||||
errMsg = "no error";
|
errMsg = "no error";
|
||||||
|
|
||||||
int fd = socket(address->ai_family,
|
int fd = socket(address->ai_family,
|
||||||
address->ai_socktype,
|
address->ai_socktype,
|
||||||
address->ai_protocol);
|
address->ai_protocol);
|
||||||
@@ -72,7 +72,7 @@ namespace ix
|
|||||||
errMsg = "Cancelled";
|
errMsg = "Cancelled";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use select to check the status of the new connection
|
// Use select to check the status of the new connection
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
timeout.tv_sec = 0;
|
timeout.tv_sec = 0;
|
||||||
@@ -179,7 +179,7 @@ namespace ix
|
|||||||
// 3. (apple) prevent SIGPIPE from being emitted when the remote end disconnect
|
// 3. (apple) prevent SIGPIPE from being emitted when the remote end disconnect
|
||||||
#ifdef SO_NOSIGPIPE
|
#ifdef SO_NOSIGPIPE
|
||||||
int value = 1;
|
int value = 1;
|
||||||
setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE,
|
setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE,
|
||||||
(void *)&value, sizeof(value));
|
(void *)&value, sizeof(value));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
struct addrinfo;
|
struct addrinfo;
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class SocketConnect {
|
class SocketConnect {
|
||||||
public:
|
public:
|
||||||
|
@@ -18,12 +18,12 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#define socketerrno errno
|
#define socketerrno errno
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
std::atomic<bool> SocketOpenSSL::_openSSLInitializationSuccessful(false);
|
std::atomic<bool> SocketOpenSSL::_openSSLInitializationSuccessful(false);
|
||||||
|
|
||||||
SocketOpenSSL::SocketOpenSSL(int fd) : Socket(fd),
|
SocketOpenSSL::SocketOpenSSL(int fd) : Socket(fd),
|
||||||
_ssl_connection(nullptr),
|
_ssl_connection(nullptr),
|
||||||
_ssl_context(nullptr)
|
_ssl_context(nullptr)
|
||||||
{
|
{
|
||||||
std::call_once(_openSSLInitFlag, &SocketOpenSSL::openSSLInitialize, this);
|
std::call_once(_openSSLInitFlag, &SocketOpenSSL::openSSLInitialize, this);
|
||||||
@@ -80,7 +80,7 @@ namespace ix
|
|||||||
return "OpenSSL failed - underlying BIO reported an I/O error";
|
return "OpenSSL failed - underlying BIO reported an I/O error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (err == SSL_ERROR_SSL)
|
else if (err == SSL_ERROR_SSL)
|
||||||
{
|
{
|
||||||
e = ERR_get_error();
|
e = ERR_get_error();
|
||||||
std::string errMsg("OpenSSL failed - ");
|
std::string errMsg("OpenSSL failed - ");
|
||||||
@@ -149,7 +149,7 @@ namespace ix
|
|||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||||
// Check server name
|
// Check server name
|
||||||
bool hostname_verifies_ok = false;
|
bool hostname_verifies_ok = false;
|
||||||
STACK_OF(GENERAL_NAME) *san_names =
|
STACK_OF(GENERAL_NAME) *san_names =
|
||||||
(STACK_OF(GENERAL_NAME)*) X509_get_ext_d2i((X509 *)server_cert,
|
(STACK_OF(GENERAL_NAME)*) X509_get_ext_d2i((X509 *)server_cert,
|
||||||
NID_subject_alt_name, NULL, NULL);
|
NID_subject_alt_name, NULL, NULL);
|
||||||
if (san_names)
|
if (san_names)
|
||||||
@@ -160,8 +160,8 @@ namespace ix
|
|||||||
if (sk_name->type == GEN_DNS)
|
if (sk_name->type == GEN_DNS)
|
||||||
{
|
{
|
||||||
char *name = (char *)ASN1_STRING_data(sk_name->d.dNSName);
|
char *name = (char *)ASN1_STRING_data(sk_name->d.dNSName);
|
||||||
if ((size_t)ASN1_STRING_length(sk_name->d.dNSName) == strlen(name) &&
|
if ((size_t)ASN1_STRING_length(sk_name->d.dNSName) == strlen(name) &&
|
||||||
checkHost(hostname, name))
|
checkHost(hostname, name))
|
||||||
{
|
{
|
||||||
hostname_verifies_ok = true;
|
hostname_verifies_ok = true;
|
||||||
break;
|
break;
|
||||||
@@ -185,8 +185,8 @@ namespace ix
|
|||||||
ASN1_STRING *cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry);
|
ASN1_STRING *cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry);
|
||||||
char *cn = (char *)ASN1_STRING_data(cn_asn1);
|
char *cn = (char *)ASN1_STRING_data(cn_asn1);
|
||||||
|
|
||||||
if ((size_t)ASN1_STRING_length(cn_asn1) == strlen(cn) &&
|
if ((size_t)ASN1_STRING_length(cn_asn1) == strlen(cn) &&
|
||||||
checkHost(hostname, cn))
|
checkHost(hostname, cn))
|
||||||
{
|
{
|
||||||
hostname_verifies_ok = true;
|
hostname_verifies_ok = true;
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ namespace ix
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SocketOpenSSL::openSSLHandshake(const std::string& host, std::string& errMsg)
|
bool SocketOpenSSL::openSSLHandshake(const std::string& host, std::string& errMsg)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@@ -17,15 +17,15 @@
|
|||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class SocketOpenSSL : public Socket
|
class SocketOpenSSL : public Socket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SocketOpenSSL(int fd = -1);
|
SocketOpenSSL(int fd = -1);
|
||||||
~SocketOpenSSL();
|
~SocketOpenSSL();
|
||||||
|
|
||||||
virtual bool connect(const std::string& host,
|
virtual bool connect(const std::string& host,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
const CancellationRequest& isCancellationRequested) final;
|
const CancellationRequest& isCancellationRequested) final;
|
||||||
|
@@ -47,7 +47,7 @@
|
|||||||
// link with ntdsapi.lib for DsMakeSpn function
|
// link with ntdsapi.lib for DsMakeSpn function
|
||||||
#pragma comment(lib, "ntdsapi.lib")
|
#pragma comment(lib, "ntdsapi.lib")
|
||||||
|
|
||||||
// The following function assumes that Winsock
|
// The following function assumes that Winsock
|
||||||
// has already been initialized
|
// has already been initialized
|
||||||
|
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
# error("This file should only be built on Windows")
|
# error("This file should only be built on Windows")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
SocketSChannel::SocketSChannel()
|
SocketSChannel::SocketSChannel()
|
||||||
{
|
{
|
||||||
@@ -68,7 +68,7 @@ namespace ix
|
|||||||
|
|
||||||
SocketSChannel::~SocketSChannel()
|
SocketSChannel::~SocketSChannel()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SocketSChannel::connect(const std::string& host,
|
bool SocketSChannel::connect(const std::string& host,
|
||||||
@@ -78,7 +78,7 @@ namespace ix
|
|||||||
return Socket::connect(host, port, errMsg);
|
return Socket::connect(host, port, errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SocketSChannel::secureSocket()
|
void SocketSChannel::secureSocket()
|
||||||
{
|
{
|
||||||
// there will be a lot to do here ...
|
// there will be a lot to do here ...
|
||||||
|
@@ -8,15 +8,15 @@
|
|||||||
|
|
||||||
#include "IXSocket.h"
|
#include "IXSocket.h"
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class SocketSChannel : public Socket
|
class SocketSChannel : public Socket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SocketSChannel();
|
SocketSChannel();
|
||||||
~SocketSChannel();
|
~SocketSChannel();
|
||||||
|
|
||||||
virtual bool connect(const std::string& host,
|
virtual bool connect(const std::string& host,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg) final;
|
std::string& errMsg) final;
|
||||||
virtual void close() final;
|
virtual void close() final;
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
#include <future>
|
#include <future>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
const int SocketServer::kDefaultPort(8080);
|
const int SocketServer::kDefaultPort(8080);
|
||||||
const std::string SocketServer::kDefaultHost("127.0.0.1");
|
const std::string SocketServer::kDefaultHost("127.0.0.1");
|
||||||
@@ -83,7 +83,7 @@ namespace ix
|
|||||||
server.sin_family = AF_INET;
|
server.sin_family = AF_INET;
|
||||||
server.sin_port = htons(_port);
|
server.sin_port = htons(_port);
|
||||||
|
|
||||||
// Using INADDR_ANY trigger a pop-up box as binding to any address is detected
|
// Using INADDR_ANY trigger a pop-up box as binding to any address is detected
|
||||||
// by the osx firewall. We need to codesign the binary with a self-signed cert
|
// by the osx firewall. We need to codesign the binary with a self-signed cert
|
||||||
// to allow that, but this is a bit of a pain. (this is what node or python would do).
|
// to allow that, but this is a bit of a pain. (this is what node or python would do).
|
||||||
//
|
//
|
||||||
@@ -216,7 +216,7 @@ namespace ix
|
|||||||
|
|
||||||
// Launch the handleConnection work asynchronously in its own thread.
|
// Launch the handleConnection work asynchronously in its own thread.
|
||||||
//
|
//
|
||||||
// the destructor of a future returned by std::async blocks,
|
// the destructor of a future returned by std::async blocks,
|
||||||
// so we need to declare it outside of this loop
|
// so we need to declare it outside of this loop
|
||||||
f = std::async(std::launch::async,
|
f = std::async(std::launch::async,
|
||||||
&SocketServer::handleConnection,
|
&SocketServer::handleConnection,
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class SocketServer {
|
class SocketServer {
|
||||||
public:
|
public:
|
||||||
|
@@ -50,7 +50,7 @@ namespace ix
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocket::~WebSocket()
|
WebSocket::~WebSocket()
|
||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onMessageCallback(WebSocket_MessageType_Open, "", 0,
|
_onMessageCallback(WebSocket_MessageType_Open, "", 0,
|
||||||
WebSocketErrorInfo(),
|
WebSocketErrorInfo(),
|
||||||
WebSocketOpenInfo(status.uri, status.headers),
|
WebSocketOpenInfo(status.uri, status.headers),
|
||||||
WebSocketCloseInfo());
|
WebSocketCloseInfo());
|
||||||
return status;
|
return status;
|
||||||
@@ -155,7 +155,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onMessageCallback(WebSocket_MessageType_Open, "", 0,
|
_onMessageCallback(WebSocket_MessageType_Open, "", 0,
|
||||||
WebSocketErrorInfo(),
|
WebSocketErrorInfo(),
|
||||||
WebSocketOpenInfo(status.uri, status.headers),
|
WebSocketOpenInfo(status.uri, status.headers),
|
||||||
WebSocketCloseInfo());
|
WebSocketCloseInfo());
|
||||||
return status;
|
return status;
|
||||||
@@ -184,7 +184,7 @@ namespace ix
|
|||||||
using millis = std::chrono::duration<double, std::milli>;
|
using millis = std::chrono::duration<double, std::milli>;
|
||||||
millis duration;
|
millis duration;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (isConnected() || isClosing() || _stop || !_automaticReconnection)
|
if (isConnected() || isClosing() || _stop || !_automaticReconnection)
|
||||||
{
|
{
|
||||||
@@ -214,7 +214,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
setThreadName(_url);
|
setThreadName(_url);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (_stop) return;
|
if (_stop) return;
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ namespace ix
|
|||||||
|
|
||||||
if (_stop) return;
|
if (_stop) return;
|
||||||
|
|
||||||
// 2. Poll to see if there's any new data available
|
// 2. Poll to see if there's any new data available
|
||||||
_ws.poll();
|
_ws.poll();
|
||||||
|
|
||||||
if (_stop) return;
|
if (_stop) return;
|
||||||
@@ -273,7 +273,7 @@ namespace ix
|
|||||||
|
|
||||||
void WebSocket::setOnMessageCallback(const OnMessageCallback& callback)
|
void WebSocket::setOnMessageCallback(const OnMessageCallback& callback)
|
||||||
{
|
{
|
||||||
_onMessageCallback = callback;
|
_onMessageCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocket::setTrafficTrackerCallback(const OnTrafficTrackerCallback& callback)
|
void WebSocket::setTrafficTrackerCallback(const OnTrafficTrackerCallback& callback)
|
||||||
@@ -294,10 +294,9 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketSendInfo WebSocket::send(const std::string& text,
|
WebSocketSendInfo WebSocket::send(const std::string& text)
|
||||||
const OnProgressCallback& onProgressCallback)
|
|
||||||
{
|
{
|
||||||
return sendMessage(text, false, onProgressCallback);
|
return sendMessage(text, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketSendInfo WebSocket::ping(const std::string& text)
|
WebSocketSendInfo WebSocket::ping(const std::string& text)
|
||||||
@@ -309,9 +308,7 @@ namespace ix
|
|||||||
return sendMessage(text, true);
|
return sendMessage(text, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketSendInfo WebSocket::sendMessage(const std::string& text,
|
WebSocketSendInfo WebSocket::sendMessage(const std::string& text, bool ping)
|
||||||
bool ping,
|
|
||||||
const OnProgressCallback& onProgressCallback)
|
|
||||||
{
|
{
|
||||||
if (!isConnected()) return WebSocketSendInfo(false);
|
if (!isConnected()) return WebSocketSendInfo(false);
|
||||||
|
|
||||||
@@ -333,7 +330,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
webSocketSendInfo = _ws.sendBinary(text, onProgressCallback);
|
webSocketSendInfo = _ws.sendBinary(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocket::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false);
|
WebSocket::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false);
|
||||||
@@ -343,7 +340,7 @@ namespace ix
|
|||||||
|
|
||||||
ReadyState WebSocket::getReadyState() const
|
ReadyState WebSocket::getReadyState() const
|
||||||
{
|
{
|
||||||
switch (_ws.getReadyState())
|
switch (_ws.getReadyState())
|
||||||
{
|
{
|
||||||
case ix::WebSocketTransport::OPEN: return WebSocket_ReadyState_Open;
|
case ix::WebSocketTransport::OPEN: return WebSocket_ReadyState_Open;
|
||||||
case ix::WebSocketTransport::CONNECTING: return WebSocket_ReadyState_Connecting;
|
case ix::WebSocketTransport::CONNECTING: return WebSocket_ReadyState_Connecting;
|
||||||
|
@@ -19,12 +19,11 @@
|
|||||||
#include "IXWebSocketSendInfo.h"
|
#include "IXWebSocketSendInfo.h"
|
||||||
#include "IXWebSocketPerMessageDeflateOptions.h"
|
#include "IXWebSocketPerMessageDeflateOptions.h"
|
||||||
#include "IXWebSocketHttpHeaders.h"
|
#include "IXWebSocketHttpHeaders.h"
|
||||||
#include "IXProgressCallback.h"
|
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Ready_state_constants
|
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Ready_state_constants
|
||||||
enum ReadyState
|
enum ReadyState
|
||||||
{
|
{
|
||||||
WebSocket_ReadyState_Connecting = 0,
|
WebSocket_ReadyState_Connecting = 0,
|
||||||
WebSocket_ReadyState_Open = 1,
|
WebSocket_ReadyState_Open = 1,
|
||||||
@@ -79,7 +78,7 @@ namespace ix
|
|||||||
|
|
||||||
using OnTrafficTrackerCallback = std::function<void(size_t size, bool incoming)>;
|
using OnTrafficTrackerCallback = std::function<void(size_t size, bool incoming)>;
|
||||||
|
|
||||||
class WebSocket
|
class WebSocket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WebSocket();
|
WebSocket();
|
||||||
@@ -98,8 +97,7 @@ namespace ix
|
|||||||
WebSocketInitResult connect(int timeoutSecs);
|
WebSocketInitResult connect(int timeoutSecs);
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
WebSocketSendInfo send(const std::string& text,
|
WebSocketSendInfo send(const std::string& text);
|
||||||
const OnProgressCallback& onProgressCallback = nullptr);
|
|
||||||
WebSocketSendInfo ping(const std::string& text);
|
WebSocketSendInfo ping(const std::string& text);
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
@@ -117,9 +115,7 @@ namespace ix
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
WebSocketSendInfo sendMessage(const std::string& text,
|
WebSocketSendInfo sendMessage(const std::string& text, bool ping);
|
||||||
bool ping,
|
|
||||||
const OnProgressCallback& callback = nullptr);
|
|
||||||
|
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
bool isClosing() const;
|
bool isClosing() const;
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
struct WebSocketErrorInfo
|
struct WebSocketErrorInfo
|
||||||
{
|
{
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
WebSocketHandshake::WebSocketHandshake(std::atomic<bool>& requestInitCancellation,
|
WebSocketHandshake::WebSocketHandshake(std::atomic<bool>& requestInitCancellation,
|
||||||
std::shared_ptr<Socket> socket,
|
std::shared_ptr<Socket> socket,
|
||||||
@@ -171,7 +171,7 @@ namespace ix
|
|||||||
|
|
||||||
std::string WebSocketHandshake::genRandomString(const int len)
|
std::string WebSocketHandshake::genRandomString(const int len)
|
||||||
{
|
{
|
||||||
std::string alphanum =
|
std::string alphanum =
|
||||||
"0123456789"
|
"0123456789"
|
||||||
"ABCDEFGH"
|
"ABCDEFGH"
|
||||||
"abcdefgh";
|
"abcdefgh";
|
||||||
@@ -201,7 +201,7 @@ namespace ix
|
|||||||
char line[256];
|
char line[256];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int colon = 0;
|
int colon = 0;
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
_requestInitCancellation = false;
|
_requestInitCancellation = false;
|
||||||
|
|
||||||
auto isCancellationRequested =
|
auto isCancellationRequested =
|
||||||
makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
|
makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
|
||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
@@ -372,7 +372,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check the value of the connection field
|
// Check the value of the connection field
|
||||||
// Some websocket servers (Go/Gorilla?) send lowercase values for the
|
// Some websocket servers (Go/Gorilla?) send lowercase values for the
|
||||||
// connection header, so do a case insensitive comparison
|
// connection header, so do a case insensitive comparison
|
||||||
if (!insensitiveStringCompare(headers["connection"], "Upgrade"))
|
if (!insensitiveStringCompare(headers["connection"], "Upgrade"))
|
||||||
{
|
{
|
||||||
@@ -418,7 +418,7 @@ namespace ix
|
|||||||
// Set the socket to non blocking mode + other tweaks
|
// Set the socket to non blocking mode + other tweaks
|
||||||
SocketConnect::configure(fd);
|
SocketConnect::configure(fd);
|
||||||
|
|
||||||
auto isCancellationRequested =
|
auto isCancellationRequested =
|
||||||
makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
|
makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
|
||||||
|
|
||||||
std::string remote = std::string("remote fd ") + std::to_string(fd);
|
std::string remote = std::string("remote fd ") + std::to_string(fd);
|
||||||
@@ -432,7 +432,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
return sendErrorResponse(400, "Error reading HTTP request line");
|
return sendErrorResponse(400, "Error reading HTTP request line");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate request line (GET /foo HTTP/1.1\r\n)
|
// Validate request line (GET /foo HTTP/1.1\r\n)
|
||||||
auto requestLine = parseRequestLine(line);
|
auto requestLine = parseRequestLine(line);
|
||||||
auto method = std::get<0>(requestLine);
|
auto method = std::get<0>(requestLine);
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
struct WebSocketInitResult
|
struct WebSocketInitResult
|
||||||
{
|
{
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
using WebSocketHttpHeaders = std::unordered_map<std::string, std::string>;
|
using WebSocketHttpHeaders = std::unordered_map<std::string, std::string>;
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
* - Reused zlib compression + decompression bits.
|
* - Reused zlib compression + decompression bits.
|
||||||
* - Refactored to have 2 class for compression and decompression, to allow multi-threading
|
* - Refactored to have 2 class for compression and decompression, to allow multi-threading
|
||||||
* and make sure that _compressBuffer is not shared between threads.
|
* and make sure that _compressBuffer is not shared between threads.
|
||||||
* - Original code wasn't working for some reason, I had to add checks
|
* - Original code wasn't working for some reason, I had to add checks
|
||||||
* for the presence of the kEmptyUncompressedBlock at the end of buffer so that servers
|
* for the presence of the kEmptyUncompressedBlock at the end of buffer so that servers
|
||||||
* would start accepting receiving/decoding compressed messages. Original code was probably
|
* would start accepting receiving/decoding compressed messages. Original code was probably
|
||||||
* modifying the passed in buffers before processing in enabled.hpp ?
|
* modifying the passed in buffers before processing in enabled.hpp ?
|
||||||
@@ -65,13 +65,13 @@ namespace ix
|
|||||||
|
|
||||||
bool WebSocketPerMessageDeflate::init(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions)
|
bool WebSocketPerMessageDeflate::init(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions)
|
||||||
{
|
{
|
||||||
bool clientNoContextTakeover =
|
bool clientNoContextTakeover =
|
||||||
perMessageDeflateOptions.getClientNoContextTakeover();
|
perMessageDeflateOptions.getClientNoContextTakeover();
|
||||||
|
|
||||||
uint8_t deflateBits = perMessageDeflateOptions.getClientMaxWindowBits();
|
uint8_t deflateBits = perMessageDeflateOptions.getClientMaxWindowBits();
|
||||||
uint8_t inflateBits = perMessageDeflateOptions.getServerMaxWindowBits();
|
uint8_t inflateBits = perMessageDeflateOptions.getServerMaxWindowBits();
|
||||||
|
|
||||||
return _compressor->init(deflateBits, clientNoContextTakeover) &&
|
return _compressor->init(deflateBits, clientNoContextTakeover) &&
|
||||||
_decompressor->init(inflateBits, clientNoContextTakeover);
|
_decompressor->init(inflateBits, clientNoContextTakeover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,7 +37,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class WebSocketPerMessageDeflateOptions;
|
class WebSocketPerMessageDeflateOptions;
|
||||||
class WebSocketPerMessageDeflateCompressor;
|
class WebSocketPerMessageDeflateCompressor;
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// The passed in size (4) is important, without it the string litteral
|
// The passed in size (4) is important, without it the string litteral
|
||||||
// is treated as a char* and the null termination (\x00) makes it
|
// is treated as a char* and the null termination (\x00) makes it
|
||||||
// look like an empty string.
|
// look like an empty string.
|
||||||
const std::string kEmptyUncompressedBlock = std::string("\x00\x00\xff\xff", 4);
|
const std::string kEmptyUncompressedBlock = std::string("\x00\x00\xff\xff", 4);
|
||||||
|
|
||||||
@@ -76,16 +76,16 @@ namespace ix
|
|||||||
{
|
{
|
||||||
//
|
//
|
||||||
// 7.2.1. Compression
|
// 7.2.1. Compression
|
||||||
//
|
//
|
||||||
// An endpoint uses the following algorithm to compress a message.
|
// An endpoint uses the following algorithm to compress a message.
|
||||||
//
|
//
|
||||||
// 1. Compress all the octets of the payload of the message using
|
// 1. Compress all the octets of the payload of the message using
|
||||||
// DEFLATE.
|
// DEFLATE.
|
||||||
//
|
//
|
||||||
// 2. If the resulting data does not end with an empty DEFLATE block
|
// 2. If the resulting data does not end with an empty DEFLATE block
|
||||||
// with no compression (the "BTYPE" bits are set to 00), append an
|
// with no compression (the "BTYPE" bits are set to 00), append an
|
||||||
// empty DEFLATE block with no compression to the tail end.
|
// empty DEFLATE block with no compression to the tail end.
|
||||||
//
|
//
|
||||||
// 3. Remove 4 octets (that are 0x00 0x00 0xff 0xff) from the tail end.
|
// 3. Remove 4 octets (that are 0x00 0x00 0xff 0xff) from the tail end.
|
||||||
// After this step, the last octet of the compressed data contains
|
// After this step, the last octet of the compressed data contains
|
||||||
// (possibly part of) the DEFLATE header bits with the "BTYPE" bits
|
// (possibly part of) the DEFLATE header bits with the "BTYPE" bits
|
||||||
@@ -168,14 +168,14 @@ namespace ix
|
|||||||
{
|
{
|
||||||
//
|
//
|
||||||
// 7.2.2. Decompression
|
// 7.2.2. Decompression
|
||||||
//
|
//
|
||||||
// An endpoint uses the following algorithm to decompress a message.
|
// An endpoint uses the following algorithm to decompress a message.
|
||||||
//
|
//
|
||||||
// 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
|
// 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
|
||||||
// payload of the message.
|
// payload of the message.
|
||||||
//
|
//
|
||||||
// 2. Decompress the resulting data using DEFLATE.
|
// 2. Decompress the resulting data using DEFLATE.
|
||||||
//
|
//
|
||||||
std::string inFixed(in);
|
std::string inFixed(in);
|
||||||
inFixed += kEmptyUncompressedBlock;
|
inFixed += kEmptyUncompressedBlock;
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class WebSocketPerMessageDeflateCompressor
|
class WebSocketPerMessageDeflateCompressor
|
||||||
{
|
{
|
||||||
|
@@ -36,7 +36,7 @@ namespace ix
|
|||||||
_serverMaxWindowBits = serverMaxWindowBits;
|
_serverMaxWindowBits = serverMaxWindowBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Four extension parameters are defined for "permessage-deflate" to
|
// Four extension parameters are defined for "permessage-deflate" to
|
||||||
// help endpoints manage per-connection resource usage.
|
// help endpoints manage per-connection resource usage.
|
||||||
//
|
//
|
||||||
@@ -88,9 +88,9 @@ namespace ix
|
|||||||
int x;
|
int x;
|
||||||
ss >> x;
|
ss >> x;
|
||||||
|
|
||||||
// Sanitize values to be in the proper range [8, 15] in
|
// Sanitize values to be in the proper range [8, 15] in
|
||||||
// case a server would give us bogus values
|
// case a server would give us bogus values
|
||||||
_serverMaxWindowBits =
|
_serverMaxWindowBits =
|
||||||
std::min(maxServerMaxWindowBits,
|
std::min(maxServerMaxWindowBits,
|
||||||
std::max(x, minServerMaxWindowBits));
|
std::max(x, minServerMaxWindowBits));
|
||||||
}
|
}
|
||||||
@@ -103,9 +103,9 @@ namespace ix
|
|||||||
int x;
|
int x;
|
||||||
ss >> x;
|
ss >> x;
|
||||||
|
|
||||||
// Sanitize values to be in the proper range [8, 15] in
|
// Sanitize values to be in the proper range [8, 15] in
|
||||||
// case a server would give us bogus values
|
// case a server would give us bogus values
|
||||||
_clientMaxWindowBits =
|
_clientMaxWindowBits =
|
||||||
std::min(maxClientMaxWindowBits,
|
std::min(maxClientMaxWindowBits,
|
||||||
std::max(x, minClientMaxWindowBits));
|
std::max(x, minClientMaxWindowBits));
|
||||||
}
|
}
|
||||||
@@ -162,7 +162,7 @@ namespace ix
|
|||||||
std::string WebSocketPerMessageDeflateOptions::removeSpaces(const std::string& str)
|
std::string WebSocketPerMessageDeflateOptions::removeSpaces(const std::string& str)
|
||||||
{
|
{
|
||||||
std::string out(str);
|
std::string out(str);
|
||||||
out.erase(std::remove_if(out.begin(),
|
out.erase(std::remove_if(out.begin(),
|
||||||
out.end(),
|
out.end(),
|
||||||
[](unsigned char x){ return std::isspace(x); }),
|
[](unsigned char x){ return std::isspace(x); }),
|
||||||
out.end());
|
out.end());
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class WebSocketPerMessageDeflateOptions
|
class WebSocketPerMessageDeflateOptions
|
||||||
{
|
{
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
struct WebSocketSendInfo
|
struct WebSocketSendInfo
|
||||||
{
|
{
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
#include <future>
|
#include <future>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
const int WebSocketServer::kDefaultHandShakeTimeoutSecs(3); // 3 seconds
|
const int WebSocketServer::kDefaultHandShakeTimeoutSecs(3); // 3 seconds
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ namespace ix
|
|||||||
auto status = webSocket->connectToSocket(fd, _handshakeTimeoutSecs);
|
auto status = webSocket->connectToSocket(fd, _handshakeTimeoutSecs);
|
||||||
if (status.success)
|
if (status.success)
|
||||||
{
|
{
|
||||||
// Process incoming messages and execute callbacks
|
// Process incoming messages and execute callbacks
|
||||||
// until the connection is closed
|
// until the connection is closed
|
||||||
webSocket->run();
|
webSocket->run();
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
#include "IXWebSocket.h"
|
#include "IXWebSocket.h"
|
||||||
#include "IXSocketServer.h"
|
#include "IXSocketServer.h"
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
using OnConnectionCallback = std::function<void(std::shared_ptr<WebSocket>)>;
|
using OnConnectionCallback = std::function<void(std::shared_ptr<WebSocket>)>;
|
||||||
|
|
||||||
|
@@ -29,15 +29,12 @@
|
|||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
const std::string WebSocketTransport::kHeartBeatPingMessage("ixwebsocket::hearbeat");
|
const std::string WebSocketTransport::kHeartBeatPingMessage("ixwebsocket::hearbeat");
|
||||||
const int WebSocketTransport::kDefaultHeartBeatPeriod(-1);
|
const int WebSocketTransport::kDefaultHeartBeatPeriod(-1);
|
||||||
constexpr size_t WebSocketTransport::kChunkSize;
|
|
||||||
|
|
||||||
WebSocketTransport::WebSocketTransport() :
|
WebSocketTransport::WebSocketTransport() :
|
||||||
_readyState(CLOSED),
|
_readyState(CLOSED),
|
||||||
@@ -48,7 +45,7 @@ namespace ix
|
|||||||
_heartBeatPeriod(kDefaultHeartBeatPeriod),
|
_heartBeatPeriod(kDefaultHeartBeatPeriod),
|
||||||
_lastSendTimePoint(std::chrono::steady_clock::now())
|
_lastSendTimePoint(std::chrono::steady_clock::now())
|
||||||
{
|
{
|
||||||
_readbuf.resize(kChunkSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketTransport::~WebSocketTransport()
|
WebSocketTransport::~WebSocketTransport()
|
||||||
@@ -132,7 +129,7 @@ namespace ix
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketTransport::ReadyStateValues WebSocketTransport::getReadyState() const
|
WebSocketTransport::ReadyStateValues WebSocketTransport::getReadyState() const
|
||||||
{
|
{
|
||||||
return _readyState;
|
return _readyState;
|
||||||
}
|
}
|
||||||
@@ -156,7 +153,7 @@ namespace ix
|
|||||||
|
|
||||||
void WebSocketTransport::setOnCloseCallback(const OnCloseCallback& onCloseCallback)
|
void WebSocketTransport::setOnCloseCallback(const OnCloseCallback& onCloseCallback)
|
||||||
{
|
{
|
||||||
_onCloseCallback = onCloseCallback;
|
_onCloseCallback = onCloseCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only consider send time points for that computation.
|
// Only consider send time points for that computation.
|
||||||
@@ -176,7 +173,7 @@ namespace ix
|
|||||||
// If (1) heartbeat is enabled, and (2) no data was received or
|
// If (1) heartbeat is enabled, and (2) no data was received or
|
||||||
// send for a duration exceeding our heart-beat period, send a
|
// send for a duration exceeding our heart-beat period, send a
|
||||||
// ping to the server.
|
// ping to the server.
|
||||||
if (pollResult == PollResultType_Timeout &&
|
if (pollResult == PollResultType_Timeout &&
|
||||||
heartBeatPeriodExceeded())
|
heartBeatPeriodExceeded())
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
@@ -185,31 +182,33 @@ namespace ix
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
ssize_t ret = _socket->recv((char*)&_readbuf[0], _readbuf.size());
|
int N = (int) _rxbuf.size();
|
||||||
|
|
||||||
if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
|
_rxbuf.resize(N + 1500);
|
||||||
_socket->getErrno() == EAGAIN))
|
ssize_t ret = _socket->recv((char*)&_rxbuf[0] + N, 1500);
|
||||||
{
|
|
||||||
|
if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
|
||||||
|
_socket->getErrno() == EAGAIN)) {
|
||||||
|
_rxbuf.resize(N);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (ret <= 0)
|
else if (ret <= 0)
|
||||||
{
|
{
|
||||||
_rxbuf.clear();
|
_rxbuf.resize(N);
|
||||||
|
|
||||||
_socket->close();
|
_socket->close();
|
||||||
setReadyState(CLOSED);
|
setReadyState(CLOSED);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_rxbuf.insert(_rxbuf.end(),
|
_rxbuf.resize(N + ret);
|
||||||
_readbuf.begin(),
|
|
||||||
_readbuf.begin() + ret);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSendBufferEmpty() && _readyState == CLOSING)
|
if (isSendBufferEmpty() && _readyState == CLOSING)
|
||||||
{
|
{
|
||||||
_socket->close();
|
_socket->close();
|
||||||
setReadyState(CLOSED);
|
setReadyState(CLOSED);
|
||||||
@@ -283,7 +282,7 @@ namespace ix
|
|||||||
//
|
//
|
||||||
void WebSocketTransport::dispatch(const OnMessageCallback& onMessageCallback)
|
void WebSocketTransport::dispatch(const OnMessageCallback& onMessageCallback)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
wsheader_type ws;
|
wsheader_type ws;
|
||||||
if (_rxbuf.size() < 2) return; /* Need at least 2 */
|
if (_rxbuf.size() < 2) return; /* Need at least 2 */
|
||||||
@@ -295,7 +294,7 @@ namespace ix
|
|||||||
ws.N0 = (data[1] & 0x7f);
|
ws.N0 = (data[1] & 0x7f);
|
||||||
ws.header_size = 2 + (ws.N0 == 126? 2 : 0) + (ws.N0 == 127? 8 : 0) + (ws.mask? 4 : 0);
|
ws.header_size = 2 + (ws.N0 == 126? 2 : 0) + (ws.N0 == 127? 8 : 0) + (ws.mask? 4 : 0);
|
||||||
if (_rxbuf.size() < ws.header_size) return; /* Need: ws.header_size - _rxbuf.size() */
|
if (_rxbuf.size() < ws.header_size) return; /* Need: ws.header_size - _rxbuf.size() */
|
||||||
|
|
||||||
//
|
//
|
||||||
// Calculate payload length:
|
// Calculate payload length:
|
||||||
// 0-125 mean the payload is that long.
|
// 0-125 mean the payload is that long.
|
||||||
@@ -333,7 +332,7 @@ namespace ix
|
|||||||
// invalid payload length according to the spec. bail out
|
// invalid payload length according to the spec. bail out
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ws.mask)
|
if (ws.mask)
|
||||||
{
|
{
|
||||||
ws.masking_key[0] = ((uint8_t) data[i+0]) << 0;
|
ws.masking_key[0] = ((uint8_t) data[i+0]) << 0;
|
||||||
@@ -356,40 +355,22 @@ namespace ix
|
|||||||
|
|
||||||
// We got a whole message, now do something with it:
|
// We got a whole message, now do something with it:
|
||||||
if (
|
if (
|
||||||
ws.opcode == wsheader_type::TEXT_FRAME
|
ws.opcode == wsheader_type::TEXT_FRAME
|
||||||
|| ws.opcode == wsheader_type::BINARY_FRAME
|
|| ws.opcode == wsheader_type::BINARY_FRAME
|
||||||
|| ws.opcode == wsheader_type::CONTINUATION
|
|| ws.opcode == wsheader_type::CONTINUATION
|
||||||
) {
|
) {
|
||||||
unmaskReceiveBuffer(ws);
|
unmaskReceiveBuffer(ws);
|
||||||
|
_receivedData.insert(_receivedData.end(),
|
||||||
|
_rxbuf.begin()+ws.header_size,
|
||||||
|
_rxbuf.begin()+ws.header_size+(size_t)ws.N);// just feed
|
||||||
|
if (ws.fin)
|
||||||
|
{
|
||||||
|
// fire callback with a string message
|
||||||
|
std::string stringMessage(_receivedData.begin(),
|
||||||
|
_receivedData.end());
|
||||||
|
|
||||||
//
|
emitMessage(MSG, stringMessage, ws, onMessageCallback);
|
||||||
// Usual case. Small unfragmented messages
|
_receivedData.clear();
|
||||||
//
|
|
||||||
if (ws.fin && _chunks.empty())
|
|
||||||
{
|
|
||||||
emitMessage(MSG,
|
|
||||||
std::string(_rxbuf.begin()+ws.header_size,
|
|
||||||
_rxbuf.begin()+ws.header_size+(size_t) ws.N),
|
|
||||||
ws,
|
|
||||||
onMessageCallback);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// Add intermediary message to our chunk list.
|
|
||||||
// We use a chunk list instead of a big buffer because resizing
|
|
||||||
// large buffer can be very costly when we need to re-allocate
|
|
||||||
// the internal buffer which is slow and can let the internal OS
|
|
||||||
// receive buffer fill out.
|
|
||||||
//
|
|
||||||
_chunks.emplace_back(
|
|
||||||
std::vector<uint8_t>(_rxbuf.begin()+ws.header_size,
|
|
||||||
_rxbuf.begin()+ws.header_size+(size_t)ws.N));
|
|
||||||
if (ws.fin)
|
|
||||||
{
|
|
||||||
emitMessage(MSG, getMergedChunks(), ws, onMessageCallback);
|
|
||||||
_chunks.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ws.opcode == wsheader_type::PING)
|
else if (ws.opcode == wsheader_type::PING)
|
||||||
@@ -439,33 +420,12 @@ namespace ix
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erase the message that has been processed from the input/read buffer
|
|
||||||
_rxbuf.erase(_rxbuf.begin(),
|
_rxbuf.erase(_rxbuf.begin(),
|
||||||
_rxbuf.begin() + ws.header_size + (size_t) ws.N);
|
_rxbuf.begin() + ws.header_size + (size_t) ws.N);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string WebSocketTransport::getMergedChunks() const
|
void WebSocketTransport::emitMessage(MessageKind messageKind,
|
||||||
{
|
|
||||||
size_t length = 0;
|
|
||||||
for (auto&& chunk : _chunks)
|
|
||||||
{
|
|
||||||
length += chunk.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string msg;
|
|
||||||
msg.reserve(length);
|
|
||||||
|
|
||||||
for (auto&& chunk : _chunks)
|
|
||||||
{
|
|
||||||
std::string str(chunk.begin(), chunk.end());
|
|
||||||
msg += str;
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketTransport::emitMessage(MessageKind messageKind,
|
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
const wsheader_type& ws,
|
const wsheader_type& ws,
|
||||||
const OnMessageCallback& onMessageCallback)
|
const OnMessageCallback& onMessageCallback)
|
||||||
@@ -488,17 +448,15 @@ namespace ix
|
|||||||
unsigned WebSocketTransport::getRandomUnsigned()
|
unsigned WebSocketTransport::getRandomUnsigned()
|
||||||
{
|
{
|
||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
auto seconds =
|
auto seconds =
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(
|
std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
now.time_since_epoch()).count();
|
now.time_since_epoch()).count();
|
||||||
return static_cast<unsigned>(seconds);
|
return static_cast<unsigned>(seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketSendInfo WebSocketTransport::sendData(
|
WebSocketSendInfo WebSocketTransport::sendData(wsheader_type::opcode_type type,
|
||||||
wsheader_type::opcode_type type,
|
const std::string& message,
|
||||||
const std::string& message,
|
bool compress)
|
||||||
bool compress,
|
|
||||||
const OnProgressCallback& onProgressCallback)
|
|
||||||
{
|
{
|
||||||
if (_readyState == CLOSING || _readyState == CLOSED)
|
if (_readyState == CLOSING || _readyState == CLOSED)
|
||||||
{
|
{
|
||||||
@@ -515,81 +473,15 @@ namespace ix
|
|||||||
|
|
||||||
if (compress)
|
if (compress)
|
||||||
{
|
{
|
||||||
if (!_perMessageDeflate.compress(message, compressedMessage))
|
bool success = _perMessageDeflate.compress(message, compressedMessage);
|
||||||
{
|
compressionError = !success;
|
||||||
bool success = false;
|
|
||||||
compressionError = true;
|
|
||||||
payloadSize = 0;
|
|
||||||
wireSize = 0;
|
|
||||||
return WebSocketSendInfo(success, compressionError, payloadSize, wireSize);
|
|
||||||
}
|
|
||||||
compressionError = false;
|
|
||||||
wireSize = compressedMessage.size();
|
wireSize = compressedMessage.size();
|
||||||
|
|
||||||
message_begin = compressedMessage.begin();
|
message_begin = compressedMessage.begin();
|
||||||
message_end = compressedMessage.end();
|
message_end = compressedMessage.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common case for most message. No fragmentation required.
|
uint64_t message_size = wireSize;
|
||||||
if (wireSize < kChunkSize)
|
|
||||||
{
|
|
||||||
sendFragment(type, true, message_begin, message_end, compress);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// Large messages need to be fragmented
|
|
||||||
//
|
|
||||||
// Rules:
|
|
||||||
// First message needs to specify a proper type (BINARY or TEXT)
|
|
||||||
// Intermediary and last messages need to be of type CONTINUATION
|
|
||||||
// Last message must set the fin byte.
|
|
||||||
//
|
|
||||||
auto steps = wireSize / kChunkSize;
|
|
||||||
|
|
||||||
std::string::const_iterator begin = message_begin;
|
|
||||||
std::string::const_iterator end = message_end;
|
|
||||||
|
|
||||||
for (uint64_t i = 0 ; i < steps; ++i)
|
|
||||||
{
|
|
||||||
bool firstStep = i == 0;
|
|
||||||
bool lastStep = (i+1) == steps;
|
|
||||||
bool fin = lastStep;
|
|
||||||
|
|
||||||
end = begin + kChunkSize;
|
|
||||||
if (lastStep)
|
|
||||||
{
|
|
||||||
end = message_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto opcodeType = type;
|
|
||||||
if (!firstStep)
|
|
||||||
{
|
|
||||||
opcodeType = wsheader_type::CONTINUATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send message
|
|
||||||
sendFragment(opcodeType, fin, begin, end, compress);
|
|
||||||
|
|
||||||
if (onProgressCallback && !onProgressCallback(i, steps))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
begin += kChunkSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return WebSocketSendInfo(true, compressionError, payloadSize, wireSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketTransport::sendFragment(wsheader_type::opcode_type type,
|
|
||||||
bool fin,
|
|
||||||
std::string::const_iterator message_begin,
|
|
||||||
std::string::const_iterator message_end,
|
|
||||||
bool compress)
|
|
||||||
{
|
|
||||||
auto message_size = message_end - message_begin;
|
|
||||||
|
|
||||||
unsigned x = getRandomUnsigned();
|
unsigned x = getRandomUnsigned();
|
||||||
uint8_t masking_key[4] = {};
|
uint8_t masking_key[4] = {};
|
||||||
@@ -602,13 +494,7 @@ namespace ix
|
|||||||
header.assign(2 +
|
header.assign(2 +
|
||||||
(message_size >= 126 ? 2 : 0) +
|
(message_size >= 126 ? 2 : 0) +
|
||||||
(message_size >= 65536 ? 6 : 0) + 4, 0);
|
(message_size >= 65536 ? 6 : 0) + 4, 0);
|
||||||
header[0] = type;
|
header[0] = 0x80 | type;
|
||||||
|
|
||||||
// The fin bit indicate that this is the last fragment. Fin is French for end.
|
|
||||||
if (fin)
|
|
||||||
{
|
|
||||||
header[0] |= 0x80;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This bit indicate that the frame is compressed
|
// This bit indicate that the frame is compressed
|
||||||
if (compress)
|
if (compress)
|
||||||
@@ -625,7 +511,7 @@ namespace ix
|
|||||||
header[4] = masking_key[2];
|
header[4] = masking_key[2];
|
||||||
header[5] = masking_key[3];
|
header[5] = masking_key[3];
|
||||||
}
|
}
|
||||||
else if (message_size < 65536)
|
else if (message_size < 65536)
|
||||||
{
|
{
|
||||||
header[1] = 126 | 0x80;
|
header[1] = 126 | 0x80;
|
||||||
header[2] = (message_size >> 8) & 0xff;
|
header[2] = (message_size >> 8) & 0xff;
|
||||||
@@ -660,6 +546,8 @@ namespace ix
|
|||||||
|
|
||||||
// Now actually send this data
|
// Now actually send this data
|
||||||
sendOnSocket();
|
sendOnSocket();
|
||||||
|
|
||||||
|
return WebSocketSendInfo(true, compressionError, payloadSize, wireSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketSendInfo WebSocketTransport::sendPing(const std::string& message)
|
WebSocketSendInfo WebSocketTransport::sendPing(const std::string& message)
|
||||||
@@ -668,13 +556,9 @@ namespace ix
|
|||||||
return sendData(wsheader_type::PING, message, compress);
|
return sendData(wsheader_type::PING, message, compress);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketSendInfo WebSocketTransport::sendBinary(
|
WebSocketSendInfo WebSocketTransport::sendBinary(const std::string& message)
|
||||||
const std::string& message,
|
|
||||||
const OnProgressCallback& onProgressCallback)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
return sendData(wsheader_type::BINARY_FRAME, message,
|
return sendData(wsheader_type::BINARY_FRAME, message, _enablePerMessageDeflate);
|
||||||
_enablePerMessageDeflate, onProgressCallback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketTransport::sendOnSocket()
|
void WebSocketTransport::sendOnSocket()
|
||||||
@@ -685,7 +569,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
ssize_t ret = _socket->send((char*)&_txbuf[0], _txbuf.size());
|
ssize_t ret = _socket->send((char*)&_txbuf[0], _txbuf.size());
|
||||||
|
|
||||||
if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
|
if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
|
||||||
_socket->getErrno() == EAGAIN))
|
_socket->getErrno() == EAGAIN))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
|
@@ -16,7 +16,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#include "IXWebSocketSendInfo.h"
|
#include "IXWebSocketSendInfo.h"
|
||||||
#include "IXWebSocketPerMessageDeflate.h"
|
#include "IXWebSocketPerMessageDeflate.h"
|
||||||
@@ -24,9 +23,8 @@
|
|||||||
#include "IXWebSocketHttpHeaders.h"
|
#include "IXWebSocketHttpHeaders.h"
|
||||||
#include "IXCancellationRequest.h"
|
#include "IXCancellationRequest.h"
|
||||||
#include "IXWebSocketHandshake.h"
|
#include "IXWebSocketHandshake.h"
|
||||||
#include "IXProgressCallback.h"
|
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class Socket;
|
class Socket;
|
||||||
|
|
||||||
@@ -68,8 +66,7 @@ namespace ix
|
|||||||
int timeoutSecs);
|
int timeoutSecs);
|
||||||
|
|
||||||
void poll();
|
void poll();
|
||||||
WebSocketSendInfo sendBinary(const std::string& message,
|
WebSocketSendInfo sendBinary(const std::string& message);
|
||||||
const OnProgressCallback& onProgressCallback);
|
|
||||||
WebSocketSendInfo sendPing(const std::string& message);
|
WebSocketSendInfo sendPing(const std::string& message);
|
||||||
void close();
|
void close();
|
||||||
ReadyStateValues getReadyState() const;
|
ReadyStateValues getReadyState() const;
|
||||||
@@ -79,6 +76,7 @@ namespace ix
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _url;
|
std::string _url;
|
||||||
|
std::string _origin;
|
||||||
|
|
||||||
struct wsheader_type {
|
struct wsheader_type {
|
||||||
unsigned header_size;
|
unsigned header_size;
|
||||||
@@ -98,31 +96,13 @@ namespace ix
|
|||||||
uint8_t masking_key[4];
|
uint8_t masking_key[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Buffer for reading from our socket. That buffer is never resized.
|
|
||||||
std::vector<uint8_t> _readbuf;
|
|
||||||
|
|
||||||
// Contains all messages that were fetched in the last socket read.
|
|
||||||
// This could be a mix of control messages (Close, Ping, etc...) and
|
|
||||||
// data messages. That buffer
|
|
||||||
std::vector<uint8_t> _rxbuf;
|
std::vector<uint8_t> _rxbuf;
|
||||||
|
|
||||||
// Contains all messages that are waiting to be sent
|
|
||||||
std::vector<uint8_t> _txbuf;
|
std::vector<uint8_t> _txbuf;
|
||||||
mutable std::mutex _txbufMutex;
|
mutable std::mutex _txbufMutex;
|
||||||
|
std::vector<uint8_t> _receivedData;
|
||||||
|
|
||||||
// Hold fragments for multi-fragments messages in a list. We support receiving very large
|
|
||||||
// messages (tested messages up to 700M) and we cannot put them in a single
|
|
||||||
// buffer that is resized, as this operation can be slow when a buffer has its
|
|
||||||
// size increased 2 fold, while appending to a list has a fixed cost.
|
|
||||||
std::list<std::vector<uint8_t>> _chunks;
|
|
||||||
|
|
||||||
// Fragments are 32K long
|
|
||||||
static constexpr size_t kChunkSize = 1 << 15;
|
|
||||||
|
|
||||||
// Underlying TCP socket
|
|
||||||
std::shared_ptr<Socket> _socket;
|
std::shared_ptr<Socket> _socket;
|
||||||
|
|
||||||
// Hold the state of the connection (OPEN, CLOSED, etc...)
|
|
||||||
std::atomic<ReadyStateValues> _readyState;
|
std::atomic<ReadyStateValues> _readyState;
|
||||||
|
|
||||||
OnCloseCallback _onCloseCallback;
|
OnCloseCallback _onCloseCallback;
|
||||||
@@ -131,14 +111,13 @@ namespace ix
|
|||||||
size_t _closeWireSize;
|
size_t _closeWireSize;
|
||||||
mutable std::mutex _closeDataMutex;
|
mutable std::mutex _closeDataMutex;
|
||||||
|
|
||||||
// Data used for Per Message Deflate compression (with zlib)
|
|
||||||
WebSocketPerMessageDeflate _perMessageDeflate;
|
WebSocketPerMessageDeflate _perMessageDeflate;
|
||||||
WebSocketPerMessageDeflateOptions _perMessageDeflateOptions;
|
WebSocketPerMessageDeflateOptions _perMessageDeflateOptions;
|
||||||
std::atomic<bool> _enablePerMessageDeflate;
|
std::atomic<bool> _enablePerMessageDeflate;
|
||||||
|
|
||||||
// Used to cancel dns lookup + socket connect + http upgrade
|
// Used to cancel dns lookup + socket connect + http upgrade
|
||||||
std::atomic<bool> _requestInitCancellation;
|
std::atomic<bool> _requestInitCancellation;
|
||||||
|
|
||||||
// Optional Heartbeat
|
// Optional Heartbeat
|
||||||
int _heartBeatPeriod;
|
int _heartBeatPeriod;
|
||||||
static const int kDefaultHeartBeatPeriod;
|
static const int kDefaultHeartBeatPeriod;
|
||||||
@@ -150,18 +129,11 @@ namespace ix
|
|||||||
bool heartBeatPeriodExceeded();
|
bool heartBeatPeriodExceeded();
|
||||||
|
|
||||||
void sendOnSocket();
|
void sendOnSocket();
|
||||||
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
bool compress,
|
bool compress);
|
||||||
const OnProgressCallback& onProgressCallback = nullptr);
|
|
||||||
|
|
||||||
void sendFragment(wsheader_type::opcode_type type,
|
void emitMessage(MessageKind messageKind,
|
||||||
bool fin,
|
|
||||||
std::string::const_iterator begin,
|
|
||||||
std::string::const_iterator end,
|
|
||||||
bool compress);
|
|
||||||
|
|
||||||
void emitMessage(MessageKind messageKind,
|
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
const wsheader_type& ws,
|
const wsheader_type& ws,
|
||||||
const OnMessageCallback& onMessageCallback);
|
const OnMessageCallback& onMessageCallback);
|
||||||
@@ -176,7 +148,5 @@ namespace ix
|
|||||||
|
|
||||||
unsigned getRandomUnsigned();
|
unsigned getRandomUnsigned();
|
||||||
void unmaskReceiveBuffer(const wsheader_type& ws);
|
void unmaskReceiveBuffer(const wsheader_type& ws);
|
||||||
|
|
||||||
std::string getMergedChunks() const;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
17
makefile
17
makefile
@@ -1,21 +1,14 @@
|
|||||||
#
|
#
|
||||||
# This makefile is just used to easily work with docker (linux build)
|
# This makefile is just used to easily work with docker (linux build)
|
||||||
#
|
#
|
||||||
all: brew
|
all: run
|
||||||
|
|
||||||
brew:
|
|
||||||
mkdir -p build && (cd build ; cmake .. ; make -j install)
|
|
||||||
|
|
||||||
.PHONY: docker
|
.PHONY: docker
|
||||||
docker:
|
docker:
|
||||||
docker build -t broadcast_server:latest .
|
docker build -t ws_connect:latest .
|
||||||
|
|
||||||
run:
|
run: docker
|
||||||
docker run --cap-add sys_ptrace -it broadcast_server:latest bash
|
docker run --cap-add sys_ptrace -it ws_connect:latest bash
|
||||||
|
|
||||||
# this is helpful to remove trailing whitespaces
|
|
||||||
trail:
|
|
||||||
sh third_party/remove_trailing_whitespaces.sh
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
(cd examples/satori_publisher ; mkdir -p build ; cd build ; cmake .. ; make)
|
(cd examples/satori_publisher ; mkdir -p build ; cd build ; cmake .. ; make)
|
||||||
@@ -31,7 +24,7 @@ test_server:
|
|||||||
(cd test && npm i ws && node broadcast-server.js)
|
(cd test && npm i ws && node broadcast-server.js)
|
||||||
|
|
||||||
# env TEST=Websocket_server make test
|
# env TEST=Websocket_server make test
|
||||||
# env TEST=Websocket_chat make test
|
# env TEST=websocket_server make test
|
||||||
# env TEST=heartbeat make test
|
# env TEST=heartbeat make test
|
||||||
test:
|
test:
|
||||||
python test/run.py
|
python test/run.py
|
||||||
|
@@ -18,14 +18,13 @@ add_subdirectory(${PROJECT_SOURCE_DIR}/.. ixwebsocket)
|
|||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${PROJECT_SOURCE_DIR}/Catch2/single_include
|
${PROJECT_SOURCE_DIR}/Catch2/single_include
|
||||||
../third_party/msgpack11
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Shared sources
|
# Shared sources
|
||||||
set (SOURCES
|
set (SOURCES
|
||||||
test_runner.cpp
|
test_runner.cpp
|
||||||
IXTest.cpp
|
IXTest.cpp
|
||||||
../third_party/msgpack11/msgpack11.cpp
|
msgpack11.cpp
|
||||||
|
|
||||||
IXDNSLookupTest.cpp
|
IXDNSLookupTest.cpp
|
||||||
IXSocketTest.cpp
|
IXSocketTest.cpp
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "IXTest.h"
|
#include "IXTest.h"
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
using namespace ix;
|
using namespace ix;
|
||||||
|
@@ -57,7 +57,7 @@ namespace ix
|
|||||||
std::string generateSessionId()
|
std::string generateSessionId()
|
||||||
{
|
{
|
||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
auto seconds =
|
auto seconds =
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(
|
std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
now.time_since_epoch()).count();
|
now.time_since_epoch()).count();
|
||||||
|
|
||||||
@@ -69,11 +69,11 @@ namespace ix
|
|||||||
Logger() << msg;
|
Logger() << msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getAnyFreePort()
|
int getFreePort()
|
||||||
{
|
{
|
||||||
int defaultPort = 8090;
|
int defaultPort = 8090;
|
||||||
|
|
||||||
int sockfd;
|
int sockfd;
|
||||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||||
{
|
{
|
||||||
log("Cannot compute a free port. socket error.");
|
log("Cannot compute a free port. socket error.");
|
||||||
@@ -117,23 +117,4 @@ namespace ix
|
|||||||
|
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getFreePort()
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int port = getAnyFreePort();
|
|
||||||
|
|
||||||
//
|
|
||||||
// Only port above 1024 can be used by non root users, but for some
|
|
||||||
// reason I got port 7 returned with macOS when binding on port 0...
|
|
||||||
//
|
|
||||||
if (port > 1024)
|
|
||||||
{
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@ namespace
|
|||||||
int _port;
|
int _port;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocketClient::WebSocketClient(int port)
|
WebSocketClient::WebSocketClient(int port)
|
||||||
: _port(port)
|
: _port(port)
|
||||||
{
|
{
|
||||||
;
|
;
|
||||||
@@ -56,7 +56,7 @@ namespace
|
|||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "ws://localhost:"
|
ss << "ws://localhost:"
|
||||||
<< _port
|
<< _port
|
||||||
<< "/";
|
<< "/";
|
||||||
|
|
||||||
url = ss.str();
|
url = ss.str();
|
||||||
@@ -64,7 +64,7 @@ namespace
|
|||||||
|
|
||||||
_webSocket.setUrl(url);
|
_webSocket.setUrl(url);
|
||||||
|
|
||||||
// The important bit for this test.
|
// 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 hearbeat ; if no traffic is present on the connection for 1 second
|
||||||
// a ping message will be sent by the client.
|
// a ping message will be sent by the client.
|
||||||
_webSocket.setHeartBeatPeriod(1);
|
_webSocket.setHeartBeatPeriod(1);
|
||||||
|
@@ -11,8 +11,7 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <queue>
|
||||||
#include <mutex>
|
|
||||||
#include <ixwebsocket/IXWebSocket.h>
|
#include <ixwebsocket/IXWebSocket.h>
|
||||||
#include <ixwebsocket/IXWebSocketServer.h>
|
#include <ixwebsocket/IXWebSocketServer.h>
|
||||||
#include "msgpack11.hpp"
|
#include "msgpack11.hpp"
|
||||||
@@ -40,11 +39,9 @@ namespace
|
|||||||
|
|
||||||
void sendMessage(const std::string& text);
|
void sendMessage(const std::string& text);
|
||||||
size_t getReceivedMessagesCount() const;
|
size_t getReceivedMessagesCount() const;
|
||||||
const std::vector<std::string>& getReceivedMessages() const;
|
|
||||||
|
|
||||||
std::string encodeMessage(const std::string& text);
|
std::string encodeMessage(const std::string& text);
|
||||||
std::pair<std::string, std::string> decodeMessage(const std::string& str);
|
std::pair<std::string, std::string> decodeMessage(const std::string& str);
|
||||||
void appendMessage(const std::string& message);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _user;
|
std::string _user;
|
||||||
@@ -53,8 +50,7 @@ namespace
|
|||||||
|
|
||||||
ix::WebSocket _webSocket;
|
ix::WebSocket _webSocket;
|
||||||
|
|
||||||
std::vector<std::string> _receivedMessages;
|
std::queue<std::string> _receivedQueue;
|
||||||
mutable std::mutex _mutex;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocketChat::WebSocketChat(const std::string& user,
|
WebSocketChat::WebSocketChat(const std::string& user,
|
||||||
@@ -69,20 +65,7 @@ namespace
|
|||||||
|
|
||||||
size_t WebSocketChat::getReceivedMessagesCount() const
|
size_t WebSocketChat::getReceivedMessagesCount() const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_mutex);
|
return _receivedQueue.size();
|
||||||
return _receivedMessages.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<std::string>& WebSocketChat::getReceivedMessages() const
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(_mutex);
|
|
||||||
return _receivedMessages;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebSocketChat::appendMessage(const std::string& message)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(_mutex);
|
|
||||||
_receivedMessages.push_back(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketChat::isReady() const
|
bool WebSocketChat::isReady() const
|
||||||
@@ -101,9 +84,8 @@ namespace
|
|||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "ws://localhost:"
|
ss << "ws://localhost:"
|
||||||
<< _port
|
<< _port
|
||||||
<< "/"
|
<< "/";
|
||||||
<< _user;
|
|
||||||
|
|
||||||
url = ss.str();
|
url = ss.str();
|
||||||
}
|
}
|
||||||
@@ -145,16 +127,10 @@ namespace
|
|||||||
// as we do for the satori chat example.
|
// as we do for the satori chat example.
|
||||||
|
|
||||||
// store text
|
// store text
|
||||||
appendMessage(result.second);
|
_receivedQueue.push(result.second);
|
||||||
|
|
||||||
std::string payload = result.second;
|
ss << std::endl
|
||||||
if (payload.size() > 2000)
|
<< result.first << " > " << result.second
|
||||||
{
|
|
||||||
payload = "<message too large>";
|
|
||||||
}
|
|
||||||
|
|
||||||
ss << std::endl
|
|
||||||
<< result.first << " > " << payload
|
|
||||||
<< std::endl
|
<< std::endl
|
||||||
<< _user << " > ";
|
<< _user << " > ";
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
@@ -293,36 +269,15 @@ TEST_CASE("Websocket_chat", "[websocket_chat]")
|
|||||||
chatB.sendMessage("from B1");
|
chatB.sendMessage("from B1");
|
||||||
chatB.sendMessage("from B2");
|
chatB.sendMessage("from B2");
|
||||||
|
|
||||||
// Test large messages that needs to be broken into small fragments
|
// Give us 1s for all messages to be received
|
||||||
size_t size = 1 * 1024 * 1024; // ~1Mb
|
ix::msleep(1000);
|
||||||
std::string bigMessage(size, 'a');
|
|
||||||
chatB.sendMessage(bigMessage);
|
|
||||||
|
|
||||||
log("Sent all messages");
|
|
||||||
|
|
||||||
// Wait until all messages are received. 10s timeout
|
|
||||||
int attempts = 0;
|
|
||||||
while (chatA.getReceivedMessagesCount() != 3 ||
|
|
||||||
chatB.getReceivedMessagesCount() != 3)
|
|
||||||
{
|
|
||||||
REQUIRE(attempts++ < 10);
|
|
||||||
ix::msleep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
chatA.stop();
|
chatA.stop();
|
||||||
chatB.stop();
|
chatB.stop();
|
||||||
|
|
||||||
REQUIRE(chatA.getReceivedMessagesCount() == 3);
|
REQUIRE(chatA.getReceivedMessagesCount() == 2);
|
||||||
REQUIRE(chatB.getReceivedMessagesCount() == 3);
|
REQUIRE(chatB.getReceivedMessagesCount() == 3);
|
||||||
|
|
||||||
REQUIRE(chatB.getReceivedMessages()[0] == "from A1");
|
|
||||||
REQUIRE(chatB.getReceivedMessages()[1] == "from A2");
|
|
||||||
REQUIRE(chatB.getReceivedMessages()[2] == "from A3");
|
|
||||||
|
|
||||||
REQUIRE(chatA.getReceivedMessages()[0] == "from B1");
|
|
||||||
REQUIRE(chatA.getReceivedMessages()[1] == "from B2");
|
|
||||||
REQUIRE(chatA.getReceivedMessages()[2].size() == bigMessage.size());
|
|
||||||
|
|
||||||
// Give us 500ms for the server to notice that clients went away
|
// Give us 500ms for the server to notice that clients went away
|
||||||
ix::msleep(500);
|
ix::msleep(500);
|
||||||
REQUIRE(server.getClients().size() == 0);
|
REQUIRE(server.getClients().size() == 0);
|
||||||
|
@@ -20,7 +20,7 @@ if osName == 'Windows':
|
|||||||
else:
|
else:
|
||||||
generator = ''
|
generator = ''
|
||||||
make = 'make -j6'
|
make = 'make -j6'
|
||||||
testBinary ='./ixwebsocket_unittest'
|
testBinary ='./ixwebsocket_unittest'
|
||||||
|
|
||||||
sanitizersFlags = {
|
sanitizersFlags = {
|
||||||
'asan': '-DSANITIZE_ADDRESS=On',
|
'asan': '-DSANITIZE_ADDRESS=On',
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#include <ixwebsocket/IXSocket.h>
|
#include <ixwebsocket/IXSocket.h>
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
ix::Socket::init(); // for Windows
|
ix::Socket::init(); // for Windows
|
||||||
|
|
||||||
|
4641
third_party/cli11/CLI11.hpp
vendored
4641
third_party/cli11/CLI11.hpp
vendored
File diff suppressed because it is too large
Load Diff
2
third_party/remove_trailing_whitespaces.sh
vendored
2
third_party/remove_trailing_whitespaces.sh
vendored
@@ -1,2 +0,0 @@
|
|||||||
find . -type f -name '*.cpp' -exec sed -i '' 's/[[:space:]]*$//' {} \+
|
|
||||||
find . -type f -name '*.h' -exec sed -i '' 's/[[:space:]]*$//' {} \+
|
|
1
ws/.gitignore
vendored
1
ws/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
build
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user