Compare commits

...

47 Commits

Author SHA1 Message Date
449c5fa138 (ixwebsocket) add getMinWaitBetweenReconnectionRetries 2021-03-23 08:29:50 -07:00
b6234ff908 compile errors due to missing changes for the introduction of setMinWaitBetweenReconnectionRetries and getMinWaitBetweenReconnectionRetries 2021-03-23 08:28:40 -07:00
d26664fccc (ixwebsocket) New option to set the min wait between reconnection attempts. Still default to 1ms. (setMinWaitBetweenReconnectionRetries). 2021-03-23 07:33:48 -07:00
def0243d6d (ws) initialize maxWaitBetweenReconnectionRetries to a non zero value ; a zero value was causing spurious reconnections attempts 2021-03-22 21:10:18 -07:00
1410797d6f document -DBUILD_DEMO=ON 2021-03-22 08:51:58 -07:00
2670187fe0 Adds demo building option. (#278) 2021-03-22 08:47:10 -07:00
95359461d7 enable test on windows + gcc 2021-03-21 10:22:44 -07:00
4d7b149649 mingw: cast fixes 2021-03-21 10:16:06 -07:00
b29a37ce76 mingw: inet_ntop and inet_pton compilation fix, use correct parameter names 2021-03-21 09:50:15 -07:00
9a4dfb40da mingw: add real implementation of inet_ntop and inet_pton taken from musl C library 2021-03-21 09:43:16 -07:00
c4c344518d disable shared-libs on windows where test does not work yet 2021-03-20 10:18:10 -07:00
d706a4a73e doc: document BUILD_SHARED_LIBS 2021-03-20 09:50:21 -07:00
88970604e3 ixwebsocketserver::broadcast server to return a boolean to know whether the server could start/listen, and use that in ws 2021-03-19 11:52:41 -07:00
7fee54464e WebSocketServer::listenAndStart: fix branch where we do not return an integer 2021-03-19 11:48:21 -07:00
1c7634d075 ws: cannot use << with an std::vector 2021-03-19 11:43:29 -07:00
99f9556aa9 ws + mingw: uses << operator to write file to disk in WebSocketReceiver::handleMessage 2021-03-19 11:39:14 -07:00
39b2a3d6df ws curl -O mingw compile fix + detect when we cannot extract a filename from the url to save file to disk with -O option 2021-03-19 11:35:25 -07:00
056b02a494 ws: WebSocketSender uses anonymous namespace load instead of its own method 2021-03-19 11:25:48 -07:00
48166a9a72 mingw: fix compile errors with linenoise and fstream 2021-03-19 11:18:55 -07:00
b36a2d1faa mingw compile fix / remove restrict in inet_* functions 2021-03-19 10:58:38 -07:00
968cc5c1c4 reference wslay as alternative C websocket library 2021-03-19 08:05:01 -07:00
0813eb1788 mention disablePerMessageDeflate in the doc 2021-03-16 09:56:08 -07:00
cadb8336f2 add reference to Teleport which is using ixwebsockets 2021-03-16 09:10:36 -07:00
7fd782f72f add WIN32_LEAN_AND_MEAN windows blip 2021-03-15 19:58:18 -07:00
85bcdaaec3 stub inet_ntop and inet_pton function that mingw does not have 2021-03-14 14:25:40 -07:00
461641f3d0 ci with unity build for windows + gcc 2021-03-14 13:23:16 -07:00
2d65c27d11 rename windows+gcc unittest ci file 2021-03-14 13:18:09 -07:00
6a7785d9d9 no set thread name on mingw 2021-03-13 19:02:20 -08:00
78a670e0c8 more mingw quirks 2021-03-13 18:55:30 -08:00
e63ac69ec6 mock poll struct and macro for mingw 2021-03-13 18:49:29 -08:00
afa15d6dcf mingw build problem fix attempt 2021-03-13 18:31:42 -08:00
432a202c07 use https://github.com/marketplace/actions/install-mingw 2021-03-13 18:21:46 -08:00
d609370a85 change compiler 2021-03-13 18:16:21 -08:00
bbe3a766f4 ci windows_gcc, disable zlib 2021-03-13 18:04:00 -08:00
09d3520b66 Create windows_gcc.yml 2021-03-13 18:00:32 -08:00
f090c7659b (ixwebsocket) Expose setHandshakeTimeout method 2021-03-07 19:29:28 -08:00
7c195219cd reorder methods in IXWebSocket.h 2021-03-07 19:25:53 -08:00
d739662a7c Allow customizing the websocket handshake timeout (#264) 2021-03-07 19:23:43 -08:00
e7f7e470e2 Case sensitive link (#269) 2021-03-04 23:04:04 -08:00
d239738ec6 add a.out to .gitignore 2021-02-19 13:51:10 -08:00
c61975bf75 minor improvement to the main.cpp builtin example 2021-02-19 13:50:50 -08:00
39cc0ed32f add comment in WebSocketServer::makeBroadcastServer 2021-01-28 21:04:18 -08:00
22c3a7264e ws: document bug in ws dnslookup command 2021-01-21 15:07:47 -08:00
ee5a2eb46e mention C++11 compatibility in the readme 2021-01-03 11:48:10 -08:00
f6e34e4b34 stop using C++14 lambda capture init, code should be C++11 compatible 2021-01-03 11:44:05 -08:00
d0359a1764 new makeBroadcastServer websocket server method for classic servers, used by ws 2021-01-03 11:24:12 -08:00
8910ebcc3c enable some unittests on windows 2020-12-26 12:44:06 -08:00
31 changed files with 488 additions and 186 deletions

View File

@ -10,11 +10,17 @@ jobs:
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-vsdevenv@master - uses: seanmiddleditch/gha-setup-vsdevenv@master
- uses: seanmiddleditch/gha-setup-ninja@master
- run: | - run: |
mkdir build mkdir build
cd build cd build
cmake -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DUSE_TEST=1 -DUSE_ZLIB=0 .. cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=c:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_C_COMPILER=cl.exe -DUSE_TEST=1 -DUSE_ZLIB=0 ..
- run: cmake --build build - run: |
cd build
ninja
- run: |
cd build
ninja test
# #
# Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg # Windows with OpenSSL is working but disabled as it takes 13 minutes (10 for openssl) to build with vcpkg

View File

@ -10,11 +10,17 @@ jobs:
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-vsdevenv@master - uses: seanmiddleditch/gha-setup-vsdevenv@master
- uses: seanmiddleditch/gha-setup-ninja@master
- run: | - run: |
mkdir build mkdir build
cd build cd build
cmake -DCMAKE_CXX_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 .. cmake -GNinja -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_C_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=OFF -DBUILD_SHARED_LIBS=OFF ..
- run: cmake --build build - run: |
cd build
ninja
- run: |
cd build
ninja test
#- run: ../build/test/ixwebsocket_unittest.exe #- run: ../build/test/ixwebsocket_unittest.exe
# working-directory: test # working-directory: test

View File

@ -0,0 +1,26 @@
name: windows_gcc
on:
push:
paths-ignore:
- 'docs/**'
jobs:
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master
- uses: egor-tensin/setup-mingw@v2
- run: |
mkdir build
cd build
cmake -GNinja -DCMAKE_CXX_COMPILER=c++ -DCMAKE_C_COMPILER=cc -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 -DCMAKE_UNITY_BUILD=ON ..
- run: |
cd build
ninja
- run: |
cd build
ninja test
#- run: ../build/test/ixwebsocket_unittest.exe
# working-directory: test

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ ws/.certs/
ws/.srl ws/.srl
ixhttpd ixhttpd
makefile makefile
a.out

View File

@ -12,6 +12,8 @@ set (CMAKE_CXX_STANDARD 11)
set (CXX_STANDARD_REQUIRED ON) set (CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF) set (CMAKE_CXX_EXTENSIONS OFF)
option (BUILD_DEMO OFF)
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif() endif()
@ -111,6 +113,7 @@ set( IXWEBSOCKET_HEADERS
ixwebsocket/IXWebSocketVersion.h ixwebsocket/IXWebSocketVersion.h
) )
option(BUILD_SHARED_LIBS "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)" OFF)
option(USE_TLS "Enable TLS support" FALSE) option(USE_TLS "Enable TLS support" FALSE)
if (USE_TLS) if (USE_TLS)
@ -144,7 +147,7 @@ if (USE_TLS)
endif() endif()
endif() endif()
add_library( ixwebsocket STATIC add_library( ixwebsocket
${IXWEBSOCKET_SOURCES} ${IXWEBSOCKET_SOURCES}
${IXWEBSOCKET_HEADERS} ${IXWEBSOCKET_HEADERS}
) )
@ -191,7 +194,7 @@ if (USE_TLS)
target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES}) target_link_libraries(ixwebsocket ${MBEDTLS_LIBRARIES})
elseif (USE_SECURE_TRANSPORT) elseif (USE_SECURE_TRANSPORT)
message(STATUS "TLS configured to use secure transport") message(STATUS "TLS configured to use secure transport")
target_link_libraries(ixwebsocket "-framework foundation" "-framework security") target_link_libraries(ixwebsocket "-framework Foundation" "-framework Security")
endif() endif()
endif() endif()
@ -273,3 +276,8 @@ if (USE_WS OR USE_TEST)
add_subdirectory(test) add_subdirectory(test)
endif() endif()
endif() endif()
if (BUILD_DEMO)
add_executable(demo main.cpp)
target_link_libraries(demo ixwebsocket)
endif()

