Compare commits
4 Commits
v1.5.2
...
feature/no
Author | SHA1 | Date | |
---|---|---|---|
e02679f744 | |||
21c155339e | |||
bbf34aef29 | |||
225aade89d |
@ -37,7 +37,6 @@ set( IXWEBSOCKET_SOURCES
|
|||||||
ixwebsocket/IXWebSocketHttpHeaders.cpp
|
ixwebsocket/IXWebSocketHttpHeaders.cpp
|
||||||
ixwebsocket/IXHttpClient.cpp
|
ixwebsocket/IXHttpClient.cpp
|
||||||
ixwebsocket/IXUrlParser.cpp
|
ixwebsocket/IXUrlParser.cpp
|
||||||
ixwebsocket/LUrlParser.cpp
|
|
||||||
ixwebsocket/IXSelectInterrupt.cpp
|
ixwebsocket/IXSelectInterrupt.cpp
|
||||||
ixwebsocket/IXSelectInterruptFactory.cpp
|
ixwebsocket/IXSelectInterruptFactory.cpp
|
||||||
ixwebsocket/IXConnectionState.cpp
|
ixwebsocket/IXConnectionState.cpp
|
||||||
@ -66,7 +65,6 @@ set( IXWEBSOCKET_HEADERS
|
|||||||
ixwebsocket/libwshandshake.hpp
|
ixwebsocket/libwshandshake.hpp
|
||||||
ixwebsocket/IXHttpClient.h
|
ixwebsocket/IXHttpClient.h
|
||||||
ixwebsocket/IXUrlParser.h
|
ixwebsocket/IXUrlParser.h
|
||||||
ixwebsocket/LUrlParser.h
|
|
||||||
ixwebsocket/IXSelectInterrupt.h
|
ixwebsocket/IXSelectInterrupt.h
|
||||||
ixwebsocket/IXSelectInterruptFactory.h
|
ixwebsocket/IXSelectInterruptFactory.h
|
||||||
ixwebsocket/IXConnectionState.h
|
ixwebsocket/IXConnectionState.h
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
* iOS
|
* iOS
|
||||||
* Linux
|
* Linux
|
||||||
* Android
|
* Android
|
||||||
* Windows (no TLS)
|
|
||||||
|
The code was made to compile once on Windows but support is currently broken on this platform.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@ -198,8 +199,6 @@ make install # will install to /usr/local on Unix, on macOS it is a good idea to
|
|||||||
|
|
||||||
Headers and a static library will be installed to the target dir.
|
Headers and a static library will be installed to the target dir.
|
||||||
|
|
||||||
A [conan](https://conan.io/) file is available at [conan-IXWebSocket](https://github.com/Zinnion/conan-IXWebSocket).
|
|
||||||
|
|
||||||
There is a unittest which can be executed by typing `make test`.
|
There is a unittest which can be executed by typing `make test`.
|
||||||
|
|
||||||
There is a Dockerfile for running some code on Linux. To use docker-compose you must make a docker container first.
|
There is a Dockerfile for running some code on Linux. To use docker-compose you must make a docker container first.
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
image:
|
image:
|
||||||
- Visual Studio 2017
|
- Visual Studio 2017
|
||||||
|
- Ubuntu
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- ls -al
|
- ls -al
|
||||||
- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
|
- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
|
||||||
- cd test
|
- python test/run.py
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- cmake -G"NMake Makefiles" ..
|
|
||||||
- nmake
|
|
||||||
- ixwebsocket_unittest.exe
|
|
||||||
|
|
||||||
build: off
|
build: off
|
||||||
|
@ -35,6 +35,7 @@ namespace ix
|
|||||||
_activeJobs.erase(_id);
|
_activeJobs.erase(_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we want hostname to be copied, not passed as a const reference
|
||||||
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
|
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg)
|
std::string& errMsg)
|
||||||
|
@ -47,8 +47,9 @@ namespace ix
|
|||||||
|
|
||||||
std::string protocol, host, path, query;
|
std::string protocol, host, path, query;
|
||||||
int port;
|
int port;
|
||||||
|
bool websocket = false;
|
||||||
|
|
||||||
if (!UrlParser::parse(url, protocol, host, path, query, port))
|
if (!UrlParser::parse(url, protocol, host, path, query, port, websocket))
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Cannot parse url: " << url;
|
ss << "Cannot parse url: " << url;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* IXNetSystem.cpp
|
* IXNetSystem.cpp
|
||||||
* Author: Korchynskyi Dmytro
|
* Author: Benjamin Sergeant
|
||||||
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -189,27 +189,11 @@ namespace ix
|
|||||||
|
|
||||||
int Socket::getErrno()
|
int Socket::getErrno()
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
err = WSAGetLastError();
|
return WSAGetLastError();
|
||||||
#else
|
#else
|
||||||
err = errno;
|
return errno;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Socket::isWaitNeeded()
|
|
||||||
{
|
|
||||||
int err = getErrno();
|
|
||||||
|
|
||||||
if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::closeSocket(int fd)
|
void Socket::closeSocket(int fd)
|
||||||
@ -244,7 +228,8 @@ namespace ix
|
|||||||
return ret == len;
|
return ret == len;
|
||||||
}
|
}
|
||||||
// There is possibly something to be writen, try again
|
// There is possibly something to be writen, try again
|
||||||
else if (ret < 0 && Socket::isWaitNeeded())
|
else if (ret < 0 && (getErrno() == EWOULDBLOCK ||
|
||||||
|
getErrno() == EAGAIN))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -272,7 +257,8 @@ namespace ix
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// There is possibly something to be read, try again
|
// There is possibly something to be read, try again
|
||||||
else if (ret < 0 && Socket::isWaitNeeded())
|
else if (ret < 0 && (getErrno() == EWOULDBLOCK ||
|
||||||
|
getErrno() == EAGAIN))
|
||||||
{
|
{
|
||||||
// Wait with a 1ms timeout until the socket is ready to read.
|
// Wait with a 1ms timeout until the socket is ready to read.
|
||||||
// This way we are not busy looping
|
// This way we are not busy looping
|
||||||
@ -331,12 +317,13 @@ namespace ix
|
|||||||
size_t size = std::min(kChunkSize, length - output.size());
|
size_t size = std::min(kChunkSize, length - output.size());
|
||||||
ssize_t ret = recv((char*)&_readBuffer[0], size);
|
ssize_t ret = recv((char*)&_readBuffer[0], size);
|
||||||
|
|
||||||
if (ret <= 0 && !Socket::isWaitNeeded())
|
if (ret <= 0 && (getErrno() != EWOULDBLOCK &&
|
||||||
|
getErrno() != EAGAIN))
|
||||||
{
|
{
|
||||||
// Error
|
// Error
|
||||||
return std::make_pair(false, std::string());
|
return std::make_pair(false, std::string());
|
||||||
}
|
}
|
||||||
else
|
else if (ret > 0)
|
||||||
{
|
{
|
||||||
output.insert(output.end(),
|
output.insert(output.end(),
|
||||||
_readBuffer.begin(),
|
_readBuffer.begin(),
|
||||||
|
@ -16,20 +16,6 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <BaseTsd.h>
|
#include <BaseTsd.h>
|
||||||
typedef SSIZE_T ssize_t;
|
typedef SSIZE_T ssize_t;
|
||||||
|
|
||||||
#undef EWOULDBLOCK
|
|
||||||
#undef EAGAIN
|
|
||||||
#undef EINPROGRESS
|
|
||||||
#undef EBADF
|
|
||||||
#undef EINVAL
|
|
||||||
|
|
||||||
// map to WSA error codes
|
|
||||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
|
||||||
#define EAGAIN WSATRY_AGAIN
|
|
||||||
#define EINPROGRESS WSAEINPROGRESS
|
|
||||||
#define EBADF WSAEBADF
|
|
||||||
#define EINVAL WSAEINVAL
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "IXCancellationRequest.h"
|
#include "IXCancellationRequest.h"
|
||||||
@ -55,6 +41,8 @@ namespace ix
|
|||||||
virtual ~Socket();
|
virtual ~Socket();
|
||||||
bool init(std::string& errorMsg);
|
bool init(std::string& errorMsg);
|
||||||
|
|
||||||
|
void configure();
|
||||||
|
|
||||||
// Functions to check whether there is activity on the socket
|
// Functions to check whether there is activity on the socket
|
||||||
PollResultType poll(int timeoutSecs = kDefaultPollTimeout);
|
PollResultType poll(int timeoutSecs = kDefaultPollTimeout);
|
||||||
bool wakeUpFromPoll(uint8_t wakeUpCode);
|
bool wakeUpFromPoll(uint8_t wakeUpCode);
|
||||||
@ -88,14 +76,14 @@ namespace ix
|
|||||||
const CancellationRequest& isCancellationRequested);
|
const CancellationRequest& isCancellationRequested);
|
||||||
|
|
||||||
static int getErrno();
|
static int getErrno();
|
||||||
static bool isWaitNeeded();
|
|
||||||
static void closeSocket(int fd);
|
|
||||||
|
|
||||||
// Used as special codes for pipe communication
|
// Used as special codes for pipe communication
|
||||||
static const uint64_t kSendRequest;
|
static const uint64_t kSendRequest;
|
||||||
static const uint64_t kCloseRequest;
|
static const uint64_t kCloseRequest;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void closeSocket(int fd);
|
||||||
|
|
||||||
std::atomic<int> _sockfd;
|
std::atomic<int> _sockfd;
|
||||||
std::mutex _socketMutex;
|
std::mutex _socketMutex;
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include "IXSocketConnect.h"
|
#include "IXSocketConnect.h"
|
||||||
#include "IXDNSLookup.h"
|
#include "IXDNSLookup.h"
|
||||||
#include "IXNetSystem.h"
|
#include "IXNetSystem.h"
|
||||||
#include "IXSocket.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -19,6 +18,18 @@
|
|||||||
# include <linux/tcp.h>
|
# include <linux/tcp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void closeSocket(int fd)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
closesocket(fd);
|
||||||
|
#else
|
||||||
|
::close(fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
@ -45,12 +56,11 @@ namespace ix
|
|||||||
// block us for too long
|
// block us for too long
|
||||||
SocketConnect::configure(fd);
|
SocketConnect::configure(fd);
|
||||||
|
|
||||||
int res = ::connect(fd, address->ai_addr, address->ai_addrlen);
|
if (::connect(fd, address->ai_addr, address->ai_addrlen) == -1
|
||||||
|
&& errno != EINPROGRESS && errno != 0)
|
||||||
if (res == -1 && !Socket::isWaitNeeded())
|
|
||||||
{
|
{
|
||||||
errMsg = strerror(Socket::getErrno());
|
errMsg = strerror(errno);
|
||||||
Socket::closeSocket(fd);
|
closeSocket(fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,17 +68,15 @@ namespace ix
|
|||||||
{
|
{
|
||||||
if (isCancellationRequested && isCancellationRequested()) // Must handle timeout as well
|
if (isCancellationRequested && isCancellationRequested()) // Must handle timeout as well
|
||||||
{
|
{
|
||||||
Socket::closeSocket(fd);
|
closeSocket(fd);
|
||||||
errMsg = "Cancelled";
|
errMsg = "Cancelled";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// On Linux the timeout needs to be re-initialized everytime
|
// Use select to check the status of the new connection
|
||||||
// http://man7.org/linux/man-pages/man2/select.2.html
|
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
timeout.tv_sec = 0;
|
timeout.tv_sec = 0;
|
||||||
timeout.tv_usec = 10 * 1000; // 10ms timeout
|
timeout.tv_usec = 10 * 1000; // 10ms timeout
|
||||||
|
|
||||||
fd_set wfds;
|
fd_set wfds;
|
||||||
fd_set efds;
|
fd_set efds;
|
||||||
|
|
||||||
@ -77,13 +85,11 @@ namespace ix
|
|||||||
FD_ZERO(&efds);
|
FD_ZERO(&efds);
|
||||||
FD_SET(fd, &efds);
|
FD_SET(fd, &efds);
|
||||||
|
|
||||||
// Use select to check the status of the new connection
|
if (select(fd + 1, nullptr, &wfds, &efds, &timeout) < 0 &&
|
||||||
res = select(fd + 1, nullptr, &wfds, &efds, &timeout);
|
(errno == EBADF || errno == EINVAL))
|
||||||
|
|
||||||
if (res < 0 && (Socket::getErrno() == EBADF || Socket::getErrno() == EINVAL))
|
|
||||||
{
|
{
|
||||||
Socket::closeSocket(fd);
|
closeSocket(fd);
|
||||||
errMsg = std::string("Connect error, select error: ") + strerror(Socket::getErrno());
|
errMsg = std::string("Connect error, select error: ") + strerror(errno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +110,7 @@ namespace ix
|
|||||||
optval != 0)
|
optval != 0)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
Socket::closeSocket(fd);
|
closeSocket(fd);
|
||||||
errMsg = strerror(optval);
|
errMsg = strerror(optval);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -115,7 +121,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket::closeSocket(fd);
|
closeSocket(fd);
|
||||||
errMsg = "connect timed out after 60 seconds";
|
errMsg = "connect timed out after 60 seconds";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ namespace ix
|
|||||||
<< "at address " << _host << ":" << _port
|
<< "at address " << _host << ":" << _port
|
||||||
<< " : " << strerror(Socket::getErrno());
|
<< " : " << strerror(Socket::getErrno());
|
||||||
|
|
||||||
Socket::closeSocket(_serverFd);
|
::close(_serverFd);
|
||||||
return std::make_pair(false, ss.str());
|
return std::make_pair(false, ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ namespace ix
|
|||||||
<< "at address " << _host << ":" << _port
|
<< "at address " << _host << ":" << _port
|
||||||
<< " : " << strerror(Socket::getErrno());
|
<< " : " << strerror(Socket::getErrno());
|
||||||
|
|
||||||
Socket::closeSocket(_serverFd);
|
::close(_serverFd);
|
||||||
return std::make_pair(false, ss.str());
|
return std::make_pair(false, ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ namespace ix
|
|||||||
<< "at address " << _host << ":" << _port
|
<< "at address " << _host << ":" << _port
|
||||||
<< " : " << strerror(Socket::getErrno());
|
<< " : " << strerror(Socket::getErrno());
|
||||||
|
|
||||||
Socket::closeSocket(_serverFd);
|
::close(_serverFd);
|
||||||
return std::make_pair(false, ss.str());
|
return std::make_pair(false, ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ namespace ix
|
|||||||
_stop = false;
|
_stop = false;
|
||||||
|
|
||||||
_conditionVariable.notify_one();
|
_conditionVariable.notify_one();
|
||||||
Socket::closeSocket(_serverFd);
|
::close(_serverFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketServer::setConnectionStateFactory(
|
void SocketServer::setConnectionStateFactory(
|
||||||
@ -242,18 +242,17 @@ namespace ix
|
|||||||
// Accept a connection.
|
// Accept a connection.
|
||||||
struct sockaddr_in client; // client address information
|
struct sockaddr_in client; // client address information
|
||||||
int clientFd; // socket connected to client
|
int clientFd; // socket connected to client
|
||||||
socklen_t addressLen = sizeof(client);
|
socklen_t addressLen = sizeof(socklen_t);
|
||||||
memset(&client, 0, sizeof(client));
|
memset(&client, 0, sizeof(client));
|
||||||
|
|
||||||
if ((clientFd = accept(_serverFd, (struct sockaddr *)&client, &addressLen)) < 0)
|
if ((clientFd = accept(_serverFd, (struct sockaddr *)&client, &addressLen)) < 0)
|
||||||
{
|
{
|
||||||
if (!Socket::isWaitNeeded())
|
if (Socket::getErrno() != EWOULDBLOCK)
|
||||||
{
|
{
|
||||||
// FIXME: that error should be propagated
|
// FIXME: that error should be propagated
|
||||||
int err = Socket::getErrno();
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "SocketServer::run() error accepting connection: "
|
ss << "SocketServer::run() error accepting connection: "
|
||||||
<< err << ", " << strerror(err);
|
<< strerror(Socket::getErrno());
|
||||||
logError(ss.str());
|
logError(ss.str());
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -267,7 +266,7 @@ namespace ix
|
|||||||
<< "Not accepting connection";
|
<< "Not accepting connection";
|
||||||
logError(ss.str());
|
logError(ss.str());
|
||||||
|
|
||||||
Socket::closeSocket(clientFd);
|
::close(clientFd);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -281,7 +280,7 @@ namespace ix
|
|||||||
if (_stop) return;
|
if (_stop) return;
|
||||||
|
|
||||||
// Launch the handleConnection work asynchronously in its own thread.
|
// Launch the handleConnection work asynchronously in its own thread.
|
||||||
std::lock_guard<std::mutex> lock(_connectionsThreadsMutex);
|
std::lock_guard<std::mutex> lock(_conditionVariableMutex);
|
||||||
_connectionsThreads.push_back(std::make_pair(
|
_connectionsThreads.push_back(std::make_pair(
|
||||||
connectionState,
|
connectionState,
|
||||||
std::thread(&SocketServer::handleConnection,
|
std::thread(&SocketServer::handleConnection,
|
||||||
|
@ -5,32 +5,43 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IXUrlParser.h"
|
#include "IXUrlParser.h"
|
||||||
#include "LUrlParser.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
|
//
|
||||||
|
// The only difference between those 2 regex is the protocol
|
||||||
|
//
|
||||||
|
std::regex UrlParser::_httpRegex("(http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
|
||||||
|
std::regex UrlParser::_webSocketRegex("(ws|wss)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
|
||||||
|
|
||||||
bool UrlParser::parse(const std::string& url,
|
bool UrlParser::parse(const std::string& url,
|
||||||
std::string& protocol,
|
std::string& protocol,
|
||||||
std::string& host,
|
std::string& host,
|
||||||
std::string& path,
|
std::string& path,
|
||||||
std::string& query,
|
std::string& query,
|
||||||
int& port)
|
int& port,
|
||||||
|
bool websocket)
|
||||||
{
|
{
|
||||||
LUrlParser::clParseURL res = LUrlParser::clParseURL::ParseURL(url);
|
std::cmatch what;
|
||||||
|
if (!regex_match(url.c_str(), what,
|
||||||
if (!res.IsValid())
|
websocket ? _webSocketRegex : _httpRegex))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol = res.m_Scheme;
|
std::string portStr;
|
||||||
host = res.m_Host;
|
|
||||||
path = res.m_Path;
|
|
||||||
query = res.m_Query;
|
|
||||||
|
|
||||||
if (!res.GetPort(&port))
|
protocol = std::string(what[1].first, what[1].second);
|
||||||
|
host = std::string(what[2].first, what[2].second);
|
||||||
|
portStr = std::string(what[3].first, what[3].second);
|
||||||
|
path = std::string(what[4].first, what[4].second);
|
||||||
|
query = std::string(what[5].first, what[5].second);
|
||||||
|
|
||||||
|
if (portStr.empty())
|
||||||
{
|
{
|
||||||
if (protocol == "ws" || protocol == "http")
|
if (protocol == "ws" || protocol == "http")
|
||||||
{
|
{
|
||||||
@ -47,6 +58,12 @@ namespace ix
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << portStr;
|
||||||
|
ss >> port;
|
||||||
|
}
|
||||||
|
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
{
|
{
|
||||||
@ -66,12 +83,12 @@ namespace ix
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UrlParser::printUrl(const std::string& url)
|
void UrlParser::printUrl(const std::string& url, bool websocket)
|
||||||
{
|
{
|
||||||
std::string protocol, host, path, query;
|
std::string protocol, host, path, query;
|
||||||
int port {0};
|
int port {0};
|
||||||
|
|
||||||
if (!parse(url, protocol, host, path, query, port))
|
if (!parse(url, protocol, host, path, query, port, websocket))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
@ -18,8 +19,13 @@ namespace ix
|
|||||||
std::string& host,
|
std::string& host,
|
||||||
std::string& path,
|
std::string& path,
|
||||||
std::string& query,
|
std::string& query,
|
||||||
int& port);
|
int& port,
|
||||||
|
bool websocket);
|
||||||
|
|
||||||
static void printUrl(const std::string& url);
|
static void printUrl(const std::string& url, bool websocket);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::regex _httpRegex;
|
||||||
|
static std::regex _webSocketRegex;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -270,7 +270,7 @@ namespace ix
|
|||||||
_onMessageCallback(WebSocket_MessageType_Error, "", 0,
|
_onMessageCallback(WebSocket_MessageType_Error, "", 0,
|
||||||
connectErr, WebSocketOpenInfo(),
|
connectErr, WebSocketOpenInfo(),
|
||||||
WebSocketCloseInfo());
|
WebSocketCloseInfo());
|
||||||
|
|
||||||
// Only sleep if we aren't in the middle of stopping
|
// Only sleep if we aren't in the middle of stopping
|
||||||
if (!_stop)
|
if (!_stop)
|
||||||
{
|
{
|
||||||
@ -340,7 +340,9 @@ namespace ix
|
|||||||
WebSocket::invokeTrafficTrackerCallback(msg.size(), true);
|
WebSocket::invokeTrafficTrackerCallback(msg.size(), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// If we aren't trying to reconnect automatically, exit if we aren't connected
|
// 4. In blocking mode, getting out of this function is triggered by
|
||||||
|
// an explicit disconnection from the callback, or by the remote end
|
||||||
|
// closing the connection, ie isConnected() == false.
|
||||||
if (!isConnected() && !_automaticReconnection) return;
|
if (!isConnected() && !_automaticReconnection) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,6 +154,7 @@ namespace ix
|
|||||||
static OnTrafficTrackerCallback _onTrafficTrackerCallback;
|
static OnTrafficTrackerCallback _onTrafficTrackerCallback;
|
||||||
|
|
||||||
std::atomic<bool> _stop;
|
std::atomic<bool> _stop;
|
||||||
|
std::atomic<bool> _backgroundThreadRunning;
|
||||||
std::atomic<bool> _automaticReconnection;
|
std::atomic<bool> _automaticReconnection;
|
||||||
std::thread _thread;
|
std::thread _thread;
|
||||||
std::mutex _writeMutex;
|
std::mutex _writeMutex;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <regex>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
@ -137,8 +137,9 @@ namespace ix
|
|||||||
{
|
{
|
||||||
std::string protocol, host, path, query;
|
std::string protocol, host, path, query;
|
||||||
int port;
|
int port;
|
||||||
|
bool websocket = true;
|
||||||
|
|
||||||
if (!UrlParser::parse(url, protocol, host, path, query, port))
|
if (!UrlParser::parse(url, protocol, host, path, query, port, websocket))
|
||||||
{
|
{
|
||||||
return WebSocketInitResult(false, 0,
|
return WebSocketInitResult(false, 0,
|
||||||
std::string("Could not parse URL ") + url);
|
std::string("Could not parse URL ") + url);
|
||||||
@ -295,7 +296,8 @@ namespace ix
|
|||||||
{
|
{
|
||||||
ssize_t ret = _socket->recv((char*)&_readbuf[0], _readbuf.size());
|
ssize_t ret = _socket->recv((char*)&_readbuf[0], _readbuf.size());
|
||||||
|
|
||||||
if (ret < 0 && Socket::isWaitNeeded())
|
if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
|
||||||
|
_socket->getErrno() == EAGAIN))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -551,7 +553,7 @@ namespace ix
|
|||||||
// Get the reason.
|
// Get the reason.
|
||||||
std::string reason(_rxbuf.begin()+ws.header_size + 2,
|
std::string reason(_rxbuf.begin()+ws.header_size + 2,
|
||||||
_rxbuf.begin()+ws.header_size + (size_t) ws.N);
|
_rxbuf.begin()+ws.header_size + (size_t) ws.N);
|
||||||
|
|
||||||
bool remote = true;
|
bool remote = true;
|
||||||
|
|
||||||
close(code, reason, _rxbuf.size(), remote);
|
close(code, reason, _rxbuf.size(), remote);
|
||||||
@ -842,7 +844,8 @@ namespace ix
|
|||||||
{
|
{
|
||||||
ssize_t ret = _socket->send((char*)&_txbuf[0], _txbuf.size());
|
ssize_t ret = _socket->send((char*)&_txbuf[0], _txbuf.size());
|
||||||
|
|
||||||
if (ret < 0 && Socket::isWaitNeeded())
|
if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
|
||||||
|
_socket->getErrno() == EAGAIN))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,265 +0,0 @@
|
|||||||
/*
|
|
||||||
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
|
||||||
* https://github.com/corporateshark/LUrlParser
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "LUrlParser.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
// check if the scheme name is valid
|
|
||||||
static bool IsSchemeValid( const std::string& SchemeName )
|
|
||||||
{
|
|
||||||
for ( auto c : SchemeName )
|
|
||||||
{
|
|
||||||
if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
|
|
||||||
{
|
|
||||||
if ( !IsValid() ) { return false; }
|
|
||||||
|
|
||||||
int Port = atoi( m_Port.c_str() );
|
|
||||||
|
|
||||||
if ( Port <= 0 || Port > 65535 ) { return false; }
|
|
||||||
|
|
||||||
if ( OutPort ) { *OutPort = Port; }
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// based on RFC 1738 and RFC 3986
|
|
||||||
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
|
|
||||||
{
|
|
||||||
LUrlParser::clParseURL Result;
|
|
||||||
|
|
||||||
const char* CurrentString = URL.c_str();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* <scheme>:<scheme-specific-part>
|
|
||||||
* <scheme> := [a-z\+\-\.]+
|
|
||||||
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
|
|
||||||
*/
|
|
||||||
|
|
||||||
// try to read scheme
|
|
||||||
{
|
|
||||||
const char* LocalString = strchr( CurrentString, ':' );
|
|
||||||
|
|
||||||
if ( !LocalString )
|
|
||||||
{
|
|
||||||
return clParseURL( LUrlParserError_NoUrlCharacter );
|
|
||||||
}
|
|
||||||
|
|
||||||
// save the scheme name
|
|
||||||
Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
if ( !IsSchemeValid( Result.m_Scheme ) )
|
|
||||||
{
|
|
||||||
return clParseURL( LUrlParserError_InvalidSchemeName );
|
|
||||||
}
|
|
||||||
|
|
||||||
// scheme should be lowercase
|
|
||||||
std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
|
|
||||||
|
|
||||||
// skip ':'
|
|
||||||
CurrentString = LocalString+1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* //<user>:<password>@<host>:<port>/<url-path>
|
|
||||||
* any ":", "@" and "/" must be normalized
|
|
||||||
*/
|
|
||||||
|
|
||||||
// skip "//"
|
|
||||||
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
|
||||||
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
|
||||||
|
|
||||||
// check if the user name and password are specified
|
|
||||||
bool bHasUserName = false;
|
|
||||||
|
|
||||||
const char* LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString )
|
|
||||||
{
|
|
||||||
if ( *LocalString == '@' )
|
|
||||||
{
|
|
||||||
// user name and password are specified
|
|
||||||
bHasUserName = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if ( *LocalString == '/' )
|
|
||||||
{
|
|
||||||
// end of <host>:<port> specification
|
|
||||||
bHasUserName = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalString++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// user name and password
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
if ( bHasUserName )
|
|
||||||
{
|
|
||||||
// read user name
|
|
||||||
while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
// proceed with the current pointer
|
|
||||||
CurrentString = LocalString;
|
|
||||||
|
|
||||||
if ( *CurrentString == ':' )
|
|
||||||
{
|
|
||||||
// skip ':'
|
|
||||||
CurrentString++;
|
|
||||||
|
|
||||||
// read password
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString && *LocalString != '@' ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip '@'
|
|
||||||
if ( *CurrentString != '@' )
|
|
||||||
{
|
|
||||||
return clParseURL( LUrlParserError_NoAtSign );
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentString++;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bHasBracket = ( *CurrentString == '[' );
|
|
||||||
|
|
||||||
// go ahead, read the host name
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString )
|
|
||||||
{
|
|
||||||
if ( bHasBracket && *LocalString == ']' )
|
|
||||||
{
|
|
||||||
// end of IPv6 address
|
|
||||||
LocalString++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
|
|
||||||
{
|
|
||||||
// port number is specified
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalString++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
|
|
||||||
// is port number specified?
|
|
||||||
if ( *CurrentString == ':' )
|
|
||||||
{
|
|
||||||
CurrentString++;
|
|
||||||
|
|
||||||
// read port number
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString && *LocalString != '/' ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// end of string
|
|
||||||
if ( !*CurrentString )
|
|
||||||
{
|
|
||||||
Result.m_ErrorCode = LUrlParserError_Ok;
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip '/'
|
|
||||||
if ( *CurrentString != '/' )
|
|
||||||
{
|
|
||||||
return clParseURL( LUrlParserError_NoSlash );
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentString++;
|
|
||||||
|
|
||||||
// parse the path
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
|
|
||||||
// check for query
|
|
||||||
if ( *CurrentString == '?' )
|
|
||||||
{
|
|
||||||
// skip '?'
|
|
||||||
CurrentString++;
|
|
||||||
|
|
||||||
// read query
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString && *LocalString != '#' ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for fragment
|
|
||||||
if ( *CurrentString == '#' )
|
|
||||||
{
|
|
||||||
// skip '#'
|
|
||||||
CurrentString++;
|
|
||||||
|
|
||||||
// read fragment
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result.m_ErrorCode = LUrlParserError_Ok;
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
|
||||||
* https://github.com/corporateshark/LUrlParser
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace LUrlParser
|
|
||||||
{
|
|
||||||
enum LUrlParserError
|
|
||||||
{
|
|
||||||
LUrlParserError_Ok = 0,
|
|
||||||
LUrlParserError_Uninitialized = 1,
|
|
||||||
LUrlParserError_NoUrlCharacter = 2,
|
|
||||||
LUrlParserError_InvalidSchemeName = 3,
|
|
||||||
LUrlParserError_NoDoubleSlash = 4,
|
|
||||||
LUrlParserError_NoAtSign = 5,
|
|
||||||
LUrlParserError_UnexpectedEndOfLine = 6,
|
|
||||||
LUrlParserError_NoSlash = 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
class clParseURL
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LUrlParserError m_ErrorCode;
|
|
||||||
std::string m_Scheme;
|
|
||||||
std::string m_Host;
|
|
||||||
std::string m_Port;
|
|
||||||
std::string m_Path;
|
|
||||||
std::string m_Query;
|
|
||||||
std::string m_Fragment;
|
|
||||||
std::string m_UserName;
|
|
||||||
std::string m_Password;
|
|
||||||
|
|
||||||
clParseURL()
|
|
||||||
: m_ErrorCode( LUrlParserError_Uninitialized )
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// return 'true' if the parsing was successful
|
|
||||||
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
|
|
||||||
|
|
||||||
/// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
|
|
||||||
bool GetPort( int* OutPort ) const;
|
|
||||||
|
|
||||||
/// parse the URL
|
|
||||||
static clParseURL ParseURL( const std::string& URL );
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit clParseURL( LUrlParserError ErrorCode )
|
|
||||||
: m_ErrorCode( ErrorCode )
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace LUrlParser
|
|
@ -8,11 +8,12 @@ project (ixwebsocket_unittest)
|
|||||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../third_party/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH})
|
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../third_party/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH})
|
||||||
find_package(Sanitizers)
|
find_package(Sanitizers)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
|
||||||
|
set(CMAKE_LD_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
|
||||||
|
|
||||||
set (CMAKE_CXX_STANDARD 14)
|
set (CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
if (NOT WIN32)
|
if (NOT WIN32)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
|
|
||||||
set(CMAKE_LD_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
|
|
||||||
option(USE_TLS "Add TLS support" ON)
|
option(USE_TLS "Add TLS support" ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -32,17 +33,16 @@ set (SOURCES
|
|||||||
IXDNSLookupTest.cpp
|
IXDNSLookupTest.cpp
|
||||||
IXSocketTest.cpp
|
IXSocketTest.cpp
|
||||||
IXSocketConnectTest.cpp
|
IXSocketConnectTest.cpp
|
||||||
IXWebSocketServerTest.cpp
|
|
||||||
IXWebSocketPingTest.cpp
|
|
||||||
IXWebSocketTestConnectionDisconnection.cpp
|
|
||||||
IXUrlParserTest.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Some unittest don't work on windows yet
|
# Some unittest don't work on windows yet
|
||||||
if (NOT WIN32)
|
if (NOT WIN32)
|
||||||
list(APPEND SOURCES
|
list(APPEND SOURCES
|
||||||
|
IXWebSocketServerTest.cpp
|
||||||
|
IXWebSocketPingTest.cpp
|
||||||
IXWebSocketPingTimeoutTest.cpp
|
IXWebSocketPingTimeoutTest.cpp
|
||||||
cmd_websocket_chat.cpp
|
cmd_websocket_chat.cpp
|
||||||
|
IXWebSocketTestConnectionDisconnection.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
log("Cannot compute a free port. bind error.");
|
log("Cannot compute a free port. bind error.");
|
||||||
|
|
||||||
Socket::closeSocket(sockfd);
|
::close(sockfd);
|
||||||
return getAnyFreePortRandom();
|
return getAnyFreePortRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,12 +118,12 @@ namespace ix
|
|||||||
{
|
{
|
||||||
log("Cannot compute a free port. getsockname error.");
|
log("Cannot compute a free port. getsockname error.");
|
||||||
|
|
||||||
Socket::closeSocket(sockfd);
|
::close(sockfd);
|
||||||
return getAnyFreePortRandom();
|
return getAnyFreePortRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
int port = ntohs(sa.sin_port);
|
int port = ntohs(sa.sin_port);
|
||||||
Socket::closeSocket(sockfd);
|
::close(sockfd);
|
||||||
|
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
* IXSocketTest.cpp
|
|
||||||
* Author: Korchynskyi Dmytro
|
|
||||||
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <ixwebsocket/IXUrlParser.h>
|
|
||||||
|
|
||||||
#include "IXTest.h"
|
|
||||||
#include "catch.hpp"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
using namespace ix;
|
|
||||||
|
|
||||||
namespace ix
|
|
||||||
{
|
|
||||||
|
|
||||||
TEST_CASE("urlParser", "[urlParser]")
|
|
||||||
{
|
|
||||||
SECTION("http://google.com")
|
|
||||||
{
|
|
||||||
std::string url = "http://google.com";
|
|
||||||
std::string protocol, host, path, query;
|
|
||||||
int port;
|
|
||||||
bool res;
|
|
||||||
|
|
||||||
res = UrlParser::parse(url, protocol, host, path, query, port);
|
|
||||||
|
|
||||||
REQUIRE(res);
|
|
||||||
REQUIRE(protocol == "http");
|
|
||||||
REQUIRE(host == "google.com");
|
|
||||||
REQUIRE(path == "/");
|
|
||||||
REQUIRE(query == "");
|
|
||||||
REQUIRE(port == 80); // default port for http
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("https://google.com")
|
|
||||||
{
|
|
||||||
std::string url = "https://google.com";
|
|
||||||
std::string protocol, host, path, query;
|
|
||||||
int port;
|
|
||||||
bool res;
|
|
||||||
|
|
||||||
res = UrlParser::parse(url, protocol, host, path, query, port);
|
|
||||||
|
|
||||||
REQUIRE(res);
|
|
||||||
REQUIRE(protocol == "https");
|
|
||||||
REQUIRE(host == "google.com");
|
|
||||||
REQUIRE(path == "/");
|
|
||||||
REQUIRE(query == "");
|
|
||||||
REQUIRE(port == 443); // default port for https
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("ws://google.com")
|
|
||||||
{
|
|
||||||
std::string url = "ws://google.com";
|
|
||||||
std::string protocol, host, path, query;
|
|
||||||
int port;
|
|
||||||
bool res;
|
|
||||||
|
|
||||||
res = UrlParser::parse(url, protocol, host, path, query, port);
|
|
||||||
|
|
||||||
REQUIRE(res);
|
|
||||||
REQUIRE(protocol == "ws");
|
|
||||||
REQUIRE(host == "google.com");
|
|
||||||
REQUIRE(path == "/");
|
|
||||||
REQUIRE(query == "");
|
|
||||||
REQUIRE(port == 80); // default port for ws
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("wss://google.com/ws?arg=value")
|
|
||||||
{
|
|
||||||
std::string url = "wss://google.com/ws?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 == "/ws?arg=value&arg2=value2");
|
|
||||||
REQUIRE(query == "arg=value&arg2=value2");
|
|
||||||
REQUIRE(port == 443); // default port for wss
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("real test")
|
|
||||||
{
|
|
||||||
std::string url = "ws://127.0.0.1:7350/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf";
|
|
||||||
std::string protocol, host, path, query;
|
|
||||||
int port;
|
|
||||||
bool res;
|
|
||||||
|
|
||||||
res = UrlParser::parse(url, protocol, host, path, query, port);
|
|
||||||
|
|
||||||
REQUIRE(res);
|
|
||||||
REQUIRE(protocol == "ws");
|
|
||||||
REQUIRE(host == "127.0.0.1");
|
|
||||||
REQUIRE(path == "/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf");
|
|
||||||
REQUIRE(query == "token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf");
|
|
||||||
REQUIRE(port == 7350);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -23,6 +23,7 @@ namespace
|
|||||||
public:
|
public:
|
||||||
WebSocketClient(int port, bool useHeartBeatMethod);
|
WebSocketClient(int port, bool useHeartBeatMethod);
|
||||||
|
|
||||||
|
void subscribe(const std::string& channel);
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
bool isReady() const;
|
bool isReady() const;
|
||||||
@ -56,7 +57,7 @@ namespace
|
|||||||
std::string url;
|
std::string url;
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "ws://127.0.0.1:"
|
ss << "ws://localhost:"
|
||||||
<< _port
|
<< _port
|
||||||
<< "/";
|
<< "/";
|
||||||
|
|
||||||
@ -346,9 +347,6 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval", "[setPingInterval]")
|
|||||||
|
|
||||||
webSocketClient.stop();
|
webSocketClient.stop();
|
||||||
|
|
||||||
// without this sleep test fails on Windows
|
|
||||||
ix::msleep(100);
|
|
||||||
|
|
||||||
// Here we test ping interval
|
// Here we test ping interval
|
||||||
// client has sent data, but ping should have been sent no matter what
|
// client has sent data, but ping should have been sent no matter what
|
||||||
// -> expected ping messages == 3 as 900+900+1300 = 3100 seconds, 1 ping sent every second
|
// -> expected ping messages == 3 as 900+900+1300 = 3100 seconds, 1 ping sent every second
|
||||||
|
@ -23,6 +23,7 @@ namespace
|
|||||||
public:
|
public:
|
||||||
WebSocketClient(int port, int pingInterval, int pingTimeout);
|
WebSocketClient(int port, int pingInterval, int pingTimeout);
|
||||||
|
|
||||||
|
void subscribe(const std::string& channel);
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
bool isReady() const;
|
bool isReady() const;
|
||||||
@ -70,7 +71,7 @@ namespace
|
|||||||
std::string url;
|
std::string url;
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "ws://127.0.0.1:"
|
ss << "ws://localhost:"
|
||||||
<< _port
|
<< _port
|
||||||
<< "/";
|
<< "/";
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ TEST_CASE("Websocket_server", "[websocket_server]")
|
|||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
bool tls = false;
|
bool tls = false;
|
||||||
std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
|
std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
|
||||||
std::string host("127.0.0.1");
|
std::string host("localhost");
|
||||||
auto isCancellationRequested = []() -> bool
|
auto isCancellationRequested = []() -> bool
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -141,7 +141,7 @@ TEST_CASE("Websocket_server", "[websocket_server]")
|
|||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
bool tls = false;
|
bool tls = false;
|
||||||
std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
|
std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
|
||||||
std::string host("127.0.0.1");
|
std::string host("localhost");
|
||||||
auto isCancellationRequested = []() -> bool
|
auto isCancellationRequested = []() -> bool
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -178,7 +178,7 @@ TEST_CASE("Websocket_server", "[websocket_server]")
|
|||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
bool tls = false;
|
bool tls = false;
|
||||||
std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
|
std::shared_ptr<Socket> socket = createSocket(tls, errMsg);
|
||||||
std::string host("127.0.0.1");
|
std::string host("localhost");
|
||||||
auto isCancellationRequested = []() -> bool
|
auto isCancellationRequested = []() -> bool
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -100,7 +100,7 @@ namespace
|
|||||||
std::string url;
|
std::string url;
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "ws://127.0.0.1:"
|
ss << "ws://localhost:"
|
||||||
<< _port
|
<< _port
|
||||||
<< "/"
|
<< "/"
|
||||||
<< _user;
|
<< _user;
|
||||||
|
@ -41,7 +41,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
if (!redisClient.publish(channel, message, errMsg))
|
if (!redisClient.publish(channel, message, errMsg))
|
||||||
{
|
{
|
||||||
std::cerr << "Error publishing to channel " << channel
|
std::cerr << "Error publishing to channel " << channel
|
||||||
<< "error: " << errMsg
|
<< "error: " << errMsg
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
|
Reference in New Issue
Block a user