Compare commits

..

6 Commits

40 changed files with 193 additions and 509 deletions

View File

@ -2,7 +2,7 @@ name: linux
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
jobs: jobs:
linux: linux:

View File

@ -2,7 +2,7 @@ name: linux_asan
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
jobs: jobs:
linux: linux:

View File

@ -2,7 +2,7 @@ name: mac_tsan_mbedtls
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
jobs: jobs:
mac_tsan_mbedtls: mac_tsan_mbedtls:

View File

@ -2,7 +2,7 @@ name: mac_tsan_openssl
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
jobs: jobs:
mac_tsan_openssl: mac_tsan_openssl:

View File

@ -2,7 +2,7 @@ name: mac_tsan_sectransport
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
jobs: jobs:
mac_tsan_sectransport: mac_tsan_sectransport:

View File

@ -2,7 +2,7 @@ name: uwp
on: on:
push: push:
paths-ignore: paths-ignore:
- 'docs/**' - './**'
jobs: jobs:
uwp: uwp:

View File

@ -14,7 +14,7 @@ jobs:
- run: | - run: |
mkdir build mkdir build
cd build cd build
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 .. cmake -GNinja -DCMAKE_CXX_COMPILER=cl.exe -DCMAKE_C_COMPILER=cl.exe -DUSE_WS=1 -DUSE_TEST=1 -DUSE_ZLIB=0 ..
- run: | - run: |
cd build cd build
ninja ninja

View File

@ -1,27 +0,0 @@
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
ctest -V
# ninja test
#- run: ../build/test/ixwebsocket_unittest.exe
# working-directory: test

1
.gitignore vendored
View File

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

View File

@ -12,8 +12,6 @@ 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()
@ -113,7 +111,6 @@ 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)
@ -147,7 +144,7 @@ if (USE_TLS)
endif() endif()
endif() endif()
add_library( ixwebsocket add_library( ixwebsocket STATIC
${IXWEBSOCKET_SOURCES} ${IXWEBSOCKET_SOURCES}
${IXWEBSOCKET_HEADERS} ${IXWEBSOCKET_HEADERS}
) )
@ -194,7 +191,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()
@ -276,8 +273,3 @@ 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,11 +15,9 @@ 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++11 --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 platforms
*/ */
#include <ixwebsocket/IXNetSystem.h> #include <ixwebsocket/IXNetSystem.h>
@ -46,12 +44,10 @@ 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;
} }
} }
); );
@ -62,16 +58,13 @@ 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");
// Display a prompt while (true)
std::cout << "> " << std::flush;
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); std::string text;
std::cout << "> " << std::flush; std::cout << "> " << std::flush;
std::getline(std::cin, text);
webSocket.send(text);
} }
return 0; return 0;
@ -84,8 +77,6 @@ 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.
@ -96,7 +87,6 @@ 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
@ -106,7 +96,6 @@ 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.
@ -124,10 +113,6 @@ 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,46 +2,6 @@
All changes to this project will be documented in this file. All changes to this project will be documented in this file.
## [11.2.3] - 2021-03-24
(ssl + windows) missing include for CertOpenStore function
## [11.2.2] - 2021-03-23
(ixwebsocket) version bump
## [11.2.1] - 2021-03-23
(ixwebsocket) version bump
## [11.2.0] - 2021-03-23
(ixwebsocket) correct mingw support (gcc on windows)
## [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,24 +256,11 @@ 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 The waiting time is capped by default at 10s between 2 attempts, but that value can be changed and queried.
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
@ -347,10 +334,6 @@ 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();
@ -418,10 +401,6 @@ 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,12 +24,6 @@
#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,22 +10,16 @@
namespace ix namespace ix
{ {
uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount, uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count,
uint32_t maxWaitBetweenReconnectionRetries, uint32_t maxWaitBetweenReconnectionRetries)
uint32_t minWaitBetweenReconnectionRetries)
{ {
uint32_t waitTime = (retryCount < 26) ? (std::pow(2, retryCount) * 100) : 0; uint32_t wait_time = (retry_count < 26) ? (std::pow(2, retry_count) * 100) : 0;
if (waitTime < minWaitBetweenReconnectionRetries) if (wait_time > maxWaitBetweenReconnectionRetries || wait_time == 0)
{ {
waitTime = minWaitBetweenReconnectionRetries; wait_time = maxWaitBetweenReconnectionRetries;
} }
if (waitTime > maxWaitBetweenReconnectionRetries || waitTime == 0) return wait_time;
{
waitTime = maxWaitBetweenReconnectionRetries;
}
return waitTime;
} }
} // namespace ix } // namespace ix