View File

@ -15,9 +15,11 @@ A bad security bug affecting users compiling with SSL enabled and OpenSSL as the
* Super simple standalone example. See ws folder, unittest and doc/usage.md for more. * Super simple standalone example. See ws folder, unittest and doc/usage.md for more.
* *
* On macOS * On macOS
* $ mkdir -p build ; cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install * $ mkdir -p build ; (cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install)
* $ clang++ --std=c++14 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation * $ clang++ --std=c++11 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation
* $ ./a.out * $ ./a.out
*
* Or use cmake -DBUILD_DEMO=ON option for other platforms
*/ */
#include <ixwebsocket/IXNetSystem.h> #include <ixwebsocket/IXNetSystem.h>
@ -44,10 +46,12 @@ int main()
if (msg->type == ix::WebSocketMessageType::Message) if (msg->type == ix::WebSocketMessageType::Message)
{ {
std::cout << "received message: " << msg->str << std::endl; std::cout << "received message: " << msg->str << std::endl;
std::cout << "> " << std::flush;
} }
else if (msg->type == ix::WebSocketMessageType::Open) else if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::cout << "Connection established" << std::endl; std::cout << "Connection established" << std::endl;
std::cout << "> " << std::flush;
} }
} }
); );
@ -58,13 +62,16 @@ int main()
// Send a message to the server (default to TEXT mode) // Send a message to the server (default to TEXT mode)
webSocket.send("hello world"); webSocket.send("hello world");
while (true) // Display a prompt
{
std::string text;
std::cout << "> " << std::flush; std::cout << "> " << std::flush;
std::getline(std::cin, text);
std::string text;
// Read text from the console and send messages in text mode.
// Exit with Ctrl-D on Unix or Ctrl-Z on Windows.
while (std::getline(std::cin, text))
{
webSocket.send(text); webSocket.send(text);
std::cout << "> " << std::flush;
} }
return 0; return 0;
@ -77,6 +84,8 @@ IXWebSocket is actively being developed, check out the [changelog](https://machi
IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version. See the current [test results](https://bsergean.github.io/autobahn/reports/clients/index.html). Some tests are still failing in the server code. IXWebSocket client code is autobahn compliant beginning with the 6.0.0 version. See the current [test results](https://bsergean.github.io/autobahn/reports/clients/index.html). Some tests are still failing in the server code.
Starting with the 11.0.8 release, IXWebSocket should be fully C++11 compatible.
## Users ## Users
If your company or project is using this library, feel free to open an issue or PR to amend this list. If your company or project is using this library, feel free to open an issue or PR to amend this list.
@ -87,6 +96,7 @@ If your company or project is using this library, feel free to open an issue or
- [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod - [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod
- [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper - [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper
- [discord.cpp](https://github.com/luccanunes/discord.cpp), a discord library for making bots - [discord.cpp](https://github.com/luccanunes/discord.cpp), a discord library for making bots
- [Teleport](http://teleportconnect.com/), Teleport is your own personal remote robot avatar
## Alternative libraries ## Alternative libraries
@ -96,6 +106,7 @@ There are plenty of great websocket libraries out there, which might work for yo
* [beast](https://github.com/boostorg/beast) - C++ * [beast](https://github.com/boostorg/beast) - C++
* [libwebsockets](https://libwebsockets.org/) - C * [libwebsockets](https://libwebsockets.org/) - C
* [µWebSockets](https://github.com/uNetworking/uWebSockets) - C * [µWebSockets](https://github.com/uNetworking/uWebSockets) - C
* [wslay](https://github.com/tatsuhiro-t/wslay) - C
[uvweb](https://github.com/bsergean/uvweb) is a library written by the IXWebSocket author which is built on top of [uvw](https://github.com/skypjack/uvw), which is a C++ wrapper for [libuv](https://libuv.org/). It has more dependencies and does not support SSL at this point, but it can be used to open multiple connections within a single OS thread thanks to libuv. [uvweb](https://github.com/bsergean/uvweb) is a library written by the IXWebSocket author which is built on top of [uvw](https://github.com/skypjack/uvw), which is a C++ wrapper for [libuv](https://libuv.org/). It has more dependencies and does not support SSL at this point, but it can be used to open multiple connections within a single OS thread thanks to libuv.
@ -113,6 +124,10 @@ To check the performance of a websocket library, you can look at the [autoroute]
| UWP | Disabled | None | [![Build2][6]][0] | | UWP | Disabled | None | [![Build2][6]][0] |
| Linux | OpenSSL | Address Sanitizer | [![Build2][7]][0] | | Linux | OpenSSL | Address Sanitizer | [![Build2][7]][0] |
* ASAN fails on Linux because of a known problem, we need a
* Some tests are disabled on Windows/UWP because of a pathing problem
* TLS and ZLIB are disabled on Windows/UWP because enabling make the CI run takes a lot of time, for setting up vcpkg.
[0]: https://github.com/machinezone/IXWebSocket [0]: https://github.com/machinezone/IXWebSocket
[1]: https://github.com/machinezone/IXWebSocket/workflows/linux/badge.svg [1]: https://github.com/machinezone/IXWebSocket/workflows/linux/badge.svg
[2]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_sectransport/badge.svg [2]: https://github.com/machinezone/IXWebSocket/workflows/mac_tsan_sectransport/badge.svg

View File

@ -2,6 +2,30 @@
All changes to this project will be documented in this file. All changes to this project will be documented in this file.
## [11.1.4] - 2021-03-23
(ixwebsocket) add getMinWaitBetweenReconnectionRetries
## [11.1.3] - 2021-03-23
(ixwebsocket) New option to set the min wait between reconnection attempts. Still default to 1ms. (setMinWaitBetweenReconnectionRetries).
## [11.1.2] - 2021-03-22
(ws) initialize maxWaitBetweenReconnectionRetries to a non zero value ; a zero value was causing spurious reconnections attempts
## [11.1.1] - 2021-03-20
(cmake) Library can be built as a static or a dynamic library, controlled with BUILD_SHARED_LIBS. Default to static library
## [11.1.0] - 2021-03-16
(ixwebsocket) Use LEAN_AND_MEAN Windows define to help with undefined link error when building a DLL. Support websocket server disablePerMessageDeflate option correctly.
## [11.0.9] - 2021-03-07
(ixwebsocket) Expose setHandshakeTimeout method
## [11.0.8] - 2020-12-25 ## [11.0.8] - 2020-12-25
(ws) trim ws dependencies no more ixcrypto and ixcore deps (ws) trim ws dependencies no more ixcrypto and ixcore deps

View File

@ -17,13 +17,13 @@ There is a unittest which can be executed by typing `make test`.
Options for building: Options for building:
* `-DBUILD_SHARED_LIBS=ON` will build the unittest as a shared libary instead of a static library, which is the default
* `-DUSE_ZLIB=1` will enable zlib support, required for http client + server + websocket per message deflate extension * `-DUSE_ZLIB=1` will enable zlib support, required for http client + server + websocket per message deflate extension
* `-DUSE_TLS=1` will enable TLS support * `-DUSE_TLS=1` will enable TLS support
* `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows) * `-DUSE_OPEN_SSL=1` will use [openssl](https://www.openssl.org/) for the TLS support (default on Linux and Windows)
* `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support * `-DUSE_MBED_TLS=1` will use [mbedlts](https://tls.mbed.org/) for the TLS support
* `-DUSE_WS=1` will build the ws interactive command line tool * `-DUSE_WS=1` will build the ws interactive command line tool
* `-DUSE_TEST=1` will build the unittest * `-DUSE_TEST=1` will build the unittest
* `-DUSE_PYTHON=1` will use Python3 for cobra bots, require Python3 to be installed.
If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file (not maintained much though) or rather the [github actions](https://github.com/machinezone/IXWebSocket/blob/master/.github/workflows/unittest_windows.yml) which have instructions for building dependencies. If you are on Windows, look at the [appveyor](https://github.com/machinezone/IXWebSocket/blob/master/appveyor.yml) file (not maintained much though) or rather the [github actions](https://github.com/machinezone/IXWebSocket/blob/master/.github/workflows/unittest_windows.yml) which have instructions for building dependencies.

View File

@ -256,11 +256,24 @@ Wait time(ms): 6400
Wait time(ms): 10000 Wait time(ms): 10000
``` ```
The waiting time is capped by default at 10s between 2 attempts, but that value can be changed and queried. The waiting time is capped by default at 10s between 2 attempts, but that value
can be changed and queried. The minimum waiting time can also be set.
```cpp ```cpp
webSocket.setMaxWaitBetweenReconnectionRetries(5 * 1000); // 5000ms = 5s webSocket.setMaxWaitBetweenReconnectionRetries(5 * 1000); // 5000ms = 5s
uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries(); uint32_t m = webSocket.getMaxWaitBetweenReconnectionRetries();
webSocket.setMinWaitBetweenReconnectionRetries(1000); // 1000ms = 1s
uint32_t m = webSocket.getMinWaitBetweenReconnectionRetries();
```
## Handshake timeout
You can control how long to wait until timing out while waiting for the websocket handshake to be performed.
```
int handshakeTimeoutSecs = 1;
setHandshakeTimeout(handshakeTimeoutSecs);
``` ```
## WebSocket server API ## WebSocket server API
@ -334,6 +347,10 @@ if (!res.first)
return 1; return 1;
} }
// Per message deflate connection is enabled by default. It can be disabled
// which might be helpful when running on low power devices such as a Rasbery Pi
server.disablePerMessageDeflate();
// Run the server in the background. Server can be stoped by calling server.stop() // Run the server in the background. Server can be stoped by calling server.stop()
server.start(); server.start();
@ -401,6 +418,10 @@ if (!res.first)
return 1; return 1;
} }
// Per message deflate connection is enabled by default. It can be disabled
// which might be helpful when running on low power devices such as a Rasbery Pi
server.disablePerMessageDeflate();
// Run the server in the background. Server can be stoped by calling server.stop() // Run the server in the background. Server can be stoped by calling server.stop()
server.start(); server.start();

View File

@ -24,6 +24,12 @@
#include <string.h> #include <string.h>
#include <thread> #include <thread>
// mingw build quirks
#if defined(_WIN32) && defined(__GNUC__)
#define AI_NUMERICSERV NI_NUMERICSERV
#define AI_ADDRCONFIG LUP_ADDRCONFIG
#endif
namespace ix namespace ix
{ {
const int64_t DNSLookup::kDefaultWait = 1; // ms const int64_t DNSLookup::kDefaultWait = 1; // ms

View File

@ -10,16 +10,22 @@
namespace ix namespace ix
{ {
uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count, uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount,
uint32_t maxWaitBetweenReconnectionRetries) uint32_t maxWaitBetweenReconnectionRetries,
uint32_t minWaitBetweenReconnectionRetries)
{ {
uint32_t wait_time = (retry_count < 26) ? (std::pow(2, retry_count) * 100) : 0; uint32_t waitTime = (retryCount < 26) ? (std::pow(2, retryCount) * 100) : 0;
if (wait_time > maxWaitBetweenReconnectionRetries || wait_time == 0) if (waitTime < minWaitBetweenReconnectionRetries)
{ {
wait_time = maxWaitBetweenReconnectionRetries; waitTime = minWaitBetweenReconnectionRetries;
} }
return wait_time; if (waitTime > maxWaitBetweenReconnectionRetries || waitTime == 0)
{
waitTime = maxWaitBetweenReconnectionRetries;
}
return waitTime;
} }
} // namespace ix } // namespace ix

View File

@ -10,6 +10,7 @@
namespace ix namespace ix
{ {
uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count, uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount,
uint32_t maxWaitBetweenReconnectionRetries); uint32_t maxWaitBetweenReconnectionRetries,
uint32_t minWaitBetweenReconnectionRetries);
} // namespace ix } // namespace ix

View File

@ -125,3 +125,146 @@ namespace ix
} }
} // namespace ix } // namespace ix
//
// mingw does not have inet_ntop and inet_pton, which were taken as is from the musl C library.
//
#if defined(_WIN32) && defined(__GNUC__)
const char* inet_ntop(int af, const void* a0, char* s, socklen_t l)
{
const unsigned char* a = (const unsigned char*) a0;
int i, j, max, best;
char buf[100];
switch (af)
{
case AF_INET:
if (snprintf(s, l, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]) < l) return s;
break;
case AF_INET6:
if (memcmp(a, "\0\0\0\0\0\0\0\0\0\0\377\377", 12))
snprintf(buf,
sizeof buf,
"%x:%x:%x:%x:%x:%x:%x:%x",
256 * a[0] + a[1],
256 * a[2] + a[3],
256 * a[4] + a[5],
256 * a[6] + a[7],
256 * a[8] + a[9],
256 * a[10] + a[11],
256 * a[12] + a[13],
256 * a[14] + a[15]);
else
snprintf(buf,
sizeof buf,
"%x:%x:%x:%x:%x:%x:%d.%d.%d.%d",
256 * a[0] + a[1],
256 * a[2] + a[3],
256 * a[4] + a[5],
256 * a[6] + a[7],
256 * a[8] + a[9],
256 * a[10] + a[11],
a[12],
a[13],
a[14],
a[15]);
/* Replace longest /(^0|:)[:0]{2,}/ with "::" */
for (i = best = 0, max = 2; buf[i]; i++)
{
if (i && buf[i] != ':') continue;
j = strspn(buf + i, ":0");
if (j > max) best = i, max = j;
}
if (max > 3)
{
buf[best] = buf[best + 1] = ':';
memmove(buf + best + 2, buf + best + max, i - best - max + 1);
}
if (strlen(buf) < l)
{
strcpy(s, buf);
return s;
}
break;
default: errno = EAFNOSUPPORT; return 0;
}
errno = ENOSPC;
return 0;
}
static int hexval(unsigned c)
{
if (c - '0' < 10) return c - '0';
c |= 32;
if (c - 'a' < 6) return c - 'a' + 10;
return -1;
}
int inet_pton(int af, const char* s, void* a0)
{
uint16_t ip[8];
unsigned char* a = (unsigned char*) a0;
int i, j, v, d, brk = -1, need_v4 = 0;
if (af == AF_INET)
{
for (i = 0; i < 4; i++)
{
for (v = j = 0; j < 3 && isdigit(s[j]); j++)
v = 10 * v + s[j] - '0';
if (j == 0 || (j > 1 && s[0] == '0') || v > 255) return 0;
a[i] = v;
if (s[j] == 0 && i == 3) return 1;
if (s[j] != '.') return 0;
s += j + 1;
}
return 0;
}
else if (af != AF_INET6)
{
errno = EAFNOSUPPORT;
return -1;
}
if (*s == ':' && *++s != ':') return 0;
for (i = 0;; i++)
{
if (s[0] == ':' && brk < 0)
{
brk = i;
ip[i & 7] = 0;
if (!*++s) break;
if (i == 7) return 0;
continue;
}
for (v = j = 0; j < 4 && (d = hexval(s[j])) >= 0; j++)
v = 16 * v + d;
if (j == 0) return 0;
ip[i & 7] = v;
if (!s[j] && (brk >= 0 || i == 7)) break;
if (i == 7) return 0;
if (s[j] != ':')
{
if (s[j] != '.' || (i < 6 && brk < 0)) return 0;
need_v4 = 1;
i++;
break;
}
s += j + 1;
}
if (brk >= 0)
{
memmove(ip + brk + 7 - i, ip + brk, 2 * (i + 1 - brk));
for (j = 0; j < 7 - i; j++)
ip[brk + j] = 0;
}
for (j = 0; j < 8; j++)
{
*a++ = ip[j] >> 8;
*a++ = ip[j];
}
if (need_v4 && inet_pton(AF_INET, (const char*) s, a - 4) <= 0) return 0;
return 1;
}
#endif // defined(_WIN32) && defined(__GNUC__)

View File

@ -7,6 +7,11 @@
#pragma once #pragma once
#ifdef _WIN32 #ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <WS2tcpip.h> #include <WS2tcpip.h>
#include <WinSock2.h> #include <WinSock2.h>
#include <basetsd.h> #include <basetsd.h>
@ -16,6 +21,22 @@
// Define our own poll on Windows, as a wrapper on top of select // Define our own poll on Windows, as a wrapper on top of select
typedef unsigned long int nfds_t; typedef unsigned long int nfds_t;
// mingw does not know about poll so mock it
#if defined(__GNUC__)
struct pollfd
{
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
#define POLLIN 0x001 /* There is data to read. */
#define POLLOUT 0x004 /* Writing now will not block. */
#define POLLERR 0x008 /* Error condition. */
#define POLLHUP 0x010 /* Hung up. */
#define POLLNVAL 0x020 /* Invalid polling request. */
#endif
#else #else
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h> #include <errno.h>
@ -32,6 +53,12 @@ typedef unsigned long int nfds_t;
#include <unistd.h> #include <unistd.h>
#endif #endif
// mingw does not have those
#if defined(_WIN32) && defined(__GNUC__)
const char* inet_ntop(int af, const void* src, char* dst, socklen_t size);
int inet_pton(int af, const char* src, void* dst);
#endif
namespace ix namespace ix
{ {
#ifdef _WIN32 #ifdef _WIN32

View File

@ -37,6 +37,7 @@ namespace ix
void SetThreadName(DWORD dwThreadID, const char* threadName) void SetThreadName(DWORD dwThreadID, const char* threadName)
{ {
#ifndef __GNUC__
THREADNAME_INFO info; THREADNAME_INFO info;
info.dwType = 0x1000; info.dwType = 0x1000;
info.szName = threadName; info.szName = threadName;
@ -51,6 +52,7 @@ namespace ix
__except (EXCEPTION_EXECUTE_HANDLER) __except (EXCEPTION_EXECUTE_HANDLER)
{ {
} }
#endif
} }
#endif #endif

View File

@ -22,12 +22,14 @@ namespace ix
const int WebSocket::kDefaultPingIntervalSecs(-1); const int WebSocket::kDefaultPingIntervalSecs(-1);
const bool WebSocket::kDefaultEnablePong(true); const bool WebSocket::kDefaultEnablePong(true);
const uint32_t WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s const uint32_t WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s
const uint32_t WebSocket::kDefaultMinWaitBetweenReconnectionRetries(1); // 1 ms
WebSocket::WebSocket() WebSocket::WebSocket()
: _onMessageCallback(OnMessageCallback()) : _onMessageCallback(OnMessageCallback())
, _stop(false) , _stop(false)
, _automaticReconnection(true) , _automaticReconnection(true)
, _maxWaitBetweenReconnectionRetries(kDefaultMaxWaitBetweenReconnectionRetries) , _maxWaitBetweenReconnectionRetries(kDefaultMaxWaitBetweenReconnectionRetries)
, _minWaitBetweenReconnectionRetries(kDefaultMinWaitBetweenReconnectionRetries)
, _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs) , _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs)
, _enablePong(kDefaultEnablePong) , _enablePong(kDefaultEnablePong)
, _pingIntervalSecs(kDefaultPingIntervalSecs) , _pingIntervalSecs(kDefaultPingIntervalSecs)
@ -56,6 +58,11 @@ namespace ix
_url = url; _url = url;
} }
void WebSocket::setHandshakeTimeout(int handshakeTimeoutSecs)
{
_handshakeTimeoutSecs = handshakeTimeoutSecs;
}
void WebSocket::setExtraHeaders(const WebSocketHttpHeaders& headers) void WebSocket::setExtraHeaders(const WebSocketHttpHeaders& headers)
{ {
std::lock_guard<std::mutex> lock(_configMutex); std::lock_guard<std::mutex> lock(_configMutex);
@ -131,12 +138,24 @@ namespace ix
_maxWaitBetweenReconnectionRetries = maxWaitBetweenReconnectionRetries; _maxWaitBetweenReconnectionRetries = maxWaitBetweenReconnectionRetries;
} }
void WebSocket::setMinWaitBetweenReconnectionRetries(uint32_t minWaitBetweenReconnectionRetries)
{
std::lock_guard<std::mutex> lock(_configMutex);
_minWaitBetweenReconnectionRetries = minWaitBetweenReconnectionRetries;
}
uint32_t WebSocket::getMaxWaitBetweenReconnectionRetries() const uint32_t WebSocket::getMaxWaitBetweenReconnectionRetries() const
{ {
std::lock_guard<std::mutex> lock(_configMutex); std::lock_guard<std::mutex> lock(_configMutex);
return _maxWaitBetweenReconnectionRetries; return _maxWaitBetweenReconnectionRetries;
} }
uint32_t WebSocket::getMinWaitBetweenReconnectionRetries() const
{
std::lock_guard<std::mutex> lock(_configMutex);
return _minWaitBetweenReconnectionRetries;
}
void WebSocket::start() void WebSocket::start()
{ {
if (_thread.joinable()) return; // we've already been started if (_thread.joinable()) return; // we've already been started
@ -213,7 +232,9 @@ namespace ix
return status; return status;
} }
WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs) WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket,
int timeoutSecs,
bool enablePerMessageDeflate)
{ {
{ {
std::lock_guard<std::mutex> lock(_configMutex); std::lock_guard<std::mutex> lock(_configMutex);
@ -221,7 +242,8 @@ namespace ix
_perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs); _perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs);
} }
WebSocketInitResult status = _ws.connectToSocket(std::move(socket), timeoutSecs); WebSocketInitResult status =
_ws.connectToSocket(std::move(socket), timeoutSecs, enablePerMessageDeflate);
if (!status.success) if (!status.success)
{ {
return status; return status;
@ -303,8 +325,10 @@ namespace ix
if (_automaticReconnection) if (_automaticReconnection)
{ {
duration = millis(calculateRetryWaitMilliseconds( duration =
retries++, _maxWaitBetweenReconnectionRetries)); millis(calculateRetryWaitMilliseconds(retries++,
_maxWaitBetweenReconnectionRetries,
_minWaitBetweenReconnectionRetries));
connectErr.wait_time = duration.count(); connectErr.wait_time = duration.count();
connectErr.retries = retries; connectErr.retries = retries;

View File

@ -58,6 +58,7 @@ namespace ix
void enablePerMessageDeflate(); void enablePerMessageDeflate();
void disablePerMessageDeflate(); void disablePerMessageDeflate();
void addSubProtocol(const std::string& subProtocol); void addSubProtocol(const std::string& subProtocol);
void setHandshakeTimeout(int handshakeTimeoutSecs);
// Run asynchronously, by calling start and stop. // Run asynchronously, by calling start and stop.
void start(); void start();
@ -100,7 +101,9 @@ namespace ix
void disableAutomaticReconnection(); void disableAutomaticReconnection();
bool isAutomaticReconnectionEnabled() const; bool isAutomaticReconnectionEnabled() const;
void setMaxWaitBetweenReconnectionRetries(uint32_t maxWaitBetweenReconnectionRetries); void setMaxWaitBetweenReconnectionRetries(uint32_t maxWaitBetweenReconnectionRetries);
void setMinWaitBetweenReconnectionRetries(uint32_t minWaitBetweenReconnectionRetries);
uint32_t getMaxWaitBetweenReconnectionRetries() const; uint32_t getMaxWaitBetweenReconnectionRetries() const;
uint32_t getMinWaitBetweenReconnectionRetries() const;
const std::vector<std::string>& getSubProtocols(); const std::vector<std::string>& getSubProtocols();
private: private:
@ -114,7 +117,9 @@ namespace ix
static void invokeTrafficTrackerCallback(size_t size, bool incoming); static void invokeTrafficTrackerCallback(size_t size, bool incoming);
// Server // Server
WebSocketInitResult connectToSocket(std::unique_ptr<Socket>, int timeoutSecs); WebSocketInitResult connectToSocket(std::unique_ptr<Socket>,
int timeoutSecs,
bool enablePerMessageDeflate);
WebSocketTransport _ws; WebSocketTransport _ws;
@ -137,7 +142,9 @@ namespace ix
// Automatic reconnection // Automatic reconnection
std::atomic<bool> _automaticReconnection; std::atomic<bool> _automaticReconnection;
static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries; static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries;
static const uint32_t kDefaultMinWaitBetweenReconnectionRetries;
uint32_t _maxWaitBetweenReconnectionRetries; uint32_t _maxWaitBetweenReconnectionRetries;
uint32_t _minWaitBetweenReconnectionRetries;
// Make the sleeping in the automatic reconnection cancellable // Make the sleeping in the automatic reconnection cancellable
std::mutex _sleepMutex; std::mutex _sleepMutex;

View File

@ -241,7 +241,8 @@ namespace ix
return WebSocketInitResult(true, status, "", headers, path); return WebSocketInitResult(true, status, "", headers, path);
} }
WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs) WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs,
bool enablePerMessageDeflate)
{ {
_requestInitCancellation = false; _requestInitCancellation = false;
@ -338,7 +339,7 @@ namespace ix
WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(header); WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(header);
// If the client has requested that extension, // If the client has requested that extension,
if (webSocketPerMessageDeflateOptions.enabled()) if (webSocketPerMessageDeflateOptions.enabled() && enablePerMessageDeflate)
{ {
_enablePerMessageDeflate = true; _enablePerMessageDeflate = true;

View File

@ -35,7 +35,7 @@ namespace ix
int port, int port,
int timeoutSecs); int timeoutSecs);
WebSocketInitResult serverHandshake(int timeoutSecs); WebSocketInitResult serverHandshake(int timeoutSecs, bool enablePerMessageDeflate);
private: private:
std::string genRandomString(const int len); std::string genRandomString(const int len);

View File

@ -97,9 +97,10 @@ namespace ix
} }
else if (_onClientMessageCallback) else if (_onClientMessageCallback)
{ {
WebSocket* webSocketRawPtr = webSocket.get();
webSocket->setOnMessageCallback( webSocket->setOnMessageCallback(
[this, &ws = *webSocket.get(), connectionState](const WebSocketMessagePtr& msg) { [this, webSocketRawPtr, connectionState](const WebSocketMessagePtr& msg) {
_onClientMessageCallback(connectionState, ws, msg); _onClientMessageCallback(connectionState, *webSocketRawPtr, msg);
}); });
} }
else else
@ -128,7 +129,8 @@ namespace ix
_clients.insert(webSocket); _clients.insert(webSocket);
} }
auto status = webSocket->connectToSocket(std::move(socket), _handshakeTimeoutSecs); auto status = webSocket->connectToSocket(
std::move(socket), _handshakeTimeoutSecs, _enablePerMessageDeflate);
if (status.success) if (status.success)
{ {
// Process incoming messages and execute callbacks // Process incoming messages and execute callbacks
@ -168,4 +170,46 @@ namespace ix
std::lock_guard<std::mutex> lock(_clientsMutex); std::lock_guard<std::mutex> lock(_clientsMutex);
return _clients.size(); return _clients.size();
} }
//
// Classic servers
//
void WebSocketServer::makeBroadcastServer()
{
setOnClientMessageCallback([this](std::shared_ptr<ConnectionState> connectionState,
WebSocket& webSocket,
const WebSocketMessagePtr& msg) {
auto remoteIp = connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Message)
{
for (auto&& client : getClients())
{
if (client.get() != &webSocket)
{
client->send(msg->str, msg->binary);
// Make sure the OS send buffer is flushed before moving on
do
{
size_t bufferedAmount = client->bufferedAmount();
std::chrono::duration<double, std::milli> duration(500);
std::this_thread::sleep_for(duration);
} while (client->bufferedAmount() != 0);
}
}
}
});
}
bool WebSocketServer::listenAndStart()
{
auto res = listen();
if (!res.first)
{
return false;
}
start();
return true;
}
} // namespace ix } // namespace ix

