Compare commits

..

3 Commits

Author SHA1 Message Date
Benjamin Sergeant
4a2a8e7b36 revert fake ci change 2023-02-25 13:57:28 -08:00
Benjamin Sergeant
f073203ac1 Use a deque instead of a vector to avoid an O(n^2) time complexity problem 2023-02-25 13:56:23 -08:00
Benjamin Sergeant
1866d94550 bump ci 2023-02-25 13:53:01 -08:00
36 changed files with 88 additions and 253 deletions

27
.github/workflows/unittest_windows.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: windows
on:
push:
paths-ignore:
- 'docs/**'
pull_request:
jobs:
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-vsdevenv@master
- uses: seanmiddleditch/gha-setup-ninja@master
- run: |
mkdir 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 ..
- run: |
cd build
ninja
- run: |
cd build
ninja test
#- run: ../build/test/ixwebsocket_unittest.exe
# working-directory: test

View File

@ -11,7 +11,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- uses: seanmiddleditch/gha-setup-ninja@master - uses: seanmiddleditch/gha-setup-ninja@master
- uses: bsergean/setup-mingw@d79ce405bac9edef3a1726ef00554a56f0bafe66 - uses: egor-tensin/setup-mingw@v2
- run: | - run: |
mkdir build mkdir build
cd build cd build

2
.gitignore vendored
View File

@ -8,5 +8,3 @@ ws/.srl
ixhttpd ixhttpd
makefile makefile
a.out a.out
.idea/
cmake-build-debug/

View File

