Compare commits
34 Commits
v1.2.0
...
feature/ht
Author | SHA1 | Date | |
---|---|---|---|
|
73f4ba7ee7 | ||
|
6f39592c7b | ||
|
38200fc5d7 | ||
|
285c12775a | ||
|
6d56f7223a | ||
|
1db3568375 | ||
|
0a752e7d18 | ||
|
7c2bc2cf7e | ||
|
79f601ac65 | ||
|
069eccf415 | ||
|
b563541b14 | ||
|
3bcd6f97a6 | ||
|
c04bc3cdfc | ||
|
846f0c680a | ||
|
c552a03ef0 | ||
|
0f175143c9 | ||
|
85569cb401 | ||
|
bd854553d4 | ||
|
38c57e1ed2 | ||
|
26cc5025fb | ||
|
806cf39efc | ||
|
daaa7ec704 | ||
|
3cffc6f9a5 | ||
|
f8b1a03ee6 | ||
|
a7ff3c41a1 | ||
|
78dbba5521 | ||
|
b211bdbe38 | ||
|
a0a53ab986 | ||
|
8d819053ff | ||
|
e20ddc2a08 | ||
|
c415ba9427 | ||
|
0b7c3ec235 | ||
|
29c96f287f | ||
|
2a17cad1bf |
31
Dockerfile
31
Dockerfile
@@ -1,31 +0,0 @@
|
|||||||
FROM debian:stretch
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND noninteractive
|
|
||||||
RUN apt-get update
|
|
||||||
RUN apt-get -y install g++
|
|
||||||
RUN apt-get -y install libssl-dev
|
|
||||||
RUN apt-get -y install gdb
|
|
||||||
RUN apt-get -y install screen
|
|
||||||
RUN apt-get -y install procps
|
|
||||||
RUN apt-get -y install lsof
|
|
||||||
RUN apt-get -y install libz-dev
|
|
||||||
RUN apt-get -y install vim
|
|
||||||
RUN apt-get -y install make
|
|
||||||
RUN apt-get -y install cmake
|
|
||||||
RUN apt-get -y install curl
|
|
||||||
RUN apt-get -y install python
|
|
||||||
RUN apt-get -y install netcat
|
|
||||||
|
|
||||||
# debian strech cmake is too old for building with Docker
|
|
||||||
COPY makefile .
|
|
||||||
RUN ["make", "install_cmake_for_linux"]
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-rc4-Linux-x86_64/bin
|
|
||||||
ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
|
|
||||||
|
|
||||||
# RUN ["make"]
|
|
||||||
|
|
||||||
EXPOSE 8765
|
|
||||||
CMD ["/ws/ws", "transfer", "--port", "8765", "--host", "0.0.0.0"]
|
|
1
Dockerfile
Symbolic link
1
Dockerfile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
docker/Dockerfile.debian
|
11
README.md
11
README.md
@@ -11,6 +11,7 @@ communication channels over a single TCP connection. *IXWebSocket* is a C++ libr
|
|||||||
* iOS
|
* iOS
|
||||||
* Linux
|
* Linux
|
||||||
* Android
|
* Android
|
||||||
|
* Windows (no TLS support yet)
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@@ -76,10 +77,7 @@ server.setOnConnectionCallback(
|
|||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocket_MessageType_Open)
|
||||||
{
|
{
|
||||||
std::cerr << "New connection" << std::endl;
|
std::cerr << "New connection" << std::endl;
|
||||||
|
|
||||||
// The uri the client did connect to.
|
|
||||||
std::cerr << "Uri: " << openInfo.uri << std::endl;
|
std::cerr << "Uri: " << openInfo.uri << std::endl;
|
||||||
|
|
||||||
std::cerr << "Headers:" << std::endl;
|
std::cerr << "Headers:" << std::endl;
|
||||||
for (auto it : openInfo.headers)
|
for (auto it : openInfo.headers)
|
||||||
{
|
{
|
||||||
@@ -180,13 +178,6 @@ CMakefiles for the library and the examples are available. This library has few
|
|||||||
|
|
||||||
There is a Dockerfile for running some code on Linux, and a unittest which can be executed by typing `make test`.
|
There is a Dockerfile for running some code on Linux, and a unittest which can be executed by typing `make test`.
|
||||||
|
|
||||||
You can build and install the ws command line tool with Homebrew.
|
|
||||||
|
|
||||||
```
|
|
||||||
brew create --cmake https://github.com/machinezone/IXWebSocket/archive/v1.1.0.tar.gz
|
|
||||||
brew install IXWebSocket
|
|
||||||
```
|
|
||||||
|
|
||||||
## Implementation details
|
## Implementation details
|
||||||
|
|
||||||
### Per Message Deflate compression.
|
### Per Message Deflate compression.
|
||||||
|
16
docker/Dockerfile
Normal file
16
docker/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM debian:stretch
|
||||||
|
|
||||||
|
# RUN yum install -y gcc-c++ make cmake openssl-devel gdb
|
||||||
|
ENV DEBIAN_FRONTEND noninteractive
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get -y install g++
|
||||||
|
RUN apt-get -y install libssl-dev
|
||||||
|
RUN apt-get -y install gdb
|
||||||
|
RUN apt-get -y install screen
|
||||||
|
RUN apt-get -y install procps
|
||||||
|
RUN apt-get -y install lsof
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
WORKDIR examples/ws_connect
|
||||||
|
RUN ["sh", "build_linux.sh"]
|
11
docker/Dockerfile.alpine
Normal file
11
docker/Dockerfile.alpine
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM alpine:3.8
|
||||||
|
|
||||||
|
RUN apk add --no-cache g++ musl-dev make cmake openssl-dev
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
WORKDIR examples/ws_connect
|
||||||
|
RUN ["sh", "build_linux.sh"]
|
||||||
|
|
||||||
|
EXPOSE 8765
|
||||||
|
CMD ["ws_connect"]
|
11
docker/Dockerfile.centos
Normal file
11
docker/Dockerfile.centos
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM alpine:3.8
|
||||||
|
|
||||||
|
RUN apk add --no-cache g++ musl-dev make cmake openssl-dev
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
WORKDIR examples/ws_connect
|
||||||
|
RUN ["sh", "build_linux.sh"]
|
||||||
|
|
||||||
|
EXPOSE 8765
|
||||||
|
CMD ["ws_connect"]
|
22
docker/Dockerfile.debian
Normal file
22
docker/Dockerfile.debian
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
FROM debian:stretch
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND noninteractive
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get -y install g++
|
||||||
|
RUN apt-get -y install libssl-dev
|
||||||
|
RUN apt-get -y install gdb
|
||||||
|
RUN apt-get -y install screen
|
||||||
|
RUN apt-get -y install procps
|
||||||
|
RUN apt-get -y install lsof
|
||||||
|
RUN apt-get -y install libz-dev
|
||||||
|
RUN apt-get -y install vim
|
||||||
|
RUN apt-get -y install make
|
||||||
|
RUN apt-get -y install cmake
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
WORKDIR ws
|
||||||
|
RUN ["sh", "docker_build.sh"]
|
||||||
|
|
||||||
|
EXPOSE 8765
|
||||||
|
CMD ["/ws/ws", "transfer", "8765"]
|
8
docker/Dockerfile.gcc
Normal file
8
docker/Dockerfile.gcc
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
FROM gcc:8
|
||||||
|
|
||||||
|
# RUN yum install -y gcc-c++ make cmake openssl-devel gdb
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
WORKDIR examples/ws_connect
|
||||||
|
RUN ["sh", "build_linux.sh"]
|
@@ -17,8 +17,6 @@
|
|||||||
// cf Android/Kernel table here
|
// cf Android/Kernel table here
|
||||||
// https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
|
// https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
|
||||||
//
|
//
|
||||||
// On macOS we use UNIX pipes to wake up select.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "IXEventFd.h"
|
#include "IXEventFd.h"
|
||||||
|
|
||||||
@@ -26,29 +24,17 @@
|
|||||||
# include <sys/eventfd.h>
|
# include <sys/eventfd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
#include <unistd.h> // for write
|
#include <unistd.h> // for write
|
||||||
#include <fcntl.h>
|
#endif
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
// File descriptor at index 0 in _fildes is the read end of the pipe
|
EventFd::EventFd() :
|
||||||
// File descriptor at index 1 in _fildes is the write end of the pipe
|
_eventfd(-1)
|
||||||
const int EventFd::kPipeReadIndex = 0;
|
|
||||||
const int EventFd::kPipeWriteIndex = 1;
|
|
||||||
|
|
||||||
EventFd::EventFd()
|
|
||||||
{
|
{
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
_eventfd = -1;
|
|
||||||
_eventfd = eventfd(0, 0);
|
_eventfd = eventfd(0, 0);
|
||||||
fcntl(_eventfd, F_SETFL, O_NONBLOCK);
|
|
||||||
#else
|
|
||||||
_fildes[kPipeReadIndex] = -1;
|
|
||||||
_fildes[kPipeWriteIndex] = -1;
|
|
||||||
|
|
||||||
pipe(_fildes);
|
|
||||||
fcntl(_fildes[kPipeReadIndex], F_SETFL, O_NONBLOCK);
|
|
||||||
fcntl(_fildes[kPipeWriteIndex], F_SETFL, O_NONBLOCK);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,43 +42,22 @@ namespace ix
|
|||||||
{
|
{
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
::close(_eventfd);
|
::close(_eventfd);
|
||||||
#else
|
|
||||||
::close(_fildes[kPipeReadIndex]);
|
|
||||||
::close(_fildes[kPipeWriteIndex]);
|
|
||||||
_fildes[kPipeReadIndex] = -1;
|
|
||||||
_fildes[kPipeWriteIndex] = -1;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventFd::notify(uint64_t value)
|
bool EventFd::notify()
|
||||||
{
|
{
|
||||||
int fd;
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
fd = _eventfd;
|
if (_eventfd == -1) return false;
|
||||||
#else
|
|
||||||
fd = _fildes[kPipeWriteIndex];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (fd == -1) return false;
|
// select will wake up when a non-zero value is written to our eventfd
|
||||||
|
uint64_t value = 1;
|
||||||
|
|
||||||
// we should write 8 bytes for an uint64_t
|
// we should write 8 bytes for an uint64_t
|
||||||
return write(fd, &value, sizeof(value)) == 8;
|
return write(_eventfd, &value, sizeof(value)) == 8;
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: return max uint64_t for errors ?
|
|
||||||
uint64_t EventFd::read()
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
fd = _eventfd;
|
|
||||||
#else
|
#else
|
||||||
fd = _fildes[kPipeReadIndex];
|
return true;
|
||||||
#endif
|
#endif
|
||||||
uint64_t value = 0;
|
|
||||||
::read(fd, &value, sizeof(value));
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventFd::clear()
|
bool EventFd::clear()
|
||||||
@@ -112,10 +77,6 @@ namespace ix
|
|||||||
|
|
||||||
int EventFd::getFd()
|
int EventFd::getFd()
|
||||||
{
|
{
|
||||||
#if defined(__linux__)
|
|
||||||
return _eventfd;
|
return _eventfd;
|
||||||
#else
|
|
||||||
return _fildes[kPipeReadIndex];
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class EventFd {
|
class EventFd {
|
||||||
@@ -15,23 +13,11 @@ namespace ix
|
|||||||
EventFd();
|
EventFd();
|
||||||
virtual ~EventFd();
|
virtual ~EventFd();
|
||||||
|
|
||||||
bool notify(uint64_t value);
|
bool notify();
|
||||||
bool clear();
|
bool clear();
|
||||||
uint64_t read();
|
|
||||||
int getFd();
|
int getFd();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if defined(__linux__)
|
|
||||||
int _eventfd;
|
int _eventfd;
|
||||||
#else
|
|
||||||
// Store file descriptors used by the communication pipe. Communication
|
|
||||||
// happens between a control thread and a background thread, which is
|
|
||||||
// blocked on select.
|
|
||||||
int _fildes[2];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Used to identify the read/write idx
|
|
||||||
static const int kPipeReadIndex;
|
|
||||||
static const int kPipeWriteIndex;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -231,17 +231,19 @@ namespace ix
|
|||||||
|
|
||||||
payload.reserve(contentLength);
|
payload.reserve(contentLength);
|
||||||
|
|
||||||
auto chunkResult = _socket->readBytes(contentLength,
|
// FIXME: very inefficient way to read bytes, but it works...
|
||||||
args.onProgressCallback,
|
for (int i = 0; i < contentLength; ++i)
|
||||||
isCancellationRequested);
|
|
||||||
if (!chunkResult.first)
|
|
||||||
{
|
{
|
||||||
errorMsg = "Cannot read chunk";
|
char c;
|
||||||
return std::make_tuple(code, HttpErrorCode_ChunkReadError,
|
if (!_socket->readByte(&c, isCancellationRequested))
|
||||||
headers, payload, errorMsg,
|
{
|
||||||
uploadSize, downloadSize);
|
return std::make_tuple(code, HttpErrorCode_ReadError,
|
||||||
|
headers, payload, "Cannot read byte",
|
||||||
|
uploadSize, downloadSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
payload += c;
|
||||||
}
|
}
|
||||||
payload += chunkResult.second;
|
|
||||||
}
|
}
|
||||||
else if (headers.find("Transfer-Encoding") != headers.end() &&
|
else if (headers.find("Transfer-Encoding") != headers.end() &&
|
||||||
headers["Transfer-Encoding"] == "chunked")
|
headers["Transfer-Encoding"] == "chunked")
|
||||||
@@ -275,20 +277,22 @@ namespace ix
|
|||||||
|
|
||||||
payload.reserve(payload.size() + chunkSize);
|
payload.reserve(payload.size() + chunkSize);
|
||||||
|
|
||||||
// Read a chunk
|
// Read another line
|
||||||
auto chunkResult = _socket->readBytes(chunkSize,
|
|
||||||
args.onProgressCallback,
|
for (uint64_t i = 0; i < chunkSize; ++i)
|
||||||
isCancellationRequested);
|
{
|
||||||
if (!chunkResult.first)
|
char c;
|
||||||
{
|
if (!_socket->readByte(&c, isCancellationRequested))
|
||||||
errorMsg = "Cannot read chunk";
|
{
|
||||||
return std::make_tuple(code, HttpErrorCode_ChunkReadError,
|
errorMsg = "Cannot read byte";
|
||||||
headers, payload, errorMsg,
|
return std::make_tuple(code, HttpErrorCode_ChunkReadError,
|
||||||
uploadSize, downloadSize);
|
headers, payload, errorMsg,
|
||||||
}
|
uploadSize, downloadSize);
|
||||||
payload += chunkResult.second;
|
}
|
||||||
|
|
||||||
|
payload += c;
|
||||||
|
}
|
||||||
|
|
||||||
// Read the line that terminates the chunk (\r\n)
|
|
||||||
lineResult = _socket->readLine(isCancellationRequested);
|
lineResult = _socket->readLine(isCancellationRequested);
|
||||||
|
|
||||||
if (!lineResult.first)
|
if (!lineResult.first)
|
||||||
|
@@ -61,7 +61,6 @@ namespace ix
|
|||||||
bool verbose;
|
bool verbose;
|
||||||
bool compress;
|
bool compress;
|
||||||
Logger logger;
|
Logger logger;
|
||||||
OnProgressCallback onProgressCallback;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class HttpClient {
|
class HttpClient {
|
||||||
|
@@ -23,14 +23,11 @@ namespace ix
|
|||||||
{
|
{
|
||||||
const int Socket::kDefaultPollNoTimeout = -1; // No poll timeout by default
|
const int Socket::kDefaultPollNoTimeout = -1; // No poll timeout by default
|
||||||
const int Socket::kDefaultPollTimeout = kDefaultPollNoTimeout;
|
const int Socket::kDefaultPollTimeout = kDefaultPollNoTimeout;
|
||||||
const uint64_t Socket::kSendRequest = 1;
|
|
||||||
const uint64_t Socket::kCloseRequest = 2;
|
|
||||||
constexpr size_t Socket::kChunkSize;
|
|
||||||
|
|
||||||
Socket::Socket(int fd) :
|
Socket::Socket(int fd) :
|
||||||
_sockfd(fd)
|
_sockfd(fd)
|
||||||
{
|
{
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket::~Socket()
|
Socket::~Socket()
|
||||||
@@ -42,38 +39,26 @@ namespace ix
|
|||||||
{
|
{
|
||||||
if (_sockfd == -1)
|
if (_sockfd == -1)
|
||||||
{
|
{
|
||||||
if (onPollCallback) onPollCallback(PollResultType_Error);
|
onPollCallback(PollResultType_Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PollResultType pollResult = select(timeoutSecs, 0);
|
|
||||||
|
|
||||||
if (onPollCallback) onPollCallback(pollResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
PollResultType Socket::select(int timeoutSecs, int timeoutMs)
|
|
||||||
{
|
|
||||||
fd_set rfds;
|
fd_set rfds;
|
||||||
FD_ZERO(&rfds);
|
FD_ZERO(&rfds);
|
||||||
FD_SET(_sockfd, &rfds);
|
FD_SET(_sockfd, &rfds);
|
||||||
|
|
||||||
// File descriptor at index 0 in _fildes is the read end of the pipe
|
#ifdef __linux__
|
||||||
int eventfd = _eventfd.getFd();
|
FD_SET(_eventfd.getFd(), &rfds);
|
||||||
if (eventfd != -1)
|
#endif
|
||||||
{
|
|
||||||
FD_SET(eventfd, &rfds);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
timeout.tv_sec = timeoutSecs;
|
timeout.tv_sec = timeoutSecs;
|
||||||
timeout.tv_usec = 1000 * timeoutMs;
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
// Compute the highest fd.
|
|
||||||
int sockfd = _sockfd;
|
int sockfd = _sockfd;
|
||||||
int nfds = (std::max)(sockfd, eventfd);
|
int nfds = (std::max)(sockfd, _eventfd.getFd());
|
||||||
|
int ret = select(nfds + 1, &rfds, nullptr, nullptr,
|
||||||
int ret = ::select(nfds + 1, &rfds, nullptr, nullptr,
|
(timeoutSecs < 0) ? nullptr : &timeout);
|
||||||
(timeoutSecs < 0) ? nullptr : &timeout);
|
|
||||||
|
|
||||||
PollResultType pollResult = PollResultType_ReadyForRead;
|
PollResultType pollResult = PollResultType_ReadyForRead;
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@@ -84,27 +69,14 @@ namespace ix
|
|||||||
{
|
{
|
||||||
pollResult = PollResultType_Timeout;
|
pollResult = PollResultType_Timeout;
|
||||||
}
|
}
|
||||||
else if (eventfd != -1 && FD_ISSET(eventfd, &rfds))
|
|
||||||
{
|
|
||||||
uint64_t value = _eventfd.read();
|
|
||||||
|
|
||||||
if (value == kSendRequest)
|
onPollCallback(pollResult);
|
||||||
{
|
|
||||||
pollResult = PollResultType_SendRequest;
|
|
||||||
}
|
|
||||||
else if (value == kCloseRequest)
|
|
||||||
{
|
|
||||||
pollResult = PollResultType_CloseRequest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pollResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up from poll/select by writing to the pipe which is watched by select
|
void Socket::wakeUpFromPoll()
|
||||||
bool Socket::wakeUpFromPoll(uint8_t wakeUpCode)
|
|
||||||
{
|
{
|
||||||
return _eventfd.notify(wakeUpCode);
|
// this will wake up the thread blocked on select, only needed on Linux
|
||||||
|
_eventfd.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Socket::connect(const std::string& host,
|
bool Socket::connect(const std::string& host,
|
||||||
@@ -193,6 +165,51 @@ namespace ix
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Socket::readByte(void* buffer,
|
||||||
|
const CancellationRequest& isCancellationRequested)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (isCancellationRequested()) return false;
|
||||||
|
|
||||||
|
ssize_t ret;
|
||||||
|
ret = recv(buffer, 1);
|
||||||
|
|
||||||
|
// We read one byte, as needed, all good.
|
||||||
|
if (ret == 1)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// There is possibly something to be read, try again
|
||||||
|
else if (ret < 0 && (getErrno() == EWOULDBLOCK ||
|
||||||
|
getErrno() == EAGAIN))
|
||||||
|
{
|
||||||
|
// Wait with a timeout until something is written.
|
||||||
|
// This way we are not busy looping
|
||||||
|
fd_set rfds;
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = 1 * 1000; // 1ms timeout
|
||||||
|
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(_sockfd, &rfds);
|
||||||
|
|
||||||
|
if (select(_sockfd + 1, &rfds, nullptr, nullptr, &timeout) < 0 &&
|
||||||
|
(errno == EBADF || errno == EINVAL))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// There was an error during the read, abort
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Socket::writeBytes(const std::string& str,
|
bool Socket::writeBytes(const std::string& str,
|
||||||
const CancellationRequest& isCancellationRequested)
|
const CancellationRequest& isCancellationRequested)
|
||||||
{
|
{
|
||||||
@@ -224,43 +241,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Socket::readByte(void* buffer,
|
std::pair<bool, std::string> Socket::readLine(const CancellationRequest& isCancellationRequested)
|
||||||
const CancellationRequest& isCancellationRequested)
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (isCancellationRequested()) return false;
|
|
||||||
|
|
||||||
ssize_t ret;
|
|
||||||
ret = recv(buffer, 1);
|
|
||||||
|
|
||||||
// We read one byte, as needed, all good.
|
|
||||||
if (ret == 1)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// There is possibly something to be read, try again
|
|
||||||
else if (ret < 0 && (getErrno() == EWOULDBLOCK ||
|
|
||||||
getErrno() == EAGAIN))
|
|
||||||
{
|
|
||||||
// Wait with a timeout until something is ready to read.
|
|
||||||
// This way we are not busy looping
|
|
||||||
int res = select(0, 1);
|
|
||||||
if (res < 0 && (errno == EBADF || errno == EINVAL))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// There was an error during the read, abort
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> Socket::readLine(
|
|
||||||
const CancellationRequest& isCancellationRequested)
|
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
std::string line;
|
std::string line;
|
||||||
@@ -270,8 +251,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
if (!readByte(&c, isCancellationRequested))
|
if (!readByte(&c, isCancellationRequested))
|
||||||
{
|
{
|
||||||
// Return what we were able to read
|
return std::make_pair(false, std::string());
|
||||||
return std::make_pair(false, line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
line += c;
|
line += c;
|
||||||
@@ -279,46 +259,4 @@ namespace ix
|
|||||||
|
|
||||||
return std::make_pair(true, line);
|
return std::make_pair(true, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<bool, std::string> Socket::readBytes(
|
|
||||||
size_t length,
|
|
||||||
const OnProgressCallback& onProgressCallback,
|
|
||||||
const CancellationRequest& isCancellationRequested)
|
|
||||||
{
|
|
||||||
if (_readBuffer.empty())
|
|
||||||
{
|
|
||||||
_readBuffer.resize(kChunkSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> output;
|
|
||||||
while (output.size() != length)
|
|
||||||
{
|
|
||||||
if (isCancellationRequested()) return std::make_pair(false, std::string());
|
|
||||||
|
|
||||||
int size = std::min(kChunkSize, length - output.size());
|
|
||||||
ssize_t ret = recv((char*)&_readBuffer[0], size);
|
|
||||||
|
|
||||||
if (ret <= 0 && (getErrno() != EWOULDBLOCK &&
|
|
||||||
getErrno() != EAGAIN))
|
|
||||||
{
|
|
||||||
// Error
|
|
||||||
return std::make_pair(false, std::string());
|
|
||||||
}
|
|
||||||
else if (ret > 0)
|
|
||||||
{
|
|
||||||
output.insert(output.end(),
|
|
||||||
_readBuffer.begin(),
|
|
||||||
_readBuffer.begin() + ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onProgressCallback) onProgressCallback((int) output.size(), (int) length);
|
|
||||||
|
|
||||||
// Wait with a timeout until something is ready to read.
|
|
||||||
// This way we are not busy looping
|
|
||||||
select(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_pair(true, std::string(output.begin(),
|
|
||||||
output.end()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -10,16 +10,14 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <BaseTsd.h>
|
#include <BaseTsd.h>
|
||||||
typedef SSIZE_T ssize_t;
|
typedef SSIZE_T ssize_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "IXCancellationRequest.h"
|
|
||||||
#include "IXProgressCallback.h"
|
|
||||||
#include "IXEventFd.h"
|
#include "IXEventFd.h"
|
||||||
|
#include "IXCancellationRequest.h"
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
@@ -27,9 +25,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
PollResultType_ReadyForRead = 0,
|
PollResultType_ReadyForRead = 0,
|
||||||
PollResultType_Timeout = 1,
|
PollResultType_Timeout = 1,
|
||||||
PollResultType_Error = 2,
|
PollResultType_Error = 2
|
||||||
PollResultType_SendRequest = 3,
|
|
||||||
PollResultType_CloseRequest = 4
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Socket {
|
class Socket {
|
||||||
@@ -41,10 +37,9 @@ namespace ix
|
|||||||
|
|
||||||
void configure();
|
void configure();
|
||||||
|
|
||||||
PollResultType select(int timeoutSecs, int timeoutMs);
|
|
||||||
virtual void poll(const OnPollCallback& onPollCallback,
|
virtual void poll(const OnPollCallback& onPollCallback,
|
||||||
int timeoutSecs = kDefaultPollTimeout);
|
int timeoutSecs = kDefaultPollTimeout);
|
||||||
virtual bool wakeUpFromPoll(uint8_t wakeUpCode);
|
virtual void wakeUpFromPoll();
|
||||||
|
|
||||||
// Virtual methods
|
// Virtual methods
|
||||||
virtual bool connect(const std::string& url,
|
virtual bool connect(const std::string& url,
|
||||||
@@ -63,36 +58,21 @@ namespace ix
|
|||||||
const CancellationRequest& isCancellationRequested);
|
const CancellationRequest& isCancellationRequested);
|
||||||
bool writeBytes(const std::string& str,
|
bool writeBytes(const std::string& str,
|
||||||
const CancellationRequest& isCancellationRequested);
|
const CancellationRequest& isCancellationRequested);
|
||||||
|
std::pair<bool, std::string> readLine(const CancellationRequest& isCancellationRequested);
|
||||||
std::pair<bool, std::string> readLine(
|
|
||||||
const CancellationRequest& isCancellationRequested);
|
|
||||||
std::pair<bool, std::string> readBytes(
|
|
||||||
size_t length,
|
|
||||||
const OnProgressCallback& onProgressCallback,
|
|
||||||
const CancellationRequest& isCancellationRequested);
|
|
||||||
|
|
||||||
static int getErrno();
|
static int getErrno();
|
||||||
static bool init(); // Required on Windows to initialize WinSocket
|
static bool init(); // Required on Windows to initialize WinSocket
|
||||||
static void cleanup(); // Required on Windows to cleanup WinSocket
|
static void cleanup(); // Required on Windows to cleanup WinSocket
|
||||||
|
|
||||||
// Used as special codes for pipe communication
|
|
||||||
static const uint64_t kSendRequest;
|
|
||||||
static const uint64_t kCloseRequest;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeSocket(int fd);
|
void closeSocket(int fd);
|
||||||
|
|
||||||
std::atomic<int> _sockfd;
|
std::atomic<int> _sockfd;
|
||||||
std::mutex _socketMutex;
|
std::mutex _socketMutex;
|
||||||
|
EventFd _eventfd;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int kDefaultPollTimeout;
|
static const int kDefaultPollTimeout;
|
||||||
static const int kDefaultPollNoTimeout;
|
static const int kDefaultPollNoTimeout;
|
||||||
|
|
||||||
// Buffer for reading from our socket. That buffer is never resized.
|
|
||||||
std::vector<uint8_t> _readBuffer;
|
|
||||||
static constexpr size_t kChunkSize = 1 << 15;
|
|
||||||
|
|
||||||
EventFd _eventfd;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -252,11 +252,6 @@ namespace ix
|
|||||||
{
|
{
|
||||||
webSocketMessageType = WebSocket_MessageType_Pong;
|
webSocketMessageType = WebSocket_MessageType_Pong;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case WebSocketTransport::FRAGMENT:
|
|
||||||
{
|
|
||||||
webSocketMessageType = WebSocket_MessageType_Fragment;
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketErrorInfo webSocketErrorInfo;
|
WebSocketErrorInfo webSocketErrorInfo;
|
||||||
@@ -379,9 +374,4 @@ namespace ix
|
|||||||
{
|
{
|
||||||
_automaticReconnection = false;
|
_automaticReconnection = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WebSocket::bufferedAmount() const
|
|
||||||
{
|
|
||||||
return _ws.bufferedAmount();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -39,8 +39,7 @@ namespace ix
|
|||||||
WebSocket_MessageType_Close = 2,
|
WebSocket_MessageType_Close = 2,
|
||||||
WebSocket_MessageType_Error = 3,
|
WebSocket_MessageType_Error = 3,
|
||||||
WebSocket_MessageType_Ping = 4,
|
WebSocket_MessageType_Ping = 4,
|
||||||
WebSocket_MessageType_Pong = 5,
|
WebSocket_MessageType_Pong = 5
|
||||||
WebSocket_MessageType_Fragment = 6
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WebSocketOpenInfo
|
struct WebSocketOpenInfo
|
||||||
@@ -112,7 +111,6 @@ namespace ix
|
|||||||
const std::string& getUrl() const;
|
const std::string& getUrl() const;
|
||||||
const WebSocketPerMessageDeflateOptions& getPerMessageDeflateOptions() const;
|
const WebSocketPerMessageDeflateOptions& getPerMessageDeflateOptions() const;
|
||||||
int getHeartBeatPeriod() const;
|
int getHeartBeatPeriod() const;
|
||||||
size_t bufferedAmount() const;
|
|
||||||
|
|
||||||
void enableAutomaticReconnection();
|
void enableAutomaticReconnection();
|
||||||
void disableAutomaticReconnection();
|
void disableAutomaticReconnection();
|
||||||
|
@@ -1,31 +1,7 @@
|
|||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012, 2013 <dhbaird@gmail.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IXWebSocketTransport.cpp
|
* IXWebSocketTransport.cpp
|
||||||
* Author: Benjamin Sergeant
|
* Author: Benjamin Sergeant
|
||||||
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
|
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -38,6 +14,14 @@
|
|||||||
#include "IXUrlParser.h"
|
#include "IXUrlParser.h"
|
||||||
#include "IXSocketFactory.h"
|
#include "IXSocketFactory.h"
|
||||||
|
|
||||||
|
#ifdef IXWEBSOCKET_USE_TLS
|
||||||
|
# ifdef __APPLE__
|
||||||
|
# include "IXSocketAppleSSL.h"
|
||||||
|
# else
|
||||||
|
# include "IXSocketOpenSSL.h"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@@ -96,6 +80,16 @@ namespace ix
|
|||||||
std::string("Could not parse URL ") + url);
|
std::string("Could not parse URL ") + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (protocol != "ws" && protocol != "wss")
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Invalid protocol: " << protocol
|
||||||
|
<< " for url " << url
|
||||||
|
<< " . Supported protocols are ws and wss";
|
||||||
|
|
||||||
|
return WebSocketInitResult(false, 0, ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
bool tls = protocol == "wss";
|
bool tls = protocol == "wss";
|
||||||
std::string errorMsg;
|
std::string errorMsg;
|
||||||
_socket = createSocket(tls, errorMsg);
|
_socket = createSocket(tls, errorMsg);
|
||||||
@@ -190,57 +184,38 @@ namespace ix
|
|||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << kHeartBeatPingMessage << "::" << _heartBeatPeriod << "s";
|
ss << kHeartBeatPingMessage << "::" << _heartBeatPeriod << "s";
|
||||||
sendPing(ss.str());
|
sendPing(ss.str());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Make sure we send all the buffered data
|
|
||||||
// there can be a lot of it for large messages.
|
|
||||||
else if (pollResult == PollResultType_SendRequest)
|
|
||||||
{
|
|
||||||
while (!isSendBufferEmpty() && !_requestInitCancellation)
|
|
||||||
{
|
|
||||||
sendOnSocket();
|
|
||||||
|
|
||||||
// Sleep 10ms between each send so that we dont busy loop
|
while (true)
|
||||||
// A better strategy would be to select on the socket to
|
{
|
||||||
// check whether we can write to it without blocking
|
ssize_t ret = _socket->recv((char*)&_readbuf[0], _readbuf.size());
|
||||||
std::chrono::duration<double, std::micro> duration(10);
|
|
||||||
std::this_thread::sleep_for(duration);
|
if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
|
||||||
|
_socket->getErrno() == EAGAIN))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (ret <= 0)
|
||||||
|
{
|
||||||
|
_rxbuf.clear();
|
||||||
|
_socket->close();
|
||||||
|
setReadyState(CLOSED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_rxbuf.insert(_rxbuf.end(),
|
||||||
|
_readbuf.begin(),
|
||||||
|
_readbuf.begin() + ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pollResult == PollResultType_ReadyForRead)
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
ssize_t ret = _socket->recv((char*)&_readbuf[0], _readbuf.size());
|
|
||||||
|
|
||||||
if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
|
if (isSendBufferEmpty() && _readyState == CLOSING)
|
||||||
_socket->getErrno() == EAGAIN))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (ret <= 0)
|
|
||||||
{
|
|
||||||
_rxbuf.clear();
|
|
||||||
_socket->close();
|
|
||||||
setReadyState(CLOSED);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_rxbuf.insert(_rxbuf.end(),
|
|
||||||
_readbuf.begin(),
|
|
||||||
_readbuf.begin() + ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (pollResult == PollResultType_Error)
|
|
||||||
{
|
{
|
||||||
_socket->close();
|
_socket->close();
|
||||||
|
setReadyState(CLOSED);
|
||||||
}
|
}
|
||||||
else if (pollResult == PollResultType_CloseRequest)
|
|
||||||
{
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
},
|
||||||
_heartBeatPeriod);
|
_heartBeatPeriod);
|
||||||
}
|
}
|
||||||
@@ -417,10 +392,6 @@ namespace ix
|
|||||||
emitMessage(MSG, getMergedChunks(), ws, onMessageCallback);
|
emitMessage(MSG, getMergedChunks(), ws, onMessageCallback);
|
||||||
_chunks.clear();
|
_chunks.clear();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
emitMessage(FRAGMENT, std::string(), ws, onMessageCallback);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ws.opcode == wsheader_type::PING)
|
else if (ws.opcode == wsheader_type::PING)
|
||||||
@@ -504,7 +475,7 @@ namespace ix
|
|||||||
size_t wireSize = message.size();
|
size_t wireSize = message.size();
|
||||||
|
|
||||||
// When the RSV1 bit is 1 it means the message is compressed
|
// When the RSV1 bit is 1 it means the message is compressed
|
||||||
if (_enablePerMessageDeflate && ws.rsv1 && messageKind != FRAGMENT)
|
if (_enablePerMessageDeflate && ws.rsv1)
|
||||||
{
|
{
|
||||||
std::string decompressedMessage;
|
std::string decompressedMessage;
|
||||||
bool success = _perMessageDeflate.decompress(message, decompressedMessage);
|
bool success = _perMessageDeflate.decompress(message, decompressedMessage);
|
||||||
@@ -602,7 +573,7 @@ namespace ix
|
|||||||
// Send message
|
// Send message
|
||||||
sendFragment(opcodeType, fin, begin, end, compress);
|
sendFragment(opcodeType, fin, begin, end, compress);
|
||||||
|
|
||||||
if (onProgressCallback && !onProgressCallback((int)i, (int) steps))
|
if (onProgressCallback && !onProgressCallback(i, steps))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -611,12 +582,6 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request to flush the send buffer on the background thread if it isn't empty
|
|
||||||
if (!isSendBufferEmpty())
|
|
||||||
{
|
|
||||||
_socket->wakeUpFromPoll(Socket::kSendRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
return WebSocketSendInfo(true, compressionError, payloadSize, wireSize);
|
return WebSocketSendInfo(true, compressionError, payloadSize, wireSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -762,18 +727,8 @@ namespace ix
|
|||||||
sendData(wsheader_type::CLOSE, normalClosure, compress);
|
sendData(wsheader_type::CLOSE, normalClosure, compress);
|
||||||
setReadyState(CLOSING);
|
setReadyState(CLOSING);
|
||||||
|
|
||||||
_socket->wakeUpFromPoll(Socket::kCloseRequest);
|
_socket->wakeUpFromPoll();
|
||||||
_socket->close();
|
_socket->close();
|
||||||
|
|
||||||
_closeCode = 1000;
|
|
||||||
_closeReason = "Normal Closure";
|
|
||||||
setReadyState(CLOSED);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t WebSocketTransport::bufferedAmount() const
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(_txbufMutex);
|
|
||||||
return _txbuf.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@@ -45,8 +45,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
MSG,
|
MSG,
|
||||||
PING,
|
PING,
|
||||||
PONG,
|
PONG
|
||||||
FRAGMENT
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using OnMessageCallback = std::function<void(const std::string&,
|
using OnMessageCallback = std::function<void(const std::string&,
|
||||||
@@ -77,7 +76,6 @@ namespace ix
|
|||||||
void setReadyState(ReadyStateValues readyStateValue);
|
void setReadyState(ReadyStateValues readyStateValue);
|
||||||
void setOnCloseCallback(const OnCloseCallback& onCloseCallback);
|
void setOnCloseCallback(const OnCloseCallback& onCloseCallback);
|
||||||
void dispatch(const OnMessageCallback& onMessageCallback);
|
void dispatch(const OnMessageCallback& onMessageCallback);
|
||||||
size_t bufferedAmount() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _url;
|
std::string _url;
|
||||||
|
11
makefile
11
makefile
@@ -8,10 +8,10 @@ brew:
|
|||||||
|
|
||||||
.PHONY: docker
|
.PHONY: docker
|
||||||
docker:
|
docker:
|
||||||
docker build -t ws:latest .
|
docker build -t broadcast_server:latest .
|
||||||
|
|
||||||
run:
|
run:
|
||||||
docker run --cap-add sys_ptrace -it ws:latest
|
docker run --cap-add sys_ptrace -it broadcast_server:latest bash
|
||||||
|
|
||||||
# this is helpful to remove trailing whitespaces
|
# this is helpful to remove trailing whitespaces
|
||||||
trail:
|
trail:
|
||||||
@@ -36,9 +36,6 @@ test_server:
|
|||||||
test:
|
test:
|
||||||
python test/run.py
|
python test/run.py
|
||||||
|
|
||||||
ws_test:
|
|
||||||
(cd ws ; sh test_ws.sh)
|
|
||||||
|
|
||||||
# For the fork that is configured with appveyor
|
# For the fork that is configured with appveyor
|
||||||
rebase_upstream:
|
rebase_upstream:
|
||||||
git fetch upstream
|
git fetch upstream
|
||||||
@@ -46,9 +43,5 @@ rebase_upstream:
|
|||||||
git reset --hard upstream/master
|
git reset --hard upstream/master
|
||||||
git push origin master --force
|
git push origin master --force
|
||||||
|
|
||||||
install_cmake_for_linux:
|
|
||||||
mkdir -p /tmp/cmake
|
|
||||||
(cd /tmp/cmake ; curl -L -O https://github.com/Kitware/CMake/releases/download/v3.14.0-rc4/cmake-3.14.0-rc4-Linux-x86_64.tar.gz ; tar zxf cmake-3.14.0-rc4-Linux-x86_64.tar.gz)
|
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
|
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
#include "IXTest.h"
|
#include "IXTest.h"
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
using namespace ix;
|
using namespace ix;
|
||||||
|
|
||||||
@@ -66,13 +65,7 @@ TEST_CASE("socket", "[socket]")
|
|||||||
std::shared_ptr<Socket> socket(new Socket);
|
std::shared_ptr<Socket> socket(new Socket);
|
||||||
std::string host("www.google.com");
|
std::string host("www.google.com");
|
||||||
int port = 80;
|
int port = 80;
|
||||||
|
std::string request("GET / HTTP/1.1\r\n\r\n");
|
||||||
std::stringstream ss;
|
|
||||||
ss << "GET / HTTP/1.1\r\n";
|
|
||||||
ss << "Host: " << host << "\r\n";
|
|
||||||
ss << "\r\n";
|
|
||||||
std::string request(ss.str());
|
|
||||||
|
|
||||||
int expectedStatus = 200;
|
int expectedStatus = 200;
|
||||||
int timeoutSecs = 3;
|
int timeoutSecs = 3;
|
||||||
|
|
||||||
|
@@ -69,15 +69,10 @@ namespace ix
|
|||||||
Logger() << msg;
|
Logger() << msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getAnyFreePortSimple()
|
|
||||||
{
|
|
||||||
static int defaultPort = 8090;
|
|
||||||
return defaultPort++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getAnyFreePort()
|
int getAnyFreePort()
|
||||||
{
|
{
|
||||||
int defaultPort = 8090;
|
int defaultPort = 8090;
|
||||||
|
|
||||||
int sockfd;
|
int sockfd;
|
||||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||||
{
|
{
|
||||||
@@ -127,15 +122,8 @@ namespace ix
|
|||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
#if defined(__has_feature)
|
|
||||||
# if __has_feature(address_sanitizer)
|
|
||||||
int port = getAnyFreePortSimple();
|
|
||||||
# else
|
|
||||||
int port = getAnyFreePort();
|
int port = getAnyFreePort();
|
||||||
# endif
|
|
||||||
#else
|
|
||||||
int port = getAnyFreePort();
|
|
||||||
#endif
|
|
||||||
//
|
//
|
||||||
// Only port above 1024 can be used by non root users, but for some
|
// Only port above 1024 can be used by non root users, but for some
|
||||||
// reason I got port 7 returned with macOS when binding on port 0...
|
// reason I got port 7 returned with macOS when binding on port 0...
|
||||||
|
@@ -164,21 +164,10 @@ namespace
|
|||||||
ss << "cmd_websocket_chat: Error ! " << error.reason;
|
ss << "cmd_websocket_chat: Error ! " << error.reason;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Ping)
|
|
||||||
{
|
|
||||||
log("cmd_websocket_chat: received ping message");
|
|
||||||
}
|
|
||||||
else if (messageType == ix::WebSocket_MessageType_Pong)
|
|
||||||
{
|
|
||||||
log("cmd_websocket_chat: received pong message");
|
|
||||||
}
|
|
||||||
else if (messageType == ix::WebSocket_MessageType_Fragment)
|
|
||||||
{
|
|
||||||
log("cmd_websocket_chat: received message fragment");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ss << "Unexpected ix::WebSocketMessageType";
|
// FIXME: missing ping/pong messages
|
||||||
|
ss << "Invalid ix::WebSocketMessageType";
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
11
test/run.py
11
test/run.py
@@ -6,10 +6,10 @@ osName = platform.system()
|
|||||||
print('os name = {}'.format(osName))
|
print('os name = {}'.format(osName))
|
||||||
|
|
||||||
root = os.path.dirname(os.path.realpath(__file__))
|
root = os.path.dirname(os.path.realpath(__file__))
|
||||||
buildDir = os.path.join(root, 'build', osName)
|
buildDir = os.path.join(root, 'build')
|
||||||
|
|
||||||
if not os.path.exists(buildDir):
|
if not os.path.exists(buildDir):
|
||||||
os.makedirs(buildDir)
|
os.mkdir(buildDir)
|
||||||
|
|
||||||
os.chdir(buildDir)
|
os.chdir(buildDir)
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ sanitizerFlags = sanitizersFlags[sanitizer]
|
|||||||
# os.environ['CC'] = 'clang-cl'
|
# os.environ['CC'] = 'clang-cl'
|
||||||
# os.environ['CXX'] = 'clang-cl'
|
# os.environ['CXX'] = 'clang-cl'
|
||||||
|
|
||||||
cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug {} {} ../..'.format(generator, sanitizerFlags)
|
cmakeCmd = 'cmake -DCMAKE_BUILD_TYPE=Debug {} {} ..'.format(generator, sanitizerFlags)
|
||||||
print(cmakeCmd)
|
print(cmakeCmd)
|
||||||
ret = os.system(cmakeCmd)
|
ret = os.system(cmakeCmd)
|
||||||
assert ret == 0, 'CMake failed, exiting'
|
assert ret == 0, 'CMake failed, exiting'
|
||||||
@@ -67,7 +67,6 @@ def findFiles(prefix):
|
|||||||
|
|
||||||
# We need to copy the zlib DLL in the current work directory
|
# We need to copy the zlib DLL in the current work directory
|
||||||
shutil.copy(os.path.join(
|
shutil.copy(os.path.join(
|
||||||
'..',
|
|
||||||
'..',
|
'..',
|
||||||
'..',
|
'..',
|
||||||
'third_party',
|
'third_party',
|
||||||
@@ -78,8 +77,6 @@ shutil.copy(os.path.join(
|
|||||||
'bin',
|
'bin',
|
||||||
'zlib.dll'), '.')
|
'zlib.dll'), '.')
|
||||||
|
|
||||||
lldb = "lldb --batch -o 'run' -k 'thread backtrace all' -k 'quit 1'"
|
testCommand = '{} {}'.format(testBinary, os.getenv('TEST', ''))
|
||||||
lldb = "" # Disabled for now
|
|
||||||
testCommand = '{} {} {}'.format(lldb, testBinary, os.getenv('TEST', ''))
|
|
||||||
ret = os.system(testCommand)
|
ret = os.system(testCommand)
|
||||||
assert ret == 0, 'Test command failed'
|
assert ret == 0, 'Test command failed'
|
||||||
|
20
third_party/homebrew_formula.rb
vendored
20
third_party/homebrew_formula.rb
vendored
@@ -1,20 +0,0 @@
|
|||||||
class Ixwebsocket < Formula
|
|
||||||
desc "WebSocket client and server, and HTTP client command-line tool"
|
|
||||||
homepage "https://github.com/machinezone/IXWebSocket"
|
|
||||||
url "https://github.com/machinezone/IXWebSocket/archive/v1.1.0.tar.gz"
|
|
||||||
sha256 "52592ce3d0a67ad0f90ac9e8a458f61724175d95a01a38d1bad3fcdc5c7b6666"
|
|
||||||
depends_on "cmake" => :build
|
|
||||||
|
|
||||||
def install
|
|
||||||
system "cmake", ".", *std_cmake_args
|
|
||||||
system "make", "install"
|
|
||||||
end
|
|
||||||
|
|
||||||
test do
|
|
||||||
system "#{bin}/ws", "--help"
|
|
||||||
system "#{bin}/ws", "send", "--help"
|
|
||||||
system "#{bin}/ws", "receive", "--help"
|
|
||||||
system "#{bin}/ws", "transfer", "--help"
|
|
||||||
system "#{bin}/ws", "curl", "--help"
|
|
||||||
end
|
|
||||||
end
|
|
58
ws/README.md
58
ws/README.md
@@ -1,62 +1,10 @@
|
|||||||
# General
|
|
||||||
|
|
||||||
ws is a command line tool that should exercise most of the IXWebSocket code, and provide example code.
|
|
||||||
|
|
||||||
```
|
|
||||||
$ ws --help
|
|
||||||
ws is a websocket tool
|
|
||||||
Usage: ws [OPTIONS] SUBCOMMAND
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h,--help Print this help message and exit
|
|
||||||
|
|
||||||
Subcommands:
|
|
||||||
send Send a file
|
|
||||||
receive Receive a file
|
|
||||||
transfer Broadcasting server
|
|
||||||
connect Connect to a remote server
|
|
||||||
chat Group chat
|
|
||||||
echo_server Echo server
|
|
||||||
broadcast_server Broadcasting server
|
|
||||||
ping Ping pong
|
|
||||||
curl HTTP Client
|
|
||||||
```
|
|
||||||
|
|
||||||
## file transfer
|
|
||||||
|
|
||||||
```
|
```
|
||||||
# Start transfer server, which is just a broadcast server at this point
|
# Start transfer server, which is just a broadcast server at this point
|
||||||
ws transfer # running on port 8080.
|
./ws transfer # running on port 8080.
|
||||||
|
|
||||||
# Start receiver first
|
# Start receiver first
|
||||||
ws receive ws://localhost:8080
|
./ws receive ws://localhost:8080
|
||||||
|
|
||||||
# Then send a file. File will be received and written to disk by the receiver process
|
# Then send a file. File will be received and written to disk by the receiver process
|
||||||
ws send ws://localhost:8080 /file/to/path
|
./ws send ws://localhost:8080 /file/to/path
|
||||||
```
|
|
||||||
|
|
||||||
## curl
|
|
||||||
|
|
||||||
```
|
|
||||||
$ ws curl --help
|
|
||||||
HTTP Client
|
|
||||||
Usage: ws curl [OPTIONS] url
|
|
||||||
|
|
||||||
Positionals:
|
|
||||||
url TEXT REQUIRED Connection url
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h,--help Print this help message and exit
|
|
||||||
-d TEXT Form data
|
|
||||||
-F TEXT Form data
|
|
||||||
-H TEXT Header
|
|
||||||
--output TEXT Output file
|
|
||||||
-I Send a HEAD request
|
|
||||||
-L Follow redirects
|
|
||||||
--max-redirects INT Max Redirects
|
|
||||||
-v Verbose
|
|
||||||
-O Save output to disk
|
|
||||||
--compress Enable gzip compression
|
|
||||||
--connect-timeout INT Connection timeout
|
|
||||||
--transfer-timeout INT Transfer timeout
|
|
||||||
```
|
```
|
||||||
|
@@ -13,7 +13,6 @@ g++ --std=c++14 \
|
|||||||
../ixwebsocket/IXSocket.cpp \
|
../ixwebsocket/IXSocket.cpp \
|
||||||
../ixwebsocket/IXSocketServer.cpp \
|
../ixwebsocket/IXSocketServer.cpp \
|
||||||
../ixwebsocket/IXSocketConnect.cpp \
|
../ixwebsocket/IXSocketConnect.cpp \
|
||||||
../ixwebsocket/IXSocketFactory.cpp \
|
|
||||||
../ixwebsocket/IXDNSLookup.cpp \
|
../ixwebsocket/IXDNSLookup.cpp \
|
||||||
../ixwebsocket/IXCancellationRequest.cpp \
|
../ixwebsocket/IXCancellationRequest.cpp \
|
||||||
../ixwebsocket/IXWebSocket.cpp \
|
../ixwebsocket/IXWebSocket.cpp \
|
||||||
@@ -23,16 +22,12 @@ g++ --std=c++14 \
|
|||||||
../ixwebsocket/IXWebSocketPerMessageDeflate.cpp \
|
../ixwebsocket/IXWebSocketPerMessageDeflate.cpp \
|
||||||
../ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp \
|
../ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp \
|
||||||
../ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp \
|
../ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp \
|
||||||
../ixwebsocket/IXWebSocketHttpHeaders.cpp \
|
|
||||||
../ixwebsocket/IXHttpClient.cpp \
|
|
||||||
../ixwebsocket/IXUrlParser.cpp \
|
|
||||||
../ixwebsocket/IXSocketOpenSSL.cpp \
|
../ixwebsocket/IXSocketOpenSSL.cpp \
|
||||||
../ixwebsocket/linux/IXSetThreadName_linux.cpp \
|
../ixwebsocket/linux/IXSetThreadName_linux.cpp \
|
||||||
../third_party/msgpack11/msgpack11.cpp \
|
../third_party/jsoncpp/jsoncpp.cpp \
|
||||||
ixcrypto/IXBase64.cpp \
|
ixcrypto/IXBase64.cpp \
|
||||||
ixcrypto/IXHash.cpp \
|
ixcrypto/IXHash.cpp \
|
||||||
ixcrypto/IXUuid.cpp \
|
ixcrypto/IXUuid.cpp \
|
||||||
ws_http_client.cpp \
|
|
||||||
ws_ping_pong.cpp \
|
ws_ping_pong.cpp \
|
||||||
ws_broadcast_server.cpp \
|
ws_broadcast_server.cpp \
|
||||||
ws_echo_server.cpp \
|
ws_echo_server.cpp \
|
||||||
|
@@ -1,52 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
rm -rf /tmp/ws_test
|
|
||||||
mkdir -p /tmp/ws_test
|
|
||||||
|
|
||||||
# Start a transport server
|
|
||||||
cd /tmp/ws_test
|
|
||||||
ws transfer --port 8090 --pidfile /tmp/ws_test/pidfile &
|
|
||||||
|
|
||||||
# Wait until the transfer server is up
|
|
||||||
while true
|
|
||||||
do
|
|
||||||
nc -zv 127.0.0.1 8090 && {
|
|
||||||
echo "Transfer server up and running"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
echo "sleep ..."
|
|
||||||
sleep 0.1
|
|
||||||
done
|
|
||||||
|
|
||||||
# Start a receiver
|
|
||||||
mkdir -p /tmp/ws_test/receive
|
|
||||||
cd /tmp/ws_test/receive
|
|
||||||
ws receive ws://127.0.0.1:8090 &
|
|
||||||
|
|
||||||
mkdir /tmp/ws_test/send
|
|
||||||
cd /tmp/ws_test/send
|
|
||||||
# mkfile 10m 10M_file
|
|
||||||
dd if=/dev/urandom of=10M_file count=10000 bs=1024
|
|
||||||
|
|
||||||
# Start the sender job
|
|
||||||
ws send ws://127.0.0.1:8090 10M_file
|
|
||||||
|
|
||||||
# Wait until the file has been written to disk
|
|
||||||
while true
|
|
||||||
do
|
|
||||||
if test -f /tmp/ws_test/receive/10M_file ; then
|
|
||||||
echo "Received file does exists, exiting loop"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo "sleep ..."
|
|
||||||
sleep 0.1
|
|
||||||
done
|
|
||||||
|
|
||||||
cksum /tmp/ws_test/send/10M_file
|
|
||||||
cksum /tmp/ws_test/receive/10M_file
|
|
||||||
|
|
||||||
# Give some time to ws receive to terminate
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Cleanup
|
|
||||||
kill `cat /tmp/ws_test/pidfile`
|
|
30
ws/ws.cpp
30
ws/ws.cpp
@@ -16,8 +16,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <cli11/CLI11.hpp>
|
#include <cli11/CLI11.hpp>
|
||||||
#include <ixwebsocket/IXSocket.h>
|
#include <ixwebsocket/IXSocket.h>
|
||||||
@@ -33,8 +31,6 @@ int main(int argc, char** argv)
|
|||||||
std::string data;
|
std::string data;
|
||||||
std::string headers;
|
std::string headers;
|
||||||
std::string output;
|
std::string output;
|
||||||
std::string hostname("127.0.0.1");
|
|
||||||
std::string pidfile;
|
|
||||||
bool headersOnly = false;
|
bool headersOnly = false;
|
||||||
bool followRedirects = false;
|
bool followRedirects = false;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
@@ -54,8 +50,6 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
CLI::App* transferApp = app.add_subcommand("transfer", "Broadcasting server");
|
CLI::App* transferApp = app.add_subcommand("transfer", "Broadcasting server");
|
||||||
transferApp->add_option("--port", port, "Connection url");
|
transferApp->add_option("--port", port, "Connection url");
|
||||||
transferApp->add_option("--host", hostname, "Hostname");
|
|
||||||
transferApp->add_option("--pidfile", pidfile, "Pid file");
|
|
||||||
|
|
||||||
CLI::App* connectApp = app.add_subcommand("connect", "Connect to a remote server");
|
CLI::App* connectApp = app.add_subcommand("connect", "Connect to a remote server");
|
||||||
connectApp->add_option("url", url, "Connection url")->required();
|
connectApp->add_option("url", url, "Connection url")->required();
|
||||||
@@ -65,12 +59,10 @@ int main(int argc, char** argv)
|
|||||||
chatApp->add_option("user", user, "User name")->required();
|
chatApp->add_option("user", user, "User name")->required();
|
||||||
|
|
||||||
CLI::App* echoServerApp = app.add_subcommand("echo_server", "Echo server");
|
CLI::App* echoServerApp = app.add_subcommand("echo_server", "Echo server");
|
||||||
echoServerApp->add_option("--port", port, "Port");
|
echoServerApp->add_option("--port", port, "Connection url");
|
||||||
echoServerApp->add_option("--host", hostname, "Hostname");
|
|
||||||
|
|
||||||
CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server");
|
CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server");
|
||||||
broadcastServerApp->add_option("--port", port, "Port");
|
broadcastServerApp->add_option("--port", port, "Connection url");
|
||||||
broadcastServerApp->add_option("--host", hostname, "Hostname");
|
|
||||||
|
|
||||||
CLI::App* pingPongApp = app.add_subcommand("ping", "Ping pong");
|
CLI::App* pingPongApp = app.add_subcommand("ping", "Ping pong");
|
||||||
pingPongApp->add_option("url", url, "Connection url")->required();
|
pingPongApp->add_option("url", url, "Connection url")->required();
|
||||||
@@ -94,21 +86,9 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
ix::Socket::init();
|
ix::Socket::init();
|
||||||
|
|
||||||
// pid file handling
|
|
||||||
|
|
||||||
if (app.got_subcommand("transfer"))
|
if (app.got_subcommand("transfer"))
|
||||||
{
|
{
|
||||||
if (!pidfile.empty())
|
return ix::ws_transfer_main(port);
|
||||||
{
|
|
||||||
unlink(pidfile.c_str());
|
|
||||||
|
|
||||||
std::ofstream f;
|
|
||||||
f.open(pidfile);
|
|
||||||
f << getpid();
|
|
||||||
f.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ix::ws_transfer_main(port, hostname);
|
|
||||||
}
|
}
|
||||||
else if (app.got_subcommand("send"))
|
else if (app.got_subcommand("send"))
|
||||||
{
|
{
|
||||||
@@ -129,11 +109,11 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else if (app.got_subcommand("echo_server"))
|
else if (app.got_subcommand("echo_server"))
|
||||||
{
|
{
|
||||||
return ix::ws_echo_server_main(port, hostname);
|
return ix::ws_echo_server_main(port);
|
||||||
}
|
}
|
||||||
else if (app.got_subcommand("broadcast_server"))
|
else if (app.got_subcommand("broadcast_server"))
|
||||||
{
|
{
|
||||||
return ix::ws_broadcast_server_main(port, hostname);
|
return ix::ws_broadcast_server_main(port);
|
||||||
}
|
}
|
||||||
else if (app.got_subcommand("ping"))
|
else if (app.got_subcommand("ping"))
|
||||||
{
|
{
|
||||||
|
8
ws/ws.h
8
ws/ws.h
@@ -24,9 +24,9 @@ namespace ix
|
|||||||
|
|
||||||
int ws_ping_pong_main(const std::string& url);
|
int ws_ping_pong_main(const std::string& url);
|
||||||
|
|
||||||
int ws_echo_server_main(int port, const std::string& hostname);
|
int ws_echo_server_main(int port);
|
||||||
int ws_broadcast_server_main(int port, const std::string& hostname);
|
|
||||||
int ws_transfer_main(int port, const std::string& hostname);
|
int ws_broadcast_server_main(int port);
|
||||||
|
|
||||||
int ws_chat_main(const std::string& url,
|
int ws_chat_main(const std::string& url,
|
||||||
const std::string& user);
|
const std::string& user);
|
||||||
@@ -36,6 +36,8 @@ namespace ix
|
|||||||
int ws_receive_main(const std::string& url,
|
int ws_receive_main(const std::string& url,
|
||||||
bool enablePerMessageDeflate);
|
bool enablePerMessageDeflate);
|
||||||
|
|
||||||
|
int ws_transfer_main(int port);
|
||||||
|
|
||||||
int ws_send_main(const std::string& url,
|
int ws_send_main(const std::string& url,
|
||||||
const std::string& path);
|
const std::string& path);
|
||||||
}
|
}
|
||||||
|
@@ -10,11 +10,11 @@
|
|||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
int ws_broadcast_server_main(int port, const std::string& hostname)
|
int ws_broadcast_server_main(int port)
|
||||||
{
|
{
|
||||||
std::cout << "Listening on " << hostname << ":" << port << std::endl;
|
std::cout << "Listening on port " << port << std::endl;
|
||||||
|
|
||||||
ix::WebSocketServer server(port, hostname);
|
ix::WebSocketServer server(port);
|
||||||
|
|
||||||
server.setOnConnectionCallback(
|
server.setOnConnectionCallback(
|
||||||
[&server](std::shared_ptr<ix::WebSocket> webSocket)
|
[&server](std::shared_ptr<ix::WebSocket> webSocket)
|
||||||
@@ -39,47 +39,16 @@ namespace ix
|
|||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocket_MessageType_Close)
|
||||||
{
|
{
|
||||||
std::cerr << "Closed connection"
|
std::cerr << "Closed connection" << std::endl;
|
||||||
<< " code " << closeInfo.code
|
|
||||||
<< " reason " << closeInfo.reason << std::endl;
|
|
||||||
}
|
|
||||||
else if (messageType == ix::WebSocket_MessageType_Error)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "Connection error: " << error.reason << std::endl;
|
|
||||||
ss << "#retries: " << error.retries << std::endl;
|
|
||||||
ss << "Wait time(ms): " << error.wait_time << std::endl;
|
|
||||||
ss << "HTTP Status: " << error.http_status << std::endl;
|
|
||||||
std::cerr << ss.str();
|
|
||||||
}
|
|
||||||
else if (messageType == ix::WebSocket_MessageType_Fragment)
|
|
||||||
{
|
|
||||||
std::cerr << "Received message fragment" << std::endl;
|
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocket_MessageType_Message)
|
||||||
{
|
{
|
||||||
std::cerr << "Received " << wireSize << " bytes" << std::endl;
|
std::cerr << "Received " << wireSize << " bytes" << std::endl;
|
||||||
|
|
||||||
for (auto&& client : server.getClients())
|
for (auto&& client : server.getClients())
|
||||||
{
|
{
|
||||||
if (client != webSocket)
|
if (client != webSocket)
|
||||||
{
|
{
|
||||||
client->send(str,
|
client->send(str);
|
||||||
[](int current, int total) -> bool
|
|
||||||
{
|
|
||||||
std::cerr << "Step " << current
|
|
||||||
<< " out of " << total << std::endl;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
size_t bufferedAmount = client->bufferedAmount();
|
|
||||||
std::cerr << bufferedAmount << " bytes left to be sent" << std::endl;
|
|
||||||
|
|
||||||
std::chrono::duration<double, std::milli> duration(10);
|
|
||||||
std::this_thread::sleep_for(duration);
|
|
||||||
} while (client->bufferedAmount() != 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -94,26 +94,16 @@ namespace ix
|
|||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocket_MessageType_Open)
|
||||||
{
|
{
|
||||||
log("ws chat: connected");
|
ss << "cmd_websocket_chat: user "
|
||||||
std::cout << "Uri: " << openInfo.uri << std::endl;
|
|
||||||
std::cout << "Handshake Headers:" << std::endl;
|
|
||||||
for (auto it : openInfo.headers)
|
|
||||||
{
|
|
||||||
std::cout << it.first << ": " << it.second << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
ss << "ws chat: user "
|
|
||||||
<< _user
|
<< _user
|
||||||
<< " Connected !";
|
<< " Connected !";
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocket_MessageType_Close)
|
||||||
{
|
{
|
||||||
ss << "ws chat: user "
|
ss << "cmd_websocket_chat: user "
|
||||||
<< _user
|
<< _user
|
||||||
<< " disconnected !"
|
<< " disconnected !";
|
||||||
<< " code " << closeInfo.code
|
|
||||||
<< " reason " << closeInfo.reason;
|
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocket_MessageType_Message)
|
||||||
@@ -127,7 +117,7 @@ namespace ix
|
|||||||
_receivedQueue.push(result.second);
|
_receivedQueue.push(result.second);
|
||||||
|
|
||||||
ss << std::endl
|
ss << std::endl
|
||||||
<< result.first << "(" << wireSize << " bytes)" << " > " << result.second
|
<< result.first << " > " << result.second
|
||||||
<< std::endl
|
<< std::endl
|
||||||
<< _user << " > ";
|
<< _user << " > ";
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
@@ -198,7 +188,5 @@ namespace ix
|
|||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
webSocketChat.stop();
|
webSocketChat.stop();
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -84,8 +84,6 @@ namespace ix
|
|||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocket_MessageType_Message)
|
||||||
{
|
{
|
||||||
std::cerr << "Received " << wireSize << " bytes" << std::endl;
|
|
||||||
|
|
||||||
ss << "ws_connect: received message: "
|
ss << "ws_connect: received message: "
|
||||||
<< str;
|
<< str;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
|
@@ -10,17 +10,17 @@
|
|||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
int ws_echo_server_main(int port, const std::string& hostname)
|
int ws_echo_server_main(int port)
|
||||||
{
|
{
|
||||||
std::cout << "Listening on " << hostname << ":" << port << std::endl;
|
std::cout << "Listening on port " << port << std::endl;
|
||||||
|
|
||||||
ix::WebSocketServer server(port, hostname);
|
ix::WebSocketServer server(port);
|
||||||
|
|
||||||
server.setOnConnectionCallback(
|
server.setOnConnectionCallback(
|
||||||
[](std::shared_ptr<ix::WebSocket> webSocket)
|
[&server](std::shared_ptr<ix::WebSocket> webSocket)
|
||||||
{
|
{
|
||||||
webSocket->setOnMessageCallback(
|
webSocket->setOnMessageCallback(
|
||||||
[webSocket](ix::WebSocketMessageType messageType,
|
[webSocket, &server](ix::WebSocketMessageType messageType,
|
||||||
const std::string& str,
|
const std::string& str,
|
||||||
size_t wireSize,
|
size_t wireSize,
|
||||||
const ix::WebSocketErrorInfo& error,
|
const ix::WebSocketErrorInfo& error,
|
||||||
@@ -39,18 +39,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocket_MessageType_Close)
|
||||||
{
|
{
|
||||||
std::cerr << "Closed connection"
|
std::cerr << "Closed connection" << std::endl;
|
||||||
<< " code " << closeInfo.code
|
|
||||||
<< " reason " << closeInfo.reason << std::endl;
|
|
||||||
}
|
|
||||||
else if (messageType == ix::WebSocket_MessageType_Error)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "Connection error: " << error.reason << std::endl;
|
|
||||||
ss << "#retries: " << error.retries << std::endl;
|
|
||||||
ss << "Wait time(ms): " << error.wait_time << std::endl;
|
|
||||||
ss << "HTTP Status: " << error.http_status << std::endl;
|
|
||||||
std::cerr << ss.str();
|
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocket_MessageType_Message)
|
||||||
{
|
{
|
||||||
|
@@ -107,12 +107,6 @@ namespace ix
|
|||||||
{
|
{
|
||||||
std::cout << msg;
|
std::cout << msg;
|
||||||
};
|
};
|
||||||
args.onProgressCallback = [](int current, int total) -> bool
|
|
||||||
{
|
|
||||||
std::cerr << "\r" << "Downloaded "
|
|
||||||
<< current << " bytes out of " << total;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
HttpParameters httpParameters = parsePostParameters(data);
|
HttpParameters httpParameters = parsePostParameters(data);
|
||||||
|
|
||||||
@@ -131,8 +125,6 @@ namespace ix
|
|||||||
out = httpClient.post(url, httpParameters, args);
|
out = httpClient.post(url, httpParameters, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << std::endl;
|
|
||||||
|
|
||||||
auto statusCode = std::get<0>(out);
|
auto statusCode = std::get<0>(out);
|
||||||
auto errorCode = std::get<1>(out);
|
auto errorCode = std::get<1>(out);
|
||||||
auto responseHeaders = std::get<2>(out);
|
auto responseHeaders = std::get<2>(out);
|
||||||
|
@@ -61,19 +61,10 @@ namespace ix
|
|||||||
const ix::WebSocketOpenInfo& openInfo,
|
const ix::WebSocketOpenInfo& openInfo,
|
||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
std::cerr << "Received " << wireSize << " bytes" << std::endl;
|
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocket_MessageType_Open)
|
||||||
{
|
{
|
||||||
log("ping_pong: connected");
|
log("ping_pong: connected");
|
||||||
|
|
||||||
std::cout << "Uri: " << openInfo.uri << std::endl;
|
|
||||||
std::cout << "Handshake Headers:" << std::endl;
|
|
||||||
for (auto it : openInfo.headers)
|
|
||||||
{
|
|
||||||
std::cout << it.first << ": " << it.second << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocket_MessageType_Close)
|
||||||
{
|
{
|
||||||
@@ -162,7 +153,5 @@ namespace ix
|
|||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
webSocketPingPong.stop();
|
webSocketPingPong.stop();
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -146,16 +146,11 @@ namespace ix
|
|||||||
std::string filename = data["filename"].string_value();
|
std::string filename = data["filename"].string_value();
|
||||||
filename = extractFilename(filename);
|
filename = extractFilename(filename);
|
||||||
|
|
||||||
std::string filenameTmp = filename + ".tmp";
|
std::cout << "Writing to disk: " << filename << std::endl;
|
||||||
|
std::ofstream out(filename);
|
||||||
std::cout << "Writing to disk: " << filenameTmp << std::endl;
|
|
||||||
std::ofstream out(filenameTmp);
|
|
||||||
out.write((char*)&content.front(), content.size());
|
out.write((char*)&content.front(), content.size());
|
||||||
out.close();
|
out.close();
|
||||||
|
|
||||||
std::cout << "Renaming " << filenameTmp << " to " << filename << std::endl;
|
|
||||||
rename(filenameTmp.c_str(), filename.c_str());
|
|
||||||
|
|
||||||
std::map<MsgPack, MsgPack> pdu;
|
std::map<MsgPack, MsgPack> pdu;
|
||||||
pdu["ack"] = true;
|
pdu["ack"] = true;
|
||||||
pdu["id"] = data["id"];
|
pdu["id"] = data["id"];
|
||||||
@@ -211,11 +206,6 @@ namespace ix
|
|||||||
handleMessage(str);
|
handleMessage(str);
|
||||||
_condition.notify_one();
|
_condition.notify_one();
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Fragment)
|
|
||||||
{
|
|
||||||
ss << "ws_receive: received fragment";
|
|
||||||
log(ss.str());
|
|
||||||
}
|
|
||||||
else if (messageType == ix::WebSocket_MessageType_Error)
|
else if (messageType == ix::WebSocket_MessageType_Error)
|
||||||
{
|
{
|
||||||
ss << "Connection error: " << error.reason << std::endl;
|
ss << "Connection error: " << error.reason << std::endl;
|
||||||
|
@@ -257,15 +257,6 @@ namespace ix
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
size_t bufferedAmount = _webSocket.bufferedAmount();
|
|
||||||
std::cout << bufferedAmount << " bytes left to be sent" << std::endl;
|
|
||||||
|
|
||||||
std::chrono::duration<double, std::milli> duration(10);
|
|
||||||
std::this_thread::sleep_for(duration);
|
|
||||||
} while (_webSocket.bufferedAmount() != 0);
|
|
||||||
|
|
||||||
bench.report();
|
bench.report();
|
||||||
auto duration = bench.getDuration();
|
auto duration = bench.getDuration();
|
||||||
auto transferRate = 1000 * content.size() / duration;
|
auto transferRate = 1000 * content.size() / duration;
|
||||||
|
@@ -10,11 +10,11 @@
|
|||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
int ws_transfer_main(int port, const std::string& hostname)
|
int ws_transfer_main(int port)
|
||||||
{
|
{
|
||||||
std::cout << "Listening on " << hostname << ":" << port << std::endl;
|
std::cout << "Listening on port " << port << std::endl;
|
||||||
|
|
||||||
ix::WebSocketServer server(port, hostname);
|
ix::WebSocketServer server(port);
|
||||||
|
|
||||||
server.setOnConnectionCallback(
|
server.setOnConnectionCallback(
|
||||||
[&server](std::shared_ptr<ix::WebSocket> webSocket)
|
[&server](std::shared_ptr<ix::WebSocket> webSocket)
|
||||||
@@ -39,22 +39,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocket_MessageType_Close)
|
||||||
{
|
{
|
||||||
std::cerr << "Closed connection"
|
std::cerr << "Closed connection" << std::endl;
|
||||||
<< " code " << closeInfo.code
|
|
||||||
<< " reason " << closeInfo.reason << std::endl;
|
|
||||||
}
|
|
||||||
else if (messageType == ix::WebSocket_MessageType_Error)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "Connection error: " << error.reason << std::endl;
|
|
||||||
ss << "#retries: " << error.retries << std::endl;
|
|
||||||
ss << "Wait time(ms): " << error.wait_time << std::endl;
|
|
||||||
ss << "HTTP Status: " << error.http_status << std::endl;
|
|
||||||
std::cerr << ss.str();
|
|
||||||
}
|
|
||||||
else if (messageType == ix::WebSocket_MessageType_Fragment)
|
|
||||||
{
|
|
||||||
std::cerr << "Received message fragment" << std::endl;
|
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocket_MessageType_Message)
|
||||||
{
|
{
|
||||||
@@ -63,22 +48,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
if (client != webSocket)
|
if (client != webSocket)
|
||||||
{
|
{
|
||||||
client->send(str,
|
client->send(str);
|
||||||
[](int current, int total) -> bool
|
|
||||||
{
|
|
||||||
std::cerr << "Step " << current
|
|
||||||
<< " out of " << total << std::endl;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
size_t bufferedAmount = client->bufferedAmount();
|
|
||||||
std::cerr << bufferedAmount << " bytes left to be sent" << std::endl;
|
|
||||||
|
|
||||||
std::chrono::duration<double, std::milli> duration(10);
|
|
||||||
std::this_thread::sleep_for(duration);
|
|
||||||
} while (client->bufferedAmount() != 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user