View File

@ -47,6 +47,9 @@ namespace ix
// Get all the connected clients // Get all the connected clients
std::set<std::shared_ptr<WebSocket>> getClients(); std::set<std::shared_ptr<WebSocket>> getClients();
void makeBroadcastServer();
bool listenAndStart();
const static int kDefaultHandShakeTimeoutSecs; const static int kDefaultHandShakeTimeoutSecs;
private: private:

View File

@ -169,7 +169,8 @@ namespace ix
// Server // Server
WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket, WebSocketInitResult WebSocketTransport::connectToSocket(std::unique_ptr<Socket> socket,
int timeoutSecs) int timeoutSecs,
bool enablePerMessageDeflate)
{ {
std::lock_guard<std::mutex> lock(_socketMutex); std::lock_guard<std::mutex> lock(_socketMutex);
@ -186,7 +187,7 @@ namespace ix
_perMessageDeflateOptions, _perMessageDeflateOptions,
_enablePerMessageDeflate); _enablePerMessageDeflate);
auto result = webSocketHandshake.serverHandshake(timeoutSecs); auto result = webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate);
if (result.success) if (result.success)
{ {
setReadyState(ReadyState::OPEN); setReadyState(ReadyState::OPEN);

View File

@ -83,7 +83,9 @@ namespace ix
int timeoutSecs); int timeoutSecs);
// Server // Server
WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs); WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket,
int timeoutSecs,
bool enablePerMessageDeflate);
PollResult poll(); PollResult poll();
WebSocketSendInfo sendBinary(const std::string& message, WebSocketSendInfo sendBinary(const std::string& message,

View File

@ -6,4 +6,4 @@
#pragma once #pragma once
#define IX_WEBSOCKET_VERSION "11.0.8" #define IX_WEBSOCKET_VERSION "11.1.4"

View File

@ -9,6 +9,8 @@
* $ mkdir -p build ; cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install * $ mkdir -p build ; cd build ; cmake -DUSE_TLS=1 .. ; make -j ; make install
* $ clang++ --std=c++14 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation * $ clang++ --std=c++14 --stdlib=libc++ main.cpp -lixwebsocket -lz -framework Security -framework Foundation
* $ ./a.out * $ ./a.out
*
* Or use cmake -DBUILD_DEMO=ON option for other platform
*/ */
#include <ixwebsocket/IXNetSystem.h> #include <ixwebsocket/IXNetSystem.h>
@ -35,10 +37,12 @@ int main()
if (msg->type == ix::WebSocketMessageType::Message) if (msg->type == ix::WebSocketMessageType::Message)
{ {
std::cout << "received message: " << msg->str << std::endl; std::cout << "received message: " << msg->str << std::endl;
std::cout << "> " << std::flush;
} }
else if (msg->type == ix::WebSocketMessageType::Open) else if (msg->type == ix::WebSocketMessageType::Open)
{ {
std::cout << "Connection established" << std::endl; std::cout << "Connection established" << std::endl;
std::cout << "> " << std::flush;
} }
} }
); );
@ -49,13 +53,16 @@ int main()
// Send a message to the server (default to TEXT mode) // Send a message to the server (default to TEXT mode)
webSocket.send("hello world"); webSocket.send("hello world");
while (true) // Display a prompt
{
std::string text;
std::cout << "> " << std::flush; std::cout << "> " << std::flush;
std::getline(std::cin, text);
std::string text;
// Read text from the console and send messages in text mode.
// Exit with Ctrl-D on Unix or Ctrl-Z on Windows.
while (std::getline(std::cin, text))
{
webSocket.send(text); webSocket.send(text);
std::cout << "> " << std::flush;
} }
return 0; return 0;

