From 872f516edec86cf1c60d411203ac30b1c180f52e Mon Sep 17 00:00:00 2001 From: arenevier Date: Fri, 13 Oct 2023 19:55:26 -0700 Subject: [PATCH 1/9] allow building when cpp exceptions are disabled (#489) IXWebSocket needs exceptions support because of the use of stoi. In order to build when cpp exceptions are disabled, we can use strtol. --- ixwebsocket/IXHttp.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ixwebsocket/IXHttp.cpp b/ixwebsocket/IXHttp.cpp index 46504026..19f17fbc 100644 --- a/ixwebsocket/IXHttp.cpp +++ b/ixwebsocket/IXHttp.cpp @@ -133,16 +133,20 @@ namespace ix if (headers.find("Content-Length") != headers.end()) { int contentLength = 0; - try { - contentLength = std::stoi(headers["Content-Length"]); + const char* p = headers["Content-Length"].c_str(); + 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::min() + || val > std::numeric_limits::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) { return std::make_tuple( From f29906c72f1739e0c373495cde068923a88c2cc2 Mon Sep 17 00:00:00 2001 From: arenevier Date: Fri, 13 Oct 2023 19:56:24 -0700 Subject: [PATCH 2/9] Allow building without rtti (#487) Since factory returns a ProxyConnectionState, setOnConnectionCallback will be a ProxyConnectionState. The code already makes that assumption, since it does not check of state return value. Using a static_pointer_cast will allow the library to be build with rtti. --- ixwebsocket/IXWebSocketProxyServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ixwebsocket/IXWebSocketProxyServer.cpp b/ixwebsocket/IXWebSocketProxyServer.cpp index 4b78c63b..4df5a115 100644 --- a/ixwebsocket/IXWebSocketProxyServer.cpp +++ b/ixwebsocket/IXWebSocketProxyServer.cpp @@ -57,7 +57,7 @@ namespace ix server.setOnConnectionCallback( [remoteUrl, remoteUrlsMapping](std::weak_ptr webSocket, std::shared_ptr connectionState) { - auto state = std::dynamic_pointer_cast(connectionState); + auto state = std::static_pointer_cast(connectionState); auto remoteIp = connectionState->getRemoteIp(); // Server connection From 3b66efbb6a4800b22a46c1ec3b3a01f9dba85171 Mon Sep 17 00:00:00 2001 From: RH Date: Thu, 16 Nov 2023 05:40:49 +1100 Subject: [PATCH 3/9] Fix C++/WinRT compile issue (#493) --- ixwebsocket/IXSocketOpenSSL.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ixwebsocket/IXSocketOpenSSL.cpp b/ixwebsocket/IXSocketOpenSSL.cpp index e37f3a6e..43d9bf3f 100644 --- a/ixwebsocket/IXSocketOpenSSL.cpp +++ b/ixwebsocket/IXSocketOpenSSL.cpp @@ -26,6 +26,7 @@ #ifdef _WIN32 // For manipulating the certificate store +#include #include #endif @@ -293,10 +294,16 @@ namespace ix */ bool SocketOpenSSL::checkHost(const std::string& host, const char* pattern) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + return true; +#else + #ifdef _WIN32 return PathMatchSpecA(host.c_str(), pattern); #else return fnmatch(pattern, host.c_str(), 0) != FNM_NOMATCH; +#endif + #endif } From e03c0be8a4c5fe89e2f67b76eb9b938eee35b495 Mon Sep 17 00:00:00 2001 From: bsergean Date: Mon, 20 Nov 2023 22:28:12 -0800 Subject: [PATCH 4/9] Update unittest_windows_gcc.yml (#494) * Update unittest_windows_gcc.yml * Update unittest_windows_gcc.yml --- .github/workflows/unittest_windows_gcc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unittest_windows_gcc.yml b/.github/workflows/unittest_windows_gcc.yml index c25a3003..4908ae1f 100644 --- a/.github/workflows/unittest_windows_gcc.yml +++ b/.github/workflows/unittest_windows_gcc.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v1 - uses: seanmiddleditch/gha-setup-ninja@master - - uses: egor-tensin/setup-mingw@v2.2.0 + - uses: bsergean/setup-mingw@d79ce405bac9edef3a1726ef00554a56f0bafe66 - run: | mkdir build cd build From c5a02f1066fb0fde48f80f51178429a27f689a39 Mon Sep 17 00:00:00 2001 From: bsergean Date: Sat, 10 Feb 2024 22:03:22 -0800 Subject: [PATCH 5/9] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4fb3b04d..a0f17753 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ## 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. 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. From 70602c4e6b26bc89c27d78db26b4fc2cbb2f3769 Mon Sep 17 00:00:00 2001 From: Daniel Wymark <114119965+dwymark-celestron@users.noreply.github.com> Date: Tue, 12 Mar 2024 09:46:27 -0700 Subject: [PATCH 6/9] Add ping interval to constructor params for WebSocketServer (#497) * Update .gitignore for CLion compatibility * Add pingIntervalSeconds to constructor for WebSocketServer * Add Heartbeat section to WebSocketServer usage documentation * Fix typo --- .gitignore | 2 ++ docs/usage.md | 11 +++++++++++ ixwebsocket/IXWebSocketServer.cpp | 6 +++++- ixwebsocket/IXWebSocketServer.h | 5 ++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 892b7bf6..36e9925a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ ws/.srl ixhttpd makefile a.out +.idea/ +cmake-build-debug/ diff --git a/docs/usage.md b/docs/usage.md index 57799be7..e7a77c75 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -445,6 +445,17 @@ 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 ```cpp diff --git a/ixwebsocket/IXWebSocketServer.cpp b/ixwebsocket/IXWebSocketServer.cpp index 4518389b..cb6988a5 100644 --- a/ixwebsocket/IXWebSocketServer.cpp +++ b/ixwebsocket/IXWebSocketServer.cpp @@ -19,17 +19,20 @@ namespace ix { const int WebSocketServer::kDefaultHandShakeTimeoutSecs(3); // 3 seconds const bool WebSocketServer::kDefaultEnablePong(true); + const int WebSocketServer::kPingIntervalSeconds(-1); // disable heartbeat WebSocketServer::WebSocketServer(int port, const std::string& host, int backlog, size_t maxConnections, int handshakeTimeoutSecs, - int addressFamily) + int addressFamily, + int pingIntervalSeconds) : SocketServer(port, host, backlog, maxConnections, addressFamily) , _handshakeTimeoutSecs(handshakeTimeoutSecs) , _enablePong(kDefaultEnablePong) , _enablePerMessageDeflate(true) + , _pingIntervalSeconds(pingIntervalSeconds) { } @@ -93,6 +96,7 @@ namespace ix auto webSocket = std::make_shared(); webSocket->setAutoThreadName(false); + webSocket->setPingInterval(_pingIntervalSeconds); if (_onConnectionCallback) { diff --git a/ixwebsocket/IXWebSocketServer.h b/ixwebsocket/IXWebSocketServer.h index dcb21e81..7636074e 100644 --- a/ixwebsocket/IXWebSocketServer.h +++ b/ixwebsocket/IXWebSocketServer.h @@ -33,7 +33,8 @@ namespace ix int backlog = SocketServer::kDefaultTcpBacklog, size_t maxConnections = SocketServer::kDefaultMaxConnections, int handshakeTimeoutSecs = WebSocketServer::kDefaultHandShakeTimeoutSecs, - int addressFamily = SocketServer::kDefaultAddressFamily); + int addressFamily = SocketServer::kDefaultAddressFamily, + int pingIntervalSeconds = WebSocketServer::kPingIntervalSeconds); virtual ~WebSocketServer(); virtual void stop() final; @@ -61,6 +62,7 @@ namespace ix int _handshakeTimeoutSecs; bool _enablePong; bool _enablePerMessageDeflate; + int _pingIntervalSeconds; OnConnectionCallback _onConnectionCallback; OnClientMessageCallback _onClientMessageCallback; @@ -69,6 +71,7 @@ namespace ix std::set> _clients; const static bool kDefaultEnablePong; + const static int kPingIntervalSeconds; // Methods virtual void handleConnection(std::unique_ptr socket, From 39e085bebca78f137e5bedba948fdc50b8e7a5ad Mon Sep 17 00:00:00 2001 From: Paul Whiting <64860722+PaulWhitingS2@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:23:14 -0600 Subject: [PATCH 7/9] Fix MbedTLS disconnect handling. (#500) --- ixwebsocket/IXSocketMbedTLS.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ixwebsocket/IXSocketMbedTLS.cpp b/ixwebsocket/IXSocketMbedTLS.cpp index 0192dc78..97c87b97 100644 --- a/ixwebsocket/IXSocketMbedTLS.cpp +++ b/ixwebsocket/IXSocketMbedTLS.cpp @@ -352,6 +352,11 @@ namespace ix return res; } + if (res == 0) + { + errno = ECONNRESET; + } + if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE) { errno = EWOULDBLOCK; From 98b4828e9315408acce234a99a1ec8a1f800d234 Mon Sep 17 00:00:00 2001 From: CryptoManiac Date: Tue, 19 Mar 2024 08:24:11 +0300 Subject: [PATCH 8/9] Update IXSelectInterruptPipe.cpp (#502) Valgrind keeps complaining that close() on the invalid descriptor -1 is happening here. I suppose that close shouldn't be called when the descriptor value is known to be invalid. It's not a fatal error or something, but this practice is able to create a lot of flood in the logs. --- ixwebsocket/IXSelectInterruptPipe.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ixwebsocket/IXSelectInterruptPipe.cpp b/ixwebsocket/IXSelectInterruptPipe.cpp index 75c42f27..0518994a 100644 --- a/ixwebsocket/IXSelectInterruptPipe.cpp +++ b/ixwebsocket/IXSelectInterruptPipe.cpp @@ -34,8 +34,12 @@ namespace ix SelectInterruptPipe::~SelectInterruptPipe() { - ::close(_fildes[kPipeReadIndex]); - ::close(_fildes[kPipeWriteIndex]); + if (-1 != _fildes[kPipeReadIndex]) { + ::close(_fildes[kPipeReadIndex]); + } + if (-1 != _fildes[kPipeWriteIndex]) { + ::close(_fildes[kPipeWriteIndex]); + } _fildes[kPipeReadIndex] = -1; _fildes[kPipeWriteIndex] = -1; } From 755d98d918ea8efd559d0cc8e4d111489b807ff5 Mon Sep 17 00:00:00 2001 From: Giuseppe Penone Date: Tue, 19 Mar 2024 05:26:45 +0000 Subject: [PATCH 9/9] Support URLs with no slash before the question mark (#507) * Support Url No Slash Before Question Mark * Support Url No Slash Before Question Mark * unit test fix --------- Co-authored-by: Giuseppe Penone --- ixwebsocket/IXUrlParser.cpp | 10 ++++++---- test/IXUrlParserTest.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/ixwebsocket/IXUrlParser.cpp b/ixwebsocket/IXUrlParser.cpp index aa1ef408..c5cdb57b 100644 --- a/ixwebsocket/IXUrlParser.cpp +++ b/ixwebsocket/IXUrlParser.cpp @@ -180,7 +180,7 @@ namespace bHasUserName = true; break; } - else if (*LocalString == '/') + else if (*LocalString == '/' || *LocalString == '?') { // end of : specification bHasUserName = false; @@ -242,7 +242,7 @@ namespace LocalString++; break; } - else if (!bHasBracket && (*LocalString == ':' || *LocalString == '/')) + else if (!bHasBracket && (*LocalString == ':' || *LocalString == '/' || *LocalString == '?')) { // port number is specified break; @@ -280,12 +280,14 @@ namespace } // skip '/' - if (*CurrentString != '/') + if (*CurrentString != '/' && *CurrentString != '?') { return clParseURL(LUrlParserError_NoSlash); } - CurrentString++; + if (*CurrentString != '?') { + CurrentString++; + } // parse the path LocalString = CurrentString; diff --git a/test/IXUrlParserTest.cpp b/test/IXUrlParserTest.cpp index cee3f3bf..dd2786dc 100644 --- a/test/IXUrlParserTest.cpp +++ b/test/IXUrlParserTest.cpp @@ -84,6 +84,40 @@ namespace ix 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") { std::string url =