View File

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

View File

@ -124,156 +124,4 @@ namespace ix
#endif #endif
} }
//
// mingw does not have inet_ntop, which were taken as is from the musl C library.
//
const char* inet_ntop(int af, const void* a0, char* s, socklen_t l)
{
#if defined(_WIN32) && defined(__GNUC__)
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;
#else
return ::inet_ntop(af, a0, s, l);
#endif
}
static int hexval(unsigned c)
{
if (c - '0' < 10) return c - '0';
c |= 32;
if (c - 'a' < 6) return c - 'a' + 10;
return -1;
}
//
// mingw does not have inet_pton, which were taken as is from the musl C library.
//
int inet_pton(int af, const char* s, void* a0)
{
#if defined(_WIN32) && defined(__GNUC__)
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;
#else
return ::inet_pton(af, s, a0);
#endif
}
} // namespace ix } // namespace ix

View File

@ -7,11 +7,6 @@
#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>
@ -21,22 +16,6 @@
// 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>
@ -65,7 +44,4 @@ namespace ix
bool uninitNetSystem(); bool uninitNetSystem();
int poll(struct pollfd* fds, nfds_t nfds, int timeout); int poll(struct pollfd* fds, nfds_t nfds, int timeout);
const char* inet_ntop(int af, const void* src, char* dst, socklen_t size);
int inet_pton(int af, const char* src, void* dst);
} // namespace ix } // namespace ix

View File

@ -37,7 +37,6 @@ 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;
@ -52,7 +51,6 @@ namespace ix
__except (EXCEPTION_EXECUTE_HANDLER) __except (EXCEPTION_EXECUTE_HANDLER)
{ {
} }
#endif
} }
#endif #endif

View File

@ -16,11 +16,6 @@
#include "IXSocketConnect.h" #include "IXSocketConnect.h"
#include <string.h> #include <string.h>
#ifdef _WIN32
// For manipulating the certificate store
#include <wincrypt.h>
#endif
namespace ix namespace ix
{ {
SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd) SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd)

View File