View File

@ -17,14 +17,11 @@ set (TEST_TARGET_NAMES
IXWebSocketTestConnectionDisconnection IXWebSocketTestConnectionDisconnection
IXUrlParserTest IXUrlParserTest
IXHttpClientTest IXHttpClientTest
IXHttpServerTest
IXUnityBuildsTest IXUnityBuildsTest
IXHttpTest IXHttpTest
IXDNSLookupTest IXDNSLookupTest
IXWebSocketSubProtocolTest IXWebSocketSubProtocolTest
IXWebSocketChatTest
# IXWebSocketBroadcastTest ## FIXME was depending on cobra / take a broadcast server from ws # IXWebSocketBroadcastTest ## FIXME was depending on cobra / take a broadcast server from ws
IXWebSocketPerMessageDeflateCompressorTest
IXStrCaseCompareTest IXStrCaseCompareTest
) )
@ -33,6 +30,17 @@ set (TEST_TARGET_NAMES
if (UNIX) if (UNIX)
list(APPEND TEST_TARGET_NAMES list(APPEND TEST_TARGET_NAMES
IXWebSocketCloseTest IXWebSocketCloseTest
# Fail on Windows in CI probably because the pathing is wrong and
# some resource files cannot be found
IXHttpServerTest
IXWebSocketChatTest
)
endif()
if (USE_ZLIB)
list(APPEND TEST_TARGET_NAMES
IXWebSocketPerMessageDeflateCompressorTest
) )
endif() endif()

View File

@ -33,11 +33,7 @@ TEST_CASE("dns", "[net]")
auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80); auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
std::string errMsg; std::string errMsg;
struct addrinfo* res = dnsLookup->resolve(errMsg, struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return false; });
[]
{
return false;
});
std::cerr << "Error message: " << errMsg << std::endl; std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res == nullptr); REQUIRE(res == nullptr);
} }
@ -48,11 +44,7 @@ TEST_CASE("dns", "[net]")
std::string errMsg; std::string errMsg;
// The callback returning true means we are requesting cancellation // The callback returning true means we are requesting cancellation
struct addrinfo* res = dnsLookup->resolve(errMsg, struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return true; });
[]
{
return true;
});
std::cerr << "Error message: " << errMsg << std::endl; std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res == nullptr); REQUIRE(res == nullptr);
} }

