Compare commits
2 Commits
v8.0.5
...
feature/ss
Author | SHA1 | Date | |
---|---|---|---|
22118d68d2 | |||
4c15964d43 |
@ -1,34 +1,6 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All changes to this project will be documented in this file.
|
All changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [8.0.6] - 2020-01-31
|
|
||||||
|
|
||||||
(snake) add an option to disable answering pongs as response to pings, to test cobra client behavior with hanged connections
|
|
||||||
|
|
||||||
## [8.0.5] - 2020-01-31
|
|
||||||
|
|
||||||
(IXCobraConnection) set a ping timeout of 90 seconds. If no pong messages are received as responses to ping for a while, give up and close the connection
|
|
||||||
|
|
||||||
## [8.0.4] - 2020-01-31
|
|
||||||
|
|
||||||
(cobra to sentry) remove noisy logging
|
|
||||||
|
|
||||||
## [8.0.3] - 2020-01-30
|
|
||||||
|
|
||||||
(ixcobra) check if we are authenticated in publishNext before trying to publish a message
|
|
||||||
|
|
||||||
## [8.0.2] - 2020-01-28
|
|
||||||
|
|
||||||
Extract severity level when emitting messages to sentry
|
|
||||||
|
|
||||||
## [8.0.1] - 2020-01-28
|
|
||||||
|
|
||||||
Fix bug #151 - If a socket connection is interrupted, calling stop() on the IXWebSocket object blocks until the next retry
|
|
||||||
|
|
||||||
## [8.0.0] - 2020-01-26
|
|
||||||
|
|
||||||
(SocketServer) add ability to bind on an ipv6 address
|
|
||||||
|
|
||||||
## [7.9.6] - 2020-01-22
|
## [7.9.6] - 2020-01-22
|
||||||
|
|
||||||
(ws) add a dnslookup sub-command, to get the ip address of a remote host
|
(ws) add a dnslookup sub-command, to get the ip address of a remote host
|
||||||
|
@ -33,16 +33,11 @@ vcpkg install ixwebsocket
|
|||||||
|
|
||||||
### Conan
|
### Conan
|
||||||
|
|
||||||
[  ](https://bintray.com/conan/conan-center/ixwebsocket%3A_/_latestVersion)
|
Support for building with conan was contributed by Olivia Zoe (thanks!). The package name to reference is `IXWebSocket/5.0.0@LunarWatcher/stable`, and a list of the uploaded versions is available on [Bintray](https://bintray.com/oliviazoe0/conan-packages/IXWebSocket%3ALunarWatcher). The package is in the process to be published to the official conan package repo, but in the meantime, it can be accessed by adding a new remote
|
||||||
|
|
||||||
Conan is currently supported through a recipe in [Conan Center](https://github.com/conan-io/conan-center-index/tree/master/recipes/ixwebsocket) ([Bintray entry](https://bintray.com/conan/conan-center/ixwebsocket%3A_)).
|
```
|
||||||
|
conan remote add remote_name_here https://api.bintray.com/conan/oliviazoe0/conan-packages
|
||||||
Package reference
|
```
|
||||||
|
|
||||||
* Conan 1.21.0 and up: `ixwebsocket/7.9.2`
|
|
||||||
* Earlier versions: `ixwebsocket/7.9.2@_/_`
|
|
||||||
|
|
||||||
Note that the version listed here might not be the latest one. See Bintray or the recipe itself for the latest version. If you're migrating from the previous, custom Bintray remote, note that the package reference _has_ to be lower-case.
|
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
|
@ -265,15 +265,7 @@ namespace ix
|
|||||||
_webSocket->setUrl(url);
|
_webSocket->setUrl(url);
|
||||||
_webSocket->setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
|
_webSocket->setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
|
||||||
_webSocket->setTLSOptions(socketTLSOptions);
|
_webSocket->setTLSOptions(socketTLSOptions);
|
||||||
|
|
||||||
// Send a websocket ping every N seconds (N = 30) now
|
|
||||||
// This should keep the connection open and prevent some load balancers such as
|
|
||||||
// the Amazon one from shutting it down
|
|
||||||
_webSocket->setPingInterval(kPingIntervalSecs);
|
_webSocket->setPingInterval(kPingIntervalSecs);
|
||||||
|
|
||||||
// If we don't receive a pong back, declare loss after 3 * N seconds
|
|
||||||
// (will be 90s now), and close and restart the connection
|
|
||||||
_webSocket->setPingTimeout(3 * kPingIntervalSecs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -514,7 +506,7 @@ namespace ix
|
|||||||
if (_messageQueue.empty()) return true;
|
if (_messageQueue.empty()) return true;
|
||||||
|
|
||||||
auto&& msg = _messageQueue.back();
|
auto&& msg = _messageQueue.back();
|
||||||
if (!_authenticated || !publishMessage(msg))
|
if (!publishMessage(msg))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -166,17 +166,6 @@ namespace ix
|
|||||||
tags.append(tag);
|
tags.append(tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg["data"]["info"].isMember("level_str"))
|
|
||||||
{
|
|
||||||
// https://docs.sentry.io/enriching-error-data/context/?platform=python#setting-the-level
|
|
||||||
std::string level = msg["data"]["info"]["level_str"].asString();
|
|
||||||
if (level == "critical")
|
|
||||||
{
|
|
||||||
level = "fatal";
|
|
||||||
}
|
|
||||||
payload["level"] = level;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,6 @@ namespace snake
|
|||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool disablePong;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isAppKeyValid(const AppConfig& appConfig, std::string appkey);
|
bool isAppKeyValid(const AppConfig& appConfig, std::string appkey);
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
RedisServer::RedisServer(int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
|
RedisServer::RedisServer(int port, const std::string& host, int backlog, size_t maxConnections)
|
||||||
: SocketServer(port, host, backlog, maxConnections, addressFamily)
|
: SocketServer(port, host, backlog, maxConnections)
|
||||||
, _connectedClientsCount(0)
|
, _connectedClientsCount(0)
|
||||||
, _stopHandlingConnections(false)
|
, _stopHandlingConnections(false)
|
||||||
{
|
{
|
||||||
|
@ -25,8 +25,7 @@ namespace ix
|
|||||||
RedisServer(int port = SocketServer::kDefaultPort,
|
RedisServer(int port = SocketServer::kDefaultPort,
|
||||||
const std::string& host = SocketServer::kDefaultHost,
|
const std::string& host = SocketServer::kDefaultHost,
|
||||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
size_t maxConnections = SocketServer::kDefaultMaxConnections);
|
||||||
int addressFamily = SocketServer::kDefaultAddressFamily);
|
|
||||||
virtual ~RedisServer();
|
virtual ~RedisServer();
|
||||||
virtual void stop() final;
|
virtual void stop() final;
|
||||||
|
|
||||||
|
@ -21,15 +21,6 @@ namespace snake
|
|||||||
, _server(appConfig.port, appConfig.hostname)
|
, _server(appConfig.port, appConfig.hostname)
|
||||||
{
|
{
|
||||||
_server.setTLSOptions(appConfig.socketTLSOptions);
|
_server.setTLSOptions(appConfig.socketTLSOptions);
|
||||||
|
|
||||||
if (appConfig.disablePong)
|
|
||||||
{
|
|
||||||
_server.disablePong();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "Listening on " << appConfig.hostname << ":" << appConfig.port;
|
|
||||||
ix::IXCoreLogger::Log(ss.str().c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -42,8 +42,8 @@ namespace
|
|||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
HttpServer::HttpServer(int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
|
HttpServer::HttpServer(int port, const std::string& host, int backlog, size_t maxConnections)
|
||||||
: SocketServer(port, host, backlog, maxConnections, addressFamily)
|
: SocketServer(port, host, backlog, maxConnections)
|
||||||
, _connectedClientsCount(0)
|
, _connectedClientsCount(0)
|
||||||
{
|
{
|
||||||
setDefaultConnectionCallback();
|
setDefaultConnectionCallback();
|
||||||
|
@ -28,8 +28,7 @@ namespace ix
|
|||||||
HttpServer(int port = SocketServer::kDefaultPort,
|
HttpServer(int port = SocketServer::kDefaultPort,
|
||||||
const std::string& host = SocketServer::kDefaultHost,
|
const std::string& host = SocketServer::kDefaultHost,
|
||||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
size_t maxConnections = SocketServer::kDefaultMaxConnections);
|
||||||
int addressFamily = SocketServer::kDefaultAddressFamily);
|
|
||||||
virtual ~HttpServer();
|
virtual ~HttpServer();
|
||||||
virtual void stop() final;
|
virtual void stop() final;
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sstream>
|
||||||
#define socketerrno errno
|
#define socketerrno errno
|
||||||
|
|
||||||
#include <Security/SecureTransport.h>
|
#include <Security/SecureTransport.h>
|
||||||
@ -31,12 +32,17 @@ namespace ix
|
|||||||
, _sslContext(nullptr)
|
, _sslContext(nullptr)
|
||||||
, _tlsOptions(tlsOptions)
|
, _tlsOptions(tlsOptions)
|
||||||
{
|
{
|
||||||
;
|
_sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
|
||||||
|
SSLSetIOFuncs(
|
||||||
|
_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketAppleSSL::~SocketAppleSSL()
|
SocketAppleSSL::~SocketAppleSSL()
|
||||||
{
|
{
|
||||||
SocketAppleSSL::close();
|
CFRelease(_sslContext);
|
||||||
|
_sslContext = nullptr;
|
||||||
|
|
||||||
|
Socket::close();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SocketAppleSSL::getSSLErrorDescription(OSStatus status)
|
std::string SocketAppleSSL::getSSLErrorDescription(OSStatus status)
|
||||||
@ -177,14 +183,16 @@ namespace ix
|
|||||||
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
|
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
|
||||||
if (_sockfd == -1) return false;
|
if (_sockfd == -1) return false;
|
||||||
|
|
||||||
_sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
|
|
||||||
|
|
||||||
SSLSetIOFuncs(
|
|
||||||
_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
|
|
||||||
SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
|
SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
|
||||||
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
|
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
|
||||||
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
|
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
|
||||||
|
|
||||||
|
// Record a peer id, which speed up SSL connection when reconnecting to the same host
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << host << ":" << port;
|
||||||
|
_peerId = ss.str();
|
||||||
|
SSLSetPeerID(_sslContext, (void*) _peerId.c_str(), _peerId.size());
|
||||||
|
|
||||||
if (_tlsOptions.isPeerVerifyDisabled())
|
if (_tlsOptions.isPeerVerifyDisabled())
|
||||||
{
|
{
|
||||||
Boolean option(1);
|
Boolean option(1);
|
||||||
@ -227,12 +235,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_mutex);
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
|
||||||
if (_sslContext == nullptr) return;
|
|
||||||
|
|
||||||
SSLClose(_sslContext);
|
SSLClose(_sslContext);
|
||||||
CFRelease(_sslContext);
|
|
||||||
_sslContext = nullptr;
|
|
||||||
|
|
||||||
Socket::close();
|
Socket::close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,8 @@ namespace ix
|
|||||||
mutable std::mutex _mutex; // AppleSSL routines are not thread-safe
|
mutable std::mutex _mutex; // AppleSSL routines are not thread-safe
|
||||||
|
|
||||||
SocketTLSOptions _tlsOptions;
|
SocketTLSOptions _tlsOptions;
|
||||||
|
|
||||||
|
std::string _peerId;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -19,10 +19,12 @@
|
|||||||
#include <linux/tcp.h>
|
#include <linux/tcp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// This function can be cancelled every 50 ms
|
// This function can be cancelled every 10 ms
|
||||||
// This is important so that we don't block the main UI thread when shutting down a
|
// This is important so that we don't block the main UI thread when shutting down a
|
||||||
// connection which is already trying to reconnect, and can be blocked waiting for
|
// connection which is already trying to reconnect, and can be blocked waiting for
|
||||||
// ::connect to respond.
|
// ::connect to respond.
|
||||||
@ -44,8 +46,15 @@ namespace ix
|
|||||||
// block us for too long
|
// block us for too long
|
||||||
SocketConnect::configure(fd);
|
SocketConnect::configure(fd);
|
||||||
|
|
||||||
|
auto start = std::chrono::system_clock::now();
|
||||||
|
|
||||||
int res = ::connect(fd, address->ai_addr, address->ai_addrlen);
|
int res = ::connect(fd, address->ai_addr, address->ai_addrlen);
|
||||||
|
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
|
||||||
|
auto ms = milliseconds.count();
|
||||||
|
std::cout << "tcp connection completed in " << ms << "ms" << std::endl;
|
||||||
|
|
||||||
if (res == -1 && !Socket::isWaitNeeded())
|
if (res == -1 && !Socket::isWaitNeeded())
|
||||||
{
|
{
|
||||||
errMsg = strerror(Socket::getErrno());
|
errMsg = strerror(Socket::getErrno());
|
||||||
@ -98,11 +107,19 @@ namespace ix
|
|||||||
std::string& errMsg,
|
std::string& errMsg,
|
||||||
const CancellationRequest& isCancellationRequested)
|
const CancellationRequest& isCancellationRequested)
|
||||||
{
|
{
|
||||||
|
auto start = std::chrono::system_clock::now();
|
||||||
|
|
||||||
//
|
//
|
||||||
// First do DNS resolution
|
// First do DNS resolution
|
||||||
//
|
//
|
||||||
auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
|
auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
|
||||||
struct addrinfo* res = dnsLookup->resolve(errMsg, isCancellationRequested);
|
struct addrinfo* res = dnsLookup->resolve(errMsg, isCancellationRequested);
|
||||||
|
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
|
||||||
|
auto ms = milliseconds.count();
|
||||||
|
std::cout << "dns resolution completed in " << ms << "ms" << std::endl;
|
||||||
|
|
||||||
if (res == nullptr)
|
if (res == nullptr)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -21,18 +21,15 @@ namespace ix
|
|||||||
const std::string SocketServer::kDefaultHost("127.0.0.1");
|
const std::string SocketServer::kDefaultHost("127.0.0.1");
|
||||||
const int SocketServer::kDefaultTcpBacklog(5);
|
const int SocketServer::kDefaultTcpBacklog(5);
|
||||||
const size_t SocketServer::kDefaultMaxConnections(32);
|
const size_t SocketServer::kDefaultMaxConnections(32);
|
||||||
const int SocketServer::kDefaultAddressFamily(AF_INET);
|
|
||||||
|
|
||||||
SocketServer::SocketServer(int port,
|
SocketServer::SocketServer(int port,
|
||||||
const std::string& host,
|
const std::string& host,
|
||||||
int backlog,
|
int backlog,
|
||||||
size_t maxConnections,
|
size_t maxConnections)
|
||||||
int addressFamily)
|
|
||||||
: _port(port)
|
: _port(port)
|
||||||
, _host(host)
|
, _host(host)
|
||||||
, _backlog(backlog)
|
, _backlog(backlog)
|
||||||
, _maxConnections(maxConnections)
|
, _maxConnections(maxConnections)
|
||||||
, _addressFamily(addressFamily)
|
|
||||||
, _serverFd(-1)
|
, _serverFd(-1)
|
||||||
, _stop(false)
|
, _stop(false)
|
||||||
, _stopGc(false)
|
, _stopGc(false)
|
||||||
@ -59,15 +56,10 @@ namespace ix
|
|||||||
|
|
||||||
std::pair<bool, std::string> SocketServer::listen()
|
std::pair<bool, std::string> SocketServer::listen()
|
||||||
{
|
{
|
||||||
if (_addressFamily != AF_INET && _addressFamily != AF_INET6)
|
struct sockaddr_in server; // server address information
|
||||||
{
|
|
||||||
std::string errMsg("SocketServer::listen() AF_INET and AF_INET6 are currently "
|
|
||||||
"the only supported address families");
|
|
||||||
return std::make_pair(false, errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a socket for accepting connections.
|
// Get a socket for accepting connections.
|
||||||
if ((_serverFd = socket(_addressFamily, SOCK_STREAM, 0)) < 0)
|
if ((_serverFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "SocketServer::listen() error creating socket): " << strerror(Socket::getErrno());
|
ss << "SocketServer::listen() error creating socket): " << strerror(Socket::getErrno());
|
||||||
@ -87,59 +79,27 @@ namespace ix
|
|||||||
return std::make_pair(false, ss.str());
|
return std::make_pair(false, ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_addressFamily == AF_INET)
|
// Bind the socket to the server address.
|
||||||
|
server.sin_family = AF_INET;
|
||||||
|
server.sin_port = htons(_port);
|
||||||
|
|
||||||
|
// Using INADDR_ANY trigger a pop-up box as binding to any address is detected
|
||||||
|
// by the osx firewall. We need to codesign the binary with a self-signed cert
|
||||||
|
// to allow that, but this is a bit of a pain. (this is what node or python would do).
|
||||||
|
//
|
||||||
|
// Using INADDR_LOOPBACK also does not work ... while it should.
|
||||||
|
// We default to 127.0.0.1 (localhost)
|
||||||
|
//
|
||||||
|
server.sin_addr.s_addr = inet_addr(_host.c_str());
|
||||||
|
|
||||||
|
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
|
||||||
{
|
{
|
||||||
struct sockaddr_in server;
|
std::stringstream ss;
|
||||||
server.sin_family = _addressFamily;
|
ss << "SocketServer::listen() error calling bind "
|
||||||
server.sin_port = htons(_port);
|
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
|
||||||
|
|
||||||
if (inet_pton(_addressFamily, _host.c_str(), &server.sin_addr.s_addr) <= 0)
|
Socket::closeSocket(_serverFd);
|
||||||
{
|
return std::make_pair(false, ss.str());
|
||||||
std::stringstream ss;
|
|
||||||
ss << "SocketServer::listen() error calling inet_pton "
|
|
||||||
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
|
|
||||||
|
|
||||||
Socket::closeSocket(_serverFd);
|
|
||||||
return std::make_pair(false, ss.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind the socket to the server address.
|
|
||||||
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "SocketServer::listen() error calling bind "
|
|
||||||
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
|
|
||||||
|
|
||||||
Socket::closeSocket(_serverFd);
|
|
||||||
return std::make_pair(false, ss.str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // AF_INET6
|
|
||||||
{
|
|
||||||
struct sockaddr_in6 server;
|
|
||||||
server.sin6_family = _addressFamily;
|
|
||||||
server.sin6_port = htons(_port);
|
|
||||||
|
|
||||||
if (inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= 0)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "SocketServer::listen() error calling inet_pton "
|
|
||||||
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
|
|
||||||
|
|
||||||
Socket::closeSocket(_serverFd);
|
|
||||||
return std::make_pair(false, ss.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind the socket to the server address.
|
|
||||||
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "SocketServer::listen() error calling bind "
|
|
||||||
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
|
|
||||||
|
|
||||||
Socket::closeSocket(_serverFd);
|
|
||||||
return std::make_pair(false, ss.str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -36,8 +36,7 @@ namespace ix
|
|||||||
SocketServer(int port = SocketServer::kDefaultPort,
|
SocketServer(int port = SocketServer::kDefaultPort,
|
||||||
const std::string& host = SocketServer::kDefaultHost,
|
const std::string& host = SocketServer::kDefaultHost,
|
||||||
int backlog = SocketServer::kDefaultTcpBacklog,
|
int backlog = SocketServer::kDefaultTcpBacklog,
|
||||||
size_t maxConnections = SocketServer::kDefaultMaxConnections,
|
size_t maxConnections = SocketServer::kDefaultMaxConnections);
|
||||||
int addressFamily = SocketServer::kDefaultAddressFamily);
|
|
||||||
virtual ~SocketServer();
|
virtual ~SocketServer();
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
|
|
||||||
@ -50,7 +49,6 @@ namespace ix
|
|||||||
const static std::string kDefaultHost;
|
const static std::string kDefaultHost;
|
||||||
const static int kDefaultTcpBacklog;
|
const static int kDefaultTcpBacklog;
|
||||||
const static size_t kDefaultMaxConnections;
|
const static size_t kDefaultMaxConnections;
|
||||||
const static int kDefaultAddressFamily;
|
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
std::pair<bool, std::string> listen();
|
std::pair<bool, std::string> listen();
|
||||||
@ -71,7 +69,6 @@ namespace ix
|
|||||||
std::string _host;
|
std::string _host;
|
||||||
int _backlog;
|
int _backlog;
|
||||||
size_t _maxConnections;
|
size_t _maxConnections;
|
||||||
int _addressFamily;
|
|
||||||
|
|
||||||
// socket for accepting connections
|
// socket for accepting connections
|
||||||
int _serverFd;
|
int _serverFd;
|
||||||
|
@ -169,7 +169,6 @@ namespace ix
|
|||||||
// wait until working thread will exit
|
// wait until working thread will exit
|
||||||
// it will exit after close operation is finished
|
// it will exit after close operation is finished
|
||||||
_stop = true;
|
_stop = true;
|
||||||
_sleepCondition.notify_one();
|
|
||||||
_thread.join();
|
_thread.join();
|
||||||
_stop = false;
|
_stop = false;
|
||||||
}
|
}
|
||||||
@ -283,13 +282,8 @@ namespace ix
|
|||||||
// Only sleep if we are retrying
|
// Only sleep if we are retrying
|
||||||
if (duration.count() > 0)
|
if (duration.count() > 0)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(_sleepMutex);
|
// to do: make sleeping conditional
|
||||||
_sleepCondition.wait_for(lock, duration);
|
std::this_thread::sleep_for(duration);
|
||||||
}
|
|
||||||
|
|
||||||
if (_stop)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to connect synchronously
|
// Try to connect synchronously
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <condition_variable>
|
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
@ -141,10 +140,6 @@ namespace ix
|
|||||||
static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries;
|
static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries;
|
||||||
uint32_t _maxWaitBetweenReconnectionRetries;
|
uint32_t _maxWaitBetweenReconnectionRetries;
|
||||||
|
|
||||||
// Make the sleeping in the automatic reconnection cancellable
|
|
||||||
std::mutex _sleepMutex;
|
|
||||||
std::condition_variable _sleepCondition;
|
|
||||||
|
|
||||||
std::atomic<int> _handshakeTimeoutSecs;
|
std::atomic<int> _handshakeTimeoutSecs;
|
||||||
static const int kDefaultHandShakeTimeoutSecs;
|
static const int kDefaultHandShakeTimeoutSecs;
|
||||||
|
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
@ -97,8 +100,16 @@ namespace ix
|
|||||||
auto isCancellationRequested =
|
auto isCancellationRequested =
|
||||||
makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
|
makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
|
||||||
|
|
||||||
|
auto start = std::chrono::system_clock::now();
|
||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
|
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
|
||||||
|
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
|
||||||
|
auto ms = milliseconds.count();
|
||||||
|
std::cout << "connection completed in " << ms << "ms" << std::endl;
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
@ -23,9 +23,8 @@ namespace ix
|
|||||||
const std::string& host,
|
const std::string& host,
|
||||||
int backlog,
|
int backlog,
|
||||||
size_t maxConnections,
|
size_t maxConnections,
|
||||||
int handshakeTimeoutSecs,
|
int handshakeTimeoutSecs)
|
||||||
int addressFamily)
|
: SocketServer(port, host, backlog, maxConnections)
|
||||||
: SocketServer(port, host, backlog, maxConnections, addressFamily)
|
|
||||||
, _handshakeTimeoutSecs(handshakeTimeoutSecs)
|
, _handshakeTimeoutSecs(handshakeTimeoutSecs)
|
||||||
, _enablePong(kDefaultEnablePong)
|
, _enablePong(kDefaultEnablePong)
|
||||||
{
|
{
|
||||||
@ -107,6 +106,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logInfo("WebSocketServer::handleConnection() done");
|
||||||
connectionState->setTerminated();
|
connectionState->setTerminated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +29,7 @@ namespace ix
|
|||||||
const std::string& host = SocketServer::kDefaultHost,
|
const std::string& host = SocketServer::kDefaultHost,
|
||||||
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);
|
|
||||||
virtual ~WebSocketServer();
|
virtual ~WebSocketServer();
|
||||||
virtual void stop() final;
|
virtual void stop() final;
|
||||||
|
|
||||||
@ -42,8 +41,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();
|
||||||
|
|
||||||
const static int kDefaultHandShakeTimeoutSecs;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Member variables
|
// Member variables
|
||||||
int _handshakeTimeoutSecs;
|
int _handshakeTimeoutSecs;
|
||||||
@ -54,6 +51,7 @@ namespace ix
|
|||||||
std::mutex _clientsMutex;
|
std::mutex _clientsMutex;
|
||||||
std::set<std::shared_ptr<WebSocket>> _clients;
|
std::set<std::shared_ptr<WebSocket>> _clients;
|
||||||
|
|
||||||
|
const static int kDefaultHandShakeTimeoutSecs;
|
||||||
const static bool kDefaultEnablePong;
|
const static bool kDefaultEnablePong;
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
|
@ -151,7 +151,18 @@ namespace ix
|
|||||||
|
|
||||||
std::string errorMsg;
|
std::string errorMsg;
|
||||||
bool tls = protocol == "wss";
|
bool tls = protocol == "wss";
|
||||||
_socket = createSocket(tls, -1, errorMsg, _socketTLSOptions);
|
|
||||||
|
if (_host == host)
|
||||||
|
{
|
||||||
|
_socket->close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_socket = createSocket(tls, -1, errorMsg, _socketTLSOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record the host for later
|
||||||
|
_host = host;
|
||||||
|
|
||||||
if (!_socket)
|
if (!_socket)
|
||||||
{
|
{
|
||||||
|
@ -107,7 +107,7 @@ namespace ix
|
|||||||
size_t bufferedAmount() const;
|
size_t bufferedAmount() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _url;
|
std::string _host;
|
||||||
|
|
||||||
struct wsheader_type
|
struct wsheader_type
|
||||||
{
|
{
|
||||||
|
@ -6,4 +6,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define IX_WEBSOCKET_VERSION "8.0.5"
|
#define IX_WEBSOCKET_VERSION "7.9.6"
|
||||||
|
3
makefile
3
makefile
@ -49,9 +49,6 @@ BUILD := ${NAME}:build
|
|||||||
print_version:
|
print_version:
|
||||||
@echo 'IXWebSocket version =>' ${TAG}
|
@echo 'IXWebSocket version =>' ${TAG}
|
||||||
|
|
||||||
set_version:
|
|
||||||
sh tools/update_version.sh ${VERSION}
|
|
||||||
|
|
||||||
docker_test:
|
docker_test:
|
||||||
docker build -f docker/Dockerfile.debian -t bsergean/ixwebsocket_test:build .
|
docker build -f docker/Dockerfile.debian -t bsergean/ixwebsocket_test:build .
|
||||||
|
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
#/bin/sh
|
|
||||||
|
|
||||||
ver_gt() {
|
|
||||||
[ "$1" != "$2" ] && [ "$2" == "$(echo "$1\n$2" | sort -V | head -n1)" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
PROJECT_ROOT="$(dirname ${SCRIPT_DIR})"
|
|
||||||
CUR_VSN=$(bash $SCRIPT_DIR/extract_version.sh)
|
|
||||||
NEW_VSN="$1"
|
|
||||||
|
|
||||||
|
|
||||||
check_ver() {
|
|
||||||
if [[ $NEW_VSN =~ ^[0-9.]+$ ]]; then
|
|
||||||
:
|
|
||||||
else
|
|
||||||
echo "Invalid version '$NEW_VSN'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_ver_increase() {
|
|
||||||
if ! ver_gt $NEW_VSN $CUR_VSN; then
|
|
||||||
echo "Invalid version '$NEW_VSN'. Must be greater than current version $CUR_VSN"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_changelog() {
|
|
||||||
if ! egrep "\b$NEW_VSN\b" $PROJECT_ROOT/docs/CHANGELOG.md >/dev/null; then
|
|
||||||
echo "Invalid version '$NEW_VSN'. Missing entry in CHANGELOG.md"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
set_version() {
|
|
||||||
sed -i '' "s/$CUR_VSN/$NEW_VSN/g" $PROJECT_ROOT/ixwebsocket/IXWebSocketVersion.h
|
|
||||||
echo "Set version to '$NEW_VSN'"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
check_ver
|
|
||||||
check_ver_increase
|
|
||||||
check_changelog
|
|
||||||
set_version
|
|
@ -94,12 +94,10 @@ int main(int argc, char** argv)
|
|||||||
bool disableAutomaticReconnection = false;
|
bool disableAutomaticReconnection = false;
|
||||||
bool disablePerMessageDeflate = false;
|
bool disablePerMessageDeflate = false;
|
||||||
bool greetings = false;
|
bool greetings = false;
|
||||||
bool ipv6 = false;
|
|
||||||
bool binaryMode = false;
|
bool binaryMode = false;
|
||||||
bool redirect = false;
|
bool redirect = false;
|
||||||
bool version = false;
|
bool version = false;
|
||||||
bool verifyNone = false;
|
bool verifyNone = false;
|
||||||
bool disablePong = false;
|
|
||||||
int port = 8008;
|
int port = 8008;
|
||||||
int redisPort = 6379;
|
int redisPort = 6379;
|
||||||
int statsdPort = 8125;
|
int statsdPort = 8125;
|
||||||
@ -173,7 +171,6 @@ int main(int argc, char** argv)
|
|||||||
echoServerApp->add_option("--port", port, "Port");
|
echoServerApp->add_option("--port", port, "Port");
|
||||||
echoServerApp->add_option("--host", hostname, "Hostname");
|
echoServerApp->add_option("--host", hostname, "Hostname");
|
||||||
echoServerApp->add_flag("-g", greetings, "Verbose");
|
echoServerApp->add_flag("-g", greetings, "Verbose");
|
||||||
echoServerApp->add_flag("-6", ipv6, "IpV6");
|
|
||||||
addTLSOptions(echoServerApp);
|
addTLSOptions(echoServerApp);
|
||||||
|
|
||||||
CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server");
|
CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server");
|
||||||
@ -310,7 +307,6 @@ int main(int argc, char** argv)
|
|||||||
snakeApp->add_option("--apps_config_path", appsConfigPath, "Path to auth data")
|
snakeApp->add_option("--apps_config_path", appsConfigPath, "Path to auth data")
|
||||||
->check(CLI::ExistingPath);
|
->check(CLI::ExistingPath);
|
||||||
snakeApp->add_flag("-v", verbose, "Verbose");
|
snakeApp->add_flag("-v", verbose, "Verbose");
|
||||||
snakeApp->add_flag("-d", disablePong, "Disable Pongs");
|
|
||||||
addTLSOptions(snakeApp);
|
addTLSOptions(snakeApp);
|
||||||
|
|
||||||
CLI::App* httpServerApp = app.add_subcommand("httpd", "HTTP server");
|
CLI::App* httpServerApp = app.add_subcommand("httpd", "HTTP server");
|
||||||
@ -394,7 +390,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else if (app.got_subcommand("echo_server"))
|
else if (app.got_subcommand("echo_server"))
|
||||||
{
|
{
|
||||||
ret = ix::ws_echo_server_main(port, greetings, hostname, tlsOptions, ipv6);
|
ret = ix::ws_echo_server_main(port, greetings, hostname, tlsOptions);
|
||||||
}
|
}
|
||||||
else if (app.got_subcommand("broadcast_server"))
|
else if (app.got_subcommand("broadcast_server"))
|
||||||
{
|
{
|
||||||
@ -494,8 +490,7 @@ int main(int argc, char** argv)
|
|||||||
redisPassword,
|
redisPassword,
|
||||||
verbose,
|
verbose,
|
||||||
appsConfigPath,
|
appsConfigPath,
|
||||||
tlsOptions,
|
tlsOptions);
|
||||||
disablePong);
|
|
||||||
}
|
}
|
||||||
else if (app.got_subcommand("httpd"))
|
else if (app.got_subcommand("httpd"))
|
||||||
{
|
{
|
||||||
|
7
ws/ws.h
7
ws/ws.h
@ -29,9 +29,7 @@ namespace ix
|
|||||||
int ws_echo_server_main(int port,
|
int ws_echo_server_main(int port,
|
||||||
bool greetings,
|
bool greetings,
|
||||||
const std::string& hostname,
|
const std::string& hostname,
|
||||||
const ix::SocketTLSOptions& tlsOptions,
|
const ix::SocketTLSOptions& tlsOptions);
|
||||||
bool ipv6);
|
|
||||||
|
|
||||||
int ws_broadcast_server_main(int port,
|
int ws_broadcast_server_main(int port,
|
||||||
const std::string& hostname,
|
const std::string& hostname,
|
||||||
const ix::SocketTLSOptions& tlsOptions);
|
const ix::SocketTLSOptions& tlsOptions);
|
||||||
@ -142,8 +140,7 @@ namespace ix
|
|||||||
const std::string& redisPassword,
|
const std::string& redisPassword,
|
||||||
bool verbose,
|
bool verbose,
|
||||||
const std::string& appsConfigPath,
|
const std::string& appsConfigPath,
|
||||||
const ix::SocketTLSOptions& tlsOptions,
|
const ix::SocketTLSOptions& tlsOptions);
|
||||||
bool disablePong);
|
|
||||||
|
|
||||||
int ws_httpd_main(int port,
|
int ws_httpd_main(int port,
|
||||||
const std::string& hostname,
|
const std::string& hostname,
|
||||||
|
@ -58,6 +58,8 @@ namespace ix
|
|||||||
std::random_shuffle(games.begin(), games.end());
|
std::random_shuffle(games.begin(), games.end());
|
||||||
std::string game = games[0];
|
std::string game = games[0];
|
||||||
|
|
||||||
|
spdlog::info("Sending event for game '{}'", game);
|
||||||
|
|
||||||
_condition.wait(lock, [this] { return !_stop; });
|
_condition.wait(lock, [this] { return !_stop; });
|
||||||
|
|
||||||
if (_queues[game].empty())
|
if (_queues[game].empty())
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ixwebsocket/IXWebSocketServer.h>
|
#include <ixwebsocket/IXWebSocketServer.h>
|
||||||
#include <ixwebsocket/IXNetSystem.h>
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@ -14,18 +13,11 @@ namespace ix
|
|||||||
int ws_echo_server_main(int port,
|
int ws_echo_server_main(int port,
|
||||||
bool greetings,
|
bool greetings,
|
||||||
const std::string& hostname,
|
const std::string& hostname,
|
||||||
const ix::SocketTLSOptions& tlsOptions,
|
const ix::SocketTLSOptions& tlsOptions)
|
||||||
bool ipv6)
|
|
||||||
{
|
{
|
||||||
spdlog::info("Listening on {}:{}", hostname, port);
|
spdlog::info("Listening on {}:{}", hostname, port);
|
||||||
|
|
||||||
ix::WebSocketServer server(port,
|
ix::WebSocketServer server(port, hostname);
|
||||||
hostname,
|
|
||||||
SocketServer::kDefaultTcpBacklog,
|
|
||||||
SocketServer::kDefaultMaxConnections,
|
|
||||||
WebSocketServer::kDefaultHandShakeTimeoutSecs,
|
|
||||||
(ipv6) ? AF_INET6 : AF_INET);
|
|
||||||
|
|
||||||
server.setTLSOptions(tlsOptions);
|
server.setTLSOptions(tlsOptions);
|
||||||
|
|
||||||
server.setOnConnectionCallback(
|
server.setOnConnectionCallback(
|
||||||
|
@ -44,8 +44,7 @@ namespace ix
|
|||||||
const std::string& redisPassword,
|
const std::string& redisPassword,
|
||||||
bool verbose,
|
bool verbose,
|
||||||
const std::string& appsConfigPath,
|
const std::string& appsConfigPath,
|
||||||
const SocketTLSOptions& socketTLSOptions,
|
const SocketTLSOptions& socketTLSOptions)
|
||||||
bool disablePong)
|
|
||||||
{
|
{
|
||||||
snake::AppConfig appConfig;
|
snake::AppConfig appConfig;
|
||||||
appConfig.port = port;
|
appConfig.port = port;
|
||||||
@ -54,7 +53,6 @@ namespace ix
|
|||||||
appConfig.redisPort = redisPort;
|
appConfig.redisPort = redisPort;
|
||||||
appConfig.redisPassword = redisPassword;
|
appConfig.redisPassword = redisPassword;
|
||||||
appConfig.socketTLSOptions = socketTLSOptions;
|
appConfig.socketTLSOptions = socketTLSOptions;
|
||||||
appConfig.disablePong = disablePong;
|
|
||||||
|
|
||||||
// Parse config file
|
// Parse config file
|
||||||
auto str = readAsString(appsConfigPath);
|
auto str = readAsString(appsConfigPath);
|
||||||
|
Reference in New Issue
Block a user