@ -24,11 +24,6 @@
#endif #endif
#define socketerrno errno #define socketerrno errno
#ifdef _WIN32
// For manipulating the certificate store
#include <wincrypt.h>
#endif
#ifdef _WIN32 #ifdef _WIN32
namespace namespace
{ {

View File

@ -104,7 +104,7 @@ namespace ix
server.sin_family = _addressFamily; server.sin_family = _addressFamily;
server.sin_port = htons(_port); server.sin_port = htons(_port);
if (ix::inet_pton(_addressFamily, _host.c_str(), &server.sin_addr.s_addr) <= 0) if (inet_pton(_addressFamily, _host.c_str(), &server.sin_addr.s_addr) <= 0)
{ {
std::stringstream ss; std::stringstream ss;
ss << "SocketServer::listen() error calling inet_pton " ss << "SocketServer::listen() error calling inet_pton "
@ -133,7 +133,7 @@ namespace ix
server.sin6_family = _addressFamily; server.sin6_family = _addressFamily;
server.sin6_port = htons(_port); server.sin6_port = htons(_port);
if (ix::inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= 0) if (inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= 0)
{ {
std::stringstream ss; std::stringstream ss;
ss << "SocketServer::listen() error calling inet_pton " ss << "SocketServer::listen() error calling inet_pton "
@ -338,7 +338,7 @@ namespace ix
if (_addressFamily == AF_INET) if (_addressFamily == AF_INET)
{ {
char remoteIp4[INET_ADDRSTRLEN]; char remoteIp4[INET_ADDRSTRLEN];
if (ix::inet_ntop(AF_INET, &client.sin_addr, remoteIp4, INET_ADDRSTRLEN) == nullptr) if (inet_ntop(AF_INET, &client.sin_addr, remoteIp4, INET_ADDRSTRLEN) == nullptr)
{ {
int err = Socket::getErrno(); int err = Socket::getErrno();
std::stringstream ss; std::stringstream ss;
@ -357,8 +357,7 @@ namespace ix
else // AF_INET6 else // AF_INET6
{ {
char remoteIp6[INET6_ADDRSTRLEN]; char remoteIp6[INET6_ADDRSTRLEN];
if (ix::inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) == if (inet_ntop(AF_INET6, &client.sin_addr, remoteIp6, INET6_ADDRSTRLEN) == nullptr)
nullptr)
{ {
int err = Socket::getErrno(); int err = Socket::getErrno();
std::stringstream ss; std::stringstream ss;

View File

@ -14,7 +14,7 @@ namespace ix
bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char& c1, bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char& c1,
const unsigned char& c2) const const unsigned char& c2) const
{ {
#if defined(_WIN32) && !defined(__GNUC__) #ifdef _WIN32
return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale()); return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale());
#else #else
return std::tolower(c1) < std::tolower(c2); return std::tolower(c1) < std::tolower(c2);

View File

@ -22,14 +22,12 @@ 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)
@ -58,11 +56,6 @@ 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);
@ -138,24 +131,12 @@ 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
@ -232,9 +213,7 @@ namespace ix
return status; return status;
} }
WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs)
int timeoutSecs,
bool enablePerMessageDeflate)
{ {
{ {
std::lock_guard<std::mutex> lock(_configMutex); std::lock_guard<std::mutex> lock(_configMutex);
@ -242,8 +221,7 @@ namespace ix
_perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs); _perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs);
} }
WebSocketInitResult status = WebSocketInitResult status = _ws.connectToSocket(std::move(socket), timeoutSecs);
_ws.connectToSocket(std::move(socket), timeoutSecs, enablePerMessageDeflate);
if (!status.success) if (!status.success)
{ {
return status; return status;
@ -325,10 +303,8 @@ namespace ix
if (_automaticReconnection) if (_automaticReconnection)
{ {
duration = duration = millis(calculateRetryWaitMilliseconds(
millis(calculateRetryWaitMilliseconds(retries++, retries++, _maxWaitBetweenReconnectionRetries));
_maxWaitBetweenReconnectionRetries,
_minWaitBetweenReconnectionRetries));
connectErr.wait_time = duration.count(); connectErr.wait_time = duration.count();
connectErr.retries = retries; connectErr.retries = retries;

View File

@ -58,7 +58,6 @@ 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();
@ -101,9 +100,7 @@ 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:
@ -117,9 +114,7 @@ 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>, WebSocketInitResult connectToSocket(std::unique_ptr<Socket>, int timeoutSecs);
int timeoutSecs,
bool enablePerMessageDeflate);
WebSocketTransport _ws; WebSocketTransport _ws;
@ -142,9 +137,7 @@ 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,8 +241,7 @@ 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;
@ -339,7 +338,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() && enablePerMessageDeflate) if (webSocketPerMessageDeflateOptions.enabled())
{ {
_enablePerMessageDeflate = true; _enablePerMessageDeflate = true;

View File

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

View File

@ -97,10 +97,9 @@ namespace ix
} }
else if (_onClientMessageCallback) else if (_onClientMessageCallback)
{ {
WebSocket* webSocketRawPtr = webSocket.get();
webSocket->setOnMessageCallback( webSocket->setOnMessageCallback(
[this, webSocketRawPtr, connectionState](const WebSocketMessagePtr& msg) { [this, &ws = *webSocket.get(), connectionState](const WebSocketMessagePtr& msg) {
_onClientMessageCallback(connectionState, *webSocketRawPtr, msg); _onClientMessageCallback(connectionState, ws, msg);
}); });
} }
else else
@ -129,8 +128,7 @@ namespace ix
_clients.insert(webSocket); _clients.insert(webSocket);
} }
auto status = webSocket->connectToSocket( auto status = webSocket->connectToSocket(std::move(socket), _handshakeTimeoutSecs);
std::move(socket), _handshakeTimeoutSecs, _enablePerMessageDeflate);
if (status.success) if (status.success)
{ {
// Process incoming messages and execute callbacks // Process incoming messages and execute callbacks
@ -170,46 +168,4 @@ 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,9 +47,6 @@ 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,8 +169,7 @@ 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);
@ -187,7 +186,7 @@ namespace ix
_perMessageDeflateOptions, _perMessageDeflateOptions,
_enablePerMessageDeflate); _enablePerMessageDeflate);
auto result = webSocketHandshake.serverHandshake(timeoutSecs, enablePerMessageDeflate); auto result = webSocketHandshake.serverHandshake(timeoutSecs);
if (result.success) if (result.success)
{ {
setReadyState(ReadyState::OPEN); setReadyState(ReadyState::OPEN);

View File

@ -83,9 +83,7 @@ namespace ix
int timeoutSecs); int timeoutSecs);
// Server // Server
WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket, int timeoutSecs);
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.2.3" #define IX_WEBSOCKET_VERSION "11.0.8"

View File

@ -9,8 +9,6 @@
* $ 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>
@ -37,12 +35,10 @@ 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;
} }
} }
); );
@ -53,16 +49,13 @@ 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");
// Display a prompt while (true)
std::cout << "> " << std::flush;
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); std::string text;
std::cout << "> " << std::flush; std::cout << "> " << std::flush;
std::getline(std::cin, text);
webSocket.send(text);
} }
return 0; return 0;