View File

@ -29,6 +29,7 @@ namespace ix
// Comparison should be case insensitive // Comparison should be case insensitive
REQUIRE(httpHeaders["Foo"] == "foo"); REQUIRE(httpHeaders["Foo"] == "foo");
REQUIRE(httpHeaders["Foo"] != "bar");
} }
SECTION("2") SECTION("2")
@ -39,7 +40,7 @@ namespace ix
headers["Upgrade"] = "webSocket"; headers["Upgrade"] = "webSocket";
REQUIRE(CaseInsensitiveLess::cmp(headers["upgrade"], "WebSocket") == 0); REQUIRE(!CaseInsensitiveLess::cmp(headers["upGRADE"], "webSocket"));
} }
} }

View File

@ -139,8 +139,9 @@ namespace ix
std::streamoff size = file.tellg(); std::streamoff size = file.tellg();
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.resize((size_t) size); memblock.reserve((size_t) size);
file.read((char*) &memblock.front(), static_cast<std::streamsize>(size)); memblock.insert(
memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>());
return memblock; return memblock;
} }

View File

@ -1639,7 +1639,10 @@ bool enableRawMode(int fd) {
/* Init windows console handles only once */ /* Init windows console handles only once */
hOut = GetStdHandle(STD_OUTPUT_HANDLE); hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut==INVALID_HANDLE_VALUE) goto fatal; if (hOut==INVALID_HANDLE_VALUE) {
errno = ENOTTY;
return false;
}
} }
DWORD consolemodeOut; DWORD consolemodeOut;