@ -6,7 +6,7 @@
cmake_minimum_required(VERSION 3.4.1...3.17.2) cmake_minimum_required(VERSION 3.4.1...3.17.2)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
project(ixwebsocket LANGUAGES C CXX VERSION 11.4.6) project(ixwebsocket C CXX)
set (CMAKE_CXX_STANDARD 11) set (CMAKE_CXX_STANDARD 11)
set (CXX_STANDARD_REQUIRED ON) set (CXX_STANDARD_REQUIRED ON)
@ -136,7 +136,6 @@ if (USE_TLS)
else() # default to OpenSSL on all other platforms else() # default to OpenSSL on all other platforms
if (NOT USE_MBED_TLS) # Unless mbedtls is requested if (NOT USE_MBED_TLS) # Unless mbedtls is requested
set(USE_OPEN_SSL ON) set(USE_OPEN_SSL ON)
set(requires "openssl")
endif() endif()
endif() endif()
@ -166,9 +165,10 @@ if(BUILD_SHARED_LIBS)
${IXWEBSOCKET_SOURCES} ${IXWEBSOCKET_SOURCES}
${IXWEBSOCKET_HEADERS} ${IXWEBSOCKET_HEADERS}
) )
# Set library version # Set library version
set_target_properties(ixwebsocket PROPERTIES VERSION ${PROJECT_VERSION}) set_target_properties(ixwebsocket PROPERTIES VERSION 11.3.2)
else() else()
# Static library # Static library
add_library( ixwebsocket add_library( ixwebsocket
@ -287,13 +287,9 @@ if (IXWEBSOCKET_INSTALL)
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket/ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ixwebsocket/
) )
configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" @ONLY) configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket-config.cmake.in" "${CMAKE_BINARY_DIR}/ixwebsocket-config.cmake" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket") install(FILES "${CMAKE_BINARY_DIR}/ixwebsocket-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket")
set(prefix ${CMAKE_INSTALL_PREFIX})
configure_file("${CMAKE_CURRENT_LIST_DIR}/ixwebsocket.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
install(EXPORT ixwebsocket install(EXPORT ixwebsocket
FILE ixwebsocket-targets.cmake FILE ixwebsocket-targets.cmake
NAMESPACE ixwebsocket:: NAMESPACE ixwebsocket::

View File

@ -1,7 +1,5 @@
## Hello world ## Hello world
(note from the main developer, sadly I don't have too much time to devote to this library anymore, maybe it's time to pass the maintenance to someone else more motivated ?)
IXWebSocket is a C++ library for WebSocket client and server development. It has minimal dependencies (no boost), is very simple to use and support everything you'll likely need for websocket dev (SSL, deflate compression, compiles on most platforms, etc...). HTTP client and server code is also available, but it hasn't received as much testing. IXWebSocket is a C++ library for WebSocket client and server development. It has minimal dependencies (no boost), is very simple to use and support everything you'll likely need for websocket dev (SSL, deflate compression, compiles on most platforms, etc...). HTTP client and server code is also available, but it hasn't received as much testing.
It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android). It was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Two important design goals are simplicity and correctness. It is been used on big mobile video game titles sending and receiving tons of messages since 2017 (iOS and Android). It was tested on macOS, iOS, Linux, Android, Windows and FreeBSD. Two important design goals are simplicity and correctness.
@ -101,16 +99,14 @@ Starting with the 11.0.8 release, IXWebSocket should be fully C++11 compatible.
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.
- [Machine Zone](https://www.mz.com) - [Machine Zone](https://www.mz.com)
- [Tokio](https://github.com/liz3/tokio), a discord library focused on audio playback with node bindings. - [Tokio](https://gitlab.com/HCInk/tokio), a discord library focused on audio playback with node bindings.
- [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), an easy to use Discord-bot framework. - [libDiscordBot](https://github.com/tostc/libDiscordBot/tree/master), an easy to use Discord-bot framework.
- [gwebsocket](https://github.com/norrbotten/gwebsocket), a websocket (lua) module for Garry's Mod - [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 (archived as of Oct 8, 2021) - [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 - [Teleport](http://teleportconnect.com/), Teleport is your own personal remote robot avatar
- [Abaddon](https://github.com/uowuo/abaddon), An alternative Discord client made with C++/gtkmm - [Abaddon](https://github.com/uowuo/abaddon), An alternative Discord client made with C++/gtkmm
- [NovaCoin](https://github.com/novacoin-project/novacoin), a hybrid scrypt PoW + PoS based cryptocurrency. - [NovaCoin](https://github.com/novacoin-project/novacoin), a hybrid scrypt PoW + PoS based cryptocurrency.
- [Candy](https://github.com/lanthora/candy), A WebSocket and TUN based VPN for Linux
- [ITGmania](https://github.com/itgmania/itgmania), a cross platform Dance Dance Revolution-like emulator.
## Alternative libraries ## Alternative libraries
@ -118,8 +114,8 @@ There are plenty of great websocket libraries out there, which might work for yo
* [websocketpp](https://github.com/zaphoyd/websocketpp) - C++ * [websocketpp](https://github.com/zaphoyd/websocketpp) - C++
* [beast](https://github.com/boostorg/beast) - C++ * [beast](https://github.com/boostorg/beast) - C++
* [µWebSockets](https://github.com/uNetworking/uWebSockets) - C++
* [libwebsockets](https://libwebsockets.org/) - C * [libwebsockets](https://libwebsockets.org/) - C
* [µWebSockets](https://github.com/uNetworking/uWebSockets) - C
* [wslay](https://github.com/tatsuhiro-t/wslay) - 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.
@ -137,7 +133,9 @@ To check the performance of a websocket library, you can look at the [autoroute]
| Windows | Disabled | None | [![Build2][5]][0] | | Windows | Disabled | None | [![Build2][5]][0] |
| 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] |
| Mingw | Disabled | None | [![Build2][8]][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 * 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. * TLS and ZLIB are disabled on Windows/UWP because enabling make the CI run takes a lot of time, for setting up vcpkg.
@ -149,4 +147,5 @@ To check the performance of a websocket library, you can look at the [autoroute]
[5]: https://github.com/machinezone/IXWebSocket/workflows/windows/badge.svg [5]: https://github.com/machinezone/IXWebSocket/workflows/windows/badge.svg
[6]: https://github.com/machinezone/IXWebSocket/workflows/uwp/badge.svg [6]: https://github.com/machinezone/IXWebSocket/workflows/uwp/badge.svg
[7]: https://github.com/machinezone/IXWebSocket/workflows/linux_asan/badge.svg [7]: https://github.com/machinezone/IXWebSocket/workflows/linux_asan/badge.svg
[8]: https://github.com/machinezone/IXWebSocket/workflows/unittest_windows_gcc/badge.svg

View File

@ -2,12 +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.4.5] - 2024-06-05
New changes are documented in the Release page in the GitHub repository.
## [11.4.4] - 2023-06-05
## [11.4.3] - 2022-05-13 ## [11.4.3] - 2022-05-13
Set shorter thread names Set shorter thread names

View File

@ -445,17 +445,6 @@ server.wait();
``` ```
### Heartbeat
You can configure an optional heartbeat / keep-alive for the WebSocket server. The heartbeat interval can be adjusted or disabled when constructing the `WebSocketServer`. Setting the interval to `-1` disables the heartbeat feature; this is the default setting. The parameter you set will be applied to every `WebSocket` object that the server creates.
To enable a 45 second heartbeat on a `WebSocketServer`:
```cpp
int pingIntervalSeconds = 45;
ix::WebSocketServer server(port, host, backlog, maxConnections, handshakeTimeoutSecs, addressFamily, pingIntervalSeconds);
```
## HTTP client API ## HTTP client API
```cpp ```cpp

View File

@ -1,11 +0,0 @@
prefix=@prefix@
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: ixwebsocket
Description: websocket and http client and server library, with TLS support and very few dependencies
Version: @CMAKE_PROJECT_VERSION@
Libs: -L${libdir} -lixwebsocket
Cflags: -I${includedir}
Requires: @requires@

View File

@ -27,19 +27,9 @@
// mingw build quirks // mingw build quirks
#if defined(_WIN32) && defined(__GNUC__) #if defined(_WIN32) && defined(__GNUC__)
#ifndef AI_NUMERICSERV
#define AI_NUMERICSERV NI_NUMERICSERV #define AI_NUMERICSERV NI_NUMERICSERV
#endif
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG LUP_ADDRCONFIG #define AI_ADDRCONFIG LUP_ADDRCONFIG
#endif #endif
#endif
#ifdef __APPLE__
#ifndef AI_NUMERICSERV
#define AI_NUMERICSERV 0
#endif
#endif
namespace ix namespace ix
{ {

View File

@ -133,20 +133,16 @@ namespace ix
if (headers.find("Content-Length") != headers.end()) if (headers.find("Content-Length") != headers.end())
{ {
int contentLength = 0; int contentLength = 0;
try
{ {
const char* p = headers["Content-Length"].c_str(); contentLength = std::stoi(headers["Content-Length"]);
char* p_end{};
errno = 0;
long val = std::strtol(p, &p_end, 10);
if (p_end == p // invalid argument
|| errno == ERANGE // out of range
|| val < std::numeric_limits<int>::min()
|| val > std::numeric_limits<int>::max()) {
return std::make_tuple(
false, "Error parsing HTTP Header 'Content-Length'", httpRequest);
}
contentLength = val;
} }
catch (const std::exception&)
{
return std::make_tuple(
false, "Error parsing HTTP Header 'Content-Length'", httpRequest);
}
if (contentLength < 0) if (contentLength < 0)
{ {
return std::make_tuple( return std::make_tuple(

View File

@ -209,12 +209,6 @@ namespace ix
ss << "User-Agent: " << userAgent() << "\r\n"; ss << "User-Agent: " << userAgent() << "\r\n";
} }
// Set an origin header if missing
if (args->extraHeaders.find("Origin") == args->extraHeaders.end())
{
ss << "Origin: " << protocol << "://" << host << ":" << port << "\r\n";
}
if (verb == kPost || verb == kPut || verb == kPatch || _forceBody) if (verb == kPost || verb == kPut || verb == kPatch || _forceBody)
{ {
// Set request compression header // Set request compression header

View File

@ -148,7 +148,6 @@ namespace ix
content = gzipCompress(content); content = gzipCompress(content);
headers["Content-Encoding"] = "gzip"; headers["Content-Encoding"] = "gzip";
} }
headers["Accept-Encoding"] = "gzip";
#endif #endif
// Log request // Log request
@ -162,6 +161,11 @@ namespace ix
// headers["Content-Type"] = "application/octet-stream"; // headers["Content-Type"] = "application/octet-stream";
headers["Accept-Ranges"] = "none"; headers["Accept-Ranges"] = "none";
for (auto&& it : request->headers)
{
headers[it.first] = it.second;
}
return std::make_shared<HttpResponse>( return std::make_shared<HttpResponse>(
200, "OK", HttpErrorCode::Ok, headers, content); 200, "OK", HttpErrorCode::Ok, headers, content);
}); });

View File

@ -69,7 +69,7 @@ namespace ix
{ {
// We must deselect the networkevents from the socket event. Otherwise the // We must deselect the networkevents from the socket event. Otherwise the
// socket will report states that aren't there. // socket will report states that aren't there.
if (_fd != nullptr && (int)_fd->fd != -1) if (_fd != nullptr && _fd->fd != -1)
WSAEventSelect(_fd->fd, _event, 0); WSAEventSelect(_fd->fd, _event, 0);
WSACloseEvent(_event); WSACloseEvent(_event);
} }
@ -171,7 +171,7 @@ namespace ix
int count = 0; int count = 0;
// WSAWaitForMultipleEvents returns the index of the first signaled event. And to emulate WSAPoll() // WSAWaitForMultipleEvents returns the index of the first signaled event. And to emulate WSAPoll()
// all the signaled events must be processed. // all the signaled events must be processed.
while (socketIndex < (int)socketEvents.size()) while (socketIndex < socketEvents.size())
{ {
struct pollfd* fd = socketEvents[socketIndex]; struct pollfd* fd = socketEvents[socketIndex];
@ -345,7 +345,7 @@ namespace ix
buf[best] = buf[best + 1] = ':'; buf[best] = buf[best + 1] = ':';
memmove(buf + best + 2, buf + best + max, i - best - max + 1); memmove(buf + best + 2, buf + best + max, i - best - max + 1);
} }
if (strlen(buf) < (size_t)l) if (strlen(buf) < l)
{ {
strcpy(s, buf); strcpy(s, buf);
return s; return s;

View File

@ -8,10 +8,6 @@
#include <cstdint> #include <cstdint>
#ifdef __FreeBSD__
#include <sys/types.h>
#endif
#ifdef _WIN32 #ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN

View File

@ -34,12 +34,8 @@ namespace ix
SelectInterruptPipe::~SelectInterruptPipe() SelectInterruptPipe::~SelectInterruptPipe()
{ {
if (-1 != _fildes[kPipeReadIndex]) { ::close(_fildes[kPipeReadIndex]);
::close(_fildes[kPipeReadIndex]); ::close(_fildes[kPipeWriteIndex]);
}
if (-1 != _fildes[kPipeWriteIndex]) {
::close(_fildes[kPipeWriteIndex]);
}
_fildes[kPipeReadIndex] = -1; _fildes[kPipeReadIndex] = -1;
_fildes[kPipeWriteIndex] = -1; _fildes[kPipeWriteIndex] = -1;
} }

View File

@ -15,10 +15,6 @@
#include <pthread_np.h> #include <pthread_np.h>
#endif #endif
#ifdef __APPLE__
#include <AvailabilityMacros.h>
#endif
// Windows // Windows
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
@ -62,7 +58,7 @@ namespace ix
void setThreadName(const std::string& name) void setThreadName(const std::string& name)
{ {
#if defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) #if defined(__APPLE__)
// //
// Apple reserves 16 bytes for its thread names // Apple reserves 16 bytes for its thread names
// Notice that the Apple version of pthread_setname_np // Notice that the Apple version of pthread_setname_np

View File

@ -13,10 +13,6 @@
#include <mutex> #include <mutex>
#include <string> #include <string>
#ifdef __APPLE__
#include <sys/types.h>
#endif
#ifdef _WIN32 #ifdef _WIN32
#include <basetsd.h> #include <basetsd.h>
#ifdef _MSC_VER #ifdef _MSC_VER

View File

@ -47,16 +47,9 @@ namespace ix
mbedtls_x509_crt_init(&_cacert); mbedtls_x509_crt_init(&_cacert);
mbedtls_x509_crt_init(&_cert); mbedtls_x509_crt_init(&_cert);
mbedtls_pk_init(&_pkey); mbedtls_pk_init(&_pkey);
// Initialize the PSA Crypto API if required by the version of Mbed TLS (3.6.0).
// This allows the X.509/TLS libraries to use PSA for crypto operations.
// See: https://github.com/Mbed-TLS/mbedtls/blob/development/docs/use-psa-crypto.md
if (MBEDTLS_VERSION_MAJOR >= 3 && MBEDTLS_VERSION_MINOR >= 6 && MBEDTLS_VERSION_PATCH >= 0)
{
psa_crypto_init();
}
} }
bool SocketMbedTLS::loadSystemCertificates(std::string& errorMsg) bool SocketMbedTLS::loadSystemCertificates(std::string& /* errorMsg */)
{ {
#ifdef _WIN32 #ifdef _WIN32
DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG | DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG |
@ -359,11 +352,6 @@ namespace ix
return res; return res;
} }
if (res == 0)
{
errno = ECONNRESET;
}
if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE) if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
{ {
errno = EWOULDBLOCK; errno = EWOULDBLOCK;

View File

@ -26,7 +26,6 @@
#ifdef _WIN32 #ifdef _WIN32
// For manipulating the certificate store // For manipulating the certificate store
#include <windows.h>
#include <wincrypt.h> #include <wincrypt.h>
#endif #endif
@ -50,7 +49,7 @@ namespace
X509_STORE* opensslStore = SSL_CTX_get_cert_store(ssl); X509_STORE* opensslStore = SSL_CTX_get_cert_store(ssl);
int certificateCount = 0; int certificateCount = 0;
while ((certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator))) while (certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator))
{ {
X509* x509 = d2i_X509(NULL, X509* x509 = d2i_X509(NULL,
(const unsigned char**) &certificateIterator->pbCertEncoded, (const unsigned char**) &certificateIterator->pbCertEncoded,
@ -294,16 +293,10 @@ namespace ix
*/ */
bool SocketOpenSSL::checkHost(const std::string& host, const char* pattern) bool SocketOpenSSL::checkHost(const std::string& host, const char* pattern)
{ {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
return true;
#else
#ifdef _WIN32 #ifdef _WIN32
return PathMatchSpecA(host.c_str(), pattern); return PathMatchSpecA(host.c_str(), pattern);
#else #else
return fnmatch(pattern, host.c_str(), 0) != FNM_NOMATCH; return fnmatch(pattern, host.c_str(), 0) != FNM_NOMATCH;
#endif
#endif #endif
} }

View File

@ -219,10 +219,6 @@ namespace ix
if (_gcThread.joinable()) if (_gcThread.joinable())
{ {
_stopGc = true; _stopGc = true;
{
std::lock_guard<std::mutex> lock{ _conditionVariableMutexGC };
_canContinueGC = true;
}
_conditionVariableGC.notify_one(); _conditionVariableGC.notify_one();
_gcThread.join(); _gcThread.join();
_stopGc = false; _stopGc = false;
@ -455,10 +451,7 @@ namespace ix
if (!_stopGc) if (!_stopGc)
{ {
std::unique_lock<std::mutex> lock(_conditionVariableMutexGC); std::unique_lock<std::mutex> lock(_conditionVariableMutexGC);
if(!_canContinueGC) { _conditionVariableGC.wait(lock);
_conditionVariableGC.wait(lock, [this]{ return _canContinueGC; });
}
_canContinueGC = false;
} }
} }
} }
@ -472,10 +465,6 @@ namespace ix
{ {
// a connection got terminated, we can run the connection thread GC, // a connection got terminated, we can run the connection thread GC,
// so wake up the thread responsible for that // so wake up the thread responsible for that
{
std::lock_guard<std::mutex> lock{ _conditionVariableMutexGC };
_canContinueGC = true;
}
_conditionVariableGC.notify_one(); _conditionVariableGC.notify_one();
} }

View File

@ -126,6 +126,5 @@ namespace ix
// as a connection // as a connection
std::condition_variable _conditionVariableGC; std::condition_variable _conditionVariableGC;
std::mutex _conditionVariableMutexGC; std::mutex _conditionVariableMutexGC;
bool _canContinueGC{ false };
}; };
} // namespace ix } // namespace ix

View File

@ -180,7 +180,7 @@ namespace
bHasUserName = true; bHasUserName = true;
break; break;
} }
else if (*LocalString == '/' || *LocalString == '?') else if (*LocalString == '/')
{ {
// end of <host>:<port> specification // end of <host>:<port> specification
bHasUserName = false; bHasUserName = false;
@ -242,7 +242,7 @@ namespace
LocalString++; LocalString++;
break; break;
} }
else if (!bHasBracket && (*LocalString == ':' || *LocalString == '/' || *LocalString == '?')) else if (!bHasBracket && (*LocalString == ':' || *LocalString == '/'))
{ {
// port number is specified // port number is specified
break; break;
@ -280,14 +280,12 @@ namespace
} }
// skip '/' // skip '/'
if (*CurrentString != '/' && *CurrentString != '?') if (*CurrentString != '/')
{ {
return clParseURL(LUrlParserError_NoSlash); return clParseURL(LUrlParserError_NoSlash);
} }
if (*CurrentString != '?') { CurrentString++;
CurrentString++;
}
// parse the path // parse the path
LocalString = CurrentString; LocalString = CurrentString;

View File

@ -41,7 +41,6 @@ namespace ix
, _enablePong(kDefaultEnablePong) , _enablePong(kDefaultEnablePong)
, _pingIntervalSecs(kDefaultPingIntervalSecs) , _pingIntervalSecs(kDefaultPingIntervalSecs)
, _pingType(SendMessageKind::Ping) , _pingType(SendMessageKind::Ping)
, _autoThreadName(true)
{ {
_ws.setOnCloseCallback( _ws.setOnCloseCallback(
[this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) [this](uint16_t code, const std::string& reason, size_t wireSize, bool remote)
@ -209,7 +208,7 @@ namespace ix
WebSocketHttpHeaders headers(_extraHeaders); WebSocketHttpHeaders headers(_extraHeaders);
std::string subProtocolsHeader; std::string subProtocolsHeader;
const auto &subProtocols = getSubProtocols(); auto subProtocols = getSubProtocols();
if (!subProtocols.empty()) if (!subProtocols.empty())
{ {
// //
@ -219,7 +218,7 @@ namespace ix
// 'json,msgpack' // 'json,msgpack'
// //
int i = 0; int i = 0;
for (const auto & subProtocol : subProtocols) for (auto subProtocol : subProtocols)
{ {
if (i++ != 0) if (i++ != 0)
{ {
@ -371,10 +370,7 @@ namespace ix
void WebSocket::run() void WebSocket::run()
{ {
if (_autoThreadName) setThreadName(getUrl());
{
setThreadName(getUrl());
}
bool firstConnectionAttempt = true; bool firstConnectionAttempt = true;
@ -631,9 +627,4 @@ namespace ix
std::lock_guard<std::mutex> lock(_configMutex); std::lock_guard<std::mutex> lock(_configMutex);
return _subProtocols; return _subProtocols;
} }
void WebSocket::setAutoThreadName(bool enabled)
{
_autoThreadName = enabled;
}
} // namespace ix } // namespace ix

View File

@ -119,8 +119,6 @@ namespace ix
uint32_t getMinWaitBetweenReconnectionRetries() const; uint32_t getMinWaitBetweenReconnectionRetries() const;
const std::vector<std::string>& getSubProtocols(); const std::vector<std::string>& getSubProtocols();
void setAutoThreadName(bool enabled);
private: private:
WebSocketSendInfo sendMessage(const IXWebSocketSendData& message, WebSocketSendInfo sendMessage(const IXWebSocketSendData& message,
SendMessageKind sendMessageKind, SendMessageKind sendMessageKind,
@ -184,9 +182,6 @@ namespace ix
// Subprotocols // Subprotocols
std::vector<std::string> _subProtocols; std::vector<std::string> _subProtocols;
// enable or disable auto set thread name
bool _autoThreadName;
friend class WebSocketServer; friend class WebSocketServer;
}; };
} // namespace ix } // namespace ix

View File

@ -87,7 +87,6 @@ namespace ix
WebSocketInitResult WebSocketHandshake::clientHandshake( WebSocketInitResult WebSocketHandshake::clientHandshake(
const std::string& url, const std::string& url,
const WebSocketHttpHeaders& extraHeaders, const WebSocketHttpHeaders& extraHeaders,
const std::string& protocol,
const std::string& host, const std::string& host,
const std::string& path, const std::string& path,
int port, int port,
@ -126,12 +125,6 @@ namespace ix
ss << "User-Agent: " << userAgent() << "\r\n"; ss << "User-Agent: " << userAgent() << "\r\n";
} }
// Set an origin header if missing
if (extraHeaders.find("Origin") == extraHeaders.end())
{
ss << "Origin: " << protocol << "://" << host << ":" << port << "\r\n";
}
for (auto& it : extraHeaders) for (auto& it : extraHeaders)
{ {
ss << it.first << ": " << it.second << "\r\n"; ss << it.first << ": " << it.second << "\r\n";

View File

@ -31,7 +31,6 @@ namespace ix
WebSocketInitResult clientHandshake(const std::string& url, WebSocketInitResult clientHandshake(const std::string& url,
const WebSocketHttpHeaders& extraHeaders, const WebSocketHttpHeaders& extraHeaders,
const std::string& protocol,
const std::string& host, const std::string& host,
const std::string& path, const std::string& path,
int port, int port,

View File

@ -57,7 +57,7 @@ namespace ix
server.setOnConnectionCallback( server.setOnConnectionCallback(
[remoteUrl, remoteUrlsMapping](std::weak_ptr<ix::WebSocket> webSocket, [remoteUrl, remoteUrlsMapping](std::weak_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState) { std::shared_ptr<ConnectionState> connectionState) {
auto state = std::static_pointer_cast<ProxyConnectionState>(connectionState); auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState);
auto remoteIp = connectionState->getRemoteIp(); auto remoteIp = connectionState->getRemoteIp();
// Server connection // Server connection

View File

@ -19,20 +19,17 @@ namespace ix
{ {
const int WebSocketServer::kDefaultHandShakeTimeoutSecs(3); // 3 seconds const int WebSocketServer::kDefaultHandShakeTimeoutSecs(3); // 3 seconds
const bool WebSocketServer::kDefaultEnablePong(true); const bool WebSocketServer::kDefaultEnablePong(true);
const int WebSocketServer::kPingIntervalSeconds(-1); // disable heartbeat
WebSocketServer::WebSocketServer(int port, WebSocketServer::WebSocketServer(int port,
const std::string& host, const std::string& host,
int backlog, int backlog,
size_t maxConnections, size_t maxConnections,
int handshakeTimeoutSecs, int handshakeTimeoutSecs,
int addressFamily, int addressFamily)
int pingIntervalSeconds)
: SocketServer(port, host, backlog, maxConnections, addressFamily) : SocketServer(port, host, backlog, maxConnections, addressFamily)
, _handshakeTimeoutSecs(handshakeTimeoutSecs) , _handshakeTimeoutSecs(handshakeTimeoutSecs)
, _enablePong(kDefaultEnablePong) , _enablePong(kDefaultEnablePong)
, _enablePerMessageDeflate(true) , _enablePerMessageDeflate(true)
, _pingIntervalSeconds(pingIntervalSeconds)
{ {
} }
@ -94,10 +91,6 @@ namespace ix
setThreadName("Srv:ws:" + connectionState->getId()); setThreadName("Srv:ws:" + connectionState->getId());
auto webSocket = std::make_shared<WebSocket>(); auto webSocket = std::make_shared<WebSocket>();
webSocket->setAutoThreadName(false);
webSocket->setPingInterval(_pingIntervalSeconds);
if (_onConnectionCallback) if (_onConnectionCallback)
{ {
_onConnectionCallback(webSocket, connectionState); _onConnectionCallback(webSocket, connectionState);

View File

@ -33,8 +33,7 @@ namespace ix
int backlog = SocketServer::kDefaultTcpBacklog, int backlog = SocketServer::kDefaultTcpBacklog,
size_t maxConnections = SocketServer::kDefaultMaxConnections, size_t maxConnections = SocketServer::kDefaultMaxConnections,
int handshakeTimeoutSecs = WebSocketServer::kDefaultHandShakeTimeoutSecs, int handshakeTimeoutSecs = WebSocketServer::kDefaultHandShakeTimeoutSecs,
int addressFamily = SocketServer::kDefaultAddressFamily, int addressFamily = SocketServer::kDefaultAddressFamily);
int pingIntervalSeconds = WebSocketServer::kPingIntervalSeconds);
virtual ~WebSocketServer(); virtual ~WebSocketServer();
virtual void stop() final; virtual void stop() final;
@ -62,7 +61,6 @@ namespace ix
int _handshakeTimeoutSecs; int _handshakeTimeoutSecs;
bool _enablePong; bool _enablePong;
bool _enablePerMessageDeflate; bool _enablePerMessageDeflate;
int _pingIntervalSeconds;
OnConnectionCallback _onConnectionCallback; OnConnectionCallback _onConnectionCallback;
OnClientMessageCallback _onClientMessageCallback; OnClientMessageCallback _onClientMessageCallback;
@ -71,7 +69,6 @@ namespace ix
std::set<std::shared_ptr<WebSocket>> _clients; std::set<std::shared_ptr<WebSocket>> _clients;
const static bool kDefaultEnablePong; const static bool kDefaultEnablePong;
const static int kPingIntervalSeconds;
// Methods // Methods
virtual void handleConnection(std::unique_ptr<Socket> socket, virtual void handleConnection(std::unique_ptr<Socket> socket,

View File

@ -71,10 +71,10 @@ namespace ix
, _closingTimePoint(std::chrono::steady_clock::now()) , _closingTimePoint(std::chrono::steady_clock::now())
, _enablePong(kDefaultEnablePong) , _enablePong(kDefaultEnablePong)
, _pingIntervalSecs(kDefaultPingIntervalSecs) , _pingIntervalSecs(kDefaultPingIntervalSecs)
, _pongReceived(false)
, _setCustomMessage(false) , _setCustomMessage(false)
, _kPingMessage("ixwebsocket::heartbeat") , _kPingMessage("ixwebsocket::heartbeat")
, _pingType(SendMessageKind::Ping) , _pingType(SendMessageKind::Ping)
, _pongReceived(false)
, _pingCount(0) , _pingCount(0)
, _lastSendPingTimePoint(std::chrono::steady_clock::now()) , _lastSendPingTimePoint(std::chrono::steady_clock::now())
{ {
@ -140,7 +140,7 @@ namespace ix
_enablePerMessageDeflate); _enablePerMessageDeflate);
result = webSocketHandshake.clientHandshake( result = webSocketHandshake.clientHandshake(
remoteUrl, headers, protocol, host, path, port, timeoutSecs); remoteUrl, headers, host, path, port, timeoutSecs);
if (result.http_status >= 300 && result.http_status < 400) if (result.http_status >= 300 && result.http_status < 400)
{ {
@ -700,7 +700,6 @@ namespace ix
if (_readyState != ReadyState::CLOSING) if (_readyState != ReadyState::CLOSING)
{ {
// send back the CLOSE frame // send back the CLOSE frame
setReadyState(ReadyState::CLOSING);
sendCloseFrame(code, reason); sendCloseFrame(code, reason);
wakeUpFromPoll(SelectInterrupt::kCloseRequest); wakeUpFromPoll(SelectInterrupt::kCloseRequest);
@ -1073,10 +1072,7 @@ namespace ix
else if (ret <= 0) else if (ret <= 0)
{ {
closeSocket(); closeSocket();
if (_readyState != ReadyState::CLOSING) setReadyState(ReadyState::CLOSED);
{
setReadyState(ReadyState::CLOSED);
}
return false; return false;
} }
else else
@ -1174,22 +1170,7 @@ namespace ix
{ {
_requestInitCancellation = true; _requestInitCancellation = true;
if (_readyState == ReadyState::CLOSING || _readyState == ReadyState::CLOSED) if (_readyState == ReadyState::CLOSING || _readyState == ReadyState::CLOSED) return;
{
// Wake up the socket polling thread, as
// Socket::isReadyToRead() might be still waiting the
// interrupt event to happen.
bool wakeUpPoll = false;
{
std::lock_guard<std::mutex> lock(_socketMutex);
wakeUpPoll = (_socket && _socket->isWakeUpFromPollSupported());
}
if (wakeUpPoll)
{
wakeUpFromPoll(SelectInterrupt::kCloseRequest);
}
return;
}
if (closeWireSize == 0) if (closeWireSize == 0)
{ {

View File

@ -28,6 +28,7 @@
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <vector> #include <vector>
#include <deque>
namespace ix namespace ix
{ {
@ -156,7 +157,7 @@ namespace ix
// Contains all messages that were fetched in the last socket read. // Contains all messages that were fetched in the last socket read.
// This could be a mix of control messages (Close, Ping, etc...) and // This could be a mix of control messages (Close, Ping, etc...) and
// data messages. That buffer is resized // data messages. That buffer is resized
std::vector<uint8_t> _rxbuf; std::deque<uint8_t> _rxbuf;
// Contains all messages that are waiting to be sent // Contains all messages that are waiting to be sent
std::vector<uint8_t> _txbuf; std::vector<uint8_t> _txbuf;

View File

@ -6,4 +6,4 @@
#pragma once #pragma once
#define IX_WEBSOCKET_VERSION "11.4.6" #define IX_WEBSOCKET_VERSION "11.4.3"

View File

@ -13,24 +13,16 @@ all: brew
install: brew install: brew
-DCMAKE_INSTALL_PREFIX=/opt/homebrew
# Use -DCMAKE_INSTALL_PREFIX= to install into another location # Use -DCMAKE_INSTALL_PREFIX= to install into another location
# on osx it is good practice to make /usr/local user writable # on osx it is good practice to make /usr/local user writable
# sudo chown -R `whoami`/staff /usr/local # sudo chown -R `whoami`/staff /usr/local
# #
# Those days (since Apple Silicon mac shipped), on macOS homebrew installs in /opt/homebrew, and /usr/local is readonly
#
# Release, Debug, MinSizeRel, RelWithDebInfo are the build types # Release, Debug, MinSizeRel, RelWithDebInfo are the build types
# #
# Default rule does not use python as that requires first time users to have Python3 installed # Default rule does not use python as that requires first time users to have Python3 installed
# #
brew: brew:
ifeq ($(shell uname),Darwin) mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja install)
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_PREFIX=/opt/homebrew -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja)
else
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=OFF -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 .. ; ninja)
endif
# Docker default target. We've had problems with OpenSSL and TLS 1.3 (on the # Docker default target. We've had problems with OpenSSL and TLS 1.3 (on the
# server side ?) and I can't work-around it easily, so we're using mbedtls on # server side ?) and I can't work-around it easily, so we're using mbedtls on
@ -39,10 +31,10 @@ ws_mbedtls_install:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_ZLIB=OFF -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; ninja install) mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_ZLIB=OFF -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; ninja install)
ws: ws:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. && ninja) mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
ws_unity: ws_unity:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. && ninja) mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_UNITY_BUILD=ON -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
ws_install: ws_install:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install) mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install)
@ -54,13 +46,13 @@ ws_openssl_install:
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; ninja install) mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; ninja install)
ws_mbedtls: ws_mbedtls:
mkdir -p build && (cd build ; cmake -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; ninja) mkdir -p build && (cd build ; cmake -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j 4)
ws_no_ssl: ws_no_ssl:
mkdir -p build && (cd build ; cmake -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; ninja) mkdir -p build && (cd build ; cmake -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=Debug -DUSE_WS=1 .. ; make -j 4)
ws_no_python: ws_no_python:
mkdir -p build && (cd build ; cmake -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. ; ninja install) mkdir -p build && (cd build ; cmake -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j4 install)
uninstall: uninstall:
xargs rm -fv < build/install_manifest.txt xargs rm -fv < build/install_manifest.txt

View File

@ -16,7 +16,7 @@ set (TEST_TARGET_NAMES
IXWebSocketServerTest IXWebSocketServerTest
IXWebSocketTestConnectionDisconnection IXWebSocketTestConnectionDisconnection
IXUrlParserTest IXUrlParserTest
# IXHttpClientTest ## FIXME httpbin.org does not seem normal IXHttpClientTest
IXUnityBuildsTest IXUnityBuildsTest
IXHttpTest IXHttpTest
IXDNSLookupTest IXDNSLookupTest
@ -24,13 +24,14 @@ set (TEST_TARGET_NAMES
# IXWebSocketBroadcastTest ## FIXME was depending on cobra / take a broadcast server from ws # IXWebSocketBroadcastTest ## FIXME was depending on cobra / take a broadcast server from ws
IXStrCaseCompareTest IXStrCaseCompareTest
IXExponentialBackoffTest IXExponentialBackoffTest
IXWebSocketCloseTest
) )
# Some unittest don't work on windows yet # Some unittest don't work on windows yet
# Windows without TLS does not have hmac yet # Windows without TLS does not have hmac yet
if (UNIX) if (UNIX)
list(APPEND TEST_TARGET_NAMES list(APPEND TEST_TARGET_NAMES
IXWebSocketCloseTest
# Fail on Windows in CI probably because the pathing is wrong and # Fail on Windows in CI probably because the pathing is wrong and
# some resource files cannot be found # some resource files cannot be found
IXHttpServerTest IXHttpServerTest

View File

@ -60,7 +60,6 @@ TEST_CASE("http server", "[httpd]")
REQUIRE(response->errorCode == HttpErrorCode::Ok); REQUIRE(response->errorCode == HttpErrorCode::Ok);
REQUIRE(response->statusCode == 200); REQUIRE(response->statusCode == 200);
REQUIRE(response->headers["Accept-Encoding"] == "gzip"); REQUIRE(response->headers["Accept-Encoding"] == "gzip");
REQUIRE(response->headers["Content-Encoding"] == "gzip");
server.stop(); server.stop();
} }

View File

@ -84,40 +84,6 @@ namespace ix
REQUIRE(port == 443); // default port for wss REQUIRE(port == 443); // default port for wss
} }
SECTION("wss://google.com/?arg=value")
{
std::string url = "wss://google.com/?arg=value&arg2=value2";
std::string protocol, host, path, query;
int port;
bool res;
res = UrlParser::parse(url, protocol, host, path, query, port);
REQUIRE(res);
REQUIRE(protocol == "wss");
REQUIRE(host == "google.com");
REQUIRE(path == "/?arg=value&arg2=value2");
REQUIRE(query == "arg=value&arg2=value2");
REQUIRE(port == 443); // default port for wss
}
SECTION("wss://google.com?arg=value")
{
std::string url = "wss://google.com?arg=value&arg2=value2";
std::string protocol, host, path, query;
int port;
bool res;
res = UrlParser::parse(url, protocol, host, path, query, port);
REQUIRE(res);
REQUIRE(protocol == "wss");
REQUIRE(host == "google.com");
REQUIRE(path == "/?arg=value&arg2=value2");
REQUIRE(query == "arg=value&arg2=value2");
REQUIRE(port == 443); // default port for wss
}
SECTION("real test") SECTION("real test")
{ {
std::string url = std::string url =