View File

@ -113,7 +113,7 @@ test_server:
test: test:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 ..) mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 ..)
(cd build ; ninja) (cd build ; ninja)
(cd build ; ninja -v test) (cd build ; ninja test)
test_asan: test_asan:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer") mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer")

View File

@ -33,7 +33,11 @@ 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, [] { return false; }); struct addrinfo* res = dnsLookup->resolve(errMsg,
[]
{
return false;
});
std::cerr << "Error message: " << errMsg << std::endl; std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res == nullptr); REQUIRE(res == nullptr);
} }
@ -44,7 +48,11 @@ 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, [] { return true; }); struct addrinfo* res = dnsLookup->resolve(errMsg,
[]
{
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,7 +29,6 @@ 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")
@ -40,7 +39,7 @@ namespace ix
headers["Upgrade"] = "webSocket"; headers["Upgrade"] = "webSocket";
REQUIRE(!CaseInsensitiveLess::cmp(headers["upGRADE"], "webSocket")); REQUIRE(CaseInsensitiveLess::cmp(headers["upgrade"], "WebSocket") == 0);
} }
} }

View File

@ -139,9 +139,8 @@ namespace ix
std::streamoff size = file.tellg(); std::streamoff size = file.tellg();
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.reserve((size_t) size); memblock.resize((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>());
return memblock; return memblock;
} }

View File

@ -1639,10 +1639,7 @@ 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) { if (hOut==INVALID_HANDLE_VALUE) goto fatal;
errno = ENOTTY;
return false;
}
} }
DWORD consolemodeOut; DWORD consolemodeOut;

166
ws/ws.cpp
View File

@ -55,17 +55,16 @@ 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.reserve((size_t) size); memblock.resize((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>());
return std::make_pair(true, memblock); return std::make_pair(true, memblock);
} }
@ -87,9 +86,9 @@ namespace
std::streamoff size = file.tellg(); std::streamoff size = file.tellg();
file.seekg(0, file.beg); file.seekg(0, file.beg);
memblock.reserve((size_t) size); memblock.resize(size);
memblock.insert(
memblock.begin(), std::istream_iterator<char>(file), std::istream_iterator<char>()); file.read((char*) &memblock.front(), static_cast<std::streamsize>(size));
std::string bytes(memblock.begin(), memblock.end()); std::string bytes(memblock.begin(), memblock.end());
return bytes; return bytes;
@ -440,6 +439,93 @@ 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
@ -902,11 +988,8 @@ 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];
ix::inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN); inet_ntop(AF_INET, &addr, str, INET_ADDRSTRLEN);
spdlog::info("host: {} ip: {}", hostname, str); spdlog::info("host: {} ip: {}", hostname, str);
@ -1425,18 +1508,10 @@ 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 << response->body; out.write((char*) &response->body.front(), response->body.size());
} out.close();
} }
else else
{ {
@ -1893,8 +1968,7 @@ 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);
std::string contentAsString(content.begin(), content.end()); out.write((char*) &content.front(), content.size());
out << contentAsString;
out.close(); out.close();
spdlog::info("ws_receive: Renaming {} to {}", filenameTmp, filename); spdlog::info("ws_receive: Renaming {} to {}", filenameTmp, filename);
@ -2082,6 +2156,23 @@ 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);
@ -2175,8 +2266,7 @@ 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");
auto res = load(filename); content = load(filename);
content = res.second;
} }
_id = uuid4(); _id = uuid4();
@ -2372,9 +2462,9 @@ namespace ix
else else
{ {
std::string readyStateString = std::string readyStateString =
readyState == ReadyState::Connecting ? "Connecting" readyState == ReadyState::Connecting
: readyState == ReadyState::Closing ? "Closing" ? "Connecting"
: "Closed"; : readyState == ReadyState::Closing ? "Closing" : "Closed";
size_t bufferedAmount = client->bufferedAmount(); size_t bufferedAmount = client->bufferedAmount();
spdlog::info( spdlog::info(
@ -2487,7 +2577,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 = 10 * 1000; // 10 seconds uint32_t maxWaitBetweenReconnectionRetries;
int pingIntervalSecs = 30; int pingIntervalSecs = 30;
int runCount = 1; int runCount = 1;
@ -2763,19 +2853,9 @@ 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") || app.got_subcommand("broadcast_server")) else if (app.got_subcommand("transfer"))
{ {
ix::WebSocketServer server(port, hostname); ret = ix::ws_transfer_main(port, hostname, tlsOptions);
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"))
{ {
@ -2790,6 +2870,10 @@ 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);