164
ws/ws.cpp
View File

@ -55,16 +55,17 @@ namespace
std::pair<bool, std::vector<uint8_t>> load(const std::string& path) std::pair<bool, std::vector<uint8_t>> load(const std::string& path)
{ {
std::vector<uint8_t> memblock; std::vector<uint8_t> memblock;
std::ifstream file(path); std::ifstream file(path);
if (!file.is_open()) return std::make_pair(false, memblock); if (!file.is_open()) return std::make_pair(false, memblock);
file.seekg(0, file.end); file.seekg(0, file.end);
std::streamoff size = file.tellg(); std::streamoff size = file.tellg();
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.resize((size_t) size); memblock.reserve((size_t) size);
file.read((char*) &memblock.front(), static_cast<std::streamsize>(size)); memblock.insert(
memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>());
return std::make_pair(true, memblock); return std::make_pair(true, memblock);
} }
@ -86,9 +87,9 @@ namespace
std::streamoff size = file.tellg(); std::streamoff size = file.tellg();
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.resize(size); memblock.reserve((size_t) size);
memblock.insert(
file.read((char*) &memblock.front(), static_cast<std::streamsize>(size)); memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>());
std::string bytes(memblock.begin(), memblock.end()); std::string bytes(memblock.begin(), memblock.end());
return bytes; return bytes;
@ -439,93 +440,6 @@ namespace ix
return generateReport(url) ? 0 : 1; return generateReport(url) ? 0 : 1;
} }
//
// broadcast server
//
int ws_broadcast_server_main(int port,
const std::string& hostname,
const ix::SocketTLSOptions& tlsOptions)
{
spdlog::info("Listening on {}:{}", hostname, port);
ix::WebSocketServer server(port, hostname);
server.setTLSOptions(tlsOptions);
server.setOnClientMessageCallback(
[&server](std::shared_ptr<ConnectionState> connectionState,
WebSocket& webSocket,
const WebSocketMessagePtr& msg) {
auto remoteIp = connectionState->getRemoteIp();
if (msg->type == ix::WebSocketMessageType::Open)
{
spdlog::info("New connection");
spdlog::info("remote ip: {}", remoteIp);
spdlog::info("id: {}", connectionState->getId());
spdlog::info("Uri: {}", msg->openInfo.uri);
spdlog::info("Headers:");
for (auto it : msg->openInfo.headers)
{
spdlog::info("{}: {}", it.first, it.second);
}
}
else if (msg->type == ix::WebSocketMessageType::Close)
{
spdlog::info("Closed connection: code {} reason {}",
msg->closeInfo.code,
msg->closeInfo.reason);
}
else if (msg->type == ix::WebSocketMessageType::Error)
{
std::stringstream ss;
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
spdlog::info(ss.str());
}
else if (msg->type == ix::WebSocketMessageType::Fragment)
{
spdlog::info("Received message fragment");
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
spdlog::info("Received {} bytes", msg->wireSize);
for (auto&& client : server.getClients())
{
if (client.get() != &webSocket)
{
client->send(msg->str, msg->binary, [](int current, int total) -> bool {
spdlog::info("Step {} out of {}", current, total);
return true;
});
do
{
size_t bufferedAmount = client->bufferedAmount();
spdlog::info("{} bytes left to be sent", bufferedAmount);
std::chrono::duration<double, std::milli> duration(500);
std::this_thread::sleep_for(duration);
} while (client->bufferedAmount() != 0);
}
}
}
});
auto res = server.listen();
if (!res.first)
{
spdlog::info(res.second);
return 1;
}
server.start();
server.wait();
return 0;
}
/* /*
* ws_chat.cpp * ws_chat.cpp
* Author: Benjamin Sergeant * Author: Benjamin Sergeant
@ -988,6 +902,9 @@ namespace ix
auto addr = res->ai_addr; auto addr = res->ai_addr;
// FIXME: this display weird addresses / we could steal libuv inet.c
// code which display correct results
char str[INET_ADDRSTRLEN]; char str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN); inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN);
@ -1508,10 +1425,18 @@ namespace ix
filename = output; filename = output;
} }
if (filename.empty())
{
spdlog::error("Cannot save content to disk: No output file supplied, and not "
"filename could be extracted from the url {}",
url);
}
else
{
spdlog::info("Writing to disk: {}", filename); spdlog::info("Writing to disk: {}", filename);
std::ofstream out(filename); std::ofstream out(filename);
out.write((char*) &response->body.front(), response->body.size()); out << response->body;
out.close(); }
} }
else else
{ {
@ -1968,7 +1893,8 @@ namespace ix
spdlog::info("ws_receive: Writing to disk: {}", filenameTmp); spdlog::info("ws_receive: Writing to disk: {}", filenameTmp);
std::ofstream out(filenameTmp); std::ofstream out(filenameTmp);
out.write((char*) &content.front(), content.size()); std::string contentAsString(content.begin(), content.end());
out << contentAsString;
out.close(); out.close();
spdlog::info("ws_receive: Renaming {} to {}", filenameTmp, filename); spdlog::info("ws_receive: Renaming {} to {}", filenameTmp, filename);
@ -2156,23 +2082,6 @@ namespace ix
_condition.wait(lock); _condition.wait(lock);
} }
std::vector<uint8_t> load(const std::string& path)
{
std::vector<uint8_t> memblock;
std::ifstream file(path);
if (!file.is_open()) return memblock;
file.seekg(0, file.end);
std::streamoff size = file.tellg();
file.seekg(0, file.beg);
memblock.resize((size_t) size);
file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
return memblock;
}
void WebSocketSender::start() void WebSocketSender::start()
{ {
_webSocket.setUrl(_url); _webSocket.setUrl(_url);
@ -2266,7 +2175,8 @@ namespace ix
std::vector<uint8_t> content; std::vector<uint8_t> content;
{ {
Bench bench("ws_send: load file from disk"); Bench bench("ws_send: load file from disk");
content = load(filename); auto res = load(filename);
content = res.second;
} }
_id = uuid4(); _id = uuid4();
@ -2462,9 +2372,9 @@ namespace ix
else else
{ {
std::string readyStateString = std::string readyStateString =
readyState == ReadyState::Connecting readyState == ReadyState::Connecting ? "Connecting"
? "Connecting" : readyState == ReadyState::Closing ? "Closing"
: readyState == ReadyState::Closing ? "Closing" : "Closed"; : "Closed";
size_t bufferedAmount = client->bufferedAmount(); size_t bufferedAmount = client->bufferedAmount();
spdlog::info( spdlog::info(
@ -2577,7 +2487,7 @@ int main(int argc, char** argv)
int delayMs = -1; int delayMs = -1;
int count = 1; int count = 1;
int msgCount = 1000 * 1000; int msgCount = 1000 * 1000;
uint32_t maxWaitBetweenReconnectionRetries; uint32_t maxWaitBetweenReconnectionRetries = 10 * 1000; // 10 seconds
int pingIntervalSecs = 30; int pingIntervalSecs = 30;
int runCount = 1; int runCount = 1;
@ -2853,9 +2763,19 @@ int main(int argc, char** argv)
ret = ix::ws_push_server( ret = ix::ws_push_server(
port, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong, sendMsg); port, hostname, tlsOptions, ipv6, disablePerMessageDeflate, disablePong, sendMsg);
} }
else if (app.got_subcommand("transfer")) else if (app.got_subcommand("transfer") || app.got_subcommand("broadcast_server"))
{ {
ret = ix::ws_transfer_main(port, hostname, tlsOptions); ix::WebSocketServer server(port, hostname);
server.setTLSOptions(tlsOptions);
server.makeBroadcastServer();
if (!server.listenAndStart())
{
spdlog::error("Error while starting the server");
}
else
{
server.wait();
}
} }
else if (app.got_subcommand("send")) else if (app.got_subcommand("send"))
{ {
@ -2870,10 +2790,6 @@ int main(int argc, char** argv)
{ {
ret = ix::ws_chat_main(url, user); ret = ix::ws_chat_main(url, user);
} }
else if (app.got_subcommand("broadcast_server"))
{
ret = ix::ws_broadcast_server_main(port, hostname, tlsOptions);
}
else if (app.got_subcommand("ping")) else if (app.got_subcommand("ping"))
{ {
ret = ix::ws_ping_pong_main(url, tlsOptions); ret = ix::ws_ping_pong_main(url, tlsOptions);