Compare commits

..

18 Commits

Author SHA1 Message Date
2ddac836ad Update unittest_windows_gcc.yml 2023-11-17 06:16:53 -08:00
69e4bc4c05 Update unittest_windows_gcc.yml 2023-11-17 06:15:35 -08:00
RH
3b66efbb6a Fix C++/WinRT compile issue (#493) 2023-11-15 10:40:49 -08:00
f29906c72f 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.
2023-10-13 19:56:24 -07:00
872f516ede 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.
2023-10-13 19:55:26 -07:00
014d43eb13 stop building mingw ; if someone wants to maintain this port please reach out ! 2023-10-13 19:54:25 -07:00
d77067e50f Update window gcc action to latest setup_mingw 2023-10-13 19:39:41 -07:00
ed5b1a0895 Fix links & update info in README (#485) 2023-09-15 22:36:52 -07:00
ef57e3a2b1 Fix hanging of WebSocket::stop() waiting for its thread to join (#481)
This might happen in some special cases where
WebSocketTransport::sendOnSocket() fails, closes the socket, and set
the ready state to CLOSED, but WebSocket::run() stills wait the
interrupt event to happen.

Possibly related to #474
2023-09-01 08:11:36 -07:00
28832f8732 Fix #286 - http response headers overwritten with request headers (#483) 2023-09-01 08:11:07 -07:00
0dd284267a Fix MinGW build warning (#482) 2023-09-01 08:00:35 -07:00
a7019631b7 Fix server empty thread name (#478) 2023-08-01 22:16:43 -07:00
632ee31509 Update IXSocketMbedTLS.cpp (#471) 2023-06-22 14:12:51 -07:00
688af99747 bump version 2023-06-05 10:06:53 -07:00
397bb5d18a Fix version in CMakeLists.txt (#467)
* Fix version in CMakeLists.txt

* Disable IXHttpClientTest
2023-06-05 10:03:17 -07:00
f79c64ae97 Add a reference to Candy in the README (#462) 2023-05-04 00:03:28 -07:00
bc765e73a3 Add pkgconfig file (#459) 2023-04-25 11:54:25 -07:00
dfa10df5ae Set an origin header in websocket and http clients (#455) 2023-04-01 12:19:38 -07:00
20 changed files with 93 additions and 60 deletions

View File

@ -1,27 +0,0 @@
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: egor-tensin/setup-mingw@v2 - uses: bsergean/setup-mingw@d79ce405bac9edef3a1726ef00554a56f0bafe66
- run: | - run: |
mkdir build mkdir build
cd build cd build

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 C CXX) project(ixwebsocket LANGUAGES C CXX VERSION 11.4.4)
set (CMAKE_CXX_STANDARD 11) set (CMAKE_CXX_STANDARD 11)
set (CXX_STANDARD_REQUIRED ON) set (CXX_STANDARD_REQUIRED ON)
@ -136,6 +136,7 @@ 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()
@ -165,9 +166,9 @@ if(BUILD_SHARED_LIBS)
${IXWEBSOCKET_SOURCES} ${IXWEBSOCKET_SOURCES}
${IXWEBSOCKET_HEADERS} ${IXWEBSOCKET_HEADERS}
) )
# Set library version # Set library version
set_target_properties(ixwebsocket PROPERTIES VERSION 11.3.2) set_target_properties(ixwebsocket PROPERTIES VERSION ${CMAKE_PROJECT_VERSION})
else() else()
# Static library # Static library
@ -290,6 +291,10 @@ if (IXWEBSOCKET_INSTALL)
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_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ixwebsocket-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ixwebsocket") install(FILES "${CMAKE_CURRENT_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

@ -99,14 +99,15 @@ 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://gitlab.com/HCInk/tokio), a discord library focused on audio playback with node bindings. - [Tokio](https://github.com/liz3/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 - [DisCPP](https://github.com/DisCPP/DisCPP), a simple but feature rich Discord API wrapper (archived as of Oct 8, 2021)
- [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
## Alternative libraries ## Alternative libraries
@ -114,8 +115,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.
@ -133,9 +134,7 @@ 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.
@ -147,5 +146,4 @@ 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,6 +2,8 @@
All changes to this project will be documented in this file. All changes to this project will be documented in this file.
## [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

11
ixwebsocket.pc.in Normal file
View File

@ -0,0 +1,11 @@
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,9 +27,13 @@
// 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
namespace ix namespace ix
{ {

View File

@ -133,16 +133,20 @@ namespace ix
if (headers.find("Content-Length") != headers.end()) if (headers.find("Content-Length") != headers.end())
{ {
int contentLength = 0; 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<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

@ -148,6 +148,7 @@ 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
@ -161,11 +162,6 @@ 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 && _fd->fd != -1) if (_fd != nullptr && (int)_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 < socketEvents.size()) while (socketIndex < (int)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) < l) if (strlen(buf) < (size_t)l)
{ {
strcpy(s, buf); strcpy(s, buf);
return s; return s;

View File

@ -49,7 +49,7 @@ namespace ix
mbedtls_pk_init(&_pkey); mbedtls_pk_init(&_pkey);
} }
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 |

View File

@ -26,6 +26,7 @@
#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
@ -49,7 +50,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,
@ -293,10 +294,16 @@ 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

@ -41,6 +41,7 @@ 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)
@ -370,7 +371,10 @@ namespace ix
void WebSocket::run() void WebSocket::run()
{ {
setThreadName(getUrl()); if (_autoThreadName)
{
setThreadName(getUrl());
}
bool firstConnectionAttempt = true; bool firstConnectionAttempt = true;
@ -627,4 +631,9 @@ 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,6 +119,8 @@ 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,
@ -182,6 +184,9 @@ 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

@ -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::dynamic_pointer_cast<ProxyConnectionState>(connectionState); auto state = std::static_pointer_cast<ProxyConnectionState>(connectionState);
auto remoteIp = connectionState->getRemoteIp(); auto remoteIp = connectionState->getRemoteIp();
// Server connection // Server connection

View File

@ -91,6 +91,9 @@ 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);
if (_onConnectionCallback) if (_onConnectionCallback)
{ {
_onConnectionCallback(webSocket, connectionState); _onConnectionCallback(webSocket, connectionState);

View File

@ -1174,7 +1174,22 @@ namespace ix
{ {
_requestInitCancellation = true; _requestInitCancellation = true;
if (_readyState == ReadyState::CLOSING || _readyState == ReadyState::CLOSED) return; if (_readyState == ReadyState::CLOSING || _readyState == ReadyState::CLOSED)
{
// 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

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

View File

@ -16,7 +16,7 @@ set (TEST_TARGET_NAMES
IXWebSocketServerTest IXWebSocketServerTest
IXWebSocketTestConnectionDisconnection IXWebSocketTestConnectionDisconnection
IXUrlParserTest IXUrlParserTest
IXHttpClientTest # IXHttpClientTest ## FIXME httpbin.org does not seem normal
IXUnityBuildsTest IXUnityBuildsTest
IXHttpTest IXHttpTest
IXDNSLookupTest IXDNSLookupTest

View File

@ -60,6 +60,7 @@ 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();
} }