Merge branch 'master' of github.com:machinezone/IXWebSocket
This commit is contained in:
commit
93e673da9f
2
.github/workflows/unittest_windows_gcc.yml
vendored
2
.github/workflows/unittest_windows_gcc.yml
vendored
@ -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
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,3 +8,5 @@ ws/.srl
|
||||
ixhttpd
|
||||
makefile
|
||||
a.out
|
||||
.idea/
|
||||
cmake-build-debug/
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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<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)
|
||||
{
|
||||
return std::make_tuple(
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
// For manipulating the certificate store
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
#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
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,7 @@ namespace
|
||||
bHasUserName = true;
|
||||
break;
|
||||
}
|
||||
else if (*LocalString == '/')
|
||||
else if (*LocalString == '/' || *LocalString == '?')
|
||||
{
|
||||
// end of <host>:<port> 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;
|
||||
|
@ -57,7 +57,7 @@ namespace ix
|
||||
server.setOnConnectionCallback(
|
||||
[remoteUrl, remoteUrlsMapping](std::weak_ptr<ix::WebSocket> webSocket,
|
||||
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();
|
||||
|
||||
// Server connection
|
||||
|
@ -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>();
|
||||
|
||||
webSocket->setAutoThreadName(false);
|
||||
webSocket->setPingInterval(_pingIntervalSeconds);
|
||||
|
||||
if (_onConnectionCallback)
|
||||
{
|
||||
|
@ -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<std::shared_ptr<WebSocket>> _clients;
|
||||
|
||||
const static bool kDefaultEnablePong;
|
||||
const static int kPingIntervalSeconds;
|
||||
|
||||
// Methods
|
||||
virtual void handleConnection(std::unique_ptr<Socket> socket,
|
||||
|
@ -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 =
|
||||
|
Loading…
Reference in New Issue
Block a user